Voilà à quoi ressemble actuellement votre classe de lecture de configuration :

<?php

class MaConfig {
  private
    $_config;

  public function __construct()
  {
    if(is_readable('./inc/config.ini')) {
      $this->_config = parse_ini_file('./inc/config.ini');
    }
    else {
      throw new Exception('ini file "./inc/config.ini" does not exists.');
    }
  }

  public function get($elem)
  {
    if(isset($this->_config[$elem])) {
      return $this->_config[$elem];
    }

    return NULL;
  }

  public function set($elem, $value) {
    $this->_config[$elem] = $value;
  }
}

?>


Cette classe permet de lire un fichier de configuration de type INI. Une fois instanciée, cette classe stock dans l'attribut privée _config tous les éléments contenus dans le fichier.

Cependant avec le code ci-dessous vous irez droit dans la chute aux performances car vous instancierez plusieurs fois cette même classe :

<?php

$config = new MaConfig();
echo $config->get('login');

unset($config);
echo '<br />';

$config = new MaConfig();
echo $config->get('password');

?>


C'est vrai que ça l'air un peu bête comme ça de l'instancier deux fois de suite. Toutefois avec les inclusions d'autres scripts, vous avez vite fait d'en arriver à cette bêtise.

Heureusement le Singleton à fait son apparition en PHP5 grâce aux attributs privées et aux méthodes privées.

<?php

class MaConfig {
  private
    $_config;
  private static
    $_instance = NULL;

  private function __construct()
  {
    if(is_readable('./inc/config.ini')) {
      $this->_config = parse_ini_file('./inc/config.ini');
    }
    else {
      throw new Exception('ini file "./inc/config.ini" does not exists.');
    }
  }

  public static function getInstance()
  {
    if(is_null(self::$_instance)) {
      self::$_instance = new MaConfig();
    }

    return self::$_instance;
  }

  public function __clone() {
    throw new Exception('No clone! I am a Singleton.');
  }

  public function get($elem)
  {
    if(isset($this->_config[$elem])) {
      return $this->_config[$elem];
    }

    return NULL;
  }

  public function set($elem, $value) {
    $this->_config[$elem] = $value;
  }
}

?>


Les différences que l'on peut noter entre notre première classe et celle-ci, sont :

  • L'ajout d'un attribut privé statique nommé _instance.
  • Le passage du constructeur en privé.
  • L'ajout de la méthode statique getInstance.
  • L'ajout de la méthode magique __clone. Dans l'exemple, la méthode __clone est publique. Lorsqu'elle sera appelé, elle lancera une exception. Vous pouvez tout aussi bien la mettre en privé.


Le fait de passer le constructeur en méthode privée nous permettra d'être sûr à 100% que ce devra être une méthode de cette classe qui devra l'instancier. Par ailleurs, c'est notre nouvelle méthode statique getInstance qui sera chargée de ce boulot. Elle enregistrera l'instance dans l'attribut privée statique _instance.
On a aussi ajouté la méthode magique __clone afin d'éviter de cloner cette classe puisqu'elle est Singleton.
Voici ce que ça donne en application :

<?php

$config = MaConfig::getInstance();
echo $config->get('login');

unset($config);
echo '<br />';

$config = MaConfig::getInstance();
echo $config->get('password');

?>


Dans le cas présent, notre classe n'a lu qu'une seule fois le fichier de configuration INI car notre méthode getInstance est pourvu d'une condition vérifiant si l'attribut _instance est de valeur NULL, auquel cas la classe sera instanciée et enregistré dans l'attribut privée statique _instance qui sera renvoyé à la fin du traitement de getInstance.
C'est donc des économies de ressources et un script plus rapide à l'exécution dans la poche.

C'est par ailleurs en écrivant cet article que j'ai découvert la méthode magique __clone qui permet de cloner une instance.