Aprender a crear un plugin de WordPress es el paso que separa a quien usa la plataforma de quien realmente la domina. Un plugin no es más que un conjunto de archivos PHP que WordPress carga junto al núcleo y que puede modificar o ampliar casi cualquier comportamiento del sistema: añadir funciones, cambiar textos, crear tipos de contenido o conectar la web con servicios externos. La buena noticia es que la barrera de entrada es mucho más baja de lo que parece: con un archivo, una cabecera de comentario y unas nociones básicas de hooks ya tienes un plugin funcional. En esta guía vamos a construir uno desde cero, paso a paso y con código completo que puedes copiar, probar y ampliar. Veremos la estructura de archivos recomendada, cómo funcionan los actions y los filters, cómo añadir una página de ajustes y qué buenas prácticas de seguridad debes aplicar desde el primer día para que tu código sea sólido y mantenible.
Por qué crear un plugin de WordPress y no tocar el tema
Antes de escribir una sola línea conviene entender dónde debe vivir cada tipo de código en WordPress. La regla general es sencilla: el tema controla cómo se ve tu web y los plugins controlan qué hace tu web. Si añades funcionalidad al archivo functions.php de tu tema, esa funcionalidad desaparecerá en cuanto cambies de tema o, peor aún, en cuanto el tema se actualice y sobrescriba tus cambios.
Un plugin, en cambio, es independiente del diseño. Puedes activarlo, desactivarlo, moverlo a otra instalación o publicarlo en el directorio oficial. Estas son las situaciones típicas en las que la respuesta correcta es crear un plugin de WordPress propio:
- Necesitas una función concreta (un shortcode, un aviso, una integración) que no justifica instalar un plugin comercial enorme.
- Quieres que la funcionalidad sobreviva a un cambio de tema.
- Vas a reutilizar el mismo código en varias webs de clientes.
- Un plugin existente hace casi lo que quieres, pero no exactamente, y prefieres controlar el código.
Para fragmentos muy pequeños existe una alternativa intermedia: los gestores de snippets. Si solo necesitas pegar diez líneas de PHP, quizá te interese leer antes nuestro análisis de WPCode, el gestor de snippets de WordPress. Pero en cuanto el código crece o necesita organización, el plugin propio gana por goleada.
Qué necesitas antes de empezar
El kit de herramientas es mínimo. No hace falta ningún programa de pago ni un entorno complicado:
- Una instalación de WordPress de pruebas. Nunca desarrolles directamente en producción. Un entorno local con Local WP, XAMPP o similar es ideal.
- Un editor de código. Visual Studio Code es la opción más popular y gratuita.
- Acceso a los archivos. En local lo tienes directo; en un servidor, por SFTP o el gestor de archivos del hosting.
- Nociones básicas de PHP. Variables, funciones y arrays son suficientes para empezar.
También conviene activar el modo de depuración mientras desarrollas. Añade esto a tu wp-config.php del entorno de pruebas:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
Con esta configuración los errores y avisos se guardan en wp-content/debug.log en lugar de mostrarse en pantalla, lo que te permitirá cazar problemas sin romper la experiencia de navegación.
La estructura mínima: carpeta, archivo y cabecera
Un plugin puede ser un único archivo PHP dentro de wp-content/plugins, pero la práctica recomendada es crear una carpeta propia desde el principio. Para nuestro ejemplo construiremos un plugin real y útil: un aviso personalizable en la parte superior de la web, con página de ajustes incluida. La estructura será esta:
wp-content/plugins/bw-aviso-superior/
├── bw-aviso-superior.php (archivo principal)
├── includes/
│ └── class-bw-aviso.php (lógica del plugin)
└── assets/
└── css/
└── aviso.css (estilos del aviso)
Lo único imprescindible para que WordPress reconozca el plugin es la cabecera de plugin: un bloque de comentario al inicio del archivo principal. Crea bw-aviso-superior.php con este contenido:
<?php
/**
* Plugin Name: BW Aviso Superior
* Plugin URI: https://biblioweb.es/
* Description: Muestra una barra de aviso personalizable en la parte superior de la web.
* Version: 1.0.0
* Requires at least: 6.0
* Requires PHP: 7.4
* Author: BiblioWeb
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: bw-aviso-superior
*/
// Seguridad: impedir el acceso directo al archivo.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
define( 'BW_AVISO_VERSION', '1.0.0' );
define( 'BW_AVISO_PATH', plugin_dir_path( __FILE__ ) );
define( 'BW_AVISO_URL', plugin_dir_url( __FILE__ ) );
require_once BW_AVISO_PATH . 'includes/class-bw-aviso.php';
// Arrancar el plugin cuando WordPress haya cargado los plugins.
add_action( 'plugins_loaded', array( 'BW_Aviso', 'init' ) );
Con solo este archivo (y la clase que veremos ahora), el plugin ya aparecerá en el listado de Complementos del escritorio, listo para activarse. Fíjate en la comprobación de ABSPATH: evita que alguien ejecute el archivo directamente escribiendo su URL, una medida de seguridad básica que debe abrir todos tus archivos PHP.
Hooks: el corazón de cualquier plugin
WordPress está construido sobre un sistema de hooks (ganchos) que permite a tu código engancharse a momentos concretos de la ejecución. Sin hooks no hay plugin: son el mecanismo oficial para intervenir sin modificar el núcleo. Existen dos tipos:
Actions: hacer algo en un momento dado
Un action ejecuta tu función cuando ocurre un evento: WordPress termina de cargar, se publica un post, se pinta el pie de página. Se usan con add_action():
add_action( 'wp_footer', 'bw_mensaje_en_footer' );
function bw_mensaje_en_footer() {
echo '<!-- Generado por BW Aviso Superior -->';
}
Filters: modificar un dato antes de que se use
Un filter recibe un valor, lo transforma y lo devuelve. WordPress lo usa para todo: el título de un post, el contenido, la longitud del extracto. Se usan con add_filter():
add_filter( 'excerpt_length', 'bw_extracto_corto' );
function bw_extracto_corto( $length ) {
return 25; // palabras del extracto
}
La diferencia clave: un action hace cosas y no devuelve nada; un filter siempre debe devolver el valor (modificado o no). Olvidar el return en un filter es uno de los errores más comunes al empezar y puede dejar textos vacíos por toda la web.
El plugin completo: clase principal con ajustes y salida
Vamos ahora con la pieza central. Crea includes/class-bw-aviso.php con la clase que registra los ajustes, pinta la página de opciones y muestra el aviso en la parte pública:
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class BW_Aviso {
const OPTION = 'bw_aviso_opciones';
public static function init() {
$instancia = new self();
// Parte pública.
add_action( 'wp_body_open', array( $instancia, 'mostrar_aviso' ) );
add_action( 'wp_enqueue_scripts', array( $instancia, 'cargar_estilos' ) );
// Administración.
add_action( 'admin_menu', array( $instancia, 'registrar_pagina_ajustes' ) );
add_action( 'admin_init', array( $instancia, 'registrar_ajustes' ) );
}
public function mostrar_aviso() {
$opciones = get_option( self::OPTION );
if ( empty( $opciones['activo'] ) || empty( $opciones['texto'] ) ) {
return;
}
printf(
'<div class="bw-aviso-superior">%s</div>',
esc_html( $opciones['texto'] )
);
}
public function cargar_estilos() {
$opciones = get_option( self::OPTION );
if ( empty( $opciones['activo'] ) ) {
return;
}
wp_enqueue_style(
'bw-aviso-superior',
BW_AVISO_URL . 'assets/css/aviso.css',
array(),
BW_AVISO_VERSION
);
}
public function registrar_pagina_ajustes() {
add_options_page(
'Aviso Superior',
'Aviso Superior',
'manage_options',
'bw-aviso-superior',
array( $this, 'render_pagina_ajustes' )
);
}
public function registrar_ajustes() {
register_setting(
'bw_aviso_grupo',
self::OPTION,
array( 'sanitize_callback' => array( $this, 'sanear_opciones' ) )
);
}
public function sanear_opciones( $entrada ) {
return array(
'activo' => ! empty( $entrada['activo'] ) ? 1 : 0,
'texto' => isset( $entrada['texto'] )
? sanitize_text_field( $entrada['texto'] )
: '',
);
}
public function render_pagina_ajustes() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$opciones = get_option( self::OPTION, array( 'activo' => 0, 'texto' => '' ) );
?>
<div class="wrap">
<h1>Aviso Superior</h1>
<form method="post" action="options.php">
<?php settings_fields( 'bw_aviso_grupo' ); ?>
<table class="form-table">
<tr>
<th scope="row">Mostrar aviso</th>
<td>
<label>
<input type="checkbox"
name="<?php echo esc_attr( self::OPTION ); ?>[activo]"
value="1" <?php checked( 1, $opciones['activo'] ); ?> />
Activar la barra de aviso
</label>
</td>
</tr>
<tr>
<th scope="row">Texto del aviso</th>
<td>
<input type="text" class="regular-text"
name="<?php echo esc_attr( self::OPTION ); ?>[texto]"
value="<?php echo esc_attr( $opciones['texto'] ); ?>" />
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
<?php
}
}
Y por último los estilos, en assets/css/aviso.css:
.bw-aviso-superior {
background: #1d2327;
color: #ffffff;
text-align: center;
padding: 10px 16px;
font-size: 15px;
}
Activa el plugin, ve a Ajustes → Aviso Superior, marca la casilla, escribe un texto y guarda. La barra aparecerá en la parte superior de tu web. Ya has conseguido crear un plugin de WordPress completo: con página de ajustes, opciones guardadas en base de datos, estilos propios y salida en la parte pública.
Buenas prácticas que marcan la diferencia
El ejemplo anterior ya aplica varias reglas que deberías interiorizar. Repasémoslas junto a otras igual de importantes:
Sanea la entrada y escapa la salida
Todo dato que entra (formularios, URL, APIs) se limpia con funciones como sanitize_text_field(), y todo dato que sale a pantalla se escapa con esc_html(), esc_attr() o esc_url(). Esta pareja de costumbres previene la inmensa mayoría de vulnerabilidades XSS. Si te interesa el enfoque defensivo, tenemos una guía con 7 trucos para reforzar la seguridad de tu WordPress que complementa muy bien lo que haces a nivel de código.
Prefijos únicos en todo
Funciones, clases, opciones y handles de estilos deben llevar un prefijo propio (en nuestro caso bw_ y BW_). PHP no permite dos funciones con el mismo nombre: si tu plugin declara enviar_email() y otro plugin también, la web caerá con un error fatal.
Comprueba capacidades y usa nonces
Antes de mostrar u operar en páginas de administración, verifica permisos con current_user_can(). En formularios propios, añade nonces con wp_nonce_field() y verifícalos al procesar. La API de ajustes que usamos (settings_fields()) ya gestiona el nonce por ti, otra razón para preferirla a procesar formularios a mano.
Carga recursos solo cuando hagan falta
Nuestro CSS solo se encola si el aviso está activo. Aplica el mismo criterio siempre: un plugin que carga scripts en todas las páginas sin necesidad penaliza el rendimiento de la web entera. De hecho, muchos de los problemas de velocidad que se atribuyen a WordPress son en realidad plugins mal escritos; en la guía sobre cómo acelerar WordPress con ajustes de PHP puedes ver el impacto real de estas decisiones.
Cómo probar y depurar tu plugin
Con WP_DEBUG activado, tu flujo de trabajo será: guardar el archivo, recargar la página y revisar wp-content/debug.log si algo no funciona. Algunos consejos adicionales:
- Prueba la activación y desactivación. Activa y desactiva el plugin varias veces comprobando que no lanza avisos.
- Prueba con otros plugins activos. Los conflictos entre plugins son la principal fuente de incidencias reales.
- Prueba con distintos usuarios. Entra con un editor o un suscriptor y confirma que no ven la página de ajustes.
- Usa
error_log()como chivato. Escribirerror_log( print_r( $variable, true ) );en un punto del código te muestra el contenido de cualquier variable en el log.
Si tu plugin va a manejar datos estructurados (por ejemplo respuestas de una API en JSON), una herramienta como el formateador y validador JSON de BiblioWeb te ahorrará tiempo al inspeccionar y validar esas respuestas durante el desarrollo.
Siguientes pasos: crecer con orden
A partir de esta base puedes ampliar el plugin en muchas direcciones: añadir un selector de color para la barra, programar fechas de inicio y fin del aviso, crear un shortcode o exponer las opciones en la REST API. Cuando el proyecto crezca, mantén la disciplina de estructura: la lógica en includes/, los recursos en assets/, y el archivo principal solo como punto de entrada.
La referencia obligada para profundizar es el Plugin Handbook oficial de WordPress, que documenta desde los hooks disponibles hasta el proceso de publicación en el directorio. Y si quieres ver cómo resuelven las cosas los grandes, nada mejor que leer el código de plugins consolidados: los que repasamos en los 5 plugins esenciales de WordPress para principiantes son un buen punto de partida para estudiar estructura y estilo.
Preguntas frecuentes
¿Necesito saber mucho PHP para crear un plugin de WordPress?
No para empezar. Con variables, funciones, arrays y condicionales puedes construir plugins útiles como el de esta guía. El propio desarrollo te irá llevando a conceptos más avanzados (clases, namespaces, APIs) de forma natural y progresiva.
¿Dónde se guardan los plugins en WordPress?
En la carpeta wp-content/plugins de tu instalación. Cada plugin ocupa su propia subcarpeta (o un único archivo PHP en casos muy simples). WordPress detecta automáticamente cualquier archivo con una cabecera de plugin válida dentro de esa ruta.
¿Puedo romper mi web al desarrollar un plugin?
Un error de sintaxis PHP en un plugin activo puede tumbar la web, por eso se desarrolla siempre en un entorno de pruebas. Si ocurre en producción, basta con renombrar la carpeta del plugin por SFTP para desactivarlo y recuperar el sitio al instante.
¿Cómo publico mi plugin en el directorio oficial?
Debes cumplir las directrices del directorio (licencia GPL, código seguro, sin ofuscación), preparar un archivo readme.txt y enviarlo a revisión desde wordpress.org. Tras la aprobación, recibes acceso a un repositorio SVN desde el que se distribuyen las versiones.
Conclusión
Crear un plugin de WordPress no es territorio exclusivo de programadores veteranos: es una carpeta, una cabecera y un puñado de hooks bien elegidos. En esta guía has construido uno completo, con página de ajustes, saneamiento de datos, estilos condicionales y las buenas prácticas de seguridad que usan los plugins profesionales. El salto de calidad respecto a pegar código en el tema es enorme: tu funcionalidad ahora es portable, actualizable y está aislada del diseño. El mejor consejo final es simple: elige un problema pequeño y real de tu propia web y resuélvelo con un plugin. No hay mejor escuela que mantener código propio funcionando en un sitio de verdad.