第三方基于PHP类的库的最佳实践


17

我目前正在开发一个需要第三方PHP库的模块,该库本质上是一个PHP类。通常,我会将其放置在include /子目录中并添加

files[] = includes/Foo.php

到我的.info文件,并在我执行$foo = new Foo()。时让Drupal 7类自动加载器完成它的工作 。

不过,我有权向公众发布此模块,而不希望将库与模块一起使用。我非常了解许可的复杂性,但是出于这个问题,我想忽略它。

还有一个类似的问题,如何包含PHP库?,但我真的不认为这可以解决我的难题。

这个回答这个问题本质上说,使用库API,但每一个模块,我已经发现,使用这只是做一个libraries_get_path()得到基本路径(包括备用路径时,不可用),然后再执行requireinclude一些错误检查(或不检查)。都做类似的事情:

if (!class_exists('Foo')) {
  $path = function_exists('libraries_get_path') ?
    libraries_get_path('foo') : 'sites/all/libraries/foo';
  if (!include($path . '/Foo.php')) {
      // handle this error
  }
}

在这种情况下,Libraries API实际上并没有做任何事情。与要求用户下载副本并将其放置在模块文件夹本身中的旧方法相比,我看不出使用此方法的优势。而且,仍然存在模块开发人员仍然需要使用/ 手动进行加载的问题。例如,Facebook模块只是将库加载到中,而HTML Purifier模块具有内部功能,可在每次需要该库时进行检查和加载。includerequirehook_init

这可能是一种普遍的做法,但似乎不是最佳做法。

我的模块应该主动声明一个a hook_libraries_info以便我使用libraries_load('foo')吗?这似乎也很奇怪。


另一个问题是第三方图书馆的许可证是否与drupal的许可证匹配。如果确实如此,并且它并不庞大,那么我就将其包括在内。如果没有,您一开始就不能/不应该包含它,因此图书馆的方法似乎更好,最终用户也可以自己下载它。
Jimajamma 2012年

一个目的if (libraries_load($name)) {..}是避免在库不存在的情况下使用WSOD。
donquixote

Answers:


7

图书馆API模块的分支2.x的允许开发人员定义,通过hook_libraries_info() ,或一个.info文件的库,下面的信息(参见libraries.api):

  • 库的依赖项
  • 对于每个依赖项,与库兼容的版本
  • 需要加载的文件列表(CSS,JavaScript或PHP文件)

需要该库时,将使用需要加载的文件列表来加载这些文件。这意味着您的模块无需使用drupal_add_css()或加载CSS和JavaScript文件。drupal_add_js()而Libraries API模块已经完成了此操作。加载依赖项是一项由Libraries API模块完成的任务,无需调用模块执行任何操作。

该模块所做的全部工作是使用以下代码来加载库。(请参阅使用Libraries API 2.x(作为模块开发人员)。)

// Try to load the library and check if that worked.
if (($library = libraries_load($name)) && !empty($library['loaded'])) {
  // Do something with the library here.
}

如果只需要检测是否存在一个库,则该模块应使用类似于以下代码的代码。

if (($library = libraries_detect($name)) && !empty($library['installed'])) {
  // The library is installed.
}
else {
  $error = $library['error'];
  $error_message = $library['error message'];
}

在属性之间hook_libraries_info()可以返回,还有'download url',即使在分支3.x中也没有实际使用。将来可能会使用它,或者第三方模块可以挂接到Libraries API模块中,并下载所请求但缺少的库。


您能指出使用PHP库执行此操作的任何流行模块吗?这个问题的部分动机是,我可以遵循公共模块的最佳实践,因此我开始寻找使用库API的方法。我没有找到任何实现hook_libraries_info()并在内部使用libraries_load()的东西。
mpdonadio

zencorderapi模块(视频模块的一部分)使用hook_libraries_info()
AyeshK 2012年


@kiamlaluno,谢谢,那是我看的第一处。在这六个库中,只有两个库实现了hook_libraries_info。我认为您的答案不对,但是我不认为这是目前广泛使用的最佳做法。其中一个库确实具有一种有趣的技术,我将对其进行测试,并可能稍后发布。
mpdonadio

