Autocarga y espacios de nombres en los complementos y temas de WordPress: ¿Puede funcionar?

65

¿Alguien ha usado autoloading y / o espacios de nombres PHP dentro de un complemento o tema?

¿Pensamientos en usarlos? ¿Cualquier daño? Trampas?

Nota: los espacios de nombres son solo PHP 5.3+. Supongamos, para esta pregunta, que sabe que tratará con servidores que sabe que tienen PHP 5.3 o superior.

    
pregunta chrisguitarguy 30.08.2012 - 22:00

3 respuestas

85

De acuerdo, he tenido dos grandes proyectos en los que he tenido el control del servidor lo suficiente como para dejar espacio de nombres y confiar en la carga automática.

En primer lugar. La carga automática es impresionante. No preocuparse por las necesidades es algo relativamente bueno.

Aquí hay un cargador que he estado usando en algunos proyectos. Comprueba que la clase esté en el espacio de nombres actual primero, y luego salte si no. Desde allí es solo una manipulación de cadenas para encontrar la clase.

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

Uno podría adaptarlo fácilmente para su uso sin espacios de nombres. Suponiendo que prefieres las clases de tu plugin / tema de manera uniforme, simplemente puedes probar ese prefijo. Luego use guiones bajos en el nombre de la clase como marcadores de posición para los separadores de directorios. Si está utilizando muchas clases, es probable que desee utilizar algún tipo de autocargador de mapas de clase.

Espacios de nombres y ganchos

El sistema de enganches de WordPress funciona usando call_user_func (y call_user_func_array ), que toma los nombres de las funciones como cadenas y las llama cuando se realiza la llamada a la función do_action (y, posteriormente, call_user_func ).

Con los espacios de nombres, eso significa que deberá pasar nombres de funciones completamente calificados que incluyan el espacio de nombres en los ganchos.

<?php
namespace WPSE\SomeNameSpace;

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

Probablemente sería mejor hacer un uso liberal de la constante __NAMESPACE__ magic si quieres hacer esto.

<?php
namespace WPSE\SomeNameSpace;

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

Si siempre pones tus ganchos en clases, es más fácil. La instancia estándar de creación de una clase y todos los enlaces en el constructor con $this funcionan bien.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

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

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

Si usa métodos estáticos como los que quiero hacer, deberá pasar el nombre de clase completo como el primer argumento de la matriz. Eso es mucho trabajo, así que solo puedes usar magic __CLASS__ constant o 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!
    }
}

Usando clases principales

La resolución del nombre de clase de PHP es un poco torpe. Si va a utilizar clases de WP principales ( WP_Widget en el ejemplo a continuación), debe proporcionar use enunciados.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

O puede usar el nombre de clase completo, básicamente con un prefijo con una barra invertida.

<?php
namespace WPSE\SomeNameSpace;

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

Define

Esto es PHP más general, pero me mordió, así que aquí está.

Es posible que desee definir cosas que usará a menudo, como la ruta de acceso a su complemento. El uso de la declaración de definición coloca las cosas en el espacio de nombres raíz a menos que pase explícitamente el espacio de nombres al primer argumento de definir.

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

También puede usar la palabra clave const en el nivel raíz de un archivo con PHP 5.3 más. consts s siempre están en el espacio de nombres actual, pero son menos flexibles que una llamada a define .

<?php
namespace WPSE\SomeNameSpace;

// in the current namespace
const MY_CONST = 1;

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

Por favor, siéntase libre de agregar cualquier otro consejo que pueda tener.

    
respondido por el chrisguitarguy 28.08.2013 - 23:17
11

Aquí hay una respuesta de 2017.

La carga automática es impresionante. El espacio para nombres es impresionante.

Aunque puede hacerlo usted mismo, en 2017 tiene más sentido utilizar el magnífico y ubicuo Compositor para manejar sus requisitos de PHP. Composer admite tanto PSR-0 como PSR-4 , pero la primera está en desuso desde 2014, así que use el PSR-4. Reduce la complejidad de tus directorios.

Mantenemos cada uno de nuestros complementos / temas en su propio repositorio Github, cada uno con su propio archivo composer.json y archivo composer.lock .

Aquí está la estructura de directorios que utilizamos para nuestros complementos. (Realmente no tenemos un complemento llamado awesome-plugin , pero deberíamos).

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 proporciona un archivo composer.json apropiado, Composer se encarga del espaciado de nombres y la carga automática aquí.

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

Cuando ejecutas composer install , crea el directorio vendor y el archivo vendor/autoload.php , que cargará automáticamente todos tus archivos con espacios de nombre en src/ , y cualquier otra biblioteca que necesites.

Luego, en la parte superior de su archivo de plugin principal (que para nosotros es awesome-plugin.php ), después de los metadatos de su plugin, simplemente necesita:

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

...

Característica de bonificación

No es una necesidad, pero usamos el Bedrock plantilla de Wordpress para usar Composer desde el principio. Luego podemos usar Composer para ensamblar los complementos que necesitamos a través de Composer, incluido su propio complemento que escribió anteriormente. Además, gracias a WPackagist , puede requerir cualquier otro complemento de Wordpress.org (vea el ejemplo de cool-theme y cool-plugin a continuación).

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

Nota 1: los comentarios no son legales en JSON, pero he anotado el archivo anterior para mayor claridad.

Nota 2: he cortado algunos fragmentos del archivo de Bedrock de boilerplate por brevedad.

Nota 3: esta es la razón por la cual el campo type en el primer archivo composer.json es significativo. Composer lo suelta automáticamente en el directorio web/app/plugins .

    
respondido por el haz 07.06.2017 - 02:34
4

Uso la carga automática (ya que mi complemento tiene muchas clases, en parte porque incluye Twig), nunca se me llamó la atención (el complemento está instalado > 20,000 veces).

Si está seguro de que nunca necesitará usar una instalación php que no admita espacios de nombres, entonces nuevamente estará bien (~ 70% de los blogs de wordpress actuales no admiten espacios de nombres). Algunas cosas a tener en cuenta:

Me parece recordar que los espacios de nombres no distinguen entre mayúsculas y minúsculas en php regular, pero sí lo son al usar fastcgi php en iis.

También si está seguro de que el código que está desarrollando actualmente solo se utilizará en > 5.3.0 no podrá reutilizar ningún código con proyectos que no tengan ese lujo, esa es la razón principal por la que no he usado espacios de nombres en proyectos internos. Descubrí que los espacios de nombres realmente no agregan mucho que en comparación con el posible dolor de cabeza de tener que eliminar la dependencia de ellos.

    
respondido por el Daniel Chatfield 03.09.2012 - 16:07

Lea otras preguntas en las etiquetas