//Desarrollo web a medida

Cómo crear un plugin de WordPress desde cero

Para crear un plugin de WordPress desde cero basta un archivo PHP con una cabecera de seis líneas. Vemos la estructura mínima, cómo funcionan las acciones y los filtros, y qué errores evitar antes de subirlo a producción.

Editor de código con un archivo PHP abierto durante el desarrollo de un plugin de WordPress

Este tutorial explica cómo se programa un plugin de WordPress desde cero. Si lo que buscas es encargarlo, desarrollamos módulos y conectores a medida sobre WordPress, WooCommerce y PrestaShop.

Para crear un plugin de WordPress desde cero no hace falta un framework ni una estructura de carpetas complicada: basta un archivo PHP con una cabecera de seis líneas. Esa es la respuesta corta, y conviene tenerla delante antes de empezar, porque la mayoría de tutoriales arrancan montando un andamiaje que todavía no necesitas.

Antes de programar un plugin de WordPress: ¿de verdad hace falta?

En el repositorio oficial hay decenas de miles de plugins. Antes de escribir una línea, busca. Si existe uno mantenido que hace lo que necesitas, usarlo casi siempre sale mejor: alguien más lo parchea cuando aparece un fallo de seguridad.

Programar el tuyo compensa en tres casos concretos:

  • No existe. La funcionalidad es específica de tu negocio: un cálculo propio, una integración con tu ERP, un tipo de contenido que sólo tiene sentido en tu sector.
  • Existe pero trae diez veces más de lo que necesitas. Instalar un plugin de cuatro megas para añadir un campo a un formulario es pagar con rendimiento algo que se resuelve en treinta líneas.
  • Existe y está abandonado. Última actualización hace tres años y avisos de incompatibilidad en la ficha. Ahí el plugin no es una solución, es una deuda.

Y hay un caso en el que un plugin es la herramienta equivocada: si lo que quieres cambiar es cómo se ve el sitio, eso va en el tema o en un tema hijo, no en un plugin. La regla es vieja pero sigue valiendo: el tema es la apariencia, el plugin es el comportamiento. Si desactivas el plugin y se rompe el diseño, algo has puesto en el sitio que no era.

Dónde vive un plugin de WordPress y cómo debe llamarse

Los plugins van en wp-content/plugins. Crea ahí una carpeta con el nombre del tuyo y, dentro, un archivo PHP con ese mismo nombre:

wp-content/plugins/
└── airearte-avisos/
    └── airearte-avisos.php

Usa guiones y minúsculas, y prefija absolutamente todo: la carpeta, el archivo, las funciones, las clases, las constantes y las opciones que guardes en la base de datos. WordPress carga todos los plugins activos en el mismo espacio de nombres global, así que si tú declaras una función enviar_aviso() y otro plugin declara otra igual, PHP aborta con un error fatal y el sitio se cae entero. No es un riesgo teórico: es la causa más común de la pantalla en blanco al activar.

La cabecera es lo único que WordPress necesita para reconocerlo

Un archivo PHP se convierte en plugin en cuanto empieza por un comentario con esta forma:

<?php
/**
 * Plugin Name:       Airearte Avisos
 * Description:       Muestra un aviso configurable encima del contenido.
 * Version:           1.0.0
 * Requires at least: 6.0
 * Requires PHP:      8.0
 * Author:            Airearte
 * Author URI:        https://airearte.es
 * License:           GPL-2.0-or-later
 * Text Domain:       airearte-avisos
 */

WordPress lee ese bloque y con él pinta la fila del plugin en el panel. Sólo Plugin Name es obligatorio, pero merece la pena rellenar dos que casi nadie pone: Requires at least y Requires PHP. Con ellas, WordPress se niega a activar el plugin en una instalación que no cumple los requisitos, en lugar de dejar que reviente a mitad.

Cierra la puerta al acceso directo

Justo después de la cabecera, en todos los archivos PHP del plugin:

defined( 'ABSPATH' ) || exit;

