Avis sur ma class CacheManager + question MVC

Rechercher

Avis sur ma class CacheManager + question MVC

Par Graphox  -  7 reponses  -  Le 23/05/2009 21:41  -  Editer  - 

Bonjour !

J'ai réalisé une classe pour gérer de différentes façons des données en cache : pouvoir mettre en cache des données spécifiques (en n'enregistrant qu'un tableau par exemplee), ou mettre en cache le contenu d'un tampon de sortie.

Pour les deux méthodes, il est possible de spécifier une durée de vie maximale à ce fichier de cache, mais ce n'est pas obligé : lors de l'ajout/modification/suppresion de donné, il suffit d'appeler une méthode statique pour supprimer le cache et donc ainsi économiser une mise en cache pour rien (si on ne poste pas de news depuis 1jour, innutile de rafraîchir toutes les 5 minutes avec cette organisation).

Donc voilà, j'ai essayé de caser tous les sytèmes les plus courants de mettre des données/pages en cache dans une seule classe, et j'aimerais, si possibl, vos avis sur ma classe, sachant que j'ai essayé d'optimiser au max, et donc la méthode Exists pourrait vous parraître longue pour pas grand chose (je débute en POO aussi) :

<?php
class CacheManager
{
  private $_file = '';
  private $_time = false;
  private $_buffer = false;
  private $_get_return = null;
  const DIR = './_cache/';
  const EXTENSION = '.php';
  public function __construct($file, $time = false, $buffer = false)
  {
    $this->_file = $file;
    $this->_time = $time;
    $this->_buffer = $buffer;
  }
  public function Exist()
  {
    if(is_int($this->_time))
    {
      if(file_exists(self::DIR.$this->_file.self::EXTENSION))
      {
        if(time() - filemtime(self::DIR.$this->_file.self::EXTENSION) >= $this->_time)
        {
          unlink(self::DIR.$this->_file.self::EXTENSION);
          return false;
        }
        else
          return true;
      }
      else
        return false;
    }
    else
      return file_exists(self::DIR.$this->_file.self::EXTENSION);
  }
  public function BufferInit()
  {
    ob_start();
  }
  public function Create($save = null, $array_splice = false)
  {
    if($this->_buffer)
    {
      $content = ob_get_contents();
      ob_end_clean();
      $this->_get_return = $content;
      file_put_contents(self::DIR.$this->_file.self::EXTENSION, $content);
    }
    else
    {
      if(is_array($save) && $array_splice)
        array_splice($save, -1);
      $this->_get_return = $save;
      file_put_contents(self::DIR.$this->_file.self::EXTENSION, serialize($save));
    }
  }
  public function Get()
  {
    if($this->_get_return != null)
      return $this->_get_return;
    else
    {
      if($this->_buffer)
        return file_get_contents(self::DIR.$this->_file.self::EXTENSION);
      else
        return unserialize(file_get_contents(self::DIR.$this->_file.self::EXTENSION));
    }
  }
  public static function Destroy($file)
  {
    if(file_exists(self::DIR.$file.self::EXTENSION))
      unlink(self::DIR.$file.self::EXTENSION);
  }
  public static function DestroyDirContent($dir = null)
  {
    if(is_dir(self::DIR.$dir))
    {
      if(!preg_match('`^.*\/$`', $dir))
        $dir .= '/';
      if($handle = opendir(self::DIR.$dir))
      {
        while( $item = readdir($handle) )
          {
             if($item != '.' && $item != '..')
             {
              if(is_dir(self::DIR.$dir.$item))
                self::DestroyDirContent($dir.$item);
              else
                unlink(self::DIR.$dir.$item);
             }
          }
        closedir($handle);
      }
    }
  }
}
?>

Et voici des exemples d'utilisation :

 

