Autoloading & Namespaces in WordPress Plugins & Themes: Kann es funktionieren?

70

Hat jemand Autoloading- und / oder PHP-Namespaces innerhalb eines Plugins oder Themas verwendet?

Überlegungen zur Verwendung? Irgendwelcher Schaden? Fallstricke?

Hinweis: Namespaces sind nur in PHP 5.3+ verfügbar. Angenommen, Sie wissen, dass Sie es mit Servern zu tun haben, von denen Sie wissen, dass sie PHP 5.3 oder höher haben.

chrisguitarguy
quelle

Antworten:

88

Okay, ich hatte zwei große Projekte, bei denen ich die Kontrolle über den Server hatte und mich auf das automatische Laden verlassen konnte.

Zuerst. Autoloading ist großartig. Sich keine Sorgen zu machen, ist eine relativ gute Sache.

Hier ist ein Loader, den ich für einige Projekte verwendet habe. Überprüft, ob sich die Klasse zuerst im aktuellen Namespace befindet. Andernfalls wird der Fehler behoben. Von dort aus ist es nur eine Art Zeichenkettenmanipulation, um die Klasse zu finden.

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

Man könnte dies leicht für die Verwendung ohne Namespaces anpassen. Unter der Annahme, dass Sie die Klassen Ihres Plugins / Themas einheitlich voranstellen, können Sie nur auf dieses Präfix testen. Verwenden Sie dann Unterstriche im Klassennamen als Platzhalter für Verzeichnisseparatoren. Wenn Sie viele Klassen verwenden, möchten Sie wahrscheinlich eine Art Classmap-Autoloader verwenden.

Namespaces und Hooks

Das Hooks-System von WordPress verwendet call_user_func(und call_user_func_array), wobei Funktionsnamen als Zeichenfolgen verwendet werden und diese aufgerufen werden, wenn der Funktionsaufruf do_action(und anschließend call_user_func) erfolgt.

Bei Namespaces müssen Sie also vollständig qualifizierte Funktionsnamen, die den Namespace enthalten, in Hooks übergeben.

<?php
namespace WPSE\SomeNameSpace;

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

Es wäre wahrscheinlich besser, die __NAMESPACE__magische Konstante liberal zu nutzen, wenn Sie dies tun möchten.

<?php
namespace WPSE\SomeNameSpace;

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

Wenn Sie immer Ihre Haken in Klassen setzen, ist es einfacher. Die standardmäßige create-Instanz einer Klasse und alle Hooks im Konstruktor mit $thisfunktionieren einwandfrei.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

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

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

Wenn Sie statische Methoden wie ich verwenden möchten, müssen Sie den vollständig qualifizierten Klassennamen als erstes Argument des Arrays übergeben. Das ist viel Arbeit, man kann also einfach die magische __CLASS__Konstante oder verwenden 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!
    }
}

Core-Klassen verwenden

PHPs Klassennamenauflösung ist ein bisschen wackelig. Wenn Sie Kern-WP-Klassen verwenden möchten ( WP_Widgetim folgenden Beispiel), müssen Sie useAnweisungen bereitstellen .

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

Oder Sie können den vollständig qualifizierten Klassennamen verwenden, indem Sie ihm einen Backslash voranstellen.

<?php
namespace WPSE\SomeNameSpace;

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

Definiert

Dies ist allgemeineres PHP, aber es hat mich gebissen, also hier ist es.

Vielleicht möchten Sie Dinge definieren, die Sie häufig verwenden, wie den Pfad zu Ihrem Plugin. Mit der define-Anweisung werden Dinge in den Root-Namespace eingefügt, es sei denn, Sie übergeben den Namespace explizit an das erste Argument von define.

<?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__));

Sie können das constSchlüsselwort on auch im Stammverzeichnis einer Datei mit PHP 5.3 plus verwenden. constss befinden sich immer im aktuellen Namespace, sind jedoch weniger flexibel als ein defineAufruf.

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

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

Sie können gerne weitere Tipps hinzufügen!

chrisguitarguy
quelle
16

Hier ist eine Antwort für 2017.

Autoloading ist großartig. Namespacing ist fantastisch.

Obwohl Sie es selbst rollen können, ist es im Jahr 2017 am sinnvollsten, den großartigen und allgegenwärtigen Composer zu verwenden, um Ihre PHP-Anforderungen zu erfüllen. Composer unterstützt sowohl das automatische Laden von PSR-0 als auch von PSR-4. Ersteres ist jedoch seit 2014 veraltet. Verwenden Sie daher PSR-4. Es reduziert die Komplexität Ihrer Verzeichnisse.

