Pré-réquis

Avant de commencer, je suppose que vous possédez déjà les connaissances de base du framework. Que vous savez ce qu'est le contrôleur, le dispatcher, la vue, etc...

Je vous invite à vous rafraichir la mémoire grâce à cet article et principalement grâce à ce schéma bien utile, à imprimer de toute urgence !

Mon article se base en grande partie sur cet article de la DevZone. Si vous êtes polyglotte, n'hésitez pas à le consulter.

Initialisation

Le but est donc d'initialiser de manière propre votre application. Toute commence, bien entendu par le fichier index.php. Ce fichier est dépendant de l'environnement que vous utilisez, c'est pourquoi nous allons le limiter au strict minimum.

Il va contenir la définition des seules constantes de notre application, et le lancement d'un bootstrap. Mais je vous laisse lire avec soin le QuickStart à ce sujet. Le code ci-dessous s'en inspire largement même si pas mal de choses ont été modifiées, principalement dans le bootstrap.

Bootstrap

A la fin de l'index, nous appelons donc le bootstrap. Nous avons séparé ce bootstrap de l'index ainsi nous pouvons l'utiliser pour plusieurs sous-domaine, si nécessaire ou bien utiliser des bootstraps différents pour un même domaine. Plus il y a d'abstraction, plus le code est modulaire, attention toutefois à ne pas abuser sous peine de voir la complexité de votre code augmenter également.

Le bootstrap, étonnemment, ne contient pas 1000 lignes de code dans lequel nous faisons tout le travail d'initialisation, il en contient 3 :

// on récupère une instance du contrôleur frontal
$front = Zend_Controller_Front::getInstance();
// on défini notre fameux plugin d'initialisation
$front->registerPlugin(new My_Controller_Plugin_Initializer($env, $root, $ap_root));
// on supprime l'instanciation locale du contrôleur par sécurité
unset($front) ;

C'est le plugin qui va gérer le chargement de la base de travail de notre application.

Il va d'abord s'occuper de s'initialiser lui-même puis appeler différentes méthodes, dans un ordre défini, pour initialiser chacun des composants nécessaires, en se basant sur un fichier de configuration unique.

Plugin

Si nous reprenons le code Matthew, on y ajoute un peu de commentaires en français, et quelques modifications pour que ça soit compatible avec Zend Framework 1.6, ça donne ça :

/**
 * Plugin d'initialisation
 * 
 * @uses Zend_Controller_Plugin_Abstract
 */
class My_Controller_Plugin_Initializer extends Zend_Controller_Plugin_Abstract
{
  /**
   * @var Zend_Config_Ini
   */
  protected $_configuration ;
  
  /**
  * @var string Environnement d'exécution courant
  **/
  protected $_env;
  
  /**
  * @var Zend_Controller_Front
  **/
  protected $_front;
  
  /**
  * @var string Chemin vers la racine des documents
  */
  protected $_root;
  
  /**
  * @var string Chemin vers l'application
  */
  protected $_ap_root;
  
  /**
   * Constructeur
   *
   * @param  string $env 
   * @param  string|null $root 
   * @param  string|null $ap_root 
   * @return void
  **/
  public function __construct($env, $root = null, $ap_root = null)
  {
    // définition de l'environnement
    $this->_env = $env;
  
    // détection de la racine des documents
    if (null === $root) {
      $root = realpath(dirname(__FILE__) . '/../') ;
    }
    $this->_root = $root;
  
    // détection du chemin vers l'application
    if (null === $ap_root) {
      $ap_root = $root . '/application' ;
    }
    $this->_ap_root = $ap_root;
  
    // initialisation de la configuration
    $this->initConfig();
  
    // récupération d'une référence vers le contrôleur
    $this->_front = Zend_Controller_Front::getInstance();
  
    if ($env == 'development') {
      // on active l'affichage de toutes les erreurs
      error_reporting(E_ALL | E_STRICT);  
	  ini_set('display_startup_errors', 1);  
	  ini_set('display_errors', 1); 
	
	  $this->_front->throwExceptions(true);  
    }
  }
	
  /**
   * Interruption avant le début du routage de la requête
   * A lire : http://framework.zend.com/manual/fr/zend.controller.plugins.html
   * 
   * @param  Zend_Controller_Request_Abstract $request
   * @return void
   */
  public function routeStartup(Zend_Controller_Request_Abstract $request)
  {
    // lancement des méthodes internes pour l'initialisation
    $this->initDb()            // initialisation de la base de données
    $this->initView()          // initialisation de la vue et du layout
    $this->initPlugins()       // initialisation des plugins supplémentaires
    $this->initRoutes()        // initialisation des routes depuis le fichier INI
    $this->initHelpers()       // si on a besoin d'helper d'action
    $this->initControllers();  // et finalement on défini notre chemin vers les contrôleurs
  }
 