$cache = new CacheManager('test'); // Le cache sera détruit manuellement grâce à la méthode statique Destroy() ou DestroyDirContent();
if(!$cache->Exist())
{
  $dbh = new PDO('mysql:host=localhost;dbname=test', 'root', '');
  $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING);
  $sth = $dbh->query('
    SELECT * FROM guestbook
  ');
  $result = $sth->fetchAll(PDO::FETCH_ASSOC);
  $cache->Create($result);
}
$donnees = $cache->Get();
foreach($donnees as $line)
{
  echo $line['id'].'<br />';
  echo $line['pseudo'].'<br /><hr />';
}
$cache = new CacheManager('test2', 60, true); // Le cache sera généré par une tamporisation de sortie et sera actualisé si il date de plus de X secondes (ici, 60)
if(!$cache->Exist())
{
  $cache->BufferInit();
  mysql_connect('localhost', 'root', '');
  mysql_select_db('test');
  $resultat = mysql_query('SELECT * FROM guestbook');
  while ($donnees = mysql_fetch_assoc($resultat)) {
    echo $donnees['pseudo'].'<br />';
    echo $donnees['id'].'<br />';
  }
  $cache->Create();
}
echo $cache->Get();

Merci !

---|--

Et sinon, une autre question, dans une application structurée en MVC, peut-on instancier des classes dans le controlleur ?

Par exemple, avec ma classe de cache si-dessus, serait-il logique de faire une condition if(cache exists) dans le contrôleur ou je dois essayer de caser les appels aux objets au modéle ?

 

Merci d'avance :)

 

Graphox.

 

Réponses apportées à cette discussion

Par Emacs  -  Le 24/05/2009 12:50  -  Haut de page  - 

Salut,

Ton code est plutôt pas mal mais il a quelques problèmes... Déjà, je te conseille d'éviter d'écrire tes noms de méthodes avec une majuscule en première caractère. C'est laid et ce n'est pas conventionnel. Ecris tes méthodes en "lower camel case" comme je le fais dans le code ci-dessous.

Autre point négatif, certaines méthodes sont trop longues et ont un niveau d'imbrication conditionnelle trop élevé. Il est préférable de limiter le niveau d'imbrication en réfléchissant par négation. C'est à dire, tu testes d'abord que tu ne peux pas faire une action et dans ce cas tu jettes une exception ou bien tu retournes directement un FALSE. De cette manière, le code se lit de haut en bas séquentiellement et non de haut en bas et de gauche à droite. Ca le rend beaucoup plus simple à maintenir.

Enfin, il existe plusieurs types de cache (caches html, caches de requêtes SQL, cache de tableaux PHP sérialisés, caches d'objets PHP sérialisés... ). Il faut donc que tu aies au moins une classe de cache par type de cache. L'idéal est donc de centraliser la logique commune dans une classe abstraite comme je le fais ci-dessous. Puis tu dérives cette classe et tu instancies ces objets dérivés.

<?php abstract class CacheManager{  private $_cacheDir;  private $_lifeTime;  private $_cacheKey;   public function __construct($cacheKey, $lifeTime)  {    $this->_cacheKey = $cacheKey;    $this->_lifeTime = (int) $lifeTime;    $this->_cacheDir = '/tmp';  }   public function cache($content)  {    if (!file_exists($this->getCacheDir()))    {      throw new Exception(sprintf('The directoy "%s" does not exists', $this->getCacheDir()));    }     if (!is_writable($this->getCacheDir()))    {      throw new Exception(sprintf('Directory "%s" is not writable', $this->getCacheDir()))    }     return file_put_contents($this->getCacheFilePath(), $content);  }   public function getContent()  {    return file_get_contents($this->getCacheFilePath());  }   public function exists()  {    return (file_exists($this->getCacheFilePath()) && !$this->isExpired());  }   public function clean()  {    unlink($this->getCacheFilePath());  }   public function cleanCacheDir()  {    foreach (glob($this->getCacheDir().'/*.cache') as $file)    {      unlink($file);    }  }   public function isExpired()  {    return ((filemtime($this->getCacheFilePath()) + $this->getLifeTime()) > time());  }   public function getCacheFilePath()  {    return $this->getCacheDir() . DIRECTORY_SEPARATOR . $this->getCacheFileName();  }   public function getCacheFileName()  {    return $this->getMd5CacheKey() . '.cache';  }   public function setCacheDir($dir)  {    $this->_cacheDir;  }   public function getCacheKey()  {    return $this->_cacheKey;  }   public function getCacheDir()  {    return $this->_cacheDir;  }   public function getMd5CacheKey()  {    return md5($this->_cacheKey);  }   public function getLifeTime()  {    return $this->_lifeTime;  }} class CacheHtml extends CacheManager{ } class CacheArray extends CacheManager{ }

 

Ca te donne une idée d'implémentation. Au final, tu implémentes les ob_start() dans ta classe CacheHtml en redéfinissant les méthodes adéquates.

Concernant le MVC, oui tu peux instancier des classes dans le contrôleur. Pourquoi ne voudrais-tu pas le faire ?

++

Hugo.

 
Par Emacs  -  Le 24/05/2009 12:53  -  Haut de page  - 

En relisant mon code, je rendrai même la méthode cache() abstraite afin de la redéfinir obligatoirement dans les classes dérivées.

 
Par Graphox  -  Le 24/05/2009 14:01  -  Haut de page  - 

Merci beaucoup pour ta réponse, je vais revoir l'organisation de ma class en m'inspirant de ton exemple :)

