X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=www%2Fecrire%2Fpublic%2Fcomposer.php;fp=www%2Fecrire%2Fpublic%2Fcomposer.php;h=bcf789b2401215be56049225c9ed0675604a1273;hb=4f443dce95ff6f8221c189880a70c74ce1c1f238;hp=f96e8fbdd91118221afbb18f15bebb88ee9fbdcc;hpb=4a628e9b277d3617535f99d663ca79fa2e891177;p=lhc%2Fweb%2Fwww.git diff --git a/www/ecrire/public/composer.php b/www/ecrire/public/composer.php index f96e8fbd..bcf789b2 100644 --- a/www/ecrire/public/composer.php +++ b/www/ecrire/public/composer.php @@ -3,14 +3,23 @@ /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * - * Copyright (c) 2001-2016 * + * Copyright (c) 2001-2017 * * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ -if (!defined('_ECRIRE_INC_VERSION')) return; +/** + * Compose un squelette : compile le squelette au besoin et vérifie + * la validité du code compilé + * + * @package SPIP\Core\Compilateur\Composer + **/ + +if (!defined('_ECRIRE_INC_VERSION')) { + return; +} include_spip('inc/texte'); include_spip('inc/documents'); @@ -30,21 +39,24 @@ include_spip('public/quete'); # 3. des declaration de tables SQL supplementaires # Toutefois pour 2. et 3. preferer la technique de la surcharge -// http://doc.spip.org/@public_composer_dist -function public_composer_dist($squelette, $mime_type, $gram, $source, $connect='') { +// http://code.spip.net/@public_composer_dist +function public_composer_dist($squelette, $mime_type, $gram, $source, $connect = '') { $nom = calculer_nom_fonction_squel($squelette, $mime_type, $connect); // si deja en memoire (INCLURE a repetition) c'est bon. - if (function_exists($nom)) return $nom; + if (function_exists($nom)) { + return $nom; + } - if (defined('_VAR_MODE') AND _VAR_MODE == 'debug') + if (defined('_VAR_MODE') and _VAR_MODE == 'debug') { $GLOBALS['debug_objets']['courant'] = $nom; + } - $phpfile = sous_repertoire(_DIR_SKELS,'',false,true) . $nom . '.php'; + $phpfile = sous_repertoire(_DIR_SKELS, '', false, true) . $nom . '.php'; // si squelette est deja compile et perenne, le charger - if (!squelette_obsolete($phpfile, $source)){ + if (!squelette_obsolete($phpfile, $source)) { include_once $phpfile; #if (!squelette_obsolete($phpfile, $source) # AND lire_fichier ($phpfile, $skel_code, @@ -54,143 +66,159 @@ function public_composer_dist($squelette, $mime_type, $gram, $source, $connect=' #} } - if (file_exists($lib = $squelette . '_fonctions'.'.php')){ + if (file_exists($lib = $squelette . '_fonctions' . '.php')) { include_once $lib; } // tester si le eval ci-dessus a mis le squelette en memoire - if (function_exists($nom)) return $nom; + if (function_exists($nom)) { + return $nom; + } // charger le source, si possible, et compiler - if (lire_fichier ($source, $skel)) { + if (lire_fichier($source, $skel)) { $compiler = charger_fonction('compiler', 'public'); $skel_code = $compiler($skel, $nom, $gram, $source, $connect); } // Ne plus rien faire si le compilateur n'a pas pu operer. - if (!$skel_code) return false; + if (!$skel_code) { + return false; + } - foreach($skel_code as $id => $boucle) { + foreach ($skel_code as $id => $boucle) { $f = $boucle->return; - if (@eval("return true; $f ;") === false) { - // Code syntaxiquement faux (critere etc mal programme') + if (@eval("return true; $f ;") === false) { + // Code syntaxiquement faux (critere etc mal programme') $msg = _T('zbug_erreur_compilation'); erreur_squelette($msg, $boucle); // continuer pour trouver d'autres fautes eventuelles // mais prevenir que c'est mort $nom = ''; - } + } // Contexte de compil inutile a present // (mais la derniere valeur de $boucle est utilisee ci-dessous) $skel_code[$id] = $f; } + $code = ''; if ($nom) { // Si le code est bon, concatener et mettre en cache - if (function_exists($nom)) + if (function_exists($nom)) { $code = squelette_traduit($skel, $source, $phpfile, $skel_code); - else { - // code semantiquement faux: bug du compilateur - // $boucle est en fait ici la fct principale du squelette + } else { + // code semantiquement faux: bug du compilateur + // $boucle est en fait ici la fct principale du squelette $msg = _T('zbug_erreur_compilation'); erreur_squelette($msg, $boucle); $nom = ''; } } - if (defined('_VAR_MODE') AND _VAR_MODE == 'debug') { + if (defined('_VAR_MODE') and _VAR_MODE == 'debug') { // Tracer ce qui vient d'etre compile $GLOBALS['debug_objets']['code'][$nom . 'tout'] = $code; // si c'est ce que demande le debusqueur, lui passer la main if ($GLOBALS['debug_objets']['sourcefile'] - AND (_request('var_mode_objet') == $nom) - AND (_request('var_mode_affiche') == 'code') ) + and (_request('var_mode_objet') == $nom) + and (_request('var_mode_affiche') == 'code') + ) { erreur_squelette(); + } } + return $nom ? $nom : false; } -function squelette_traduit($squelette, $sourcefile, $phpfile, $boucles) -{ +function squelette_traduit($squelette, $sourcefile, $phpfile, $boucles) { // Le dernier index est '' (fonction principale) - $noms = substr(join (', ', array_keys($boucles)), 0, -2); - if (CODE_COMMENTE) - $code = " + $noms = substr(join(', ', array_keys($boucles)), 0, -2); + if (CODE_COMMENTE) { + $code = " /* * Squelette : $sourcefile - * Date : ".gmdate("D, d M Y H:i:s", @filemtime($sourcefile))." GMT - * Compile : ".gmdate("D, d M Y H:i:s", time())." GMT - * " . (!$boucles ? "Pas de boucle" : ("Boucles : " . $noms)) ." - */ " ; + * Date : " . gmdate("D, d M Y H:i:s", @filemtime($sourcefile)) . " GMT + * Compile : " . gmdate("D, d M Y H:i:s", time()) . " GMT + * " . (!$boucles ? "Pas de boucle" : ("Boucles : " . $noms)) . " + */ "; + } - $code = '<'. "?php\n" . $code . join('', $boucles) . "\n?" .'>'; - if (!defined('_VAR_NOCACHE') OR !_VAR_NOCACHE) + $code = '<' . "?php\n" . $code . join('', $boucles) . "\n?" . '>'; + if (!defined('_VAR_NOCACHE') or !_VAR_NOCACHE) { ecrire_fichier($phpfile, $code); + } + return $code; } // Le squelette compile est-il trop vieux ? -// http://doc.spip.org/@squelette_obsolete +// http://code.spip.net/@squelette_obsolete function squelette_obsolete($skel, $squelette) { static $date_change = null; // ne verifier la date de mes_fonctions et mes_options qu'une seule fois // par hit - if (is_null($date_change)){ - if (@file_exists($fonc = 'mes_fonctions.php')) - $date_change = @filemtime($fonc); # compatibilite - if (defined('_FILE_OPTIONS')) - $date_change = max($date_change,@filemtime(_FILE_OPTIONS)); + if (is_null($date_change)) { + if (@file_exists($fonc = 'mes_fonctions.php')) { + $date_change = @filemtime($fonc); + } # compatibilite + if (defined('_FILE_OPTIONS')) { + $date_change = max($date_change, @filemtime(_FILE_OPTIONS)); + } } + return ( - (defined('_VAR_MODE') AND in_array(_VAR_MODE, array('recalcul','preview','debug'))) - OR !@file_exists($skel) - OR ((@file_exists($squelette)?@filemtime($squelette):0) + (defined('_VAR_MODE') and in_array(_VAR_MODE, array('recalcul', 'preview', 'debug'))) + or !@file_exists($skel) + or ((@file_exists($squelette) ? @filemtime($squelette) : 0) > ($date = @filemtime($skel))) - OR ($date_change > $date) + or ($date_change > $date) ); } // Activer l'invalideur de session -// http://doc.spip.org/@invalideur_session -function invalideur_session(&$Cache, $code=NULL) { - $Cache['session']=spip_session(); +// http://code.spip.net/@invalideur_session +function invalideur_session(&$Cache, $code = null) { + $Cache['session'] = spip_session(); + return $code; } -// http://doc.spip.org/@analyse_resultat_skel -function analyse_resultat_skel($nom, $cache, $corps, $source='') { +// http://code.spip.net/@analyse_resultat_skel +function analyse_resultat_skel($nom, $cache, $corps, $source = '') { static $filtres = array(); $headers = array(); // Recupere les < ?php header('Xx: y'); ? > pour $page['headers'] // note: on essaie d'attrapper aussi certains de ces entetes codes // "a la main" dans les squelettes, mais evidemment sans exhaustivite - if (stripos($corps,'header')!==false - AND preg_match_all( - '/(<[?]php\s+)@?header\s*\(\s*.([^:\'"]*):?\s*([^)]*)[^)]\s*\)\s*[;]?\s*[?]>/ims', - $corps, $regs, PREG_SET_ORDER)){ + if (stripos($corps, 'header') !== false + and preg_match_all( + '/(<[?]php\s+)@?header\s*\(\s*.([^:\'"]*):?\s*([^)]*)[^)]\s*\)\s*[;]?\s*[?]>/ims', + $corps, $regs, PREG_SET_ORDER) + ) { foreach ($regs as $r) { $corps = str_replace($r[0], '', $corps); # $j = Content-Type, et pas content-TYPE. $j = join('-', array_map('ucwords', explode('-', strtolower($r[2])))); - if ($j=='X-Spip-Filtre' AND isset($headers[$j])) - $headers[$j].="|".$r[3]; - else + if ($j == 'X-Spip-Filtre' and isset($headers[$j])) { + $headers[$j] .= "|" . $r[3]; + } else { $headers[$j] = $r[3]; + } } } // S'agit-il d'un resultat constant ou contenant du code php $process_ins = ( - strpos($corps,'<'.'?') === false - OR - (strpos($corps,'<'.'?xml')!==false AND - strpos(str_replace('<'.'?xml', '', $corps),'<'.'?') === false) + strpos($corps, '<' . '?') === false + or + (strpos($corps, '<' . '?xml') !== false and + strpos(str_replace('<' . '?xml', '', $corps), '<' . '?') === false) ) ? 'html' : 'php'; @@ -205,13 +233,25 @@ function analyse_resultat_skel($nom, $cache, $corps, $source='') { ); // traiter #FILTRE{} et filtres - if (!isset($filtres[$nom])) { - $filtres[$nom] = pipeline('declarer_filtres_squelettes',array('args'=>$skel,'data'=>array())); - } - if (count($filtres[$nom]) OR (isset($headers['X-Spip-Filtre']) AND strlen($headers['X-Spip-Filtre']))) { + if (!isset($filtres[$nom])) { + $filtres[$nom] = pipeline('declarer_filtres_squelettes', array('args' => $skel, 'data' => array())); + } + if (count($filtres[$nom]) or (isset($headers['X-Spip-Filtre']) and strlen($headers['X-Spip-Filtre']))) { include_spip('public/sandbox'); - $corps = sandbox_filtrer_squelette($skel,$corps,strlen($headers['X-Spip-Filtre'])?explode('|', $headers['X-Spip-Filtre']):array(),$filtres[$nom]); + $corps = sandbox_filtrer_squelette($skel, $corps, + strlen($headers['X-Spip-Filtre']) ? explode('|', $headers['X-Spip-Filtre']) : array(), $filtres[$nom]); unset($headers['X-Spip-Filtre']); + + if ($process_ins == 'html') { + $skel['process_ins'] = ( + strpos($corps, '<' . '?') === false + or + (strpos($corps, '<' . '?xml') !== false and + strpos(str_replace('<' . '?xml', '', $corps), '<' . '?') === false) + ) + ? 'html' + : 'php'; + } } $skel['entetes'] = $headers; @@ -229,16 +269,35 @@ function analyse_resultat_skel($nom, $cache, $corps, $source='') { // -// -// fonction standard de calcul de la balise #INTRODUCTION -// on peut la surcharger en definissant dans mes_fonctions : -// function filtre_introduction() -// -// http://doc.spip.org/@filtre_introduction_dist -function filtre_introduction_dist($descriptif, $texte, $longueur, $connect) { +/** + * Calcul d'une introduction + * + * L'introduction est prise dans le descriptif s'il est renseigné, + * sinon elle est calculée depuis le texte : à ce moment là, + * l'introduction est prise dans le contenu entre les balises + * `` et `` si présentes, sinon en coupant le + * texte à la taille indiquée. + * + * Cette fonction est utilisée par la balise #INTRODUCTION + * + * @param string $descriptif + * Descriptif de l'introduction + * @param string $texte + * Texte à utiliser en absence de descriptif + * @param string $longueur + * Longueur de l'introduction + * @param string $connect + * Nom du connecteur à la base de données + * @param string $suite + * points de suite si on coupe (par defaut _INTRODUCTION_SUITE et sinon  (...) + * @return string + * Introduction calculée + **/ +function filtre_introduction_dist($descriptif, $texte, $longueur, $connect, $suite = null) { // Si un descriptif est envoye, on l'utilise directement - if (strlen($descriptif)) - return propre($descriptif,$connect); + if (strlen($descriptif)) { + return appliquer_traitement_champ($descriptif, 'introduction', '', array(), $connect); + } // De preference ce qui est marque ... $intro = ''; @@ -246,8 +305,9 @@ function filtre_introduction_dist($descriptif, $texte, $longueur, $connect) { while ($fin = strpos($texte, "")) { $zone = substr($texte, 0, $fin); $texte = substr($texte, $fin + strlen("")); - if ($deb = strpos($zone, "") OR substr($zone, 0, 7) == "") + if ($deb = strpos($zone, "") or substr($zone, 0, 7) == "") { $zone = substr($zone, $deb + 7); + } $intro .= $zone; } @@ -262,34 +322,45 @@ function filtre_introduction_dist($descriptif, $texte, $longueur, $connect) { // Cependant pour des questions de perfs on coupe quand meme, en prenant // large et en se mefiant des tableaux #1323 - if (strlen($intro)) + if (strlen($intro)) { $texte = $intro; - - else - if (strpos("\n".$texte, "\n|")===false - AND strlen($texte) > 2.5*$longueur){ - if (strpos($texte," 2.5 * $longueur + ) { + if (strpos($texte, " + if ($GLOBALS['toujours_paragrapher']) // Fermer les paragraphes + { $texte = paragrapher($texte, $GLOBALS['toujours_paragrapher']); + } return $texte; } @@ -298,141 +369,258 @@ function filtre_introduction_dist($descriptif, $texte, $longueur, $connect) { // Balises dynamiques // -// elles sont traitees comme des inclusions -// http://doc.spip.org/@synthetiser_balise_dynamique - +/** Code PHP pour inclure une balise dynamique à l'exécution d'une page */ define('CODE_INCLURE_BALISE', '<' . '?php -include_once("./" . _DIR_RACINE . "%s"); +include_once("%s"); if ($lang_select = "%s") $lang_select = lang_select($lang_select); inserer_balise_dynamique(balise_%s_dyn(%s), array(%s)); if ($lang_select) lang_select(); ?' - .'>'); - + . '>'); +/** + * Synthétise une balise dynamique : crée l'appel à l'inclusion + * en transmettant les arguments calculés et le contexte de compilation. + * + * @uses argumenter_squelette() Pour calculer les arguments de l'inclusion + * + * @param string $nom + * Nom de la balise dynamique + * @param array $args + * Liste des arguments calculés + * @param string $file + * Chemin du fichier de squelette à inclure + * @param array $context_compil + * Tableau d'informations sur la compilation + * @return string + * Code PHP pour inclure le squelette de la balise dynamique + **/ function synthetiser_balise_dynamique($nom, $args, $file, $context_compil) { + if (strncmp($file, "/", 1) !== 0) { + $file = './" . _DIR_RACINE . "' . $file; + } $r = sprintf(CODE_INCLURE_BALISE, - $file, - $context_compil[4]?$context_compil[4]:'', - $nom, - join(', ', array_map('argumenter_squelette', $args)), - join(', ', array_map('_q', $context_compil))); + $file, + $context_compil[4] ? $context_compil[4] : '', + $nom, + join(', ', array_map('argumenter_squelette', $args)), + join(', ', array_map('_q', $context_compil))); + return $r; } -// http://doc.spip.org/@argumenter_squelette +/** + * Crée le code PHP pour transmettre des arguments (généralement pour une inclusion) + * + * @param array|string $v + * Arguments à transmettre : + * + * - string : un simple texte à faire écrire + * - array : couples ('nom' => 'valeur') liste des arguments et leur valeur + * @return string + * + * - Code PHP créant le tableau des arguments à transmettre, + * - ou texte entre quote `'` (si `$v` était une chaîne) + **/ function argumenter_squelette($v) { - if (!is_array($v)) + if (!is_array($v)) { return "'" . texte_script($v) . "'"; - else { + } else { $out = array(); - foreach($v as $k=>$val) + foreach ($v as $k => $val) { $out [] = argumenter_squelette($k) . '=>' . argumenter_squelette($val); - return 'array(' . join(", ", $out) . ')'; + } + + return 'array(' . join(", ", $out) . ')'; } } -// verifier leurs arguments et filtres, et calculer le code a inclure -// http://doc.spip.org/@executer_balise_dynamique + +/** + * Calcule et retourne le code PHP retourné par l'exécution d'une balise + * dynamique. + * + * Vérifier les arguments et filtres et calcule le code PHP à inclure. + * + * - charge le fichier PHP de la balise dynamique dans le répertoire + * `balise/`, soit du nom complet de la balise, soit d'un nom générique + * (comme 'formulaire_.php'). Dans ce dernier cas, le nom de la balise + * est ajouté en premier argument. + * - appelle une éventuelle fonction de traitement des arguments `balise_NOM_stat()` + * - crée le code PHP de la balise si une fonction `balise_NOM_dyn()` (ou variantes) + * est effectivement trouvée. + * + * @uses synthetiser_balise_dynamique() + * Pour calculer le code PHP d'inclusion produit + * + * @param string $nom + * Nom de la balise dynamique + * @param array $args + * Liste des arguments calculés de la balise + * @param array $context_compil + * Tableau d'informations sur la compilation + * @return string + * Code PHP d'exécutant l'inclusion du squelette (ou texte) de la balise dynamique + **/ function executer_balise_dynamique($nom, $args, $context_compil) { - $p = strpos($nom,"_"); $nomfonction = $nom; - $nomfonction_generique = substr($nom,0,$p+1); - if (!$file = include_spip("balise/". strtolower($nomfonction))) { - // pas de fichier associe, passer au traitement generique - $file = include_spip("balise/" .strtolower($nomfonction_generique)); - if ($file) { + $nomfonction_generique = ""; + + // Calculer un nom générique (ie. 'formulaire_' dans 'formulaire_editer_article') + if (false !== ($p = strpos($nom, "_"))) { + $nomfonction_generique = substr($nom, 0, $p + 1); + } + + if (!$fonction_balise = charger_fonction($nomfonction, 'balise', true)) { + if ($nomfonction_generique and $fonction_balise = charger_fonction($nomfonction_generique, 'balise', true)) { // et injecter en premier arg le nom de la balise - array_unshift($args,$nom); - // et passer sur la fonction generique pour la suite + array_unshift($args, $nom); $nomfonction = $nomfonction_generique; } - else { - $msg = array('zbug_balise_inexistante',array('from'=>'CVT','balise'=>$nom)); - erreur_squelette($msg, $context_compil); - return ''; - } } + + if (!$fonction_balise) { + $msg = array('zbug_balise_inexistante', array('from' => 'CVT', 'balise' => $nom)); + erreur_squelette($msg, $context_compil); + + return ''; + } + + // retrouver le fichier qui a déclaré la fonction + // même si la fonction dynamique est déclarée dans un fichier de fonctions. + // Attention sous windows, getFileName() retourne un antislash. + $reflector = new ReflectionFunction($fonction_balise); + $file = str_replace('\\', '/', $reflector->getFileName()); + if (strncmp($file, str_replace('\\', '/', _ROOT_RACINE), strlen(_ROOT_RACINE)) === 0) { + $file = substr($file, strlen(_ROOT_RACINE)); + } + // Y a-t-il une fonction de traitement des arguments ? $f = 'balise_' . $nomfonction . '_stat'; - $r = !function_exists($f) ? $args : $f($args, $context_compil); + $r = !function_exists($f) ? $args : $f($args, $context_compil); - if (!is_array($r)) return $r; + if (!is_array($r)) { + return $r; + } // verifier que la fonction dyn est la, // sinon se replier sur la generique si elle existe if (!function_exists('balise_' . $nomfonction . '_dyn')) { - $file = include_spip("balise/" .strtolower($nomfonction_generique)); - if (function_exists('balise_' . $nomfonction_generique . '_dyn')) { + if ($nomfonction_generique + and $file = include_spip("balise/" . strtolower($nomfonction_generique)) + and function_exists('balise_' . $nomfonction_generique . '_dyn') + ) { // et lui injecter en premier arg le nom de la balise - array_unshift($r,$nom); + array_unshift($r, $nom); $nomfonction = $nomfonction_generique; + if (!_DIR_RESTREINT) { + $file = _DIR_RESTREINT_ABS . $file; + } } else { - $msg = array('zbug_balise_inexistante',array('from'=>'CVT','balise'=>$nom)); + $msg = array('zbug_balise_inexistante', array('from' => 'CVT', 'balise' => $nom)); erreur_squelette($msg, $context_compil); + return ''; } } - if (!_DIR_RESTREINT) - $file = _DIR_RESTREINT_ABS . $file; return synthetiser_balise_dynamique($nomfonction, $r, $file, $context_compil); + } -// http://doc.spip.org/@lister_objets_avec_logos -function lister_objets_avec_logos ($type) { - global $formats_logos; +/** + * Retourne pour une clé primaire d'objet donnée les identifiants ayant un logo + * + * @uses type_du_logo() Pour calculer le nom du logo + * + * @param string $type + * Nom de la clé primaire de l'objet + * @return string + * Liste des identifiants ayant un logo (séparés par une virgule) + **/ +function lister_objets_avec_logos($type) { + $logos = array(); $chercher_logo = charger_fonction('chercher_logo', 'inc'); $type = '/' - . type_du_logo($type) - . "on(\d+)\.(" - . join('|',$formats_logos) - . ")$/"; - - if ($d = @opendir(_DIR_LOGOS)) { - while($f = readdir($d)) { - if (preg_match($type, $f, $r)) + . type_du_logo($type) + . "on(\d+)\.(" + . join('|', $GLOBALS['formats_logos']) + . ")$/"; + + if ($d = opendir(_DIR_LOGOS)) { + while (($f = readdir($d)) !== false) { + if (preg_match($type, $f, $r)) { $logos[] = $r[1]; + } } } @closedir($d); - return join(',',$logos); + + return join(',', $logos); } -// fonction appelee par la balise #NOTES -// Renvoyer l'etat courant des notes, le purger et en preparer un nouveau -// http://doc.spip.org/@calculer_notes + +/** + * Renvoie l'état courant des notes, le purge et en prépare un nouveau + * + * Fonction appelée par la balise `#NOTES` + * + * @see balise_NOTES_dist() + * @uses inc_notes_dist() + * + * @return string + * Code HTML des notes + **/ function calculer_notes() { - $r=''; + $r = ''; if ($notes = charger_fonction('notes', 'inc', true)) { $r = $notes(array()); - $notes('','depiler'); - $notes('','empiler'); + $notes('', 'depiler'); + $notes('', 'empiler'); } + return $r; } -// Selectionner la langue de l'objet dans la boucle, sauf dans les -// cas ou il ne le faut pas :-) -function lang_select_public($lang, $lang_select, $titre=null) { +/** + * Selectionner la langue de l'objet dans la boucle + * + * Applique sur un item de boucle la langue de l'élément qui est parcourru. + * Sauf dans les cas ou il ne le faut pas ! + * + * La langue n'est pas modifiée lorsque : + * - la globale 'forcer_lang' est définie à true + * - l'objet ne définit pas de langue + * - le titre contient une balise multi. + * + * @param string $lang + * Langue de l'objet + * @param string $lang_select + * 'oui' si critère lang_select est présent, '' sinon. + * @param null|string $titre + * Titre de l'objet + * @return null; + **/ +function lang_select_public($lang, $lang_select, $titre = null) { // Cas 1. forcer_lang = true et pas de critere {lang_select} - if (isset($GLOBALS['forcer_lang']) AND $GLOBALS['forcer_lang'] - AND $lang_select !== 'oui') + if (isset($GLOBALS['forcer_lang']) and $GLOBALS['forcer_lang'] + and $lang_select !== 'oui' + ) { $lang = $GLOBALS['spip_lang']; - - // Cas 2. l'objet n'a pas de langue definie (ou definie a '') - elseif (!strlen($lang)) + } // Cas 2. l'objet n'a pas de langue definie (ou definie a '') + elseif (!strlen($lang)) { $lang = $GLOBALS['spip_lang']; - - // Cas 3. l'objet est multilingue ! + } // Cas 3. l'objet est multilingue ! elseif ($lang_select !== 'oui' - AND strlen($titre) > 10 - AND strpos($titre, '') !== false - AND strpos(echappe_html($titre), '') !== false) + and strlen($titre) > 10 + and strpos($titre, '') !== false + and strpos(echappe_html($titre), '') !== false + ) { $lang = $GLOBALS['spip_lang']; + } // faire un lang_select() eventuellement sur la langue inchangee lang_select($lang); @@ -443,57 +631,113 @@ function lang_select_public($lang, $lang_select, $titre=null) { // Si un tableau &doublons[articles] est passe en parametre, // il faut le nettoyer car il pourrait etre injecte en SQL -// http://doc.spip.org/@nettoyer_env_doublons +// http://code.spip.net/@nettoyer_env_doublons function nettoyer_env_doublons($envd) { foreach ($envd as $table => $liste) { $n = ''; - foreach(explode(',',$liste) as $val) { - if ($a = intval($val) AND $val === strval($a)) - $n.= ','.$val; + foreach (explode(',', $liste) as $val) { + if ($a = intval($val) and $val === strval($a)) { + $n .= ',' . $val; + } } - if (strlen($n)) + if (strlen($n)) { $envd[$table] = $n; - else + } else { unset($envd[$table]); + } } + return $envd; } -// http://doc.spip.org/@match_self -function match_self($w){ - if (is_string($w)) return false; +/** + * Cherche la présence d'un opérateur SELF ou SUBSELECT + * + * Cherche dans l'index 0 d'un tableau, la valeur SELF ou SUBSELECT + * indiquant pour une expression WHERE de boucle que nous sommes + * face à une sous-requête. + * + * Cherche de manière récursive également dans les autres valeurs si celles-ci + * sont des tableaux + * + * @param string|array $w + * Description d'une condition WHERE de boucle (ou une partie de cette description) + * @return string|bool + * Opérateur trouvé (SELF ou SUBSELECT) sinon false. + **/ +function match_self($w) { + if (is_string($w)) { + return false; + } if (is_array($w)) { - if (in_array(reset($w),array("SELF","SUBSELECT"))) return $w; - foreach(array_filter($w,'is_array') as $sw) - if ($m=match_self($sw)) return $m; + if (in_array(reset($w), array("SELF", "SUBSELECT"))) { + return $w; + } + foreach (array_filter($w, 'is_array') as $sw) { + if ($m = match_self($sw)) { + return $m; + } + } } + return false; } -// http://doc.spip.org/@remplace_sous_requete -function remplace_sous_requete($w,$sousrequete){ + +/** + * Remplace une condition décrivant une sous requête par son code + * + * @param array|string $w + * Description d'une condition WHERE de boucle (ou une partie de cette description) + * qui possède une description de sous-requête + * @param string $sousrequete + * Code PHP de la sous requête (qui doit remplacer la description) + * @return array|string + * Tableau de description du WHERE dont la description de sous-requête + * est remplacée par son code. + **/ +function remplace_sous_requete($w, $sousrequete) { if (is_array($w)) { - if (in_array(reset($w),array("SELF","SUBSELECT"))) return $sousrequete; - foreach($w as $k=>$sw) - $w[$k] = remplace_sous_requete($sw,$sousrequete); + if (in_array(reset($w), array("SELF", "SUBSELECT"))) { + return $sousrequete; + } + foreach ($w as $k => $sw) { + $w[$k] = remplace_sous_requete($sw, $sousrequete); + } } + return $w; } -// http://doc.spip.org/@trouver_sous_requetes -function trouver_sous_requetes($where){ + +/** + * Sépare les conditions de boucles simples de celles possédant des sous-requêtes. + * + * @param array $where + * Description d'une condition WHERE de boucle + * @return array + * Liste de 2 tableaux : + * - Conditions simples (ne possédant pas de sous requêtes) + * - Conditions avec des sous requêtes + **/ +function trouver_sous_requetes($where) { $where_simples = array(); $where_sous = array(); - foreach($where as $k=>$w){ - if (match_self($w)) $where_sous[$k] = $w; - else $where_simples[$k] = $w; + foreach ($where as $k => $w) { + if (match_self($w)) { + $where_sous[$k] = $w; + } else { + $where_simples[$k] = $w; + } } - return array($where_simples,$where_sous); + + return array($where_simples, $where_sous); } /** - * La fonction presente dans les squelettes compiles + * Calcule une requête et l’exécute * - * http://doc.spip.org/@calculer_select + * Cette fonction est présente dans les squelettes compilés. + * Elle peut permettre de générer des requêtes avec jointure. * * @param array $select * @param array $from @@ -510,42 +754,61 @@ function trouver_sous_requetes($where){ * @param bool $requeter * @return resource */ -function calculer_select ($select = array(), $from = array(), - $from_type = array(), - $where = array(), $join=array(), - $groupby = array(), $orderby = array(), $limit = '', - $having=array(), $table = '', $id = '', $serveur='', $requeter=true) { +function calculer_select( + $select = array(), + $from = array(), + $from_type = array(), + $where = array(), + $join = array(), + $groupby = array(), + $orderby = array(), + $limit = '', + $having = array(), + $table = '', + $id = '', + $serveur = '', + $requeter = true +) { // retirer les criteres vides: // {X ?} avec X absent de l'URL // {par #ENV{X}} avec X absent de l'URL // IN sur collection vide (ce dernier devrait pouvoir etre fait a la compil) $menage = false; - foreach($where as $k => $v) { - if (is_array($v)){ - if ((count($v)>=2) && ($v[0]=='REGEXP') && ($v[2]=="'.*'")) $op= false; - elseif ((count($v)>=2) && ($v[0]=='LIKE') && ($v[2]=="'%'")) $op= false; - else $op = $v[0] ? $v[0] : $v; - } else $op = $v; - if ((!$op) OR ($op==1) OR ($op=='0=0')) { + foreach ($where as $k => $v) { + if (is_array($v)) { + if ((count($v) >= 2) && ($v[0] == 'REGEXP') && ($v[2] == "'.*'")) { + $op = false; + } elseif ((count($v) >= 2) && ($v[0] == 'LIKE') && ($v[2] == "'%'")) { + $op = false; + } else { + $op = $v[0] ? $v[0] : $v; + } + } else { + $op = $v; + } + if ((!$op) or ($op == 1) or ($op == '0=0')) { unset($where[$k]); $menage = true; } } // evacuer les eventuels groupby vide issus d'un calcul dynamique - $groupby = array_diff($groupby,array('')); + $groupby = array_diff($groupby, array('')); // remplacer les sous requetes recursives au calcul - list($where_simples,$where_sous) = trouver_sous_requetes($where); - foreach($where_sous as $k=>$w) { + list($where_simples, $where_sous) = trouver_sous_requetes($where); + foreach ($where_sous as $k => $w) { $menage = true; // on recupere la sous requete $sous = match_self($w); - if ($sous[0]=='SELF') { + if ($sous[0] == 'SELF') { // c'est une sous requete identique a elle meme sous la forme (SELF,$select,$where) - array_push($where_simples,$sous[2]); - $wheresub = array($sous[2],'0=0'); // pour accepter une string et forcer a faire le menage car on a surement simplifie select et where + array_push($where_simples, $sous[2]); + $wheresub = array( + $sous[2], + '0=0' + ); // pour accepter une string et forcer a faire le menage car on a surement simplifie select et where $jsub = $join; // trouver les jointures utiles a // reinjecter dans le where de la sous requete les conditions supplementaires des jointures qui y sont mentionnees @@ -553,50 +816,51 @@ function calculer_select ($select = array(), $from = array(), // on construit le where une fois, puis on ajoute les where complentaires si besoin, et on reconstruit le where en fonction $i = 0; do { - $where[$k] = remplace_sous_requete($w,"(".calculer_select( - array($sous[1]." AS id"), - $from, - $from_type, - $wheresub, - $jsub, - array(),array(),'', - $having,$table,$id,$serveur,false).")"); - if (!$i){ + $where[$k] = remplace_sous_requete($w, "(" . calculer_select( + array($sous[1] . " AS id"), + $from, + $from_type, + $wheresub, + $jsub, + array(), array(), '', + $having, $table, $id, $serveur, false) . ")"); + if (!$i) { $i = 1; $wherestring = calculer_where_to_string($where[$k]); - foreach ($join as $cle=>$wj){ - if (count($wj)==4 - AND strpos($wherestring,"{$cle}.")!==FALSE - ){ + foreach ($join as $cle => $wj) { + if (count($wj) == 4 + and strpos($wherestring, "{$cle}.") !== false + ) { $i = 0; $wheresub[] = $wj[3]; unset($jsub[$cle][3]); } } } - } while ($i++<1); + } while ($i++ < 1); } - if ($sous[0]=='SUBSELECT') { + if ($sous[0] == 'SUBSELECT') { // c'est une sous requete explicite sous la forme identique a sql_select : (SUBSELECT,$select,$from,$where,$groupby,$orderby,$limit,$having) - array_push($where_simples,$sous[3]); // est-ce utile dans ce cas ? - $where[$k] = remplace_sous_requete($w,"(".calculer_select( - $sous[1], # select - $sous[2], #from - array(), #from_type - $sous[3]?(is_array($sous[3])?$sous[3]:array($sous[3])):array(), #where, qui peut etre de la forme string comme dans sql_select - array(), #join - $sous[4]?$sous[4]:array(), #groupby - $sous[5]?$sous[5]:array(), #orderby - $sous[6], #limit - $sous[7]?$sous[7]:array(), #having - $table,$id,$serveur,false - ).")"); + array_push($where_simples, $sous[3]); // est-ce utile dans ce cas ? + $where[$k] = remplace_sous_requete($w, "(" . calculer_select( + $sous[1], # select + $sous[2], #from + array(), #from_type + $sous[3] ? (is_array($sous[3]) ? $sous[3] : array($sous[3])) : array(), + #where, qui peut etre de la forme string comme dans sql_select + array(), #join + $sous[4] ? $sous[4] : array(), #groupby + $sous[5] ? $sous[5] : array(), #orderby + $sous[6], #limit + $sous[7] ? $sous[7] : array(), #having + $table, $id, $serveur, false + ) . ")"); } array_pop($where_simples); } - foreach($having as $k => $v) { - if ((!$v) OR ($v==1) OR ($v=='0=0')) { + foreach ($having as $k => $v) { + if ((!$v) or ($v == 1) or ($v == '0=0')) { unset($having[$k]); } } @@ -605,36 +869,43 @@ function calculer_select ($select = array(), $from = array(), // Retirer celles seulement utiles aux criteres finalement absents mais // parcourir de la plus recente a la moins recente pour pouvoir eliminer Ln // si elle est seulement utile a Ln+1 elle meme inutile - + $afrom = array(); $equiv = array(); $k = count($join); - foreach(array_reverse($join,true) as $cledef=>$j) { + foreach (array_reverse($join, true) as $cledef => $j) { $cle = $cledef; // le format de join est : // array(table depart, cle depart [,cle arrivee[,condition optionnelle and ...]]) - if (count($join[$cle])==2) $join[$cle][] = $join[$cle][1]; - if (count($join[$cle])==3) $join[$cle][] = ''; - list($t,$c,$carr,$and) = $join[$cle]; + $join[$cle] = array_values($join[$cle]); // recalculer les cles car des unset ont pu perturber + if (count($join[$cle]) == 2) { + $join[$cle][] = $join[$cle][1]; + } + if (count($join[$cle]) == 3) { + $join[$cle][] = ''; + } + list($t, $c, $carr, $and) = $join[$cle]; // si le nom de la jointure n'a pas ete specifiee, on prend Lx avec x sont rang dans la liste // pour compat avec ancienne convention - if (is_numeric($cle)) + if (is_numeric($cle)) { $cle = "L$k"; + } if (!$menage - OR isset($afrom[$cle]) - OR calculer_jointnul($cle, $select) - OR calculer_jointnul($cle, array_diff_key($join, array($cle=>$join[$cle]))) - OR calculer_jointnul($cle, $having) - OR calculer_jointnul($cle, $where_simples)) { + or isset($afrom[$cle]) + or calculer_jointnul($cle, $select) + or calculer_jointnul($cle, array_diff_key($join, array($cle => $join[$cle]))) + or calculer_jointnul($cle, $having) + or calculer_jointnul($cle, $where_simples) + ) { // corriger les references non explicites dans select // ou groupby - foreach($select as $i=>$s) { + foreach ($select as $i => $s) { if ($s == $c) { $select[$i] = "$cle.$c AS $c"; break; } } - foreach($groupby as $i=>$g) { + foreach ($groupby as $i => $g) { if ($g == $c) { $groupby[$i] = "$cle.$c"; break; @@ -643,22 +914,26 @@ function calculer_select ($select = array(), $from = array(), // on garde une ecriture decomposee pour permettre une simplification ulterieure si besoin // sans recours a preg_match // un implode(' ',..) est fait dans reinjecte_joint un peu plus bas - $afrom[$t][$cle] = array("\n" . - (isset($from_type[$cle])?$from_type[$cle]:"INNER")." JOIN", + $afrom[$t][$cle] = array( + "\n" . + (isset($from_type[$cle]) ? $from_type[$cle] : "INNER") . " JOIN", $from[$cle], "AS $cle", "ON (", "$cle.$c", "=", "$t.$carr", - ($and ? "AND ". $and:"") . - ")"); - if (isset($afrom[$cle])){ + ($and ? "AND " . $and : "") . + ")" + ); + if (isset($afrom[$cle])) { $afrom[$t] = $afrom[$t] + $afrom[$cle]; unset($afrom[$cle]); } - $equiv[]= $carr; - } else { unset($join[$cledef]);} + $equiv[] = $carr; + } else { + unset($join[$cledef]); + } unset($from[$cle]); $k--; } @@ -678,135 +953,146 @@ function calculer_select ($select = array(), $from = array(), // #ID_ARTICLE // penser a regarder aussi la clause groubpy pour ne pas simplifier abusivement // #TOTAL_BOUCLE - - list($t,$c) = each($from); - reset($from); - $e = '/\b(' . "$t\\." . join("|" . $t . '\.', $equiv) . ')\b/'; - if (!(strpos($t, ' ') OR // jointure des le depart cf boucle_doc - calculer_jointnul($t, $select, $e) OR - calculer_jointnul($t, $join, $e) OR - calculer_jointnul($t, $where, $e) OR - calculer_jointnul($t, $orderby, $e) OR - calculer_jointnul($t, $groupby, $e) OR - calculer_jointnul($t, $having, $e)) - && count($afrom[$t])) { - reset($afrom[$t]); - list($nt,$nfrom) = each($afrom[$t]); - unset($from[$t]); - $from[$nt] = $nfrom[1]; - unset($afrom[$t][$nt]); - $afrom[$nt] = $afrom[$t]; - unset($afrom[$t]); - $e = '/\b'.preg_quote($nfrom[6]).'\b/'; - $t = $nfrom[4]; - $alias = ""; - // verifier que les deux cles sont homonymes, sinon installer un alias dans le select - $oldcle = explode('.',$nfrom[6]); - $oldcle = end($oldcle); - $newcle = explode('.',$nfrom[4]); - $newcle = end($newcle); - if ($newcle!=$oldcle){ - // si l'ancienne cle etait deja dans le select avec un AS - // reprendre simplement ce AS - $as = '/\b'.preg_quote($nfrom[6]).'\s+(AS\s+\w+)\b/'; - if (preg_match($as,implode(',',$select),$m)){ - $alias = ""; - } - else - $alias = ", ".$nfrom[4]." AS $oldcle"; - } - $select = remplacer_jointnul($t . $alias, $select, $e); - $join = remplacer_jointnul($t, $join, $e); - $where = remplacer_jointnul($t, $where, $e); - $having = remplacer_jointnul($t, $having, $e); - $groupby = remplacer_jointnul($t, $groupby, $e); - $orderby = remplacer_jointnul($t, $orderby, $e); - } - $from = reinjecte_joint($afrom, $from); - } - $GLOBALS['debug']['aucasou'] = array ($table, $id, $serveur, $requeter); + + list($t, $c) = each($from); + reset($from); + $e = '/\b(' . "$t\\." . join("|" . $t . '\.', $equiv) . ')\b/'; + if (!(strpos($t, ' ') or // jointure des le depart cf boucle_doc + calculer_jointnul($t, $select, $e) or + calculer_jointnul($t, $join, $e) or + calculer_jointnul($t, $where, $e) or + calculer_jointnul($t, $orderby, $e) or + calculer_jointnul($t, $groupby, $e) or + calculer_jointnul($t, $having, $e)) + && count($afrom[$t]) + ) { + reset($afrom[$t]); + list($nt, $nfrom) = each($afrom[$t]); + unset($from[$t]); + $from[$nt] = $nfrom[1]; + unset($afrom[$t][$nt]); + $afrom[$nt] = $afrom[$t]; + unset($afrom[$t]); + $e = '/\b' . preg_quote($nfrom[6]) . '\b/'; + $t = $nfrom[4]; + $alias = ""; + // verifier que les deux cles sont homonymes, sinon installer un alias dans le select + $oldcle = explode('.', $nfrom[6]); + $oldcle = end($oldcle); + $newcle = explode('.', $nfrom[4]); + $newcle = end($newcle); + if ($newcle != $oldcle) { + // si l'ancienne cle etait deja dans le select avec un AS + // reprendre simplement ce AS + $as = '/\b' . preg_quote($nfrom[6]) . '\s+(AS\s+\w+)\b/'; + if (preg_match($as, implode(',', $select), $m)) { + $alias = ""; + } else { + $alias = ", " . $nfrom[4] . " AS $oldcle"; + } + } + $select = remplacer_jointnul($t . $alias, $select, $e); + $join = remplacer_jointnul($t, $join, $e); + $where = remplacer_jointnul($t, $where, $e); + $having = remplacer_jointnul($t, $having, $e); + $groupby = remplacer_jointnul($t, $groupby, $e); + $orderby = remplacer_jointnul($t, $orderby, $e); + } + $from = reinjecte_joint($afrom, $from); + } + $GLOBALS['debug']['aucasou'] = array($table, $id, $serveur, $requeter); $r = sql_select($select, $from, $where, $groupby, array_filter($orderby), $limit, $having, $serveur, $requeter); unset($GLOBALS['debug']['aucasou']); + return $r; } /** * Analogue a calculer_mysql_expression et autre (a unifier ?) + * * @param string|array $v * @param string $join * @return string */ -function calculer_where_to_string($v, $join = 'AND'){ - if (empty($v)) +function calculer_where_to_string($v, $join = 'AND') { + if (empty($v)) { return ''; + } if (!is_array($v)) { return $v; } else { $exp = ""; - if (strtoupper($join) === 'AND') + if (strtoupper($join) === 'AND') { return $exp . join(" $join ", array_map('calculer_where_to_string', $v)); - else + } else { return $exp . join($join, $v); + } } } //condition suffisante (mais non necessaire) pour qu'une table soit utile -// http://doc.spip.org/@calculer_jointnul -function calculer_jointnul($cle, $exp, $equiv='') -{ +// http://code.spip.net/@calculer_jointnul +function calculer_jointnul($cle, $exp, $equiv = '') { if (!is_array($exp)) { - if ($equiv) $exp = preg_replace($equiv, '', $exp); + if ($equiv) { + $exp = preg_replace($equiv, '', $exp); + } + return preg_match("/\\b$cle\\./", $exp); } else { - foreach($exp as $v) { - if (calculer_jointnul($cle, $v, $equiv)) return true; + foreach ($exp as $v) { + if (calculer_jointnul($cle, $v, $equiv)) { + return true; + } } + return false; } } -// http://doc.spip.org/@reinjecte_joint -function reinjecte_joint($afrom, $from) -{ - $from_synth = array(); - foreach($from as $k=>$v){ - $from_synth[$k]=$from[$k]; - if (isset($afrom[$k])) { - foreach($afrom[$k] as $kk=>$vv) $afrom[$k][$kk] = implode(' ',$afrom[$k][$kk]); - $from_synth["$k@"]= implode(' ',$afrom[$k]); - unset($afrom[$k]); - } - } - return $from_synth; +// http://code.spip.net/@reinjecte_joint +function reinjecte_joint($afrom, $from) { + $from_synth = array(); + foreach ($from as $k => $v) { + $from_synth[$k] = $from[$k]; + if (isset($afrom[$k])) { + foreach ($afrom[$k] as $kk => $vv) { + $afrom[$k][$kk] = implode(' ', $afrom[$k][$kk]); + } + $from_synth["$k@"] = implode(' ', $afrom[$k]); + unset($afrom[$k]); + } + } + + return $from_synth; } -// http://doc.spip.org/@remplacer_jointnul -function remplacer_jointnul($cle, $exp, $equiv='') -{ +// http://code.spip.net/@remplacer_jointnul +function remplacer_jointnul($cle, $exp, $equiv = '') { if (!is_array($exp)) { return preg_replace($equiv, $cle, $exp); } else { - foreach($exp as $k => $v) { - $exp[$k] = remplacer_jointnul($cle, $v, $equiv); + foreach ($exp as $k => $v) { + $exp[$k] = remplacer_jointnul($cle, $v, $equiv); } + return $exp; } } // calcul du nom du squelette -// http://doc.spip.org/@calculer_nom_fonction_squel -function calculer_nom_fonction_squel($skel, $mime_type='html', $connect='') -{ +// http://code.spip.net/@calculer_nom_fonction_squel +function calculer_nom_fonction_squel($skel, $mime_type = 'html', $connect = '') { // ne pas doublonner les squelette selon qu'ils sont calcules depuis ecrire/ ou depuis la racine - if ($l=strlen(_DIR_RACINE) AND strncmp($skel,_DIR_RACINE,$l)==0) - $skel = substr($skel,strlen(_DIR_RACINE)); + if ($l = strlen(_DIR_RACINE) and strncmp($skel, _DIR_RACINE, $l) == 0) { + $skel = substr($skel, strlen(_DIR_RACINE)); + } + return $mime_type - . (!$connect ? '' : preg_replace('/\W/',"_", $connect)) . '_' - . md5($GLOBALS['spip_version_code'] . ' * ' . $skel . (isset($GLOBALS['marqueur_skel'])?'*'.$GLOBALS['marqueur_skel']:'')); + . (!$connect ? '' : preg_replace('/\W/', "_", $connect)) . '_' + . md5($GLOBALS['spip_version_code'] . ' * ' . $skel . (isset($GLOBALS['marqueur_skel']) ? '*' . $GLOBALS['marqueur_skel'] : '')); } - -?>