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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user