améliorer Cursor
This commit is contained in:
parent
a120143794
commit
0c40769619
@ -11,6 +11,10 @@ use Traversable;
|
|||||||
/**
|
/**
|
||||||
* Class Cursor: parcours des lignes itérable
|
* Class Cursor: parcours des lignes itérable
|
||||||
*
|
*
|
||||||
|
* XXX si on spécifie $cols ou $colsFunc, il y a une possibilité que les clés de
|
||||||
|
* $row ne soient pas dans le bon ordre, ou que les clés de $cols ne soient pas
|
||||||
|
* présentes dans $row. ajouter les paramètres ensure_keys et order_keys
|
||||||
|
*
|
||||||
* @property-read array|null $value alias pour $row
|
* @property-read array|null $value alias pour $row
|
||||||
* @property-read iterable|null $rows la source des lignes
|
* @property-read iterable|null $rows la source des lignes
|
||||||
*/
|
*/
|
||||||
@ -18,14 +22,53 @@ class Cursor implements Iterator {
|
|||||||
const PARAMS_SCHEMA = [
|
const PARAMS_SCHEMA = [
|
||||||
"rows" => ["?iterable"],
|
"rows" => ["?iterable"],
|
||||||
"rows_func" => ["?callable"],
|
"rows_func" => ["?callable"],
|
||||||
"cols" => ["?array"],
|
|
||||||
"cols_func" => ["?callable"],
|
|
||||||
"map" => ["?array"],
|
|
||||||
"map_func" => ["?callable"],
|
|
||||||
"filter" => ["?array"],
|
"filter" => ["?array"],
|
||||||
"filter_func" => ["?callable"],
|
"filter_func" => ["?callable"],
|
||||||
|
"map" => ["?array"],
|
||||||
|
"map_func" => ["?callable"],
|
||||||
|
"cols" => ["?array"],
|
||||||
|
"cols_func" => ["?callable"],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
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 ($row === null) return false;
|
||||||
|
if (!array_key_exists($value, $row)) return false;
|
||||||
|
} elseif (is_bool($value)) {
|
||||||
|
if ($value) {
|
||||||
|
if ($row === null || !array_key_exists($key, $row)) return false;
|
||||||
|
} else {
|
||||||
|
if ($row !== null && array_key_exists($key, $row)) return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($row === null) return false;
|
||||||
|
if (!array_key_exists($key, $row)) return false;
|
||||||
|
if ($row[$key] !== $value) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mapper le tableau source $row selon les règles suivantes illustrées dans
|
* mapper le tableau source $row selon les règles suivantes illustrées dans
|
||||||
* l'exemple suivant:
|
* l'exemple suivant:
|
||||||
@ -33,8 +76,11 @@ class Cursor implements Iterator {
|
|||||||
* $map = ["a", "b" => "x", "c" => function() { return "y"; }, "d" => null]
|
* $map = ["a", "b" => "x", "c" => function() { return "y"; }, "d" => null]
|
||||||
* alors retourner le tableau
|
* alors retourner le tableau
|
||||||
* ["a" => $row["a"], "b" => $row["x"], "c" => "y", "d" => null]
|
* ["a" => $row["a"], "b" => $row["x"], "c" => "y", "d" => null]
|
||||||
|
*
|
||||||
|
* si une fonction est utilisée, sa signature est
|
||||||
|
* <code>function(mixed $value, string|int $key, ?array $row)</code>
|
||||||
*/
|
*/
|
||||||
protected static function map_row(array $row, ?array $map): array {
|
static function map_row(?array $row, ?array $map): array {
|
||||||
if ($map === null) return $row;
|
if ($map === null) return $row;
|
||||||
$index = 0;
|
$index = 0;
|
||||||
$mapped = [];
|
$mapped = [];
|
||||||
@ -55,43 +101,6 @@ class Cursor implements Iterator {
|
|||||||
return $mapped;
|
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) {
|
function __construct(?iterable $rows=null, ?array $params=null) {
|
||||||
if ($rows !== null) $params["rows"] = $rows;
|
if ($rows !== null) $params["rows"] = $rows;
|
||||||
|
|
||||||
@ -103,7 +112,7 @@ class Cursor implements Iterator {
|
|||||||
$rowsGenerator = $rowsFunc;
|
$rowsGenerator = $rowsFunc;
|
||||||
$rowsFunc = null;
|
$rowsFunc = null;
|
||||||
} else {
|
} else {
|
||||||
$rowsFunc = func::with($rowsFunc, [$rows]);
|
$rowsFunc = func::with($rowsFunc, [$rows, $this]);
|
||||||
}
|
}
|
||||||
} elseif ($rows instanceof Traversable) {
|
} elseif ($rows instanceof Traversable) {
|
||||||
$rowsGenerator = $rows;
|
$rowsGenerator = $rows;
|
||||||
@ -115,61 +124,95 @@ class Cursor implements Iterator {
|
|||||||
$this->rowsGenerator = $rowsGenerator;
|
$this->rowsGenerator = $rowsGenerator;
|
||||||
$this->rowsFunc = $rowsFunc;
|
$this->rowsFunc = $rowsFunc;
|
||||||
|
|
||||||
$this->cols = $params["cols"] ?? null;
|
$filter = $params["filter"] ?? null;
|
||||||
$colsFunc = $params["cols_func"] ?? null;
|
$filterFunc = $params["filter_func"] ?? null;
|
||||||
if ($colsFunc !== null) $colsFunc = func::with($colsFunc);
|
if ($filterFunc !== null) $this->setFilterFunc($filterFunc);
|
||||||
$this->colsFunc = $colsFunc;
|
elseif ($filter !== null) $this->setFilter($filter);
|
||||||
|
|
||||||
$map = $params["map"] ?? null;
|
$map = $params["map"] ?? null;
|
||||||
$mapFunc = $params["map_func"] ?? null;
|
$mapFunc = $params["map_func"] ?? null;
|
||||||
if ($mapFunc !== null) {
|
if ($mapFunc !== null) $this->setMapFunc($mapFunc);
|
||||||
$mapFunc = func::with($mapFunc);
|
elseif ($map !== null) $this->setMap($map);
|
||||||
} elseif ($map !== null) {
|
|
||||||
$mapFunc = func::with(function(array $row) use ($map) {
|
|
||||||
return self::map_row($row, $map);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$this->mapFunc = $mapFunc;
|
|
||||||
|
|
||||||
$filter = $params["filter"] ?? null;
|
$this->cols = $params["cols"] ?? null;
|
||||||
$filterFunc = $params["filter_func"] ?? null;
|
$this->setColsFunc($params["cols_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 */
|
/** un générateur de lignes */
|
||||||
private ?Traversable $rowsGenerator;
|
private ?Traversable $rowsGenerator;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor): ?iterable</code> */
|
/** une fonction de signature <code>function(mixed $rows, Cursor): ?iterable</code> */
|
||||||
private ?func $rowsFunc;
|
private ?func $rowsFunc;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor): ?array</code> */
|
/** une fonction de signature <code>function(?array $row, Cursor): bool</code> */
|
||||||
private ?func $colsFunc;
|
private ?func $filterFunc = null;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor): ?array</code> */
|
function setFilter(array $filter): self {
|
||||||
private ?func $mapFunc;
|
$this->filterFunc = func::with(function(?array $row) use ($filter) {
|
||||||
|
return self::filter_row($row, $filter);
|
||||||
|
});
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor): bool</code> */
|
function setFilterFunc(?callable $func): self {
|
||||||
private ?func $filterFunc;
|
if ($func === null) $this->filterFunc = null;
|
||||||
|
else $this->filterFunc = func::with($func)->bind($this);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** une fonction de signature <code>function(?array $row, Cursor): ?array</code> */
|
||||||
|
private ?func $mapFunc = null;
|
||||||
|
|
||||||
|
function setMap(array $map): self {
|
||||||
|
$this->mapFunc = func::with(function(?array $row) use ($map) {
|
||||||
|
return self::map_row($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 <code>function(?array $row, Cursor): ?array</code> */
|
||||||
|
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;
|
protected ?iterable $rows;
|
||||||
|
|
||||||
|
/** @var array|null listes des colonnes de chaque enregistrement */
|
||||||
public ?array $cols;
|
public ?array $cols;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int index de l'enregistrement (en ne comptant pas les éléments filtrés)
|
||||||
|
*/
|
||||||
public int $index;
|
public int $index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int index original de l'enregistrement (en tenant compte des éléments
|
||||||
|
* filtrés)
|
||||||
|
*/
|
||||||
public int $origIndex;
|
public int $origIndex;
|
||||||
|
|
||||||
|
/** @var string|int clé de l'enregistrement */
|
||||||
public $key;
|
public $key;
|
||||||
|
|
||||||
|
/** @var mixed élément original récupéré depuis la source */
|
||||||
public $raw;
|
public $raw;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array|null enregistrement après conversion en tableau et application
|
||||||
|
* du mapping
|
||||||
|
*/
|
||||||
public ?array $row;
|
public ?array $row;
|
||||||
|
|
||||||
function __get($name) {
|
function __get($name) {
|
||||||
@ -179,18 +222,22 @@ class Cursor implements Iterator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function cols(): ?array {
|
protected function convertToRow($raw): ?array {
|
||||||
return $this->row !== null? array_keys($this->row): null;
|
return cl::withn($raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function filter(): bool {
|
protected function filterFunc(?array $row): bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function map(): ?array {
|
protected function mapFunc(?array $row): ?array {
|
||||||
return $this->row;
|
return $this->row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function colsFunc(?array $row): ?array {
|
||||||
|
return $this->row !== null? array_keys($this->row): null;
|
||||||
|
}
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
# Iterator
|
# Iterator
|
||||||
|
|
||||||
@ -212,26 +259,30 @@ class Cursor implements Iterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function valid(): bool {
|
function valid(): bool {
|
||||||
$cols = $this->colsFunc;
|
$colsFunc = $this->colsFunc;
|
||||||
$filter = $this->filterFunc;
|
$filterFunc = $this->filterFunc;
|
||||||
$map = $this->mapFunc;
|
$mapFunc = $this->mapFunc;
|
||||||
while ($valid = iter::valid($this->rows)) {
|
while ($valid = iter::valid($this->rows)) {
|
||||||
$this->raw = iter::current($this->rows, $this->key);
|
$this->raw = iter::current($this->rows, $this->key);
|
||||||
|
# conversion en enregistrement
|
||||||
$this->key ??= $this->origIndex;
|
$this->key ??= $this->origIndex;
|
||||||
$this->row = cl::withn($this->raw);
|
$this->row = $this->convertToRow($this->raw);
|
||||||
if ($filter === null) $filtered = $this->filter();
|
# filtrage
|
||||||
else $filtered = $filter->invoke([$this]);
|
if ($filterFunc === null) $filtered = $this->filterFunc($this->row);
|
||||||
if (!$filtered) {
|
else $filtered = $filterFunc->invoke([$this->row, $this]);
|
||||||
if ($map === null) $this->row = $this->map();
|
if ($filtered) {
|
||||||
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);
|
iter::next($this->rows);
|
||||||
$this->origIndex++;
|
$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) {
|
if (!$valid) {
|
||||||
|
Loading…
Reference in New Issue
Block a user