/** * selectionner les donnees, ie faire la requete SQL * * @return void */ protected function select() { $this->row = null; $v =& $this->command; $this->sqlresult = calculer_select($v['select'], $v['from'], $v['type'], $v['where'], $v['join'], $v['groupby'], $v['orderby'], $v['limit'], $v['having'], $v['table'], $v['id'], $v['connect'], $this->info); $this->err = !$this->sqlresult; $this->firstseek = false; $this->pos = -1; // pas d'init a priori, le calcul ne sera fait qu'en cas de besoin (provoque une double requete souvent inutile en sqlite) //$this->total = $this->count(); }
/** * Determininer si un objet est publie ou non * * On se base pour cela sur sa declaration de statut * pour des cas particuliers non declarables, on permet de fournir une fonction * base_xxxx_test_si_publie qui sera appele par la fonction * * @param string $objet * @param int $id_objet * @param string $serveur * @return bool */ function objet_test_si_publie($objet, $id_objet, $serveur = '') { // voir si une fonction est definie pour faire le boulot // elle a la priorite dans ce cas if ($f = charger_fonction($objet . "_test_si_publie", "base", true)) { return $f($objet, $id_objet, $serveur); } // sinon on se fie a la declaration de l'objet si presente $id_table = $table_objet = table_objet($objet); $id_table_objet = id_table_objet($objet, $serveur); $trouver_table = charger_fonction('trouver_table', 'base'); if ($desc = $trouver_table($table_objet, $serveur) and isset($desc['statut']) and $desc['statut']) { $boucle = new Boucle(); $boucle->show = $desc; $boucle->nom = 'objet_test_si_publie'; $boucle->id_boucle = $id_table; $boucle->id_table = $id_table; $boucle->sql_serveur = $serveur; $boucle->select[] = $id_table_objet; $boucle->from[$table_objet] = table_objet_sql($objet, $serveur); $boucle->where[] = $id_table . "." . $id_table_objet . '=' . intval($id_objet); include_spip('public/compiler'); include_spip('public/composer'); instituer_boucle($boucle, false); $res = calculer_select($boucle->select, $boucle->from, $boucle->from_type, $boucle->where, $boucle->join, $boucle->group, $boucle->order, $boucle->limit, $boucle->having, $table_objet, $id_table, $serveur); if (sql_fetch($res)) { return true; } return false; } // si pas d'info statut ni de fonction : l'objet est publie return true; }
/** * La fonction presente dans les squelettes compiles * * http://doc.spip.org/@calculer_select * * @param array $select * @param array $from * @param array $from_type * @param array $where * @param array $join * @param array $groupby * @param array $orderby * @param string $limit * @param array $having * @param string $table * @param string $id * @param string $serveur * @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) { // 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') { unset($where[$k]); $menage = true; } } // evacuer les eventuels groupby vide issus d'un calcul dynamique $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) { $menage = true; // on recupere la sous requete $sous = match_self($w); 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 $jsub = $join; // trouver les jointures utiles a // reinjecter dans le where de la sous requete les conditions supplementaires des jointures qui y sont mentionnees // ie L1.objet='article' // 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) { $i = 1; $wherestring = calculer_where_to_string($where[$k]); 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); } 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], $sous[2], array(), $sous[3] ? is_array($sous[3]) ? $sous[3] : array($sous[3]) : array(), array(), $sous[4] ? $sous[4] : array(), $sous[5] ? $sous[5] : array(), $sous[6], $sous[7] ? $sous[7] : array(), $table, $id, $serveur, false) . ")"); } array_pop($where_simples); } foreach ($having as $k => $v) { if (!$v or $v == 1 or $v == '0=0') { unset($having[$k]); } } // Installer les jointures. // 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) { $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]; // 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)) { $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)) { // corriger les references non explicites dans select // ou groupby foreach ($select as $i => $s) { if ($s == $c) { $select[$i] = "{$cle}.{$c} AS {$c}"; break; } } foreach ($groupby as $i => $g) { if ($g == $c) { $groupby[$i] = "{$cle}.{$c}"; break; } } // 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", $from[$cle], "AS {$cle}", "ON (", "{$cle}.{$c}", "=", "{$t}.{$carr}", ($and ? "AND " . $and : "") . ")"); if (isset($afrom[$cle])) { $afrom[$t] = $afrom[$t] + $afrom[$cle]; unset($afrom[$cle]); } $equiv[] = $carr; } else { unset($join[$cledef]); } unset($from[$cle]); $k--; } if (count($afrom)) { // Regarder si la table principale ne sert finalement a rien comme dans //<BOUCLE3(MOTS){id_article}{id_mot}> class='on'</BOUCLE3> //<BOUCLE2(MOTS){id_article} />#TOTAL_BOUCLE<//B2> //<BOUCLE5(RUBRIQUES){id_mot}{tout} />#TOTAL_BOUCLE<//B5> // ou dans //<BOUCLE8(HIERARCHIE){id_rubrique}{tout}{type='Squelette'}{inverse}{0,1}{lang_select=non} />#TOTAL_BOUCLE<//B8> // qui comporte plusieurs jointures // ou dans // <BOUCLE6(ARTICLES){id_mot=2}{statut==.*} />#TOTAL_BOUCLE<//B6> // <BOUCLE7(ARTICLES){id_mot>0}{statut?} />#TOTAL_BOUCLE<//B7> // penser a regarder aussi la clause orderby pour ne pas simplifier abusivement // <BOUCLE9(ARTICLES){recherche truc}{par titre}>#ID_ARTICLE</BOUCLE9> // penser a regarder aussi la clause groubpy pour ne pas simplifier abusivement // <BOUCLE10(EVENEMENTS){id_rubrique} />#TOTAL_BOUCLE<//B10> list($t, $c) = each($from); reset($from); $e = '/\\b(' . "{$t}\\." . join("|" . $t . '\\.', $equiv) . ')\\b/'; if (!(strpos($t, ' ') or 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; }
/** * Calcule la liste des langues reellement utilisees dans le site public * * La recherche de langue est effectuée en recréant une boucle pour chaque * objet éditorial gérant des langues de sorte que les éléments non publiés * ne sont pas pris en compte. * * @param string $serveur * Nom du connecteur à la base de données * @return string * Liste des langues utilisées séparées par des virgules **/ function calculer_langues_utilisees($serveur = '') { include_spip('public/interfaces'); include_spip('public/compiler'); include_spip('public/composer'); $langues = array(); $langues[$GLOBALS['meta']['langue_site']] = 1; include_spip('base/objets'); $tables = lister_tables_objets_sql(); $trouver_table = charger_fonction('trouver_table', 'base'); foreach (array_keys($tables) as $t) { $desc = $trouver_table($t, $serveur); // c'est une table avec des langues if ($desc['exist'] and isset($desc['field']['lang']) and isset($desc['field']['langue_choisie'])) { $boucle = new Boucle(); $boucle->show = $desc; $boucle->nom = 'calculer_langues_utilisees'; $boucle->id_boucle = $desc['table_objet']; $boucle->id_table = $desc['table_objet']; $boucle->sql_serveur = $serveur; $boucle->select[] = "DISTINCT lang"; $boucle->from[$desc['table_objet']] = $t; $boucle = pipeline('pre_boucle', $boucle); if (isset($desc['statut']) and $desc['statut']) { instituer_boucle($boucle, false); $res = calculer_select($boucle->select, $boucle->from, $boucle->from_type, $boucle->where, $boucle->join, $boucle->group, $boucle->order, $boucle->limit, $boucle->having, $desc['table_objet'], $desc['table_objet'], $serveur); } else { $res = sql_select(implode(',', $boucle->select), $boucle->from); } while ($row = sql_fetch($res)) { $langues[$row['lang']] = 1; } } } $langues = array_filter(array_keys($langues)); sort($langues); $langues = join(',', $langues); spip_log("langues utilisees: {$langues}"); return $langues; }
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')) { unset($where[$k]); $menage = true; } } // evacuer les eventuels groupby vide issus d'un calcul dynamique $groupby = array_diff($groupby,array('')); // remplacer les sous requetes recursives au calcul list($where_simples,$where_sous) = trouver_sous_requetes($where); //var_dump($where_sous); foreach($where_sous as $k=>$w) { $menage = true; // on recupere la sous requete $sous = match_self($w); 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]); $where[$k] = remplace_sous_requete($w,"(".calculer_select( $sous[1], $from, $from_type, array($sous[2],'0=0'), // pour accepter une string et forcer a faire le menage car on a surement simplifie select et where $join, array(),array(),'', $having,$table,$id,$serveur,false).")"); } 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_pop($where_simples); } foreach($having as $k => $v) { if ((!$v) OR ($v==1) OR ($v=='0=0')) { unset($having[$k]); } } // Installer les jointures. // 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) { $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]; // 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)) $cle = "L$k"; if (!$menage OR isset($afrom[$cle]) OR calculer_jointnul($cle, $select) OR calculer_jointnul($cle, array_diff($join,array($cle=>$join[$cle]))) OR calculer_jointnul($cle, $having) OR calculer_jointnul($cle, $where_simples)) { // 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", $from[$cle], "AS $cle", "ON (", "$cle.$c", "=", "$t.$carr", ($and ? "AND ". $and:"") . ")"); if (isset($afrom[$cle])){ $afrom[$t] = $afrom[$t] + $afrom[$cle]; unset($afrom[$cle]); } $equiv[]= $carr; } else { unset($join[$cledef]);} unset($from[$cle]); $k--; } if (count($afrom)) { // Regarder si la table principale ne sert finalement a rien comme dans //<BOUCLE3(MOTS){id_article}{id_mot}> class='on'</BOUCLE3> //<BOUCLE2(MOTS){id_article} />#TOTAL_BOUCLE<//B2> //<BOUCLE5(RUBRIQUES){id_mot}{tout} />#TOTAL_BOUCLE<//B5> // ou dans //<BOUCLE8(HIERARCHIE){id_rubrique}{tout}{type='Squelette'}{inverse}{0,1}{lang_select=non} />#TOTAL_BOUCLE<//B8> // qui comporte plusieurs jointures // ou dans // <BOUCLE6(ARTICLES){id_mot=2}{statut==.*} />#TOTAL_BOUCLE<//B6> // <BOUCLE7(ARTICLES){id_mot>0}{statut?} />#TOTAL_BOUCLE<//B7> // penser a regarder aussi la clause orderby pour ne pas simplifier abusivement // <BOUCLE9(ARTICLES){recherche truc}{par titre}>#ID_ARTICLE</BOUCLE9> // penser a regarder aussi la clause groubpy pour ne pas simplifier abusivement // <BOUCLE10(EVENEMENTS){id_rubrique} />#TOTAL_BOUCLE<//B10> 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){ $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; }