Cómo abordar el desarrollo moderno de WordPress (Parte 2)
Publicado: 2022-03-11WordPress es la tecnología de sitios más utilizada en el planeta y por una buena razón. Sin embargo, el código heredado en su núcleo es un desastre, y este problema afecta a los desarrolladores externos. Algunos desarrolladores toman esto como una excusa para tomar atajos en su propio código PHP de WordPress, pero este enfoque es más costoso a largo plazo para todos los cambios, excepto los más triviales.
En la Parte 1 de nuestra serie de dos partes, nos enfocamos en el proyecto general y las herramientas de flujo de trabajo, seguidas del desarrollo front-end. Ahora es el momento de tomar el toro por los cuernos y luchar con PHP: Específicamente, cómo seguir las mejores prácticas cuando se trabaja con el código back-end de WordPress. Puede pensar en esto como un tutorial de PHP/WordPress, pero un poco más avanzado, suponiendo que ya ha realizado algunas personalizaciones de back-end de WordPress.
Entonces, ¿qué principios de diseño de software moderno y características de PHP le darán el mejor valor por su tiempo? Las siguientes son 10 prácticas de desarrollo de WordPress y PHP que recomiendo encarecidamente.
Práctica recomendada n.° 1 para el desarrollo moderno de WordPress: siga la “separación de preocupaciones”
La separación de preocupaciones significa que las partes del código PHP de WordPress que tienen diferentes funciones o propósitos no deben mezclarse. En cambio, deben organizarse en distintas secciones o módulos, pasándose datos entre sí a través de una interfaz definida. (Una interfaz es un conjunto de parámetros definidos que un módulo toma como entrada y lo que devuelve). Un término estrechamente relacionado es el principio de responsabilidad única : cada módulo de código (o función) debe ser responsable de una sola cosa.
El objetivo final de seguir estos principios es producir código que sea modular y, por lo tanto, mantenible, extensible y reutilizable.
Eso fue bastante complicado, así que veamos un ejemplo de un PHP de WordPress (del núcleo de WordPress) que enreda todo. Este estilo de codificación a menudo se denomina "código espagueti" porque comprender su funcionamiento interno es casi imposible. El extracto a continuación fue redactado por razones de brevedad; sin embargo, se conservaron el estilo y el formato originales.
$id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; <table class="form-table"> <?php $blog_prefix = $wpdb->get_blog_prefix( $id ); $sql = "SELECT * FROM {$blog_prefix}options WHERE option_name NOT LIKE %s AND option_name NOT LIKE %s"; $query = $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', '%' . $wpdb->esc_like( 'user_roles' ) ); $options = $wpdb->get_results( $query ); foreach ( $options as $option ) { if ( strpos( $option->option_value, "\n" ) === false ) { ?> <tr class="form-field"> <th scope="row"><label for="<?php echo esc_attr( $option->option_name ); ?>"><?php echo esc_html( ucwords( str_replace( '_', ' ', $option->option_name ) ) ); ?></label></th> <?php if ( $is_main_site && in_array( $option->option_name, array( 'siteurl', 'home' ) ) ) { ?> <td><code><?php echo esc_html( $option->option_value ); ?></code></td> <?php } else { ?> <td><input class="<?php echo $class; ?>" name="option[<?php echo esc_attr( $option->option_name ); ?>]" type="text" value="<?php echo esc_attr( $option->option_value ); ?>" size="40" <?php disabled( $disabled ); ?> /></td> <?php } ?> </tr> <?php } } // End foreach </table>
En primer lugar, es totalmente incomprensible. Y me encanta que el único comentario sea End foreach
, que es completamente redundante. Tenemos consultas de bases de datos, procesamiento de resultados de consultas, procesamiento adicional incrustado en HTML (hay un if
/ else
anidado allí si no lo ha notado), escape de salida y plantillas HTML, todo combinado. Otro problema es que el parámetro $id
proviene directamente del $_REQUEST
global en lugar de pasar un parámetro real a una función.
Mirando esto, es perfectamente comprensible por qué el núcleo de WordPress se ha mantenido casi igual durante años. Refactorizar este tipo de código, especialmente manteniendo el comportamiento existente, es una tarea verdaderamente épica que nadie querría hacer.
Entonces, ¿cómo lo hacemos correctamente? Bueno, una cosa para recordar es que no hay una única manera verdadera . Mencionamos anteriormente las cualidades por las que debemos esforzarnos: Necesitamos que el código PHP personalizado de WordPress sea mantenible y modular. Veamos cómo podemos dividir el código anterior en módulos.
- Las consultas SQL deben estar en un módulo separado, obviamente. WordPress ya tiene una clase
WP_Query
muy bien resumida que debería usarse como ejemplo. - Todo HTML va en una plantilla. Cubriremos las plantillas de PHP más adelante.
- El código PHP restante debe incluirse en una función: varias funciones si el código es demasiado largo o complejo para una función. Los parámetros como
$id
se pasan a través de argumentos de función.
Esta es una reescritura muy simplificada del ejemplo anterior:
function betterSiteSettings($args) { $data = WP_Settings_Query($args); // process $data here $context = array_merge([], $data_processed, $other_data); return Template::render('template.name', $context); }
Mejor práctica moderna de desarrollo de WordPress #2: Evite las variables globales
WordPress tiene demasiadas variables globales. ¿Por qué las variables globales son malas? Hacen que su código PHP de WordPress sea difícil de seguir y hacen que el estado de la aplicación no sea confiable. Cualquier pieza de código PHP, y eso significa cualquier complemento que esté instalado en WordPress, puede leer y escribir variables globales, por lo que no hay garantía de que contengan datos válidos. Tratar de entender qué variables globales se usan en algo como el Loop tampoco es una tarea trivial.
Veamos esto desde un ángulo práctico. Este ejemplo proviene de WooCommerce. Probablemente todos los desarrolladores de WordPress saben lo que es: el bucle:
<?php while ( have_posts() ) : the_post(); ?> <?php wc_get_template_part( 'content', 'single-product' ); ?> <?php endwhile; // end of the loop. ?>
El fragmento anterior representa una plantilla de producto. ¿Cómo sabe qué producto mostrar, dado que no se pasan parámetros a wc_get_template_part
? Mirando la plantilla, vemos que comienza con global $product;
, así que ahí es donde se almacena el objeto del producto actual.
Ahora imagine que tenemos una página de catálogo que busca y filtra productos, y queremos mostrar una ventana emergente de "detalles del producto" mientras permanecemos en la misma página. Detrás de escena, el script de front-end realiza una solicitud AJAX para obtener esa plantilla de producto en particular. No podemos simplemente llamar a wc_get_template_part('content', 'single-product')
porque no usa parámetros, así que necesitamos establecer un par de variables globales para que esto funcione.
Los casos de uso más complejos implicarían más de una plantilla, enlaces activados en esas plantillas y complementos de terceros que agregarían sus devoluciones de llamada a esos enlaces. Puede escalar rápidamente. No tenemos forma de saber en qué estado global se basan esas devoluciones de llamada. Los complementos de terceros son libres de modificar cualquier variable global en sus devoluciones de llamada. En lugar de usar el sistema, comenzamos a pelear con el sistema, encontrando errores extraños que provienen de un estado global poco confiable.
¿No sería más sensato pasar ese ID de producto como parámetro? Entonces podríamos reutilizar esa plantilla sin preocuparnos por estropear las variables globales utilizadas por WordPress.
Práctica recomendada de desarrollo moderno de WordPress n.° 3: utilizar la programación orientada a objetos (POO)
La modularidad conduce al concepto de objetos y programación orientada a objetos. En un nivel muy básico, OOP es una forma de organizar el código. Las funciones y las variables se agrupan en clases y se denominan métodos y propiedades de clase, respectivamente. El Manual de complementos de WordPress recomienda usar OOP para organizar su código PHP personalizado de WordPress.
Un principio importante en OOP es limitar el acceso a métodos y propiedades, o en términos de PHP, denotarlos como private
o protected
, para que solo otros métodos de clase puedan acceder y cambiarlos. Un término OOP para esto es encapsulación : los datos se encapsulan dentro de la clase, y la única forma de cambiar esos datos es mediante el uso de métodos de clase proporcionados.
Esto hace que la depuración y el mantenimiento de su código sean mucho más fáciles que cuando se usan variables globales que se pueden modificar en cualquier parte de la base de código completa. Considere la variable post
global de WordPress. Puede acceder a él en cualquier parte de su código, y mucha funcionalidad depende de su uso. ¿Qué pasaría si pudiera restringir las modificaciones solo a las funciones principales de WordPress, pero cualquiera pudiera leer? Ocultar o encapsular la variable de post
global en una clase y crear una interfaz a su alrededor lo haría posible.
Esta es solo una descripción muy básica de OOP y cómo se puede usar en el desarrollo moderno de WordPress. Para un estudio más profundo, recomiendo encarecidamente el libro electrónico de Carl Alexander, Descubra la programación orientada a objetos usando WordPress, que tiene el contenido más completo y útil disponible sobre el tema de la programación orientada a objetos en WordPress.
Es importante recordar que la programación orientada a objetos no es una panacea: se puede escribir código incorrecto con programación orientada a objetos tan fácilmente como con cualquier otro paradigma de programación.
Profundicemos en algunos consejos específicos sobre el uso de PHP para el desarrollo de WordPress.
Práctica recomendada moderna de PHP n.° 1: Target PHP 7.0+
El uso de funciones modernas de PHP requiere, bueno, una versión moderna de PHP. Simplemente no hay razón para admitir versiones de PHP anteriores a la 7.0. Incluso el núcleo de WordPress requerirá PHP 7.0 a finales de 2019.
Sin embargo, es una buena práctica verificar su versión mínima para evitar la "pantalla blanca de la muerte" en entornos incompatibles. El fragmento a continuación muestra el uso de un encabezado de complemento para declarar una versión mínima de PHP con una condición de protección en el código.
<?php /** * Plugin Name: My Awesome Plugin * Requires PHP: 7.0 */ // bails if PHP version is lower than required if (version_compare(PHP_VERSION, '7.0.0', '<')) { // add admin notice here return; } // the rest of the actual plugin here
Práctica recomendada moderna de PHP n.º 2: adoptar los estándares de la industria de PHP (Guía de estilo de codificación PSR-2)
Los PSR son recomendaciones publicadas por PHP Framework Interop Group. Son los estándares industriales de facto en cualquier flujo de trabajo PHP moderno, y se puede decir con seguridad que la comunidad PHP en su conjunto sigue estos estándares. PSR-2 es una recomendación que describe el estilo de codificación. Los marcos PHP populares como Symfony y Laravel siguen a PSR-2.
¿Por qué usaría PSR-2 en lugar del estándar de codificación de WordPress? Principalmente porque los estándares de WordPress están obsoletos y no utilizan ninguna de las funciones de lenguaje más nuevas. Eso es comprensible porque el núcleo de WordPress tiene que seguir sus propios estándares. Tenía que ser compatible con PHP 5.2 hasta hace muy poco, y PSR-2 no es compatible con PHP 5.2.
Puede que no sea obvio, pero no hay ningún requisito para usar los estándares de codificación de WordPress a menos que se comprometa con el núcleo. No habría ningún problema con enviar un complemento que siga el estándar PSR-2 al directorio de complementos de WordPress. De hecho, hay muy buenos argumentos para hacerlo.

Práctica recomendada moderna de PHP n.º 3: utilice un motor de plantillas de PHP
PHP no es un motor de plantillas. Comenzó como uno, pero luego evolucionó hasta convertirse en un lenguaje de programación con todas las funciones, y no hay razón para seguir usándolo para crear plantillas. Los dos motores de plantillas más populares para PHP son Twig y Blade, que son utilizados por Symfony y Laravel, respectivamente. Este artículo utilizará Twig como un motor de plantillas de ejemplo; sin embargo, Blade tiene características y funcionalidades comparables. Te imploro que investigues ambos y decidas por ti mismo cuál te conviene más.
El siguiente ejemplo compara una plantilla PHP y su plantilla Twig correspondiente. Mostrar y escapar de la salida es particularmente detallado en el ejemplo de PHP:
foreach ( $options as $option ) { ?> <tr class="form-field"> <th scope="row"> <label for="<?php echo esc_attr( $option->option_name ); ?>"> <?php echo esc_html( strtolower( $option->option_name ) ); ?> </label> </th> </tr> <?php } // End foreach
En Twig, esto es más conciso y legible:
{% for option in options %} <tr class="form-field"> <th scope="row"> <label for="{{ option.option_name }}"> {{ option.option_name }} </label> </th> </tr> {% endfor %}
Las principales ventajas de Twig son:
- Sintaxis legible y concisa
- Salida automática de escape
- Extensión de plantilla a través de herencia y bloques.
En cuanto al rendimiento, Twig compila en plantillas PHP y prácticamente no tiene gastos generales. Twig tiene solo un subconjunto de construcciones de lenguaje PHP limitadas solo a plantillas. Esto obliga a los desarrolladores a eliminar la lógica comercial de las plantillas, lo que impone la separación de preocupaciones.
Incluso existe Twig para WordPress. Se llama Timber y es una excelente manera de comenzar a crear mejores plantillas. El tema inicial de Timber es un ejemplo perfecto de organización de temas al estilo OOP.
Práctica recomendada moderna de PHP n.° 4: Usar Composer
Composer es un administrador de dependencias para PHP. Es una herramienta que permite declarar bibliotecas que un proyecto está usando y luego automatizar sus descargas, instalaciones y actualizaciones. Luego, solo necesita incluir el archivo de carga automática de Composer vendor/autoload.php
en lugar de requerir manualmente cada biblioteca.
Los complementos y temas de WordPress no suelen utilizar bibliotecas de terceros. Esto se debe en parte a que WordPress tiene una API extensa que satisface casi cualquier necesidad y en parte a los posibles conflictos de versión. Considere dos complementos que requieren la misma biblioteca de PHP pero diferentes versiones de la misma. El complemento que se ejecuta primero obtiene la versión correcta y el segundo complemento también obtiene esa versión. Esta es posiblemente otra situación de pantalla blanca de la muerte.
Para evitar conflictos, la gestión de dependencias debe utilizarse a nivel de la aplicación, es decir, el sitio de WordPress como un todo. Esto es lo que hace Roots (Bedrock, más específicamente). Cuando se usa a nivel de paquete (complemento o tema), Composer puede causar conflictos al usar bibliotecas de terceros. Es un problema conocido. La única solución que existe hasta ahora es cambiar el nombre de los espacios de nombres de esa biblioteca externa a algo único, y eso no es una tarea trivial.
Sin embargo, todavía hay un uso para Composer: la carga automática de sus propias clases. Pero antes de continuar con la carga automática, debemos ponernos al día con los espacios de nombres de PHP.
Práctica recomendada moderna de PHP n.º 5: usar espacios de nombres
El núcleo de WordPress es un proyecto heredado, utiliza un espacio de nombres global o, dicho de otra manera, ningún espacio de nombres. Cualquier clase o función declarada globalmente (es decir, no dentro de otra clase o función) es visible en cualquier parte de la base de código completa. Sus nombres deben ser únicos no solo dentro de su base de código, sino también para todos los complementos y temas que se usan ahora o se pueden usar en el futuro.
La colisión de nombres (p. ej., declarar una función con un nombre que ya existe) generalmente conduce a la pantalla blanca de la muerte, y no queremos eso. El Codex de WordPress aconseja prefijar todas sus funciones y clases con algo único. Entonces, en lugar de tener nombres de clase simples como Order
, obtenemos algo como Akrte_Awesome_Plugin_Order
donde "Akrte" es el prefijo único que acabo de inventar.
Los espacios de nombres se pueden ver como grupos, o carpetas, si usamos una analogía con el sistema de archivos, que ayudan a organizar el código y evitan la colisión de nombres. Puede tener espacios de nombres complejos separados por una barra inclinada, como las carpetas anidadas. (Los espacios de nombres de PHP usan una barra invertida en particular).
Esas partes del espacio de nombres se denominan subespacios de nombres. Nuestra clase de ejemplo Akrte_Awesome_Plugin_Order
sería Akrte\Awesome_Plugin\Order
si se hace usando espacios de nombres. Aquí Akrte
y Awesome_Plugin
son partes del espacio de nombres (o subespacios de nombres) y Order
es el nombre de la clase. Luego puede agregar una declaración de use
y usar solo el nombre de la clase después. Definitivamente se ve mejor:
use Akrte\Awesome_Plugin\Order; $a = new Order;
Obviamente, los espacios de nombres deben ser únicos; por lo tanto, debemos dar al primer subespacio de nombres "raíz" un nombre único, y ese suele ser un nombre de proveedor. Como ejemplo, la clase de WooCommerce WC_REST_Order_Notes_V2_Controller
podría rehacerse con espacios de nombres como este:
namespace WooCommerce\RestApi\V2\Controllers; class OrderNotes {}
El código base de WooCommerce hoy en día usa espacios de nombres; por ejemplo, en la versión 4 de la API REST de WooCommerce.
Práctica recomendada moderna de PHP n.º 6: utilice un cargador automático
En la mayoría de los flujos de trabajo de PHP, la forma habitual de vincular archivos PHP es mediante el uso de instrucciones require
o include
. A medida que crece un proyecto, obtiene docenas de declaraciones require
en su archivo de complemento principal. Un cargador automático automatiza la inclusión de archivos y lo hace solo cuando es necesario. Técnicamente, es una función que require
un archivo que contenga una clase o una función cuando se encuentra por primera vez en el código. Ya no hay necesidad de agregar declaraciones require
manualmente.
A menudo, también hay una mejora significativa en el rendimiento, ya que un cargador automático solo carga los módulos que se usan en una solicitud en particular. Sin un cargador automático, se incluye todo el código base incluso si una solicitud usa solo, digamos, el 10 por ciento de su código.
Una función de cargador automático necesita saber en qué archivos viven sus clases y funciones. Y existe un estándar PHP-FIG, PSR-4, para eso.
Dice que una parte de un espacio de nombres, el prefijo, está designado para corresponder a una carpeta base. Los subespacios de nombres que le siguen corresponden a carpetas dentro de la carpeta base. Finalmente, el nombre de la clase corresponde al nombre del archivo. Una clase de ejemplo Akrte\AwesomePlugin\Models\Order
necesitaría la siguiente estructura de carpetas:
/awesome-plugin awesome-plugin.php /includes /Models Order.php
El prefijo del espacio de nombres Akrte\AwesomePlugin\
corresponde a la carpeta de includes
como se especifica en la configuración del cargador automático que se analiza a continuación. El subespacio de nombres de Models
tiene una carpeta de Models
correspondiente, y la clase Order
está contenida en Order.php
.
Afortunadamente, no hay necesidad de implementar una función de cargador automático usted mismo. Composer puede crear un cargador automático para usted:
- Instalar compositor
- Cree un archivo
composer.json
en la carpeta raíz de su proyecto. Debe contener estas líneas:
{ "name": "vendor-name/plugin-name", "require": {}, "autoload": { "psr-4": { "Akrte\\AwesomePlugin\\": "includes/" } } }
- Ejecute la
composer install
. - Incluya
vendor/autoload.php
en la parte superior de su archivo PHP de complemento principal como este:
<?php /** * Plugin Name: My Awesome Plugin */ defined('ABSPATH') || exit; require __DIR__ . '/vendor/autoload.php'; // do stuff
Además de los espacios de nombres, el código base más reciente de WooCommerce también utiliza un cargador automático Composer.
Con estos principios de diseño de PHP cubiertos, es hora de vincular todas nuestras lecciones de PHP con la personalización del back-end de WordPress con una recomendación final.
Mejor práctica moderna de desarrollo de WordPress #4: Considere usar la pila de raíces
Roots es el flujo de trabajo de desarrollo de WordPress moderno más completo que existe. Sin embargo, diría que no necesariamente debería usarse en todos los proyectos de WordPress, porque:
- Roots tiene que ser utilizado desde el principio. La razón más común, en realidad. Refactorizar un proyecto existente sería demasiado costoso.
- Es obstinado. Bueno cuando estás de acuerdo, malo cuando no lo estás. Es posible que prefiera otras formas de organizar su tema, por ejemplo. Los proyectos de opinión también requieren tiempo para aprender “a su manera”.
- No todo el mundo lo sabe. El desarrollador que vendrá detrás de ti para mantener el sitio que creaste con la pila Roots puede no tener idea de qué es y se preguntará qué pasó con las carpetas de WordPress. Deberíamos pensar en nuestros compañeros desarrolladores de WordPress.
En general, le gustaría comprender completamente todos los beneficios y los inconvenientes de cualquier proyecto fuertemente opinado antes de comprometerse a usarlo.
Principios modernos de PHP y software: hacer que el desarrollo back-end de WordPress sea sólido
Es bastante obvio que no existe una forma verdadera de escribir software. Los conceptos, como la separación de intereses, tienen décadas de antigüedad; sin embargo, lo que significa en la práctica siempre ha sido cuestionado. Tomemos, por ejemplo, CSS. Al principio, lo alineamos como style
en HTML, luego decidimos que las hojas CSS separadas son de lo que se trata la separación de preocupaciones.
Avance rápido una década: las aplicaciones de JavaScript actuales usan componentes como una implementación de separación de preocupaciones. Los desarrolladores front-end gravitan hacia CSS-in-JS, y eso básicamente significa insertar CSS en HTML nuevamente (bueno, no es tan simple, pero entiendes la idea). ¡El círculo está completo!
Las mejores prácticas siempre se han centrado en mejorar la experiencia del desarrollador:
Los programas deben estar escritos para que la gente los lea, y solo de manera incidental para que las máquinas los ejecuten.
Abelson & Sussman, Estructura e interpretación de programas informáticos
Algunas de las prácticas en este tutorial de PHP WordPress son rápidas y fáciles de implementar en su proyecto. Por ejemplo, cargador automático: hágalo una vez por proyecto y disfrute. Por otro lado, las nuevas ideas de arquitectura de software necesitan tiempo, práctica y numerosas iteraciones para volverse prácticas y cómodas. Sin embargo, las recompensas son mucho mayores. No solo será más eficiente en lo que hace, sino que también disfrutará más de lo que hace. Y el disfrute regular del trabajo que hace para los clientes es quizás la única forma en que puede ser sostenible.