erreur)
* et volontaires (var_mode et var_profile)
*
* Si pas d'autorisation, les erreurs ne sont pas affichées
* (mais seront dans les logs)
*
* Si l'erreur vient de SPIP, en parler sur `spip@rezo.net`
*
* @param bool|string|array $message
* - Message d'erreur (string|array)
* - false pour retourner le texte des messages d'erreurs
* - vide pour afficher les messages d'erreurs
* @param string|Contexte $lieu
* Contexte : lieu d'origine de l'erreur
* @param array $opt
* Options pour debug ou tests unitaires
* - 'erreurs' = 'get' : Retourne le tableau des erreurs
* - 'erreurs' = 'reset' : Efface le tableau des erreurs
* @return null|string
* - string si $message à false.
**/
function public_debusquer_dist($message = '', $lieu = '', $opt = array()) {
static $tableau_des_erreurs = array();
// Pour des tests unitaires, pouvoir récupérer les erreurs générées
if (isset($opt['erreurs'])) {
if ($opt['erreurs'] == 'get') {
return $tableau_des_erreurs;
}
if ($opt['erreurs'] == 'reset') {
$tableau_des_erreurs = array();
return true;
}
}
// Erreur ou appel final ?
if ($message) {
$message = debusquer_compose_message($message);
$tableau_des_erreurs[] = array($message, $lieu);
set_request('var_mode', 'debug');
$GLOBALS['bouton_admin_debug'] = true;
// Permettre a la compil de continuer
if (is_object($lieu) and (!isset($lieu->code) or !$lieu->code)) {
$lieu->code = "''";
}
// forcer l'appel au debusqueur en cas de boucles infernales
$urgence = (_DEBUG_MAX_SQUELETTE_ERREURS and count($tableau_des_erreurs) > _DEBUG_MAX_SQUELETTE_ERREURS);
if (!$urgence) {
return;
}
}
if (empty($GLOBALS['debug_objets']['principal'])) {
// espace public ?
if (isset($GLOBALS['fond'])) {
$GLOBALS['debug_objets']['principal'] = $GLOBALS['fond'];
}
}
include_spip('inc/autoriser');
if (!autoriser('debug')) {
return;
}
include_spip('inc/headers');
include_spip('inc/filtres');
// en cas de squelette inclus, virer le code de l'incluant:
// - il contient souvent une Div restreignant la largeur a 3 fois rien
// - ca fait 2 headers !
// sauf si l'on se trouve deja dans un flux compresse (plugin compresseur
// actif par exemple)
if (ob_get_length()
and
!in_array('ob_gzhandler', ob_get_status())
) {
ob_end_clean();
}
lang_select($GLOBALS['visiteur_session']['lang']);
$fonc = _request('var_mode_objet');
$mode = _request('var_mode_affiche');
$fonc = preg_replace(",\W,", "_", isset($fonc) ? $fonc : '');
$mode = preg_replace(",\W,", "_", isset($mode) ? $mode : '');
$self = str_replace("\\'", ''', self());
$self = parametre_url($self, 'var_mode', 'debug');
$res = debusquer_bandeau($tableau_des_erreurs)
. '
'
. debusquer_squelette($fonc, $mode, $self);
if (!_DIR_RESTREINT or headers_sent()) {
return $res;
}
if ($tableau_des_erreurs) {
http_status(503);
}
http_no_cache();
if (isset($_GET['var_profile'])) {
$titre = parametre_url($GLOBALS['REQUEST_URI'], 'var_profile', '');
$titre = parametre_url($titre, 'var_mode', '');
} else {
if (!$fonc) {
$fonc = $GLOBALS['debug_objets']['principal'];
}
$titre = !$mode ? $fonc : ($mode . (isset($GLOBALS['debug_objets']['sourcefile'][$fonc]) ? " " . $GLOBALS['debug_objets']['sourcefile'][$fonc] : ""));
}
if ($message === false) {
lang_select();
return debusquer_entete($titre, $res);
} else {
echo debusquer_entete($titre, $res);
}
exit;
}
function debusquer_compose_message($msg) {
if (is_array($msg)) {
// si c'est un texte, c'est une traduction a faire, mais
// sqlite renvoit aussi des erreurs alpha num (mais avec 3 arguments)
if (!is_numeric($msg[0]) and count($msg) == 2) {
// message avec argument: instancier
$msg = _T($msg[0], $msg[1], 'spip-debug-arg');
} else {
// message SQL: interpreter
$msg = debusquer_requete($msg);
}
}
// FIXME: le fond n'est pas la si on n'est pas dans un squelette
// cela dit, ca serait bien d'indiquer tout de meme d'ou vient l'erreur
$fond = isset($GLOBALS['fond']) ? $GLOBALS['fond'] : "";
// une erreur critique sort $message en array
$debug = is_array($msg) ? $msg[1] : $msg;
spip_log("Debug: " . $debug . " (" . $fond . ")");
return $msg;
}
function debusquer_bandeau($erreurs) {
if (!empty($erreurs)) {
$n = array(count($erreurs) . ' ' . _T('zbug_erreur_squelette'));
return debusquer_navigation($erreurs, $n);
} elseif (!empty($GLOBALS['tableau_des_temps'])) {
include_spip('public/tracer');
list($temps, $nav) = chrono_requete($GLOBALS['tableau_des_temps']);
return debusquer_navigation($temps, $nav, 'debug-profile');
} else {
return '';
}
}
/**
* Affiche proprement dans un tableau le contexte d'environnement
*
* @param array|string $env
* @return string Code HTML
**/
function debusquer_contexte($env) {
if (is_array($env_tab = @unserialize($env))) {
$env = $env_tab;
}
if (!$env) {
return '';
}
$res = "";
foreach ($env as $nom => $valeur) {
if (is_array($valeur)) {
$valeur_simple = array();
foreach ($valeur as $v) {
$valeur_simple[] = is_array($v) ? 'array(size=' . count($v) . ')' : $v;
}
$valeur = '(' . count($valeur) . ' items) [' . join(',', $valeur_simple) . ']';
}
$res .= "\n
" . _T('numero') . " | " . _T('public:message') . " | " . _T('squelette') . " | " . _T('zbug_boucle') . " | " . _T('ligne') . " |
---|
') {
$s = substr($s, 6);
$res = '';
}
$s = preg_replace(',<(\w[^<>]*)>([^<]*)
([^<]*)\1>,',
'<\1>\2\1>
' . "\n" . '<\1>\3\1>',
$s);
$tableau = explode("
", $s);
$format = "%0" . strval(@strlen(count($tableau))) . "d %s
\n";
$format10 = str_replace('white', 'lightgrey', $format);
$formaterr = "color: red;";
$i = 1;
$flignes = array();
$loc = array(0, 0);
foreach ($fautifs as $lc) {
if (is_array($lc)) {
$l = array_shift($lc);
$flignes[$l] = $lc;
} else {
$flignes[$lc] = $loc;
}
}
$ancre = md5($texte);
foreach ($tableau as $ligne) {
if (isset($flignes[$i])) {
$ligne = str_replace(' ', ' ', $ligne);
$indexmesg = $flignes[$i][1];
$err = textebrut($flignes[$i][2]);
// tentative de pointer sur la colonne fautive;
// marche pas car highlight_string rajoute des entites. A revoir.
// $m = $flignes[$i][0];
// $ligne = substr($ligne, 0, $m-1) .
// sprintf($formaterr, substr($ligne,$m));
$bg = $formaterr;
} else {
$indexmesg = $ancre;
$err = $bg = '';
}
$res .= sprintf((($i % 10) ? $format : $format10), $i, $bg, $indexmesg, $err, $i, $ligne);
$i++;
}
return ""
. ''
. ($nocpt ? '' : _T('info_numero_abbreviation'))
. "
" . $res . "\n";
}
// l'environnement graphique du debuggueur
function debusquer_squelette($fonc, $mode, $self) {
$texte = '';
if ($mode !== 'validation') {
if (isset($GLOBALS['debug_objets']['sourcefile']) and $GLOBALS['debug_objets']['sourcefile']) {
$res = "\n"
. debusquer_navigation_squelettes($self)
. "";
} else {
$res = '';
}
if ($fonc) {
$id = " id='$fonc'";
if (!empty($GLOBALS['debug_objets'][$mode][$fonc])) {
list($legend, $texte, $res2) = debusquer_source($fonc, $mode);
$texte .= $res2;
} elseif (!empty($GLOBALS['debug_objets'][$mode][$fonc . 'tout'])) {
$legend = _T('zbug_' . $mode);
$texte = $GLOBALS['debug_objets'][$mode][$fonc . 'tout'];
$texte = ancre_texte($texte, array('', ''));
}
} else {
if (strlen(trim($res))) {
return "$res";
} else {
// cas de l'appel sur erreur: montre la page
return isset($GLOBALS['debug_objets']['resultat']['tout'])
? $GLOBALS['debug_objets']['resultat']['tout']
: '';
}
}
} else {
$valider = charger_fonction('valider', 'xml');
$val = $valider($GLOBALS['debug_objets']['validation'][$fonc . 'tout']);
// Si erreur, signaler leur nombre dans le formulaire admin
$GLOBALS['debug_objets']['validation'] = $val->err ? count($val->err) : '';
list($texte, $err) = emboite_texte($val, $fonc, $self);
if ($err === false) {
$err = _T('impossible');
} elseif ($err === true) {
$err = _T('correcte');
} else {
$err = ": $err";
}
$legend = _T('validation') . ' ' . $err;
$res = $id = '';
}
return !trim($texte) ? '' : (
"$res"
. ""
. "");
}
// http://code.spip.net/@emboite_texte
function emboite_texte($res, $fonc = '', $self = '') {
$errs = $res->err;
$texte = $res->entete . ($errs ? '' : $res->page);
if (!$texte and !$errs) {
return array(ancre_texte('', array('', '')), false);
}
if (!$errs) {
return array(ancre_texte($texte, array('', '')), true);
}
if (!isset($GLOBALS['debug_objets'])) {
$colors = array('#e0e0f0', '#f8f8ff');
$encore = count_occ($errs);
$encore2 = array();
$fautifs = array();
$err = ''
. _T('numero')
. " "
. _T('occurence')
. " "
. _T('ligne')
. " "
. _T('colonne')
. " "
. _T('erreur')
. " ";
$i = 0;
$style = "style='text-align: right; padding-right: 5px'";
foreach ($errs as $r) {
$i++;
list($msg, $ligne, $col) = $r;
#spip_log("$r = list($msg, $ligne, $col");
if (isset($encore2[$msg])) {
$ref = ++$encore2[$msg];
} else {
$encore2[$msg] = $ref = 1;
}
$err .= ""
. $i
. " "
. "$ref/$encore[$msg] "
. ""
. $ligne
. " "
. $col
. " $msg \n";
$fautifs[] = array($ligne, $col, $i, $msg);
}
$err = ""
. $i
. ""
. " " . _T('erreur_texte')
. "
"
. $err
. "
";
return array(ancre_texte($texte, $fautifs), $err);
} else {
list($msg, $fermant, $ouvrant) = $errs[0];
$rf = reference_boucle_debug($fermant, $fonc, $self);
$ro = reference_boucle_debug($ouvrant, $fonc, $self);
$err = $msg .
"$fermant$rf
" .
"$ouvrant$ro";
return array(ancre_texte($texte, array(array($ouvrant), array($fermant))), $err);
}
}
// http://code.spip.net/@count_occ
function count_occ($regs) {
$encore = array();
foreach ($regs as $r) {
if (isset($encore[$r[0]])) {
$encore[$r[0]]++;
} else {
$encore[$r[0]] = 1;
}
}
return $encore;
}
function debusquer_navigation_squelettes($self) {
$res = '';
$boucles = !empty($GLOBALS['debug_objets']['boucle']) ? $GLOBALS['debug_objets']['boucle'] : '';
$contexte = $GLOBALS['debug_objets']['contexte'];
$t_skel = _T('squelette');
foreach ($GLOBALS['debug_objets']['sourcefile'] as $nom => $sourcefile) {
$self2 = parametre_url($self, 'var_mode_objet', $nom);
$nav = !$boucles ? '' : debusquer_navigation_boucles($boucles, $nom, $self, $sourcefile);
$temps = !isset($GLOBALS['debug_objets']['profile'][$sourcefile]) ? '' : _T('zbug_profile',
array('time' => $GLOBALS['debug_objets']['profile'][$sourcefile]));
$res .= "\n";
}
return $res;
}
function debusquer_navigation_boucles($boucles, $nom_skel, $self, $nom_source) {
$i = 0;
$res = '';
$var_mode_objet = _request('var_mode_objet');
$gram = preg_match('/[.](\w+)$/', $nom_source, $r) ? $r[1] : '';
foreach ($boucles as $objet => $boucle) {
if (substr($objet, 0, strlen($nom_skel)) == $nom_skel) {
$i++;
$nom = $boucle->id_boucle;
$req = $boucle->type_requete;
$crit = public_decompiler($boucle, $gram, 0, 'criteres');
$self2 = $self . "&var_mode_objet=" . $objet;
$res .= "\n$i \n" .
"" .
_T('zbug_boucle') .
" \n" .
_T('zbug_resultat') .
" \n" .
_T('zbug_code') .
" \n" .
_T('zbug_calcul') .
" \n" .
(($var_mode_objet == $objet) ? "$nom" : $nom) .
" \n" .
$req .
" \n" .
spip_htmlspecialchars($crit) .
" ";
}
}
return $res;
}
function debusquer_source($objet, $affiche) {
$quoi = $GLOBALS['debug_objets'][$affiche][$objet];
if (!empty($GLOBALS['debug_objets']['boucle'][$objet]->id_boucle)) {
$nom = $GLOBALS['debug_objets']['boucle'][$objet]->id_boucle;
} else {
$nom = $GLOBALS['debug_objets']['sourcefile'][$objet];
}
$res2 = "";
if ($affiche == 'resultat') {
$legend = $nom;
$req = $GLOBALS['debug_objets']['requete'][$objet];
if (function_exists('_mysql_traite_query')) {
$c = strtolower(_request('connect'));
$c = $GLOBALS['connexions'][$c ? $c : 0]['prefixe'];
$req = _mysql_traite_query($req, '', $c);
}
// permettre le copier/coller facile
// $res = ancre_texte($req, array(), true);
$res = "\n\n" . $req . "
\n\n";
// formatage et affichage des resultats bruts de la requete
$ress_req = spip_query($req);
$brut_sql = '';
$num = 1;
// eviter l'affichage de milliers de lignes
// personnalisation possible dans mes_options
$max_aff = defined('_MAX_DEBUG_AFF') ? _MAX_DEBUG_AFF : 50;
while ($retours_sql = sql_fetch($ress_req)) {
if ($num <= $max_aff) {
$brut_sql .= "" . ($num == 1 ? $num . " sur " . sql_count($ress_req) : $num) . "
";
$brut_sql .= "";
foreach ($retours_sql as $key => $val) {
$brut_sql .= "" . $key . " => " . spip_htmlspecialchars(couper($val, 150)) . "
\n";
}
$brut_sql .= "
";
}
$num++;
}
$res2 = interdire_scripts($brut_sql);
foreach ($quoi as $view) {
// ne pas afficher les $contexte_inclus
$view = preg_replace(",<\?php.+\?[>],Uims", "", $view);
if ($view) {
$res2 .= "\n
";
}
}
} elseif ($affiche == 'code') {
$legend = $nom;
$res = ancre_texte("<" . "?php\n" . $quoi . "\n?" . ">");
} elseif ($affiche == 'boucle') {
$legend = _T('zbug_boucle') . ' ' . $nom;
// Le compilateur prefixe le nom des boucles par l'extension du fichier source.
$gram = preg_match('/^([^_]+)_/', $objet, $r) ? $r[1] : '';
$res = ancre_texte(public_decompiler($quoi, $gram, 0, 'boucle'));
} elseif ($affiche == 'squelette') {
$legend = $GLOBALS['debug_objets']['sourcefile'][$objet];
$res = ancre_texte($GLOBALS['debug_objets']['squelette'][$objet]);
}
return array($legend, $res, $res2);
}
// http://code.spip.net/@debusquer_entete
function debusquer_entete($titre, $corps) {
include_spip('balise/formulaire_admin');
include_spip('public/assembler'); // pour inclure_balise_dynamique
include_spip('inc/texte'); // pour corriger_typo
return _DOCTYPE_ECRIRE .
html_lang_attributes() .
"\n" .
('SPIP ' . $GLOBALS['spip_version_affichee'] . ' ' .
_T('admin_debug') . ' ' . spip_htmlspecialchars($titre) . ' (' .
supprimer_tags(corriger_typo($GLOBALS['meta']['nom_site']))) .
") \n" .
"\n" .
http_script('', 'jquery.js')
. "" .
"\n" .
"\n" .
"" .
$corps .
inclure_balise_dynamique(balise_FORMULAIRE_ADMIN_dyn('spip-admin-float', $GLOBALS['debug_objets']), false) .
'