@MPD版本7.x-2.0已于7月29日发布;大多数模块可能仍在使用7.x-1方法。
kiamlaluno

5

经过大量的挖掘之后,我仍然不知道最佳实践是什么。受PHPMailer模块的启发,我为基于类的PHP库提供此功能:

function foo_registry_files_alter (&$files, $modules)
{
  if (!class_exists('Foo')) {
    $library_path = function_exists('libraries_get_path') ?
      libraries_get_path('foo') : 'sites/all/libraries/foo';

    $files[$library_path . '/Foo.php'] = array(
      'module' => 'foo',
      'weight' => 0,
    );
  }
}

这使用hook_registry_files_alter来检查类的存在,如果找不到,则将一个文件添加到类注册表中(相当于files[] = ...模块.info文件中的一行)。然后,在foo.php中定义的类将随自动加载器一起提供,因此在使用该类之前无需显式加载文件。

这还会对Libraries API产生一个软要求,并将在可用时使用它,否则使用合理的默认值。

通过hook_requirements添加一些检查以确保文件存在,自动装载器找到类,版本检查等也是一个好主意。

还值得注意的是,在发布队列中正在讨论库API的自动加载方法。


在实现hook_registry_files_alter之后,不要忘记清除缓存,否则它不会触发;)
saadlulu 2013年

2

简而言之: 如果您打算将模块发布给公众,并且(第三方)库不是经过GPL授权的,则您将需要使用库作为依赖项,或者要求用户手动下载这些文件(但您将无法从.info文件自动加载)

再过一会儿:

我们之所以需要库模块,基本上是许可。无论是否使用该模块,都将以某种方式包含该文件。

好吧,我认为您没有找到有关此类模块库的很好的例子。检出SMTP模块,它带有GPL中必需的类。(.info文件blob)。

另请参见simplehtmldom模块,该模块仅包含文件,而没有其他内容。

库模块方便使用的地方是,您可以要求用户将文件上传到任何需要的地方。用户将其上传到site / all / libraries文件夹并不明显。可能是site / example.com / libraries或类似的东西。库模块可以通过为您执行目录发现工作来帮助您专注于实际工作。

对于为客户开发的定制模块,通常会在模块文件夹中包含文件,并根据库的使用情况使用require_once或.info文件条目。

同样,许可问题也不是使用库模块的唯一原因。如果第三方库具有快速发布周期并且您的模块开发最少,该怎么办?如果将其包含在模块中,则每次都必须发布一个新版本。您可能不想发布7.x-1.99,它与7.x-1.0非常相似。


感谢您抽出宝贵的时间回答。我编辑了我的问题以澄清。问题并不是真正的关于许可和发布时间表的复杂性,以及Libraries API如何提供帮助。我对实际加载第三方库的最佳做法感到好奇。
mpdonadio

2

看来主要问题是自动加载。

您可以使用模块加xautoload模块。

然后在您自己的模块中

function mymodule_libraries_info() {

  return array(
    'mymodule-test-lib' => array(
      'name' => 'My test library',
      ..
      'xautoload' => function($api) {
        // Register a namespace with PSR-0 root in <library dir>/lib/
        // Note: $api already knows the library directory.
        // Note: We could omit the 'lib', as this is the default value.
        $api->namespaceRoot('XALib\TestNamespace', 'lib');
      },
    ),
  );
}

此处将
对此进行详细说明:xautoload.api.php
有关$ api参数的更多信息。

注意:您还可以编写自己的“处理程序”,以实现除PSR-0或PEAR以外的更多奇特的老式模式。如果您需要帮助,请在xautoload队列中发布问题。

注意:注册库名称空间有多种方法。如果要在每个请求中注册名称空间,则这是最简单的。


1
我应该补充,这对加载程序文件没有帮助。一旦需要在请求中使用库,就需要手动完成此操作。
donquixote 2012年

另外,某些库具有自己的类加载解决方案。不过,使用已经在Drupal / contrib中提供的装载程序会更方便。
donquixote 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.