close(); return true; } elseif ($it instanceof Generator) { try { $it->throw(new StopException()); return true; } catch (StopException $e) { } } return false; } /** * retourner la première valeur du tableau, de l'itérateur ou de l'instance * de Traversable, ou $default si aucun élément n'est trouvé. */ static final function first($values, $default=null) { if ($values instanceof IteratorAggregate) $values = $values->getIterator(); if ($values instanceof Iterator) { try { $values->rewind(); $value = $values->valid()? $values->current(): $default; } finally { self::close($values); } } elseif (is_array($values) || $values instanceof Traversable) { $value = $default; foreach ($values as $value) { break; } } else { throw self::unexpected_type($values); } return $value; } /** * retourner la première clé du tableau, de l'itérateur ou de l'instance * de Traversable, ou $default si aucun élément n'est trouvé. */ static final function first_key($values, $default=null) { if ($values instanceof IteratorAggregate) $values = $values->getIterator(); if ($values instanceof Iterator) { try { $values->rewind(); $key = $values->valid()? $values->key(): $default; } finally { self::close($values); } } elseif (is_array($values) || $values instanceof Traversable) { $key = $default; foreach ($values as $key => $ignored) { break; } } else { throw self::unexpected_type($values); } return $key; } /** * retourner la première valeur du tableau ou de l'itérateur, ou $default si * aucun élément n'est trouvé. Les instances de Traversable ne sont pas supportés. * * 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 */ static final function one($values, $default=null, bool $rewind=false): array { if (is_array($values)) $values = new ArrayIterator($values); $value = $default; $have_next = false; $it_nexts = new EmptyIterator(); if ($values instanceof IteratorAggregate) $values = $values->getIterator(); if ($values instanceof Iterator) { $values->rewind(); if ($values->valid()) { $value = $values->current(); $values->next(); $have_next = $values->valid(); if ($have_next) { $next = $values->current(); $values->next(); if (!$rewind) { $it_nexts = new AppendIterator(); $it_nexts->append(new ArrayIterator([$next])); $it_nexts->append(new NoRewindIterator($values)); } } } if ($rewind) $values->rewind(); } else { throw ValueException::unexpected_type(Iterator::class, $values); } return [$value, $have_next, $it_nexts]; } /** * retourner [$first, $second, $all] où: * - $first est la première valeur de l'itérateur, ou $default si pas de * premier élément * - $second est la deuxième valeur de l'itérateur, ou $default si pas de * deuxième élément * - $all est un iterateur permettant de parcourir *toute* les valeurs, sans * devoir rembobiner $iterator * * cette méthode permet de savoir: * - si l'itérateur n'a aucun élément ($first === $default) * - si l'itérateur n'a qu'un seul élémement ($second === $default) * - sinon il permet de parcourir toutes les valeurs normalement * * si $rewind est true, appeler rewind() à la fin pour s'assurer que * l'itérateur est fermé correctement. */ static final function peek($iterator, $default=null, bool $rewind=false): array { if (is_array($iterator)) $iterator = new ArrayIterator($iterator); $first = $default; $second = $default; $all = new EmptyIterator(); if ($iterator instanceof IteratorAggregate) $iterator = $iterator->getIterator(); if ($iterator instanceof Iterator) { $fsValues = []; $iterator->rewind(); if ($iterator->valid()) { $first = $iterator->current(); $fsValues[$iterator->key()] = $first; $iterator->next(); if ($iterator->valid()) { $second = $iterator->current(); $fsValues[$iterator->key()] = $second; $iterator->next(); } } if ($rewind) { $iterator->rewind(); } else { $all = new AppendIterator(); $all->append(new ArrayIterator($fsValues)); $all->append(new NoRewindIterator($iterator)); } } else { throw ValueException::unexpected_type(Iterator::class, $iterator); } return [$first, $second, $all]; } ############################################################################# # outils pour gérer de façon générique des instances de {@link Iterator} ou # des arrays /** * @param $it ?iterable|array * @return bool true si l'itérateur ou le tableau ont pu être réinitialisés */ static function rewind(&$it, ?Exception &$exception=null): bool { if ($it instanceof Iterator) { try { $exception = null; $it->rewind(); return true; } catch (Exception $e) { $exception = $e; } } elseif ($it !== null) { reset($it); return true; } return false; } /** * @param $it ?iterable|array */ static function valid($it): bool { if ($it instanceof Iterator) { return $it->valid(); } elseif ($it !== null) { return key($it) !== null; } else { return false; } } /** * @param $it ?iterable|array */ static function current($it, &$key=null) { if ($it instanceof Iterator) { $key = $it->key(); return $it->current(); } elseif ($it !== null) { $key = key($it); return current($it); } else { $key = null; return null; } } /** * @param $it ?iterable|array */ static function next(&$it, ?Exception &$exception=null): void { if ($it instanceof Iterator) { try { $exception = null; $it->next(); } catch (Exception $e) { $exception = $e; } } elseif ($it !== null) { next($it); } } /** * obtenir la valeur de retour si $it est un générateur terminé, ou null sinon */ static function get_return($it) { if ($it instanceof Generator) { try { return $it->getReturn(); } catch (Exception $e) { } } return null; } }