Hook/Acción de Actualización de Plugin en WordPress desde 3.9

20 may 2014, 04:15:47
Vistas: 24.3K
Votos: 17

He investigado esto varias veces, pero mi búsqueda no revela mucho excepto código personalizado que puede o no ser una buena práctica de WordPress.

A partir de las últimas versiones (WordPress 3.9 "Smith"), ¿se ha añadido algún hook al proceso de actualización de plugins? Lo pregunto porque es una necesidad muy básica, sin embargo, no lo veo añadido al codex (todavía). Si no es así, ¿cuál es la práctica común y mejor que emplean los desarrolladores?

EDICIÓN: Solo para aclarar, no estoy hablando de la activación, sino de la actualización, de manera que si hay cambios en la base de datos o de otro tipo, se puedan abordar.

3
Comentarios

La respuesta de @drzaus proporcionada allí no es una buena práctica.

Rens Tillmann Rens Tillmann
5 ago 2016 19:32:37

@RensTillmann aparte de que esto ya tiene 2 años de antigüedad, la pregunta/respuesta enlazada tiene básicamente la misma respuesta pero es anterior a esta pregunta por otros 2 años, de ahí que sea un 'duplicado'.

drzaus drzaus
8 ago 2016 22:45:56
Todas las respuestas a la pregunta 5
6
21

No creo que se haya agregado una acción. Puedes revisar los detalles de versión para cualquier versión y ver si se han añadido nuevas acciones.

La forma correcta en WordPress para ejecutar código al actualizar un plugin es lo que se describe aquí:

La manera adecuada de manejar una ruta de actualización es ejecutar un procedimiento de actualización solo cuando sea necesario. Idealmente, almacenarías una "versión" en las opciones de base de datos de tu plugin, y otra versión en el código. Si no coinciden, activarías tu procedimiento de actualización y luego establecerías la opción de base de datos para que sea igual a la versión en el código. Así es como muchos plugins manejan las actualizaciones, y así es como funciona el núcleo también.

y con un ejemplo de código aquí:

function myplugin_update_db_check() {
    global $jal_db_version;
    if (get_site_option( 'jal_db_version' ) != $jal_db_version) {
        jal_install();
    }
}
add_action( 'plugins_loaded', 'myplugin_update_db_check' );
20 may 2014 04:58:08
Comentarios

Gracias - simplemente usaré ese método entonces. WP realmente debería añadir una acción para esto :D

user1915665 user1915665
20 may 2014 05:20:11

técnicamente se supone que debes usar register_activation_hook, ya que en la mayoría de los casos un plugin se desactiva/reactiva cuando lo actualizas desde el administrador. Engancharte a plugins_loaded hará tu comprobación en cada carga de página (incluyendo el frontend). Hubo conversaciones sobre introducir register_update_hook, pero fue marcado como WONTFIX hace un tiempo. La discusión allí es útil.

drzaus drzaus
9 sept 2014 12:10:17

Es importante entender que una actualización masiva de plugins NO ejecuta los hooks de activación - DEBERÍA hacerlo, pero no lo hace en la 3.9.2. Por "actualización masiva" me refiero a una actualización hecha desde la página de actualizaciones del Panel. Las actualizaciones individuales hechas desde dentro de la página del plugin ejecutan los hooks sin problemas.

Brian C Brian C
21 sept 2014 03:26:26

El problema es que los plugins también pueden actualizarse a través de FTP, lo que significa que el hook no se ejecutará en ningún caso. Por eso necesitas recurrir a la opción almacenada en la base de datos.

giraff giraff
1 sept 2015 19:37:21

Para ampliar el comentario de @giraff, lo mismo ocurre con las personas que gestionan su código con control de versiones como SVN o Git. Por eso, esta respuesta es la mejor manera de manejar las actualizaciones.

doublesharp doublesharp
10 sept 2015 19:53:03

No estoy seguro si esto es lo que querías decir, pero si estás buscando algunos hooks que se activen antes y después de la actualización, entonces echa un vistazo a upgrader_pre_install y upgrader_post_install respectivamente

