Débogage d'un code php (suivit des variables)

Description

  • La classe originalcompo\Debug est une classe PHP5 d'aide au débogage de code PHP permettant l'affichage du contenu de variables PHP, mais aussi de la définition d'une classe à partir d'une de ses instances (utilisation de la reflexion).
  • Sa particularité est que l'affichage se fait sur une fenêtre HTML à part, qui est créée sur le serveur (qui contient le script à déboguer).
  • Avantage: sur une page web, l'affichage de celui-ci n'est pas polué, les valeurs sont toujours visibles (plus de problème avec la mise en page css au moment ou on écrit la trace...)
  • Il y a deux versions possibles:
  • chaque page html est tracée indépendemment: toto.php --> toto.php.Debug.html
  • une trace unique cumulant les traces: toto.php, tata.php --> trace.html


Ce code a été écrit dans un but pratique (aider au débogage de PHP) mais aussi didactique:
  • il existe deux méthodes pour sauver la trace dans un seul fichier: en travaillant à partir d'une sauvegarde XML ou d'une sauvegarde de l'objet sérialisé;
  • il existe deux méthodes dans la version XML: en travaillant avec DOM ou avec l'objet SimpleXMLElement


A chaque fois, l'utilisation de l'une ou l'autre des méthodes amène au même résultat. Cela m'a juste permis de tester plusieurs manières de faire !

Les explications du code sont incluses dans celui-ci

Source / Exemple :


