Categorías
Tecnologia

Crear un sistema de plugins en PHP

Hace poco me enfrenté al problema de crear un sistema o infraestructura de plugins en un programa PHP. La verdad es que ya había creado sistemas de plugins en otros lenguajes y no suele ser una tarea demasiado compleja (otra cosa es dónde colocar los hooks). El caso es que después de pensar unos minutos en el problema me di cuenta de lo fácil que es hacerlo en PHP.

Vamos al grano. Estas son las apenas 20 líneas que se requieren para crear un sistema básico de plugins:

$_plugin_actions = array();

function plugin_enable() {
    global $plugins_folder;
    find_files($plugins_folder, '/^.*\.pi\.php$/',  'plugin_load');
}

function plugin_load($plugin) {
    require($plugin);
}

function plugin_set_hook($event, $function) {
    global $_plugin_actions;
    $_plugin_actions[$event][] = $function;
}

function plugin_set_event($event) {
    global $_plugin_actions;

    // Get parameters
    $param = array();
    $ct = func_num_args();
    for ($i=1; $i<$ct; $i++) {
        $param[] = func_get_arg($i);
    }

    // Call each attatched plugin
    if (isset($_plugin_actions[$event])) {
        $functions = $_plugin_actions[$event];
        foreach ($functions as $action) {
            $param = call_user_func_array($action, $param);
        }
    }
    return $param;
}

Aunque es fácil de entender, lo explico un poco. Hay un array ($_plugin_actions) que almacena todos los plugins que se cargan al inicio en la función plugin_enable() que ha de colocarse en algún punto cercano al inicio de la carga de la página (siempre antes de que pueda saltar algún hook, obviamente). Para que plugin_enable funcione usa una función de librería que busca mediante una expresión regular todos los ficheros en un determinado directorio. En este caso, hacemos que todos los ficheros dentro del directorio $plugins_folder terminados en .pi.php se consideren ficheros de plugins. El código de la función find_files es este:

function find_files($path, $pattern, $callback) {
    $path = rtrim(str_replace("\\", "/", $path), '/') . '/';
    $matches = Array();
    $entries = Array();
    @$dir = dir($path);
    if ($dir !== false) {
        while (false !== ($entry = $dir->read())) {
            $entries[] = $entry;
        }
        $dir->close();
    }
    foreach ($entries as $entry) {
        $fullname = $path . $entry;
        if ($entry != '.' && $entry != '..' && is_dir($fullname)) {
            find_files($fullname, $pattern, $callback);
        } else if (is_file($fullname) && preg_match($pattern, $entry)) {
            call_user_func($callback, $fullname);
        }
    }
}

Lo siguiente es diseminar por nuestro código llamadas a plugin_set_event() pasando como primer parámetro el nombre del evento a lanzar (string) y luego un número indeterminado de parámetros que se le pasarán a la función que atiende al hook. La función devuelve un array de parámetros exactamente igual a los parámetros pasados a la función. Este comportamiento se puede cambiar por devolver un único valor o un array o lo que se prefiera, pero yo lo uso de esta manera para simular (en cierta forma) el paso de parámetros por referencia que no permite func_get_arg() . Por ejemplo, definimos un evento llamado «destructor» para ser llamado al finalizar la carga de la página con un par de parámetros:

....
list(param1, param2) = plugin_set_event('destructor', param1, param2);

Cuando se ejecute está nueva línea de código no pasará nada si no hemos definido un hook que haga algo con ella. Para hacer uso de este hook, podríamos crear un nuevo fichero llamado ejemplo.pi.php, con un contenido como este:

plugin_set_hook('destructor', 'my_destructor');

function my_destructor(param1, param2) {
   ....
   return array(param1, param2);
}

En fin, crear un sistema de plugins en PHP es pan comido. Está claro que se puede complicar mucho más el sistema, por ejemplo, estableciendo prioridades en el disparo de hooks, controlando los parámetros, etc. En cualquier caso, lo más importante es saber dónde colocar las llamadas a los hooks (plugin_set_event()), pero eso da para un artículo entero.

4 respuestas a «Crear un sistema de plugins en PHP»

Bufff… es muy complicado poner un ejemplo ya que primero sería necesario hacer una aplicación que requiera plugins lo cual no es sencillo ya que estas aplicaciones suelen ser «grandes». Pero lo tendré en cuenta para un futuro artículo..

Deja una respuesta

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