Sélectionner une page

Un plugin WordPress peut faire appel à de nombreuses class qu’il faudra inclure via autant de require ou include. C’est long, fastidieux et potentiellement source d’erreurs. Pourquoi s’embêter avec ça quand PHP peut le faire pour nous ?

Les conventions

En bon codeur respecteux des standards, on part du principe que toutes nos class sont regroupées dans un même dossier et que les fichiers sont nommés selon ce modèle : class-nomdemaclass.php Si toutes nos class sont regroupées dans un dossier includes, on obtient quelque chose ressemblant à ceci :

L’autoloader

Nous allons créer notre autoloader. Et vous savez quoi ? Ce sera lui aussi une… class. Dans le dossier includes de notre plugin, on crée un fichier class-rrphf-myplugin-autoloader.php et on y rédige le code suivant :

<?php defined( 'ABSPATH' ) || exit;
/**
 * Project: RRPhF MyPlugin
 * File: class-rrphf-myplugin-autoloader.php
 * User: reskator
 * Author: RR PhF
 *
 * @Description Class autoloader for RRPhF MyPlugin plugin
 * @since        1.0.0
 */

if ( ! class_exists( 'RRPhF_MyPlugin_autoloader' ) ) :
   class RRPhF_MyPlugin_autoloader {
      /**
       * Singleton
       *
       * @var null Single instance
       */
      private static $_instance = null;
      
      /**
       * RRPhF_MyPlugin_autoloader constructor.
       *
       */
      private function __construct() {
         spl_autoload_register( [$this, 'load'] );
      }
      
      /**
       * Singleton method
       * 
       * If it has not already been done, creates and return an instance of the class
       *
       * @author RR PhF
       * @since  1.0.0
       *
       * @static
       * @return \RRPhF_MyPlugin_autoloader
       */
      public static function get_instance() {
         if ( ! self::$_instance ) {
            self::$_instance = new self();
         }
         
         return self::$_instance;
      }
      
      /**
       * Class loader
       *
       * Converts the name of the class to a file name and includes the class file.
       *
       * @author RR PhF
       * @since  1.0.0
       *
       * @param string $class_name - class to load
       */
      public function load( $class_name ) {
         //Converts the class name to a file name
         $class_file = str_replace( '_', '-', strtolower( $class_name ) );
         $class_file = 'class-' . $class_file;
         $class_file = __DIR__ . '/' . $class_file;
         
         // do the autoloading
         spl_autoload( $class_file, '.php' );
         }
      }
      
      /**
       * Make sure an instance can't be cloned
       *
       * @author RR PhF
       * @since  1.0.0
       *
       */
      private function __clone() {
         // do nothing
      }
   }
   
   RRPhF_MyPlugin_autoloader::get_instance();

endif;

Notre class est instanciée à la ligne 81. par l’appel de la méthode… get_intance()

RRPhF_MyPlugin_autoloader::get_instance();

Le fait d’instancier la class fait que PHP cherche si un constructeur est présent dans la class et l’exécute automatiquement. Ça tombe bien notre class en comporte un :

private function __construct() {
      spl_autoload_register( [$this, 'load'] );
}

Pour schématiser, spl_autoload_register() permet d’indiquer à PHP la fonction (méthode) qu’il devra utiliser lorsqu’il découvrira une nouvelle class durant l’exécution de notre plugin. En bonus, il se chargera de transmettre à notre méthode le nom de la class concernée. Dans le cas présent, comme vous pouvez le voir, nous lui indiquons d’utiliser la méthode load :

   */
  public function load( $class_name ) {
    //Converts the class name to a file name
    $class_file = str_replace( '_', '-', strtolower( $class_name ) );
    $class_file = 'class-'. $class_file;
    $class_file = __DIR__ .'/'. $class_file;
    
    // do the autoloading
    spl_autoload( $class_file, '.php' );
  }

La méthode load dispose de l’argument $class_name. C’est cet argument qui reçoit le nom de la class ayant déclenché l’appel _autoload. Après avoir converti le nom de la class en nom de fichier (mais sans l’extension), on appelle la méthode PHP spl_autoload() avec, en 1er argument, le path du fichier à charger (toujours sans l’extension), et en deuxième argument, l’extension. Notez que notre class est particulièrement ‘verrouillée’ :

  • la propriété $_instance est déclarée private et static ;
  • le __constructor est déclaré private ;
  • et get_instance() fait sont possible pour ne déclarer qu’une instance.

Je dis qu’il fait son possible car il ne peut empêcher que l’instance soit… clonée :/ D’où la dernière méthode de notre class :

private function __clone() {
   // do nothing
}

Dans le cas où l’on tenterait de cloner l’instance de cette class, la méthode __clone() sera appelée… mais ne fera rien : plus de clonage possible.

Implémenter notre autoloader

L’autoloader étant en place, il nous faut l’implémenter dans notre plugin :

<?php defined( 'ABSPATH' ) || exit;
/*
Plugin Name: RRPhF MyPlugin
Plugin URI: https://www.reskator.fr
Description: The best plugin in the world that everyone expected
Version: 1.0.0
Author: Reskator
Author URI: https://www.reskator.fr
Text Domain: rrphfmyplugin
Domain Path: /languages
License: GPLv2 or later
*/

if ( ! defined( 'RRPHF_MYPLUGIN_FILE' ) ) {
   define( 'RRPHF_MYPLUGIN_FILE', __FILE__ );
}

if ( ! class_exists( 'RRPhFMyPlugin_autoloader' ) ) {
   include_once __DIR__ .'/includes/class-rrphfmyplugin-autoloader.php';
}

// Calls plugin's main class
$myplugin = new RRPhF_MyPlugin();

Et voilà ! On n’a désormais qu’un seul et unique include pour charger toutes nos class :) Et on peux voir la ‘magie’ opérer dès l’instruction suivante qui instancie la class principale du plugin. Sans autoloader, on a tendance à inclure TOUTES les class. Or, il se peut qu’elles ne soient pas toutes requises à chaque fois. Avec un autoloader, PHP ne chargera que les class nécessaires, libérant ainsi des ressources (mémoire, accès disques, etc.).