Pour le MVC, j'avais vu plusieurs définitions, et j'avais compris que le modéle devait récupérer les données et les transmettre au controleur, mais que le controleur ne devait pas s'occuper de la recherche de données.

Je vais donc instancier un objet News, par exemple, et appeler les méthodes dans le controleur.

Mais alors, la class sera le modéle ? (Je devrais placer le code de la class dans mon dossier /modeles)

 

Merci en tout cas,

Graphox.

 
Par Graphox  -  Le 24/05/2009 14:04  -  Haut de page  - 

Désolé du double post, mais pour mes fichiers de cache, qu'elle extension me conseilles-tu ? (j'ai souvent vu des .cache, .cache.php, .html, .php)

merci

 
Par Emacs  -  Le 24/05/2009 17:36  -  Haut de page  - 

Tu peux utiliser l'extension que tu veux mais .cache convient très bien. De nombreux frameworks ou librairies de cache utilisent cette extension.

 
Par Graphox  -  Le 24/05/2009 19:07  -  Haut de page  - 

D'accord merci ! Je vais utiliser .cache alors :)

Et trois autres questions :

  • Le constructeur d'une classe abstraite sert à quoi ? Je dois renseigner les paramètres obligatoires de ce constructeur de la classe abstraite lors de l'instancion de mon objet dérivé ?

  • Je n'ai pas très bien compris ce que le contrôleur devait contenir : tout d'abord, j'inclus ma clase (le modèle donc), puis j'instancie mon objet puis j'appele des méthodes : mais c'est à partir de là que je suis perdu :

Dois-je juste appeler une méthode getNews, par exemple, qui fera tout le travail nécessaire puis appeler la vue juste après, ou je dois découper mon code (getNbrNews, instancion de l'objet pagination, getPagination, des conditions, etc..).

Je pense que découper serait mieux, mais ce n'ai pas "grave" d'avoir un long controleur qui, appele des méthodes, au lieu de faire des requêtes mais fait presque tout ?

Par exemple, un if(isset($_GET['page'])) { $pagination->getLink(); } serait mieux dans le controleur ou le modéle ?

  • J'ai deux modules : articles et projets. Je souhaites utiliser le même code pour les commentaires, donc extactement le même code à part le nom de la table qui change. J'ai pensé à créer un module commentaire et l'utiliser pour mes deux autres modules articles et projets. Miais quelle est la meilleure façon de spécifier au module commentaires que je veux les commentaires pour les articles, et donc changer le nom de la table ?

Merci en tout cas pour tes conseils, et merci aussi pour les tutos sur la POO, ça m'a permis de débuter en OO :)

 
Par heavenfr  -  Le 11/08/2009 00:28  -  Haut de page  - 

@Emacs : Ton exemple de gestion du cache est sympa, mais pourrais-tu donner un exemple pour les classes filles stp ? Je n'ai pas bien compris comment l'utiliser ensuite oO

Pourquoi ne pas faire simplement une classe cache qui met en cache tout le fichier ?

 

 

Ajouter une réponse à la discussion

Seuls les membres connectés sont autorisés à poster dans les forums !

Identifiez-vous
Join |  ID/MDP? |