Page 1 sur 1

Comportement aléatoire de la méthode destruct

Posté : 08 mars 2007, 14:49
par jojolapine
Bonjour à tous,
je me suis fait un toute petite class de cache aujoud'hui, et y a quelque chose qui cloche...
tout d'abord la classe:
<?php
/**
 * Classe cache
 * Gestion d'un système de cache
 *
 * 
 * Exemple d'utilisation :
 * <code><?php
 * include('cache.class.php');
 * $cache = new cache('cachetest');
 * 
 * $cache->initCache();
 * 
 * echo microtime();
 * 
 * $cache=null;
 * ?></code>
 * 
 * @author Joris Mulliez
 * @package Cache
 */
class cache{
    
    /**
     *  Dossier où l'on stocke les fichiers de cache
     */
    const DOSSIER='./';
    
    /**
     *  Extension des fichiers
     */
    const EXTENSION='.cache.html';
    
    /**
     *  Nom du fichier traité
     */
    private $file=null;
    
    
    /**
     * Constructeur
     *
     * prend en paramètre le nom du fichier *.php, sans l'extension .php
     * @param String $file
     * @return void
     */
    public function __construct($file){
        $this->file=$file;
    }
    
    /**
     * Fonction initCache
     * Si le fichier de cache correspondant ) $this->file existe, on l'inclu, et on arrête tout
     * sinon on démarre la temporisation...
     */
    public function initCache(){
        if(file_exists(self::DOSSIER.$this->file.self::EXTENSION)){
            include(self::DOSSIER.$this->file.self::EXTENSION);
            exit();
        } else {
            ob_start();
        }
    }
    
    /**
     * Fonction clearCache
     * supprime le fichier de cache
     */
    public function clearCache(){
        if(file_exists(self::DOSSIER.$this->file.self::EXTENSION))
            unlink(self::DOSSIER.$this->file.self::EXTENSION);
    }

    /**
     * Fonction _destruct
     * récupère le fruit de la temporisation et le stocke dans le fichier de cache, puis affiche
     * le résultat de la page
     */
    public function __destruct(){
        $content=ob_get_contents();
        ob_end_clean();
        file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content);
        echo $content;
    }
}
?>
elle est toute simple à priori...
donc j'ai fait ceci comme code:
<?php
include('cache.class.php');//on inclu la classe

$cache = new cache('cachetest');//instanciation

if(isset($_GET['clear_cache'])){//permet de vider le cache
    $cache->clearCache();
}

$cache->initCache();//on démarre la gestion de cache
echo microtime();
echo "  bonjour";
$cache=null;// on appel _destruct
?>
voilà le prolème, c'est que si j'enlève le $cache=null; la méthode _destruct n'est pas appelée comme il faut...
En effet, avec le code comme il est, si j'appel http://localhost/cachetest.php plusieurs fois, le résultat est toujours le même => normal, si j'appel http://locahost/cachetest.php?clear_cache l'affichage change => toujours normal, et si je reviens à l'url de départ, l'affichage ne change plus, c'est bon... tout fonctionne!
Seulement si j'enlève $cache=null; et que je compte sur php pour appeler la méthode _destruct à la fin de l'éxécution, et bien ça ne marche, car après être passé par clear_cache, l'affichage change tout le temps, car le fichier de cache n'a pas été créé...
Peut-être est-ce du à une mauvaise config de php ?
Je suis tout ouïe...

Posté : 08 mars 2007, 16:28
par titerm
Le probleme, c'est que php va automatiquement appeler ob_end_flush tout seul avant de détruire les objets....

C'est donc a toi de faire en sorte que les objets soit détruit avant le flush automatique, (auto_append_file, callback sur ob_start , etc...)

par contre je te suggère quelques optimisations:

dans ta fonction initCache

un readfile() au lieu de ton include... Meme résultat mais tu ne fais pas parser a php un fichier qui ne contient pas de php...

Dans ta fonction __destruct()
un ob_get_flush() bien placé ...

file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,ob_get_flush());

Posté : 08 mars 2007, 16:38
par jojolapine
Le probleme, c'est que php va automatiquement appeler ob_end_flush tout seul avant de détruire les objets....

C'est donc a toi de faire en sorte que les objets soit détruit avant le flush automatique, (auto_append_file, callback sur ob_start , etc...)
ça veut dire que je ne peux pas à priori éviter d'appeler explicitement __desctruct() ?
un readfile() au lieu de ton include...
je change tout de suite :)
Dans ta fonction __destruct()
un ob_get_flush() bien placé ...

file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,ob_get_flush());
la par contre, je ne peu pas, car sinon je n'ai plus de $contenu à afficher... mais je peux faire ça:
public function __destruct(){
        $content=ob_get_flush();
        file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content);
        echo $content;
    } 

Posté : 08 mars 2007, 16:44
par jojolapine
sur le coup de la méthode __destruct(), la dernière solution que j'ai proposée ne fonctionne pas, je ne sais pas pourquoi, ob_start() n'a pas l'air de fonctionner quand je change ma fonction destruct...

