Autoloading & Namespaces dans WordPress Plugins & Themes: peut-il fonctionner?

70

Est-ce que quelqu'un a déjà utilisé le chargement automatique et / ou les espaces de noms PHP dans un plugin ou un thème?

Pensées sur leur utilisation? Un mal? Les pièges?

Remarque: les espaces de noms sont uniquement PHP 5.3+. Supposons, pour cette question, que vous savez que vous aurez affaire à des serveurs dont vous savez qu'ils utilisent PHP 5.3 ou une version ultérieure.

chrisguitarguy
la source

Réponses:

88

D'accord, j'ai eu deux grands projets dans lesquels je maîtrisais suffisamment le serveur pour créer un espace de noms et qui reposait sur le chargement automatique.

Le premier debout. Le chargement automatique est génial. Ne pas s'inquiéter des exigences est une bonne chose

Voici un chargeur que j'ai utilisé sur quelques projets. Vérifie que la classe est d'abord dans l'espace de noms actuel, puis échoue si ce n'est pas le cas. A partir de là, il suffit d'une manipulation de chaîne pour trouver la classe.

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

On pourrait facilement adapter cela pour une utilisation sans espaces de noms. En supposant que vous préfixez les classes de votre plugin / theme de manière uniforme, vous pouvez simplement tester ce préfixe. Ensuite, utilisez des traits de soulignement dans le nom de la classe comme espaces réservés pour les séparateurs de répertoires. Si vous utilisez beaucoup de classes, vous voudrez probablement utiliser une sorte d'autoloader de classmap.

Espaces de noms et crochets

Le système d'accroche de WordPress fonctionne en utilisant call_user_func(et call_user_func_array), qui prend les noms de fonction sous forme de chaînes et les appelle à l'appel de la fonction do_action(et ultérieurement call_user_func).

Avec les espaces de noms, cela signifie que vous devrez transmettre des noms de fonction complets comprenant l’espace de noms à des points d'ancrage.

<?php
namespace WPSE\SomeNameSpace;

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

Il serait probablement préférable de faire un usage libéral de la __NAMESPACE__constante magique si vous voulez le faire.

<?php
namespace WPSE\SomeNameSpace;

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

Si vous mettez toujours vos crochets en classe, c'est plus facile. L'instance standard de création d'une classe et tous les points d'ancrage du constructeur $thisfonctionnent correctement.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

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

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

Si vous utilisez des méthodes statiques comme je le souhaite, vous devrez passer le nom de classe complet comme premier argument du tableau. C'est beaucoup de travail, vous pouvez donc simplement utiliser la __CLASS__constante magique ou 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!
    }
}

Utiliser les classes de base

La résolution du nom de classe de PHP est un peu bizarre. Si vous envisagez d'utiliser des classes WP principales ( WP_Widgetdans l'exemple ci-dessous), vous devez fournir des useinstructions.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

Ou vous pouvez utiliser le nom de classe complet - en le préfixant simplement par une barre oblique inverse.

<?php
namespace WPSE\SomeNameSpace;

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

Définit

C'est plus général en PHP, mais ça m’a mordu, alors le voici.

Vous voudrez peut-être définir des éléments que vous utiliserez souvent, tels que le chemin d'accès à votre plugin. L'utilisation de l'instruction define place les éléments dans l'espace de noms racine, à moins que vous ne les transmettiez explicitement au premier argument de 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__));

Vous pouvez également utiliser le constmot - clé à la racine d'un fichier avec PHP 5.3 plus. constss sont toujours dans l’espace de noms actuel, mais sont moins flexibles qu’un defineappel.

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

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

S'il vous plaît n'hésitez pas à ajouter d'autres conseils que vous pourriez avoir!

chrisguitarguy
la source
16

Voici une réponse pour 2017.

Le chargement automatique est génial. Namespacing est génial.

Bien que vous puissiez le faire vous-même, en 2017, il est tout à fait judicieux d'utiliser le magnifique et omniprésent Composer pour répondre à vos besoins en PHP. Composer prend en charge le chargement automatique PSR-0 et PSR-4 , mais ce dernier est obsolète depuis 2014, utilisez donc PSR-4. Cela réduit la complexité de vos répertoires.

Nous conservons chacun de nos plugins / thèmes dans son propre référentiel Github, chacun avec son propre composer.jsonfichier et son propre composer.lockfichier.

Voici la structure de répertoires que nous utilisons pour nos plugins. (Nous n'avons pas vraiment de plugin appelé awesome-plugin, mais nous devrions le faire.)

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

Si vous fournissez un composer.jsonfichier approprié , Composer gère ici l’espacement des noms et le chargement automatique.

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

Lorsque vous l'exécutez composer install, il crée le vendorrépertoire et le vendor/autoload.phpfichier, qui chargeront automatiquement tous vos fichiers nom-espacés src/, ainsi que toutes les autres bibliothèques dont vous pourriez avoir besoin.

Ensuite, en haut de votre fichier de plugin principal (ce qui est pour nous awesome-plugin.php), après les métadonnées de votre plugin, vous avez simplement besoin de:

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

...

Bonus

Ce n'est pas une nécessité, mais nous utilisons le passe- partout Bedrock Wordpress pour utiliser Composer dès le début. Ensuite, nous pouvons utiliser Composer pour assembler les plug-ins dont nous avons besoin via Composer, y compris votre propre plug-in que vous avez écrit ci-dessus. De plus, grâce à WPackagist , vous pouvez exiger n'importe quel autre plugin de Wordpress.org (voir l'exemple de cool-themeet cool-pluginci - dessous).

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

Remarque 1: Les commentaires en JSON ne sont pas légaux, mais j'ai annoté le fichier ci-dessus pour plus de clarté.

Note 2: J'ai coupé quelques morceaux du fichier de référence du substratum rocheux pour plus de concision.

Note 3: C'est pourquoi le typechamp du premier composer.jsonfichier est significatif. Le compositeur le dépose automatiquement dans le web/app/pluginsrépertoire.

haz
la source
Appréciez votre réponse, très utile! Mais je suis curieux de savoir le "bootstrap.php" dont vous parlez. Que contient-il? :)
INT
1
Avoir un fichier bootstrap.php est une chose stylistique que je fais dans la plupart de mes projets, dans ou hors de WP. Mon bootstrapper ne vérifie normalement que les paramètres et les variables d’environnement; Son objectif principal est de s'assurer que mon plugin a toujours ce qu'il faut pour fonctionner, qu'il soit exécuté depuis WP ou en tant qu'application PHP autonome.
haz
4

J'utilise l'autoloading (mon plugin contient de nombreuses classes, en partie parce qu'il inclut Twig), jamais un problème ne m'a été signalé (plugin installé plus de 20 000 fois).

Si vous êtes sûr de ne jamais avoir besoin d'utiliser une installation php ne prenant pas en charge les espaces de noms, tout va bien alors (environ 70% des blogs WordPress actuels ne prennent pas en charge les espaces de noms). Quelques points à noter:

Il semble que je me souvienne que les espaces de noms ne sont pas sensibles à la casse dans php normal, mais le sont quand on utilise fastcgi php sur iis.

De plus, même si vous êtes sûr que le code que vous développez actuellement ne sera utilisé que sur> 5.3.0, vous ne pourrez pas réutiliser de code avec des projets qui n'ont pas ce luxe - c'est la raison principale pour laquelle je ne l'ai pas espaces de noms utilisés sur des projets internes. J'ai trouvé que vraiment namespaces ne pas ajouter que beaucoup en comparaison avec le mal de tête possible d'avoir à supprimer la dépendance sur eux.

Daniel Chatfield
la source