1. Code
  2. WordPress
  3. Plugin Development

Uso de namespaces y Autoloading en complementos de WordPress, parte 4

Scroll to top
This post is part of a series called Using Namespaces and Autoloading in WordPress Plugins.
Using Namespaces and Autoloading in WordPress Plugins, Part 3

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

Si este es el primer tutorial que estás leyendo en esta serie, entonces recomiendo encarecidamente ponerse al día con lo que hemos cubierto hasta ahora.

Esencialmente, estas asistiendo al final del show. En este punto, hemos sentado las bases para nuestro complemento, hemos escrito el complemento y hemos definido y explorado namespace y autoloaders. Todo lo que queda es aplicar lo que hemos aprendido.

Así que en este tutorial, vamos a juntar todas las piezas. Específicamente, vamos a revisar el código fuente de nuestro plugin, espacio de nombres todas las clases relevantes, y escribir un autocargador para que podamos eliminar todas nuestras declaraciones de inclusión.

Voy a discutir todo en detalle a medida que trabajamos a través del código. Una vez más, si este es el primer tutorial que estás leyendo en esta serie, ponte al día con lo que hemos cubierto hasta ahora y vuelve a este tutorial.

Antes de escribir cualquier código

En este punto, usted debe estar familiarizado con la forma en que hemos creado nuestro entorno de desarrollo. Como un refresco, he aquí un resumen rápido del software que estamos usando:

  • Al menos PHP 5.6.20
  • El servidor web Apache
  • Un servidor de base de datos MySQL
  • WordPress 4.6.1
  • Un conocimiento práctico de la API Plugin de WordPress

También vas a necesitar una copia del código fuente del plugin con el que estamos trabajando. Puedes tomar una copia de el aquí mismo. Suponiendo que esté instalado, activado y que su IDE esté funcionando, comencemos.

namespace del código

Recuerdo del tutorial anterior, soy un fan de asegurarse de que nuestros namespace siguen la organización de los archivos en el disco. Si observa la estructura de directorios de nuestro complemento o si ha estado siguiendo la serie hasta ahora, debería ver algo como esto:

The directory structure of our pluginThe directory structure of our pluginThe directory structure of our plugin

Tenga en cuenta que si ha configurado su complemento de forma diferente, está bien. Sus namespaces probablemente serán diferentes, pero eso no debería afectar nada de lo que está cubierto en esta serie.

Usando la estructura de directorios como una guía, vamos a pasar por todos los archivos PHP que componen nuestro complemento y definir sus namespaces. Hacer esto es fácil: es simplemente una cuestión de usar la palabra clave de namespace y colocar un nombre calificado en la parte superior de cada archivo.

Voy a listar cada uno a continuación.

tutsplus-namespace-demo.php

1
<?php
2
/**

3
 * The plugin bootstrap file

4
 *

5
 * This file is read by WordPress to generate the plugin information in the

6
 * plugin admin area. This file also includes all of the dependencies used by

7
 * the plugin, registers the activation and deactivation functions, and defines

8
 * a function that starts the plugin.

9
 *

10
 * @link              https://.tutsplus.com/tutorials/using-namespaces-and-autoloading-in-wordpress-plugins-part-1

11
 * @since             0.1.0

12
 * @package           tutsplus_namespace_demo

13
 *

14
 * @wordpress-plugin

15
 * Plugin Name:       Tuts+ Namespace Demo

16
 * Plugin URI:        http://.tutsplus.com/tutorials/using-namespaces-and-autoloading-in-wordpress-plugins-part-1

17
 * Description:       Learn how to use Namespaces and Autoloading in WordPress.

18
 * Version:           0.2.0

19
 * Author:            Tom McFarlin

20
 * Author URI:        https://tommcfarlin.com/

21
 * License:           GPL-2.0+

22
 * License URI:       http://www.gnu.org/licenses/gpl-2.0.txt

23
 */
24
25
namespace Tutsplus_Namespace_Demo;
26
27
// If this file is accessed directory, then abort.

28
if ( ! defined( 'WPINC' ) ) {
29
    die;
30
}

class-meta-box.php

1
<?php
2
/**

3
 * Represents a meta box to be displayed within the 'Add New Post' page.

4
 */
5
6
namespace Tutsplus_Namespace_Demo\Admin;

class-meta-box-display.php

