nur-ture/src/php/iter.php

170 lines
4.2 KiB
PHP

<?php # -*- coding: utf-8 mode: php -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
namespace nur\sery\wip\php;
use Exception;
use Generator;
use Iterator;
use IteratorAggregate;
use nulib\php\ICloseable;
use nulib\StopException;
use nulib\ValueException;
use Traversable;
/**
* Class iter: gestion des itérateurs
*/
class iter {
private static function unexpected_type($object): ValueException {
return ValueException::invalid_type($object, "iterable");
}
/**
* fermer "proprement" un itérateur ou un générateur. retourner true en cas de
* succès, ou false si c'est un générateur et qu'il ne supporte pas l'arrêt
* avec StopException (la valeur de retour n'est alors pas disponible)
*/
static function close($it): bool {
if ($it instanceof ICloseable) {
$it->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;
}
#############################################################################
# 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;
}
}