Page 1 sur 3

Fonction exit() et classe...

Posté : 09 oct. 2007, 14:28
par jojolapine
Bonjour à tous,
je suis en train de créer une petite classe de cache, et j'ai un petit problème,
dans ma méthode initCache, si le fichier de cache existe déjà, je l'inclus et j'arrête le script (fonction exit()) seulement la fonction exit() n'a pas l'air de fonctionner comme il faut, car ma méthode __desctruct() est appelée malgré tout...
Y a t'il une quelconque configuration à mettre en place? ou faut t'il que je rajoute une variable "flag" pour dire si oui ou non on exécute la fonction destruct...

voici la classe (pas finie):
<?php
/**
 * Classe cache
 * classe de gestion de cache
 * 
 * Exemple d'utilisation :
 * <code>
 * <?php
 * include('path/cache.class.php');
 * $cache= new cache($_SERVER['PHP_SELF']);
 * 
 * //script de la page
 *
 * $cache=null;
 * ?>
 * </code>
 * 
 * @author Joris Mulliez
 * @package Cache
 */ 
class cache {

  /**
   *  Dossier où l'on stocke les fichiers de cache
   */
  const DOSSIER='./cache/';
  
  /**
   *  Extension des fichiers
   */
  const EXTENSION='.cache.html';
  
  /**
   *  Indique si on doit créer le dossier de cache ou non
   */
  const CREATE_DIR=false;
  
  private 
  private $file=null;
  
  public function __construct($file=''){
    if(empty($file)){
      $this->file=$this->clearUri($_SERVER['REQUEST_URI']);
    } else {
      $this->file=$this->clearUri($file);
    }

    if(!is_dir(self::DOSSIER)){
      if(self::CREATE_DIR){
        //création du dossier
      } else {
        throw new Exception('Le dossier '.self::DOSSIER.' n\'existe pas.');
      }
    }
  }
  
  /**
   * Fonction clearUri
   * Simplifi la valeur de REQUEST_URI pour que accueil-bonjour.html?var=3 devienne accueil-bonjourhtmlvar3
   */
  private function clearUri($uri){
    return preg_replace('#[^a-zA-Z0-9_-]#','',$uri);
  }
  
  /**
   * 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_flush();
      file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content.'<!-- fichier de cache générer le '.date("d/m/Y \à H:i:s").' -->');
  } 

}
?>
Voilà et c'est appelé comme ça:
<?php
try{
  $cache=new cache();
} catch (Exeption $e){
  echo $e->getMessage();
}
$cache->initCache();

//code normal

$cache=null;
?>
merci d'avance ;)

Posté : 09 oct. 2007, 15:36
par Berzemus
Ca ne va sans doute pas te plaire, mais regarde ici (1er commentaire) :
http://www.php.net/manual/nl/function.die.php

die() (un equivalent de exit()) n'empêche pas les destructeurs de s'exécuter.

Apparament il en est vraiment de même avec exit().

Posté : 09 oct. 2007, 15:58
par jojolapine
C'est vrai que c'est ce qui à l'air de se passer... :(
Mais j'ai souvenir d'avoir déja utiliser une classe similaire à celle-ci qui fonctionnait...
En attendant d'autres avis, j'ai créer une variable supplémentaire, sui si elle est false shunte la fonction __destruct, mais c'est pas très propre :? et ça me plait moyen...

Posté : 09 oct. 2007, 17:57
par Invité
Tu peux, pour stoper ton script utiliser la fonction break

http://fr.php.net/manual/fr/control-str ... .break.php

Elle te permet de faire une verrification sur ton code et de voir pourquoi la fonction exit() n'est pas enclanchée.

Re: Fonction exit() et classe...

Posté : 09 oct. 2007, 20:36
par Hubert Roksor
Tu peux, pour stoper ton script utiliser la fonction break
Euh... seulement dans une boucle. Ce n'est pas comme dans d'autres langages où "break" sert à arrêter l'exécution du script. En PHP "break" c'est comme un "jump".

@jojolapine: pour être honnête je suis même étonné que ton script mette en cache quoi que ce soit parce que je pensais que l'output buffer était détruit avant l'appel aux destructors. Puisque les objets sont détruits vraiment au dernier moment de la vie d'un script je déconseille d'afficher quoi que ce soit dans une méthode __destruct(). À la place, utilise un callback dans ton ob_start(). Exemple non-testé :
public function initCache(){
    if(file_exists(self::DOSSIER.$this->file.self::EXTENSION)){
        include(self::DOSSIER.$this->file.self::EXTENSION);
        exit();
    } else {
        ob_start(array($this, 'ob_end'));
    }
}

public function ob_end($content){
    file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content.'<!-- fichier de cache générer le '.date("d/m/Y \à H:i:s").' -->');
}

Posté : 09 oct. 2007, 23:13
par jojolapine
bonsoir,
alors j'ai essayer ta méthode hubert, et ça ne fonctionne pas... :( voici ce que j'ai fait:
  public function initCache(){
      if(file_exists(self::DOSSIER.$this->file.self::EXTENSION)){
          include(self::DOSSIER.$this->file.self::EXTENSION);
          $this->create_file=false;
          exit();
      } else {
          echo 'ahah';
          ob_start(array($this, 'ob_end')); 
          echo 'hihi';
      }
  }

  public function ob_end($content){
        file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content.'<!-- fichier de cache générer le '.date("d/m/Y \à H:i:s").' -->');
      
  } 
et je n'ai que le 'ahah' qui apparait, donc la fonction ob_end ne doit pas être trouvée... :-k
je sais pas trop quoi faire...

Posté : 09 oct. 2007, 23:21
par Hubert Roksor
C'est parce que j'ai oublié le retour de ob_end(). Il faut faire un return $content; pour afficher le contenu.

Au fait, j'ai remarqué que tu faisais un include sur un fichier HTML : s'il ne contient pas de PHP, il vaut mieux utiliser readfile(), plus rapide et plus sûr.

Posté : 09 oct. 2007, 23:34
par jojolapine
Alors j'ai rajouter le return à la fonction ob_end, et les pages s'affiches, mais il n' y a pas de mise en cache...
En fait la fonction ob_end ne semble pas appelée, des echo à l'intèrieur de cette dernière ne donnent rien :-k

merci encore pour l'aide ;)

edit: par contre, j'ai bien le 'hihi', mais en tout début de page...

Posté : 10 oct. 2007, 00:35
par Hubert Roksor
des echo à l'intèrieur de cette dernière ne donnent rien
Ça c'est normal, il ne faut rien afficher dans un callback appelé par ob_start(). Pour afficher des trucs en plus, il faut simplement modifier la valeur de retour.

Pour le reste, voici le script que j'ai utilisé pour tester (sous CLI, mais ça devrait faire la même chose via serveur web)
class cache
{
	const FILE = './cache.txt';

	public function initCache() { 
		if (file_exists(self::FILE)){ 
			readfile(self::FILE);
			exit;
		} else { 
			ob_start(array($this, 'ob_end'));    
		}   
	}   

	public function ob_end($content){   
		file_put_contents(self::FILE,$content.'<!-- fichier de cache généré le '.date('d/m/Y \\à H:i:s').' -->');
		return $content;
	}
}

$cache = new cache;
$cache->initCache();

echo time();
PS: concernant le terme anglais "contents", on l'utilise généralement au pluriel (http://dictionary.reference.com/browse/content)

Posté : 10 oct. 2007, 10:09
par jojolapine
Bonjour,
alors j'ai testé directement ton code dans un fichier à part, résultat, si cache.txt n'éxiste pas, la page s'affiche, mais rien n'est mis en cache (pas de ficheir créer).
Si je crée le fichier à la main: page blanche (c'est d'ailleur ce qui se passait avec mon code)
Donc je ne sais pas si on "passe" dans la fonction ob_end, mais en tout cas si c'est le cas, c'est file_put_contents qui ne marche pas...

Posté : 10 oct. 2007, 18:40
par Hubert Roksor
Si je crée le fichier à la main: page blanche
Ça voudrait dire que readfile ne marche même pas, donc ça ferait plus penser à un problème de config. Mets le error_reporting au maximum, assure-toi de logger les erreurs dans un fichier et vois ce qu'il se passe. Le bug numéro un à résoudre c'est readfile().
Donc je ne sais pas si on "passe" dans la fonction ob_end, mais en tout cas si c'est le cas, c'est file_put_contents qui ne marche pas...
C'est l'explication la plus probable. Peut-être n'as-tu pas les droits nécessaire sur le dossier. Il faut savoir que ce qu'il se passe dans ob_end() est un petit peu en dehors du continuum espace-temps : si une erreur se produit, elle ne sera pas affichée (comme tes "echo" un peu plus haut). C'est pour cela qu'il faut logger les erreurs dans un fichier.

Essaie de supprimer le fichier de cache et commente la ligne du file_put_contents(), et tu verras bien si tu passes par ob_end() ou pas. Ensuite, essaie de voir ce qu'il se passe avec readfile().

Posté : 10 oct. 2007, 18:54
par jojolapine
Alors en fait si readfile fonctionne bien, milles pardon, j'ai créer un fichier vide, donc normal pour la page blanche... dsl :-*

Pour ce qui est de la fonction ob_end...
Je vais essayer de commenter file_put_contents (je suis pas chez moi je peux pas essayer)
Et j'essayerais aussi d'appeler directement ob_end sans passer par ob_start...
Pour voir... et tu dits que chez toi ça marche? pourtant j'ai une config très "permissive" je suis en local donc bon... :-k

Autre chose, dans ma classe de départ modifiée avec un rajout de variable "flag"
<?php
/**
 * Classe cache
 * classe de gestion de cache
 * 
 * Exemple d'utilisation :
 * <code>
 * <?php
 * include('path/cache.class.php');
 * $cache= new cache($_SERVER['PHP_SELF']);
 * 
 * //script de la page
 *
 * $cache=null;
 * ?>
 * </code>
 * 
 * @author Joris Mulliez
 * @package Cache
 */ 
class cache {

