Autocarga y Espacios de Nombres en Plugins y Temas de WordPress: ¿Puede Funcionar?

30 ago 2012, 23:00:45
Vistas: 29.2K
Votos: 83

¿Alguien ha utilizado autocarga y/o espacios de nombres de PHP dentro de un plugin o tema?

¿Qué opinan sobre su uso? ¿Hay algún inconveniente? ¿Puntos problemáticos?

Nota: los espacios de nombres son exclusivos de PHP 5.3+. Asume, para esta pregunta, que sabes que estarás trabajando con servidores que tienen PHP 5.3 o superior.

0
Todas las respuestas a la pregunta 3
0
100

De acuerdo, he tenido dos grandes proyectos donde he tenido suficiente control del servidor como para usar espacios de nombres y confiar en la autocarga.

Primero que nada. La autocarga es increíble. No tener que preocuparse por los includes es algo relativamente bueno.

Aquí hay un cargador que he estado usando en varios proyectos. Verifica primero si la clase está en el espacio de nombres actual, luego sale si no es así. A partir de ahí, es solo 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 adaptar fácilmente esto para usarlo sin espacios de nombres. Asumiendo que prefijas las clases de tu plugin/tema de manera uniforme, podrías simplemente probar ese prefijo. Luego usa guiones bajos en el nombre de la clase como marcadores de posición para separadores de directorios. Si usas muchas clases, probablemente querrás usar algún tipo de autocargador con mapa de clases.

Espacios de nombres y Hooks

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

Con espacios de nombres, eso significa que necesitarás pasar nombres de funciones completamente calificados que incluyan el espacio de nombre a los hooks.

<?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 mágica __NAMESPACE__ 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 hooks en clases, es más fácil. El estándar de crear una instancia de una clase y todos los hooks en el constructor con $this funciona bien.

<?php
namespace WPSE\SomeNameSpace;

new Plugin;

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

    function loaded()
    {
        // ¡esto funciona!
    }
}

Si usas métodos estáticos como yo quiero hacer, necesitarás pasar el nombre de clase completamente calificado como primer argumento del array. Eso es mucho trabajo, así que puedes simplemente usar la constante mágica __CLASS__ o get_class.

<?php
namespace WPSE\SomeNameSpace;

Plugin::init();

class Plugin
{
    public static function init()
    {
        add_action('plugins_loaded', array(__CLASS__, 'loaded'));
        // O: add_action('plugins_loaded', array(get_class(), 'loaded'));
    }

    public static function loaded()
    {
        // ¡esto funciona!
    }
}

Usando Clases del Núcleo

La resolución de nombres de clase en PHP es un poco irregular. Si vas a usar clases del núcleo de WP (WP_Widget en el ejemplo de abajo) debes proporcionar declaraciones use.

use \WP_Widget;

class MyWidget extends WP_Widget
{
   // ...
}

O puedes usar el nombre de clase completamente calificado - básicamente solo prefijándolo con una barra invertida.

<?php
namespace WPSE\SomeNameSpace;

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

Definiciones

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

Puede que quieras definir cosas que usarás a menudo, como la ruta a tu plugin. Usar la declaración define pone las cosas en el espacio de nombres raíz a menos que pases explícitamente el espacio de nombres en el primer argumento de define.

<?php
namespace WPSE\SomeNameSpace;

// espacio de nombres raíz
define('WPSE_63668_PATH', plugin_dir_path(__FILE__));

// en el espacio de nombres actual
define(__NAMESPACE__ . '\\PATH', plugin_dir_path(__FILE__));

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

<?php
namespace WPSE\SomeNameSpace;

// en el espacio de nombres actual
const MY_CONST = 1;

// ¡esto no funcionará!
const MY_PATH = plugin_dir_path(__FILE__);

¡Por favor, siéntete libre de añadir cualquier otro consejo que puedas tener!

27 sept 2012 23:08:44
4
20

Aquí hay una respuesta de 2017.

El autoloading es genial. Los espacios de nombres son geniales.

Aunque podrías implementarlo tú mismo, en 2017 tiene más sentido usar el magnífico y omnipresente Composer para manejar tus requisitos de PHP. Composer soporta tanto el autoloading PSR-0 como PSR-4, pero el primero ha quedado obsoleto desde 2014, así que usa PSR-4. Reduce la complejidad de tus directorios.

Nosotros mantenemos cada uno de nuestros plugins/temas en su propio repositorio de Github, cada uno con su propio archivo composer.json y archivo composer.lock.