ABSPATH la define WordPress al arrancar. Si alguien pide tu archivo directamente por URL, esa constante no existe y el script termina sin ejecutar nada. Es una línea y evita toda una familia de problemas.

Las rutas las calcula WordPress, no las escribas a mano

Aquí es donde se cuela un error clásico: escribir la ruta del plugin concatenando ABSPATH con 'wp-content/plugins/mi-plugin'. Funciona en tu portátil y falla en cuanto el sitio tiene wp-content movido de sitio, que es algo que se hace a menudo por seguridad. Usa las funciones que existen para esto:

define( 'AIREARTE_AVISOS_DIR', plugin_dir_path( __FILE__ ) );
define( 'AIREARTE_AVISOS_URL', plugin_dir_url( __FILE__ ) );

La primera te da la ruta en disco y sirve para hacer require de otros archivos. La segunda te da la URL pública y sirve para encolar hojas de estilo, scripts o imágenes. No son intercambiables, y confundirlas produce el fallo más desconcertante del principiante: una imagen que no carga y una ruta del servidor asomando en el HTML.

Portátil mostrando en el editor el código PHP de un plugin de WordPress
Los hooks son el mecanismo por el que WordPress llama a tu plugin, y no al revés.

Los hooks de WordPress: cómo entra tu código en la plataforma

WordPress no llama a tu plugin. Tu plugin le dice a WordPress cuándo quiere que lo llamen. Ese mecanismo son los hooks, y hay de dos tipos.

Las acciones ejecutan código en un momento concreto

Una acción es un punto del ciclo de carga donde puedes colgar una función tuya. No devuelve nada: hace algo.

add_action( 'wp_enqueue_scripts', 'airearte_avisos_estilos' );

function airearte_avisos_estilos() {
    wp_enqueue_style(
        'airearte-avisos',
        AIREARTE_AVISOS_URL . 'css/avisos.css',
        array(),
        '1.0.0'
    );
}

Fíjate en el hook elegido. Para cargar CSS es wp_enqueue_scripts, no wp_head. Escribir un <link> a pelo en wp_head funciona, pero se salta el sistema de dependencias y versiones de WordPress, y acabas con el mismo estilo cargado dos veces y sin forma de invalidar la caché del navegador. Casi siempre existe un hook más específico que el que se te ocurre primero, y usar el correcto es la diferencia entre un plugin que convive con los demás y uno que pelea con ellos.

Los filtros modifican un valor antes de que se use

Un filtro recibe algo, lo cambia y lo devuelve. Esa última parte es obligatoria:

add_filter( 'the_content', 'airearte_avisos_contenido' );

function airearte_avisos_contenido( $contenido ) {
    if ( ! is_singular( 'post' ) || ! is_main_query() ) {
        return $contenido;
    }

    $aviso = get_option( 'airearte_avisos_texto', '' );

    if ( '' === $aviso ) {
        return $contenido;
    }

    return '<p class="aviso">' . esc_html( $aviso ) . '</p>' . $contenido;
}

Si olvidas el return, la función devuelve null y borras el contenido de todas las entradas del sitio. Es el fallo más frecuente con filtros y también el más aparatoso.

Ese ejemplo hace además otras dos cosas que conviene copiar. La primera, comprobar dónde estás: the_content se dispara también en los listados, en los feeds y en cualquier consulta secundaria, así que sin la condición el aviso sale multiplicado por toda la portada. La segunda, esc_html() al sacar un valor guardado. Todo lo que venga de la base de datos o de un formulario se escapa al imprimirlo, sin excepciones.

Prioridad y número de argumentos

Tanto add_action() como add_filter() aceptan dos parámetros más:

add_filter( 'the_content', 'airearte_avisos_contenido', 20, 1 );

El tercero es la prioridad: por defecto 10, y cuanto más bajo, antes se ejecuta. Súbelo cuando necesites actuar después de otro plugin que toca lo mismo. El cuarto es el número de argumentos que quieres recibir. Por defecto es 1, y si el hook pasa tres y tú no lo declaras, los otros dos no llegan: te encuentras con una variable vacía sin ningún error que lo explique.

Activación y desactivación del plugin

