begin process at 2012 05 27 19:51:12
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Class et Objet ( POO )

 > DÉBOGAGE D'UN CODE PHP (SUIVIT DES VARIABLES)

DÉBOGAGE D'UN CODE PHP (SUIVIT DES VARIABLES)


 Information sur la source

Note :
9 / 10 - par 1 personne
9,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Class et Objet ( POO ) Classé sous :debogage debug, trace, serialize, XML DOM, reflexion Niveau :Initié Date de création :12/08/2010 Date de mise à jour :25/08/2010 13:25:07 Vu / téléchargé :2 260 / 54

Auteur : originalcompo

Ecrire un message privé
Site perso
Commentaire sur cette source (6)
Ajouter un commentaire et/ou une note

 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

  • <?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 = '&lt;'; //$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++;
  • }
  • }
  • }
  • }
  • // ================================================================================================
  • }
  • ?>
<?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 = '&lt;';        //$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)

 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 Historique

12 août 2010 14:18:25 :
explications supplémentaires (pas de modif su source)
14 août 2010 19:28:44 :
Nouvelle méthode permettant d'afficher la définition d'une classe à partir d'une de ses instances (utilisation de la "reflexion"). exemple d'utilisation : $instance_ocDEBUG->reflexion($instance_une_classe);
15 août 2010 09:34:44 :
- Interdiction du clonage - Utilisation de l'écriture HEREDOC - Reflection() affiche maintenant le nom de la classe parente ainsi que les interfaces utilisées
25 août 2010 13:25:07 :
Possibilité de garder toutes les traces venant de plusieurs pages html dans un seul fichier

 Sources du même auteur

Source avec Zip Source avec une capture HISTOGRAMME EN PHP AVEC GD. PROGRAMMATION OBJET
Source avec Zip Source avec une capture GÉNÉRATION D'HISTOGRAMMES EN PHP
Source avec Zip Source avec une capture AFFICHER GÉNÉRIQUEMENT PLUSIEURS FICHIERS DANS IFRAME AVEC 2...

 Sources de la même categorie

Source avec Zip GÉNÉRATION AUTOMATIQUE DE FICHIER .CLASS.PHP EN FONCTION D'U... par ig3
CLASSE D'OBJET DE CRYPTAGE ET DÉCRYPTAGE DE CHAINES DE CARAC... par 8Tnerolf8
Source avec Zip MY.DEVIANTART API par inwebo
CLASSE DE GESTION DE "VARIABLES GLOBALES D'ENVIRONNEMENT" par pifou25
Source avec Zip COLLECTION.CLASS.MIN.PHP par thunderhunter

 Sources en rapport avec celle ci

UNSERIALIZE ERROR par abdoulax
TRACE DES ERREURS EN PHP par Laurpierre
Source avec Zip FORMULAIRE MULTI-PAGES INTERACTIF par Laurpierre
[PHP5] FACTORY ET PARAMÈTRES CONSTRUCTEURS par Naixn
GARDER LES PARAMÈTRES D'UNE CLASS À TRAVERS PLUSIEURS PAGES. par massacr

Commentaires et avis

Commentaire de stay le 14/08/2010 09:14:49

Travailler en dev pour le debugging et commiter en prod par la suite serait plus sur.

Commentaire de Delphiprog le 14/08/2010 13:47:07 administrateur CS 9/10

Pour que l'application du design pattern soit parfaite en PHP 5, il faut penser à surcharger la méthode magique __clone et à la déclarer de portée privée, tout comme le constructeur. Sans cela, il devient facile de multiplier les instances là où elles devraient être uniques.
Code intéressant, propre et bien écrit.
Personnellement, j'aurais utilisé la notation Heredoc pour alléger le code, notamment au niveau de l'affectation du membre $this->BEGIN. Mais c'est affaire de goût...
Quant au CSS employé, dans la mesure où il ne contient pas d'éléments générés dynamiquement, il serait bon de l'externaliser, séparant ainsi le contenu de la mise en page. Dans la mesure où le code est facilement modifiable, chacun pourra l'adapter à ses goûts, ses besoins ou encore aux contraintes de développement qui lui sont imposées.

Commentaire de originalcompo le 14/08/2010 19:20:00

Merci Delphiprog pour les commentaires.

__clone : je vais suivre le conseil. (Comme c'est une classe pour debuguer, je suis parti
du principe que l'utilisateur n'a aucun interet à essayer de faire des trucs bizarres, comme cloner cette classe, sauf s'il veut volontairement écraser ou planter ses résultats. Pour être totalemnt franc, j'avais aussi zappé cette fonctionnalité... je reviens tout juste à la programmation php !)

