From dad1ae35f0d03b2d925eae1eadc5636adf20ba99 Mon Sep 17 00:00:00 2001 From: Cerdic <cedric@yterium.com> Date: Thu, 16 Dec 2010 19:19:37 +0000 Subject: [PATCH] Une fabrique d'iterateur qui permet de creer n'importe quel iterateur defini dans le dossier iterateurs/ --- ecrire/inc_version.php | 2 +- ecrire/public/compiler.php | 11 +- ecrire/public/creer_boucle_data.php | 2 +- ecrire/public/creer_boucle_enum.php | 2 +- ecrire/public/creer_boucle_pour.php | 2 +- ecrire/public/iterateur.php | 708 +--------------------------- 6 files changed, 27 insertions(+), 700 deletions(-) diff --git a/ecrire/inc_version.php b/ecrire/inc_version.php index 1906df4501..8389102e70 100644 --- a/ecrire/inc_version.php +++ b/ecrire/inc_version.php @@ -319,7 +319,7 @@ $liste_des_authentifications = array( $spip_version_branche = "2.3.0-dev"; // version des signatures de fonctions PHP // (= numero SVN de leur derniere modif cassant la compatibilite et/ou necessitant un recalcul des squelettes) -$spip_version_code = 16459;//16459 +$spip_version_code = 16759;//16459 // version de la base SQL (= numero SVN de sa derniere modif) $spip_version_base = 16428; diff --git a/ecrire/public/compiler.php b/ecrire/public/compiler.php index 1a7f84c7ac..3f69111bb7 100644 --- a/ecrire/public/compiler.php +++ b/ecrire/public/compiler.php @@ -358,7 +358,8 @@ function calculer_boucle_rec($id_boucle, &$boucles, $trace) { define('CODE_CORPS_BOUCLE', '%s $t0 = ""; // REQUETE - $iter = new %s( + $iter = IterFactory::create( + %s, %s, array(%s) ); @@ -506,7 +507,7 @@ function calculer_boucle_nonrec($id_boucle, &$boucles, $trace) { $contexte = memoriser_contexte_compil($boucle); switch ($boucle->iterateur) { - case 'IterSQL': + case 'SQL': $command = 'array( "select"=>$select, "from"=>$from, @@ -523,8 +524,8 @@ function calculer_boucle_nonrec($id_boucle, &$boucles, $trace) { )'; break; - case 'IterDATA': - case 'IterENUM': + case 'DATA': + case 'ENUM': $command = 'array("where" => $where, "source"=>$source, "sourcemode"=>$sourcemode, "limit" => $limit)'; break; @@ -1037,7 +1038,7 @@ function compiler_squelette($squelette, $boucles, $nom, $descr, $sourcefile, $co $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]->iterateur = 'IterSQL'; + $boucles[$id]->iterateur = 'SQL'; $boucles[$id]->descr = &$descr; if ((!$boucles[$id]->jointures) diff --git a/ecrire/public/creer_boucle_data.php b/ecrire/public/creer_boucle_data.php index 6f720b9151..08ffa7e4dd 100644 --- a/ecrire/public/creer_boucle_data.php +++ b/ecrire/public/creer_boucle_data.php @@ -15,7 +15,7 @@ // annonce au compilo les "champs" disponibles // function public_creer_boucle_DATA_dist($b) { - $b->iterateur = 'IterDATA'; # designe la classe d'iterateur + $b->iterateur = 'DATA'; # designe la classe d'iterateur $b->show = array( 'field' => array( 'cle' => 'STRING', diff --git a/ecrire/public/creer_boucle_enum.php b/ecrire/public/creer_boucle_enum.php index dba2766233..504a2ca749 100644 --- a/ecrire/public/creer_boucle_enum.php +++ b/ecrire/public/creer_boucle_enum.php @@ -15,7 +15,7 @@ // annonce au compilo les "champs" disponibles // function public_creer_boucle_ENUM_dist($b) { - $b->iterateur = 'IterENUM'; # designe la classe d'iterateur + $b->iterateur = 'ENUM'; # designe la classe d'iterateur $b->show = array( 'field' => array( 'cle' => 'STRING', diff --git a/ecrire/public/creer_boucle_pour.php b/ecrire/public/creer_boucle_pour.php index 79e80179e1..6a4f165147 100644 --- a/ecrire/public/creer_boucle_pour.php +++ b/ecrire/public/creer_boucle_pour.php @@ -15,7 +15,7 @@ // annonce au compilo les "champs" disponibles // function public_creer_boucle_POUR_dist($b) { - $b->iterateur = 'IterDATA'; # designe la classe d'iterateur + $b->iterateur = 'DATA'; # designe la classe d'iterateur $b->show = array( 'field' => array( 'tableau' => 'ARRAY', diff --git a/ecrire/public/iterateur.php b/ecrire/public/iterateur.php index f5bc4808e6..5cd2d6c9a1 100644 --- a/ecrire/public/iterateur.php +++ b/ecrire/public/iterateur.php @@ -11,704 +11,30 @@ * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ - -/** - * Iterateurs - * http://php.net/manual/fr/class.iterator.php - */ -class Iter implements Iterator { - /** - * Constructeur & initialise - */ - public function __construct() { - $this->pos = 0; - $this->total = $this->total(); - } - - /** - * revient au depart - * @return void - */ - public function rewind() { - $this->pos = 0; - } - - /** - * avons-nous un element - * @return void - */ - public function valid() { - return $this->pos<$this->total; - } - - /** - * Valeur courante - * @return void - */ - public function current() {} - - /** - * Cle courante - * @return void - */ - public function key() {} - - /** - * avancer d'un cran - * @return void - */ - public function next() { - $this->pos++; - } - - # Extension SPIP des iterateurs PHP - /** - * type de l'iterateur - * @var string - */ - protected $type; - - /** - * parametres de l'iterateur - * @var array - */ - protected $command; - - /** - * infos de compilateur - * @var array - */ - protected $info; - - /** - * position courante de l'iterateur - * @var int - */ - protected $pos=null; - - /** - * nombre total resultats dans l'iterateur - * @var int - */ - protected $total=null; - - /** - * aller a la position absolue n, - * comptee depuis le debut - * - * @param int $n - * absolute pos - * @param string $continue - * param for sql_ api - * @return bool - * success or fail if not implemented - */ - public function seek($n=0, $continue=null) { - if ($this->pos>$n) - $this->rewind(); - - while($this->pos<$n AND $this->valid()) - $this->next(); - return true; - } - - /** - * Avancer de $saut pas - * @param $saut - * @param $max - * @return int - */ - public function skip($saut, $max=null){ - // pas de saut en arriere autorise pour cette fonction - if (($saut=intval($saut))<=0) return $this->pos; - $seek = $this->pos + $saut; - // si le saut fait depasser le maxi, on libere la resource - // et on sort - if (is_null($max)) - $max = $this->total(); - - if ($seek>=$max OR $seek>=$this->total()) { - // sortie plus rapide que de faire next() jusqu'a la fin ! - $this->free(); - return $max; - } - - $this->seek($seek); - return $this->pos; - } - - /** - * Renvoyer un tableau des donnees correspondantes - * a la position courante de l'iterateur - * - * @return array|bool - */ - public function fetch() { - if ($this->valid()) { - $r = array('cle' => $this->key(), 'valeur' => $this->current()); - $this->next(); - } else - $r = false; - return $r; - } - - /** - * liberer la ressource - * @return bool - */ - public function free() { - $this->pos = $this->total = 0; - return true; - } - - /** - * Compter le nombre total de resultats - * pour #TOTAL_BOUCLE - * @return int - */ - public function total() { - if (is_null($this->total)) - $this->total = 0; - return $this->total; - } -} - /** - * Iterateur SQL + * Fabrique d'iterateur + * permet de charger n'importe quel iterateur IterateurXXX + * fourni dans le fichier iterateurs/iterateur_xxx.php + * */ -class IterSQL extends Iter { +class IterFactory{ + public static function create($iterateur,$command, $info=null){ - /** - * ressource sql - * @var resource|bool - */ - protected $sqlresult = false; + // chercher la classe d'iterateur + // IterateurXXX + // definie dans le fichier iterateurs/iterateur_xxx.php + $class = "Iterateur".$iterateur; + if (!include_spip("iterateurs/iterateur_" . strtolower($iterateur)) + OR !class_exists($class)) { - /** - * row sql courante - * @var array|null - */ - protected $row = null; - - /** - * 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); - $ok = !!$this->sqlresult; - if ($ok) - $this->row = sql_fetch($this->sqlresult, $this->command['connect']); - $this->pos = 0; - $this->total = $this->total(); - } - - /* - * array command: les commandes d'initialisation - * array info: les infos sur le squelette - */ - public function __construct($command, $info=array()) { - $this->type='SQL'; - $this->command = $command; - $this->info = $info; - $this->select(); - } - - /** - * Rembobiner - * @return bool - */ - public function rewind() { - parent::rewind(); - return $this->seek(0); - } - - /** - * Verifier l'etat de l'iterateur - * @return bool - */ - public function valid() { - return $this->sqlresult AND is_array($this->row); - } - - /** - * Valeurs sur la position courante - * @return array - */ - public function current() { - return $this->row; - } - - /** - * Sauter a une position absolue - * @param int $n - * @param null|string $continue - * @return bool - */ - public function seek($n=0, $continue=null) { - if (!sql_seek($this->sqlresult, $n, $this->command['connect'], $continue)) { - // SQLite ne sait pas seek(), il faut relancer la query - // si la position courante est apres la position visee - // il faut relancer la requete - if ($this->pos>$n){ - $this->free(); - $this->select(); - } - // et utiliser la methode par defaut pour se deplacer au bon endroit - parent::seek($n); - return true; + die("Iterateur $iterateur non trouve"); + // si l'iterateur n'existe pas, on se rabat sur le generique + $class = "IterateurSPIP"; + include_spip("iterateurs/iterateur"); } - $this->row = sql_fetch($this->sqlresult, $this->command['connect']); - $this->pos = min($n,$this->total()); - return true; - } - - /** - * Avancer d'un cran - * @return void - */ - public function next(){ - $this->row = sql_fetch($this->sqlresult, $this->command['connect']); - parent::next(); - } - - /** - * Avancer et retourner les donnees pour le nouvel element - * @return array|bool|null - */ - public function fetch(){ - if ($this->valid()) { - $r = $this->current(); - $this->next(); - } else - $r = false; - return $r; - } - /** - * liberer les ressources - * @return bool - */ - public function free(){ - parent::free(); - if (!$this->sqlresult) return true; - $a = sql_free($this->sqlresult, $this->command['connect']); - $this->sqlresult = null; - return $a; - } - - /** - * Compter le nombre de resultats - * @return int - */ - public function total() { - if (is_null($this->total)) - if (!$this->sqlresult) - $this->total = 0; - else - $this->total = sql_count($this->sqlresult, $this->command['connect']); - return $this->total; + return new $class($command, $info); } } -/** - * IterENUM pour iterer sur un intervalle de nombre - * repondant eventuellement a des conditions de filtrage - */ -class IterENUM extends Iter { - - /** - * Valeur entiere de l'iterateur, ce qui est renvoye - * @var int - */ - protected $n = 0; - - /** - * Valeur de depart de l'iteration, zero - * @var int - */ - protected $start = 0; - - /** - * Maximum d'iteration, valeur de securite - * @var int - */ - protected $max = 1000000; - - /** - * Offset dans les resultats - * @var int - */ - protected $offset = 0; - - /** - * Conditions de filtrage - * ie criteres de selection - * @var array - */ - protected $filtre = array(); - - /** - * Fonction de filtrage compilee a partir des criteres de filtre - * @var string - */ - protected $func_filtre = null; - - public function __construct($command=array(), $info=array()) { - $this->type='ENUM'; - $this->command = $command; - $this->info = $info; - - $op = ''; - if (is_array($this->command['where'])) { - foreach ($this->command['where'] as $k => $com) { - switch($com[1]) { - case 'valeur': - unset($op); - if ($com[0] == 'REGEXP') - $this->filtre[] = 'preg_match("/". '.str_replace('\"', '"', $com[2]).'."/", $'.$com[1].')'; - else if ($com[0] == '=') - $op = '=='; - else if (in_array($com[0], array('<','<=', '>', '>='))) - $op = $com[0]; - - if ($op) - $this->filtre[] = '$'.$com[1].$op.str_replace('\"', '"', $com[2]); - - break; - } - - } - } - - $this->pos = 0; - $this->total = $this->max; - - // critere {2,7} - if ($this->command['limit']) { - $limit = explode(',',$this->command['limit']); - $this->offset = $limit[0]; - $this->total = $limit[1]; - } - - // Appliquer les filtres sur (valeur) - if ($this->filtre) { - $this->func_filtre = create_function('$cle,$valeur', $b = 'return ('.join(') AND (', $this->filtre).');'); - } - - $this->rewind(); - } - - /** - * Rembobiner - * On part de n=0 et on next() tant qu'on a pas satisfait les filtres, - * en bloquant pos=0 - * @return void - */ - public function rewind() { - $this->n = $this->start-1; - for ($i=0; $i<=$this->offset; $i++) { - $this->pos = -1; # forcer la position courante - $this->next(); # pour filtrage par func_filtre - } - } - - /** - * L'iterateur est il encore en cours ? - * @return bool - */ - public function valid(){ - return - $this->n <= $this->max - AND $this->pos < $this->total; - } - - /** - * Valeur courante de la valeur - * @return int - */ - public function current() { - return $this->n; - } - - /** - * Valeur courante du compteur - * @return int - */ - public function key() { - return $this->pos; - } - - /** - * Avancer d'un pas - * Ici c'est un peu tordu : - * - on incremente pos d'une unite, - * car c'est next(), donc on va juste a la position d'apres - * - on incremente n jusqu'a ce que les conditions de filtrage - * soient satisfaites pour trouver le "prochain" resultat - * - * @return void - */ - public function next() { - $this->pos++; - $this->n++; - if ($f = $this->func_filtre) { - while ($this->valid() - AND !$f($this->pos,$this->n)) { - $this->n++; - } - } - } - - /** - * Total - * @return int - */ - public function total() { - return $this->total; - } -} - - -/** - * IterDATA pour iterer sur des donnees - */ -class IterDATA extends Iter { - /** - * tableau de donnees - * @var array - */ - protected $tableau = array(); - - /** - * Conditions de filtrage - * ie criteres de selection - * @var array - */ - protected $filtre = array(); - - - /** - * Cle courante - * @var null - */ - protected $cle = null; - - /** - * Valeur courante - * @var null - */ - protected $valeur = null; - - /** - * Constructeur - * - * @param $command - * @param array $info - */ - public function __construct($command, $info=array()) { - $this->type='DATA'; - $this->command = $command; - $this->info = $info; - - $this->select($command); - } - - /** - * Revenir au depart - * @return void - */ - public function rewind() { - parent::rewind(); - reset($this->tableau); - list($this->cle, $this->valeur) = each($this->tableau); - } - - protected function select($command) { - // les commandes connues pour l'iterateur POUR - // sont : tableau=#ARRAY ; cle=...; valeur=... - // source URL - if (isset($this->command['source'])) { - if (isset($this->command['sourcemode']) - AND in_array($this->command['sourcemode'], - array('table', 'array', 'tableau')) - ) { - if (is_array($a = $this->command['source']) - OR is_array($a = unserialize($this->command['source']))) { - $this->tableau = $a; - $this->ok = true; - } - } - else if (preg_match(',^http://,', $this->command['source'])) { - include_spip('inc/distant'); - $u = recuperer_page($this->command['source']); - } else if (@is_readable($this->command['source'])) - $u = spip_file_get_contents($this->command['source']); - else - $u = $this->command['source']; - - // tout ce bloc devrait marcher par charger_fonction('xxx_to_array') - // si c'est du RSS - if (isset($this->command['sourcemode'])) { - if ($g = charger_fonction($this->command['sourcemode'] . '_to_array', 'inc', true)) { - if (is_array($a = $g($u))) { - $this->tableau = $a; - $this->ok = true; - } - } - else - switch ($this->command['sourcemode']) { - case 'rss': - case 'atom': - include_spip('inc/syndic'); - if (is_array($rss = analyser_backend($u))) { - $this->tableau = $rss; - $this->ok = true; - } - break; - case 'json': - if (is_array($json = json_decode($u))) { - $this->tableau = $json; - $this->ok = true; - } - break; - case 'yaml': - include_spip('inc/yaml'); - if (is_array($yaml = yaml_decode($u))) { - $this->tableau = $yaml; - $this->ok = true; - } - break; - case 'csv': - # decodage csv a passer en inc/csv - # cf. http://www.php.net/manual/en/function.str-getcsv.php#100579 et suiv. - if (function_exists('str_getcsv')) # PHP 5.3.0 - $this->tableau = str_getcsv($u); - else - foreach(preg_split('/\r?\n/',$u) as $ligne) - $this->tableau[] = explode(',', $ligne); - $this->ok = true; - } - } - } - - if (is_array($this->command['where'])) { - $op = ''; - foreach ($this->command['where'] as $k => $com) { - switch($com[1]) { - case 'tableau': - if ($com[0] !== '=') { - // erreur - } - # sql_quote a l'envers : pas propre... - # c'est pour la compat ascendante avec le critere - # {tableau=#ENV...} de la boucle POUR de SPIP-Bonux-2 - $x = null; - eval ('$x = '.str_replace('\"', '"', $com[2]).';'); - if (is_array($x) OR is_array($x = @unserialize($x))) { - $this->tableau = $x; - $this->ok = true; - } - else - { - // erreur - } - break; - case 'cle': - case 'valeur': - unset($op); - if ($com[0] == 'REGEXP') - $this->filtre[] = 'preg_match("/". '.str_replace('\"', '"', $com[2]).'."/", $'.$com[1].')'; - else if ($com[0] == '=') - $op = '=='; - else if (in_array($com[0], array('<','<=', '>', '>='))) - $op = $com[0]; - - if ($op) - $this->filtre[] = '$'.$com[1].$op.str_replace('\"', '"', $com[2]); - - break; - } - - } - } - - // Appliquer les filtres sur (cle,valeur) - if ($this->filtre) { - $func_filtre = create_function('$cle,$valeur', $b = 'return ('.join(') AND (', $this->filtre).');'); - #var_dump($b); - foreach($this->tableau as $cle=>$valeur) { - if (!$func_filtre($cle,$valeur)) - unset($this->tableau[$cle]); - } - } - - // critere {2,7} - if ($this->command['limit']) { - $limit = explode(',',$this->command['limit']); - $this->tableau = array_slice($this->tableau, - $limit[0],$limit[1],true); - } - - $this->rewind(); - #var_dump($this->tableau); - } - - - /** - * L'iterateur est-il encore valide ? - * @return bool - */ - public function valid(){ - return !is_null($this->cle); - } - - /** - * Retourner la valeur - * @return null - */ - public function current() { - return $this->valeur; - } - - /** - * Retourner la cle - * @return null - */ - public function key() { - return $this->cle; - } - - /** - * Passer a la valeur suivante - * @return void - */ - public function next(){ - parent::next(); - if ($this->valid()) - list($this->cle, $this->valeur) = each($this->tableau); - } - - /** - * Compter le nombre total de resultats - * @return int - */ - public function total() { - if (is_null($this->total)) - $this->total = count($this->tableau); - return $this->total; - } -} - - -function inc_file_to_array_dist($u) { - return preg_split('/\r?\n/', $u); -} -function inc_plugins_to_array_dist($u) { - include_spip('inc/plugin'); - return liste_chemin_plugin_actifs(); -} - ?> -- GitLab