1
<?php
2
/**

3
 * Defines the functionality required to render the content within the Meta Box

4
 * to which this display belongs.

5
 */
6
7
namespace Tutsplus_Namespace_Demo\Admin;

interface-assets.php

1
<?php
2
namespace Tutsplus_Namespace_Demo\Admin\Util;

class-css-loader.php

1
<?php
2
/**

3
 * Provides a consistent way to enqueue all administrative-related stylesheets.

4
 */
5
6
namespace Tutsplus_Namespace_Demo\Admin\Util;

class-question-reader.php

1
<?php
2
/**

3
 * Reads the contents of a specified file and returns a random line from the

4
 * file.

5
 */
6
7
namespace Tutsplus_Namespace_Demo\Admin\Util;

Hay algunas cosas a notar sobre las convenciones que he utilizado arriba:

  • El espacio de nombres raíz es Tutsplus_Namespace_Demo, que corresponde al nombre del directorio del complemento.
  • El resto de los namespaces como Tutsplus_Namespace_Demo\Admin y Tutsplus_Namespace_Demo\Admin\Util también corresponden a sus respectivos directorios; Sin embargo, los nombres de directorio están encapsulados (en lugar de estar en minúsculas).

Por último, si has intentado actualizar la página o has intentado navegar por WordPress desde la introducción de las sentencias de namespace, es probable que aparezca un error en tu consola que se vea así:

PHP Errors when loading namespaced codePHP Errors when loading namespaced codePHP Errors when loading namespaced code

E incluye el siguiente mensaje:

PHP Warning: call_user_func_array() expects parameter 1 to be a valid callback, function 'tutsplus_namespace_demo' not found or invalid function name in /Users/tommcfarlin/Dropbox/Projects/tutsplus/wp-includes/plugin.php on line 524

O tal vez muestra:

PHP Fatal error: Class 'Meta_Box' not found in /Users/tommcfarlin/Dropbox/Projects/tutsplus/wp-content/plugins/tutsplus-namespace-demo/tutsplus-namespace-demo.php on line 48

O puede ver cualquier número de otros mensajes de error similares. Esta bien. Es normal.

Pero plantea la pregunta: ¿Qué pasa con nuestro complemento? Por suerte, nada. Este es el comportamiento esperado.

El primer mensaje que ve puede ser el resultado de otro complemento que ha instalado. No pude reproducirlo por sí solo; Sin embargo, cuando desactivo algunos de los otros complementos que tengo en ejecución, el complemento generó el segundo mensaje (que es el mensaje que quería demostrar).

Cuando el código de namespace, PHP espera encontrar una clase dentro de un espacio de nombres dado. Conceptualmente usted puede pensar en sus clases que ahora pertenecen a su propio paquete (o subpaquete) o como lo defina. Y para que una función acceda a una clase dentro de un paquete, necesita tener conocimiento de los paquetes que existen.

Aquí es donde la funcionalidad de namespace adicional y autoloading entra en juego. Así que antes de ir a tratar de acceder a nuestro código a través de sus namespace , vamos a trabajar en un autoloader.

Todo acerca de la Autoloading

Escribir un autoloader requerirá lo siguiente:

  1. Entendiendo una función PHP llamada spl_autoload_register
  2. Escribiendo una función que cargará automáticamente nuestros archivos con namespace
  3. Incluyendo nuestra función de autoloading personalizada

No dejes que el nombre spl_autoload_register te intimide. Simplemente significa que esta es una función que forma parte de la "Biblioteca estándar de PHP" y es como "registramos" una función de "carga automática". Es dificil para explicar y un montón de caracteres para escribir, pero es sólo una función que vamos a utilizar para decir a PHP cómo analizar namespace, nombres de clases y donde puede encontrar nuestros archivos.

Esta función es lo que nos permite escribir nuestro propio código personalizado para autoloading archivos y luego enganchar dicha función en PHP. Es decir, vamos a decirle a PHP dónde encontrar nuestros archivos y cómo analizar namespaces, nombres de archivos, etc. para que incluya los archivos.

Con todo eso dicho, estamos listos para escribir un autoloader.

Escribir un autocargador

Al escribir un autoloader, la cosa a tener en cuenta es cómo nuestros archivos están organizados. Es decir, queremos saber cómo mapear nuestros namespaces a nuestros directorios.

