Best Practice für PHP-Klassenbibliotheken von Drittanbietern


17

Ich arbeite derzeit an einem Modul, das eine PHP-Bibliothek eines Drittanbieters benötigt, die im Wesentlichen eine einzelne PHP-Klasse ist. Normalerweise würde ich es in ein include / -Unterverzeichnis legen und hinzufügen

files[] = includes/Foo.php

in meine .info Datei und lass den Drupal 7 Klasse Autoloader sein Ding machen, wenn ich a $foo = new Foo().

Ich habe jedoch die Erlaubnis, dieses Modul der Öffentlichkeit zugänglich zu machen, und möchte die Bibliothek lieber nicht in das Modul aufnehmen. Ich bin mir der Komplikationen in Bezug auf die Lizenzierung durchaus bewusst, möchte sie jedoch wegen dieser Frage ignorieren.

Es gibt eine ähnliche Frage: Wie binde ich eine PHP-Bibliothek ein? , aber ich glaube nicht, dass dies mein Dilema beantwortet.

Diese Antworten auf diese Frage sagen , im Wesentlichen die verwenden Bibliotheken API , aber jedes einzelne Modul , dass ich gefunden habe , dass Verwendungen dies nur funktioniert eine libraries_get_path()die basepath zu bekommen (und enthält Ausweichpfad , wenn es nicht verfügbar ist) und dann tut einem requireoder includemit einigen Fehlerprüfung (oder nicht). Alle machen so etwas wie:

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

In diesem Fall macht die Bibliotheks-API eigentlich nichts. Ich sehe keinen Vorteil darin, diese gegenüber der alten Methode zu verwenden, bei der Benutzer aufgefordert werden, eine Kopie herunterzuladen und sie im Modulordner selbst abzulegen. Und es ist immer noch das Problem , dass die Modulentwickler noch muss manuell mit der Last tun include/ require. Das Facebook-Modul lädt beispielsweise nur die Bibliothek in a hook_initund das HTML-Purifier-Modul verfügt über eine interne Funktion, mit der jedes Mal geprüft und geladen werden kann, wenn die Bibliothek benötigt wird.

Dies mag eine weit verbreitete Praxis sein, aber es scheint keine bewährte Praxis zu sein.

Sollte mein Modul die Initiative ergreifen und ein deklarieren, hook_libraries_infodamit ich es verwenden kann libraries_load('foo')? Auch das scheint seltsam.


Ein weiteres Problem ist, ob die Lizenz der Drittanbieter-Bibliothek mit der von Drupal übereinstimmt. Wenn ja, und es ist nicht riesig, würde ich es einfach einschließen. Wenn dies nicht der Fall ist, können / sollten Sie es zunächst nicht einschließen, sodass der Bibliotheksansatz besser zu sein scheint und Ihre Endbenutzer es selbst herunterladen müssen.
Jimajamma

Ein Zweck von if (libraries_load($name)) {..}ist, eine WSOD zu vermeiden, falls die Bibliothek nicht vorhanden ist.
donquixote

Antworten:


7

In Branch 2.x des Libraries API-Moduls können Entwickler über hook_libraries_info () oder eine .info-Datei für die Bibliothek die folgenden Informationen definieren (siehe libraries.api ):

  • Die Abhängigkeiten der Bibliothek
  • Die Version, mit der die Bibliothek kompatibel ist, für jede der Abhängigkeiten
  • Die Liste der Dateien, die geladen werden müssen (CSS-, JavaScript- oder PHP-Dateien)

Die Liste der zu ladenden Dateien wird verwendet, um diese Dateien zu laden, wenn die Bibliothek benötigt wird. Dies bedeutet, dass Ihr Modul keine CSS- und JavaScript-Dateien mit drupal_add_css()oder laden muss drupal_add_js(), da dies bereits über das Bibliotheks-API-Modul erfolgt. Das Laden der Abhängigkeiten erfolgt über das Bibliotheks-API-Modul, ohne dass das aufrufende Modul etwas unternimmt.

Das Modul verwendet lediglich den folgenden Code zum Laden einer Bibliothek. (Siehe Verwenden der Bibliotheks-API 2.x (als Modulentwickler) .)

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

Wenn Sie nur feststellen müssen, ob eine Bibliothek vorhanden ist, sollte das Modul einen Code verwenden, der dem folgenden ähnlich ist.

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

Zwischen den Eigenschaften hook_libraries_info()kann auch zurückkehren, 'download url'was eigentlich nicht verwendet wird, auch nicht in der Verzweigung 3.x. Möglicherweise wird es in Zukunft verwendet, oder Module von Drittanbietern könnten sich in das Bibliotheks-API-Modul einbinden und die angeforderten, aber fehlenden Bibliotheken herunterladen.


Können Sie auf beliebte Module hinweisen, die dies mit PHP-Bibliotheken tun? Ein Teil der Motivation für die Frage war, dass ich Best Practices für ein öffentliches Modul befolgen konnte, und deshalb begann ich, nach solchen zu suchen, die die Bibliotheks-API verwenden. Ich habe keine gefunden, die hook_libraries_info () implementiert und libraries_load () intern verwendet.
mpdonadio

zencorderapi-Modul (Teil des Videomoduls) verwendet hook_libraries_info ()
AyeshK

@MPD Es gibt eine unvollständige Liste der Beispiele für Module, die die Bibliotheks-API verwenden .
kiamlaluno

@kiamlaluno, danke, das war der erste Ort, den ich sah. Von den sechs implementieren nur zwei dieser Bibliotheken hook_libraries_info. Ich glaube nicht, dass Ihre Antwort falsch ist, aber ich bin nicht davon überzeugt, dass dies derzeit eine weit verbreitete Best Practice ist. Eine der Bibliotheken hatte eine interessante Technik, die ich später ausprobieren und möglicherweise veröffentlichen werde.
mpdonadio

