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 插件 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
一个 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
一个 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,勾选复选框,输入文本并保存。通知栏将出现在你网站的顶部。你已经成功 crear un plugin de WordPress 完整:包含设置页面、数据库中保存的选项、自定义样式以及在公共部分的输出。
带来改变的最佳实践
前面的示例已经应用了你应该内化的几条规则。让我们回顾一下它们以及其他同样重要的规则:
清理输入并转义输出
所有输入数据(表单、URL、API)都使用诸如 sanitize_text_field(),并且所有输出到屏幕的数据都使用 esc_html(), esc_attr() o esc_url()。这两种习惯可以预防绝大多数XSS漏洞。如果你对防御性方法感兴趣,我们有一份指南,其中包含 7 个增强 WordPress 安全性的技巧 这与你在代码层面所做的工作相得益彰。
所有内容都使用唯一前缀
函数、类、选项和样式句柄都应带有自己的前缀(在我们的例子中是 bw_ y BW_)。PHP不允许两个函数同名:如果你的插件声明了 enviar_email() 而另一个插件也这样做,网站将因致命错误而崩溃。
检查能力并使用nonce
在管理页面显示或操作之前,请使用 current_user_can()。在自定义表单中,使用 wp_nonce_field() 并在处理时进行验证。我们使用的设置API(settings_fields())已经为你管理了nonce,这是优先选择它而不是手动处理表单的另一个原因。
仅在需要时加载资源
我们的CSS只有在通知处于活动状态时才会被排队。始终应用相同的标准:一个不必要地在所有页面加载脚本的插件会影响整个网站的性能。事实上,许多归因于WordPress的速度问题实际上是编写不当的插件造成的;在关于 如何通过PHP设置加速WordPress 你可以看到这些决定的实际影响。
如何测试和调试你的插件
借助 WP_DEBUG 激活后,你的工作流程将是:保存文件、重新加载页面并检查 wp-content/debug.log 如果出现问题。一些额外建议:
- 测试激活和停用。 多次激活和停用插件,检查是否会发出警告。
- 与其他活动插件一起测试。 插件之间的冲突是实际问题的主要来源。
- 用不同用户进行测试。 以编辑者或订阅者身份登录,并确认他们看不到设置页面。
- 使用
error_log()作为提示。 写入error_log( print_r( $variable, true ) );在代码的某个点会向日志显示任何变量的内容。
如果你的插件将处理结构化数据(例如API的JSON响应),那么像 BiblioWeb的JSON格式化和验证工具 在开发过程中检查和验证这些响应时,可以节省你的时间。
下一步:有序成长
在此基础上,你可以从多个方向扩展插件:为通知栏添加颜色选择器、设置通知的开始和结束日期、创建短代码或在REST API中公开选项。当项目增长时,请保持结构纪律:逻辑放在 includes/,以及 assets/,而主文件仅作为入口点。
深入学习的必备参考是 WordPress官方插件手册,它记录了从可用钩子到目录发布过程的所有内容。如果你想看看那些大厂是如何解决问题的,没有什么比阅读成熟插件的代码更好的了:比如我们在 WordPress初学者必备的5个插件 是学习结构和风格的良好起点。
常见问题
创建WordPress插件需要了解很多PHP吗?
刚开始不需要。通过变量、函数、数组和条件语句,你可以构建像本指南中这样有用的插件。开发过程本身会自然而然地、循序渐进地引导你接触更高级的概念(类、命名空间、API)。
WordPress插件存储在哪里?
在文件夹中 wp-content/plugins 你的安装目录。每个插件都占用自己的子文件夹(或在非常简单的情况下是一个单独的PHP文件)。WordPress会自动检测该路径中任何带有有效插件头的文件。
开发插件时会破坏我的网站吗?
活动插件中的PHP语法错误可能会导致网站崩溃,因此开发时应始终在测试环境中进行。如果发生在生产环境中,只需通过SFTP重命名插件文件夹即可将其禁用并立即恢复网站。
如何将我的插件发布到官方目录?
你必须遵守目录指南(GPL许可证、安全代码、无混淆),并准备一个文件 readme.txt 并从wordpress.org提交审核。批准后,你将获得一个SVN仓库的访问权限,版本将从该仓库分发。
结论
创建WordPress插件并非资深程序员的专属领域:它只是一个文件夹、一个头部信息和一些精心选择的钩子。在本指南中,你已经构建了一个完整的插件,包含设置页面、数据清理、条件样式以及专业插件所使用的安全最佳实践。与将代码粘贴到主题中相比,这是一个巨大的质量飞跃:你的功能现在是可移植的、可更新的,并且与设计分离。最好的最终建议很简单:选择你网站上一个真实的小问题,并用插件解决它。没有比在真实网站上维护自己的代码更好的学习方式了。