["?iterable"], "rows_func" => ["?callable"], "cols" => ["?array"], "cols_func" => ["?callable"], "map" => ["?array"], "map_func" => ["?callable"], "filter" => ["?array"], "filter_func" => ["?callable"], ]; /** * mapper le tableau source $row selon les règles suivantes illustrées dans * l'exemple suivant: * si * $map = ["a", "b" => "x", "c" => function() { return "y"; }, "d" => null] * alors retourner le tableau * ["a" => $row["a"], "b" => $row["x"], "c" => "y", "d" => null] */ protected static function map_row(array $row, ?array $map): array { if ($map === null) return $row; $index = 0; $mapped = []; foreach ($map as $key => $value) { if ($key === $index) { $index++; if ($value === null) $mapped[] = null; else $mapped[$value] = cl::get($row, $value); } elseif (is_callable($value)) { $func = func::with($value); $value = cl::get($row, $key); $mapped[$key] = $func->invoke([$value, $key, $row]); } else { if ($value === null) $mapped[$key] = null; else $mapped[$key] = cl::get($row, $value); } } return $mapped; } /** * tester si $row satisfait les conditions de $filter * - $filter est un scalaire, le transformer en [$filter] * - sinon $filter doit être un tableau de scalaires * * les règles des conditions sont les suivantes: * - une valeur séquentielle $key est équivalente à la valeur associative * $key => true * - une valeur associative $key => bool indique que la clé correspondante ne * doit pas (resp. doit) exister selon que bool vaut false (resp. true) * - une valeur associative $key => $value indique que la clé correspondante * doit exiter avec la valeur spécifiée */ protected static function filter_row(array $row, $filter): bool { if ($filter === null) return false; if (!is_array($filter)) $filter = [$filter]; if (!$filter) return false; $index = 0; foreach ($filter as $key => $value) { if ($key === $index) { $index++; if (!array_key_exists($value, $row)) return false; } elseif (is_bool($value)) { if ($value) { if (!array_key_exists($key, $row)) return false; } else { if (array_key_exists($key, $row)) return false; } } else { if (!array_key_exists($key, $row)) return false; if ($row[$key] !== $value) return false; } } return true; } 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]); } } elseif ($rows instanceof Traversable) { $rowsGenerator = $rows; } else { $rowsFunc = func::with(function() use ($rows) { return $rows; }); } $this->rowsGenerator = $rowsGenerator; $this->rowsFunc = $rowsFunc; $this->cols = $params["cols"] ?? null; $colsFunc = $params["cols_func"] ?? null; if ($colsFunc !== null) $colsFunc = func::with($colsFunc); $this->colsFunc = $colsFunc; $map = $params["map"] ?? null; $mapFunc = $params["map_func"] ?? null; if ($mapFunc !== null) { $mapFunc = func::with($mapFunc); } elseif ($map !== null) { $mapFunc = func::with(function(array $row) use ($map) { return self::map_row($row, $map); }); } $this->mapFunc = $mapFunc; $filter = $params["filter"] ?? null; $filterFunc = $params["filter_func"] ?? null; if ($filterFunc !== null) { $filterFunc = func::with($filterFunc); } elseif ($filter !== null) { $filterFunc = func::with(function(array $row) use ($filter) { return self::filter_row($row, $filter); }); } $this->filterFunc = $filterFunc; } /** un générateur de lignes */ private ?Traversable $rowsGenerator; /** une fonction de signature function(Cursor): ?iterable */ private ?func $rowsFunc; /** une fonction de signature function(Cursor): ?array */ private ?func $colsFunc; /** une fonction de signature function(Cursor): ?array */ private ?func $mapFunc; /** une fonction de signature function(Cursor): bool */ private ?func $filterFunc; protected ?iterable $rows; public ?array $cols; public int $index; public int $origIndex; public $key; public $raw; 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; } protected function cols(): ?array { return $this->row !== null? array_keys($this->row): null; } protected function filter(): bool { return false; } protected function map(): ?array { return $this->row; } ############################################################################# # Iterator function rewind() { $this->cols = null; $this->index = 0; $this->origIndex = 0; $this->key = null; $this->raw = null; $this->row = 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 { $cols = $this->colsFunc; $filter = $this->filterFunc; $map = $this->mapFunc; while ($valid = iter::valid($this->rows)) { $this->raw = iter::current($this->rows, $this->key); $this->key ??= $this->origIndex; $this->row = cl::withn($this->raw); if ($filter === null) $filtered = $this->filter(); else $filtered = $filter->invoke([$this]); if (!$filtered) { if ($map === null) $this->row = $this->map(); else $this->row = $map->invoke([$this]); if ($this->cols === null) { if ($cols === null) $this->cols = $this->cols(); else $this->cols = $cols->invoke([$this]); } break; } else { iter::next($this->rows); $this->origIndex++; } } 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; } 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(); } } }