php cache objet

Cache objet dans des fichiers

Comment cacher des objets dans des fichiers pour soulager son serveur MySQL

cache, objet

Date de publication : 2010-08-10 17:24:55

Suite à cet article paru sur WRI je me décide enfin à partager avec vous une version allégée de ma classe qui permet de cacher des objets en php.

A lire aussi :

Pourquoi une version allégée ?

Tout simplement car je pense que la version brute est plus complexe à comprendre et que l'idée de ce partage est de vous permettre d'implémenter votre propre classe de cache objet.

Le résultat final

Une fois n'est pas coutume je commence par la fin pour vous montrer le résultat final. Dans l'exemple je simule l'appel de l'album ayant pour identifiant 1 dans ma base de données.


<?php
$album = ObjectCache::getInstance('Album',1);
var_dump($album);
?>

Ce qu'affiche ce code :


object(Album)[1]
public 'tracks' =>
array
0 => int 1
1 => int 2
2 => int 3
public 'artistId' => int 1
public 'title' => string 'Lorem ipsum' (length=11)
public 'valid' => boolean true
protected 'duration' => int 86400

Si vous êtes familier du modèle MVC cet exemple d'utilisation du cache objet intervient au niveau modèle mais l'utilisation de ce type de gestionnaire ne s'arrête pas là.

Personnellement j'utilise ce système de cache aussi bien sur mes templates que pour cacher mes contrôleurs et leurs paramètres.

Classe de cache objet dans des fichiers

Le code que je vous propose est commenté et j'espère que cette lecture vous inspirera pour coder votre gestionnaire de cache.

Voici une liste des fonctionnalités qu'il serait intéressant d'ajouter :

  • Utilisation de memcache
  • Utilisation d'apc
  • Régénération forcée des objets (appel d'objet "frais")

<?php
/**
* Cache d'objet dans des fichiers
* @author Patrick Poulain
* @license GPL
* @see http://petitchevalroux.net
*/
class ObjectCache
{
/* @var boolean Vrai si l'objet est valide*/
public $valid = false;
/* @var integer Durée en seconde de l'objet en cache */
protected $duration = 86400;
/* @var array Objets en cache ram php (gestion du multiton) */
private static $instances = array();
/* @var string Chemin où sont stockés les objets */
const cachePath = '/tmp/cache/';

/**
* Constructeur des objets cachés
*
* Private pour ne pas autoriser les appels directs aux classes
*
* @param string $id
*/
protected function __construct($id)
{
$this->valid = false;
}
/**
* Retourne l'instance d'un object depuis le cache ou le crée si nécessaire
*
* @param string $class classe de l'objet
* @param string/int $id identifiant de l'objet
* @return ObjectCache
*/
public static function &getInstance ($class, $id)
{
$strId = (string)$id;
$instance = &self::$instances[$class][$strId];
/*Si l'instance n'est pas déja en RAM du process php*/
if (isset($instance) === false)
{
/*Construction du path du fichier de cache*/
$path = self::cachePath.$class.'/'.(isset($strId[0]) === true ? $strId[0] : '0').'/';
/*On répartit pour limiter le risque de dépasser la limite du nombre de fichier par répertoire*/
$path .= (isset($strId[1]) === true ? $strId[1] : '0').'/'.$strId.'.obj';
/*Si le fichier de cache est valide*/
if ((int) @filemtime($path) > $_SERVER['REQUEST_TIME'])
{
/*On récupère la version en cache*/
$object = unserialize(gzuncompress(file_get_contents($path)));
/*Si on a réussi la désérialisation on retourne l'objet*/
if ($object !== false)
{
$instance = $object;
unset($object);
return $instance;
}
}
/*Le cache n'est pas valide ou la désérialisation a échoué on construit à nouveau l'objet*/
$instance = new $class($id);
/*Si l'objet est valide */
if ($instance->valid === true)
{
/*Si l'objet doit être mis en cache*/
if($instance->duration > 0)
{
$cache = gzcompress(serialize($instance));
/*Si l'ecriture du fichier échoue c'est certainement que le répertoire n'existe pas*/
if (false === ($cacheOk = @file_put_contents($path, $cache)))
{
/*On crée le répertoire et on tente a nouveau de créer le fichier de cache*/
mkdir(dirname($path), 0777, true);
$cacheOk = file_put_contents($path, $cache);
}
/*Si on a réussi à ecrire le fichier de cache on le valide pour la durée de cache*/
if($cacheOk !== false)
{
touch($path, $_SERVER['REQUEST_TIME'] + $instance->duration);
}
}
}
/*L'instance n'est pas valide ce n'est pas normal on leve une erreur*/
else
{
trigger_error('Object : ' . $class . '::' . $id . ' nok (url:'.(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '').')', E_USER_WARNING);
}
}
return $instance;
}
}
?>

Utilisation du cache d'objet

Voici une classe sommaire qui donne un exemple d'implémentation d'un modèle d'une classe Album (dans le contexte musical). Pour rester simple, j'ai simulé les requêtes mysql par des tableaux et le modèle de données est des plus simples :

  • La relation artiste<=>album est (1,N)
  • La relation album<=>chanson est (1,N)

<?php
class Album extends ObjectCache {

/* @var array Identifiants des chansons de l'album */
public $tracks = array();
/* @var int Identifiant de l'artiste de l'album */
public $artistId = 0;
/* @var string Titre de l'album */
public $title = '';

protected function __construct($id)
{
/*On simule le résultat d'une requête qui retourne les données de l'album*/
$data = array(
'ALBUM_ID'=>1,
'ARTIST_ID'=>1,
'ALBUM_TITLE'=>'Lorem ipsum'
);
/*Si on a trouvé l'album en base*/
if(isset($data['ALBUM_ID']) === true)
{
$this->title = $data['ALBUM_TITLE'];
$this->artistId = (int)$data['ARTIST_ID'];
$this->valid = true;
/*On simule la requête sur la table de liaison entre ALBUMS et TRACKS*/
$tracks = array(
array('TRACK_ID'=>1),
array('TRACK_ID'=>2),
array('TRACK_ID'=>3)
);
foreach ($tracks as $t)
{
$this->tracks[] = (int)$t['TRACK_ID'];
}
}
}
}
?>

Image : Stopnlook

 
 

b1n@sp1n