Best practice per libreria basata su classi PHP di terze parti


17

Attualmente sto lavorando su un modulo che richiede una libreria PHP di terze parti, che è essenzialmente una singola classe PHP. Normalmente, lo inserisco in una sottodirectory include / add e aggiungo

files[] = includes/Foo.php

al mio file .info e lascia che il caricatore automatico di classe Drupal 7 faccia la sua cosa quando lo faccio $foo = new Foo().

Ho il permesso, tuttavia, di rilasciare questo modulo al pubblico e preferirei non includere la libreria con il modulo. Sono ben consapevole delle complicazioni relative alle licenze, ma per il bene di questa domanda, vorrei ignorarla.

C'è una domanda simile, come posso includere una libreria PHP? , ma non credo che questo risponda al mio dilema.

Questa risposta a questa domanda dice essenzialmente di usare l' API Librerie , ma ogni singolo modulo che ho trovato che usa questo fa semplicemente un libraries_get_path()per ottenere il basepath (e include il percorso di fallback quando non è disponibile) e quindi fa un requireo includecon alcuni controllo errori (o no). Tutti fanno qualcosa come:

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 questo caso, l'API Librerie non sta realmente facendo nulla. Non vedo il vantaggio di usare questo, rispetto al vecchio metodo di chiedere agli utenti di scaricare una copia e metterlo nella cartella del modulo stesso. E c'è ancora il problema che lo sviluppatore del modulo deve ancora eseguire manualmente il carico con include/ require. Ad esempio, il modulo Facebook carica la libreria in hook_initae il modulo Purificatore HTML ha una funzione interna per controllare e caricare ogni volta che è necessaria la libreria.

Questa può essere una pratica diffusa , ma non sembra una buona pratica.

Il mio modulo dovrebbe prendere l'iniziativa e dichiarare un hook_libraries_infocosì che io possa usare libraries_load('foo')? Anche questo sembra strano.


Un altro problema è se la licenza della libreria di terze parti corrisponde o meno a quella di Drupal. Se lo fa, e non è enorme, lo includerei semplicemente. In caso contrario, non puoi / non dovresti includerlo per cominciare, quindi l'approccio della libreria sembra migliore e fai in modo che i tuoi eventuali utenti finali lo scarichino da soli.
Jimajamma,

Uno scopo if (libraries_load($name)) {..}è quello di evitare un WSOD nel caso in cui la libreria non sia presente.
donquixote,

Risposte:


7

Il ramo 2.x del modulo API Libraries consente agli sviluppatori di definire, tramite hook_libraries_info () , o un file .info per la libreria, le seguenti informazioni (consultare libraries.api ):

  • Le dipendenze della libreria
  • La versione con cui la libreria è compatibile, per ciascuna delle dipendenze
  • L'elenco dei file che devono essere caricati (file CSS, JavaScript o PHP)

L'elenco dei file che devono essere caricati viene utilizzato per caricare tali file, quando è richiesta la libreria. Ciò significa che il tuo modulo non ha bisogno di caricare file CSS e JavaScript con drupal_add_css(), o drupal_add_js(), come già fatto dal modulo API Librerie. Il caricamento delle dipendenze è un'attività eseguita dal modulo API Librerie, senza che il modulo chiamante faccia nulla.

Tutto il modulo fa è usare il seguente codice, per caricare una libreria. (Vedi Utilizzo delle Librerie API 2.x (come sviluppatore di moduli) .)

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

Se hai solo bisogno di rilevare se è presente una libreria, il modulo dovrebbe usare un codice simile al seguente.

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

Tra le proprietà hook_libraries_info()possono tornare, c'è anche 'download url', che non viene effettivamente utilizzato, nemmeno nel ramo 3.x. Probabilmente verrà utilizzato in futuro, oppure moduli di terze parti potrebbero agganciarsi al modulo API Librerie e scaricare le librerie richieste, ma mancanti.


Puoi indicare qualche modulo popolare che lo fa con le librerie PHP? Parte della motivazione per la domanda è stata quella di poter seguire le migliori pratiche per un modulo pubblico, così ho iniziato a cercare quelli che utilizzano l'API delle librerie. Non ho trovato nessuno che implementasse hook_libraries_info () e utilizzato libraries_load () internamente.
mpdonadio

Il modulo zencorderapi (parte del modulo Video) utilizza hook_libraries_info ()
AyeshK,


@kiamlaluno, grazie, è stato il primo posto in cui ho cercato. Dei sei, solo due di quelle librerie implementano hook_libraries_info. Non penso che la tua risposta sia sbagliata, ma non sono convinto che questa sia una best practice diffusa in questo momento. Una delle biblioteche aveva una tecnica interessante che ho intenzione di testare e possibilmente postare in seguito.
mpdonadio