css: le probleme comme j'y fais allusion dans le code est que si on debogue plusieurs pages se trouvant dans des dossiers/chemins différents, il faut prendre en compte tous les
chemins, et les changer à chaque fois que l'on change de projet. Le fait de l'insérer dans le head fait que l'on n'a pas à toucher au source de la classe (moins de travail donc).
J'avais initialement externalisé le js et le css, pour revenir à cette dernière version qui est plus pratique. (De plus le css est spécifique à l'affichage du débogage, et n'est pas sensé évoluer ni être utilisé ailleurs).

Commentaire de originalcompo le 14/08/2010 19:47:50

oups, j'ai oublié dans les nouveautés la possibilité de ne créer ou mettre à jour le fichier de trace que lorsqu'on le désire grace à la méthode set_UpdateTrace($booleen)
Ex
$instance_ocDEBUG->set_UpdateTrace(false);
...
$instance_ocDEBUG->set_UpdateTrace(true);  // à mettre avant la fin du script à deboguer

Commentaire de Delphiprog le 15/08/2010 18:54:35 administrateur CS

Merci pour l'utilisation de la notation Heredoc qui allège le code en évitant les guillemets simples/doubles et les opérateurs de concaténation à répétition.
Mais je vois que tu n'es pas allé au bout de la démarche comme ici :
  $this->BEGIN .= PHP_EOL
.$JS.PHP_EOL
.$CSS.PHP_EOL
.'</HEAD>'.PHP_EOL
.PHP_EOL
.'<BODY>';  

Alors qu'il suffit d'écrire chaque variable entre accolades :
  {PHP_EOL.$JS.PHP_EOL.$CSS.PHP_EOL}
</HEAD>{PHP_EOL.PHP_EOL}
<BODY>

Il suffisait de se souvenir qu'au départ, les variables devaient être encadrées d'accolades en PHP. Cette habitude a disparu avec l'évolution du langage mais reste néanmoins utilisable.

En ce qui concerne le CSS et le javascript, ton point de vue est tout à fait approprié.
Bonne continuation.

Commentaire de originalcompo le 15/08/2010 23:11:41

Je découvre HEREDOC, donc voilà:

je n'ai pas réussi à faire de concaténation de variable {$JS.$CSS}, et PHP_EOL est ignoré et affiché tel quel (pas anormal, puisqu'avec HEREDOC il suffit de sauter la ligne !)

J'ai finalement réussi à obtenir ce que je voulais (et c'est vrai que c'est mieux) en faisant ainsi:

    $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' />

{$JS}

{$CSS}
</HEAD>
<BODY>
BEGIN;

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

Probleme avec serialize et unserialize [ par matou82 ] J'ai un objet que je serialize et passe en paramètre dans l'url d'un popup.Dans ce popup je le unserialize. Il s'agit d'un objet possédant une fonctio probleme de class [ par damien174 ] bonjour, je voudrai cr&#233;er une classe pour dessin&#233; des graphet j'ai un petit soucis, lorsque j'appelle la fonction init_graph ca marche mais passage de tableau en url [ par eddie5150 ] Salut à tous,J'aimerai passer en url un tableau, et le récupérer avec un $_GET.J'ai essayé addslashes(urlencode(serialize($tab))), ou encore urlencode Trace du Telnet [ par A_Script ] Bonjour j'ai besoin de sauvgarder une trace de ce que je fais  sous l'interface telnet (les commandes et les reponses) , est ce que c'est possible de DomXML, TextNode tronqué ... [ par JoJo738 ] Helo tout le monde :pJe suis depuis ce matin sur une class de log, et je rencontre un petit problème ...Je veux enregistrer la Trace de mon erreur ... $_SESSION perte de données [ par ciberrique ] Bonjour, lorsque je veux mettre des objets en session avec la fonction serialize cela fonctionne mais si j'actualise ma page alors les données de l'ob reflexion construction de base de donnée pour annuaire hôtels et restaurants [ par math567 ] J'ai besoin d'un coup de pouce !!! Je m'explique, j'ai une table "t_etablissement" contenant plusieurs champs "id,nom,adresse,..." et pour chaque étab


Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Mai 2012
LMMJVSD
 123456
78910111213
14151617181920
21222324252627
28293031   

Consulter la suite du CalendriCode

Photothèque

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,108 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales