From 254f44b4287158d8a83317f617f75abed6e2d900 Mon Sep 17 00:00:00 2001
From: Cerdic <cedric@yterium.com>
Date: Thu, 16 Dec 2010 18:59:22 +0000
Subject: [PATCH] Separer les iterateurs dans des fichiers homonymes Renommer
 Iter en IterateurSPIP et les 3 variantes en IterateurXXX

Travail preparatif, il faut encore brancher le compilateur sur cette version
---
 .gitattributes                       |   3 +
 ecrire/iterateurs/iterateur.php      | 543 +--------------------------
 ecrire/iterateurs/iterateur_data.php | 255 +++++++++++++
 ecrire/iterateurs/iterateur_enum.php | 175 +++++++++
 ecrire/iterateurs/iterateur_sql.php  | 156 ++++++++
 5 files changed, 593 insertions(+), 539 deletions(-)
 create mode 100644 ecrire/iterateurs/iterateur_data.php
 create mode 100644 ecrire/iterateurs/iterateur_enum.php
 create mode 100644 ecrire/iterateurs/iterateur_sql.php

diff --git a/.gitattributes b/.gitattributes
index 20931f086c..d794394b00 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -230,6 +230,9 @@ ecrire/install/etape_sup1.php -text
 ecrire/install/etape_sup2.php -text
 ecrire/install/index.php -text
 ecrire/iterateurs/iterateur.php -text
+ecrire/iterateurs/iterateur_data.php -text
+ecrire/iterateurs/iterateur_enum.php -text
+ecrire/iterateurs/iterateur_sql.php -text
 ecrire/lang/ecrire_ast.php -text
 ecrire/lang/ecrire_ay.php -text
 ecrire/lang/ecrire_br.php -text
diff --git a/ecrire/iterateurs/iterateur.php b/ecrire/iterateurs/iterateur.php
index f5bc4808e6..accd243383 100644
--- a/ecrire/iterateurs/iterateur.php
+++ b/ecrire/iterateurs/iterateur.php
@@ -15,8 +15,11 @@
 /**
  * Iterateurs
  * http://php.net/manual/fr/class.iterator.php
+ *
+ * IterateurSPIP
+ * implementation de base
  */
-class Iter implements Iterator {
+class IterateurSPIP implements Iterator {
 	/**
 	 * Constructeur & initialise
 	 */
@@ -173,542 +176,4 @@ class Iter implements Iterator {
 	}
 }
 
-/**
- * Iterateur SQL
- */
-class IterSQL extends Iter {
-
-	/**
-	 * ressource sql
-	 * @var resource|bool
-	 */
-	protected $sqlresult = false;
-
-	/**
-	 * 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;
-		}
-		$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;
-	}
-}
-
-/**
- * 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();
-}
-
 ?>
diff --git a/ecrire/iterateurs/iterateur_data.php b/ecrire/iterateurs/iterateur_data.php
new file mode 100644
index 0000000000..fe963f2ec3
--- /dev/null
+++ b/ecrire/iterateurs/iterateur_data.php
@@ -0,0 +1,255 @@
+<?php
+
+
+/***************************************************************************\
+ *  SPIP, Systeme de publication pour l'internet                           *
+ *                                                                         *
+ *  Copyright (c) 2001-2010                                                *
+ *  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.   *
+\***************************************************************************/
+
+include_spip('iterateurs/iterateur');
+
+/**
+ * IterateurDATA pour iterer sur des donnees
+ */
+class IterateurDATA extends IterateurSPIP {
+	/**
+	 * 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();
+}
+
+?>
diff --git a/ecrire/iterateurs/iterateur_enum.php b/ecrire/iterateurs/iterateur_enum.php
new file mode 100644
index 0000000000..3243bc888f
--- /dev/null
+++ b/ecrire/iterateurs/iterateur_enum.php
@@ -0,0 +1,175 @@
+<?php
+
+
+/***************************************************************************\
+ *  SPIP, Systeme de publication pour l'internet                           *
+ *                                                                         *
+ *  Copyright (c) 2001-2010                                                *
+ *  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.   *
+\***************************************************************************/
+
+include_spip('iterateurs/iterateur');
+
+/**
+ * IterateurENUM pour iterer sur un intervalle de nombre
+ * repondant eventuellement a des conditions de filtrage
+ */
+class IterateurENUM extends IterateurSPIP {
+
+	/**
+	 * 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;
+	}
+}
+
+
+?>
diff --git a/ecrire/iterateurs/iterateur_sql.php b/ecrire/iterateurs/iterateur_sql.php
new file mode 100644
index 0000000000..25da7d03e3
--- /dev/null
+++ b/ecrire/iterateurs/iterateur_sql.php
@@ -0,0 +1,156 @@
+<?php
+
+
+/***************************************************************************\
+ *  SPIP, Systeme de publication pour l'internet                           *
+ *                                                                         *
+ *  Copyright (c) 2001-2010                                                *
+ *  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.   *
+\***************************************************************************/
+
+include_spip('iterateurs/iterateur');
+
+/**
+ * Iterateur SQL
+ */
+class IterateurSQL extends IterateurSPIP {
+
+	/**
+	 * ressource sql
+	 * @var resource|bool
+	 */
+	protected $sqlresult = false;
+
+	/**
+	 * 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;
+		}
+		$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;
+	}
+}
+
+?>
-- 
GitLab