Najlepsza praktyka w przypadku bibliotek opartych na klasach PHP innych firm

17

Obecnie pracuję nad modułem, który wymaga biblioteki PHP innej firmy, która jest w zasadzie pojedynczą klasą PHP. Zwykle umieszczałbym go w podkatalogu include / i dodaje

files[] = includes/Foo.php

do mojego pliku .info i pozwól, aby auto-moduł ładujący klasy Drupal 7 zrobił to, co robię $foo = new Foo().

Mam jednak pozwolenie na upublicznienie tego modułu i wolę nie dołączać biblioteki z modułem. Zdaję sobie sprawę z komplikacji związanych z licencjonowaniem, ale ze względu na to pytanie chciałbym je zignorować.

Podobne pytanie brzmi: jak dołączyć bibliotekę PHP? , ale tak naprawdę nie sądzę, że to odpowiada na mój dylemat.

To odpowiedzi na to pytanie w zasadzie powiedzieć użyć API bibliotek , ale każdy pojedynczy moduł, który znalazłem, że używa tego właśnie robi libraries_get_path(), aby uzyskać basePath (i zawiera ścieżkę awaryjnej, gdy nie jest dostępny), a następnie robi requirelub includeniektóre sprawdzanie błędów (lub nie). Wszyscy robią coś takiego:

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

W tym przypadku interfejs API bibliotek naprawdę nic nie robi. Nie widzę korzyści z używania tego, w porównaniu ze starą metodą proszenia użytkowników o pobranie kopii i umieszczenie jej w samym folderze modułu. A, jest jeszcze kwestia, że deweloper moduł nadal musi ręcznie zrobić obciążenie z include/require . Na przykład moduł Facebooka po prostu ładuje bibliotekę do hook_inita moduł HTML Purifier ma wewnętrzną funkcję sprawdzania i ładowania za każdym razem, gdy biblioteka jest potrzebna.

Może to być powszechna praktyka, ale nie wydaje się najlepsza praktyką.

Czy mój moduł powinien przejąć inicjatywę i zadeklarować hook_libraries_info, żebym mógł z niej skorzystać libraries_load('foo')? To też wydaje się dziwne.

mpdonadio
źródło
Innym problemem jest to, czy licencja biblioteki innej firmy jest zgodna z licencją drupala. Jeśli tak, i to nie jest ogromne, po prostu to dołączę. Jeśli tak się nie stanie, nie możesz / nie powinieneś dołączyć go na początek, więc podejście do biblioteki wydaje się lepsze i niech twoi użytkownicy końcowi sami ją pobiorą.
Jimajamma
Jednym z celów if (libraries_load($name)) {..}jest uniknięcie WSOD w przypadku braku biblioteki.
donquixote

Odpowiedzi:

7

Oddział 2.x modułu API bibliotek umożliwia programistom zdefiniowanie, za pomocą hook_libraries_info () lub pliku .info dla biblioteki, następujących informacji (patrz libraries.api ):

  • Zależności biblioteki
  • Wersja, z którą biblioteka jest kompatybilna, dla każdej zależności
  • Lista plików, które należy załadować (pliki CSS, JavaScript lub PHP)

Lista plików, które należy załadować, służy do ładowania tych plików, gdy wymagana jest biblioteka. Oznacza to, że moduł nie musi ładować plików CSS i JavaScript za pomocą drupal_add_css(), lubdrupal_add_js() , jak to już zrobiono z modułu API bibliotek. Ładowanie zależności jest zadaniem wykonywanym z modułu API bibliotek, bez wykonywania modułu wywołującego.

Wszystko, co robi moduł, to użycie następującego kodu do załadowania biblioteki. (Zobacz Korzystanie z bibliotek API 2.x (jako programista modułów) .)

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

Jeśli chcesz tylko wykryć obecność biblioteki, moduł powinien użyć kodu podobnego do następującego.

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

Pomiędzy właściwościami hook_libraries_info()można zwrócić, jest też to 'download url', co nie jest faktycznie używane, nawet w gałęzi 3.x. Prawdopodobnie będzie on używany w przyszłości lub moduły innych firm mogłyby podłączyć się do modułu API bibliotek i pobierać biblioteki, które są wymagane, ale których brakuje.

kiamlaluno
źródło
Czy możesz wskazać popularne moduły, które robią to z bibliotekami PHP? Częścią motywacji tego pytania było to, że mogłem postępować zgodnie z najlepszymi praktykami dotyczącymi modułu publicznego, więc zacząłem szukać tych, które używają interfejsu API bibliotek. Nie znalazłem żadnego, który zaimplementowałby hook_libraries_info () i używał libraries_load () wewnętrznie.
mpdonadio
Moduł zencorderapi (część modułu wideo) używa hook_libraries_info ()
AyeshK
@MPD Istnieje częściowa lista przykładów przykładów modułów korzystających z API bibliotek .
kiamlaluno
@kiamlaluno, dzięki, to było pierwsze miejsce, w którym szukałem. Spośród sześciu tylko dwie z tych bibliotek implementują hook_libraries_info. Nie sądzę, aby twoja odpowiedź była błędna, ale nie jestem przekonany, że jest to obecnie szeroko rozpowszechniona najlepsza praktyka. Jedna z bibliotek miała interesującą technikę, którą zamierzam przetestować i być może opublikuję później.
mpdonadio
@MPD Wersja 7.x-2.0 została wydana 29 lipca; jest prawdopodobne, że większość modułów nadal stosuje podejście 7.x-1.
kiamlaluno
5

Po przyzwoitej ilości kopania wciąż nie jestem przekonany co do najlepszych praktyk. Zainspirowany modułem PHPMailer , oferuję to dla klasowych bibliotek 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,
    );
  }
}

Używa hook_registry_files_alter do sprawdzenia istnienia klasy, a jeśli nie zostanie znaleziony, dodaj plik do rejestru klas (odpowiednik files[] = ...linii w pliku .info modułów). Następnie klasy zdefiniowane w foo.php będą dostępne z autoloaderem, więc nie ma potrzeby jawnego ładowania pliku przed użyciem klasy.

Spowoduje to również miękkie wymaganie dotyczące interfejsu API bibliotek i będzie go używać, jeśli jest dostępne, w przeciwnym razie należy zastosować rozsądną wartość domyślną.

Dobrym pomysłem jest również dodanie niektórych sprawdzeń za pomocą hook_requirements, aby upewnić się, że plik istnieje, że autoloader znajdzie klasę, sprawdzenie wersji itp.

Warto również zauważyć, że w kolejce problemów dyskutowane jest podejście do automatycznego ładowania interfejsu API bibliotek .

mpdonadio
źródło
Nie zapomnij wyczyścić pamięci podręcznej po wdrożeniu hook_registry_files_alter, w przeciwnym razie nie
zadziała
2

Krótko mówiąc: jeśli planujesz udostępnić moduł publicznie, a biblioteka (strony trzeciej) nie jest objęta licencją GPL, będziesz musiał użyć bibliotek jako zależności lub poprosić użytkowników o ręczne pobranie tych plików (ale nie będziesz w stanie załaduj go automatycznie z pliku .info)

W trochę dłużej:

Powodem, dla którego potrzebujemy modułu Biblioteki, jest w zasadzie licencjonowanie. Bez względu na to, czy używasz tego modułu, czy nie, w jakiś sposób dołączasz ten plik.

Cóż, myślę, że nie znalazłeś dobrych przykładów dla takich bibliotek dostarczanych z modułem. Sprawdź moduł SMTP i zawiera niezbędne klasy, takie jak w GPL. ( Kropla pliku .info ).

Zobacz także moduł simplehtmldom , który po prostu zawiera plik, ale nic więcej.

Tam, gdzie przydatny jest moduł Biblioteki, możesz poprosić użytkowników o przesłanie pliku w dowolnym miejscu. Nie jest oczywiste, że użytkownicy prześlą go do folderu witryn / wszystkich / bibliotek. Mogą to być strony / example.com / biblioteki lub coś w tym rodzaju. Moduł Biblioteki może pomóc Ci skoncentrować się na faktycznej pracy, wykonując czynności związane z odnajdywaniem katalogów.

W przypadku niestandardowych modułów, które opracowuję dla moich klientów, zwykle umieszczam pliki w folderze modułów i używam wpisu wymaganego pliku lub pliku .info w zależności od wykorzystania biblioteki.

Ponadto problemy z licencjonowaniem nie są jedynym powodem korzystania z modułu Biblioteki. Co się stanie, jeśli biblioteka strony trzeciej ma szybkie cykle wydawania, a moduł jest minimalnie rozwijany? Jeśli umieścisz go w module, będziesz musiał za każdym razem tworzyć nowe wydanie. Nie będziesz chciał mieć wersji 7.x-1.99, która wydaje się podobna do wersji 7.x-1.0.

AyeshK
źródło
Dzięki za poświęcenie czasu na odpowiedź. Zredagowałem trochę moje pytanie, aby wyjaśnić. Pytanie tak naprawdę nie dotyczy komplikacji związanych z licencjonowaniem i harmonogramem wydań oraz tego, w jaki sposób API bibliotek pomaga w tym. Bardziej interesują mnie najlepsze praktyki dotyczące ładowania bibliotek stron trzecich.
mpdonadio
2

Wydaje się, że głównym problemem jest automatyczne ładowanie.

Możesz użyć modułu bibliotek plus modułu xautoload .

Następnie we własnym module

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

Wyjaśniono to bardziej szczegółowo tutaj:
xautoload.api.php
Więcej informacji na temat argumentu $ api.

Uwaga: Możesz także pisać własne „moduły obsługi”, aby wdrażać bardziej egzotyczne wzorce oldschoolowe poza PSR-0 lub PEAR. Jeśli potrzebujesz pomocy, opublikuj problem w kolejce xautoload.

Uwaga: Istnieje więcej niż jeden sposób zarejestrowania przestrzeni nazw biblioteki. Ten jest najłatwiejszy, jeśli chcesz, aby przestrzenie nazw były rejestrowane w każdym żądaniu.

Don Kichot
źródło
1
Powinienem dodać, że to nie pomaga w ładowaniu plików proceduralnych. Należy to zrobić ręcznie, gdy tylko biblioteka będzie potrzebna w żądaniu.
donquixote
Ponadto niektóre biblioteki mają własne rozwiązania ładowania klas. Mimo to wygodniej jest użyć modułu ładującego, który jest już dostępny w Drupal / contrib.
donquixote