Лучшие практики для сторонней библиотеки на основе классов PHP

17

В настоящее время я работаю над модулем, который требует сторонней библиотеки PHP, которая по сути является одним классом PHP. Обычно я помещаю его в подкаталог include / и добавляю

files[] = includes/Foo.php

в мой файл .info, и пусть автозагрузчик класса Drupal 7 сделает свое дело, когда я сделаю a $foo = new Foo().

У меня есть разрешение, однако, выпустить этот модуль для общественности, и я бы предпочел не включать библиотеку с этим модулем. Я хорошо осведомлен о сложностях, связанных с лицензированием, но ради этого вопроса я хотел бы проигнорировать это.

Есть похожий вопрос: как включить библиотеку PHP? , но я не думаю, что это отвечает моей дилемме.

Это ответы на этот вопрос, по сути, говорят об использовании API библиотек , но каждый найденный мною модуль, использующий его, просто libraries_get_path()получает базовый путь (и включает запасной путь, когда он недоступен), а затем выполняет requireили includeс некоторыми проверка ошибок (или нет). Все делают что-то вроде:

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
  }
}

В этом случае API библиотек ничего не делает. Я не вижу преимущества в использовании этого по сравнению со старым методом запроса пользователей загрузить копию и поместить ее в саму папку модуля. И еще есть проблема, что разработчику модуля все еще нужно вручную загружать с помощью include/ require. Например, модуль Facebook просто загружает библиотеку в a, hook_initа модуль HTML Purifier имеет внутреннюю функцию для проверки и загрузки каждый раз, когда библиотека нужна.

Это может быть широко распространенной практикой, но это не похоже на лучшую практику.

Должен ли мой модуль проявить инициативу и объявить, hook_libraries_infoчтобы я мог его использовать libraries_load('foo')? Это тоже кажется странным.

mpdonadio
источник
Другая проблема заключается в том, совпадает ли лицензия сторонней библиотеки с Drupal. Если это так, и оно не очень большое, я бы просто включил его. Если этого не произойдет, вы не можете / не должны включать его с самого начала, поэтому библиотечный подход выглядит лучше, и ваши конечные пользователи могут загрузить его самостоятельно.
Джимаджамма
Одна из целей if (libraries_load($name)) {..}состоит в том, чтобы избежать WSOD в случае отсутствия библиотеки.
Donquixote

Ответы:

7

Ветка 2.x модуля API библиотек позволяет разработчикам определять с помощью hook_libraries_info () или файла .info для библиотеки следующую информацию (см. Library.api ):

  • Зависимости библиотеки
  • Версия, с которой библиотека совместима, для каждой из зависимостей
  • Список файлов, которые необходимо загрузить (файлы CSS, JavaScript или PHP)

Список файлов, которые должны быть загружены, используется для загрузки этих файлов, когда требуется библиотека. Это означает, что вашему модулю не нужно загружать файлы CSS и JavaScript с помощью drupal_add_css()или drupal_add_js(), как это уже сделано из модуля API библиотек. Загрузка зависимостей - задача, выполняемая из модуля API библиотек, без вызова вызывающего модуля.

Все, что делает модуль, использует следующий код для загрузки библиотеки. (См. Использование библиотек 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. Возможно, он будет использоваться в будущем, или сторонние модули могут подключиться к модулю API библиотек и загрузить библиотеки, которые запрашиваются, но отсутствуют.

киамалуно
источник
Можете ли вы указать какие-либо популярные модули, которые делают это с PHP-библиотеками? Часть мотивации для этого вопроса заключалась в том, чтобы я мог следовать рекомендациям для общедоступного модуля, поэтому я начал искать те, которые используют API библиотек. Я не нашел ни одного, который бы реализовал hook_libraries_info () и использовал library_load () внутри.
mpdonadio
Модуль zencorderapi (часть модуля Video) использует hook_libraries_info ()
AyeshK
@kiamlaluno, спасибо, это было первое место, которое я посмотрел. Из шести только две из этих библиотек реализуют hook_libraries_info. Я не думаю, что ваш ответ неправильный, но я не уверен, что сейчас это широко распространенная лучшая практика. В одной из библиотек была интересная техника, которую я собираюсь опробовать и, возможно, опубликую позже.
mpdonadio
@MPD версия 7.x-2.0 была выпущена 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, будут доступны с автозагрузчиком, поэтому нет необходимости явно загружать файл перед использованием класса.

Это также создает мягкое требование для API библиотек и будет использовать его, если доступно, в противном случае используйте разумное значение по умолчанию.

Добавление некоторых проверок через hook_requirements, чтобы убедиться, что файл существует, что автозагрузчик находит класс, проверку версии и т. Д., Также является хорошей идеей.

Стоит также отметить, что подход с автозагрузкой для API библиотек обсуждается в очереди проблем.

mpdonadio
источник
Не забудьте очистить кеш после реализации hook_registry_files_alter, иначе он не сработает;)
saadlulu
2

Вкратце: если вы планируете выпустить модуль для общего пользования, и (сторонняя) библиотека не является лицензией GPL, вам нужно будет использовать библиотеки в качестве зависимости или попросить пользователей загрузить эти файлы вручную (но вы не сможете загрузить его из .info файла)

Немного дольше:

Причина, по которой нам нужен модуль Библиотеки, заключается в основном в лицензировании. Независимо от того, используете ли вы этот модуль или нет, вы каким-то образом включаете этот файл.

Ну, я думаю, что вы не нашли хороших примеров для таких библиотек, поставляемых с модулем. Проверьте модуль SMTP, и он поставляется с необходимыми классами, как в GPL. ( BLOB-файл .info ).

Также смотрите модуль simplehtmldom, который просто включает файл, но не более того.

Модуль Библиотеки очень удобен в том, что вы можете попросить пользователей загрузить файл куда угодно. Не очевидно, что пользователи будут загружать его в папку sites / all / library. Это могут быть сайты / example.com / библиотеки или что-то в этом роде. Модуль «Библиотеки» может помочь вам сосредоточиться на своей реальной работе, выполнив для вас работу по обнаружению каталогов.

Для пользовательских модулей, которые я разрабатываю для своих клиентов, я обычно включаю файлы в папку модулей и использую запись файла require_once или .info в зависимости от использования библиотеки.

Кроме того, проблемы с лицензированием - не единственная причина использования модуля библиотек. Что, если сторонняя библиотека имеет быстрые циклы выпуска, а ваш модуль минимально разработан? Если вы включите его в модуль, вам придется делать новый выпуск каждый раз. Вы не захотите иметь релиз 7.x-1.99, который очень похож на 7.x-1.0, я думаю.

AyeshK
источник
Спасибо, что нашли время ответить. Я немного отредактировал свой вопрос, чтобы уточнить. Вопрос не в сложности усложнения графиков лицензирования и выпуска, а в том, как 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
Кроме того, некоторые библиотеки имеют свои собственные решения для загрузки классов. Тем не менее, может быть удобнее использовать загрузчик, уже доступный в Drupal / contrib.
Donquixote