Eugen Mihailescu Eugen Mihailescu
16 sept 2015 09:46:18
Mostrar los 1 comentarios restantes
4

Desde WordPress 3.9 puedes usar el hook upgrader_process_complete.
Este hook se ejecutará cuando el proceso de actualización esté completo (plugins y temas actualizados).

Ver referencias 1, 2, 3

Aquí tienes un ejemplo de código:

<?php 
/**
 * Plugin Name: Plugin de prueba 1
 * Plugin URI: https://rundiz.com
 * Description: Un plugin muy simple para pruebas. Este plugin no hace nada.
 * Version: 0.1.8
 * Author: Vee Winch
 * Author URI: http://rundiz.com
 * License: MIT
 * License URI: https://opensource.org/licenses/MIT
 * Text Domain: test-plugin1
 * Domain Path: 
 */


add_action('upgrader_process_complete', 'testplugin_upgrade_completed', 10, 2);
/**
 * Proceso de actualización completado.
 *
 * @see \WP_Upgrader::run() (wp-admin/includes/class-wp-upgrader.php)
 * @param \WP_Upgrader $upgrader_object
 * @param array $hook_extra
 */
function testplugin_upgrade_completed(\WP_Upgrader $upgrader_object, $hook_extra)
{
    // obtener versión actual del plugin. ( https://wordpress.stackexchange.com/a/18270/41315 )
    if(!function_exists('get_plugin_data')){
        require_once(ABSPATH . 'wp-admin/includes/plugin.php');
    }
    // https://developer.wordpress.org/reference/functions/get_plugin_data/
    $plugin_data = get_plugin_data(__FILE__);
    $plugin_version = ($plugin_data['Version'] ?? 'unknown.version');
    unset($plugin_data);

    if (
        is_array($hook_extra) && 
        array_key_exists('action', $hook_extra) && 
        $hook_extra['action'] == 'update'
    ) {
        if (
            array_key_exists('type', $hook_extra) && 
            $hook_extra['type'] == 'plugin'
        ) {
            // si se actualizaron plugins.
            $this_plugin = plugin_basename(__FILE__);
            $this_plugin_updated = false;
            if (array_key_exists('plugins', $hook_extra)) {
                // si es actualización masiva de plugins (en página de actualizaciones)
                foreach ($hook_extra['plugins'] as $each_plugin) {
                    if ($each_plugin === $this_plugin) {
                        $this_plugin_updated = true;
                        break;
                    }
                }// endforeach;
                unset($each_plugin);
            } elseif (array_key_exists('plugin', $hook_extra)) {
                // si es actualización normal o automática.
                if ($this_plugin === $hook_extra['plugin']) {
                    $this_plugin_updated = true;
                }
            }
            if ($this_plugin_updated === true) {
                // si este plugin acaba de actualizarse.
                // realiza tus tareas aquí.
                // NO proceses nada del nuevo código aquí, porque se ejecutará la versión antigua del plugin.
                // ¡¡léelo de nuevo!! el código que se ejecuta aquí NO es la nueva versión (recién actualizada) sino la versión anterior.
                file_put_contents(WP_CONTENT_DIR . '/test.txt', 'v'.$plugin_version."\r\n", FILE_APPEND);
                // configura transient para ejecutar más tarde.
                set_transient('testplugin_just_updated', 1);
            }
        } elseif (
            array_key_exists('type', $hook_extra) && 
            $hook_extra['type'] == 'theme'
        ) {
            // si se actualizaron temas.
            // igual que con plugins, la actualización masiva estará en $hook_extra['themes'] como 'theme1', 'theme2'.
            // la actualización normal o automática estará en $hook_extra['theme'] como 'theme1'.
        }
    }// endif; $hook_extra
}// testplugin_upgrade_completed


add_action('plugins_loaded', 'testplugin_pluginloaded');
/**
 * Ejecutar una vez que el plugin se cargue (en cada carga de página).
 */