@MPD Version 7.x-2.0 wurde am 29. Juli veröffentlicht. Es ist wahrscheinlich, dass die meisten Module noch den 7.x-1-Ansatz verwenden.
kiamlaluno

5

Nach einigem Graben bin ich immer noch nicht davon überzeugt, was die beste Vorgehensweise ist. Inspiriert vom PHPMailer- Modul biete ich dies für klassenbasierte PHP-Bibliotheken an:

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,
    );
  }
}

Dies verwendet hook_registry_files_alter , um zu prüfen, ob eine Klasse existiert, und fügt der Klassenregistrierung eine Datei hinzu, falls diese nicht gefunden wird (das Äquivalent zu einer files[] = ...Zeile in einer Modul-INFO-Datei). Dann stehen die in foo.php definierten Klassen mit dem Autoloader zur Verfügung, sodass die Datei nicht explizit geladen werden muss, bevor die Klasse verwendet wird.

Dadurch wird auch eine Softwareanforderung für die Bibliotheks-API erstellt und verwendet, sofern verfügbar. Andernfalls wird ein angemessener Standard verwendet.

Es ist auch eine gute Idee, einige Prüfungen über hook_requirements hinzuzufügen , um sicherzustellen, dass die Datei vorhanden ist, dass der Autoloader die Klasse, die Versionsprüfung usw. findet.

Es ist auch erwähnenswert, dass ein Autoload-Ansatz für die Bibliotheks-API in der Problemwarteschlange diskutiert wird .


Vergessen Sie nicht, Ihren Cache nach der Implementierung von hook_registry_files_alter zu leeren, da er sonst nicht ausgelöst wird;)
saadlulu

2

Kurz gesagt: Wenn Sie vorhaben, das Modul für die Öffentlichkeit freizugeben und die Bibliothek (von Drittanbietern) nicht unter GPL steht, müssen Sie Bibliotheken als Abhängigkeit verwenden oder Benutzer bitten, diese Dateien manuell herunterzuladen (dies ist jedoch nicht möglich) Autoload aus der .info-Datei)

In etwas länger:

Der Grund, warum wir das Bibliotheksmodul benötigen, ist im Grunde genommen die Lizenzierung. Egal, ob Sie dieses Modul verwenden oder nicht, Sie schließen diese Datei auf irgendeine Weise ein.

Nun, ich denke, Sie haben keine guten Beispiele für solche Bibliotheken gefunden, die mit dem Modul geliefert wurden. Check out SMTP-Modul und es kommt mit den erforderlichen Klassen, wie es in der GPL ist. ( .info file blob ).

Siehe auch simplehtmldom- Modul, das nur die Datei enthält, aber sonst nichts.

Das nützlichste Modul für Bibliotheken ist, dass Sie Benutzer auffordern können, die Datei an einer beliebigen Stelle hochzuladen. Es ist nicht offensichtlich, dass Benutzer es in den Ordner sites / all / libraries hochladen. Es können sites / example.com / libraries oder ähnliches sein. Das Bibliotheksmodul kann Ihnen dabei helfen, sich auf Ihre eigentliche Arbeit zu konzentrieren, indem es das Verzeichniserkennungsmaterial für Sie erledigt.

Bei benutzerdefinierten Modulen, die ich für meine Kunden entwickle, füge ich normalerweise Dateien in den Modulordner ein und verwende je nach Verwendung der Bibliothek den Eintrag require_once oder .info.

Lizenzprobleme sind nicht der einzige Grund, das Bibliotheksmodul zu verwenden. Was ist, wenn die Bibliothek eines Drittanbieters schnelle Veröffentlichungszyklen aufweist und Ihr Modul nur minimal entwickelt ist? Wenn Sie es in das Modul aufnehmen, müssen Sie jedes Mal eine neue Version erstellen. Sie werden wahrscheinlich kein Release 7.x-1.99 wollen, das dem 7.x-1.0 sehr ähnlich ist.


Vielen Dank, dass Sie sich die Zeit genommen haben, uns zu antworten. Ich habe meine Frage ein wenig bearbeitet, um sie zu klären. Die Frage dreht sich nicht wirklich um die Komplikationen von Lizenzierungs- und Veröffentlichungsplänen und wie die Bibliotheks-API dabei hilft. Ich bin eher neugierig auf bewährte Methoden zum Laden von Bibliotheken von Drittanbietern.
mpdonadio

2

Das Hauptproblem scheint das automatische Laden zu sein.

Sie können das Bibliotheksmodul und das xautoload- Modul verwenden.

Dann tun Sie dies in Ihrem eigenen Modul

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');
      },
    ),
  );
}

Dies wird hier ausführlicher erklärt:
xautoload.api.php Weitere Informationen
zum Argument $ api.

Hinweis: Sie können auch eigene "Handler" schreiben, um exotischere Old-School-Muster jenseits von PSR-0 oder PEAR zu implementieren. Wenn Sie dabei Hilfe benötigen, stellen Sie ein Problem in die Warteschlange von xautoload.

Hinweis: Es gibt mehrere Möglichkeiten, die Namespaces Ihrer Bibliothek zu registrieren. Dies ist die einfachste Methode, wenn die Namespaces in jeder Anforderung registriert werden sollen.


1
Ich sollte hinzufügen, dies hilft nicht beim Laden von prozeduralen Dateien. Dies muss manuell erfolgen, sobald Sie die Bibliothek in einer Anforderung benötigen.
donquixote

Einige Bibliotheken haben auch ihre eigenen Klassenladelösungen. Es kann jedoch praktischer sein, einen bereits in Drupal / contrib verfügbaren Loader zu verwenden.
donquixote
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.