Wir composer.jsonspeichern jedes unserer Plugins / Themes in einem eigenen Github-Repository, jedes mit einer eigenen Datei und einer eigenen composer.lockDatei.

Hier ist die Verzeichnisstruktur, die wir für unsere Plugins verwenden. (Wir haben nicht wirklich ein Plugin namens awesome-plugin, aber wir sollten.)

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/*

Wenn Sie eine entsprechende composer.jsonDatei bereitstellen , übernimmt Composer hier den Namensabstand und das automatische Laden.

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

Bei der Ausführung composer installwerden das vendorVerzeichnis und die vendor/autoload.phpDatei erstellt, in die automatisch alle Dateien mit Namensabständen geladen werden src/, sowie alle anderen Bibliotheken, die Sie möglicherweise benötigen.

Dann awesome-plugin.phpbrauchen Sie oben in Ihrer Haupt-Plugin-Datei (was für uns ist ) nach Ihren Plugin-Metadaten einfach:

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

...

Bonus-Funktion

Keine Notwendigkeit, aber wir verwenden die Bedrock Wordpress-Kesselplatte, um Composer von Anfang an zu verwenden. Dann können wir Composer verwenden, um die benötigten Plugins über Composer zusammenzustellen, einschließlich Ihres eigenen Plugins, das Sie oben geschrieben haben. Zusätzlich können Sie dank WPackagist jedes andere Plugin von Wordpress.org benötigen (siehe Beispiel von cool-themeund cool-pluginunten).

{
  "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"
    ]
  }
}

Hinweis 1: Kommentare sind in JSON nicht zulässig. Die obige Datei wurde jedoch aus Gründen der Übersichtlichkeit mit Anmerkungen versehen.

Anmerkung 2: Der Kürze halber habe ich einige Teile der Bedrock-Datei auf dem Kesselschild herausgeschnitten.

Hinweis 3: Aus diesem Grund ist das typeFeld in der ersten composer.jsonDatei von Bedeutung. Composer legt es automatisch im web/app/pluginsVerzeichnis ab.

haz
quelle
Schätzen Sie Ihre Antwort, sehr hilfreich! Aber ich bin neugierig auf die "bootstrap.php", auf die Sie sich beziehen. Was enthält es? :)
INT
1
Eine bootstrap.php-Datei zu haben, ist eine stilistische Sache, die ich in den meisten meiner Projekte mache, in oder außerhalb von WP. Mein Bootstrapper überprüft normalerweise nur Einstellungen und Umgebungsvariablen. Der Hauptzweck ist es, sicherzustellen, dass mein Plugin immer das hat, was es ausführen muss, unabhängig davon, ob es in WP oder als eigenständige PHP-App ausgeführt wurde.
Haz
4

Ich verwende Autoloading (da mein Plugin viele Klassen hat - auch weil es Twig enthält), habe nie auf ein Problem aufmerksam gemacht (Plugin> 20.000-mal installiert).

Wenn Sie sicher sind, dass Sie niemals eine PHP-Installation benötigen werden, die keine Namespaces unterstützt, sind Sie wieder in Ordnung (~ 70% der aktuellen WordPress-Blogs unterstützen keine Namespaces). Ein paar Dinge zu beachten:

Ich scheine mich daran zu erinnern, dass Namespaces in regulärem PHP nicht zwischen Groß- und Kleinschreibung unterscheiden, aber wenn Sie FastCGI-PHP unter iis verwenden - dies verursacht Kopfschmerzen, wenn Sie unter Linux testen und keinen falschen Kleinbuchstaben erkennen.

Auch wenn Sie sicher sind, dass der Code, den Sie gerade entwickeln, nur für> 5.3.0 verwendet wird, können Sie keinen Code für Projekte wiederverwenden, die nicht über diesen Luxus verfügen - das ist der Hauptgrund, warum ich dies nicht getan habe verwendete Namespaces für interne Projekte. Ich habe festgestellt, dass Namespaces im Vergleich zu den möglichen Kopfschmerzen, die durch das Entfernen der Abhängigkeit von ihnen entstehen können, nicht so viel hinzufügen .

Daniel Chatfield
quelle