190 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nur\b\coll;
 | |
| 
 | |
| use AppendIterator;
 | |
| use ArrayIterator;
 | |
| use EmptyIterator;
 | |
| use Exception;
 | |
| use Iterator;
 | |
| use NoRewindIterator;
 | |
| use nur\b\io\EOFException;
 | |
| 
 | |
| /**
 | |
|  * Class AbstractIterator: implémentation de base d'un itérateur
 | |
|  *
 | |
|  * les classes dérivées *doivent* implémenter les méthodes key() et current()
 | |
|  * qui peuvent simplement être implémentée comme des méthodes déléguées de
 | |
|  * respectivement {@link _key()} et {@link _current()}.
 | |
|  *
 | |
|  * Ce mode opératoire permet de créer ces méthodes avec une signature appropriée,
 | |
|  * permettant à un IDE de découvrir le type des données qui sont accédées
 | |
|  */
 | |
| abstract class AbstractIterator implements Iterator {
 | |
|   const AUTO_REWIND = false;
 | |
| 
 | |
|   private $setup = false;
 | |
|   private $valid = false;
 | |
|   private $toredown = true;
 | |
| 
 | |
|   private $index = 0;
 | |
|   protected $key;
 | |
|   protected $item = null;
 | |
| 
 | |
|   /**
 | |
|    * initialiser les ressources nécessaires à l'itération.
 | |
|    * les exceptions lancées par cette méthode sont ignorées.
 | |
|    */
 | |
|   protected function _setup() {}
 | |
| 
 | |
|   /**
 | |
|    * retourner le prochain élément. retourner false ou lancer l'exception
 | |
|    * EOFException pour indiquer que plus aucun élément n'est disponible
 | |
|    *
 | |
|    * le cas échéant, initialiser $key
 | |
|    *
 | |
|    * @throws EOFException
 | |
|    */
 | |
|   abstract protected function _next(&$key);
 | |
| 
 | |
|   /**
 | |
|    * libérer les ressources allouées.
 | |
|    * les exceptions lancées par cette méthode sont ignorées.
 | |
|    */
 | |
|   protected function _teardown() {}
 | |
| 
 | |
|   /**
 | |
|    * lancer un traitement avant de commencer l'itération.
 | |
|    *
 | |
|    * cette méthode est appelée après _setup() et l'objet est garanti d'être dans
 | |
|    * un état valide. elle est prévue pour être surchargée par l'utilisateur
 | |
|    */
 | |
|   protected function beforeIter() {}
 | |
| 
 | |
|   /**
 | |
|    * modifier un élément avant de le retourner.
 | |
|    *
 | |
|    * cette méthode est prévue pour être surchargée par l'utilisateur.
 | |
|    */
 | |
|   protected function cook(&$item) {}
 | |
| 
 | |
|   function _key() {
 | |
|     return $this->key;
 | |
|   }
 | |
| 
 | |
|   function _current() {
 | |
|     return $this->item;
 | |
|   }
 | |
| 
 | |
|   function next() {
 | |
|     if ($this->toredown) return;
 | |
|     try {
 | |
|       $item = $this->_next($key);
 | |
|     } catch (EOFException $e) {
 | |
|       $item = false;
 | |
|     }
 | |
|     $this->valid = false;
 | |
|     if ($item !== false) {
 | |
|       $this->cook($item);
 | |
|       $this->item = $item;
 | |
|       if ($key !== null) {
 | |
|         $this->key = $key;
 | |
|       } else {
 | |
|         $this->index++;
 | |
|         $this->key = $this->index;
 | |
|       }
 | |
|       $this->valid = true;
 | |
|     } else {
 | |
|       try {
 | |
|         $this->_teardown();
 | |
|       } catch (Exception $e) {
 | |
|       }
 | |
|       $this->toredown = true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function rewind() {
 | |
|     if ($this->setup) {
 | |
|       if (!$this->toredown) {
 | |
|         try {
 | |
|           $this->_teardown();
 | |
|         } catch (Exception $e) {
 | |
|         }
 | |
|       }
 | |
|       $this->setup = false;
 | |
|       $this->valid = false;
 | |
|       $this->toredown = true;
 | |
|       $this->index = 0;
 | |
|       $this->key = null;
 | |
|       $this->item = null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function valid() {
 | |
|     if (!$this->setup) {
 | |
|       try {
 | |
|         $this->_setup();
 | |
|       } catch (Exception $e) {
 | |
|       }
 | |
|       $this->setup = true;
 | |
|       $this->toredown = false;
 | |
|       $this->beforeIter();
 | |
|       $this->next();
 | |
|     }
 | |
|     return $this->valid;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * retourner la première valeur de la liste ou $default si aucun élément n'est
 | |
|    * trouvé.
 | |
|    *
 | |
|    * si static::AUTO_REWIND est true, appeler rewind() à la fin pour s'assurer
 | |
|    * que l'itérateur est fermé correctement.
 | |
|    */
 | |
|   function get($default=null) {
 | |
|     # obtenir le premier élément de la liste
 | |
|     $this->rewind();
 | |
|     $value = $this->valid()? $this->current(): $default;
 | |
|     if (static::AUTO_REWIND) $this->rewind();
 | |
|     return $value;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * retourner la première valeur de la liste, ou $default si aucun élément
 | |
|    * n'est trouvé.
 | |
|    *
 | |
|    * si $rewind est true, appeler rewind() à la fin pour s'assurer que
 | |
|    * l'itérateur est fermé correctement.
 | |
|    *
 | |
|    * retourner un tableau [$value, $have_next, $it_nexts]
 | |
|    * - $have_next vaut true s'il y a encore des données qui suivent
 | |
|    * - si $rewind==false, $it_nexts est un itérateur qui permet d'accéder aux
 | |
|    *   données suivantes
 | |
|    */
 | |
|   function one($default=null, ?bool $rewind=null): array {
 | |
|     if ($rewind === null) $rewind = static::AUTO_REWIND;
 | |
| 
 | |
|     $value = $default;
 | |
|     $have_next = false;
 | |
|     $it_nexts = new EmptyIterator();
 | |
| 
 | |
|     $this->rewind();
 | |
|     if ($this->valid()) {
 | |
|       $value = $this->current();
 | |
|       $this->next();
 | |
|       $have_next = $this->valid();
 | |
|       if ($have_next) {
 | |
|         $next = $this->current();
 | |
|         $this->next();
 | |
|         if (!$rewind) {
 | |
|           $it_nexts = new AppendIterator();
 | |
|           $it_nexts->append(new ArrayIterator([$next]));
 | |
|           $it_nexts->append(new NoRewindIterator($this));
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if ($rewind) $this->rewind();
 | |
| 
 | |
|     return [$value, $have_next, $it_nexts];
 | |
|   }
 | |
| }
 |