Si tu plugin necesita preparar algo la primera vez —una opción por defecto, una tabla, una tarea programada—, ese código no va suelto en el archivo, porque se ejecutaría en cada carga de cada página. Va en el hook de activación, que corre una sola vez:

register_activation_hook( __FILE__, 'airearte_avisos_activar' );

function airearte_avisos_activar() {
    add_option( 'airearte_avisos_texto', '' );
}

register_deactivation_hook( __FILE__, 'airearte_avisos_desactivar' );

function airearte_avisos_desactivar() {
    wp_clear_scheduled_hook( 'airearte_avisos_tarea' );
}

Al desactivar se limpia lo que consume recursos —tareas programadas, cachés—, pero no se borran los datos del usuario. Mucha gente desactiva un plugin para descartar un problema y lo vuelve a activar cinco minutos después; si al desactivarlo has borrado su configuración, te has ganado una reseña de una estrella. El borrado definitivo va en uninstall.php, que WordPress ejecuta sólo cuando se elimina el plugin de verdad.

Lo que aquí no hemos visto

Con lo anterior tienes un plugin funcional. Para uno que vaya a producción faltan cuatro cosas, y merece la pena saber que existen:

  • Página de ajustes. En cuanto haya un formulario en el panel, necesitas nonces para verificar que la petición viene de tu formulario y current_user_can() para comprobar que quien la manda tiene permiso. Sin las dos, cualquier usuario registrado puede cambiar la configuración.
  • Saneado a la entrada. Escapar a la salida no basta: lo que llega de un formulario se sanea antes de guardarlo, con sanitize_text_field(), wp_kses_post() o la función que corresponda al tipo de dato.
  • Traducciones. Envuelve los textos visibles en __() o esc_html__() con tu text domain desde el principio. Añadirlas después obliga a repasar el plugin entero.
  • Clases en vez de funciones sueltas. Pasadas dos o tres funcionalidades, agrupar en una clase con prefijo ahorra la mitad de los conflictos de nombres.

Los cinco fallos más comunes al programar un plugin

  1. No prefijar. Colisión de nombres y error fatal al activar.
  2. Un filtro sin return. Se vacía el contenido del sitio.
  3. Escribir rutas a mano en lugar de plugin_dir_path() y plugin_dir_url().
  4. No escapar a la salida. Cualquier valor guardado que se imprime sin esc_html() o equivalente es una puerta abierta.
  5. Modificar el plugin directamente en producción. Un error de sintaxis deja el sitio en blanco y el editor del panel ya no está disponible para arreglarlo.

Y cuándo dejar de programarlo tú

Un plugin propio es código que alguien tiene que mantener cuando WordPress publique la siguiente versión mayor, cuando cambie el mínimo de PHP o cuando el plugin con el que convive se actualice. Si lo que necesitas toca pagos, datos personales o la sincronización con otro sistema, el coste no está en escribirlo: está en los años siguientes.

Esa es justamente la parte que cubrimos nosotros en módulos y conectores a medida. Y si no lo tienes claro, cuéntanos qué quieres resolver: si lo cubre un plugin del repositorio, te lo diremos.

Publicado en diciembre de 2016 como serie en dos partes. Refundido y revisado en agosto de 2026.

//Artículos relacionados
  • Cómo crear un módulo de PrestaShop desde cero

    Desarrollo web a medida

    Cómo crear un módulo de PrestaShop desde cero

    Vamos a crear un módulo de PrestaShop completo y funcional: un bloque de HTML editable desde el back-office que se muestra en la portada de la tienda. Instalar, configurar, guardar y mostrar: con esas cuatro piezas se hacen casi todos los demás.

    Leer artículo
  • Software de gestión en la nube o en tu servidor: qué cambia de verdad

    Desarrollo web a medida

    Software de gestión en la nube o en tu servidor: qué cambia de verdad

    La pregunta ya no es si mover el programa de gestión a internet: casi todo está ahí. La útil es otra, y casi nadie la hace a tiempo: quién administra el sistema y dónde viven tus datos.

    Leer artículo