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];
 | 
						|
  }
 | 
						|
}
 |