  /**
   *  Dossier où l'on stocke les fichiers de cache
   */
  const DOSSIER='./cache/';
  
  /**
   *  Extension des fichiers
   */
  const EXTENSION='.cache.html';
  
  /**
   *  Indique si on doit créer le dossier de cache ou non
   */
  const CREATE_DIR=false;
  
  private $create_file=true;
  private $file=null;
  
  public function __construct($file=''){
    if(empty($file)){
      $this->file=$this->clearUri($_SERVER['REQUEST_URI']);
    } else {
      $this->file=$this->clearUri($file);
    }

    if(!is_dir(self::DOSSIER)){
      if(self::CREATE_DIR){
        //création du dossier
      } else {
        throw new Exception('Le dossier '.self::DOSSIER.' n\'existe pas.');
      }
    }
  }
  
  /**
   * Fonction clearUri
   * Simplifi la valeur de REQUEST_URI pour que accueil-bonjour.html?var=3 devienne accueil-bonjourhtmlvar3
   */
  private function clearUri($uri){
    return preg_replace('#[^a-zA-Z0-9_-]#','',$uri);
  }
  
  /**
   * 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);
          $this->create_file=false;
          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(){
      if($this->create_file){
        $content=ob_get_flush();
        file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content.'<!-- fichier de cache générer le '.date("d/m/Y \à H:i:s").' -->');
      }
  } 

}
?>
et bien le file_put_contents fonctionne bien (à vrai dire la classe fonctionne très bien comme ça, mais je trouve pas ça "propre", et j'aimerais bien arriver à faire fonctionner ta solution)

voilà tout pour aujourd'hui, je reposterais quand j'aurais pu faire mes tests ;)
bonne soirée



Edit: 14h et des poussières
Eureka, j'ai à priori localisé le problème...
il vient bien de la fonction file_put_contents...
j'ai mis ça dans la fonction ob_end:
  public function ob_end($content){
        $retour=file_put_contents(self::DOSSIER.$this->file.self::EXTENSION,$content.'<!-- fichier de cache générer le '.date("d/m/Y \à H:i:s").' -->');
        if($retour===false) $retour="false";
        return $content.'valeur de $retour='.$retour;
      
  } 
et dans le source de la page, j'ai bien
valeur de $retour=false
(au passage, c'est la galère de débugger avec de la bufferisation... :roll: )
Donc maintenant que je sais ça...
Et ben je vois pas ce qui faut que je fasse de plus?
Avant d'utiliser ta méthode, file_put_contents fonctionnait, et je n'ai pas touché aux droits de fichier depuis... (je suis sous windows en plus, donc c'est moins important que sous linux)
Quoique je viens de voir, c'est important le fait que le dossier "cache" soit en lecture seule?

Edit: 16h30
J'avance doucement, j'ai tenté un fopen en "w+" ou "x+" et aucun résultats...
Les retours sont toujours négatifs...
Je me demande si j'ai pas un petit problème de config...?

Edit: 16h42
J'ai testé de créer un fichier test.php comme ceci:
<?php
file_put_contents('./cache/test.cache.html','contenu');
?>
et bien le fichier test.cache.html est bien créé, bien rempli, tout correct quoi...
Donc ce qui cloche c'est lorsque j'utilise file_put_contents à l'intérieur de ob_end...
Et je précise que le problème ne vient pas du nom de chemin de fichier variable, car après tests, les nom de fichiers sont tout à fait "normaux"...
ex:
./cache/regrave2accueilhtml.cache.html
Donc je penche à priori pour un problème de configuration, mais je suis pas très fortiche là dessus :s

Edit: 16:47
C'est quand même pénible de pas pouvoir poster un nouveau message dans un cas comme ça... :(

Posté : 11 oct. 2007, 20:08
par Invité
Bonsoir,
Alors je préviens tout de suite désolé d'avoir recours à ce genre de stratagèmes...
Mais oui je vous le dit, c'est bien jojolapine qui se cache derrière cet anomyme...
Bon je sais c'est pas bien de faire des ups, mais dans ce cas de figure, après avoir éditer 200 fois mon message (non?) je ne pense pas que ces modifications et attirées l'oeil des passants éventuels, et surtout n'ont pas envoyer d'emails au participants de cette discussion...
Donc euh mille excuse...
Mais euh voilà :oops:

Posté : 11 oct. 2007, 22:26
par Hubert Roksor
Quoique je viens de voir, c'est important le fait que le dossier "cache" soit en lecture seule?
À ma connaissance, tu n'es pas sensé pouvoir créer de fichiers dans un dossier en lecture seule.
C'est quand même pénible de pas pouvoir poster un nouveau message dans un cas comme ça... :(
On demande aux gens d'attendre un peu avant de faire remonter leurs propres sujets, pour éviter les abus et pour forcer les gens à prendre le temps de réfléchir à leur problème avant de reposter.

Si file_put_contents() ne fonctionne pas, tu devais avoir une erreur dans le fichier log de PHP, n'oublie pas d'activer error_log.

Sinon, tu peux essayer de stopper la bufferisation en fin de script sans invoquer ob_end() puis invoquer ob_end() directement, en dehors de tout output buffer, pour voir les messages d'erreur éventuels. Pour ce faire, en fin de script :
// avec ou sans "echo", d'ailleurs
echo ob_end(ob_get_clean());

Posté : 11 oct. 2007, 23:18
par jojolapine
Alors tout d'abord pardon pour le up... je referais pu... :(

Pour notre problème, j'ai regardé les logs d'erreurs php, et j'ai ceci:
[11-Oct-2007 23:10:20] PHP Warning: file_put_contents(./cache/regrave2accueilhtml.cache.html) [<a href='function.file-put-contents'>function.file-put-contents</a>]: failed to open stream: No such file or directory in C:\Program Files\wamp\www\regrave2\includes\cache.class.php on line 94
donc on avait bien identifié la source de l'erreur...
Maintenant tu dis
À ma connaissance, tu n'es pas sensé pouvoir créer de fichiers dans un dossier en lecture seule.
Et ben on a un petit problème... lorsque j'essaye de "décocher" lecture seule, j'applique les modifications, je reviens voir les propriétés et paf, il est de nouveau en lecture seule, j'ai essayer de supprimer recréer le dossier, rien n'y fait...
Je ne sais pas ou je peux trouver les options plus générales de dossier...?
En tout cas merci encore de t'intéresser à mon cas :D