Infografía con los pasos para crear un plugin en WordPress, desde la carpeta y el archivo PHP principal hasta su activación y personalización.

Cómo crear un plugin de WordPress desde cero: guía completa

Un plugin de WordPress es la forma correcta de añadir funcionalidad a una web sin tocar el núcleo ni el tema. Con un archivo PHP, una cabecera y unos cuantos hooks ya tienes un plugin funcionando. En esta guía construimos uno completo desde cero: shortcode, página de ajustes, seguridad, internacionalización y empaquetado final.

Está pensada para WordPress 7.x y PHP 8.x. Todo el código que verás se puede copiar y probar en una instalación local o de staging.

Resumen rápido

  • Un plugin es una carpeta dentro de wp-content/plugins/ con al menos un archivo PHP y una cabecera de comentario.
  • La cabecera (Plugin Name, Version, Text Domain…) es lo que WordPress lee para listarlo en el panel.
  • Todo el código se engancha con acciones y filtros: no se ejecuta «suelto».
  • Valida siempre: ABSPATH, capacidades, nonces, sanitización al guardar y escaping al mostrar.
  • Guarda los datos con la Settings API o con tablas propias creadas con dbDelta().
  • Empaqueta en ZIP, pruébalo en staging y documenta el readme.txt.

1. Qué es un plugin y cuándo crearlo

Un plugin es un paquete de código que se engancha al ciclo de vida de WordPress para añadir o modificar funcionalidad. Se carga después del núcleo y antes del tema, así que puede intervenir en casi cualquier parte de la ejecución: contenido, panel, correos, API REST, tareas programadas o la base de datos.

La regla es sencilla: si el cambio debe sobrevivir a un cambio de tema, va en un plugin. Si es puramente visual y depende del tema, va en un tema hijo. Y si es una modificación del núcleo, casi siempre hay un hook para hacerlo sin tocarlo.

NecesidadDónde va
Añadir un tipo de contenido, un shortcode o una integraciónPlugin propio
Cambiar estilos, plantillas o layoutTema hijo
Ajuste pequeño y atado al temafunctions.php del tema hijo
Cambiar comportamiento del núcleoPlugin con hooks, nunca editando wp-includes/
Funcionalidad crítica que no debe desactivarseMu-plugin en wp-content/mu-plugins/

Versión: esta guía se centra en WordPress 7.x y PHP 8.0 o superior. Las APIs que usamos (Settings API, Shortcode API, REST API, i18n) son estables desde hace años y siguen siendo el estándar.

2. Requisitos y entorno de desarrollo

Antes de escribir la primera línea necesitas un entorno donde probar sin miedo. Puedes usar WordPress local con LocalWP, Docker, XAMPP o un staging del hosting.

  • WordPress 7.x actualizado.
  • PHP 8.0 o superior, con WP_DEBUG y WP_DEBUG_LOG activos.
  • Un editor con soporte PHP (VS Code, PhpStorm) y la extensión de estándares de WordPress.
  • WP-CLI para activar, desactivar y probar el plugin desde la terminal.
  • Un repositorio Git: aunque el plugin sea pequeño, el historial te salva.
// wp-config.php (solo en desarrollo)
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'SCRIPT_DEBUG', true );

Los avisos PHP aparecerán en wp-content/debug.log. Si tu plugin genera un warning, lo verás ahí antes de que lo vea un visitante.

3. Estructura mínima de un plugin

Un plugin puede ser un único archivo PHP dentro de wp-content/plugins/. Para algo serio, conviene una carpeta con esta estructura:

wp-content/plugins/solusen-avisos/
├── solusen-avisos.php        # Archivo principal con la cabecera
├── includes/
│   ├── shortcode.php         # Lógica del shortcode
│   └── settings.php          # Página de ajustes
├── assets/
│   └── aviso.css             # Estilos del front
├── languages/
│   └── solusen-avisos-es_ES.po
├── readme.txt                # Documentación y changelog
└── uninstall.php             # Limpieza al desinstalar

Las carpetas no son obligatorias, pero separar responsabilidades evita que el archivo principal se convierta en un monolito de mil líneas. El nombre de la carpeta y del archivo principal deben coincidir para que el plugin se pueda distribuir limpiamente.

Convención de nombres: usa un prefijo único (solusen_avisos_, Solusen_Avisos) en funciones, clases, opciones y hooks. Es la mejor defensa contra colisiones con otros plugins.

4. La cabecera del plugin

