/** * Permet de faire un comptage par table liee * * @syntaxe `{compteur table[, champ]}` * @link http://www.spip-contrib.net/Classer-les-articles-par-nombre-de#forum409210 * * @example * Pour avoir les auteurs classes par articles et * le nombre d'article de chacun : * * ``` * <BOUCLE1(AUTEURS){compteur articles}{par compteur_articles}> * #ID_AUTEUR : #COMPTEUR{articles} * </BOUCLE1> * ``` * * @note * Avec un seul argument {compteur autre_table} le groupby est fait * implicitement sur la cle primaire de la boucle en cours. * Avec un second argument {compteur autre_table,champ_fusion} * le groupby est fait sur le champ_fusion" * * @param string $idb * Identifiant de la boucle * @param Boucle[] $boucles * AST du squelette * @param Critere $crit * Paramètres du critère dans cette boucle * @param bool $left * true pour utiliser un left join plutôt qu'un inner join. * @return void */ function critere_compteur($idb, &$boucles, $crit, $left = false) { $boucle =& $boucles[$idb]; if (isset($crit->param[1])) { $_fusion = calculer_liste($crit->param[1], array(), $boucles, $boucle->id_parent); } else { $_fusion = "''"; } $params = $crit->param; $table = reset($params); $table = $table[0]->texte; $op = false; if (preg_match(',^(\\w+)([<>=])([0-9]+)$,', $table, $r)) { $table = $r[1]; if (count($r) >= 3) { $op = $r[2]; } if (count($r) >= 4) { $op_val = $r[3]; } } $type = objet_type($table); $type_id = id_table_objet($type); /** * Si la clé primaire est une clé multiple, on prend la première partie * Utile pour compter les versions de spip_versions par exemple */ if (count($types = explode(',', $type_id)) > 1) { $type_id = $types[0]; } $table_sql = table_objet_sql($type); $trouver_table = charger_fonction('trouver_table', 'base'); $arrivee = array($table, $trouver_table($table, $boucle->sql_serveur)); $depart = array($boucle->id_table, $trouver_table($boucle->id_table, $boucle->sql_serveur)); // noter les jointures deja installees $joins = array_keys($boucle->from); if ($compt = calculer_jointure($boucle, $depart, $arrivee)) { if ($_fusion != "''") { // en cas de jointure, on ne veut pas du group_by sur la cle primaire ! // cela casse le compteur ! foreach ($boucle->group as $k => $group) { if ($group == $boucle->id_table . '.' . $boucle->primary) { unset($boucle->group[$k]); } } $boucle->group[] = '".($gb=' . $_fusion . ')."'; } $boucle->select[] = "COUNT({$compt}.{$type_id}) AS compteur_{$table}"; if ($op) { $boucle->having[] = array("'" . $op . "'", "'compteur_" . $table . "'", $op_val); } if ($left) { foreach ($boucle->from as $k => $val) { if (!in_array($k, $joins)) { $boucle->from_type[$k] = 'left'; } } } } }
/** * Retourne le code PHP d'un argument de balise s'il est présent * * @uses calculer_liste() * @example * ``` * // Retourne le premier argument de la balise * // #BALISE{premier,deuxieme} * $arg = interprete_argument_balise(1,$p); * ``` * * @param int $n * Numéro de l'argument * @param Champ $p * Pile au niveau de la balise * @return string|null * Code PHP si cet argument est présent, sinon null **/ function interprete_argument_balise($n, $p) { if ($p->param && !$p->param[0][0] && count($p->param[0]) > $n) { return calculer_liste($p->param[0][$n], $p->descr, $p->boucles, $p->id_boucle); } else { return null; } }
/** * Fonction privee pour mutualiser de code des criteres_MESSAGES_rv_* * Retourne le code php pour obtenir la date de reference de comparaison * des evenements a trouver * * @param string $idb * @param object $boucles * @param object $crit * * @return string code PHP concernant la date. **/ function organiseur_calculer_date_reference($idb, &$boucles, $crit) { if (isset($crit->param[0])) { return calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent); } else { return "date('Y-m-d H:i:00')"; } }
function interprete_argument_balise($n,$p) { if (($p->param) && (!$p->param[0][0]) && (count($p->param[0])>$n)) return calculer_liste($p->param[0][$n], $p->descr, $p->boucles, $p->id_boucle); else return NULL; }
/** * Compter les articles publies lies a un auteur, dans une boucle auteurs * pour la vue prive/liste/auteurs.html * * @param <type> $idb * @param <type> $boucles * @param <type> $crit * @param <type> $left */ function critere_compteur_articles_filtres_dist($idb, &$boucles, $crit, $left = false) { $boucle =& $boucles[$idb]; $_statut = calculer_liste($crit->param[0], array(), $boucles, $boucle->id_parent); $not = ''; if ($crit->not) { $not = ", 'NOT'"; } $boucle->from['LAA'] = 'spip_auteurs_liens'; $boucle->from_type['LAA'] = 'left'; $boucle->join['LAA'] = array("'auteurs'", "'id_auteur'", "'id_auteur'", "'LAA.objet=\\'article\\''"); $boucle->from['articles'] = 'spip_articles'; $boucle->from_type['articles'] = 'left'; $boucle->join['articles'] = array("'LAA'", "'id_article'", "'id_objet'", "'(articles.statut IS NULL OR '.sql_in('articles.statut',_q({$_statut}){$not}).')'"); $boucle->select[] = 'COUNT(articles.id_article) AS compteur_articles'; $boucle->group[] = 'auteurs.id_auteur'; }
/** * {agendafull ..} variante etendue du crietre agenda du core * qui accepte une date de debut et une date de fin * * {agendafull date_debut, date_fin, jour, #ENV{annee}, #ENV{mois}, #ENV{jour}} * {agendafull date_debut, date_fin, semaine, #ENV{annee}, #ENV{mois}, #ENV{jour}} * {agendafull date_debut, date_fin, mois, #ENV{annee}, #ENV{mois}} * {agendafull date_debut, date_fin, periode, #ENV{annee}, #ENV{mois}, #ENV{jour}, * #ENV{annee_fin}, #ENV{mois_fin}, #ENV{jour_fin}} * * @param string $idb * @param object $boucles * @param object $crit */ function critere_agendafull_dist($idb, &$boucles, $crit) { $params = $crit->param; if (count($params) < 1) { erreur_squelette(_T('zbug_info_erreur_squelette'), "{agenda ?} BOUCLE{$idb}"); } $parent = $boucles[$idb]->id_parent; // les valeurs $date et $type doivent etre connus a la compilation // autrement dit ne pas etre des champs $date_deb = array_shift($params); $date_deb = $date_deb[0]->texte; $date_fin = array_shift($params); $date_fin = $date_fin[0]->texte; $type = array_shift($params); $type = $type[0]->texte; $annee = $params ? array_shift($params) : ""; $annee = "\n" . 'sprintf("%04d", ($x = ' . calculer_liste($annee, array(), $boucles, $parent) . ') ? $x : date("Y"))'; $mois = $params ? array_shift($params) : ""; $mois = "\n" . 'sprintf("%02d", ($x = ' . calculer_liste($mois, array(), $boucles, $parent) . ') ? $x : date("m"))'; $jour = $params ? array_shift($params) : ""; $jour = "\n" . 'sprintf("%02d", ($x = ' . calculer_liste($jour, array(), $boucles, $parent) . ') ? $x : date("d"))'; $annee2 = $params ? array_shift($params) : ""; $annee2 = "\n" . 'sprintf("%04d", ($x = ' . calculer_liste($annee2, array(), $boucles, $parent) . ') ? $x : date("Y"))'; $mois2 = $params ? array_shift($params) : ""; $mois2 = "\n" . 'sprintf("%02d", ($x = ' . calculer_liste($mois2, array(), $boucles, $parent) . ') ? $x : date("m"))'; $jour2 = $params ? array_shift($params) : ""; $jour2 = "\n" . 'sprintf("%02d", ($x = ' . calculer_liste($jour2, array(), $boucles, $parent) . ') ? $x : date("d"))'; $boucle =& $boucles[$idb]; $date = $boucle->id_table . ".{$date}"; $quote_end = ",'" . $boucle->sql_serveur . "','text'"; if ($type == 'jour') { $boucle->where[] = array("'AND'", array("'<='", "'DATE_FORMAT({$date_deb}, \\'%Y%m%d\\')'", "sql_quote({$annee} . {$mois} . {$jour}{$quote_end})"), array("'>='", "'DATE_FORMAT({$date_fin}, \\'%Y%m%d\\')'", "sql_quote({$annee} . {$mois} . {$jour}{$quote_end})")); } elseif ($type == 'mois') { $boucle->where[] = array("'AND'", array("'<='", "'DATE_FORMAT({$date_deb}, \\'%Y%m\\')'", "sql_quote({$annee} . {$mois}{$quote_end})"), array("'>='", "'DATE_FORMAT({$date_fin}, \\'%Y%m\\')'", "sql_quote({$annee} . {$mois}{$quote_end})")); } elseif ($type == 'semaine') { $boucle->where[] = array("'AND'", array("'>='", "'DATE_FORMAT({$date_fin}, \\'%Y%m%d\\')'", "date_debut_semaine({$annee}, {$mois}, {$jour})"), array("'<='", "'DATE_FORMAT({$date_deb}, \\'%Y%m%d\\')'", "date_fin_semaine({$annee}, {$mois}, {$jour})")); } elseif (count($crit->param) > 3) { $boucle->where[] = array("'AND'", array("'>='", "'DATE_FORMAT({$date_fin}, \\'%Y%m%d\\')'", "sql_quote({$annee} . {$mois} . {$jour}{$quote_end})"), array("'<='", "'DATE_FORMAT({$date_deb}, \\'%Y%m%d\\')'", "sql_quote({$annee2} . {$mois2} . {$jour2}{$quote_end})")); } // sinon on prend tout }
/** * Compile le critère {tableau} d'une boucle POUR * * {tableau #XX} pour compatibilite ascendante boucle POUR * ... préférer la notation (DATA){source tableau,#XX} * * @deprecated Utiliser une boucle (DATA){source tableau,#XX} * * @param string $idb Identifiant de la boucle * @param array $boucles AST du squelette * @param Critere $crit Paramètres du critère dans cette boucle */ function critere_POUR_tableau_dist($idb, &$boucles, $crit) { $boucle =& $boucles[$idb]; $boucle->hash .= ' $command[\'source\'] = array(' . calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent) . '); $command[\'sourcemode\'] = \'table\';'; }
/** * Critère de compatibilité avec une version précise ou une branche de SPIP. * * Fonctionne sur les tables spip_paquets et spip_plugins * * Si aucune valeur n'est explicité dans le critère, tous les enregistrements * sont retournés. * * Le ! (NOT) fonctionne sur le critère BRANCHE * * @critere compatible_spip * @example * {compatible_spip} * {compatible_spip 2.0.8} ou {compatible_spip 1.9} * {compatible_spip #ENV{vers}} ou {compatible_spip #ENV{vers, 1.9.2}} * {compatible_spip #GET{vers}} ou {compatible_spip #GET{vers, 2.1}} * * @param string $idb Identifiant de la boucle * @param array $boucles AST du squelette * @param Critere $crit Paramètres du critère dans cette boucle * @return void */ function critere_compatible_spip_dist($idb, &$boucles, $crit) { $boucle =& $boucles[$idb]; $table = $boucle->id_table; // Si on utilise ! la fonction LOCATE doit retourner 0. // -> utilise uniquement avec le critere BRANCHE $op = $crit->not == '!' ? '=' : '>'; $boucle->hash .= ' // COMPATIBILITE SPIP $creer_where = charger_fonction(\'where_compatible_spip\', \'inc\');'; // version/branche explicite dans l'appel du critere if (isset($crit->param[0][0])) { $version = calculer_liste(array($crit->param[0][0]), array(), $boucles, $boucle->id_parent); $boucle->hash .= ' $where = $creer_where(' . $version . ', \'' . $table . '\', \'' . $op . '\'); '; } else { $boucle->hash .= ' $version = isset($Pile[0][\'compatible_spip\']) ? $Pile[0][\'compatible_spip\'] : \'\'; $where = $creer_where($version, \'' . $table . '\', \'' . $op . '\'); '; } $boucle->where[] = '$where'; }
function balise_LOGO__dist($p) { preg_match(",^LOGO_([A-Z_]+?)(|_NORMAL|_SURVOL|_RUBRIQUE)\$,i", $p->nom_champ, $regs); $type = strtolower($regs[1]); $suite_logo = $regs[2]; // cas de #LOGO_SITE_SPIP if ($type == 'site_spip') { $type = 'site'; $_id_objet = "\"'0'\""; } $id_objet = id_table_objet($type); if (!isset($_id_objet) or !$_id_objet) { $_id_objet = champ_sql($id_objet, $p); } $fichier = $p->etoile === '**' ? -1 : 0; $coord = array(); $align = $lien = ''; $mode_logo = ''; if ($p->param and !$p->param[0][0]) { $params = $p->param[0]; array_shift($params); foreach ($params as $a) { if ($a[0]->type === 'texte') { $n = $a[0]->texte; if (is_numeric($n)) { $coord[] = $n; } elseif (in_array($n, array('top', 'left', 'right', 'center', 'bottom'))) { $align = $n; } elseif (in_array($n, array('auto', 'icone', 'apercu', 'vignette'))) { $mode_logo = $n; } } else { $lien = calculer_liste($a, $p->descr, $p->boucles, $p->id_boucle); } } } $coord_x = !$coord ? 0 : intval(array_shift($coord)); $coord_y = !$coord ? 0 : intval(array_shift($coord)); if ($p->etoile === '*') { include_spip('balise/url_'); $lien = generer_generer_url_arg($type, $p, $_id_objet); } $connect = $p->id_boucle ? $p->boucles[$p->id_boucle]->sql_serveur : ''; if ($type == 'document') { $qconnect = _q($connect); $doc = "quete_document({$_id_objet}, {$qconnect})"; if ($fichier) { $code = "quete_logo_file({$doc}, {$qconnect})"; } else { $code = "quete_logo_document({$doc}, " . ($lien ? $lien : "''") . ", '{$align}', '{$mode_logo}', {$coord_x}, {$coord_y}, {$qconnect})"; } // (x=non-faux ? y : '') pour affecter x en retournant y if ($p->descr['documents']) { $code = '(($doublons["documents"] .= ",". ' . $_id_objet . ") ? {$code} : '')"; } } elseif ($connect) { $code = "''"; spip_log("Les logos distants ne sont pas prevus"); } else { $code = logo_survol($id_objet, $_id_objet, $type, $align, $fichier, $lien, $p, $suite_logo); } // demande de reduction sur logo avec ecriture spip 2.1 : #LOGO_xxx{200, 0} if ($coord_x or $coord_y) { $code = "filtrer('image_graver',filtrer('image_reduire'," . $code . ", '{$coord_x}', '{$coord_y}'))"; } $p->code = $code; $p->interdire_scripts = false; return $p; }
function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect = '') { static $trouver_table; spip_timer('calcul_skel'); if (defined('_VAR_MODE') and _VAR_MODE == 'debug') { $GLOBALS['debug_objets']['squelette'][$nom] = $descr['squelette']; $GLOBALS['debug_objets']['sourcefile'][$nom] = $sourcefile; if (!isset($GLOBALS['debug_objets']['principal'])) { $GLOBALS['debug_objets']['principal'] = $nom; } } foreach ($boucles as $id => $boucle) { $GLOBALS['debug_objets']['boucle'][$nom . $id] = $boucle; } $descr['documents'] = compile_inclure_doublons($squelette); // Demander la description des tables une fois pour toutes // et reperer si les doublons sont demandes // pour un inclure ou une boucle document // c'est utile a la fonction champs_traitements if (!$trouver_table) { $trouver_table = charger_fonction('trouver_table', 'base'); } foreach ($boucles as $id => $boucle) { if (!($type = $boucle->type_requete)) { continue; } if (!$descr['documents'] and ($type == 'documents' and $boucle->doublons or compile_inclure_doublons($boucle->avant) or compile_inclure_doublons($boucle->apres) or compile_inclure_doublons($boucle->milieu) or compile_inclure_doublons($boucle->altern))) { $descr['documents'] = true; } if ($type != TYPE_RECURSIF) { if (!$boucles[$id]->sql_serveur and $connect) { $boucles[$id]->sql_serveur = $connect; } // chercher dans les iterateurs du repertoire iterateur/ if ($g = charger_fonction(preg_replace('/\\W/', '_', $boucle->type_requete), 'iterateur', true)) { $boucles[$id] = $g($boucle); // sinon, en cas de requeteur d'un type predefini, // utiliser les informations donnees par le requeteur // cas "php:xx" et "data:xx". } else { if ($boucle->sql_serveur and $requeteur = charger_fonction($boucle->sql_serveur, 'requeteur', true)) { $requeteur($boucles, $boucle, $id); // utiliser la description des champs transmis } else { $show = $trouver_table($type, $boucles[$id]->sql_serveur); // si la table n'existe pas avec le connecteur par defaut, // c'est peut etre une table qui necessite son connecteur dedie fourni // permet une ecriture allegee (GEO) -> (geo:GEO) if (!$show and $show = $trouver_table($type, strtolower($type))) { $boucles[$id]->sql_serveur = strtolower($type); } if ($show) { $boucles[$id]->show = $show; // recopie les infos les plus importantes $boucles[$id]->primary = isset($show['key']["PRIMARY KEY"]) ? $show['key']["PRIMARY KEY"] : ''; $boucles[$id]->id_table = $x = preg_replace(",^spip_,", "", $show['id_table']); $boucles[$id]->from[$x] = $nom_table = $show['table']; $boucles[$id]->iterateur = 'SQL'; $boucles[$id]->descr =& $descr; if (!$boucles[$id]->jointures and is_array($show['tables_jointures']) and count($x = $show['tables_jointures'])) { $boucles[$id]->jointures = $x; } if ($boucles[$id]->jointures_explicites) { $jointures = preg_split("/\\s+/", $boucles[$id]->jointures_explicites); while ($j = array_pop($jointures)) { array_unshift($boucles[$id]->jointures, $j); } } } else { // Pas une erreur si la table est optionnelle if ($boucles[$id]->table_optionnelle) { $boucles[$id]->type_requete = ''; } else { $boucles[$id]->type_requete = false; $boucle = $boucles[$id]; $x = (!$boucle->sql_serveur ? '' : $boucle->sql_serveur . ":") . $type; $msg = array('zbug_table_inconnue', array('table' => $x)); erreur_squelette($msg, $boucle); } } } } } } // Commencer par reperer les boucles appelees explicitement // car elles indexent les arguments de maniere derogatoire foreach ($boucles as $id => $boucle) { if ($boucle->type_requete == TYPE_RECURSIF and $boucle->param) { $boucles[$id]->descr =& $descr; $rec =& $boucles[$boucle->param[0]]; if (!$rec) { $msg = array('zbug_boucle_recursive_undef', array('nom' => $boucle->param[0])); erreur_squelette($msg, $boucle); $boucles[$id]->type_requete = false; } else { $rec->externe = $id; $descr['id_mere'] = $id; $boucles[$id]->return = calculer_liste(array($rec), $descr, $boucles, $boucle->param); } } } foreach ($boucles as $id => $boucle) { $id = strval($id); // attention au type dans index_pile $type = $boucle->type_requete; if ($type and $type != TYPE_RECURSIF) { $res = ''; if ($boucle->param) { // retourne un tableau en cas d'erreur $res = calculer_criteres($id, $boucles); } $descr['id_mere'] = $id; $boucles[$id]->return = calculer_liste($boucle->milieu, $descr, $boucles, $id); // Si les criteres se sont mal compiles // ne pas tenter d'assembler le code final // (mais compiler le corps pour detection d'erreurs) if (is_array($res)) { $boucles[$id]->type_requete = false; } } } // idem pour la racine $descr['id_mere'] = ''; $corps = calculer_liste($squelette, $descr, $boucles); // Calcul du corps de toutes les fonctions PHP, // en particulier les requetes SQL et TOTAL_BOUCLE // de'terminables seulement maintenant foreach ($boucles as $id => $boucle) { $boucle = $boucles[$id] = pipeline('pre_boucle', $boucle); if ($boucle->return === false) { $corps = false; continue; } // appeler la fonction de definition de la boucle if ($req = $boucle->type_requete) { // boucle personnalisée ? $table = strtoupper($boucle->type_requete); $serveur = strtolower($boucle->sql_serveur); if ((!$serveur or !function_exists($f = "boucle_" . $serveur . "_" . $table) and !function_exists($f = $f . "_dist")) and !function_exists($f = "boucle_" . $table) and !function_exists($f = $f . "_dist")) { // fonction de boucle standard if (!function_exists($f = 'boucle_DEFAUT')) { $f = 'boucle_DEFAUT_dist'; } } $req = "\n\n\tstatic \$command = array();\n\t" . "static \$connect;\n\t" . "\$command['connect'] = \$connect = " . _q($boucle->sql_serveur) . ";" . $f($id, $boucles); } else { $req = "\n\treturn '';"; } $boucles[$id]->return = "\n\nfunction BOUCLE" . strtr($id, "-", "_") . $nom . '(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' . $req . "\n}\n"; } // Au final, si le corps ou un critere au moins s'est mal compile // retourner False, sinon inserer leur decompilation if (is_bool($corps)) { return false; } $principal = "\nfunction " . $nom . '($Cache, $Pile, $doublons=array(), $Numrows=array(), $SP=0) { ' . ' if (isset($Pile[0]["doublons"]) AND is_array($Pile[0]["doublons"])) $doublons = nettoyer_env_doublons($Pile[0]["doublons"]); $connect = ' . _q($connect) . '; $page = ' . $corps . ";\n\n\treturn analyse_resultat_skel(" . var_export($nom, true) . ", \$Cache, \$page, " . var_export($sourcefile, true) . ");\n}"; $secondes = spip_timer('calcul_skel'); spip_log("COMPIL ({$secondes}) [{$sourcefile}] {$nom}.php"); // $connect n'est pas sûr : on nettoie $connect = preg_replace(',[^\\w],', '', $connect); // Assimiler la fct principale a une boucle anonyme, pour retourner un resultat simple $code = new Boucle(); $code->descr = $descr; $code->return = ' // // Fonction principale du squelette ' . $sourcefile . ($connect ? " pour {$connect}" : '') . (!CODE_COMMENTE ? '' : "\n// Temps de compilation total: {$secondes}") . "\n//\n" . $principal; $boucles[''] = $code; return $boucles; }
function calculer_critere_infixe_ops($idb, &$boucles, $crit) { // cas d'une valeur comparee a elle-meme ou son referent if (count($crit->param) == 0) { $op = '='; $col = $val = $crit->op; if (preg_match('/^(.*)\.(.*)$/', $col, $r)) $val = $r[2]; // Cas special {lang} : aller chercher $GLOBALS['spip_lang'] if ($val == 'lang') $val = array(kwote('$GLOBALS[\'spip_lang\']')); else { // Si id_parent, comparer l'id_parent avec l'id_objet // de la boucle superieure.... faudrait verifier qu'il existe // pour eviter l'erreur SQL if ($val == 'id_parent') $val = $boucles[$idb]->primary; // Si id_enfant, comparer l'id_objet avec l'id_parent // de la boucle superieure else if ($val == 'id_enfant') $val = 'id_parent'; // un critere conditionnel sur date est traite a part // car la date est mise d'office par SPIP, $val = calculer_argument_precedent($idb, $val, $boucles); if ($crit->cond AND ($col == "date" OR $col == "date_redac")) { if($val == "\$Pile[0]['".$col."']") { $val = "(\$Pile[0]['{$col}_default']?'':$val)"; } } $val = array(kwote($val)); } } else { // comparaison explicite // le phraseur impose que le premier param soit du texte $params = $crit->param; $op = $crit->op; if ($op == '==') $op = 'REGEXP'; $col = array_shift($params); $col = $col[0]->texte; $val = array(); $desc = array('id_mere' => $idb); $parent = $boucles[$idb]->id_parent; // Dans le cas {x=='#DATE'} etc, defaire le travail du phraseur, // celui ne sachant pas ce qu'est un critere infixe // et a fortiori son 2e operande qu'entoure " ou ' if (count($params)==1 AND count($params[0]==3) AND $params[0][0]->type == 'texte' AND @$params[0][2]->type == 'texte' AND ($p=$params[0][0]->texte) == $params[0][2]->texte AND (($p == "'") OR ($p == '"')) AND $params[0][1]->type == 'champ' ) { $val[]= "$p\\$p#" . $params[0][1]->nom_champ . "\\$p$p"; } else foreach ((($op != 'IN') ? $params : calculer_vieux_in($params)) as $p) { $a = calculer_liste($p, $desc, $boucles, $parent); if ($op == 'IN') $val[]= $a; else $val[]=kwote($a); } } $fct = $args_sql = ''; // fonction SQL ? if (preg_match('/^(.*)' . SQL_ARGS . '$/', $col, $m)) { $fct = $m[1]; preg_match('/^\(([^,]*)(.*)\)$/', $m[2], $a); $col = $a[1]; if (preg_match('/^(\S*)(\s+AS\s+.*)$/i', $col, $m)) { $col=$m[1]; $args_sql = $m[2]; } $args_sql .= $a[2];; } return array($fct, $col, $op, $val, $args_sql); }
function compose_filtres_args($p, $args, $sep) { $arglist = ""; foreach ($args as $arg) { $arglist .= $sep . calculer_liste($arg, $p->descr, $p->boucles, $p->id_boucle); } return $arglist; }
function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $connect = ''){ global $tables_jointures; static $trouver_table; spip_timer('calcul_skel'); if (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode']=='debug'){ $GLOBALS['debug_objets']['squelette'][$nom] = $descr['squelette']; $GLOBALS['debug_objets']['sourcefile'][$nom] = $sourcefile; if (!isset($GLOBALS['debug_objets']['principal'])) $GLOBALS['debug_objets']['principal'] = $nom; } foreach ($boucles as $id => $boucle){ $GLOBALS['debug_objets']['boucle'][$nom . $id] = $boucle; } $descr['documents'] = compile_inclure_doublons($squelette); // Demander la description des tables une fois pour toutes // et reperer si les doublons sont demandes // pour un inclure ou une boucle document // c'est utile a la fonction champs_traitements if (!$trouver_table) $trouver_table = charger_fonction('trouver_table', 'base'); foreach ($boucles as $id => $boucle){ if (!($type = $boucle->type_requete)) continue; if (!$descr['documents'] AND ( (($type=='documents') AND $boucle->doublons) OR compile_inclure_doublons($boucle->avant) OR compile_inclure_doublons($boucle->apres) OR compile_inclure_doublons($boucle->milieu) OR compile_inclure_doublons($boucle->altern)) ) $descr['documents'] = true; if ($type!='boucle'){ if (!$boucles[$id]->sql_serveur AND $connect) $boucles[$id]->sql_serveur = $connect; $show = $trouver_table($type, $boucles[$id]->sql_serveur); // si la table n'existe pas avec le connecteur par defaut, // c'est peut etre une table qui necessite son connecteur dedie fourni // permet une ecriture allegee (GEO) -> (geo:GEO) if (!$show AND $show = $trouver_table($type, strtolower($type))) $boucles[$id]->sql_serveur = strtolower($type); if ($show){ $boucles[$id]->show = $show; // recopie les infos les plus importantes $boucles[$id]->primary = $show['key']["PRIMARY KEY"]; $boucles[$id]->id_table = $x = $show['id_table']; $boucles[$id]->from[$x] = $nom_table = $show['table']; $boucles[$id]->descr = &$descr; if ((!$boucles[$id]->jointures) AND (isset($tables_jointures[$nom_table])) AND is_array($x = $tables_jointures[$nom_table]) ) $boucles[$id]->jointures = $x; if ($boucles[$id]->jointures_explicites){ $jointures = preg_split("/\s+/", $boucles[$id]->jointures_explicites); while ($j = array_pop($jointures)) array_unshift($boucles[$id]->jointures, $j); } } else { // Pas une erreur si la table est optionnelle if ($boucles[$id]->table_optionnelle) $boucles[$id]->type_requete = ''; else { $boucles[$id]->type_requete = false; $boucle = $boucles[$id]; $x = (!$boucle->sql_serveur ? '' : ($boucle->sql_serveur . ":")) . $type; $msg = array('zbug_table_inconnue', array('table' => $x)); erreur_squelette($msg, $boucle); } } } } // Commencer par reperer les boucles appelees explicitement // car elles indexent les arguments de maniere derogatoire foreach ($boucles as $id => $boucle){ if ($boucle->type_requete=='boucle' AND $boucle->param){ $boucles[$id]->descr = &$descr; $rec = &$boucles[$boucle->param[0]]; if (!$rec){ $msg = array('zbug_boucle_recursive_undef', array('nom' => $boucle->param[0])); erreur_squelette($msg, $boucle); $boucles[$id]->type_requete = false; } else { $rec->externe = $id; $descr['id_mere'] = $id; $boucles[$id]->return = calculer_liste(array($rec), $descr, $boucles, $boucle->param); } } } foreach ($boucles as $id => $boucle){ $id = strval($id); // attention au type dans index_pile $type = $boucle->type_requete; if ($type AND $type!='boucle'){ $crit = !$boucle->param ? true : calculer_criteres($id, $boucles); $descr['id_mere'] = $id; $boucles[$id]->return = calculer_liste($boucle->milieu, $descr, $boucles, $id); // Si les criteres se sont mal compiles // ne pas tenter d'assembler le code final // (mais compiler le corps pour detection d'erreurs) if (is_array($crit)) $boucles[$id]->type_requete = false; } } // idem pour la racine $descr['id_mere'] = ''; $corps = calculer_liste($squelette, $descr, $boucles); $debug = (isset($GLOBALS['var_mode']) AND $GLOBALS['var_mode']=='debug'); if ($debug){ include_spip('public/decompiler'); include_spip('public/format_' . _EXTENSION_SQUELETTES); } // Calcul du corps de toutes les fonctions PHP, // en particulier les requetes SQL et TOTAL_BOUCLE // de'terminables seulement maintenant foreach ($boucles as $id => $boucle){ $boucle = $boucles[$id] = pipeline('pre_boucle', $boucle); if ($boucle->return===false) continue; // appeler la fonction de definition de la boucle if ($req = $boucle->type_requete){ $f = 'boucle_' . strtoupper($req); // si pas de definition perso, definition spip if (!function_exists($f)) $f = $f . '_dist'; // laquelle a une definition par defaut if (!function_exists($f)) $f = 'boucle_DEFAUT'; if (!function_exists($f)) $f = 'boucle_DEFAUT_dist'; $req = "\n\n\tstatic \$connect = " . _q($boucle->sql_serveur) . ";" . $f($id, $boucles); } else $req = ("\n\treturn '';"); $boucles[$id]->return = "function BOUCLE" . strtr($id, "-", "_") . $nom . '(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' . $req . "\n}\n\n"; if ($debug) $GLOBALS['debug_objets']['code'][$nom . $id] = $boucles[$id]->return; } // Au final, si le corps ou un critere au moins s'est mal compile // retourner False, sinon inserer leur decompilation if (is_bool($corps)) return false; foreach ($boucles as $id => $boucle){ if ($boucle->return===false) return false; $boucle->return = "\n\n/* BOUCLE " . $boucle->type_requete . " " . (!$debug ? '' : str_replace('*/', '* /', decompiler_criteres($boucle->param, $boucle->criteres))) . " */\n\n " . $boucle->return; } $secondes = spip_timer('calcul_skel'); spip_log("COMPIL ($secondes) [$sourcefile] $nom.php"); // $connect n'est pas sûr : on nettoie $connect = preg_replace(',[^\w],', '', $connect); // Assimiler la fct principale a une boucle anonyme, c'est plus simple $code = new Boucle; $code->descr = $descr; $code->return = ' // // Fonction principale du squelette ' . $sourcefile . ($connect ? " pour $connect" : '') . (!CODE_COMMENTE ? '' : "\n// Temps de compilation total: $secondes") . "\n//" . (!$debug ? '' : ("\n/*\n" . str_replace('*/', '* /', public_decompiler($squelette)) . "\n*/")) . " function " . $nom . '($Cache, $Pile, $doublons=array(), $Numrows=array(), $SP=0) { ' // reporter de maniere securisee les doublons inclus . ' if (isset($Pile[0]["doublons"]) AND is_array($Pile[0]["doublons"])) $doublons = nettoyer_env_doublons($Pile[0]["doublons"]); $connect = ' . _q($connect) . '; $page = ' . // ATTENTION, le calcul de l'expression $corps affectera $Cache // c'est pourquoi on l'affecte a la variable auxiliaire $page. // avant de referencer $Cache $corps . "; return analyse_resultat_skel(" . var_export($nom, true) . ", \$Cache, \$page, " . var_export($sourcefile, true) . "); }"; $boucles[''] = $code; return $boucles; }
/** * Plugin Agenda pour Spip 2.0 * Licence GPL * * */ function critere_agendafull_dist($idb, &$boucles, $crit) { $params = $crit->param; if (count($params) < 1) erreur_squelette(_T('zbug_info_erreur_squelette'), "{agenda ?} BOUCLE$idb"); $parent = $boucles[$idb]->id_parent; // les valeurs $date et $type doivent etre connus a la compilation // autrement dit ne pas etre des champs $date_deb = array_shift($params); $date_deb = $date_deb[0]->texte; $date_fin = array_shift($params); $date_fin = $date_fin[0]->texte; $type = array_shift($params); $type = $type[0]->texte; $annee = $params ? array_shift($params) : ""; $annee = "\n" . 'sprintf("%04d", ($x = ' . calculer_liste($annee, array(), $boucles, $parent) . ') ? $x : date("Y"))'; $mois = $params ? array_shift($params) : ""; $mois = "\n" . 'sprintf("%02d", ($x = ' . calculer_liste($mois, array(), $boucles, $parent) . ') ? $x : date("m"))'; $jour = $params ? array_shift($params) : ""; $jour = "\n" . 'sprintf("%02d", ($x = ' . calculer_liste($jour, array(), $boucles, $parent) . ') ? $x : date("d"))'; $annee2 = $params ? array_shift($params) : ""; $annee2 = "\n" . 'sprintf("%04d", ($x = ' . calculer_liste($annee2, array(), $boucles, $parent) . ') ? $x : date("Y"))'; $mois2 = $params ? array_shift($params) : ""; $mois2 = "\n" . 'sprintf("%02d", ($x = ' . calculer_liste($mois2, array(), $boucles, $parent) . ') ? $x : date("m"))'; $jour2 = $params ? array_shift($params) : ""; $jour2 = "\n" . 'sprintf("%02d", ($x = ' . calculer_liste($jour2, array(), $boucles, $parent) . ') ? $x : date("d"))'; $boucle = &$boucles[$idb]; $date = $boucle->id_table . ".$date"; if ($type == 'jour') $boucle->where[]= array("'AND'", array("'<='", "'DATE_FORMAT($date_deb, \'%Y%m%d\')'",("$annee . $mois . $jour")), array("'>='", "'DATE_FORMAT($date_fin, \'%Y%m%d\')'",("$annee . $mois . $jour"))); elseif ($type == 'mois') $boucle->where[]= array("'AND'", array("'<='", "'DATE_FORMAT($date_deb, \'%Y%m\')'",("$annee . $mois")), array("'>='", "'DATE_FORMAT($date_fin, \'%Y%m\')'",("$annee . $mois"))); elseif ($type == 'semaine') $boucle->where[]= array("'AND'", array("'>='", "'DATE_FORMAT($date_fin, \'%Y%m%d\')'", ("date_debut_semaine($annee, $mois, $jour)")), array("'<='", "'DATE_FORMAT($date_deb, \'%Y%m%d\')'", ("date_fin_semaine($annee, $mois, $jour)"))); elseif (count($crit->param) > 3) $boucle->where[]= array("'AND'", array("'>='", "'DATE_FORMAT($date_fin, \'%Y%m%d\')'", ("$annee . $mois . $jour")), array("'<='", "'DATE_FORMAT($date_deb, \'%Y%m%d\')'", ("$annee2 . $mois2 . $jour2"))); // sinon on prend tout }
/** * {tri [champ_par_defaut][,sens_par_defaut][,nom_variable]} * champ_par_defaut : un champ de la table sql * sens_par_defaut : -1 ou inverse pour decroissant, 1 ou direct pour croissant * nom_variable : nom de la variable utilisee (par defaut tri_nomboucle) * * {tri titre} * {tri titre,inverse} * {tri titre,-1} * {tri titre,-1,truc} * * @param unknown_type $idb * @param unknown_type $boucles * @param unknown_type $crit */ function critere_tri_dist($idb, &$boucles, $crit) { $boucle = &$boucles[$idb]; $id_table = $boucle->id_table; // definition du champ par defaut $_champ_defaut = !isset($crit->param[0][0]) ? "''" : calculer_liste(array($crit->param[0][0]), array(), $boucles, $boucle->id_parent); $_sens_defaut = !isset($crit->param[1][0]) ? "1" : calculer_liste(array($crit->param[1][0]), array(), $boucles, $boucle->id_parent); $_variable = !isset($crit->param[2][0]) ? "'$idb'" : calculer_liste(array($crit->param[2][0]), array(), $boucles, $boucle->id_parent); $_tri = "((\$t=(isset(\$Pile[0]['tri'.$_variable]))?\$Pile[0]['tri'.$_variable]:$_champ_defaut)?tri_protege_champ(\$t):'')"; $_sens_defaut = "(is_array(\$s=$_sens_defaut)?(isset(\$s[\$st=$_tri])?\$s[\$st]:reset(\$s)):\$s)"; $_sens ="((intval(\$t=(isset(\$Pile[0]['sens'.$_variable]))?\$Pile[0]['sens'.$_variable]:$_sens_defaut)==-1 OR \$t=='inverse')?-1:1)"; $boucle->modificateur['tri_champ'] = $_tri; $boucle->modificateur['tri_sens'] = $_sens; $boucle->modificateur['tri_nom'] = $_variable; // faut il inserer un test sur l'existence de $tri parmi les champs de la table ? // evite des erreurs sql, mais peut empecher des tri sur jointure ... $boucle->hash .= " \$senstri = ''; \$tri = $_tri; if (\$tri){ \$senstri = $_sens; \$senstri = (\$senstri<0)?' DESC':''; }; "; $field = serialize(array_keys($boucle->show['field'])); $boucle->select[] = "\".tri_champ_select(\$tri).\""; $boucle->order[] = "tri_champ_order(\$tri,'$id_table','$field').\$senstri"; }
/** * Fonction privee pour mutualiser de code des criteres_evenement_* * Retourne le code php pour obtenir la date de reference de comparaison * des evenements a trouver * * @param <type> $idb * @param <type> $boucles * @param <type> $crit * * @return string code PHP concernant la date. **/ function agenda_calculer_date_reference($idb, &$boucles, $crit) { if (isset($crit->param[0])) return calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent); else return "date('Y-m-d H:i:00')"; }
function critere_frequence_dist($idb, &$boucles, $crit) { global $table_des_tables; $not = $crit->not; $boucle = &$boucles[$idb]; if ($not) erreur_squelette(_T('zbug_info_erreur_squelette'), $crit->op); if (empty($boucle->jointures)) { erreur_squelette(_T('zbug_info_erreur_squelette'), _L('frequence sur table sans jointures')); return; } //analyser chaque criteres de frequence $nom = $table_des_tables[$boucle->type_requete]; $parent = $boucles[$idb]->id_parent; $criteres = array(); //Pour l'instant, un seul parametre while(list(,$p) = each($crit->param)) { $param = calculer_liste($p, array(), $boucles, $parent); $type = preg_match(',^\(?\'(\w+)(\s*)?([!=<>]+)?(\s*)?,', $param, $regs) ? $regs[1] : $boucle->jointures[0]; $op = $regs[3] ? $regs[3] : '>='; if($val = $regs[0] ? preg_replace(',' . preg_quote($regs[0]) . ',', '', $param) : 0) { $val = preg_replace(',\'$,', '', $val); $val = preg_replace(',^\'\s\.\s(.*)\)$,Um', '$1', $val); $val = $val ? $val : 0; } //Trouver une jointure n:n (cad table spip_mots_articles) if(in_array($_type = $nom.'_'.$type, $boucle->jointures)) $criteres[] = array($op, $_type, $val); else { erreur_squelette(_T('zbug_info_erreur_squelette'), _L('frequence '.$type.': jointure inconnue')); } } if(empty($criteres)) $criteres[0] = array('>=', $boucle->jointures[0], 0); //composer la requete pour la jointure $primary = $boucle->primary; $id_table = $boucle->id_table . '.' . $primary; foreach($criteres as $critere) { $frequence = $boucle->modificateur['frequence'] = "frequence".$idb; list($op, $type, $val) = $critere; //compatibilite SVN et 1.9.2 if(function_exists('trouver_def_table')) { $nom = $table_des_tables[$type]; list($table, $desc) = trouver_def_table($nom ? $nom : $type, $boucle); } else { $trouver_table = charger_fonction('trouver_table','base'); $desc=$trouver_table($type, $boucle->sql_serveur); $table = $desc['table']; } /*Ajouter ici un test et produire une erreur si table non trouvee*/ $ids = $desc['key']['PRIMARY KEY']; foreach(split(',', $ids) as $_id) if(trim($_id) != $primary) $id = $_id; $boucle->select[]= 'COUNT('.$frequence.'.'.$id.') AS '.$frequence; $boucle->from[$frequence] = $table; $boucle->where[] = array("'='", "'$id_table'", "'$frequence.$primary'"); $boucle->group[] = $id_table; $boucle->having[] = array("'$op'", "'$frequence'", $val); } }