it-swarm.com.de

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

Hat jemand Autoloading und/oder PHP Namespaces in einem Plugin oder Theme verwendet?

Überlegungen zur Verwendung? Irgendwelcher Schaden? Fallstricke?

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

69
chrisguitarguy

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, 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 Hook-System von WordPress verwendet call_user_func (und call_user_func_array), wobei Funktionsnamen als Zeichenfolgen verwendet werden und diese beim Aufruf der Funktion do_action (und anschließend call_user_func) aufgerufen werden.

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 magische Konstante __NAMESPACE__ 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 Erstellungsinstanz einer Klasse und alle Hooks im Konstruktor mit $this funktionieren 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 eine Menge Arbeit, also können Sie einfach die magische __CLASS__-Konstante oder den get_class verwenden.

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

Kernklassen verwenden

PHPs Klassennamenauflösung ist ein bisschen wackelig. Wenn Sie WP Kernklassen verwenden möchten (WP_Widget im folgenden Beispiel), müssen Sie use-Anweisungen 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 Anweisung define werden Dinge in den Root-Namespace eingefügt, sofern Sie den Namespace nicht explizit an das erste Argument von define übergeben.

<?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 Schlüsselwort const auch im Stammverzeichnis einer Datei mit PHP 5.3 plus verwenden. constss befinden sich immer im aktuellen Namespace, sind jedoch weniger flexibel als ein define-Aufruf.

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

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

Bitte zögern Sie nicht, andere Tipps hinzuzufügen, die Sie haben könnten!

87
chrisguitarguy

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 PSR-0 als auch PSR-4 Autoloading. Ersteres ist jedoch seit 2014 veraltet. Verwenden Sie daher PSR-4. Es reduziert die Komplexität Ihrer Verzeichnisse.

Wir speichern jedes unserer Plugins/Themes in einem eigenen Github-Repository, jedes mit einer eigenen composer.json-Datei und einer eigenen composer.lock-Datei.

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.json-Datei angeben, ü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"
        }
    }
}

Wenn Sie composer install ausführen, werden das Verzeichnis vendor und die Datei vendor/autoload.php erstellt, in die alle Dateien mit Namensabständen in src/ und alle anderen Bibliotheken, die Sie möglicherweise benötigen, automatisch geladen werden.

Dann benötigen Sie oben in Ihrer Haupt-Plugin-Datei (für uns awesome-plugin.php) 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 für cool-theme und cool-plugin unten).

{
  "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 Boilerplate herausgeschnitten.

Hinweis 3: Aus diesem Grund ist das Feld type in der ersten composer.json-Datei von Bedeutung. Composer legt es automatisch im Verzeichnis web/app/plugins ab.

13
haz

Ich verwende Autoloading (da mein Plugin viele Klassen hat - auch weil es Twig enthält), habe nie ein Problem bemerkt (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 zu erinnern, dass Namespaces in regulärem PHP nicht zwischen Groß- und Kleinschreibung unterscheiden, aber wenn Sie FastCgi-PHP unter iis verwenden - dies bereitet 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 diesen Luxus nicht bieten - 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 Problemen, die damit verbunden sind, die Abhängigkeit von ihnen zu beseitigen, nicht wirklich das viel hinzufügen.

4