WordPress no ejecuta el plugin hasta que encuentra la cabecera en el archivo principal. Es un comentario con campos fijos que el panel lee para mostrar nombre, versión, autor y compatibilidad.

<?php
/**
 * Plugin Name:       Solusen Avisos
 * Plugin URI:        https://solusen.es/
 * Description:       Añade un shortcode de avisos configurables y una página de ajustes.
 * Version:           1.0.0
 * Requires at least: 6.5
 * Requires PHP:      8.0
 * Author:            Solusen
 * Author URI:        https://solusen.es/
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       solusen-avisos
 * Domain Path:       /languages
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

define( 'SOLUSEN_AVISOS_VERSION', '1.0.0' );
define( 'SOLUSEN_AVISOS_FILE', __FILE__ );
define( 'SOLUSEN_AVISOS_DIR', plugin_dir_path( __FILE__ ) );

require_once SOLUSEN_AVISOS_DIR . 'includes/shortcode.php';
require_once SOLUSEN_AVISOS_DIR . 'includes/settings.php';

La comprobación de ABSPATH impide que alguien llame al archivo directamente por URL y ejecute código sin cargar WordPress. Es obligatoria en todos los archivos PHP del plugin, no solo en el principal.

Campos de cabecera más importantes

CampoPara qué sirve
Plugin NameNombre visible en el listado de plugins. Obligatorio.
VersionVersión actual. Se usa para caché de assets y actualizaciones.
Requires at least / Requires PHPRequisitos mínimos; WordPress avisa si no se cumplen.
Text Domain / Domain PathDominio de traducción y carpeta de los .po/.mo.
LicenseLicencia del código; GPL-2.0-or-later es lo habitual en WordPress.

5. Activación, desactivación y desinstalación

WordPress ofrece hooks para reaccionar al ciclo de vida del plugin. Son el lugar correcto para crear opciones por defecto, tablas o tareas programadas, y para limpiar cuando el plugin se desinstala.

Al activar

register_activation_hook( __FILE__, 'solusen_avisos_activar' );

function solusen_avisos_activar() {
    if ( false === get_option( 'solusen_avisos_opciones' ) ) {
        add_option( 'solusen_avisos_opciones', array(
            'mensaje' => 'Bienvenido a nuestra web.',
            'tipo'    => 'info',
        ) );
    }

    if ( ! wp_next_scheduled( 'solusen_avisos_cron' ) ) {
        wp_schedule_event( time(), 'daily', 'solusen_avisos_cron' );
    }
}

La comprobación con false === get_option() evita sobrescribir los ajustes si el plugin se reactiva. Es un error muy común añadir la opción sin comprobar y borrar la configuración del usuario.

Al desactivar

register_deactivation_hook( __FILE__, 'solusen_avisos_desactivar' );

function solusen_avisos_desactivar() {
    wp_clear_scheduled_hook( 'solusen_avisos_cron' );
}

La desactivación debe ser reversible: no borres datos del usuario, solo detén lo que hayas programado.

Al desinstalar

El archivo uninstall.php se ejecuta cuando el usuario borra el plugin. Aquí sí se eliminan opciones y tablas, siempre con comprobación previa.

<?php
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
    exit;
}

delete_option( 'solusen_avisos_opciones' );

global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}solusen_avisos" );

Cuidado: borrar datos en la desinstalación es irreversible. Si el plugin guarda contenido que el usuario puede querer conservar, ofrécele una opción para decidir si eliminar los datos al desinstalar.

6. Hooks: acciones y filtros

Los hooks son el mecanismo de extensión de WordPress. Hay dos tipos:

  • Acciones (add_action): ejecutan código en un momento concreto, por ejemplo init, admin_menu o wp_enqueue_scripts.
  • Filtros (add_filter): reciben un valor, lo modifican y lo devuelven, por ejemplo the_content o wp_mail_from.
// Acción: se ejecuta al cargar el front.
add_action( 'wp_footer', 'solusen_avisos_pie' );
function solusen_avisos_pie() {
    echo '<!-- Solusen Avisos activo -->';
}

// Filtro: modifica el contenido de las entradas.
add_filter( 'the_content', 'solusen_avisos_anadir_al_contenido' );
function solusen_avisos_anadir_al_contenido( $contenido ) {
    if ( ! is_singular( 'post' ) || ! in_the_loop() || ! is_main_query() ) {
        return $contenido;
    }
    return $contenido . do_shortcode( '[solusen_aviso]' );
}

El tercer argumento de add_action/add_filter es la prioridad (10 por defecto) y el cuarto el número de argumentos. Si dos plugins modifican lo mismo, la prioridad decide el orden.

7. Crear un shortcode

Un shortcode es una etiqueta corta como [solusen_aviso] que el usuario puede insertar en una entrada, una página o un bloque de shortcode. Se registra con add_shortcode() y devuelve HTML, nunca lo imprime directamente.

<?php
// includes/shortcode.php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

add_shortcode( 'solusen_aviso', 'solusen_avisos_shortcode' );

function solusen_avisos_shortcode( $atts ) {
    $opciones = get_option( 'solusen_avisos_opciones', array() );

    $atts = shortcode_atts(
        array(
            'tipo' => $opciones['tipo'] ?? 'info',
        ),
        $atts,
        'solusen_aviso'
    );

    $mensaje = $opciones['mensaje'] ?? '';
    if ( '' === $mensaje ) {
        return '';
    }

    return sprintf(
        '<div class="solusen-aviso solusen-aviso--%s">%s</div>',
        esc_attr( $atts['tipo'] ),
        esc_html( $mensaje )
    );
}

El uso sería [solusen_aviso] o [solusen_aviso tipo="aviso"]. Fíjate en dos detalles clave: shortcode_atts() define valores por defecto y esc_html()/esc_attr() protegen la salida.

8. Página de ajustes en el panel

La Settings API conecta un formulario del panel con la tabla wp_options. WordPress se encarga del guardado, los nonces y los avisos de confirmación si usas settings_fields() y do_settings_sections().

<?php
// includes/settings.php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

add_action( 'admin_menu', 'solusen_avisos_menu' );
function solusen_avisos_menu() {
    add_options_page(
        __( 'Solusen Avisos', 'solusen-avisos' ),
        __( 'Solusen Avisos', 'solusen-avisos' ),
        'manage_options',
        'solusen-avisos',
        'solusen_avisos_pagina'
    );
}

add_action( 'admin_init', 'solusen_avisos_registrar_ajustes' );
function solusen_avisos_registrar_ajustes() {
    register_setting( 'solusen_avisos_grupo', 'solusen_avisos_opciones', array(
        'type'              => 'array',
        'sanitize_callback' => 'solusen_avisos_sanitizar',
        'default'           => array(),
    ) );

    add_settings_section(
        'solusen_avisos_seccion',
        __( 'Ajustes del aviso', 'solusen-avisos' ),
        '__return_false',
        'solusen-avisos'
    );

    add_settings_field(
        'solusen_avisos_mensaje',
        __( 'Mensaje', 'solusen-avisos' ),
        'solusen_avisos_campo_mensaje',
        'solusen-avisos',
        'solusen_avisos_seccion'
    );

    add_settings_field(
        'solusen_avisos_tipo',
        __( 'Tipo', 'solusen-avisos' ),
        'solusen_avisos_campo_tipo',
        'solusen-avisos',
        'solusen_avisos_seccion'
    );
}

function solusen_avisos_sanitizar( $entrada ) {
    $tipos = array( 'info', 'exito', 'aviso' );

    return array(
        'mensaje' => sanitize_text_field( $entrada['mensaje'] ?? '' ),
        'tipo'    => in_array( $entrada['tipo'] ?? '', $tipos, true ) ? $entrada['tipo'] : 'info',
    );
}

Y la función que pinta el formulario:

function solusen_avisos_campo_mensaje() {
    $opciones = get_option( 'solusen_avisos_opciones', array() );
    printf(
        '<input type="text" class="regular-text" name="solusen_avisos_opciones[mensaje]" value="%s">',
        esc_attr( $opciones['mensaje'] ?? '' )
    );
}

function solusen_avisos_campo_tipo() {
    $opciones = get_option( 'solusen_avisos_opciones', array() );
    $actual   = $opciones['tipo'] ?? 'info';
    $tipos    = array( 'info' => 'Info', 'exito' => 'Éxito', 'aviso' => 'Aviso' );

    echo '<select name="solusen_avisos_opciones[tipo]">';
    foreach ( $tipos as $valor => $etiqueta ) {
        printf(
            '<option value="%s" %s>%s</option>',
            esc_attr( $valor ),
            selected( $actual, $valor, false ),
            esc_html( $etiqueta )
        );
    }
    echo '</select>';
}

function solusen_avisos_pagina() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    <div class="wrap">
        <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
        <form action="options.php" method="post">
            <?php
            settings_fields( 'solusen_avisos_grupo' );
            do_settings_sections( 'solusen-avisos' );
            submit_button( __( 'Guardar cambios', 'solusen-avisos' ) );
            ?>
        </form>
    </div>
    <?php
}

No reinventes el formulario: si usas la Settings API, WordPress añade el nonce, comprueba capacidades y muestra el aviso «Ajustes guardados». Si haces el guardado a mano con $_POST, tendrás que implementar todo eso tú.

9. Encolar CSS y JavaScript

Nunca enlaces CSS o JS con etiquetas <link> o <script> sueltas. Se encolan con wp_enqueue_style() y wp_enqueue_script() para que WordPress gestione dependencias, versiones y orden de carga.

add_action( 'wp_enqueue_scripts', 'solusen_avisos_assets' );
function solusen_avisos_assets() {
    wp_enqueue_style(
        'solusen-avisos',
        plugin_dir_url( __FILE__ ) . 'assets/aviso.css',
        array(),
        SOLUSEN_AVISOS_VERSION
    );
}

add_action( 'admin_enqueue_scripts', 'solusen_avisos_admin_assets' );
function solusen_avisos_admin_assets( $hook ) {
    if ( 'settings_page_solusen-avisos' !== $hook ) {
        return;
    }

    wp_enqueue_script(
        'solusen-avisos-admin',
        plugin_dir_url( __FILE__ ) . 'assets/admin.js',
        array( 'jquery' ),
        SOLUSEN_AVISOS_VERSION,
        true
    );
}

Pasar la versión como cuarto argumento hace que el navegador descargue el archivo nuevo cuando subes la versión del plugin. En el panel, comprueba siempre $hook para cargar assets solo en tu página y no en todo el admin.

10. Seguridad: nonces, capacidades y escaping

Un plugin mal escrito es una puerta abierta. Estas cuatro reglas cubren la mayoría de los fallos:

  1. Comprueba la capacidad antes de permitir una acción: current_user_can( 'manage_options' ).
  2. Verifica el nonce en formularios y peticiones AJAX: check_admin_referer() o wp_verify_nonce().
  3. Sanitiza la entrada al guardar: sanitize_text_field(), absint(), sanitize_email(), wp_kses_post().
  4. Escapa la salida al mostrar: esc_html(), esc_attr(), esc_url(), wp_kses_post().
// Formulario con nonce.
<form method="post">
    <?php wp_nonce_field( 'solusen_avisos_accion', 'solusen_avisos_nonce' ); ?>
    <input type="text" name="mensaje">
    <?php submit_button(); ?>
</form>

<?php
// Procesar el envío de forma segura.
if ( isset( $_POST['solusen_avisos_nonce'] ) ) {
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( esc_html__( 'No tienes permisos.', 'solusen-avisos' ) );
    }

    check_admin_referer( 'solusen_avisos_accion', 'solusen_avisos_nonce' );

    $mensaje = sanitize_text_field( wp_unslash( $_POST['mensaje'] ?? '' ) );
    update_option( 'solusen_avisos_opciones', array( 'mensaje' => $mensaje, 'tipo' => 'info' ) );
}

Sanitizar al guardar y escapar al imprimir no son alternativas: son capas distintas. Sanitizar protege la base de datos; escapar protege el HTML en el momento de mostrarlo, aunque el dato venga de otro sitio.

DatoAl guardarAl mostrar
Texto planosanitize_text_field()esc_html()
HTML de entradawp_kses_post()wp_kses_post()
URLesc_url_raw()esc_url()
Emailsanitize_email()esc_html()
Enteroabsint()absint()
Atributos / clasessanitize_html_class()esc_attr()

Nunca confíes en $_GET ni en $_POST: usa siempre wp_unslash() y la función de sanitización adecuada. Y no uses extract() ni variables globales sin prefijo.

11. Guardar datos: options y tablas propias

Para ajustes pequeños, get_option()/update_option() es suficiente y está cacheado. Si necesitas muchos registros, relaciones o consultas, crea una tabla propia con dbDelta().

register_activation_hook( __FILE__, 'solusen_avisos_crear_tabla' );

function solusen_avisos_crear_tabla() {
    global $wpdb;

    $tabla   = $wpdb->prefix . 'solusen_avisos';
    $charset = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE {$tabla} (
        id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        mensaje text NOT NULL,
        creado datetime NOT NULL,
        PRIMARY KEY  (id)
    ) {$charset};";

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta( $sql );
}

dbDelta() compara la definición con la tabla existente y aplica los cambios. Respeta su formato: dos espacios tras PRIMARY KEY, una columna por línea y tipos en minúsculas. Cuando cambies el esquema, sube la versión y ejecuta la migración una sola vez.

// Ejemplo de consulta con $wpdb y prepare().
global $wpdb;
$tabla = $wpdb->prefix . 'solusen_avisos';

$filas = $wpdb->get_results(
    $wpdb->prepare( "SELECT * FROM {$tabla} WHERE id > %d ORDER BY creado DESC", 0 )
);

Usa siempre $wpdb->prepare() con marcadores (%d, %s, %f) cuando la consulta lleve variables. Concatenar directamente es una inyección SQL esperando a ocurrir.

12. Internacionalización

Si el plugin va a usarse en varios idiomas, envuelve todos los textos en funciones de traducción y carga el textdomain. El Text Domain de la cabecera debe coincidir con el que uses en el código.

add_action( 'init', 'solusen_avisos_cargar_textdomain' );
function solusen_avisos_cargar_textdomain() {
    load_plugin_textdomain(
        'solusen-avisos',
        false,
        dirname( plugin_basename( __FILE__ ) ) . '/languages'
    );
}

// Uso en el código:
echo esc_html__( 'Guardar cambios', 'solusen-avisos' );
FunciónCuándo usarla
__()Devuelve el texto traducido para usarlo en variables.
_e()Imprime el texto traducido directamente.
esc_html__()Devuelve y escapa para HTML. La opción más segura.
_n()Singular/plural según un número.
esc_attr__()Devuelve y escapa para atributos.

Puedes generar el archivo .pot con WP-CLI:

wp i18n make-pot . languages/solusen-avisos.pot

13. REST API y AJAX

La REST API es la vía moderna para exponer datos del plugin. Registra rutas en rest_api_init y define siempre un permission_callback: si la ruta es pública, usa __return_true de forma consciente.

add_action( 'rest_api_init', 'solusen_avisos_rest' );
function solusen_avisos_rest() {
    register_rest_route( 'solusen/v1', '/aviso', array(
        'methods'             => 'GET',
        'callback'            => 'solusen_avisos_rest_leer',
        'permission_callback' => '__return_true',
    ) );
}

function solusen_avisos_rest_leer() {
    $opciones = get_option( 'solusen_avisos_opciones', array() );

    return rest_ensure_response( array(
        'mensaje' => $opciones['mensaje'] ?? '',
        'tipo'    => $opciones['tipo'] ?? 'info',
    ) );
}

La ruta quedaría en /wp-json/solusen/v1/aviso. Para AJAX clásico, registra la acción con wp_ajax_ (usuarios logueados) y wp_ajax_nopriv_ (visitantes), y valida el nonce con check_ajax_referer().

14. Empaquetar, probar y publicar

Cuando el plugin funcione en local, toca empaquetarlo y probarlo en un entorno real. Un ZIP mal montado es la causa habitual de que un plugin no aparezca o falle al activarse.

Checklist antes de publicar

  • El ZIP contiene una única carpeta con el nombre del plugin y el archivo principal dentro.
  • No incluye node_modules/, .git/, vendor/ innecesario ni archivos de desarrollo.
  • Se activa sin errores ni warnings con WP_DEBUG activo.
  • Se desactiva y se desinstala limpio, sin dejar tablas ni opciones huérfanas.
  • Funciona en las versiones mínimas declaradas (WordPress y PHP).
  • Los textos están listos para traducir y el readme.txt está completo.
  • No hay rutas absolutas, credenciales ni claves API en el código.
  • El formulario de ajustes valida capacidades y nonce.
# Crear el ZIP desde la carpeta de plugins
cd wp-content/plugins
zip -r solusen-avisos.zip solusen-avisos -x "*.git*" -x "*node_modules*"

# Activar y comprobar por WP-CLI
wp plugin activate solusen-avisos
wp plugin list

Si vas a publicarlo en el repositorio oficial de WordPress.org, necesitas una cuenta, un readme.txt válido, la licencia GPL y pasar la revisión. Para uso privado o de cliente, basta con entregar el ZIP y documentar la instalación.

15. Buenas prácticas y errores comunes

La mayoría de los problemas de un plugin se evitan con disciplina desde el primer archivo:

  • Prefija todo (funciones, clases, opciones, hooks, tablas) para evitar colisiones.
  • Comprueba ABSPATH en cada archivo PHP.
  • No edites nunca el núcleo ni plugins de terceros: usa hooks.
  • Separa lógica, vistas y assets en archivos distintos.
  • Sanitiza al guardar y escapa al mostrar, siempre.
  • Usa nonces y capacidades en cada acción del panel o AJAX.
  • No borres datos del usuario en la desactivación; reserva la limpieza para la desinstalación.
  • Versiona el plugin y usa esa versión en los assets encolados.
  • Documenta el código y mantén un readme.txt con changelog.
  • Prueba en staging y con WP_DEBUG antes de producción.
SíntomaCausa probableSolución
El plugin no aparece en el listadoFalta la cabecera o el archivo no está en la carpeta correctaRevisa Plugin Name y la ruta wp-content/plugins/.
Error 500 al activarError de sintaxis PHP o dependencia no cargadaMira debug.log, corrige el error y revisa los require_once.
El shortcode se muestra como textoEl shortcode no está registrado o el contenido no pasa por do_shortcode()Verifica add_shortcode() y el contexto de salida.
Los ajustes no se guardanregister_setting() mal configurado o falta el name del campoRevisa el grupo, la opción y los nombres de los inputs.
El panel dice que no tienes permisosCapacidad incorrecta o falta el nonceUsa manage_options y settings_fields().
El CSS/JS no se actualizaCaché del navegador o versión fijaSube la versión del plugin y púrgala caché.
Colisión con otro pluginFunciones o hooks sin prefijoRenombra con prefijo único y revisa el orden de prioridad.
Datos huérfanos tras desinstalarNo hay uninstall.phpAñade limpieza condicionada a WP_UNINSTALL_PLUGIN.

La seguridad del plugin también afecta a la web completa. Si sospechas que una extensión comprometida ha abierto una puerta, revisa el análisis técnico de intrusiones en WordPress y la guía de ataques habituales.

16. Preguntas frecuentes sobre plugins de WordPress

¿Cuál es el archivo mínimo para crear un plugin?

Un único archivo PHP dentro de wp-content/plugins/ con una cabecera de comentario que incluya al menos Plugin Name. A partir de ahí puedes crecer con carpetas y más archivos.

¿Dónde se guardan las opciones de un plugin?

Con get_option()/update_option() se guardan en la tabla wp_options. Si necesitas muchos registros o consultas, crea una tabla propia con dbDelta() en la activación.

¿Qué diferencia hay entre acción y filtro?

Una acción ejecuta código en un punto concreto y no devuelve nada. Un filtro recibe un valor, lo modifica y lo devuelve para que WordPress siga usándolo. Ambos se registran con prioridad y número de argumentos.

¿Puedo crear un plugin sin tocar código?

Para cambios simples existen plugins de snippets, pero si la funcionalidad debe sobrevivir a cambios de tema y ser mantenible, lo correcto es un plugin propio o un mu-plugin.

¿Cómo evito colisiones con otros plugins?

Usa un prefijo único en funciones, clases, opciones, hooks y tablas. Además, envuelve el código en clases o namespaces y evita declarar funciones globales con nombres genéricos.

¿Qué es un mu-plugin?

Es un plugin obligatorio que vive en wp-content/mu-plugins/ y se carga automáticamente. No se puede desactivar desde el panel y se usa para funcionalidad crítica.

¿Cómo pruebo el plugin sin romper la web?

Usa un entorno local o de staging con WP_DEBUG y WP_DEBUG_LOG activos. Prueba activación, uso, desactivación y desinstalación antes de tocar producción.

¿Cómo publico el plugin en WordPress.org?

Necesitas una cuenta, un readme.txt válido, licencia GPL, código limpio y pasar la revisión del equipo de plugins. Para uso privado basta con distribuir el ZIP.

17. Conclusión

Crear un plugin de WordPress es más sencillo de lo que parece: un archivo con cabecera, unos hooks bien puestos y las APIs correctas. La diferencia entre un plugin que aguanta años y uno que se rompe en la primera actualización está en los detalles: prefijos, seguridad, sanitización, escaping y un ciclo de activación y desinstalación limpio.

Empieza pequeño, prueba en staging y añade complejidad solo cuando haga falta. Si tu web necesita una funcionalidad a medida, una integración o mantener un plugin existente, podemos ayudarte.

¿Necesitas un plugin a medida para tu WordPress?

Desarrollamos plugins propios, integraciones y mantenimiento para que tu web haga exactamente lo que necesitas sin depender de extensiones genéricas.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio