web-dev-qa-db-fra.com

Autoloading & Namespaces dans les plugins et les thèmes WordPress: ça marche?

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

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

Remarque: les espaces de noms ne sont que PHP 5.3+. Supposons, pour cette question, que vous savez que vous aurez affaire à des serveurs dont vous savez qu'ils ont PHP 5.3 ou supérieur.

69
chrisguitarguy

D'accord, j'ai eu deux projets importants 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 besoins est une chose relativement bonne.

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'accrochage de WordPress fonctionne à l'aide de 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 (puis de 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 constante magique __NAMESPACE__ 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 de création standard d'une classe et tous les points d'ancrage du constructeur avec $this fonctionnent 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. Cela demande beaucoup de travail, vous pouvez donc utiliser simplement la constante magique __CLASS__ constante 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 les classes principales WP (WP_Widget dans l'exemple ci-dessous), vous devez fournir des instructions use.

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 mot clé const dans 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 appel define.

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

87
chrisguitarguy

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 Compositeur pour répondre à vos PHP exigences. 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 fichier composer.json et son propre fichier composer.lock.

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 fichier composer.json 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 exécutez composer install, il crée le répertoire vendor et le fichier vendor/autoload.php, qui chargeront automatiquement tous vos fichiers nom-espacés dans src/, ainsi que toute autre bibliothèque dont vous pourriez avoir besoin.

Ensuite, en haut de votre fichier de plugin principal (qui pour nous est 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 Bedrock Wordpress warmplate 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-theme et de cool-plugin ci-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 ne sont pas légaux en JSON, 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.

Remarque 3: C'est pourquoi le champ type du premier fichier composer.json est significatif. Composer le place automatiquement dans le répertoire web/app/plugins.

13
haz

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'une installation php ne prenant pas en charge les espaces de noms, tout va bien (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 les espaces de noms n'ajoutaient vraiment pas que beaucoup par rapport au mal de tête possible de devoir supprimer la dépendance à leur égard.

4
Daniel Chatfield