En el ejemplo que estamos utilizando, es simple: Los namespaces son versiones encasilladas de la estructura de directorios. Esto no siempre es cierto para otros proyectos; Sin embargo, es otra razón por la que me gusta organizar lógicamente mis archivos basados en su ubicación física.

Cuando PHP intenta cargar una clase, nuestro autoloader va a necesitar hacer lo siguiente:

  1. Dividir el namespace en función de las barras.
  2. Dividir el paquete y los subpaquetes de acuerdo con los guiones bajos y sustitúirlos con guiones (si es necesario).
  3. Saber cómo asignar nombres de clases, interfaces y así sucesivamente a nombres de archivo.
  4. Crear una representación de cadena del nombre de archivo basado en la información anterior.
  5. Incluir el archivo.

Con todos esos puntos hechos, tenemos nuestro trabajo simplificado para nosotros. En el directorio de complementos, cree un subdirectorio llamado inc, y en el directorio inc cree un archivo llamado autoload.php.

Dentro de ese archivo, vamos a seguir adelante y corregir la función que vamos a utilizar para autoload nuestros archivos. Debe ser algo como esto:

1
<?php
2
/**

3
 * Dynamically loads the class attempting to be instantiated elsewhere in the

4
 * plugin.

5
 *

6
 * @package Tutsplus_Namespace_Demo\Inc

7
 */
8
9
spl_autoload_register( 'tutsplus_namespace_demo_autoload' );
10
11
/**

12
 * Dynamically loads the class attempting to be instantiated elsewhere in the

13
 * plugin by looking at the $class_name parameter being passed as an argument.

14
 *

15
 * The argument should be in the form: TutsPlus_Namespace_Demo\Namespace. The

16
 * function will then break the fully-qualified class name into its pieces and

17
 * will then build a file to the path based on the namespace.

18
 *

19
 * The namespaces in this plugin map to the paths in the directory structure.

20
 *

21
 * @param string $class_name The fully-qualified name of the class to load.

22
 */
23
function tutsplus_namespace_demo_autoload( $class_name ) {
24
25
    // If the specified $class_name does not include our namespace, duck out.

26
    if ( false === strpos( $class_name, 'Tutsplus_Namespace_Demo' ) ) {
27
        return;
28
    }
29
30
    // More to come...

31
}

Obviamente, esto no está haciendo nada todavía.

Una nota sobre cómo escribir un Autoloader

Tenga en cuenta que voy a escribir el código y los comentarios de código para explicar a fondo lo que estamos haciendo. Si usted acaba de aventurarse en esto por su cuenta por primera vez, escribir un autoloader junto con el uso de namespaces y trabajar con archivos puede ser un poco frustrante. Aquí es donde un depurador y el uso de archivos de registro puede ser útil.

Esto está fuera del alcance de este tutorial, pero sepa que escribir un autoloader no es algo que pueda realizar correctamente la primera vez que lo esté haciendo.

Cómo completar el Autoloader

Comencemos a agregar alguna funcionalidad dada las etapas enumeradas al principio de esta sección.

En primer lugar, tenemos que configurar un bucle que va a iterar hacia atrás a través de las partes del nombre de archivo que se pasan a la función de carga automática. Lo hacemos porque facilita la creación de una ruta de acceso al archivo para su carga automática.

1
<?php
2
function tutsplus_namespace_demo_autoload( $class_name ) {
3
4
    // If the specified $class_name does not include our namespace, duck out.

5
    if ( false === strpos( $class_name, 'Tutsplus_Namespace_Demo' ) ) {
6
        return;
7
    }
8
9
    // Split the class name into an array to read the namespace and class.

10
    $file_parts = explode( '\\', $class_name );
11
12
    // Do a reverse loop through $file_parts to build the path to the file.

13
    $namespace = '';
14
    for ( $i = count( $file_parts ) - 1; $i > 0; $i-- ) {
15
        // More to come...

16
    }
17
}

Después de esto, necesitamos mirar las $file_parts y reemplazar todas las apariciones del subrayado con un guión porque todos nuestros nombres de clase e interfaz usan subrayados mientras que nuestros nombres de archivo usan guiones.

Las dos líneas siguientes son las dos primeras líneas dentro del bucle que hemos mostrado arriba:

