Usando register_activation_hook en clases
Estoy tratando de desarrollar un plugin para propósitos básicos de SEO, ya que muchas personas que conozco no les gusta usar Yoast. Literalmente acabo de comenzar el plugin y estoy construyendo el mensaje de activación que se muestra al usuario cuando activan el plugin. Estoy teniendo problemas con una mezcla entre POO y funciones incorporadas de WordPress, y no estoy seguro de dónde me estoy equivocando.
La única forma en que puedo hacer que esto funcione es creando una nueva instancia de la clase SEO_Plugin_Activation dentro del constructor de mis archivos principales, y luego llamando al método activatePlugin en esa clase. Siento que esto es innecesario. También creo que la forma en que estoy ejecutando mis funciones en el archivo de la clase de activación no tiene mucho sentido tampoco. Pero es la única manera en que puedo hacer que funcione ahora mismo.
No estoy seguro si lo que estoy haciendo es porque no estoy comprendiendo las técnicas de POO al 100%, o si no estoy utilizando la API de WordPress correctamente. He incluido tres ejemplos de código en el siguiente orden:
- Archivo principal del plugin
- Archivo de clase que maneja mis requisitos de activación
- Lo que realmente esperaba que se pudiera hacer.
seo.php (archivo principal del plugin)
<?php
/*
... información genérica del plugin
*/
require_once(dirname(__FILE__) . '/admin/class-plugin-activation.php');
class SEO {
function __construct() {
$activate = new SEO_Plugin_Activation();
$activate->activatePlugin();
}
}
new SEO();
?>
class-plugin-activation.php
<?php
class SEO_Plugin_Activation {
function __construct() {
register_activation_hook(__FILE__ . '../seo.php', array($this, 'activatePlugin'));
add_action('admin_notices', array($this, 'showSitemapInfo'));
}
function activatePlugin() {
set_transient('show_sitemap_info', true, 5);
}
function showSitemapInfo() {
if(get_transient('show_sitemap_info')) {
echo '<div class="updated notice is-dismissible">' .
'Los archivos de su mapa del sitio se pueden encontrar en los siguientes enlaces: ' .
'</div>';
delete_transient('show_sitemap_info');
}
}
}
?>
seo.php (Lo que esperaba)
<?php
/*
... bla bla bla
*/
require_once(dirname(__FILE__) . '/admin/class-plugin-activation.php');
class SEO {
function __construct() {
register_activation_hook(__FILE__, array($this, 'wp_install'));
}
function wp_install() {
$activate = new SEO_Plugin_Activation();
// Ejecutar algún método(s) aquí que se encargaría
// de todo el arranque de activación de mi plugin
}
}
new SEO();
?>
Intenté hacerlo de la manera que describo en el tercer script, pero no tengo éxito. Por ahora, el mensaje se muestra correctamente, sin mensajes de error.

Después de releer tu pregunta, creo que entiendo el problema, y surge de un malentendido sobre cómo funciona register_activation_hook
, combinado con cierta confusión sobre cómo estás iniciando tu código y qué significa iniciar.
Parte 1: register_activation_hook
Esta función toma 2 parámetros:
register_activation_hook( string $file, callable $function )
El primer parámetro, $file
es el archivo principal del plugin, no el archivo que contiene lo que quieres ejecutar. Se usa de la misma manera que plugins_url
, por lo que necesitas el valor de __FILE__
, específicamente su valor en el archivo raíz del plugin con tu encabezado de plugin.
El segundo parámetro es un callable y funciona como esperas, pero en realidad solo está usando add_action
internamente
Cuando un plugin se activa, se llama al hook 'activate_NOMBREDELPLUGIN'. En el nombre de este hook, NOMBREDELPLUGIN se reemplaza con el nombre del plugin, incluyendo el subdirectorio opcional. Por ejemplo, cuando el plugin está ubicado en wp-content/plugins/pluginEjemplo/ejemplo.php, el nombre de este hook será 'activate_pluginEjemplo/ejemplo.php'.
Parte 2: Inicio y __FILE__
Un problema fundamental aquí es que __FILE__
tendrá valores diferentes en diferentes ubicaciones, y necesitas un valor específico.
También tienes un problema, que el inicio debería ensamblar el gráfico de objetos, pero no haces eso. Todos tus objetos se crean tan pronto como se definen, o se crean dentro de otros, lo que dificulta o imposibilita pasarles valores.
Como ejemplo, podría escribir un plugin así:
plugin.php
:
<?php
/**
* Plugin Name: Mi Plugin
* Version: 0.1
*/
// paso de carga
require_once( 'php/app.php' );
// paso de inicio
$app = new App( __FILE__ );
// paso de ejecución
$app->run();
Definí todas mis clases en la subcarpeta php
, y las cargué todas en el mismo lugar. PHP ahora sabe qué son mis clases, sus nombres, etc., pero aún no ha pasado nada.
Luego creo todos mis objetos, pasándoles lo que necesitan, en este caso App necesita el valor de __FILE__
así que se lo paso. Nota que esto crea los objetos en memoria, pero no realiza ningún trabajo. La aplicación del plugin está lista para funcionar, para entrar en acción, pero está en la fase de preparación.
El siguiente paso podría ser pasar estos objetos a un conjunto de pruebas unitarias, pero ahora voy a ejecutarlos. He terminado mi proceso de inicio, y la aplicación está lista para ejecutarse, así que disparo el método run
. No debería necesitar pasar nada a run
ya que todo lo necesario se ha pasado a los constructores. Durante el método run
, agrego todos mis filtros y hooks. Es durante esos hooks que se ejecutan todas las demás partes del plugin.
La parte importante sin embargo es que tengo una estructura definida, y que paso lo necesario durante la fase de construcción/inicio, con un ciclo de vida definido.

Gracias por la respuesta detallada. Por lo que entiendo de tu explicación, al menos lo que estoy comprendiendo, es que necesito encontrar alguna forma para que mi plugin principal tenga conocimiento de todas las clases que he escrito? Por ejemplo, probablemente tendré archivos de clases en una carpeta "inc", y más archivos de clases en una carpeta "admin". Tenía la impresión de que cada vez que quisiera usar una clase en algún lugar, necesitaba hacer "$var = new NombreDeClase();" para poder acceder a esos métodos. Por eso me ves creando la clase dentro de la clase principal.

no tienes que crear el objeto dentro de la clase, simplemente pasa $var
a ella, los constructores también pueden recibir parámetros y los objetos se pueden pasar como cualquier otra variable. Puedes hacerlo como lo has hecho, pero hay desventajas. El problema con acceder a __FILE__
es uno de ellos, ese valor debe venir del archivo principal del plugin, o no será correcto, así que necesita ser pasado a otras partes del plugin

Perfecto. Gracias. Esta respuesta definitivamente será suficiente. Ahora sé en qué necesito repasar.

El aspecto de programación orientada a objetos (OO) de esta pregunta está un poco fuera de tema para este sitio, mantuve la pregunta abierta con el pretexto de usar correctamente el register_activation_hook
. Para el lado OO de las cosas, mira https://tomjn.com/2015/06/24/root-composition-in-wordpress-plugins/ pero hay otras formas de hacerlo. No hay una forma correcta de hacerlo, todas tienen sus beneficios y desventajas, podrías escribir tu plugin usando puramente construcciones de programación funcional al estilo Haskell

Puedes definir una constante que contendrá el valor de __FILE__ y usarla donde quieras en tus clases.
Ejemplo:
archivo-principal-del-plugin.php
<?php
/**
* Plugin Name: Plugin con Clases
*/
define( 'PLUGIN_WITH_CLASSES__FILE__', __FILE__ );
include( 'mi-clase.php' );
mi-clase.php
<?php
class myClass {
function __construct() {
// Ejecutar esto al activar el plugin
register_activation_hook( PLUGIN_WITH_CLASSES__FILE__, [ $this, 'crear_tabla_bd_del_plugin' ] );
}
function crear_tabla_bd_del_plugin(){
//Crear tabla en la base de datos...
}
}
new myClass();

Realmente deberías registrar tus hooks de activación/desactivación/desinstalación fuera de tu clase de plugin, como se explica en la respuesta de kaiser aquí, que proporciona una explicación mucho más completa sobre el tema de lo que yo podría escribir.
Eso debería cubrirte si estás buscando escribir este plugin como un ejercicio de aprendizaje. Si lo único que buscas es un plugin de SEO que funcione bien y no esté plagado de anuncios horteras como Yoast, puedo recomendarte encarecidamente The SEO Framework.

Tom McFarlin creó un boilerplate para plugins muy completo (ahora mantenido por Devin Vinson), todo escrito en POO (Programación Orientada a Objetos), que puedes usar tanto para crear tu nuevo plugin como para estudiar el flujo de la aplicación y responder a tu pregunta. Lo he utilizado en varios plugins personalizados y debo decir que realmente me abrió los ojos a algunos de los misterios de la POO.
