Avis sur ma class CacheManager + question MVC
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
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.
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.
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.
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
Tu peux utiliser l'extension que tu veux mais .cache convient très bien. De nombreux frameworks ou librairies de cache utilisent cette extension.
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 :)
@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 ?