@MPD versione 7.x-2.0 è stata rilasciata il 29 luglio; è probabile che la maggior parte dei moduli stia ancora utilizzando l'approccio 7.x-1.
kiamlaluno

5

Dopo una discreta quantità di scavi, non sono ancora convinto di quale sia la migliore pratica. Ispirato dal modulo PHPMailer , lo sto offrendo per le librerie PHP basate su classi:

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

Questo utilizza hook_registry_files_alter per verificare l'esistenza di una classe e, se non trovato, aggiunge un file al registro di classe (l'equivalente di una files[] = ...riga in un file .info dei moduli). Quindi, le classi definite in foo.php saranno disponibili con il caricatore automatico, quindi non è necessario caricare esplicitamente il file prima di utilizzare la classe.

Ciò crea anche un requisito software sull'API Librerie e lo utilizzerà se disponibile, altrimenti utilizza un valore predefinito ragionevole.

L'aggiunta di alcuni controlli tramite hook_requirements per assicurarsi che il file esista, che il caricatore automatico trovi la classe, il controllo della versione, ecc., È anche una buona idea.

Vale anche la pena notare che un approccio di caricamento automatico per l'API Libraries è in discussione nella coda dei problemi.


Non dimenticare di cancellare la cache dopo aver implementato hook_registry_files_alter, altrimenti non si innescherà;)
saadlulu

2

In breve: se stai pianificando di rilasciare il modulo al pubblico e la libreria (di terze parti) non è GPL'd, dovrai utilizzare le Librerie come dipendenza o chiedere agli utenti di scaricare questi file manualmente (ma non sarai in grado di caricarlo automaticamente dal file .info)

Tra poco più:

Il motivo per cui abbiamo bisogno del modulo Librerie è fondamentalmente la licenza. Indipendentemente dal fatto che tu usi quel modulo o meno, stai includendo quel file in qualche modo.

Beh, penso che non hai trovato buoni esempi per tali casi di librerie spedite con il modulo. Dai un'occhiata al modulo SMTP e viene fornito con le classi necessarie in quanto è in GPL. ( BLOB di file .info ).

Vedi anche il modulo simplehtmldom che include solo il file ma nient'altro.

Quando il modulo Librerie è utile, puoi chiedere agli utenti di caricare il file dove vogliono. Non è ovvio che gli utenti lo caricheranno nella cartella siti / all / libraries. Potrebbe essere siti / example.com / librerie o qualcosa del genere. Il modulo Librerie può aiutarti a concentrarti sul tuo lavoro effettivo facendo le cose di individuazione delle directory per te.

Per i moduli personalizzati che sviluppo per i miei clienti, di solito includo i file nella cartella del modulo e utilizzo la voce di file request_once o .info a seconda dell'uso della libreria.

Inoltre, i problemi di licenza non sono l'unico motivo per utilizzare il modulo Librerie. Cosa succede se la libreria di terze parti ha cicli di rilascio rapido e il modulo è minimamente sviluppato? Se lo includi nel modulo, dovrai effettuare una nuova versione ogni volta. Non vorrai avere una versione 7.x-1.99 che è molto simile a 7.x-1.0 credo.


Grazie per il tempo dedicato a rispondere. Ho modificato un po 'la mia domanda per chiarire. La domanda non riguarda in realtà le complicazioni delle licenze e dei programmi di rilascio e come l'API Librerie aiuta in questo. Sono più curioso delle migliori pratiche sul caricamento effettivo di librerie di terze parti.
mpdonadio

2

Sembra che il problema principale sia il caricamento automatico.

È possibile utilizzare il modulo librerie più il modulo xautoload .

Quindi, nel tuo modulo, lo fai

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

Questo è spiegato in modo più dettagliato qui:
xautoload.api.php
Maggiori informazioni sull'argomento $ api.

Nota: è anche possibile scrivere i propri "gestori", per implementare schemi di vecchia scuola più esotici oltre PSR-0 o PEAR. Se hai bisogno di aiuto, pubblica un problema nella coda xautoload.

Nota: esiste più di un modo per registrare gli spazi dei nomi della libreria. Questo è il più semplice, se si desidera che gli spazi dei nomi siano registrati in ogni richiesta.


1
Dovrei aggiungere, questo non aiuta con il caricamento di file procedurali. Questo deve essere fatto manualmente, non appena è necessaria la libreria in una richiesta.
donquixote,

Inoltre, alcune librerie hanno le proprie soluzioni di caricamento di classe. Tuttavia, può essere più comodo utilizzare un caricatore già disponibile in Drupal / contrib.
donquixote,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.