web-dev-qa-db-fra.com

Meilleure pratique pour les tiers PHP bibliothèque basée sur la classe

Je travaille actuellement sur un module qui nécessite une bibliothèque tierce PHP, qui est essentiellement une seule classe PHP. Normalement, je la placerais dans un inclut/sous-répertoire et ajouter

files[] = includes/Foo.php

à mon fichier .info et laissez le Drupal 7 chargeur automatique de classe faire son travail quand je fais un $foo = new Foo().

J'ai cependant la permission de publier ce module au public et je préfère ne pas inclure la bibliothèque avec le module. Je suis bien conscient des complications liées à l'octroi de licences, mais pour cette question, je voudrais l'ignorer.

Il y a une question similaire, Comment puis-je inclure une PHP? , mais je ne pense pas vraiment que cela réponde à mon dilema.

Cette réponse à cette question dit essentiellement d'utiliser Libraries API , mais chaque module que j'ai trouvé qui utilise cela fait juste unelibraries_get_path() pour obtenir le chemin de base (et inclut le chemin de secours lorsque il n'est pas disponible), puis fait un require ou include avec une vérification d'erreur (ou non). Tous font quelque chose comme:

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

Dans ce cas, l'API Libraries ne fait vraiment rien. Je ne vois pas l'avantage d'utiliser cela, par rapport à l'ancienne méthode consistant à demander aux utilisateurs de télécharger une copie et de la placer dans le dossier du module lui-même. Et, il y a toujours le problème que le développeur du module doit toujours manuellement faites le chargement avec include/require. Par exemple, le module Facebook charge simplement la bibliothèque dans un hook_init Et le module HTML Purifier a une fonction interne pour vérifier et charger chaque fois que la bibliothèque est nécessaire.

Cela peut être une pratique répandue , mais cela ne semble pas être une meilleure entraine toi.

Mon module doit-il prendre l'initiative et déclarer un hook_libraries_info Pour que je puisse utiliser libraries_load('foo')? Cela aussi semble étrange.

16
mpdonadio

La branche 2.x du module API Libraries permet aux développeurs de définir, via hook_libraries_info () , ou un fichier .info pour la bibliothèque, les informations suivantes (voir bibliothèques.api ):

  • Les dépendances de la bibliothèque
  • La version avec laquelle la bibliothèque est compatible, pour chacune des dépendances
  • La liste des fichiers à charger (CSS, JavaScript ou PHP)

La liste des fichiers à charger est utilisée pour charger ces fichiers, lorsque la bibliothèque est requise. Cela signifie que votre module n'a pas besoin de charger des fichiers CSS et JavaScript avec drupal_add_css() ou drupal_add_js(), comme cela est déjà fait à partir du module API des bibliothèques. Le chargement des dépendances est une tâche effectuée à partir du module API des bibliothèques, sans que le module appelant ne fasse quoi que ce soit.

Tout le module utilise le code suivant pour charger une bibliothèque. (Voir tilisation des bibliothèques API 2.x (en tant que développeur de module) .)

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

Si vous avez juste besoin de détecter si une bibliothèque est présente, le module doit utiliser un code similaire au suivant.

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

Entre les propriétés que hook_libraries_info() peut renvoyer, il y a aussi 'download url', Qui n'est pas réellement utilisé, pas même dans la branche 3.x. Il sera probablement utilisé à l'avenir, ou des modules tiers pourraient se connecter au module API des bibliothèques et télécharger les bibliothèques demandées, mais manquantes.

7
kiamlaluno

Après avoir creusé une quantité décente, je ne suis toujours pas convaincu de la meilleure pratique. Inspiré du module PHPMailer , je propose ceci pour les bibliothèques basées sur la classe 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,
    );
  }
}

Cela utilise hook_registry_files_alter pour vérifier l'existence d'une classe, et s'il n'est pas trouvé, ajouter un fichier au registre de classe (l'équivalent d'un files[] = ... ligne dans un fichier .info des modules). Ensuite, les classes définies dans foo.php seront disponibles avec l'autochargeur, il n'est donc pas nécessaire de charger explicitement le fichier avant d'utiliser la classe.

Cela crée également une exigence souple sur l'API des bibliothèques et l'utilisera si disponible, sinon utilisez une valeur par défaut raisonnable.

Ajouter quelques vérifications via un hook_requirements pour s'assurer que le fichier existe, que l'autochargeur trouve la classe, la vérification de version, etc., est également une bonne idée.

Il convient également de noter qu'une approche de chargement automatique pour l'API des bibliothèques est en cours de discussion dans la file d'attente des problèmes.

5
mpdonadio

En bref: Si vous prévoyez de publier le module au public et que la bibliothèque (tierce) n'est pas sous GPL, vous devrez utiliser les bibliothèques comme une dépendance ou demander aux utilisateurs de télécharger ces fichiers manuellement (mais vous ne pourrez pas le charger automatiquement à partir du fichier .info)

En un peu plus longtemps:

La raison pour laquelle nous avons besoin du module Bibliothèques est essentiellement la licence. Peu importe que vous utilisiez ou non ce module, vous incluez ce fichier d'une manière ou d'une autre.

Eh bien, je pense que vous n'avez pas trouvé de bons exemples pour ces cas de bibliothèques livrées avec le module. Vérifiez module SMTP et il est livré avec les classes nécessaires car il est en GPL. ( blob du fichier info ).

Voir aussi le module simplehtmldom qui inclut simplement le fichier mais rien d'autre.

Lorsque le module Bibliothèques est pratique, c'est que vous pouvez demander aux utilisateurs de télécharger le fichier où ils le souhaitent. Il n'est pas évident que les utilisateurs le téléchargeront dans le dossier sites/all/bibliothèques. Il peut s'agir de sites/example.com/bibliothèques ou quelque chose comme ça. Le module Bibliothèques peut vous aider à vous concentrer sur votre travail réel en effectuant les tâches de découverte d'annuaire pour vous.

Pour les modules personnalisés que je développe pour mes clients, j'inclus généralement des fichiers dans le dossier du module et j'utilise l'entrée de fichier require_once ou .info selon l'utilisation de la bibliothèque.

De plus, les problèmes de licence ne sont pas la seule raison d'utiliser le module Bibliothèques. Que faire si la bibliothèque tierce a des cycles de publication rapides et que votre module est peu développé? Si vous l'incluez dans le module, vous devrez faire une nouvelle version à chaque fois. Vous ne voudrez pas avoir une version 7.x-1.99 qui est très similaire à 7.x-1.0 je suppose.

2
AyeshK

Il semble que le problème majeur soit le chargement automatique.

Vous pouvez utiliser le module bibliothèques plus le module xautoload .

Ensuite, dans votre propre module, vous faites

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

Ceci est expliqué plus en détail ici:
xautoload.api.php
En savoir plus sur l'argument $ api.

Remarque: Vous pouvez également écrire vos propres "gestionnaires", pour implémenter des modèles old school plus exotiques au-delà de PSR-0 ou PEAR. Si vous avez besoin d'aide, postez un problème dans la file d'attente xautoload.

Remarque: il existe plusieurs façons d'enregistrer les espaces de noms de votre bibliothèque. Celui-ci est le plus simple, si vous souhaitez que les espaces de noms soient enregistrés dans chaque demande.

2
donquixote