Automatyczne ładowanie i przestrzenie nazw w wtyczkach i motywach WordPress: czy to działa?

70

Czy ktoś używał automatycznego ładowania i / lub przestrzeni nazw PHP we wtyczce lub motywie?

Myśli o ich użyciu? Jakakolwiek krzywda? Pułapki?

Uwaga: przestrzenie nazw to tylko PHP 5.3+. Załóżmy, że na to pytanie wiesz, że będziesz mieć do czynienia z serwerami, o których wiesz, że mają PHP 5.3 lub nowszy.

chrisguitarguy
źródło

Odpowiedzi:

89

Okej, miałem dwa duże projekty, w których kontrolowałem serwer na tyle, aby przestrzeń nazw i polegać na automatycznym ładowaniu.

Najpierw Automatyczne ładowanie jest niesamowite. Nie martwienie się o wymagania jest względnie dobrą rzeczą.

Oto program ładujący, z którego korzystałem przy kilku projektach. Sprawdza, czy klasa najpierw znajduje się w bieżącej przestrzeni nazw, a następnie wysyła kaucję, jeśli nie. Stamtąd jest tylko trochę manipulacji ciągiem, aby znaleźć klasę.

<?php
spl_autoload_register(__NAMESPACE__ . '\\autoload');
function autoload($cls)
{
    $cls = ltrim($cls, '\\');
    if(strpos($cls, __NAMESPACE__) !== 0)
        return;

    $cls = str_replace(__NAMESPACE__, '', $cls);

    $path = PLUGIN_PATH_PATH . 'inc' . 
        str_replace('\\', DIRECTORY_SEPARATOR, $cls) . '.php';

    require_once($path);
}

Można to łatwo dostosować do użytku bez przestrzeni nazw. Zakładając, że jednolicie prefiksujesz klasy wtyczki / motywu, możesz po prostu przetestować ten prefiks. Następnie użyj podkreślników w nazwie klasy jako symboli zastępczych dla separatorów katalogów. Jeśli korzystasz z wielu klas, prawdopodobnie będziesz chciał użyć jakiegoś autoloadera z mapami klas.

Przestrzenie nazw i haki

System przechwytujący WordPress działa przy użyciu call_user_func(i call_user_func_array), który bierze nazwy funkcji jako ciągi znaków i wywołuje je w momencie wywołania funkcji do_action(a następnie call_user_func).

Dzięki przestrzeniom nazw oznacza to, że musisz przekazać w pełni kwalifikowane nazwy funkcji, które obejmują przestrzeń nazw w zaczepach.

<?php
namespace WPSE\SomeNameSpace;

add_filter('some_filter', 'WPSE\\SomeNameSpace\\the_function');
function the_function()
{
   return 'did stuff';
}

Prawdopodobnie lepiej byłoby swobodnie korzystać ze __NAMESPACE__stałej magii, jeśli chcesz to zrobić.

<?php
namespace WPSE\SomeNameSpace;

add_filter('some_filter', __NAMESPACE__ . '\\the_function');
function the_function()
{
   return 'did stuff';
}

Jeśli zawsze umieszczasz swoje haczyki na zajęciach, jest to łatwiejsze. Standard tworzy instancję klasy i wszystkie zaczepienia w konstruktorze, które $thisdziałają poprawnie.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

class Plugin
{
    function __construct()
    {
        add_action('plugins_loaded', array($this, 'loaded'));
    }

    function loaded()
    {
        // this works!
    }
}

Jeśli używasz metod statycznych takich jak ja, musisz przekazać w pełni kwalifikowaną nazwę klasy jako pierwszy argument tablicy. To dużo pracy, więc możesz po prostu użyć magicznej __CLASS__stałej lub get_class.

<?php
namespace WPSE\SomeNameSpace;

Plugin::init();

class Plugin
{
    public static function init()
    {
        add_action('plugins_loaded', array(__CLASS__, 'loaded'));
        // OR: add_action('plugins_loaded', array(get_class(), 'loaded'));
    }

    public static function loaded()
    {
        // this works!
    }
}

Korzystanie z podstawowych klas

Rozwiązywanie nazw klas PHP jest nieco nieporadne. Jeśli zamierzasz używać podstawowych klas WP ( WP_Widgetw poniższym przykładzie), musisz podać useinstrukcje.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

Możesz też użyć w pełni kwalifikowanej nazwy klasy - po prostu poprzedzając ją odwrotnym ukośnikiem.

<?php
namespace WPSE\SomeNameSpace;

class MyWidget extends \WP_Widget
{
   // ...
}

Definiuje

To jest bardziej ogólny PHP, ale mnie ugryzł, więc oto jest.

Możesz zdefiniować rzeczy, których będziesz często używać, np. Ścieżkę do wtyczki. Użycie instrukcji defin powoduje umieszczenie rzeczy w głównej przestrzeni nazw, chyba że jawnie przepiszesz przestrzeń nazw w pierwszym argumencie definicji.

<?php
namespace WPSE\SomeNameSpace;

// root namespace
define('WPSE_63668_PATH', plugin_dir_path(__FILE__));

// in the current namespace
define(__NAMESPACE__ . '\\PATH', plugin_dir_path(__FILE__));

Możesz także użyć tego constsłowa kluczowego na poziomie głównym pliku z PHP 5.3 plus. constss są zawsze w bieżącej przestrzeni nazw, ale są mniej elastyczne niż definepołączenie.

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

// this won't work!
const MY_PATH = plugin_dir_path(__FILE__);

Dodaj dowolne inne wskazówki, które możesz mieć!

chrisguitarguy
źródło
16

Oto odpowiedź na 2017 rok.

Automatyczne ładowanie jest niesamowite. Przestrzeń nazw jest niesamowita.

Chociaż możesz to zrobić samodzielnie, w 2017 r. Najbardziej sensowne jest użycie wspaniałego i wszechobecnego Kompozytora do obsługi wymagań PHP. Kompozytor obsługuje zarówno automatyczne ładowanie PSR-0, jak i PSR-4 , ale ten pierwszy jest przestarzały od 2014 r., Więc użyj PSR-4. Zmniejsza złożoność twoich katalogów.

Wszystkie nasze wtyczki / motywy przechowujemy we własnym repozytorium Github, każdy z własnym composer.jsonplikiem i composer.lockplikiem.

Oto struktura katalogów, której używamy do naszych wtyczek. (Tak naprawdę nie mamy wtyczki o nazwie awesome-plugin, ale powinniśmy.)

