Autocarga y Espacios de Nombres en Plugins y Temas de WordPress: ¿Puede Funcionar?
¿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.

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!

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
.

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

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.

@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í.

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.
