Краща практика для сторонніх бібліотек PHP-класів


17

Зараз я працюю над модулем, який вимагає сторонньої бібліотеки PHP, яка по суті є єдиним класом PHP. Як правило, я розміщую його у підкаталозі include / add та add

files[] = includes/Foo.php

до мого файлу .info, і дозвольте автонавантажувачу класу Drupal 7 робити все, що я роблю $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 має внутрішню функцію для перевірки та завантаження кожного разу, коли бібліотека потрібна.

Це може бути поширеною практикою, але це не здається найкращою практикою.

Чи повинен мій модуль брати на себе ініціативу та заявити таке, hook_libraries_infoщоб я міг його використовувати libraries_load('foo')? Це теж здається дивним.


Інше питання полягає в тому, чи відповідає ліцензія третьої сторони бібліотеці друкарській. Якщо це так, і воно не величезне, я б просто включив його. Якщо це не так, ви не можете / не повинні включати його для початку, тому бібліотечний підхід здається кращим, і ваші кінцеві кінцеві користувачі повинні завантажити його самостійно.
Jimajamma

Однією метою if (libraries_load($name)) {..}є уникнути WSOD у випадку, якщо бібліотека відсутня.
donquixote

Відповіді:


7

Відділення 2.x модуля API бібліотек дозволяє розробникам визначати за допомогою гачок_libraries_info () або .info-файлу для бібліотеки наступну інформацію (див. Бібліотеки.api ):

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

Список файлів, які потрібно завантажити, використовується для завантаження цих файлів, коли потрібна бібліотека. Це означає, що вашому модулю не потрібно завантажувати файли CSS та JavaScript drupal_add_css(), або drupal_add_js(), як це вже зроблено з модуля API бібліотек. Завантаження залежностей - це завдання, виконане за допомогою модуля 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. Можливо, він буде використовуватися в майбутньому, або сторонні модулі можуть підключитися до модуля API бібліотек і завантажити бібліотеки, які вимагаються, але відсутні.


Чи можете ви вказати будь-які популярні модулі, які роблять це з бібліотеками PHP? Частина мотивації запитання полягала в тому, щоб я міг дотримуватися кращих практик публічного модуля, тому я почав шукати ті, які використовують API бібліотек. Я не знайшов жодного, який реалізував bika_libraries_info () і використовував библиотеки_load () внутрішньо.
mpdonadio

Модуль zencorderapi (частина відеомодуля) використовує гак_лібрарії_info ()
AyeshK

@MPD Існує частковий список прикладів модулів, що надаються за допомогою API бібліотек .
kiamlaluno

@kiamlaluno, дякую, це було перше місце, яке я подивився. З шести лише дві з цих бібліотек реалізують прив'язку_лібрацій_інфо. Я не думаю, що ваша відповідь є неправильною, але я не переконаний, що це найкраща практична практика зараз. В одній з бібліотек була цікава техніка, яку я збираюсь перевірити і, можливо, опублікувати пізніше.
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,
    );
  }
}

При цьому використовується гак_реєстр_філе_алтер, щоб перевірити наявність класу, а якщо його не знайти, додати файл до реєстру класів (еквівалент files[] = ...рядку в модулі .info-файла). Тоді класи, визначені у foo.php, будуть доступні разом з автозавантажувачем, тому немає необхідності явно завантажувати файл перед використанням класу.

Це також створює м'яку вимогу до API бібліотек, і використовуватиме його, якщо є, інакше використовувати розумний дефолт.

Додавання декількох чеків за допомогою кнопок_запрошення, щоб переконатися, що файл існує, що автозавантажувач знаходить клас, перевірку версій тощо, також є хорошою ідеєю.

Варто також зазначити, що підхід до автоматичного завантаження API бібліотек обговорюється в черзі випусків.


Не забудьте очистити кеш-пам’яті після впровадження крюка_реєстра_файла_алтера, інакше він не буде тригером;)
saadlulu

2

Якщо коротко: якщо ви плануєте випустити модуль у загальнодоступну, а бібліотека (третя сторона) не є GPL'd, вам потрібно буде використовувати Бібліотеки як залежність або попросити користувачів завантажити ці файли вручну (але ви не зможете автозавантажте його з .info файлу)

Трохи довше:

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

Я думаю, ви не знайшли хороших прикладів для таких бібліотек, що постачаються із модулем. Перевірте модуль SMTP, і він постачається з необхідними класами, як це є в GPL. ( .info файл blob ).

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

Де модуль бібліотек стане у нагоді, це те, що ви можете попросити користувачів завантажити файл, куди вони хочуть. Не очевидно, що користувачі завантажуватимуть його у папку сайтів / усіх / бібліотек. Це можуть бути сайти / example.com / бібліотеки чи щось подібне. Модуль "Бібліотеки" може допомогти вам зосередитись на власній роботі, зробивши для вас матеріали з пошуку каталогів.

Для спеціальних модулів, які я розробляю для своїх клієнтів, я зазвичай включаю файли в папку модулів і використовую файл Requ_once або .info в залежності від використання бібліотеки.

Також проблеми з ліцензуванням не є єдиною причиною використання модуля Бібліотеки. Що робити, якщо стороння бібліотека має швидкі цикли випуску і ваш модуль розвинений мінімально? Якщо ви включите його в модуль, вам доведеться робити щойно новий випуск. Ви не хочете мати випуск 7.x-1.99, який значно схожий на 7.x-1.0, я думаю.


Дякуємо, що знайшли час для відповіді. Я трохи відредагував своє запитання, щоб уточнити. Питання не в тому, що стосується складності графіків ліцензування та випуску, а в тому, як 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.