<?php
namespace originalcompo;
/** -----------------------------------------------------------------------------------------------
Ce code a été écrit dans un but pratique (aider au débogage de PHP) mais aussi didactique:
- il existe deux méthodes pour sauver la trace dans un seul fichier: en travaillant à partir d'une 
  sauvegarde XML ou d'une sauvegarde de l'objet sérialisé;
- il existe deux méthodes dans la version XML: en travaillant avec DOM ou avec l'objet 
  SimpleXMLElement
A chaque fois, l'utilisation de l'une ou l'autre des méthodes amène au même résultat.
Cela m'a juste permis de tester plusieurs manière de faire !
---------------------------------------------------------------------------------------------------
La classe Debug est une classe d'aide au débugage de code PHP permettant l'affichage de variables 
PHP intermédiaires.
Sa particularité est que l'affichage se fait sur une fenêtre HTML à part, qui est créée sur le 
serveur (où se trouve ce script).
Avantages: si on travaille sur une page web, l'affichage de celui-ci n'est pas polué, les valeurs 
sont toujours visibles (plus de problème avec la mise en page css au moment ou on écrit la trace...) 
Il y a deux versions possibles:
- chaque page html est tracée indépendemment: toto.php --> toto.php.Debug.html
- une trace unique cumulant les traces: toto.php, tata.php --> trace.html
---------------------------------------------------------------------------------------------------
2010/08/14 : 
- Pas d'écriture dans le fichier si $updateTrace est à false
- Possibilité d'afficher la structure d'une classe à partir d'une instance de celle-ci

2010/08/15 :
- Affichage de la classe parente ainsi que des interfaces utilisées 
- Interdiction du clonage 
- Notation HEREDOC

2010/08/16 :
- Rajout d'un espace de nom 
(la classe ocDEBUG a été renommé en Debug, plus standard, l'espace de nom permettant de garantir 
l'unicité de la classe)

2010/08/17 : 
- Corrections de l'interprétation des span dans certains cas (fonction "describe_variable") 

2010/08/19 : 
- Possibilité de générer les traces de plusieurs fichiers php dans un seul fichier 
(refresh y compris) via la sauvegarde dans un fichier xml des résultats intermédiaires

2010/08/24 : 
- rajout de deux liens: Clear (pour vider la trace) et Refresh (équivalent au F5 du navigateur)
- Possibilité de générer les traces de plusieurs fichiers php dans un seul fichier 
(refresh y compris) via la sauvegarde de l'objet serialisé

  • /
/** ----------------------------------------------------------------------------------------------- Le fait de définir un espace de nom fait que les classes/fonctions/constantes sont recherchées dans cet espace. - Pour les fonctions et constantes, PHP va aller les chercher dans l'espace global s'il ne peut les trouver dans l'espace de noms courant. - Les noms de classes sont toujours résolus avec l'espace de noms courant. Il faut donc ici préciser que les classes ReflectionClass et ReflectionMethod sont dans l'espace de nom global "\". On peut le faire en utilisant directement l'espace de nom lors de l'utilisation de la classe... exemple: $reflectMethod = new \ReflectionMethod($className, $fct->name); ...ou bien en utilisant un alias comme cela est fait dans le code ("use")
  • /
use \ReflectionClass as ReflectionClass, // \a\b\c as c; équivalent à \a\b\c; \ReflectionMethod, \SimpleXMLElement, \DomDocument, \DomXPath; class Debug { const ONETRACE_XML = 'trace.xml'; // nom du fichier de sortie si trace unique const ONETRACE_SER = 'trace.ser'; // nom du fichier de sortie si trace unique const ONETRACE_HTML = 'trace.html'; // nom du fichier de sortie si trace unique const EXT = '.Debug.html'; // extension du fichier de sortie si "une trace par fichier" const ENCODING = 'ISO-8859-1'; // const ENCODING = 'UTF-8'; private $BEGIN; // "constante" private $END; // "constante" private $END_XML_SER; // "constante" private $nbRaccourcis = 0; private $trace = array(); private $inittrace = array(); // sert au chargement de la page // Il est possible d'afficher l'heure d'écriture des variables public $withTime = false; // voir SaveToXML() : public $checksymbols = true; private $original4xml = array(); private $_replace4xml = array(); private $oldchar4xml; private $newchar4xml; // ---------------------------------------------------------------------------------------------- private $updateTrace = true; public function get_UpdateTrace() { return $this->updateTrace; } public function set_UpdateTrace($new) { if ($new != $this->updateTrace) { $this->updateTrace = $new; if ($this->updateTrace) $this->update_debughtml(); } } /** --------------------------------------------------------------------------------------------- Il y a 2 types d'informations qui ont un affichage particulier: les objets et les tableaux. Ces informations pouvant occuper beaucoup de place, pour faciliter/alléger le parcours de la page de débugage, on peut choisir de réduire ou développer ces informations, directement sur la page HTML, en appuyant respectivement sur le "-" ou le "+" qui apparait à côté de ce type de variables. - si un '+' est affiché, l'information est minimisé - si un '-' est affiché, toute l'information est affichée La propriété collapsed permet de choisir l'affichage par défaut au démarrage: true pour tout réduire, false pour tout afficher NOTES: la fonctionnalité réduire/développer est gérée via javascript. Des chaines de caractères sont générées par PHP et passées en paramètre d'une fonction javascript. Le javascript pouvant être interprété différemment selon les navigateurs, il est cnseillé de laisser $collapsed à true par défaut, certains d'entre eux pouvant ne pas réussir à interpréter certaines variables complexes. Par exemple: si $f est une instance de la classe de débugage, le script généré pour réduire/développer le code associée à $f->print_variable($f) fonctionne bien sur Mozilla Firefox mais pas sur IE8. ($f contient des propriétés chaines de caractère avec code HTML et fonctions javascript!)
  • /
public $collapsed = false; /** --------------------------------------------------------------------------------------------- Si on choisit XML pour gérer la trace unique Debug::getInstance('XML'); ici, on décide si on utilise DOM ou SimpleXMLElement pour générer le XML (choix transparent puisque sans conséquence sur le résultat)
  • /
private $DOM = false; //private $DOM = true; /** --------------------------------------------------------------------------------------------- Définition des constantes : Le code CSS et JS était initialement externalisé dans 2 fichiers, mais on a alors un problème d'accès à définir si les différents fichiers à débuguer ne se trouvent pas sous le même chemin (répertoire). Ces codes sont donc directement inclus dans le fichier de trace généré.
  • /
public function init_const() { $JS = <<<JS <SCRIPT type='text/javascript' language='JavaScript1.2'> function dhtml_Click(INFO, VARIABLE, NUMERO) { INFO_ID = "INFO"+NUMERO.toString(); PLUS_ID = "PLUS"+NUMERO.toString(); if (document.getElementById(PLUS_ID).innerHTML == "+") { document.getElementById(PLUS_ID).innerHTML = "-"; document.getElementById(INFO_ID).innerHTML = VARIABLE +"<br /><table><tr><td><PRE>"+INFO+"</PRE></td></tr></table>"; } else { document.getElementById(PLUS_ID).innerHTML = "+"; document.getElementById(INFO_ID).innerHTML = VARIABLE; } } </SCRIPT> JS; $CSS = <<<CSS <style type="text/css"> TABLE, BODY { color: #0000a0; background-color: #ffffff; font-family:sans-serif, arial, times, helvetica; font-size: 10pt;} A:after {content: " "; } A:before {content: " "; } A:link {text-decoration: none; color: #FF0000; font-weight:bold; font-size:14pt;} A:visited {text-decoration: none; color: #FF0000; font-weight:bold; font-size:14pt;} A:active {text-decoration: none; color: #FF0000; font-weight:bold; font-size:14pt;} A:hover {text-decoration: none; color: #339900; font-weight:bold; font-size:14pt;} .changeFile { background-color: yellow; color: #FF0000; text-align: center; } .property { color : #0000a0; } .methodname { color : #0000a0; } .methodparam { color: green; } .titre {color: #FF0000;} .infoplus {color: black;} .constant {color: gray; } PRE { font-size: 8pt; } .italic { font-style: italic; } TABLE { width:90%; margin-left:1.9pt; border: none; border-collapse:collapse; } TH, TD { border:solid red 1.0pt; padding:0cm 5.4pt 0cm 5.4pt; } .TABLE { width:100%; text-align:center;} // margin-left: auto; margin-right: auto; width : 50% </style> CSS; $this->BEGIN = <<<BEGIN <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>DEBUG</TITLE> <meta name='author' content='OriginalCompo' /> <meta name='owner' content='Jean-Pierre Aguado' /> <meta name='Debug unit for PHP 5' content='Debug unit for PHP 5' /> <meta name='keywords' content='PHP5, Debug' /> <META http-equiv="Cache-Control" content="no-cache"> <META http-equiv="Pragma" content="no-cache"> <META http-equiv="Expires" content="0"> {$JS} {$CSS} </HEAD> <BODY> BEGIN; $this->END_XML_SER = <<<END_XMLSER <a name="here"></a> <table class=TABLE><tr> <td width=50%><a target="_self" href="reload.php">Refresh</a></td> <td width=50%><a target="_self" href="clear.php">Clear</a></td> </tr></table> </BODY> </HTML> END_XMLSER; $this->END = <<<END </BODY> </HTML> END; $newXMLLine = chr(10); $this->original4xml = array('<' , '>' , '&' ); $this->_replace4xml = array($newXMLLine.'{i,n,f', 's,u,p}', $newXMLLine.'{e,t]'); $this->oldchar4xml = ','; $this->newchar4xml = ';'; } // ---------------------------------------------------------------------------------------------- private $xmlfile = ''; private $serfile = ''; private $onefile = ''; // ---------------------------------------------------------------------------------------------- static public function getPath() { $d = new Debug(); $reflectClass = new ReflectionClass($d); unset($d); return dirname($reflectClass->getFileName()); } // ---------------------------------------------------------------------------------------------- private function setFiles($mode = '') { if ($mode=='') { $this->xmlfile = ''; $this->serfile = ''; $this->onefile = ''; } else { $chemin = Debug::getPath(); if ($mode=='XML') { $this->onefile = $chemin.'/'.Debug::ONETRACE_HTML; $this->serfile = ''; $this->xmlfile = $chemin.'/'.Debug::ONETRACE_XML; } elseif ($mode='SER') { $this->onefile = $chemin.'/'.Debug::ONETRACE_HTML; $this->serfile = $chemin.'/'.Debug::ONETRACE_SER; $this->xmlfile = ''; } } } /** --------------------------------------------------------------------------------------------- Clear efface les traces cumulatives (dans le cas d'une trace unique)
  • /
public function clear() { $this->trace = array(); // on efface le tableau $this->inittrace = array(); // on efface le tableau $this->nbRaccourcis=0; $this->update_debughtml(true); } /** ============================================================================================= Une seule instante de Debug peut exister à la fois. Le design pattern SINGLETON est utilisé dans cet objectif Pour travailler sur l'instance de Debug à partir de plusieurs scripts PHP, il faut donc chercher l'instance dans chaque script (avec l'espace de nom) ... $instanceDebug = originalcompo\Debug::getInstance(); ... et travailler sur $instanceDebug
  • /
private static $_singleton; private function __construct() { $this->init_const(); } /** --------------------------------------------------------------------------------------------- Il y a 3 appels possibles de getInstance Debug::getInstance(); --> chaque trace est générée dans un fichier indépendant. Ex: si la page web toto.php est en cours de débogage, alors la trace sera sur toto.php.debug.html Debug::getInstance('SER'); --> les traces sont toutes générées dans un unique fichier trace.html se trouvant au même endroit que ce fichier (contenant la classe originalcompo\Debug Pour cela, un cache contenant la classe serialisée est stockée sur le disque dur Debug::getInstance('XML'); --> les traces sont toutes générées dans un unique fichier trace.html se trouvant au même endroit que ce fichier (contenant la classe originalcompo\Debug Pour cela, un cache au format XML contenant une partie du code HTML de la page trace.html est stokée sur le disque dur Note: il n'y a aucune différence à l'exécution entre getInstance('SER') et getInstance('XML'). Par contre il est IMPERATIF de TOUJOURS garder le même choix. Si vous utilisez 'SER' dans un fichier, puis 'XML' pour déboguer un autre, vous n'obtiendrez pas le résultat désiré...
  • /
public static function getInstance($mode = '') // XML, SER, { if (is_null(self::$_singleton)) { if ($mode=='') { self::$_singleton = new Debug(); self::$_singleton->setFiles(); } else { $chemin = Debug::getPath(); if ($mode == 'SER') { self::$_singleton = self::unserializeMe(); self::$_singleton->setFiles('SER'); } elseif ($mode == 'XML') { self::$_singleton = new Debug(); self::$_singleton->setFiles('XML'); try { self::$_singleton->LoadXML(); } catch (Exception $e) { } } self::$_singleton->trace[] = '<p class=changeFile>' .date("Y/m/d H:i:s : ", time()) .$_SERVER['PHP_SELF'] .'</p>'; } } return self::$_singleton; } private function __clone() { // Lorsqu'un objet est cloné, PHP 5 effectue une copie superficielle // de toutes les propriétés de l'objet. Toutes les propriétés qui // sont des références à d'autres variables demeureront des références. } /** ---------------------------------------------------------------------------------------------
  • /
private function serializeMe() { $s = serialize(Debug::getInstance()); file_put_contents($this->serfile, $s); } // .............................................................................................. private static function unserializeMe() { $fichier = Debug::getPath() . '/' . Debug::ONETRACE_SER; return (file_exists($fichier) ? unserialize(file_get_contents($fichier)) : new Debug()); } // ============================================================================================== private function describe_variable($titre, $toprint) { // Le titre est inclus dans une balise qui doit être interprétée, alors que la valeur doit être // affichée en brut (balises html non interprétées) $paramTitre = ($titre=='') ? '?' : htmlentities($titre, ENT_QUOTES, Debug::ENCODING, true); if (isset($toprint)) { $titre_begin = '<span class=titre>'; $titre_end = '</span>'; if ($titre=='') { $titre_begin = $titre_begin.'<span class=italic>'; $titre_end = '</span>'.$titre_end; } if (is_object($toprint) || is_array($toprint)) { // Mise en place d'un lien javascript pour déclencher la fonction réduire/développer ... // '<a href="JavaScript: dhtml_Click($param2_a_js, $param1_a_js); ...' // ... et de (l'équivalent) à l'appel automatique de ce lien au démarrage // '<script language="JavaScript"> dhtml_Click($param2_js, $param1_js); ...' // $param1_(a_)js, $param2_(a_)js vont générer des chaines de caractères qui ne doivent // pas avoir de retour à la ligne, sinon le code javascript ne fonctionnera pas $rc = array(chr(13).chr(10), chr(10)); $br = array('<br />', '<br />'); $nb = ++$this->nbRaccourcis; $param1_js = $paramTitre; $param1_a_js = htmlspecialchars($paramTitre, ENT_QUOTES, Debug::ENCODING, true); $param1_js = str_replace($rc, $br, $param1_js ); $param1_a_js = str_replace($rc, $br, $param1_a_js); $infoplus = is_object($toprint) ? ' (class ' .get_class($toprint).')' : ' (array ' .count($toprint).' elements)'; $param1_js = '\''.$titre_begin.$param1_js .$titre_end.' <span class=infoplus>'.$infoplus.'</span>\''; $param1_a_js = '\''.$titre_begin.$param1_a_js.$titre_end.' <span class=infoplus>'.$infoplus.'</span>\''; // GESTION DU DETAIL (partie qui sera visible si -, invisible si +) $paramDetails = var_export($toprint, TRUE); $paramDetails = htmlentities($paramDetails, ENT_QUOTES, Debug::ENCODING, true); $param2_js = $paramDetails; $param2_a_js = htmlspecialchars($paramDetails, ENT_QUOTES, Debug::ENCODING, true); $param2_js = '\''.str_replace($rc, $br, $param2_js ).'\''; $param2_a_js = '\''.str_replace($rc, $br, $param2_a_js).'\''; // création de la balise avec appel du js $ret = '<a class=local href="JavaScript: dhtml_Click('.$param2_a_js.', '.$param1_a_js.', '.$nb.')">' .'<span id=PLUS'.$nb.'>'.($this->collapsed ? "-" : "+").'</span></a>' .'<span id=INFO'.$nb.'></span>'.PHP_EOL; // création et stockage de l'appel direct du js $this->inittrace[] = 'dhtml_Click('.$param2_js.', '.$param1_js.', '.$nb.');'; } else { if (is_bool($toprint)) $ret = $titre_begin.$paramTitre.' (bool) = '.$titre_end.($toprint==true ? 'true' : 'false'); elseif (is_string($toprint)) $ret = $titre_begin.$paramTitre.' = '.$titre_end .(htmlentities($toprint, ENT_QUOTES, Debug::ENCODING, false)); else // numérique $ret = $titre_begin.$paramTitre.' = '.$titre_end.$toprint; } } else { $ret = $paramTitre.' variable unset !'; } if ($this->withTime) $ret = date("H:i:s . ", time()).$ret; return $ret; } /** ............................................................................................. Fonction à utiliser pour tracer une variable $name représente un commentaire à associer à la variable (en général, rappeler son nom !)
  • /
public function print_variable($name, $toprint) { $this->trace[] = $this->describe_variable($name, $toprint); $this->update_debughtml(); } /** ............................................................................................. Fonction à utiliser pour regrouper la trace de plusieurs variables Exemple d'utilisation: - print_variables('Etape 1', array('$a', '$b', '$c propriété x'), array($a, $b, $c->x));
  • /
public function print_variables($comment, array $names, array $variables) { $error = (!is_array($variables)) || (!is_array($names)) || (count($variables)==0); if (!$error) $error = count($variables)!=count($names); if ($error) $ret = '<span class=constant>Error calling : '.__METHOD__.'<br>File : '.__FILE__.'<br>Line : '.__LINE__.'</span>'; else { $param2_js = ''; $nb = ++$this->nbRaccourcis; for ($i=0; $i <count($variables); $i++) { $arg = $variables[$i]; $tab = $this->describe_variable($names[$i], $arg); $param2_js .= '<li>'.$tab.'</li>'; } $ret = '<span class=titre>' .htmlentities($comment, ENT_QUOTES, Debug::ENCODING, true) .' : </span> ' .'<table><tr><td><PRE><ul>'.$param2_js.'</ul></PRE></td></tr></table><br />'.PHP_EOL; } $this->trace[] = $ret; $this->update_debughtml(); } /** --------------------------------------------------------------------------------------------- Fonction à utiliser pour afficher un commentaire
  • /
public function print_constant($toprint) { $this->trace[] = htmlentities('<span class=constant>' .($this->withTime ? date("H:i:s : ", time()) : '') .$toprint.'</span>' , ENT_QUOTES, Debug::ENCODING, true); $this->update_debughtml(); } /** --------------------------------------------------------------------------------------------- Possibilité d'afficher la structure d'une classe à partir d'une instance de celle-ci
  • /
public function reflexion($toAnalyse) { if (is_object($toAnalyse)) { $reflectClass = new ReflectionClass($toAnalyse); $className = $reflectClass->getName(); // get_class($toAnalyse); $reflectParentClass = $reflectClass->getParentClass(); $heriteDe = $reflectParentClass != null ? ' extends '.$reflectParentClass->getName() : ''; $lesInterfaces = $reflectClass->getInterfaceNames(); $interfaces = count($lesInterfaces)==0 ? '' : ' implements '.implode(', ', $lesInterfaces); $info_class = '<span class=titre>class '.$className.'</span>'.$heriteDe.$interfaces.'<br />'.PHP_EOL .$reflectClass->getFileName().'<br />'.PHP_EOL .'Lines: '.$reflectClass->getStartLine(). ' - ' .$reflectClass->getEndLine().'<br />'.PHP_EOL; $lesMethodes = $reflectClass->getMethods(); $functions = array(); foreach($lesMethodes as $fct) { $c = ''; $reflectMethod = new ReflectionMethod($className, $fct->name); if ($reflectMethod->IsPublic()) $c = 'public '; elseif ($reflectMethod->IsPrivate()) $c = 'private '; elseif ($reflectMethod->IsProtected()) $c = 'protected '; if ($reflectMethod->IsStatic()) $c = 'static '.$c; if ($reflectMethod->IsAbstract()) $c = 'abstract '.$c; if ($reflectMethod->IsFinal()) $c = 'final '.$c; $c = $c.'<span class=methodname>'.$fct->name.'</span>'; if ($reflectMethod->getNumberOfParameters()==0) $c .= '()'; else { $params = $reflectMethod->getParameters(); $args = array(); $i = 0; $c = $c . '('; foreach ($params as $param) { $arg = ''; $parametre = '<span class=methodparam>'.$param->getName().'</span>'; if ($param->isPassedByReference()) $arg = '&'; if ($param->isOptional()) { if ($param->isArray()) $arg .= '[$' .$parametre. ' (array) ]'; else{ $val = $param->getDefaultValue(); $arg = '[$' .$parametre. ' = ' .($val==null ? 'null' : $val). ']'; } } else $arg .= '$'.$parametre; $args[] = $arg; } $c .= implode(', ', $args) . ')'; } $functions[] = $c; } $info_methods = implode('<br />'.PHP_EOL, $functions); $lesProprietes = $reflectClass->getProperties(); $proprietes = array(); foreach($lesProprietes as $pro) { $c = ''; if ($pro->IsPublic()) $c = 'public '; elseif ($pro->IsPrivate()) $c = 'private '; elseif ($pro->IsProtected()) $c = 'protected '; if ($pro->IsStatic()) $c = 'static '.$c; $c .= '$<span class=property>'.$pro->name.'</span>'; $proprietes[] = $c; } $info_properties = implode('<br />'.PHP_EOL, $proprietes); $lesConstantes = $reflectClass->getConstants(); $constants = array(); foreach($lesConstantes as $key=>$value) $constants[] = '<span class=property>'.$key.'</span> = '.htmlspecialchars($value, ENT_QUOTES, Debug::ENCODING, true); $info_constants = implode('<br />'.PHP_EOL, $constants); $this->print_constant($info_class.'<hr /><span class=infoplus>Methods :</span><br/>' .$info_methods.'<hr /><span class=infoplus>Properties :</span><br/>' .$info_properties.'<hr /><span class=infoplus>Constants :</span><br/>' .$info_constants.'<hr />'); } else { $this->print_constant('<span class=infoplus>Error:</span> The variable tested is not an instance of a class<br>'.PHP_EOL); } } /** --------------------------------------------------------------------------------------------- Fonction écrivant le fichier de débugage
  • /
private function update_debughtml($all = false) { if (!$this->updateTrace) return; if ($this->xmlfile == '' && $this->serfile == '') $nom = basename($_SERVER['PHP_SELF'].Debug::EXT); else { if ($all || $this->xmlfile != '') { $this->SaveToXml(); $nom = $this->onefile; } if ($all || $this->serfile != '') { $this->serializeMe(); $nom = $this->onefile; } } $resultat = fopen($nom, 'w+'); if (!$resultat) { echo '<p>DEBUG IMPOSSIBLE</p>'.PHP_EOL; exit; } else { fputs($resultat, $this->BEGIN); fputs($resultat, PHP_EOL.'<ul>'.PHP_EOL); $inf_ETlt = '<'; //$inf_ETlt2 = '&amp;lt;'; for ($i=0; $i < count($this->trace); $i++) { $trace = $this->trace[$i]; if (substr($trace, 0, strlen($inf_ETlt))==$inf_ETlt){ $trace = html_entity_decode($trace, ENT_QUOTES, Debug::ENCODING); } fputs($resultat, '<li>'.$trace.'</li>'); // .PHP_EOL } fputs($resultat, '</ul>'.PHP_EOL); fputs($resultat, PHP_EOL.'<script language="JavaScript">'.PHP_EOL.PHP_EOL); for ($i=0; $i < count($this->inittrace); $i++) { $trace = $this->inittrace[$i]; if (substr($trace, 0, strlen($inf_ETlt))==$inf_ETlt){ $trace = html_entity_decode($trace, ENT_QUOTES, Debug::ENCODING); } fputs($resultat, $trace.PHP_EOL.PHP_EOL); } fputs($resultat, '</script>'.PHP_EOL); fputs($resultat, (($this->xmlfile == '') && ($this->serfile == '')) ? $this->END : $this->END_XML_SER); fclose($resultat); } } /** --------------------------------------------------------------------------------------------- Afin de garder plusieurs traces successives dans un même fichier (après changement de page ou bien rechargement d'une même page), un fichier XML peut être utilisé. /* STRUCTURE DU FICHIER XML UTILISE <?xml version="1.0"?> <rootelement> <param symbol="<"> <equivalent>{i;n;f</equivalent> </param> <trace position="1"> <print>...</print> </trace> ... <inittrace position="1"> <print>...</print> </inittrace> ... </rootelement>
  • /
/** --------------------------------------------------------------------------------------------- SaveToXML(): sauvegarde les infos permettant de re-générer le fichier html de trace. Le but est de remplacer les balises perturbantes pour XML par des balises personnelles neutres (qui ne seront pas interprétées par XML ou HTML) Pour cela, il faut remplacer les caractères < > et & par des chaines de caractères de notre choix AVANT de construire le fichier XML, et faire l'inverse lorsqu'on récupère le XML. Ces chaines de correspondances pouvant se retrouver dans le texte à afficher, il faut vérifier si elles n'y sont pas et en choisir éventuellement une autre. Ex: '<' est remplacé par '{i;n;f', mais si '{i;n;f' est trouvé, on utilisera '{i;n;f1', sinon '{i;n;f2' etc... La vérification est activée par défaut mais peut être désactivée ($checksymbols = false;) Détail: Si '{i;n;f' est une chaine de codage stockée dans une propriété de l'instance $f de la classe Debug, $f->print_variable('$f', $f) va devoir utiliser '{i;n;f1' pour générer le XML. Lors d'un deuxième appel, ce sera '{i;n;f2'... C'est dans l'unique but (!!) d'utiliser la classe originalcompo\Debug avec elle même sans modifier inutilement la chaine de remplacement que celle-ci n'est pas stockée telle qu'elle dans la classe. La propriété $_replace4xml contient la valeur '{i,n,f', mais la chaine de remplacement est recalculée en '{i;n;f'. De même pour les autres valeurs 's,u,p}' et '{e,t]'
  • /
public function SaveToXML() { $xmlBEGIN = '<?xml version="1.0" encoding="'.Debug::ENCODING.'"?><rootelement>'; $xmlEND = '</rootelement>'; // Création d'un document XML "vide" $xmltext = $xmlBEGIN.$xmlEND; // Construction du tableau $replace4xml des chaines de substitutions à < > & // (basé sur $_replace4xml) $replace4xml = str_replace($this->oldchar4xml, $this->newchar4xml, $this->_replace4xml); $r = $replace4xml; $i=0; // Parcours de toutes les chaines qu'il va falloir modifier, et vérification // que les chaines de remplacement ne sont pas déjà présentes dans ces chaines if ($this->checksymbols) do { $retester = false; foreach($this->trace as $trace) { foreach ($replace4xml as $ri) { $retester = !(stripos($trace, $ri) === false); if ($retester) break 2; } } if (!$retester) { foreach($this->inittrace as $trace) { foreach ($replace4xml as $ri) { $retester = !(stripos($trace, $ri) === false); if ($retester) break 2; } } } if ($retester) { $i++; for ($j = 0; $j<count($r); $j++) $replace4xml[$j] = $r[$j].$i; } } while($retester); $this->DOM ? $this->DOM_SaveXML ($xmltext, $replace4xml) : $this->simple_SaveXML($xmltext, $replace4xml); } // -------------------------------------------------------------------------- public function simple_SaveXML($xmltext, $replace4xml) { $XMLelements = new SimpleXMLElement($xmltext, NULL, false); // for ($i=0; $i<count($replace4xml); $i++) { $newXMLelement = $XMLelements->addChild('param'); $newXMLelement->addAttribute('symbol', $this->original4xml[$i]); $newXMLelement->addChild('equivalent', $replace4xml[$i]); // // } foreach($this->trace as $trace) { $i++; $trace = str_replace($this->original4xml, $replace4xml, $trace); $newXMLelement = $XMLelements->addChild('trace'); $newXMLelement->addAttribute('position', $i); $newXMLelement->addChild('print', $trace); // // } $i=0; foreach($this->inittrace as $inittrace) { $i++; $inittrace = str_replace($this->original4xml, $replace4xml, $inittrace); $newXMLelement = $XMLelements->addChild('inittrace'); $newXMLelement->addAttribute('position', $i); $newXMLelement->addChild('print', $inittrace); // // } $resultat = fopen($this->xmlfile, 'w+'); fputs($resultat, $XMLelements->asXML()); fclose($resultat); } // -------------------------------------------------------------------------- public function DOM_SaveXML($xmltext, $replace4xml) { $XMLelements = new DomDocument(); $XMLelements->loadXML($xmltext); for ($i=0; $i<count($replace4xml); $i++) { $newXMLelement = $XMLelements->createElement('param'); $newXMLelement2 = $XMLelements->createElement('equivalent', $replace4xml[$i]); $newXMLelement->SetAttribute('symbol', $this->original4xml[$i]); $newXMLelement->appendChild($newXMLelement2); $XMLelements->documentElement->appendChild($newXMLelement); } foreach($this->trace as $trace) { $i++; $trace = str_replace($this->original4xml, $replace4xml, $trace); $newXMLelement = $XMLelements->createElement('trace'); $newXMLelement->SetAttribute('position', $i); $newXMLelement2 = $XMLelements->createElement('print', $trace); $newXMLelement->appendChild($newXMLelement2); $XMLelements->documentElement->appendChild($newXMLelement); } $i=0; foreach($this->inittrace as $inittrace) { $i++; $inittrace = str_replace($this->original4xml, $replace4xml, $inittrace); $newXMLelement = $XMLelements->createElement('inittrace'); $newXMLelement->SetAttribute('position', $i); $newXMLelement2 = $XMLelements->createElement('print', $inittrace); $newXMLelement->appendChild($newXMLelement2); $XMLelements->documentElement->appendChild($newXMLelement); } $XMLelements->save($this->xmlfile); // // } // ============================================================================================== public function LoadXML() { if (!file_exists($this->xmlfile)) return; $this->trace = array(); //array_splice($this->trace, 0); // on efface le tableau $this->inittrace = array(); // on efface le tableau $this->nbRaccourcis=0; $this->DOM ? $this->DOM_LoadXML() : $this->simple_LoadXML(); } // ---------------------------------------------------------------------------------------------- function simple_LoadXML() { $replace4xml = array(); $XMLelements = new SimpleXMLElement($this->xmlfile, NULL, true); // true : path, false : string // // ALTERNATIVES // 1) $xmlstr = file_get_contents($xmlfile); // $XMLelements = simplexml_load_string($xmlstr); // 2) $XMLelements = simplexml_load_file($xmlfile); // 3) $xmlstr = file_get_contents($xmlfile); // $XMLelements = new SimpleXMLElement($xmlstr); $results = $XMLelements->xpath('/rootelement/param'); // foreach ($results as $noeud) { $position = ''; foreach ($noeud->attributes() as $attr) if ($attr->getName()=='symbol') $position = $attr; if ($position=='') throw new Exception('Mauvaise structure du fichier XML'); foreach ($noeud->children() as $subnoeud) { if ($subnoeud->getName()=='equivalent') { for ($i=0; $i<count($this->original4xml); $i++) { if ($this->original4xml[$i]==$attr) { $replace4xml[$i] = $subnoeud; break; } } } } } $results = $XMLelements->xpath('/rootelement/trace'); foreach ($results as $noeud) { $position = -1; foreach ($noeud->attributes() as $attr) if ($attr->getName()=='position') $position = $attr; if ($position==-1) throw new Exception('Mauvaise structure du fichier XML'); foreach ($noeud->children() as $subnoeud) { if ($subnoeud->getName()=='print') { $this->trace[] = str_replace($replace4xml, $this->original4xml, $subnoeud); $this->nbRaccourcis++; } } } $results = $XMLelements->xpath('/rootelement/inittrace'); foreach ($results as $noeud) { $position = -1; foreach ($noeud->attributes() as $attr) if ($attr->getName()=='position') $position = $attr; if ($position=='') throw new Exception('Mauvaise structure du fichier XML'); foreach ($noeud->children() as $subnoeud) { if ($subnoeud->getName()=='print') { $this->inittrace[] = str_replace($replace4xml, $this->original4xml, $subnoeud); $this->nbRaccourcis++; } } } } // ---------------------------------------------------------------------------------------------- // Fonctions DOM interressantes: (file, string) // DomDocument::loadHtmlFile() , DomDocument::loadHTML() // DomDocument::save() , // DomDocument:saveHTMLFile() , DomDocument::saveHTML(), DomDocument::saveXML() /* RAPPEL : STRUCTURE DU FICHIER XML UTILISE <?xml version="1.0"?> <rootelement> <param symbol="<"> <equivalent>{i;n;f</equivalent> </param> <trace position="1"> <print>...</print> </trace> ... <inittrace position="1"> <print>...</print> </inittrace> ... </rootelement>
  • /
// ---------------------------------------------------------------------------------------------- public function DOM_LoadXML() { $replace4xml = array(); $XMLelements = new DomDocument(); $XMLelements->load($this->xmlfile); // ALTERNATIVES // 1) $XMLelements = new DomDocument(); // $XMLelements->C // // // $xpath = new DomXPath($XMLelements); $results = $xpath->query('//rootelement/param'); foreach ($results as $noeud) { $position = ''; foreach ($noeud->attributes as $attr) if ($attr->nodeName=='symbol') $position = $attr->nodeValue; if ($position=='') throw new Exception('Mauvaise structure du fichier XML'); foreach ($noeud->childNodes as $subnoeud) { if ($subnoeud->nodeName=='equivalent') { for ($i=0; $i<count($this->original4xml); $i++) { if ($this->original4xml[$i]==$attr->nodeValue) { $replace4xml[$i] = $subnoeud->nodeValue; break; } } } } } $results = $xpath->query('//rootelement/trace'); foreach ($results as $noeud) { $position = -1; foreach ($noeud->attributes as $attr) if ($attr->nodeName=='position') $position = $attr->nodeValue; if ($position==-1) throw new Exception('Mauvaise structure du fichier XML'); foreach ($noeud->childNodes as $subnoeud) { if ($subnoeud->nodeName=='print') { $this->trace[] = str_replace($replace4xml, $this->original4xml, $subnoeud->nodeValue); $this->nbRaccourcis++; } } } $results = $xpath->query('//rootelement/inittrace'); foreach ($results as $noeud) { $position = -1; foreach ($noeud->attributes as $attr) if ($attr->nodeName=='position') $position = $attr->nodeValue; if ($position=='') throw new Exception('Mauvaise structure du fichier XML'); foreach ($noeud->childNodes as $subnoeud) { if ($subnoeud->nodeName=='print') { $this->inittrace[] = str_replace($replace4xml, $this->original4xml, $subnoeud->nodeValue); $this->nbRaccourcis++; } } } } // ================================================================================================ } ?>

Conclusion :


Toutes les explications se trouvent dans le code php (fichier source)

Le sources se trouvent aussi sur un de mes sites web:
http://mywebdev.free.fr (section PHP)

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.