Aquí está la estructura de directorios que usamos para nuestros plugins. (En realidad no tenemos un plugin 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 proporcionas un archivo composer.json apropiado, Composer se encarga de los espacios de nombres y el autoloading aquí.

{
    "name": "awesome-company/awesome-plugin",
    "description": "Plugin de WordPress para el sitio de AwesomeCompany, proporcionando funcionalidad increíble.",
    "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 nombres en src/, y cualquier otra biblioteca que puedas requerir.

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

// Autoloading de Composer.
require_once __DIR__ . '/vendor/autoload.php';

...

Característica Adicional

No es una necesidad, pero usamos la plantilla de WordPress Bedrock para usar Composer desde el principio. Luego podemos usar Composer para ensamblar los plugins que necesitamos, incluyendo tu propio plugin que escribiste arriba. Además, gracias a WPackagist, puedes requerir cualquier otro plugin de Wordpress.org (ver ejemplo de cool-theme y cool-plugin abajo).

{
  "name": "awesome-company/awesome-website",
  "type": "project",
  "license": "proprietary",
  "description": "Plantilla de WordPress con herramientas modernas de desarrollo, configuración más sencilla y una estructura de carpetas mejorada",
  "config": {
    "preferred-install": "dist"
  },
  "repositories": [
    {
      "type": "composer",
      "url": "https://wpackagist.org"
    },
    { // Le dice a Composer que busque nuestro Plugin Awesome aquí.
        "url": "https://github.com/awesome-company/awesome-plugin.git",
        "type": "git"
    }
  ],
  "require": {
    "php": ">=5.5",
    "awesome-company/awesome-plugin": "dev-production", // ¡Nuestro plugin!
    "wpackagist-plugin/cool-plugin": "dev-trunk",       // Plugin de alguien más
    "wpackagist-theme/cool-theme": "dev-trunk",         // Tema de alguien más
    "composer/installers": "~1.2.0",     // Por defecto en Bedrock
    "vlucas/phpdotenv": "^2.0.1",        // Por defecto en Bedrock
    "johnpbloch/wordpress": "4.7.5",     // Por defecto en Bedrock
    "oscarotero/env": "^1.0",            // Por defecto en Bedrock
    "roots/wp-password-bcrypt": "1.0.0"  // Por defecto en Bedrock
  },
  "extra": {
    // Esta es la magia que coloca los paquetes con el TIPO correcto en la ubicación correcta.
    "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 válidos en JSON, pero he anotado el archivo anterior para mayor claridad.

Nota 2: He eliminado algunas partes del archivo de plantilla Bedrock para brevedad.

Nota 3: Por esto el campo type en el primer archivo composer.json es significativo. Composer automáticamente lo coloca en el directorio web/app/plugins.

7 jun 2017 03:34:02
Comentarios

¡Agradezco tu respuesta, muy útil! Pero tengo curiosidad sobre el "bootstrap.php" al que te refieres. ¿Qué contiene? :)

INT INT
22 sept 2018 23:15:53

Tener un archivo bootstrap.php es algo estilístico que hago en la mayoría de mis proyectos, dentro o fuera de WP. Mi bootstrapper normalmente solo verifica configuraciones y variables de entorno; su propósito principal es asegurarse de que mi plugin siempre tenga lo que necesita para ejecutarse, independientemente de si se ejecuta dentro de WP o como una aplicación PHP independiente.

haz haz
24 sept 2018 01:43:25

Hola, ¿dónde ejecuto ´composer install´?

LΞИIИ LΞИIИ
27 abr 2020 08:13:09

@LeninZapata Debes ejecutar composer install en el directorio que contiene el archivo composer.json. Por ejemplo, si clonas Bedrock (https://github.com/roots/bedrock), luego entra en tu directorio bedrock y ejecútalo allí.

haz haz
6 may 2020 09:43:54
0

Yo uso autocarga (ya que mi plugin tiene muchas clases - en parte porque incluye Twig), nunca he tenido un problema reportado (el plugin tiene más de 20,000 instalaciones).

Si estás seguro de que nunca necesitarás usar una instalación de PHP que no soporte espacios de nombres (namespaces), entonces no hay problema (~70% de los blogs de WordPress actuales no los soportan). Algunas cosas a tener en cuenta:

Recuerdo que los espacios de nombres no distinguen entre mayúsculas y minúsculas en PHP normal, pero sí lo hacen cuando usas FastCGI PHP en IIS - esto puede causar dolores de cabeza si pruebas en Linux y no detectas una letra minúscula incorrecta.

Incluso si estás seguro de que el código que estás desarrollando solo se usará en PHP > 5.3.0, no podrás reutilizar ese código en proyectos que no tengan ese lujo - esa es la razón principal por la que no he usado espacios de nombres en proyectos internos. He encontrado que los espacios de nombres realmente no añaden tanto valor comparado con el posible dolor de cabeza de tener que eliminar la dependencia de ellos.

3 sept 2012 17:07:31