diff --git a/src/php/iter.php b/src/php/iter.php deleted file mode 100644 index 0a67146..0000000 --- a/src/php/iter.php +++ /dev/null @@ -1,168 +0,0 @@ -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; - } -} diff --git a/src/php/iter/Cursor.php b/src/php/iter/Cursor.php deleted file mode 100644 index 1579c1f..0000000 --- a/src/php/iter/Cursor.php +++ /dev/null @@ -1,310 +0,0 @@ - ["?iterable"], - "rows_func" => ["?callable"], - "filter" => ["?array"], - "filter_func" => ["?callable"], - "map" => ["?array"], - "map_func" => ["?callable"], - "cols" => ["?array"], - "cols_func" => ["?callable"], - ]; - - function __construct(?iterable $rows=null, ?array $params=null) { - if ($rows !== null) $params["rows"] = $rows; - - $rows = $params["rows"] ?? null; - $rowsGenerator = null; - $rowsFunc = $params["rows_func"] ?? null; - if ($rowsFunc !== null) { - if ($rowsFunc instanceof Traversable) { - $rowsGenerator = $rowsFunc; - $rowsFunc = null; - } else { - $rowsFunc = func::with($rowsFunc, [$rows, $this]); - } - } elseif ($rows instanceof Traversable) { - $rowsGenerator = $rows; - } else { - $rowsFunc = func::with(function() use ($rows) { - return $rows; - }); - } - $this->rowsGenerator = $rowsGenerator; - $this->rowsFunc = $rowsFunc; - - $filter = $params["filter"] ?? null; - $filterFunc = $params["filter_func"] ?? null; - if ($filterFunc !== null) $this->setFilterFunc($filterFunc); - elseif ($filter !== null) $this->setFilter($filter); - - $map = $params["map"] ?? null; - $mapFunc = $params["map_func"] ?? null; - if ($mapFunc !== null) $this->setMapFunc($mapFunc); - elseif ($map !== null) $this->setMap($map); - - $this->cols = $params["cols"] ?? null; - $this->setColsFunc($params["cols_func"] ?? null); - } - - /** un générateur de lignes */ - private ?Traversable $rowsGenerator; - - /** - * une fonction de signature function(mixed $rows, Cursor): ?iterable - */ - private ?func $rowsFunc; - - /** - * une fonction de signature function(?array $row, Cursor): bool - */ - private ?func $filterFunc = null; - - function setFilter(array $filter): self { - $this->filterFunc = func::with(function(?array $row) use ($filter) { - return cl::filter($row, $filter); - }); - return $this; - } - - function setFilterFunc(?callable $func): self { - if ($func === null) $this->filterFunc = null; - else $this->filterFunc = func::with($func)->bind($this); - return $this; - } - - /** - * une fonction de signature function(?array $row, Cursor): ?array - */ - private ?func $mapFunc = null; - - function setMap(array $map): self { - $this->mapFunc = func::with(function(?array $row) use ($map) { - return cl::map($row, $map); - }); - return $this; - } - - function setMapFunc(?callable $func): self { - if ($func === null) $this->mapFunc = null; - else $this->mapFunc = func::with($func)->bind($this); - return $this; - } - - /** - * une fonction de signature function(?array $row, Cursor): ?array - */ - private ?func $colsFunc = null; - - function setColsFunc(?callable $func): self { - $this->cols = null; - if ($func === null) $this->colsFunc = null; - else $this->colsFunc = func::with($func)->bind($this); - return $this; - } - - /** @var iterable|null source des éléments */ - protected ?iterable $rows; - - /** @var array|null listes des colonnes de chaque enregistrement */ - public ?array $cols; - - /** - * @var int index de l'enregistrement (en ne comptant pas les éléments filtrés) - */ - public int $index; - - /** - * @var int index original de l'enregistrement (en tenant compte des éléments - * filtrés) - */ - public int $origIndex; - - /** @var string|int clé de l'enregistrement */ - public $key; - - /** @var mixed élément original récupéré depuis la source */ - public $raw; - - /** - * @var array|null enregistrement après conversion en tableau et application - * du mapping - */ - public ?array $row; - - function __get($name) { - if ($name === "value") return $this->row; - elseif ($name == "rows") return $this->rows; - trigger_error("Undefined property $name"); - return null; - } - - function offsetExists($offset): bool { - return cl::has($this->row, $offset); - } - - function offsetGet($offset) { - return cl::get($this->row, $offset); - } - - function offsetSet($offset, $value): void { - cl::set($this->row, $offset, $value); - } - - function offsetUnset($offset): void { - cl::del($this->row, $offset); - } - - /** - * données de session: cela permet de maintenir certaines informations pendant - * le parcours des données - */ - protected ?array $data; - - /** @param string|int $key */ - function has($key): bool { - return cl::has($this->data, $key); - } - - /** @param string|int $key */ - function get($key) { - return cl::get($this->data, $key); - } - - /** @param string|int $key */ - function set($key, $value): void { - $this->data[$key] = $value; - } - - /** @param string|int $key */ - function del($key) { - $orig = cl::get($this->data, $key); - unset($this->data[$key]); - return $orig; - } - - protected function convertToRow($raw): ?array { - return cl::withn($raw); - } - - protected function filterFunc(?array $row): bool { - return false; - } - - protected function mapFunc(?array $row): ?array { - return $this->row; - } - - protected function colsFunc(?array $row): ?array { - return $this->row !== null? array_keys($this->row): null; - } - - ############################################################################# - # Iterator - - function rewind() { - $this->cols = null; - $this->index = 0; - $this->origIndex = 0; - $this->key = null; - $this->raw = null; - $this->row = null; - $this->data = null; - if ($this->rowsGenerator !== null) { - $rows = $this->rowsGenerator; - if ($rows instanceof IteratorAggregate) $rows = $rows->getIterator(); - $rows->rewind(); - $this->rows = $rows; - } else { - $this->rows = $this->rowsFunc->invoke(); - } - } - - function valid(): bool { - $colsFunc = $this->colsFunc; - $filterFunc = $this->filterFunc; - $mapFunc = $this->mapFunc; - while ($valid = iter::valid($this->rows)) { - $this->raw = iter::current($this->rows, $this->key); - # conversion en enregistrement - $this->key ??= $this->origIndex; - $this->row = $this->convertToRow($this->raw); - # filtrage - if ($filterFunc === null) $filtered = $this->filterFunc($this->row); - else $filtered = $filterFunc->invoke([$this->row, $this]); - if ($filtered) { - iter::next($this->rows); - $this->origIndex++; - } else { - # l'enregistrement n'as pas été filtré: faire le mapping - if ($mapFunc === null) $this->row = $this->mapFunc($this->row); - else $this->row = $mapFunc->invoke([$this->row, $this]); - # calculer la liste des colonnes le cas échéant - if ($this->cols === null) { - if ($colsFunc === null) $this->cols = $this->colsFunc($this->row); - else $this->cols = $colsFunc->invoke([$this->row, $this]); - } - break; - } - } - if (!$valid) { - iter::close($this->rows); - $this->rows = null; - $this->cols = null; - $this->index = -1; - $this->origIndex = -1; - $this->key = null; - $this->raw = null; - $this->row = null; - # ne pas toucher à data, l'utilisateur peut vouloir continuer à consulter - # les valeurs - } - return $valid; - } - - function current(): ?array { - return $this->row; - } - - function key() { - return $this->key; - } - - function next() { - iter::next($this->rows); - $this->index++; - $this->origIndex++; - } - - ############################################################################# - - function each(callable $func): void { - $func = func::with($func); - $this->rewind(); - while ($this->valid()) { - $func->invoke([$this]); - $this->next(); - } - } -} diff --git a/tests/php/coll/CursorTest.php b/tests/php/coll/CursorTest.php deleted file mode 100644 index b572c4c..0000000 --- a/tests/php/coll/CursorTest.php +++ /dev/null @@ -1,152 +0,0 @@ - 1, "b" => 2, "c" => 3, "x" => 99]; - $map = ["a", "b" => "x", "c" => function() { return "y"; }, "d" => null]; - self::assertSame([ - "a" => $row["a"], - "b" => $row["x"], - "c" => "y", - "d" => null - ], $cursor->mapRow($row, $map)); - } - - function test_filter_row() { - $cursor = new class extends Cursor { - function filterRow(array $row, $filter): bool { - return cl::filter($row, $filter); - } - }; - $row = ["a" => 1, "b" => 2, "c" => 3, "x" => 99]; - self::assertTrue($cursor->filterRow($row, "a")); - self::assertTrue($cursor->filterRow($row, ["a"])); - self::assertTrue($cursor->filterRow($row, ["a" => true])); - self::assertFalse($cursor->filterRow($row, ["a" => false])); - self::assertTrue($cursor->filterRow($row, ["a" => 1])); - self::assertFalse($cursor->filterRow($row, ["a" => 2])); - - self::assertFalse($cursor->filterRow($row, "z")); - self::assertFalse($cursor->filterRow($row, ["z"])); - self::assertFalse($cursor->filterRow($row, ["z" => true])); - self::assertTrue($cursor->filterRow($row, ["z" => false])); - self::assertFalse($cursor->filterRow($row, ["z" => 1])); - } - - const SCALARS = [0, 1, 2, 3, 4]; - - function generator() { - yield from self::SCALARS; - } - - function testVanilla() { - $c = new Cursor(self::SCALARS); - self::assertSame([[0], [1], [2], [3], [4]], cl::all($c)); - self::assertSame([[0], [1], [2], [3], [4]], cl::all($c)); - - $c = new Cursor($this->generator()); - self::assertSame([[0], [1], [2], [3], [4]], cl::all($c)); - self::assertException(Exception::class, function() use ($c) { - // pas possible de rewind un générateur - return cl::all($c); - }); - - $c = new Cursor(null, [ - "rows" => function() { - return self::SCALARS; - }, - ]); - self::assertError(TypeError::class, function() use ($c) { - // rows doit être un iterable, pas une fonction - return cl::all($c); - }); - - $c = new Cursor(null, [ - "rows" => $this->generator(), - ]); - self::assertSame([[0], [1], [2], [3], [4]], cl::all($c)); - self::assertException(Exception::class, function() use ($c) { - // pas possible de rewind un générateur - return cl::all($c); - }); - - $c = new Cursor(null, [ - "rows_func" => function() { - return self::SCALARS; - }, - ]); - self::assertSame([[0], [1], [2], [3], [4]], cl::all($c)); - self::assertSame([[0], [1], [2], [3], [4]], cl::all($c)); - - $c = new Cursor(null, [ - "rows_func" => $this->generator(), - ]); - self::assertSame([[0], [1], [2], [3], [4]], cl::all($c)); - self::assertException(Exception::class, function() use ($c) { - // pas possible de rewind un générateur - return cl::all($c); - }); - - $c = new Cursor(null, [ - "rows_func" => function() { - yield from self::SCALARS; - }, - ]); - self::assertSame([[0], [1], [2], [3], [4]], cl::all($c)); - self::assertSame([[0], [1], [2], [3], [4]], cl::all($c)); - } - - function testMap() { - $c = new Cursor(self::SCALARS, [ - "map_func" => function(Cursor $c) { - return [$c->raw + 1]; - }, - ]); - self::assertSame([[1], [2], [3], [4], [5]], cl::all($c)); - } - - function testFilter() { - $c = new Cursor(self::SCALARS, [ - "filter_func" => function(Cursor $c) { - return $c->raw % 2 == 0; - }, - ]); - self::assertSame([[1], [3]], cl::all($c)); - } - - function testEach() { - $c = new Cursor(self::SCALARS, [ - "filter_func" => function(Cursor $c) { - return $c->raw % 2 == 0; - }, - "map_func" => function(Cursor $c) { - return [$c->raw + 1]; - }, - ]); - $xs = []; - $xitems = []; - $oxs = []; - $kitems = []; - $c->each(function(Cursor $c) use (&$xs, &$xitems, &$oxs, &$kitems) { - $xs[] = $c->index; - $oxs[] = $c->origIndex; - $xitems[$c->index] = $c->row[0]; - $kitems[$c->key] = $c->row[0]; - }); - self::assertSame([0, 1], $xs); - self::assertSame([2, 4], $xitems); - self::assertSame([1, 3], $oxs); - self::assertSame([1 => 2, 3 => 4], $kitems); - } -}