Exemple #1
0
/**
 * 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;
}
Exemple #2
0
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;
}