1
<?php
2
3
// Read the current component of the file part.

4
$current = strtolower( $file_parts[ $i ] );
5
$current = str_ireplace( '_', '-', $current );

A continuación, vamos a necesitar un condicional que haga algunas cosas.

  1. Necesita comprobar cual entrada de la ruta del nombre de archivo que estamos leyendo.
  2. Si estamos en la primera entrada, entonces estamos en el nombre del archivo; De lo contrario, estamos en su namespace.
  3. A continuación, si estamos leyendo la primera entrada, necesitamos determinar si estamos intentando cargar automáticamente una interfaz o si estamos cargando una clase.
  4. Si es el primero, entonces tenemos que ajustar el nombre de la interfaz para que lo carguemos correctamente sobre la base de su nombre de archivo; De lo contrario, cargaremos la clase en función del valor de la variable $current.

Se lee como mucho, pero no debe ser terriblemente complicado de leer. Vea el código comentado a continuación:

1
<?php
2
3
// If we're at the first entry, then we're at the filename.

4
if ( count( $file_parts ) - 1 === $i ) {
5
6
        /* If 'interface' is contained in the parts of the file name, then

7
   * define the $file_name differently so that it's properly loaded.

8
	 * Otherwise, just set the $file_name equal to that of the class

9
	 * filename structure.

10
	 */
11
	if ( strpos( strtolower( $file_parts[ count( $file_parts ) - 1 ] ), 'interface' ) ) {
12
13
		// Grab the name of the interface from its qualified name.

14
		$interface_name = explode( '_', $file_parts[ count( $file_parts ) - 1 ] );
15
		$interface_name = $interface_name[0];
16
17
		$file_name = "interface-$interface_name.php";
18
19
	} else {
20
		$file_name = "class-$current.php";
21
	}
22
} else {
23
	$namespace = '/' . $current . $namespace;
24
}

Con eso hecho, es hora de construir una trayectoria totalmente calificada al archivo. Por suerte, esto es poco más que concatenación de cadena básica:

1
<?php
2
3
// Now build a path to the file using mapping to the file location.

4
$filepath  = trailingslashit( dirname( dirname( __FILE__ ) ) . $namespace );
5
$filepath .= $file_name;

Por último, necesitamos asegurarnos de que el archivo existe. Si no, mostraremos un mensaje de error estándar de WordPress:

1
<?php
2
3
// If the file exists in the specified path, then include it.

4
if ( file_exists( $filepath ) ) {
5
    include_once( $filepath );
6
} else {
7
    wp_die(
8
        esc_html( "The file attempting to be loaded at $filepath does not exist." )
9
    );
10
}

Y en este punto, tenemos un autoloader completo (que se puede recuperar descargando los archivos del enlace en la barra lateral de este post como el código fuente sería un poco largo para publicar aquí en el tutorial).

Finalmente, es importante notar que esta función particular podría (o debería) ser reescrita como una clase. Además, la clase debe consistir en varias funciones más pequeñas de las cuales son comprobables, tienen una sola responsabilidad, y leen más claramente que lo que está arriba. Tal vez en un tutorial de bonificación, voy a caminar a través del proceso de lo que se vería.

Pero todavía estamos incluyendo archivos

Si miras cerca de la parte superior del archivo principal del complemento (o el archivo de arranque que a menudo lo hemos llamado), notarás varias sentencias de inclusión que tienen este aspecto:

1
<?php
2
3
// Include the files for rendering the display.

4
include_once( 'admin/class-meta-box.php' );
5
include_once( 'admin/class-meta-box-display.php' );
6
include_once( 'admin/util/class-question-reader.php' );
7
8
// Include the files for loading the assets

9
include_once( 'admin/util/interface-assets.php' );
10
include_once( 'admin/util/class-css-loader.php' );

Dado el trabajo que hemos hecho hasta este punto, podemos finalmente eliminar estas declaraciones y reemplazarlas por una sola:

1
<?php
2
3
// Include the autoloader so we can dynamically include the rest of the classes.

4
require_once( trailingslashit( dirname( __FILE__ ) ) . 'inc/autoloader.php' );

Para estar claro, lo estamos reemplazando con nuestro autoloader. En este punto, debemos hacerlo con nuestro complemento.

Poniendolo todo junto

Ahora que hemos namespace nuestro código para proporcionar una organización lógica de clases relacionadas, y escrito un cargador automático para incluir automáticamente los archivos sobre la base de namespace y ubicación del archivo de cada clase, debemos ser capaces de iniciar nuestro plugin y hacer que se ejecute exactamente como lo hizo durante la primera iteración exitosa.

Lo último que debemos hacer es asegurarnos de que actualizamos el archivo bootstrap para que instruyamos a PHP a usar los espacios de nombres para Meta_Box, Meta_Box_Display, Question_Reader y CSS_Loader.

1
<?php
2
3
use Tutsplus_Namespace_Demo\Admin;
4
use Tutsplus_Namespace_Demo\Admin\Util;
5
6
// If this file is accessed directory, then abort.

7
if ( ! defined( 'WPINC' ) ) {
8
    die;
9
}
10
11
// Include the autoloader so we can dynamically include the rest of the classes.

12
require_once( trailingslashit( dirname( __FILE__ ) ) . 'inc/autoloader.php' );
13
14
add_action( 'plugins_loaded', 'tutsplus_namespace_demo' );
15
/**

16
 * Starts the plugin by initializing the meta box, its display, and then

17
 * sets the plugin in motion.

18
 */
19
function tutsplus_namespace_demo() {
20
21
    $meta_box = new Admin\Meta_Box(
22
        new Admin\Meta_Box_Display(
23
            new Util\Question_Reader()
24
        )
25
    );
26
27
    $css_loader = new Util\CSS_Loader();
28
    $css_loader->init();
29
30
    $meta_box->init();
31
}

Observe en el código anterior que estamos usando la palabra clave use de PHP, y estamos prefijando nuestros nombres de clase con sus subpaquetes inmediatos. Usted puede leer más sobre el uso en el manual, pero el corto de él es:

La palabra clave use se debe declarar en el ámbito más externo de un archivo (el ámbito global) o dentro de las declaraciones de namespace. Esto se debe a que la importación se realiza en tiempo de compilación y no en tiempo de ejecución, por lo que no se puede bloquear el ámbito.

Con esto dicho y asumiendo que todo funciona correctamente, debería ser capaz de navegar a la página Agregar nuevo mensaje (o Editar publicación), ver nuestro cuadro de meta y ver un mensaje de pregunta en la parte superior de la barra lateral:

The meta box prompting users for inspirationThe meta box prompting users for inspirationThe meta box prompting users for inspiration

Si es así, entonces felicitaciones. Ha configurado correctamente su complemento a sus namespace y carga automática. Si no es así, compruebe el código de nuevo con respecto a lo que hemos compartido aquí, revise los registros de errores y asegúrese de que no aparece nada fuera de lo común en la pantalla de administración de WordPress.

Si usted ve algo, las probabilidades son que tiene que ver con algo menor. Revisa el código que hemos cubierto, comparalo con lo que se adjunta aquí a esta publicación (en la barra lateral junto con el botón azul grande) y ver si puedes ir descubriendo el problema.

Conclusion

En este punto, hemos llegado al final de nuestra serie. A lo largo de los últimos cuatro tutoriales, hemos cubierto un montón de terreno:

  • Hemos creado un complemento que solicita a los usuarios preguntas para ayudar a iniciar su blog.
  • Hemos utilizado las funciones de PHP para leer archivos del sistema de archivos y mostrarlo en la pantalla.
  • Hemos definido los namespace y el autoloading, y echado un vistazo a cómo se pueden aplicar.
  • Hemos organizado nuestro código y escrito nuestro propio cargador automático, haciendo que el código sea más legible, organizado y menos desordenado.

En última instancia, una gran parte del material cubierto a lo largo de esta serie se puede utilizar en proyectos existentes y futuros en los que puede estar trabajando.

Recuerde que también puede encontrar otros productos relacionados con WordPress en el marketplace. Y si quieres saber más sobre el desarrollo de soluciones para WordPress, puedes encontrar todos mis tutoriales y series en mi perfil. No dude en seguirme en mi blog o en Twitter, en los cuales discuto el desarrollo de software en el contexto de WordPress casi a diario.

Y recuerde, el enlace es para descargar el código fuente final está en la barra lateral en un botón titulado Descargar Adjunto. Por supuesto, ¡no dude en hacer cualquier pregunta en los comentarios!

Recursos