Posté : 08 mars 2007, 16:48
par jojolapine
hop ça y est j'ai réussit à faire la méthode destruct comme ceci
public function __destruct(){
        $content=ob_get_clean();
        file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content);
        echo $content;
    }
utilisation de ob_get_clean, plutôt qu ob_get_flush...
ça fonctionne nickel, il n'y a plus que le fait d'appeler eplicitement cette méthode qui me chagrine... :cry:

Posté : 08 mars 2007, 16:59
par titerm
ob_get_flush() affiche le buffer (equivalent de ton echo $content) et retourne le contenu du buffer dans une string (que tu ecrit dans ton fichier)
Essai... tu veras, ca fonctionne

Posté : 08 mars 2007, 18:11
par jojolapine
je croit que tu faits erreur, car ob_get_flush retourne bien le buffer, mais ne l'affiche pas...
De plus, je doit stopper en plus la bufferisation avec ob_clean, si j'utilise ob_get_flush, alors que ob_get_clean(), fait ceci:
ob_get_clean() exécute successivement ob_get_contents() et ob_end_clean().

Posté : 08 mars 2007, 18:29
par titerm
Je ne peux que redire ce que je t'ai déjà dis... essai...

ob_get_flush — Flush the output buffer, return it as a string and turn off output buffering

Note: This function is similar to ob_end_flush(), except that this function returns the buffer as a string.
ob_end_flush — Flush (send) the output buffer and turn off output buffering

Posté : 08 mars 2007, 18:35
par jojolapine
je te rassure, j'ai déja fait des essais...
à saoivr, si je veux utiliser ob_get_flush.. voici ce que ça donne dans le code:
public function __destruct(){
        $content=ob_get_flush();
        ob_end_clean();
        file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content);
        echo $content;
    }
tandis qu'avec ob_get_clean():
public function __destruct(){
        $content=ob_get_clean();
        file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content);
        echo $content;
    }
et je le répète, ob_get_flush() n'affiche rien sur la sortie standard...

Posté : 08 mars 2007, 18:47
par titerm
Déjà, le ob_end_clean() sert a détruire les données s il y en a et a arreté la bufferisation, le ob_get_flush vide déjà le tampon et stop la buffurisation.
Donc appeler ob_end_clean apres un ob_get_flush ne sert a rien.

ensuite concernant ob_get_flush.... tu ne sembles pas lire la doc , je t'ai pourant extrait les parties explicites.... alors un petit exemple...
ob_start();
echo microtime();
echo "bonjour<hr>";
$foo = ob_get_flush();
echo $foo;
Si d'après toi, le ob_get_flush n'affiche rien, tu ne devrait avoir qu'un seul bonjour a l'écran.... Copy paste ce bout de code et devra bien admetre que ce que flush signigie en anglais...

Posté : 08 mars 2007, 18:52
par jojolapine
je reste sur a position, ob_get_flush() n'affiche, rien, il retourne quelquechose, mais n'affiche rien, c'est echo sui sert à afficher le contenu du buffer... ensuite, ob_get_flush() m'est insuffisant, car il me faut stopper la buffurisation, d'où ob_get_clean() :wink:

Posté : 08 mars 2007, 19:00
par titerm
Copy ce bout de code, lance le et affiche ici le résultat....

J'ai ajouter quelque ligne pour mettre en évidence l'inutilité de ob_end_clean() dans le cas présent

echo "niveau de cache  =>".ob_get_level() ."<br>";
ob_start();
echo "niveau de cache  =>".ob_get_level() ."<br>";
echo microtime();
echo "bonjour<hr>";
$foo = ob_get_flush();
echo "ob get flush est terminé<hr>";
echo "niveau de cache  =>".ob_get_level() ."<br>";
echo $foo;

Quand tu aura paster les résultats de ce code, on pourra en discuter si tu veux...

Posté : 08 mars 2007, 19:03
par jojolapine
voici le résultat...:

Code : Tout sélectionner

niveau de cache =>0<br> niveau de cache =>1<br> 0.07812900 1173373293bonjour <hr> ob get flush est terminé <hr> niveau de cache =>0<br> niveau de cache =>1<br> 0.07812900 1173373293bonjour <hr>
...

Posté : 08 mars 2007, 19:06
par jojolapine
ça y est j'ai compris...
voici ma fonction au final
public function __destruct(){
        $content=ob_get_flush();
        file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content);
    }
mais je ne lacherai pas, ob_get_flush() n'affiche rien, c'est php qui éxécute ob_flush() en cloturant le traitement de php...
Ayé, le débat est clos :langue:

Posté : 08 mars 2007, 19:12
par titerm
Je vois 2 fois bonjours alors que je n'ai fais qu'un seul echo ... Il y a bien un echo effecué juste avant le ob_get_flush()...

je vois aussi qu'après le ob_get_flush, je suis de retour au niveau 0, le meme niveau qu'avant l'appel de ob_start()... Il n'y a donc plus de bufferisation en cours.


A part te montrer les sources PHP en c, je vois pas ce que je peux faire d'autre....