  // ... méthode d'intialisation
}

Simple, non ?

Bien entendu, ceci est un exemple. J'utilise une arborescence précise et il faudra adapter ce code en fonction de la votre.

Vous avez besoin d'autres méthodes ? Créez les ! Votre code d'initialisation restera organisé. Vous pouvez même surcharger le plugin pour chacune de vos applications, si nécessaire.

Je décris ci-dessous quelques unes des méthodes pré-citées.

initConfig

Cette fonction s'occupe du chargement de la configuration. Vous pouvez disposer d'un fichier de configuration par application. On peut imaginer également étendre cette fonction pour aller rechercher des fichiers de configuration pour chacun des modules de l'application.

/**
 * Initialisation de la configuration
 * 
 * @return void
**/
public function initConfig()  {
  // Chargement de la configuration de l'application
  $config_ini = $this->_ap_root . '/config/config.ini' ;
  
  if (file_exists($config_ini) && is_readable($config_ini)) {
    $configuration = new Zend_Config_Ini($config_ini, $this->_env) ;
    
    // Ajout de la configuration dans le registre
    Zend_Registry::set('configuration', $configuration);
    
    $this->_configuration = $configuration ;
  }    
	
  // Définition du fuseau horaire par défaut
  date_default_timezone_set($configuration->defaultTimezone) ;    
  
  // Configuration de l'objet Zend_Date (si nécessaire)
  Zend_Date::setOptions(array('format_type' => 'php'));
}

initView

Voici l'endroit où on peut tranquillement paramétrer sa vue. Charger un layout ou non, par module ou par application, définir les prémices de la mise en page de notre site, c'est ici que ça se passe...

/**
 * Initialisation de la vue par défaut.
 * 
 * @return void
 */
public function initView()  {
  // Définition du répertoire du layout
  Zend_Layout::startMvc(array(
    'layoutPath' => $this->_ap_root .  '/views/layouts'
  ));    

  $view = Zend_Layout::getMvcInstance()->getView(); 
  
  // On peut s'amuser à définir ici le doctype
  $view->doctype('XHTML1_STRICT'); 
}

initDb

Je crois qu'aucun commentaire n'est nécessaire. Je travaille personnellement qu'avec des classes héritées de Zend_Db_Table_Abstract donc je n'ai pas oublié de définir l'adaptateur par défaut afin de gagner du temps dans la totalité de mes déclarations et appels à ces classes.

/**
 * Initialisation de la base de données
 * 
 * @return void
 */
public function initDb()  {
  // Contrôle de l'existence d'une configuration à ce sujet
  if (isset($this->_configuration->database)) {
    $database = Zend_Db::factory($this->_configuration->database); 
    Zend_Db_Table_Abstract::setDefaultAdapter($database); 
	
    // Et on sauve le tout dans le registre
    Zend_Registry::set('database', $database);
  } 
}

initRoutes

Cette méthode charge et initialise les routes définies en dur dans un fichier de configuration prévu à cet effet.

/**
 * Configuration du routage
 * 
 * @return void
 */
public function initRoutes()  {
  $routes_ini = $this->_ap_root . '/config/routes.ini' ;
  
  // Rien ne nous empêche de choisir un autre moyen de charger ces routes
  if (file_exists($routes_ini) && is_readable($routes_ini)) {
    $routes = new Zend_Config_Ini($routes_ini, $this->_env) ;
    $router = $this->_front->getRouter() ;
    $router->addConfig($routes);
  }
}

initControllers

Et pour finir, l'initialisation du contrôleur à proprement parler. Ici, pour l'exemple, j'initialise le répertoire contenant divers modules que j'utiliserai dans mon application.

/**
 * Initialisation du contrôleur
 * 
 * @return void
**/
public function initControllers()  {
  // On réalise ici l'initialisation de base, par exemple, la 
  // définition du répertoire contenant les modules ou les contrôleurs d'action
  $this->_front->addModuleDirectory($this->_ap_root . '/modules') ;
}

Conclusion

Toutes les méthodes ne sont pas décrites car ce n'est pas le but. Mais je pense que cet article est suffisant pour expliquer le principe de base de ce plugin. D'ailleurs, si vous avez lu le premier article de Matthew à ce sujet, vous l'aviez déjà compris.

J'espère que vous trouverez une bonne source d'inspiration et n'oubliez pas de lire les articles sur lesquels j'ai basé ma propre inspiration ! :)

Ah oui, n'oubliez pas non plus, que le but est de faire du réutilisable. Donc évitez de mettre du code propre à une application dans le plugin mais déplacez plutôt ce code en tant que paramètre dans le fichier de configuration.