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