Débogage SQL
Pour l'activer, il faut la paramétrer dans le fichier de configuration :
"debug" => "yes",
Exemple de sortie :
Query log :
select * from wikini_pages where tag = 'ActualitesDeWikini' and latest = 'Y' limit 1 (0.0018)
select * from wikini_pages where tag = 'DerniersChangements' and latest = 'Y' limit 1 (0.0016)
select * from wikini_pages where tag = 'DerniersCommentaires' and latest = 'Y' limit 1 (0.0015)
select * from wikini_pages where tag = 'ParametresUtilisateur' and latest = 'Y' limit 1 (0.0015)
select * from wikini_acls where page_tag = 'ActualitesDeWikini' and privilege = 'read' limit 1 (0.0011)
select * from wikini_pages where tag = 'WikiNi' and latest = 'Y' limit 1 (0.0025)
select * from wikini_pages where tag = 'WikiniChangeLog' and latest = 'Y' limit 1 (0.0016)
select * from wikini_pages where tag = 'ActualitesDeWikiniPointNet' and latest = 'Y' limit 1 (0.0018)
select * from wikini_pages where tag = 'DifferencesEntreWikiNi01103EtWikiNi041' and latest = 'Y' limit 1 (0.0017)
select * from wikini_pages where tag = 'SitesUtilisantWikini' and latest = 'Y' limit 1 (0.0025)
select * from wikini_pages where tag = 'WikiniEtLesFluxRSS' and latest = 'Y' limit 1 (0.0018)
select * from wikini_pages where tag = 'WakkaFr' and latest = 'Y' limit 1 (0.0018)
select * from wikini_pages where tag = 'UneMacroPourWord' and latest = 'Y' limit 1 (0.0016)
select * from wikini_pages where comment_on = 'ActualitesDeWikini' and latest = 'Y' order by time (0.0021)
select * from wikini_acls where page_tag = 'ActualitesDeWikini' and privilege = 'write' limit 1 (0.0011)
select * from wikini_pages where tag = 'CharlesNepote' and latest = 'Y' limit 1 (0.0027)
0.0286 s (total SQL time)
0.0627 s (total time)
SQL time represent : 45.64% of total time
Proposition de nouvelle sortie
Query log :
01 -- 0.0018 s : select * from wikini_pages where tag = 'ActualitesDeWikini' and latest = 'Y' limit 1
02 -- 0.0016 s : select * from wikini_pages where tag = 'DerniersChangements' and latest = 'Y' limit 1
03 -- 0.0015 s : select * from wikini_pages where tag = 'DerniersCommentaires' and latest = 'Y' limit 1
04 -- 0.0015 s : select * from wikini_pages where tag = 'ParametresUtilisateur' and latest = 'Y' limit 1
05 -- 0.0011 s : select * from wikini_acls where page_tag = 'ActualitesDeWikini' and privilege = 'read' limit 1
06 -- 0.0017 s : select * from wikini_pages where tag = 'DifferencesEntreWikiNi01103EtWikiNi041' and latest = 'Y' limit 1
07 -- 0.0021 s : select * from wikini_pages where comment_on = 'ActualitesDeWikini' and latest = 'Y' order by time
08 -- 0.0011 s : select * from wikini_acls where page_tag = 'ActualitesDeWikini' and privilege = 'write' limit 1
09 -- 0.0027 s : select * from wikini_pages where tag = 'CharlesNepote' and latest = 'Y' limit 1
--------------
0.0286 s (total SQL time)
0.0627 s (total time)
SQL time represent : 45.64% of total time
Le nouveautés :
- numérotation des requêtes permettant d'en évaluer le nombre d'un seul coup d'oeil
- déplacement du temps de traitement en début de ligne, permettant d'évaluer plus rapidement la requête la plus grosse
- utilisation d'une police à chasse fixe permettant une meilleure lisibilité
- séparation de la partie log de la partie "totaux", facilitant la lecture
- le pavé de débogage est désormais inscrit dans une <div> permettant plus de souplesse pour sa mise en page
Si personne n'y voit d'inconvénient, je passe dans le CVS mettons jeudi 20 janvier.
--
CharlesNepote
Pour ce genre de données ce n'est pas mieux d'utiliser un tableau ? Ce que tu proposes donne pas mal mais quand une requête s'étalera sur plusieurs lignes la lisibilité en sera perturbée... (ce qui est déjà le cas actuellement) --
LordFarquaad
- Je m'étais tâté et je me suis dit que le log devaient aussi être le plus lisible possible en cas d'affichage (ou impression) du code source de la page. Si ça te gène, on peut peut-être utiliser un <pre> qui évitera les retours à la ligne. Je vais réfléchir. Hormis cet aspect là, je pense que tes autres questions, ci-dessous, doivent être décorrélées de ma proposition. On peut les traiter plus tard si tu veux ; j'aime bien la technique des petits pas ;) -- CharlesNepote
- Bah ça ne me gêne pas plus que ça, les requêtes longues sont assez rares dans wikini (la tendance est malheureusement à la multiplication des requêtes...). Sinon pas de problèmes pour les "petits pas", pour moi rien ne presse ;-) -- LordFarquaad
Calcul du temps total
Il faudrait changer le calcul du
t_end (je ne sais pas si tu l'as fait...): on a à notre disposition la méthode
GetMicroTime?(), autant l'utiliser... Il faudrait peut-être aussi calculer le temps de debugging, pour voir s'il n'influe pas sur le temps total (ça doit être minime mais bon, on sait jamais...) --
LordFarquaad
- Pour le t_end, je ne vois pas trop le problème. Pour le temps de debugging, j'estime qu'il est négligeable. Je veux -- CharlesNepote
- Ben vu qu'on a à notre diposition GetMicroTime(), autant l'utiliser non ? Sinon c'est du code copié-collé quoi... Sinon c'est vrai que la temps de debugging est certainement négligeable, il faut voir (il l'est peut-être moins pour des pages faisant beaucoup de requêts...) -- LordFarquaad
Nombre d'enregistrements retournés ou affectés
Sinon je ne sais pas si ça serait fort utile mais le nombre d'enregistrements retournés ou affectés pourrait être intéressant (je pense à ça surtout pour les requêtes comme la purge). Cf les fonctions
mysql_num_rows (uniquement pour les
SELECT) et
mysql_affected_rows (uniquement pour les mises à jour). L'inconvéniant est qu'il faut voir si la requête est un
SELECT ou non... Je pense que le mieux serait un
0 === stripos($query, 'select') (ou
false !== mais on sait jamais qu'il y ait le mot "select" plus loin...) pour le savoir. --
LordFarquaad
- Pourquoi pas. Comment cela pourrait-il se présenter ? Je pense que ce besoin n'est pas urgent. -- CharlesNepote
- Par exemple comme tu me le propose par jabber:
- 01 -- 0.0027 s : select * from wikini_pages where tag = 'CharlesNepote' and latest = 'Y' limit 1 ==> 1 result
- 02 -- 0.0038 s : UPDATE matable SET machin = 'bidule' WHERE chouette = 'truc' ==> 7 affected rows
En cas de plantage du script
Ah et puis sinon il faudrait changer le fait que l'exécution s'interrompt complètement à la moindre erreur, et à ce moment là l'erreur pourrait s'afficher dans le query log. --
LordFarquaad
- Oui. Je sais qu'il existe des techniques là-dessus mais je ne les connais pas. -- CharlesNepote
- Mais en fait le truc c'est que WikiNi commet un suicide quand il y a une erreur, via le die("Query failed: " .$query...). Je trouve qu'il devrait plutôt afficher une alerte ou quelque chose de ce genre en fait. Il suffirait donc, dans la méthode Query(), d'appeler mysql_query() en le préfixant d'un "@" pour être certain qu'il n'affiche pas d'erreur, et ensuite d'ajouter au query log les mysql_errno() et mysql_error() (si le dubug est activé) -- LordFarquaad
Remarques diverses à reclasser ailleurs ;)
Au fait, tant que j'y suis: la méthode LoadSingle() laisse penser qu'elle est plus optimisée que LoadAll(), or ce n'est pas du tout le cas... et si on ajoutait un paramètre
$limit à cette dernière ? [tiens le
if dans LoadAll() ne sert strictement à rien vu que Query() tue le script dans le cas contraire...] --
LordFarquaad
- Comme tu l'as vu, je suis un peu nul en SQL... et je ne comprends même pas ces remarques... Le mieux c'est que tu les formules et expliquent ailleurs où elles pourront être lues par un spécialiste. -- CharlesNepote
- Eh bien LoadSingle() fait un simple appel à LoadAll() pour ensuite n'en retourner que la première ligne. Si la requête n'a pas été construite avec un LIMIT 1, cela va donc tout de même lire tous les résultats et remplir un tableau avec pour ensuite n'en prendre que le premier... Evidemment c'est aussi aux programmeurs de prévoir un LIMIT mais bon... Tiens au fait, lorsque la requête ne retourne aucune ligne ça devrait afficher une notice... -- LordFarquaad
- On pourrait aussi, dans un premier temps, ajouter automatiquement ce LIMIT 1 dans LoadSingle(). -- ProgFou
- En fait la requête LoadSingle() est souvent appelée avec ce LIMIT 1, donc on ne pourrait l'ajouter de façon systématique... Tant qu'on le fait il n'y a pas de problème d'optimisation en fait... Je pense que tout compte fait le plus gros problème se pose pour les requête ne retournant aucun résultat... Je crois que le mieux serait que LoadSingle() fase un appel direct de Query(), stocke la première ligne de résultat (false s'il n'y en a aucune), libère le résultat et renvoie la ligne stockée. Je suis d'ailleurs étonné qu'on ne soit jamais tombé sur un "warning: undefined offset 0 in wakka.php on line 121" -- LordFarquaad
Proposition de nouvelles options
On pourrait rafiner le débogage SQL de la manière suivante :
- "debug" => "yes", : débogage actuel
- "debug" => "sum", : affichage uniquement du temps de traitement et du temps SQL : exemple : 0.0624 s dont 45.64% de SQl
- Il me semble qu'il faudrait au moins changer cela en true/false comme je l'avais déjà suggéré... Je sais que cela poserait des problèmes de compatibilité, mais ça me parait plus logique d'utiliser celles-ci... On pourrait aussi passer cela dans une constante, ce serait plus simple d'utilisation je pense (disponible en tout point) -- LordFarquaad
- Bin non justement ? si on ajoute d'autres valeurs possible au paramètre debug (ici "sum"), il n'est donc plus question d'utiliser "true" et "false" qui sont des constantes... -- CharlesNepote
- Oui, c'est sûr, mais à ce moment là je trouve que le mieux serait de définir de nouvelles constantes pour l'état du debug, par exemple DEBUG_ON, DEBUG_SUM, DEBUG_OFF etc. Et il faut bien sûr prévoir que ces constantes puissent éventuellement être combinées si cela a un sens. Ce serait donc similaire au error_reporting, et on aurait par exemple DEBUG_OFF qui vaudrait 0, DEBUG_SUM qui vaudrait 1, puis 2, 4, 8 etc. avec DEBUG_ON qui vaudrait la somme de toutes ces constantes. Ensuite pour vérifier si une option est activée, il suffit de faire (par exemple pour vérifier que DEBUG_SUM est activé):
- if(DEBUG_SUM == DEBUG_SUM & $this->GetConfigValue('debug')) ...
- pour plus de simplicité il faudrait une méthode du genre DebugOptionActive($option) qui prendrait comme paramètre une des constantes DEBUG_ et returnerait true si cette option est activée. -- LordFarquaad
- Je ne comprends pas bien. Je voyais éventuellement un système mais avec quelque chose dans le goût de "debug" => "sql,formatter,sum", permettant l'affichage des requêtes, du formatteur et des totaux. "debug" => "all" pour afficher tout. Mais je ne comprends pas bien ton système. Peux-tu préciser ? -- CharlesNepote
- Vous voulez tous deux faire la même chose, mais LordFarquaad le voit de façon optimisé coté machine tandis que CharlesNetope? le voit de façon lisible coté humain. Des goûts et des couleurs... Vu que c'est pour du débogage, moi je pencherais plus pour le coté optimisé machine pour que ça interfère le moins possible dans le temps d'exécution. -- ProgFou
- Ouep, mais cela reste très lisible si les noms des constantes sont bien choisies. Je préfère nettement l'utilisation de constantes pour ce genre de chose, car cela donne aussi des valeurs plus sûres: si on fait une faute de frappe dans le nom de la constante, php le signalera directement (ou du moins une erreur de niveau E_NOTICE je pense), mais si on fait une erreur dans la chaine de carracères, php ne se rend compte de rien tant qu'on n'a pas programmé une détection des erreurs...
- De plus il est beaucoup plus simple de travailler sur des entiers que sur des chaines, surtout que pour un ensemble de valeurs de type on/off (true/false etc.) on peut tout stocker dans une seule variable avec un bit pour chaque valeur - on travaille en binaire. C'est ce que je propose ici et c'est exactement le même système que pour les options de l'error_reporting (E_ERROR vaut 1, E_WARNING vaut 2 etc. jusque E_USER_NOTICE qui vaut 1024. E_ALL est la somme de toutes celles-ci, c'est à dire 2047.) Pour combiner les options il suffit d'utiliser les , principalement les And, Or et Not (et éventuellement Xor). -- LordFarquaad
Conversation entre
CharlesNepote et
LordFarquaad en réflexion à ce sujet (à poursuivre):
Solution 1
Pour améliorer le débogage des scripts, je propose d'améliorer légèrement les possibilités de débogage. En substance :
- création d'une variable supplémentaire dans la classe wiki : var $processLog;
- création d'une variable de configuration supplémentaire : "debug_process" => "yes|no"
- renommage de la variable de configuration "debug" en "debug_sql"
- ajout à quelques points stratégiques ; par exemple, dans /formatters/wakka.php :
- if($wiki->GetConfigValue("debug_process") == "yes") $wiki->processLog .= "<li>[formatter found] ".htmlspecialchars($things[1]) . "</li>\n";
- puis, dans /actions/footer.php : echo "<ol>\n".$this->processLog."</ol>\n";
Exemple de sortie :
1. [formatter found] DerniersChangements
2. [formatter found] DerniersCommentaires
3. [formatter found] ParametresUtilisateur
4. [formatter found]
5. [formatter found]
6. [formatter found] =====
7. [formatter found] ""{{backlinks}}""
8. [formatter found] =====
9. [formatter found]
10. [formatter found]
11. [formatter found]
12. [formatter found] -
13. [formatter found]
14. [formatter found] -
15. [formatter found]
Le but c'est d'avoir à disposition du développeur une variable permettant de faire des tests :
- afin de savoir si le script a exécuté telle portion de code (afin de vérifier par exemple, que le script a bien vérifié telle condition à tel moment)
- afin de connaître par exemple la valeur d'une variable à tel moment d'exécution du script, etc.
- afin de connaître par exemple quel motif a reconnu telle expressions rationnelle, etc.
- afin de mesurer des écarts de temps entre tel traitement et tel autre pour optimiser tout ça
Ca me parait plus propre que de faire nos propres bidouilles chacun dans son coin.
Avantages :
- pas d'outil supplémentaire à installer : ce mode de débogage est intégré au moteur ; il peut donc être utilisé par n'importe quel développeur
- très simple à employer : il suffit d'incrémenter la variable $this->processLog
Inconvénient :
- cet outil ne convient dans le cas où le script plante (ce qui n'est pas réellement un inconvénient puisqu'il n'est pas fait pour ça)
- surcharge légèrement le script (de façon négligeable à mon avis)
Il est éventuellement possible de rendre cette fonction disponible à tout moment en passant au script un paramètre : par exemple :
http://www.wikini.net/wakka.php?wiki=DiscussionsDebogageDeWikiNi&debug_process=1
Le débogage serait alors encore plus facile à mettre en oeuvre ; on pourrait notamment partiellement déboguer un site à distance sans forcément en avoir vu les sources.
--
CharlesNepote
- Ca ressemble à une faille de sécurité potentielle ça: n'importe qui pourrait facilement tirer des informations capitales sur l'exécution du script... Pour moi il faudrait trois états: jamais afficher (par défaut), toujours afficher, et afficher si demandé (via paramètre GET). A noter que cette technique est bien pour la méthode GET, mais pas pour la méthode POST... Dès lors il serait peut-être bon d'envisager un gestion de ceci via les variables de session -- LordFarquaad
Solution 2 : une méthode spécifique pour la constitution du log
[code non testé]
Dans
wakka.config.php, indiquer :
- soit "debug" => '', si on ne veut pas de débogage
- soit "debug" => "Formatteur;ActionX?", si on souhaite seulement les informations de débogages concernant les catégories "Formatteur" et "ActionX?"
- soit "debug" => "all", si on souhaite tout déboguer
Dans le noyau :
/* Test un débogage est demandé */
if (!empty($this->GetConfigValue?("debug"))) define ("WN_DEBUG", "true");
else define("WN_DEBUG", '')
/* Méthode */
function Debug($message, $category)
{
- $toBeShown = $this->GetConfigValue?("debug"); /* Exemple : "Formatter; ActionX?" */
- /* Is this message to be shown */
- if ($what == "all" or strpos($toBeShown, $category))
- {
- $this->debug_log .= "<li>" . $message . "</li>\n";
- }
}
Usage du débogueur n'importe où dans les sources :
if (WN_DEBUG) $this->Debug("[Formatteur] Tel message", "Formatteur");
Affichage du log de débogage (/actions/footer.php) :
echo $this->debog;
Les avantages de cette méthode :
- l'ajout d'une nouvelle information de log dans le code est plutôt simple et tiens en une ligne
- l'ajout d'une nouvelle catégorie se fait simplement
On ne peut pas avoir "all à l'exception d'une catégorie", mais ça ne me parait pas très important.
--
CharlesNepote
En fait je me pose une question quant à l'utilité de permettre de demander spécifiquement dans le debug de n'enregistrer celui-ci que pour une seule action/un seul handler ou autre module :
- en tant que développeur d'une action ou d'un handler, est-ce que ça me dérange vraiment si le debug est activé sur l'ensemble des actions alors que je n'en développe qu'une ? En toute logique, lorsque je fais mes tests, je n'utilise que ma nouvelle action... (et cela est encore plus vrai pour les handlers)
- si je renomme mon module:
- soit je change partout la vérification du débug (c. à d. changer $this->Debug("message", "ActionX"); en $this->Debug("message", "ActionY");, et changer la valeur dans le fichier de configuration)
- soit je suis négligeant et je laisse tout comme ça...
- n'aurai-je pas tendance à laisser toujours la configuration sur "all", ce qui est plus simple à gérer (pas besoin de la modifier chaque fois que je commence/termine un développement)
- si quelqu'un d'autre veut repérer un bug éventuel, il est obligé de lire mon code (ou simplement sa documentation si cela y est indiqué) pour savoir ce qu'il faut ajouter à son debug pour repérer un problème éventuel... Dès lors je pense qu'il va mettre "all" lui aussi...
- et le risque de faute de frappe ? Eh oui, php ne s'en rendra pas compte à votre place ! Voilà qui peut faire tourner en bourique pendant de longues minutes avant de se rendre compte de sa bêtise...
D'où l'intérêt pour moi de se contenter de catégories plus générales (
DEBUG_ACTIONS, DEBUG_FORMATTERS, DEBUG_HANDLERS, DEBUG_WIKI, DEBUG_QUERIES, DEBUG_TIMES etc.) qui ne devraient, en toute logique, pas trop évoluer. Si un développeur indépendant a besoin d'une nouvelle, rien ne l'empêche de l'ajouter, si nous avons besoin d'une nouvelle, ce serait le fruit de discussions et cela ne posera pas de problème de compatibilité avec les versions antérieures (c'est juste une nouvelle constante...)
L'avantage des constantes entières est que justement on peut faire un "tout à l'exception de telle catégorie" (par exemple
DEBUG_ALL & ~ DEBUG_QUERIES pour désactiver uniquement le débogage des requêtes)
Le paramètre
$category prendrait donc comme valeur une de ces constantes (n'importe laquelle sauf
DEBUG_ALL bien entendu)
Sinon je trouve que ce serait mieux de ne pas avoir à mettre dans le message quelle catégorie de debogage cela concerne, à mon sens ce serait à la méthode Debug de l'ajouter automatiquement. On pourrait aussi envisager qu'elle ne construise pas l'affichage directement mais que cela se fasse en fin d'exécution, regroupant éventuellement les données informations par type. Il suffirait pour cela de stocker les informations de débug dans un tableau du type:
array(
- [DEBUG_QUERIES => array( /* le tableau tel qu'il est actuellement */ ),]
- [DEBUG_ACTIONS => array( $msg1[, $msg2[, ...]] ),]
- [DEBUG_HANDLERS => array( $msg3[, $msg4[, ...]] ),]
- [... => array( ... ),]
)
L'inconvéniant est alors qu'on n'a plus les informations dans l'ordre, mais par contre elles sont beaucoup mieux structurées...
--
LordFarquaad
A propos de tes propositions :
- je ne trouve pas forcément bien de multiplier les constantes (risques de collision avec d'autres classes)
- discuter entre nous de chaque nouvelle constante ne vas dans le sens de l'autonomie des développeurs... et allonge encore un processus qui est déjà bien long dans WikiNi
- l'ordre exact des opérations me parait absolument primordial pour déboguer ; donc l'histoire du tableau DEBUG_QUERIES => array........ est à proscrire
- le développeur est obligé de consulter systématiquement la DocumentationDeveloppeur à chaque fois qu'il veut déboguer pour savoir exactement quelle catégorie il doit utiliser
- dans le cas où la méthode debug ajoute elle même la catégorie, on perd énormément de souplesse pour déboguer un tout petit bout de code dans le formatteur par exemple
- les développeurs qui vont utiliser "all" ne cherchent pas à faire du débogage chirurgical
En bref, je ne suis pas du tout d'accord avec tes arguments et tes propositions.
Quelqu'un d'autre pour donner son avis ?
--
CharlesNepote
Solution 3 : Xdebug
Une seule reponse :
XDebug qui permet d'avoir le tracage des fonctions et contient un profiler, tout ca en evitant de modifier le code php de
WikiNi, et donc d'ajouter des tests inutiles. C'est une extension PHP facile à installer. Le resultat peut être afficher dans la page en rajoutant une ligne de code mais ce n'est pas obligatoire, les resultats peuvent être lu dans des fichiers texte. --
GarfieldFr
- Avantages d'Xdebug :
- outil très riche en terme de débogage
- fonctionne même dans le cas d'un bogue qui fait planter le script
- Inconvénients :
- nécessite une installation qui n'est pas toujours rapide, simple et possible de la part du développeur ; il ne peut donc pas être utilisé par n'imprte quel dévelopeur
- outil trop riche et trop verbeux : donc mal commode pour réaliser des débogages
- Je suis persuadé qu'Xdebug est un excellent outil mais il me paraît très lourd et compliqué pour une utilisation occasionnelle. Ma proposition répond à des besoins plus modestes mais elle est disponible à tout moment et pour n'importe quel développeur, sans installation et presque sans apprentissage (son utilisation est simplissime). -- CharlesNepote
Autre
Je viens de découvrir une fonction qui pourrait s'avérer très pratique pour le débuggage, existante depuis php 4.3.0:
debug_backtrace(). Elle permet entre autre de lister tous les appels de fonctions/classes/fichiers à n'importe quel point du programme, c'est donc je pense assez semblable à Xdebug... (A priori son seul inconvéniant peut être lors d'appels récursifs, où le tableau retourné pourrait s'avérer très long, mais ça doit être pareil pour Xdebug)
- Oui, elle est très pratique comme fonction, mais elle impose une modification du code source ... pas XDebug (qui n'est pas si compliqué que ca à configurer) --GarfieldFr
Au fait, une petite fonction qui peut être utile pour étudier le comportement des fonctions récursives (je l'écris sur le tas, elle donc est peut-être buggée...), à adapter au besoin:
<?php
/**
* Fonction permettant le débuggage de fonctions récursives (entre autres) grâce à un pseudo-compteur interne
* et à l'enrégistrement d'informations à chaque appel.
* @param string $name Nom d'enrégistrement du debug. Si cette valeur n'est pas spécifiée (ou vaut false),
* la fonction tuera le script en affichant toutes les données qu'elle a enrégistrées au cours des appels précédents.
* @param mixed $info Informations de débug à stocker/afficher. Si elle n'est pas spécifiée (ou vaut false), aucune information ne sera stockée
* et le compteur interne ne sera pas incrémenté. (valeur par défaut)
* @param mixed $dieAtCallNb Indique le nombre d'appels après lequel la fonction debug_recur doit tuer l'exécution et afficher les données.
* (false = jamais, c'est la valeur par défaut) Ceci permet d'éviter les boucles infinies ou trop longues.
* Ex.: 0 tuera le script de toute façon, 1 lors du premier appel enrégistré, 10 lors du dixième appel etc.
* NB.: c'est la valeur passée à l'appel courant qui compte, et non celle passée lors du premier appel.
* @param bool $showAllOnDie Indique s'il faut afficher toutes les informations de tous les appels précédents de debug_recur pour tous les
* nom de débugs enrégistrés ou uniquement les informations du nom courant. (défaut: false)
* @return array Toutes les informations des appels précédents pour l'enrégistrement $name
* sous forme d'un tableau dont les éléments sont toutes les $info qui ont été passées au cours des appels de debug_recur pour $name.
* @exemple // appel classique, tue le script lors du 50e passage à cette ligne:
* debug_recur('MaFonction', array($param1DeMaFonction, $param2DeMaFonction), 50);
* // récupération des informations stockées:
* $infos = debug_recur('MaFonction', null, false, false, true);
* // tuer le script sans stocker d'informations mais en affichant les données stockées pour 'MaFonction':
* debug_recur('MaFonction', false, 0, false);
* // idem mais en affichant les données stockées lors de tous les appels précédents:
* debug_recur();
*/
function debug_recur($name = false, $info = false, $dieAtCallNb = false, $showAllOnDie = false){
static $data = array();
if(!$name) {
if(!isset($data[$name])) $data[$name] = array();
$a = $data[$name]; // ce sera moins long que de le recopier à chaque fois...
if($info) $a[] = $info;
} else $showAllOnDie = true; // dans ce cas on ne se sert pas de $a après.
if(!$name || ($dieAtCallNb !== false && $dieAtCallNb >= count($a))) { // pseudo compteur: nombre d'éléments dans $a...
if($name) echo "\nMort subite: '$name' a été appelé au moins $dieAtCallNb fois.<br />\n"
echo "Pile d'informations enrégistrées:<br />\n";
ob_start();
if($showAllOnDie) print_r($data);
else print_r($a['infos']);
$output = ob_get_content();
ob_end_flush();
$output = str_replace("\t", ' &nbps; ', htmlentities($output));
die(nl2br($output));
}
return $a;
}
?>
(n'hésitez pas à modifier, je n'ai d'ailleurs pas vérifié qu'elle marchait mais j'ai assez confiance...) --
LordFarquaad
Débat sur les chaînes
Sinon pourquoi toujours utiliser des chaines, j'ai vu ça un peu partout sur
WikiNi.net: ici "yes" ou "no", ailleurs "1" ou "0"... pourquoi ne pas utiliser ces magnifiques constantes baptisées
true et
false ? Dans ce cas-ci par exemple cela éviterait d'avait utiliser un == pour faire le test... --
LordFarquaad
- Ok pour les true et false. -- CharlesNepote
- Ca reviens au même de tester si une variable vaut 0 ou false ... si tu as $var = 0; alors if ($var) {...} comprend que $var est faux --GarfieldFr
- Oui bien entendu, c'est utilisé très souvent (à tort ?) pour vérifier si une chaine ou un tableau est vide (par exemple). Mais ici je parlais de la chaine "0", je trouve que c'est une des utilisations les plus mauvaises... Quand on travaille avec des variables qui ne peuvent prendre que deux valeurs différentes (en général vrai ou faux), alors autant utiliser true et false ! Je n'y vois que des avantages: clarté du code (on comprend directement qu'il ne peut y avoir que deux valeurs), légèreté (un booléen consomme forcément moins de mémoire qu'une chaine ou même un entier...) etc. )) -- LordFarquaad
- En php "0" et 0 et False sont équivalent et il n'y a pas de surconsommation de mémoire dans un cas plus que dans un autre. Ceci dit, il est vrai que pour des raisons de clarté du code il est mieux d'utiliser true/false quand la variable est censée être booléen. -- GarfieldFr
- Oui, ils sont équivalents pour la conversion en booléen, mais cela reste des valeurs différentes... Un des problèmes quand on les utilise c'est que certains ont tout de même tendance à les tester avec un double égal... (par exemple $a == '0' au lieu de !$a) Au niveau de la mémoire c'est vrai que je me suis un peu égaré car il s'agit tout de même de valeurs très petites et qu'en php on ne se soucie pas trop de l'utilisation de mémoire (à part peut-être pour des fonctions récursives avec passage par valeur...). [En fait la documentation php ne me parait pas très claire à ce sujet: un entier utilise-t-il toujours 32bits (4 octets) ou uniquement la taille nécessaire pour le stocker ? Combien d'octets prend un charactère dans une chaine ? Un seul je suppose... Et un booléen ?] -- LordFarquaad
- Ce n'est pas tant la taille qui compte (même si d'autre prétendraient que si... ;-)) car il me semble que PHP stoque chaque variable sous ses différentes formes => taille fixe et pas de perte de temps de conversion lors des comparaisons (puisque la conversion est faite lors de l'affectation). Je n'arrive hélas pas à remettre la main sur la page où j'avais lu ça... -- ProgFou
- Ah intéressant cela, mais cela me parait tout de même assez étrange... Je serais curieux de voir cet article. Le test suivant me laisse tout de même penser le contraire:
- echo '' === (string) (bool) '0' ? 'oui': 'non'; // affiche 'oui'
- (tiens c'est où déjà la discussion au sujet de l'utilisation des ## au fait ?) -- LordFarquaad
- Mais pour des raisons de clarté du code, il serait bien plus efficace de commenter le code. Pratiquement aucune fonction n'est commentée !!! --GarfieldFr
- En fait il me semble qu'on est en train de s'éloigner du sujet original là :-j -- LordFarquaad
- Oui, mais c'est quand même assez lié : les commentaires aideraient grandement au débogage. -- ProgFou
- Pas dutout, essaye de debugger un code auquel tu ne comprend ren parce qu'il a pas de commentaire. La documentation du code est probablement une des chose les plus importante pour une maintenance et un debuggage efficace. --GarfieldFr
- C'est vrai, mais il y a déjà une autre discussion au sujet de la documentation. Je trouve que la documentation aurait dû être écrite au fur et à mesure du développement et qu'y songer seulement quand on veut se mettre à chercher des bugs, c'est bien trop tard... Mais en fait je dirais qu'il y a deux types de documentation: la documentations des fonctions, classes, méthodes etc. qui sert principalement pour les futurs développements, et la documentation à l'intérieur du code qui sert normalement à se rappeler plus tard (ou à faire comprendre aux autres développeurs) ce qu'on a voulu faire à tel ou tel endroit... Ca c'est clairement le plus important pour le débuggage, mais c'est aussi le plus difficile (il faut notemment être très concis pour ne pas surcharger le code...). Il me semble qu'il y a déjà quelques commentaires de ce genre dans WikiNi en fait, souvent en anglais d'ailleurs; il doit y avoir pas mal d'endroits où c'est suffisant... -- LordFarquaad
- WikiNi n'est pas documenté dés le départ simplement parce que c'est un fork de wakka et que celui-ci ne doit pas être documenté (en tout cas à l'époque du fork). --GarfieldFr
- Ca ne change rien au fait que la documentation aurait dû être écrite au fur et à mesure, il n'y avait pas de raison pour que les développeurs de wakka ne documentent pas le code... Et quand bien même: il aurait fallu rattrapper cette lacune dès le début, et surtout documenter les nouvelles fonctions etc. même si le reste ne l'était pas encore... -- LordFarquaad