function testplugin_pluginloaded()
{
    // obtener versión actual del plugin. ( https://wordpress.stackexchange.com/a/18270/41315 )
    if(!function_exists('get_plugin_data')){
        require_once(ABSPATH . 'wp-admin/includes/plugin.php');
    }
    // https://developer.wordpress.org/reference/functions/get_plugin_data/
    $plugin_data = get_plugin_data(__FILE__);
    $plugin_version = ($plugin_data['Version'] ?? 'unknown.version');
    unset($plugin_data);

    if (get_transient('testplugin_just_updated') && current_user_can('manage_options')) {
        // si está marcado en el transient que este plugin se acaba de actualizar y el usuario actual es admin.
        // aquí puedes usar el código de la nueva versión.
        file_put_contents(WP_CONTENT_DIR . '/test-update-by-transient.txt', 'v'.$plugin_version."\r\n", FILE_APPEND);

        // tu código de actualización aquí.
        
        // eliminar el transient cuando termines para evitar que este código se ejecute de nuevo.
        delete_transient('testplugin_just_updated');
    }
}// testplugin_pluginloaded

El hook upgrader_process_complete se ejecutará con tu versión actual de código mientras el plugin/tema se está actualizando. No usa la nueva versión.

Escenario

  1. Tienes el plugin en versión 1.0
  2. Ejecutas la página de actualizaciones o actualización automática.
  3. Se descargará y extraerá tu plugin versión 2.0. Se llamará al hook upgrader_process_complete.
  4. Tu plugin versión 1.0 se ejecutará en el hook upgrader_process_complete.
  5. Una vez terminado, al recargar la página se llamará al hook plugins_loaded.
  6. Tu plugin versión 2.0 se ejecutará en el hook plugins_loaded. (El plugin debe estar activado).

Esto ya está explicado en el código que he publicado anteriormente (antes de editar) pero quizás no de forma clara o es difícil de ver.

El hook upgrader_process_complete está creado para esto (lee la referencia 3). Para ejecutarse después de que la actualización haya terminado.

Puedes usar el hook plugins_loaded con el código de la respuesta aceptada. Funciona y es más corto o puedes tener alguna idea mejor para usar con el hook upgrader_process_complete.

El hook upgrader_process_complete funcionará cuando:

  1. Actualizas desde la página de actualizaciones.
  2. Actualizas desde la página de plugins o temas.
  3. Actualizas automáticamente o mediante WP Cron.

El código anterior no funciona cuando actualizas plugins o temas vía FTP porque no puede detectar la opción transient. En este caso, la respuesta aceptada es la mejor opción para ti.

23 mar 2018 09:54:55
Comentarios

Este hook suena bien al principio, pero si revisas el codex, hay una advertencia que dice que ejecuta código de la versión anterior del plugin. Esto se debe a que este hook no se ejecuta después de todo el proceso de actualización. En realidad se ejecuta después de que se completa la descarga y antes de que se elimine la versión anterior.

user54141 user54141
26 oct 2021 22:10:21

@user54141 Sí, utiliza el código de la versión anterior. Por eso uso set_transient() para marcar que este plugin acaba de actualizarse y uso get_transient() después del hook plugins_loaded (funciona al recargar la página) para verificar si realmente se acaba de actualizar y luego ejecutar el nuevo código. El hook plugins_loaded funcionará después de recargar la página y el transient verificará que el plugin acaba de actualizarse. Una vez que el nuevo código actualizado se haya ejecutado, eliminará ese transient para que solo se ejecute una vez después de la actualización del plugin.

vee vee
14 feb 2022 09:46:58

Dado que esta solución finalmente verifica si el plugin se actualizó en plugins_loaded, es mucho más simple usar la respuesta seleccionada que hace lo mismo con mucho menos código.

user54141 user54141
15 feb 2022 16:19:23

@user54141 Sí, el código de la respuesta aceptada es muy bueno. Estoy de acuerdo. Sin embargo, este hook sigue siendo útil para alguien que pueda tener una idea mejor que la mía para usarlo cuando se complete la actualización del plugin. Para eso está el hook upgrader_process_complete.

vee vee
15 feb 2022 17:28:53
3

Por la discusión donde decidieron no añadir un hook/función personalizado específico para actualizaciones, parece que "la mayoría de la gente" (hace 4 años) usa register_activation_hook, ya que se llama cuando un plugin se actualiza a través de la página de administración; la mayoría de ejemplos que he visto desde entonces siguen esa tendencia.

Para la mayoría de los casos de uso, sugeriría no enganchar a través de plugins_loaded, ya que se llamaría en cada carga de página. La excepción a esto se menciona en la discusión: las rutas de actualización mediante FTP/SVN son 'casos marginales', ya que WP no tendría un mecanismo para saber que el plugin fue cambiado, en cuyo caso la respuesta previa podría ser más relevante.

Mira https://gist.github.com/zaus/c08288c68b7f487193d1 para ver un ejemplo de 'marco simple' usando register_activation_hook.

9 sept 2014 12:25:32
Comentarios

register_activation_hook no está garantizado que se ejecute en actualizaciones, ver https://make.wordpress.org/core/2010/10/27/plugin-activation-hooks-no-longer-fire-for-updates/

Flimm Flimm
15 sept 2017 13:49:59

Mucho cuidado - NO uses plugins_loaded - se ejecuta en cada carga, y puede ser pesado/lento.

random_user_name random_user_name
1 mar 2019 01:17:03

register_activation_hook ya no se ejecuta en actualizaciones. Era una inconsistencia que corrigieron. Ahora solo se activa al activar plugins.

El uso de plugins_loaded está recomendado en el codex. No es lento porque WP hace una sola consulta para todas las opciones del sitio y las guarda en caché, así que aunque uses get_option() para verificar la versión guardada en la BD, en realidad no hace una petición a la BD. Solo verifica la caché.

user54141 user54141
26 oct 2021 22:06:01
0

Mientras buscaba algo relacionado en noviembre de 2023, pensé en ofrecer esta sugerencia: el filtro update_plugins_{$hostname} se introdujo en la versión 5.8.0 de WordPress.

Esta característica solo funciona si el plugin utiliza la cabecera Update URI. Así que si estás creando un plugin, puedes incluir la cabecera Update URI en la parte superior del archivo principal con el valor siendo el endpoint de tu JSON. Luego engancharías el filtro reemplazando {hostname} con el nombre de host de esa URL. Por ejemplo, digamos que este es mi archivo de plugin personalizado ubicado en /wp-content/plugins/mi-plugin-personalizado/mi-plugin-personalizado.php y tengo esto en la parte superior:

/**
 * Plugin Name: Mi Plugin Personalizado
 * Version: 1.0.0
 * Author: AuRise Creative
 * Update URI: https://aurisecreative.com/actualizar-mi-plugin-personalizado/
 */

Ahora podría engancharme a las actualizaciones del plugin para recuperar mis datos de actualización (si los hay) para ese plugin.

/**
 * Comprobar actualizaciones del plugin
 *
 * @param array|false $update Los datos de actualización del plugin con los últimos detalles. Por defecto false.
 * @param array $plugin_data Cabeceras del plugin.
 * @param string $plugin_file Nombre del archivo del plugin.
 * @param string $locales Idiomas instalados para buscar traducciones.
 *
 * @return array|false Un array asociativo con datos de actualización en caso de éxito. False en caso contrario.
 */
function prefijo_comprobar_actualizaciones_plugin($update, $plugin_data, $plugin_file, $locales = '')
{
    // Hacer cosas
    return $update;
}
add_filter('update_plugins_aurisecreative.com', 'prefijo_comprobar_actualizaciones_plugin', 10, 4);

Desafortunadamente, esto solo funciona para plugins que usan la cabecera, por lo que probablemente solo en plugins premium.

Fuentes:

2 nov 2023 19:21:41
0

Puedes engancharte a los filtros upgrader_pre_install y upgrader_post_install.

7 jun 2020 06:47:34