plugins/awesome-plugin/bootstrap.php
plugins/awesome-plugin/composer.json
plugins/awesome-plugin/composer.lock
plugins/awesome-plugin/awesome-plugin.php
plugins/awesome-plugin/src/*

plugins/awesome-plugin/vendor/autoload.php
plugins/awesome-plugin/vendor/*

Jeśli podasz odpowiedni composer.jsonplik, Kompozytor obsługuje tutaj odstępy między nazwami i automatyczne ładowanie.

{
    "name": "awesome-company/awesome-plugin",
    "description": "Wordpress plugin for AwesomeCompany website, providing awesome functionality.",
    "type": "wordpress-plugin",
    "autoload": {
        "psr-4": {
            "AwesomeCompany\\Plugins\\AwesomePlugin\\": "src"
        }
    }
}

Po uruchomieniu composer installtworzy vendorkatalog i vendor/autoload.phpplik, który automatycznie załaduje wszystkie twoje pliki z odstępami między nazwami src/oraz wszelkie inne biblioteki, których możesz potrzebować.

Następnie u góry głównego pliku wtyczki (który jest dla nas awesome-plugin.php), po metadanych wtyczki potrzebujesz:

// Composer autoloading.
require_once __DIR__ . '/vendor/autoload.php';

...

Funkcja bonusowa

Nie jest to konieczne, ale używamy płyty głównej Bedrock Wordpress do korzystania z Composer od samego początku. Następnie możemy użyć Composer do złożenia potrzebnych wtyczek za pośrednictwem Composer, w tym własnej wtyczki, którą napisałeś powyżej. Dodatkowo, dzięki WPackagist , możesz wymagać dowolnej innej wtyczki z Wordpress.org (patrz przykład cool-themei cool-pluginponiżej).

{
  "name": "awesome-company/awesome-website",
  "type": "project",
  "license": "proprietary",
  "description": "WordPress boilerplate with modern development tools, easier configuration, and an improved folder structure",
  "config": {
    "preferred-install": "dist"
  },
  "repositories": [
    {
      "type": "composer",
      "url": "https://wpackagist.org"
    },
    { // Tells Composer to look for our proprietary Awesome Plugin here.
        "url": "https://github.com/awesome-company/awesome-plugin.git",
        "type": "git"
    }
  ],
  "require": {
    "php": ">=5.5",
    "awesome-company/awesome-plugin": "dev-production", // Our plugin!
    "wpackagist-plugin/cool-plugin": "dev-trunk",       // Someone else' plugin
    "wpackagist-theme/cool-theme": "dev-trunk",         // Someone else' theme
    "composer/installers": "~1.2.0",     // Bedrock default
    "vlucas/phpdotenv": "^2.0.1",        // Bedrock default
    "johnpbloch/wordpress": "4.7.5",     // Bedrock default
    "oscarotero/env": "^1.0",            // Bedrock default
    "roots/wp-password-bcrypt": "1.0.0"  // Bedrock default
  },
  "extra": {
    // This is the magic that drops packages with the correct TYPE in the correct location. 
    "installer-paths": {
      "web/app/mu-plugins/{$name}/": ["type:wordpress-muplugin"],
      "web/app/plugins/{$name}/": ["type:wordpress-plugin"],
      "web/app/themes/{$name}/": ["type:wordpress-theme"]
    },
    "wordpress-install-dir": "web/wp"
  },
  "scripts": {
    "test": [
      "vendor/bin/phpcs"
    ]
  }
}

Uwaga 1: Komentarze nie są legalne w JSON, ale dodałem adnotacje do powyższego pliku dla większej przejrzystości.

Uwaga 2: Dla zwięzłości wyciąłem niektóre fragmenty pliku płyty Bedrock.

Uwaga 3: Właśnie dlatego typepole w pierwszym composer.jsonpliku jest znaczące. Kompozytor automatycznie upuszcza go do web/app/pluginskatalogu.

haz
źródło
Doceń swoją odpowiedź, bardzo pomocna! Ale jestem ciekawy „bootstrap.php”, o którym mówisz. Co zawiera :)
INT
1
Posiadanie pliku bootstrap.php to stylistyczna rzecz, którą robię w większości moich projektów, zarówno w WP, jak i poza nią. Mój bootstrapper zwykle po prostu sprawdza ustawienia i zmienne środowiskowe; jego głównym celem jest upewnienie się, że moja wtyczka zawsze ma to, co musi uruchomić, niezależnie od tego, czy została uruchomiona z WP, czy jako samodzielna aplikacja PHP.
haz
4

Korzystam z automatycznego ładowania (ponieważ moja wtyczka ma mnóstwo klas - częściowo dlatego, że zawiera Twig), nigdy nie zwróciłem uwagi na problem (wtyczka zainstalowana> 20 000 razy).

Jeśli masz pewność, że nigdy nie będziesz musiał korzystać z instalacji php, która nie obsługuje przestrzeni nazw, to wszystko jest w porządku (~ 70% obecnych blogów wordpress nie obsługuje przestrzeni nazw). Kilka rzeczy do zapamiętania:

Wydaje mi się, że pamiętam, że w nazwach nie rozróżniana jest wielkość liter w zwykłym php, ale w przypadku korzystania z Fastcgi php na iis - powoduje to pewne bóle głowy, jeśli testujesz na systemie Linux i nie wykrywasz nieuczciwych małych liter.

Nawet jeśli masz pewność, że kod, który obecnie opracowujesz, będzie używany tylko w wersji> 5.3.0, nie będziesz mógł ponownie użyć żadnego kodu w projektach, które nie mają tego luksusu - to główny powód, dla którego nie używane przestrzenie nazw w projektach wewnętrznych. Przekonałem się, że przestrzenie nazw naprawdę nie dodają tak wiele w porównaniu z możliwym bólem głowy związanym z koniecznością usunięcia zależności od nich.

Daniel Chatfield
źródło