1568 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			1568 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nur;
 | |
| 
 | |
| use ArrayAccess;
 | |
| use nur\b\coll\BaseArray;
 | |
| use nur\b\coll\Flattener;
 | |
| use nur\b\coll\IArray;
 | |
| use Traversable;
 | |
| 
 | |
| /**
 | |
|  * Class A: méthodes utilitaires pour gérer les array
 | |
|  */
 | |
| class A {
 | |
|   static final function with($array): array { return SL::with($array); }
 | |
|   static final function withn($array): ?array { return SL::withn($array); }
 | |
| 
 | |
|   /**
 | |
|    * tester si $array est un tableau de type array, BaseArray ou Traversable.
 | |
|    * tous ces objets peuvent être transformés en array par {@link with()}
 | |
|    *
 | |
|    * pour être sûr de pouvoir les parcourir en lecture seule, il faut les
 | |
|    * transformer en array avec {@link with()}
 | |
|    */
 | |
|   static final function is_array($array): bool {
 | |
|     if ($array === null || $array === false) return false;
 | |
|     return is_iterable($array) || $array instanceof BaseArray;
 | |
|   }
 | |
| 
 | |
|   static final function ensure_array(&$array): bool { return SL::ensure_array($array); }
 | |
|   static final function ensure_narray(&$array): bool { return SL::ensure_narray($array); }
 | |
| 
 | |
|   /**
 | |
|    * retourner une référence permettant de modifier $array en tant qu'array de
 | |
|    * préférence ou en tant qu'instance de ArrayAccess au pire.
 | |
|    */
 | |
|   static final function &ensure_access(&$array) {
 | |
|     if (is_array($array)) return $array;
 | |
|     if ($array instanceof IArray) return $array->array();
 | |
|     if ($array instanceof ArrayAccess) return $array;
 | |
|     if ($array instanceof Traversable) $array = iterator_to_array($array);
 | |
|     elseif ($array === null || $array === false) $array = [];
 | |
|     else $array = [$array];
 | |
|     return $array;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * s'assurer que $array est un tableau de $size éléments, en complétant avec
 | |
|    * des occurrences de $default si nécessaire
 | |
|    *
 | |
|    * @return bool true si le tableau a été modifié, false sinon
 | |
|    */
 | |
|   static final function ensure_size(?array &$array, int $size, $default=null): bool {
 | |
|     $modified = false;
 | |
|     if ($array === null) {
 | |
|       $array = [];
 | |
|       $modified = true;
 | |
|     }
 | |
|     if ($size < 0) return $modified;
 | |
|     $count = count($array);
 | |
|     if ($count == $size) return $modified;
 | |
|     if ($count < $size) {
 | |
|       # agrandir le tableau
 | |
|       while ($count++ < $size) {
 | |
|         $array[] = $default;
 | |
|       }
 | |
|       return true;
 | |
|     }
 | |
|     # rétrécir le tableau
 | |
|     $tmparray = [];
 | |
|     foreach ($array as $key => $value) {
 | |
|       if ($size-- == 0) break;
 | |
|       $tmparray[$key] = $value;
 | |
|     }
 | |
|     $array = $tmparray;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   /** tester si $array contient la clé $key */
 | |
|   static final function has(?array $array, $key): bool {
 | |
|     return $array !== null && array_key_exists($key, $array);
 | |
|   }
 | |
| 
 | |
|   /** retourner $array[$key] ou $default si la clé n'existe pas */
 | |
|   static final function get(?array $array, $key, $default=null) {
 | |
|     if ($array === null) return $default;
 | |
|     elseif (array_key_exists($key, $array)) return $array[$key];
 | |
|     else return $default;
 | |
|   }
 | |
| 
 | |
|   /** s'assurer que $array est un array puis spécifier $array[$key] */
 | |
|   static final function set(&$array, $key, $value): void {
 | |
|     self::ensure_array($array);
 | |
|     if ($key === null) $array[] = $value;
 | |
|     else $array[$key] = $value;
 | |
|   }
 | |
| 
 | |
|   /** s'assurer que $array est un array puis supprimer $array[$key] */
 | |
|   static final function del(&$array, $key): void {
 | |
|     if ($array === null || $array === false) return;
 | |
|     self::ensure_array($array);
 | |
|     unset($array[$key]);
 | |
|   }
 | |
| 
 | |
|   /** retourner le nombre d'éléments de $array */
 | |
|   static final function count(?array $array): int {
 | |
|     return $array === null? 0: count($array);
 | |
|   }
 | |
| 
 | |
|   /** retourner la liste des clés de $array */
 | |
|   static final function keys(?array $array): array {
 | |
|     return $array === null? []: array_keys($array);
 | |
|   }
 | |
| 
 | |
|   static final function join(?array $array, string $sep=" "): string {
 | |
|     return $array === null? "": implode($sep, $array);
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
| 
 | |
|   /**
 | |
|    * tester si $array est un tableau séquentiel.
 | |
|    *
 | |
|    * NB: un tableau vide est séquentiel
 | |
|    */
 | |
|   static final function is_seq($array): bool {
 | |
|     if (!is_array($array)) return false;
 | |
|     $count = count($array);
 | |
|     if ($count == 0) return true;
 | |
|     return array_keys($array) === range(0, $count - 1);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * tester si $array est un tableau associatif.
 | |
|    *
 | |
|    * NB: un tableau vide est associatif
 | |
|    */
 | |
|   static final function is_assoc($array): bool {
 | |
|     if (!is_array($array)) return false;
 | |
|     $count = count($array);
 | |
|     if ($count == 0) return true;
 | |
|     return array_keys($array) !== range(0, $count - 1);
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
| 
 | |
|   /**
 | |
|    * s'assurer que $array est un array puis y fusionner tous les autres tableaux
 | |
|    * avec array_merge(). les clés numériques sont réordonnées, et les clés du
 | |
|    * tableau destination sont écrasées par les clés correspondantes des tableaux
 | |
|    * entrants. par exemple:
 | |
|    * ~~~
 | |
|    * $a = [10 => "a", 15 => "b",            "x" => "y"];
 | |
|    * $b = [           15 => "c", 20 => "d", "x" => "z"];
 | |
|    * A::merge($a, $b);
 | |
|    * # $a vaut maintenant [0 => "a", 1 => "b", "x" => "z", 2 => "c", 3 => "d"]
 | |
|    * ~~~
 | |
|    *
 | |
|    * parmi les tableaux de $arrays, ignorer les occurrences de null et false
 | |
|    *
 | |
|    * NB: dans $arrays, les valeurs scalaires sont traitées comme un singleton
 | |
|    * [$value]. elles sont donc simplement ajoutées à $array comme avec la
 | |
|    * commande "$array[] = $value;"
 | |
|    */
 | |
|   static final function merge(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     if ($arrays) {
 | |
|       $merges = [$array];
 | |
|       foreach ($arrays as $merge) {
 | |
|         if ($merge === null || $merge === false) continue;
 | |
|         $merges[] = self::with($merge);
 | |
|       }
 | |
|       $array = array_merge(...$merges);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * s'assurer que $array est un array puis y fusionner tous les autres tableaux
 | |
|    * comme avec {@link merge()} mais sans réordonner les clés numériques. Cela
 | |
|    * ressemble au comportement de l'opérateur union de PHP, mais la différence
 | |
|    * est que les clés du tableau destination sont écrasées par les clés
 | |
|    * correspondantes des tableaux entrants. par exemple:
 | |
|    * ~~~
 | |
|    * $a = [10 => "a", 15 => "b",            "x" => "y"];
 | |
|    * $b = [           15 => "c", 20 => "d", "x" => "z"];
 | |
|    * A::merge2($a, $b);
 | |
|    * # $a vaut maintenant [10 => "a", 15 => "c", "x" => "z", 20 => "d"]
 | |
|    * ~~~
 | |
|    *
 | |
|    * parmi les tableaux de $arrays, ignorer les occurrences de null et false
 | |
|    *
 | |
|    * NB: dans $arrays, les valeurs scalaires sont traitées comme un singleton
 | |
|    * [$value]. elles écrasent donc la valeur à la clé '0' si celle-ci existe
 | |
|    * déjà dans le tableau destination
 | |
|    */
 | |
|   static final function merge2(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     foreach ($arrays as $merge) {
 | |
|       if ($merge === null || $merge === false) continue;
 | |
|       foreach (self::with($merge) as $key => $value) {
 | |
|         $array[$key] = $value;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * s'assurer que $array est un array puis y fusionner tous les autres tableaux
 | |
|    * un peu comme avec {@link merge()} mais en ne réordonnant *que* les clés
 | |
|    * séquentielles. par exemple:
 | |
|    * ~~~
 | |
|    * $a = ["1st", "2nd",               10 => "a", 15 => "b",            "x" => "y"];
 | |
|    * $b = [              "3rd", "4th",            15 => "c", 20 => "d", "x" => "z"];
 | |
|    * A::merge3($a, $b);
 | |
|    * # $a vaut maintenant ["1st", "2nd", 10 => "a", 15 => "c", "x" => "z", "3rd", "4th", 20 => "d"]
 | |
|    * ~~~
 | |
|    *
 | |
|    * parmi les tableaux de $arrays, ignorer les occurrences de null et false
 | |
|    */
 | |
|   static final function merge3(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     $desti = 0;
 | |
|     foreach ($array as $key => $value) {
 | |
|       if ($key === $desti) {
 | |
|         $desti++;
 | |
|       }
 | |
|     }
 | |
|     foreach ($arrays as $merge) {
 | |
|       $srci = 0;
 | |
|       if ($merge === null || $merge === false) continue;
 | |
|       foreach (self::with($merge) as $key => $value) {
 | |
|         if ($key === $srci) {
 | |
|           $srci++;
 | |
|           $array[$desti++] = $value;
 | |
|         } else {
 | |
|           $array[$key] = $value;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge()} mais dans chacun des tableaux sources, ceux de
 | |
|    * $arrays, ignorer les valeurs null.
 | |
|    */
 | |
|   static final function merge_nn(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     if ($arrays) {
 | |
|       $merges = [$array];
 | |
|       foreach ($arrays as $tmp) {
 | |
|         if ($tmp === false || $tmp === null) continue;
 | |
|         $merge = [];
 | |
|         foreach (self::with($tmp) as $key => $value) {
 | |
|           if ($value === null) continue;
 | |
|           $merge[$key] = $value;
 | |
|         }
 | |
|         $merges[] = $merge;
 | |
|       }
 | |
|       $array = array_merge(...$merges);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge2()} mais dans chacun des tableaux sources, ceux de
 | |
|    * $arrays, ignorer les valeurs null.
 | |
|    */
 | |
|   static final function merge_nn2(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     foreach ($arrays as $merge) {
 | |
|       if ($merge === false || $merge === null) continue;
 | |
|       foreach (self::with($merge) as $key => $value) {
 | |
|         if ($value === null) continue;
 | |
|         $array[$key] = $value;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge()} mais dans chacun des tableaux sources, ceux de
 | |
|    * $arrays, ignorer les valeurs null et false.
 | |
|    */
 | |
|   static final function merge_nz(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     if ($arrays) {
 | |
|       $merges = [$array];
 | |
|       foreach ($arrays as $tmp) {
 | |
|         if ($tmp === false || $tmp === null) continue;
 | |
|         $merge = [];
 | |
|         foreach (self::with($tmp) as $key => $value) {
 | |
|           if ($value === null || $value === false) continue;
 | |
|           $merge[$key] = $value;
 | |
|         }
 | |
|         $merges[] = $merge;
 | |
|       }
 | |
|       $array = array_merge(...$merges);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge2()} mais dans chacun des tableaux sources, ceux de
 | |
|    * $arrays, ignorer les valeurs null et false.
 | |
|    */
 | |
|   static final function merge_nz2(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     foreach ($arrays as $merge) {
 | |
|       if ($merge === false || $merge === null) continue;
 | |
|       foreach (self::with($merge) as $key => $value) {
 | |
|         if ($value === null || $value === false) continue;
 | |
|         $array[$key] = $value;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge()} mais pour chacun des tableaux sources, ceux de
 | |
|    * $arrays, ne fusionner les valeurs dans la destination $array que si la clé
 | |
|    * n'y existe pas
 | |
|    */
 | |
|   static final function update_nx(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     if ($arrays) {
 | |
|       $updates = [$array];
 | |
|       foreach ($arrays as $tmp) {
 | |
|         if ($tmp === false || $tmp === null) continue;
 | |
|         $update = [];
 | |
|         foreach (self::with($tmp) as $key => $value) {
 | |
|           if (!array_key_exists($key, $array)) {
 | |
|             $update[$key] = $value;
 | |
|           }
 | |
|         }
 | |
|         $updates[] = $update;
 | |
|       }
 | |
|       $array = array_merge(...$updates);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge2()} mais pour chacun des tableaux sources, ceux de
 | |
|    * $arrays, ne fusionner les valeurs dans la destination $array que si la clé
 | |
|    * n'y existe pas
 | |
|    *
 | |
|    * NB: il s'agit de la définition du l'opérateur union. l'implémentation fait
 | |
|    * d'ailleurs usage de cet opérateur
 | |
|    */
 | |
|   static final function update_nx2(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     foreach ($arrays as $update) {
 | |
|       if ($update === null || $update === false) continue;
 | |
|       $array += self::with($update);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge()} mais pour chacun des tableaux sources, ceux de
 | |
|    * $arrays, ne fusionner les valeurs dans la destination $array que si la clé
 | |
|    * n'y existe pas ou, si elle y existe, que la valeur y est null
 | |
|    */
 | |
|   static final function update_n(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     if ($arrays) {
 | |
|       $updates = [$array];
 | |
|       foreach ($arrays as $tmp) {
 | |
|         if ($tmp === false || $tmp === null) continue;
 | |
|         $update = [];
 | |
|         foreach (self::with($tmp) as $key => $value) {
 | |
|           if (!array_key_exists($key, $array)) {
 | |
|             $update[$key] = $value;
 | |
|           } elseif ($array[$key] === null) {
 | |
|             $update[$key] = $value;
 | |
|           }
 | |
|         }
 | |
|         $updates[] = $update;
 | |
|       }
 | |
|       $array = array_merge(...$updates);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge2()} mais pour chacun des tableaux sources, ceux de
 | |
|    * $arrays, ne fusionner les valeurs dans la destination $array que si la clé
 | |
|    * n'y existe pas ou, si elle y existe, que la valeur y est null
 | |
|    */
 | |
|   static final function update_n2(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     foreach ($arrays as $update) {
 | |
|       if ($update === false || $update === null) continue;
 | |
|       foreach (self::with($update) as $key => $value) {
 | |
|         if (!array_key_exists($key, $array)) {
 | |
|           $array[$key] = $value;
 | |
|         } elseif ($array[$key] === null) {
 | |
|           $array[$key] = $value;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge()} mais pour chacun des tableaux sources, ceux de
 | |
|    * $arrays, ne fusionner les valeurs dans la destination $array que si la clé
 | |
|    * n'y existe pas ou, si elle y existe, que la valeur y est null ou false
 | |
|    */
 | |
|   static final function update_z(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     if ($arrays) {
 | |
|       $updates = [$array];
 | |
|       foreach ($arrays as $tmp) {
 | |
|         if ($tmp === false || $tmp === null) continue;
 | |
|         $update = [];
 | |
|         foreach (self::with($tmp) as $key => $value) {
 | |
|           if (!array_key_exists($key, $array)) {
 | |
|             $update[$key] = $value;
 | |
|           } else {
 | |
|             $rvalue = $array[$key];
 | |
|             if ($rvalue === null || $rvalue === false) {
 | |
|               $update[$key] = $value;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         $updates[] = $update;
 | |
|       }
 | |
|       $array = array_merge(...$updates);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link merge2()} mais pour chacun des tableaux sources, ceux de
 | |
|    * $arrays, ne fusionner les valeurs dans la destination $array que si la clé
 | |
|    * n'y existe pas ou, si elle y existe, que la valeur y est null ou false
 | |
|    */
 | |
|   static final function update_z2(&$array, ...$arrays): void {
 | |
|     self::ensure_array($array);
 | |
|     foreach ($arrays as $tomerge) {
 | |
|       if ($tomerge === false || $tomerge === null) continue;
 | |
|       foreach (self::with($tomerge) as $key => $value) {
 | |
|         if (!array_key_exists($key, $array)) {
 | |
|           $array[$key] = $value;
 | |
|         } else {
 | |
|           $rvalue = $array[$key];
 | |
|           if ($rvalue === null || $rvalue === false) {
 | |
|             $array[$key] = $value;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
|   ## get
 | |
| 
 | |
|   /** obtenir le premier élément du tableau */
 | |
|   static final function first(?array $array, $default=null) {
 | |
|     if (!$array) return $default;
 | |
|     else return $array[array_key_first($array)];
 | |
|   }
 | |
| 
 | |
|   /** obtenir le second élément du tableau */
 | |
|   static final function second(?array $array, $default=null) {
 | |
|     if (!$array) return $default;
 | |
|     $first = true;
 | |
|     foreach ($array as $value) {
 | |
|       if ($first) $first = false;
 | |
|       else return $value;
 | |
|     }
 | |
|     return $default;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * obtenir le n-ième élément du tableau, en commençant à 0 (i.e 0=premier,
 | |
|    * 1=second, etc.)
 | |
|    */
 | |
|   static final function nth(?array $array, int $index, $default=null) {
 | |
|     if (!$array) return $default;
 | |
|     $i = 0;
 | |
|     foreach ($array as $value) {
 | |
|       if ($index == $i) return $value;
 | |
|       $i++;
 | |
|     }
 | |
|     return $default;
 | |
|   }
 | |
| 
 | |
|   /** obtenir la première clé du tableau */
 | |
|   static final function first_key(?array $array, $default_key=null) {
 | |
|     if (!$array) return $default_key;
 | |
|     else return array_key_first($array);
 | |
|   }
 | |
| 
 | |
|   /** obtenir la seconde clé du tableau */
 | |
|   static final function second_key(?array $array, $default_key=null) {
 | |
|     if (!$array) return $default_key;
 | |
|     $first = true;
 | |
|     foreach (array_keys($array) as $key) {
 | |
|       if ($first) $first = false;
 | |
|       else return $key;
 | |
|     }
 | |
|     return $default_key;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * obtenir la clé du n-ième élément du tableau, en commençant à 0 (i.e
 | |
|    * 0=premier, 1=second, etc.)
 | |
|    */
 | |
|   static final function nth_key(?array $array, int $index, $default_key=null) {
 | |
|     if (!$array) return $default_key;
 | |
|     $i = 0;
 | |
|     foreach (array_keys($array) as $key) {
 | |
|       if ($index == $i) return $key;
 | |
|       $i++;
 | |
|     }
 | |
|     return $default_key;
 | |
|   }
 | |
| 
 | |
|   /** obtenir le dernier élément du tableau */
 | |
|   static final function last(?array $array, $default=null) {
 | |
|     if (!$array) return $default;
 | |
|     else return $array[array_key_last($array)];
 | |
|   }
 | |
| 
 | |
|   /** obtenir la dernière clé du tableau */
 | |
|   static final function last_key(?array $array, $default_key=null) {
 | |
|     if (!$array) return $default_key;
 | |
|     else return array_key_last($array);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * si $array est un tableau séquentiel avec un seul élément, retourner cet élément.
 | |
|    * si c'est un tableau vide ou null retourner $default
 | |
|    * sinon retourner le tableau inchangé
 | |
|    */
 | |
|   static final function one_or_array(?array $array, $default=null) {
 | |
|     if (!$array) {
 | |
|       return $default;
 | |
|     } elseif (count($array) == 1 && array_key_exists(0, $array)) {
 | |
|       return $array[0];
 | |
|     } else {
 | |
|       return $array;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * retourner un tableau avec pour chaque tableau $array de $arrays, la valeur
 | |
|    * de get($array, $key, $default)
 | |
|    */
 | |
|   static final function each_get(?array $arrays, $key, $default=null): array {
 | |
|     $values = [];
 | |
|     foreach ($arrays as $index => $array) {
 | |
|       $values[$index] = self::get($array, $key, $default);
 | |
|     }
 | |
|     return $values;
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
|   ## set
 | |
| 
 | |
|   /**
 | |
|    * Mettre à jour la clé $key avec $value si $value !== null
 | |
|    *
 | |
|    * si aucune modification ne doit se faire, $array n'est pas modifié
 | |
|    *
 | |
|    * Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
 | |
|    * non)
 | |
|    */
 | |
|   static final function set_nn(&$array, $key, $value) {
 | |
|     if ($value !== null) {
 | |
|       self::ensure_array($array);
 | |
|       self::set($array, $key, $value);
 | |
|       return $value;
 | |
|     } else {
 | |
|       return self::get($array, $key);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Mettre à jour la clé $key avec $array[$key_indirect] si cette valeur ne
 | |
|    * vaut pas null
 | |
|    *
 | |
|    * Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
 | |
|    * non)
 | |
|    */
 | |
|   static final function set_nn_indirect(&$array, $key, $key_indirect) {
 | |
|     self::ensure_narray($array);
 | |
|     $value = self::get($array, $key_indirect);
 | |
|     return self::set_nn($array, $key, $value);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Mettre à jour la clé $key avec $value si $value ne vaut ni null ni false
 | |
|    *
 | |
|    * si aucune modification ne doit se faire, $array n'est pas modifié
 | |
|    *
 | |
|    * Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
 | |
|    * non)
 | |
|    */
 | |
|   static final function set_nz(&$array, $key, $value) {
 | |
|     if ($value !== false && $value !== null) {
 | |
|       self::set($array, $key, $value);
 | |
|       return $value;
 | |
|     } else {
 | |
|       return self::get($array, $key);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Mettre à jour la clé $key avec $array[$key_indirect] si cette valeur ne
 | |
|    * vaut ni null ni false
 | |
|    *
 | |
|    * Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
 | |
|    * non)
 | |
|    */
 | |
|   static final function set_nz_indirect(&$array, $key, $key_indirect) {
 | |
|     self::ensure_narray($array);
 | |
|     $value = self::get($array, $key_indirect);
 | |
|     return self::set_nz($array, $key, $value);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Ajouter la valeur avec la clé spécifiée si elle n'existe pas déjà
 | |
|    *
 | |
|    * si aucune modification ne doit se faire, $array n'est pas modifié
 | |
|    *
 | |
|    * Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
 | |
|    * non)
 | |
|    */
 | |
|   static final function replace_nx(&$array, $key, $value) {
 | |
|     if (!is_array($array) || !array_key_exists($key, $array)) {
 | |
|       self::set($array, $key, $value);
 | |
|       return $value;
 | |
|     } else {
 | |
|       return self::get($array, $key);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Mettre à jour la clé $key avec $value si la clé $key n'existe pas ou si sa
 | |
|    * valeur actuelle est null.
 | |
|    *
 | |
|    * Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
 | |
|    * non)
 | |
|    */
 | |
|   static final function replace_n(&$array, $key, $value) {
 | |
|     self::ensure_narray($array);
 | |
|     $rvalue = self::get($array, $key);
 | |
|     if ($rvalue === null) {
 | |
|       self::set($array, $key, $value);
 | |
|       $rvalue = $value;
 | |
|     }
 | |
|     return $rvalue;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Mettre à jour la clé $key avec $array[$key_indirect] si cette clé n'existe
 | |
|    * pas ou si sa valeur actuelle est null.
 | |
|    *
 | |
|    * Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
 | |
|    * non)
 | |
|    */
 | |
|   static final function replace_n_indirect(&$array, $key, $key_indirect) {
 | |
|     self::ensure_narray($array);
 | |
|     $rvalue = self::get($array, $key);
 | |
|     if ($rvalue === null) {
 | |
|       $value = self::get($array, $key_indirect);
 | |
|       self::set($array, $key, $value);
 | |
|       $rvalue = $value;
 | |
|     }
 | |
|     return $rvalue;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Mettre à jour la clé $key avec $value si la clé $key n'existe pas ou si sa
 | |
|    * valeur actuelle est null ou false.
 | |
|    *
 | |
|    * Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
 | |
|    * non)
 | |
|    */
 | |
|   static final function replace_z(&$array, $key, $value) {
 | |
|     self::ensure_narray($array);
 | |
|     $rvalue = self::get($array, $key);
 | |
|     if ($rvalue === false || $rvalue === null) {
 | |
|       self::set($array, $key, $value);
 | |
|       $rvalue = $value;
 | |
|     }
 | |
|     return $rvalue;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Mettre à jour la clé $key avec $array[$key_indirect] si cette clé n'existe
 | |
|    * pas ou si sa valeur actuelle est null ou false.
 | |
|    *
 | |
|    * Retourner la valeur effective de la clé (qu'elle aie été mise à jour ou
 | |
|    * non)
 | |
|    */
 | |
|   static final function replace_z_indirect(&$array, $key, $key_indirect) {
 | |
|     self::ensure_narray($array);
 | |
|     $rvalue = self::get($array, $key);
 | |
|     if ($rvalue === false || $rvalue === null) {
 | |
|       $value = self::get($array, $key_indirect);
 | |
|       self::set($array, $key, $value);
 | |
|       $rvalue = $value;
 | |
|     }
 | |
|     return $rvalue;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Ajouter $value à la fin de $array
 | |
|    *
 | |
|    * Retourner la valeur ajoutée
 | |
|    */
 | |
|   static final function append(&$array, $value) {
 | |
|     self::ensure_array($array);
 | |
|     $array[] = $value;
 | |
|     return $value;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Si $value n'est pas null, ajouter $value à la fin de $array
 | |
|    *
 | |
|    * si aucune modification ne doit se faire, $array n'est pas modifié
 | |
|    *
 | |
|    * Retourner la valeur ajoutée, ou null si la valeur n'a pas été ajoutée
 | |
|    */
 | |
|   static final function append_nn(&$array, $value) {
 | |
|     if ($value !== null) {
 | |
|       self::ensure_array($array);
 | |
|       $array[] = $value;
 | |
|       return $value;
 | |
|     } else {
 | |
|       return null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Si $value ne vaut ni null ni false, ajouter $value à la fin de $array
 | |
|    *
 | |
|    * si aucune modification ne doit se faire, $array n'est pas modifié
 | |
|    *
 | |
|    * Retourner la valeur ajoutée, ou null si la valeur n'a pas été ajoutée
 | |
|    */
 | |
|   static final function append_nz(&$array, $value) {
 | |
|     if (base::nz($value)) {
 | |
|       self::ensure_array($array);
 | |
|       $array[] = $value;
 | |
|       return $value;
 | |
|     } else {
 | |
|       return null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Insérer $value au début de $array
 | |
|    *
 | |
|    * Retourner la valeur insérée
 | |
|    */
 | |
|   static final function prepend(&$array, $value) {
 | |
|     self::ensure_array($array);
 | |
|     array_unshift($array, $value);
 | |
|     return $value;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Si $value n'est pas null, insérer $value au début de $array
 | |
|    *
 | |
|    * si aucune modification ne doit se faire, $array n'est pas modifié
 | |
|    *
 | |
|    * Retourner la valeur ajoutée, ou null si la valeur n'a pas été ajoutée
 | |
|    */
 | |
|   static final function prepend_nn(&$array, $value) {
 | |
|     if ($value !== null) {
 | |
|       self::ensure_array($array);
 | |
|       array_unshift($array, $value);
 | |
|       return $value;
 | |
|     } else {
 | |
|       return null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Si $value ne vaut ni null ni false, insérer $value au début de $array
 | |
|    *
 | |
|    * si aucune modification ne doit se faire, $array n'est pas modifié
 | |
|    *
 | |
|    * Retourner la valeur ajoutée, ou null si la valeur n'a pas été ajoutée
 | |
|    */
 | |
|   static final function prepend_nz(&$array, $value) {
 | |
|     if (base::nz($value)) {
 | |
|       self::ensure_array($array);
 | |
|       array_unshift($array, $value);
 | |
|       return $value;
 | |
|     } else {
 | |
|       return null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * dans le tableau séquentiel $array, insérer $value à la position $index et
 | |
|    * décaler toutes les autres clés
 | |
|    *
 | |
|    * Retourner la valeur insérée
 | |
|    */
 | |
|   static final function insert(&$array, $index, $value) {
 | |
|     self::ensure_array($array);
 | |
|     $count = count($array);
 | |
|     if ($count == 0 || $index >= $count) {
 | |
|       $array[] = $value;
 | |
|     } else {
 | |
|       while ($index < 0) $index += $count;
 | |
|       if ($index == 0) $prefix = [];
 | |
|       else $prefix = array_slice($array, 0, $index);
 | |
|       $suffix = array_slice($array, $index);
 | |
|       $array = array_merge($prefix, [$value], $suffix);
 | |
|     }
 | |
|     return $value;
 | |
|   }
 | |
| 
 | |
|   /** Ajouter une valeur dans le tableau, pour utilisation avec pop() */
 | |
|   static final function push(&$array, $value): void {
 | |
|     self::ensure_array($array);
 | |
|     $array[] = $value;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * enlever la dernière valeur ajoutée dans le tableau avec push() et la
 | |
|    * retourner
 | |
|    */
 | |
|   static final function pop(&$array, $default=null) {
 | |
|     self::ensure_array($array);
 | |
|     $count = count($array);
 | |
|     if ($count > 0) {
 | |
|       $value = array_pop($array);
 | |
|     } else {
 | |
|       $value = $default;
 | |
|     }
 | |
|     return $value;
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
|   ## del
 | |
| 
 | |
|   static final function del_value(&$array, $value, int $max_count=1, bool $strict=false): int {
 | |
|     if ($array === null || $array === false) return 0;
 | |
|     self::ensure_array($array);
 | |
|     $count = 0;
 | |
|     $rekey = null;
 | |
|     while ($max_count <= 0 || $count < $max_count) {
 | |
|       $key = array_search($value, $array, $strict);
 | |
|       if ($key === false) break;
 | |
|       # s'il faut supprimer des clés, vérifier d'abord si c'est un tableau
 | |
|       # séquentiel, afin de refaire la numérotation le cas échéant
 | |
|       if ($rekey === null) $rekey = self::is_seq($array);
 | |
|       unset($array[$key]);
 | |
|       $count++;
 | |
|     }
 | |
|     if ($rekey) $array = array_values($array);
 | |
|     return $count;
 | |
|   }
 | |
| 
 | |
|   static final function del_first_key(&$array, int $max_count=1): int {
 | |
|     if ($array === null || $array === false) return 0;
 | |
|     self::ensure_array($array);
 | |
|     $count = 0;
 | |
|     $rekey = null;
 | |
|     while ($max_count <= 0 || $count < $max_count) {
 | |
|       $key = array_key_first($array);
 | |
|       if ($key === null) break;
 | |
|       # s'il faut supprimer des clés, vérifier d'abord si c'est un tableau
 | |
|       # séquentiel, afin de refaire la numérotation le cas échéant
 | |
|       if ($rekey === null) $rekey = self::is_seq($array);
 | |
|       unset($array[$key]);
 | |
|       $count++;
 | |
|     }
 | |
|     if ($rekey) $array = array_values($array);
 | |
|     return $count;
 | |
|   }
 | |
| 
 | |
|   static final function del_last_key(&$array, int $max_count=1): int {
 | |
|     if ($array === null || $array === false) return 0;
 | |
|     self::ensure_array($array);
 | |
|     $count = 0;
 | |
|     while ($max_count <= 0 || $count < $max_count) {
 | |
|       $key = array_key_last($array);
 | |
|       if ($key === null) break;
 | |
|       unset($array[$key]);
 | |
|       $count++;
 | |
|     }
 | |
|     return $count;
 | |
|   }
 | |
| 
 | |
|   static final function del_keys(&$array, ...$keys): void {
 | |
|     if ($array === null || $array === false) return;
 | |
|     self::ensure_array($array);
 | |
|     foreach ($keys as $key) {
 | |
|       unset($array[$key]);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
|   ## getdel
 | |
| 
 | |
|   /**
 | |
|    * obtenir la valeur correspondant à la clé $key, ou $default si elle n'est
 | |
|    * pas trouvée. puis supprimer la clé du tableau
 | |
|    */
 | |
|   static final function getdel(&$array, $key, $default=null) {
 | |
|     self::ensure_array($array);
 | |
|     $value = self::get($array, $key, $default);
 | |
|     unset($array[$key]);
 | |
|     return $value;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * obtenir les valeurs correspondantes aux clés $keys sous forme de tableau
 | |
|    * séquentiel. supprimer les clés du tableau
 | |
|    */
 | |
|   static final function getdels(&$array, ?array $keys): array {
 | |
|     self::ensure_array($array);
 | |
|     $values = [];
 | |
|     if ($keys !== null) {
 | |
|       foreach ($keys as $key) {
 | |
|         $values[] = self::get($array, $key);
 | |
|         unset($array[$key]);
 | |
|       }
 | |
|     }
 | |
|     return $values;
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
|   ## Chemins de clé
 | |
| 
 | |
|   /**
 | |
|    * vérifier que le chemin $keys fourni sous forme de tableau existe dans le
 | |
|    * tableau $array
 | |
|    *
 | |
|    * si $keys est vide ou null, retourner true
 | |
|    */
 | |
|   static final function _phas(?array $array, ?array $keys): bool {
 | |
|     $first = true;
 | |
|     foreach($keys as $key) {
 | |
|       if ($key === "" && $first) {
 | |
|         # une chaine vide en première position est ignorée
 | |
|         continue;
 | |
|       } elseif (is_array($array)) {
 | |
|         if (!array_key_exists($key, $array)) return false;
 | |
|         $array = $array[$key];
 | |
|       } elseif ($array instanceof ArrayAccess) {
 | |
|         if (!$array->offsetExists($key)) return false;
 | |
|         $array = $array->offsetGet($key);
 | |
|       } else {
 | |
|         return false;
 | |
|       }
 | |
|       $first = false;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Vérifier que le chemin $keys qui est de la forme key[.keys...] existe
 | |
|    * dans le tableau $array
 | |
|    *
 | |
|    * si $keys === null, retourner true
 | |
|    */
 | |
|   static final function phas_s(?array $array, ?string $keys): bool {
 | |
|     if ($keys === null) return true;
 | |
|     $keys = explode(".", $keys);
 | |
|     return self::_phas($array, $keys);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Vérifier que le chemin $keys fourni sous forme de tableau existe dans le
 | |
|    * tableau $array
 | |
|    *
 | |
|    * si $keys est nul ou vide, retourner true
 | |
|    */
 | |
|   static final function phas_a(?array $array, ?array $keys): bool {
 | |
|     if (!$keys) return true;
 | |
|     $keys = implode(".", $keys);
 | |
|     $keys = explode(".", $keys);
 | |
|     return self::_phas($array, $keys);
 | |
|   }
 | |
| 
 | |
|   /** vérifier que le chemin $keys existe dans le tableau $array */
 | |
|   static final function phas($array, $keys): bool {
 | |
|     if ($keys === null) return true;
 | |
|     elseif (is_array($keys)) return self::phas_a($array, $keys);
 | |
|     else return self::phas_s($array, strval($keys));
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Parcourir les enfants de $array avec le chemin $keys fourni sous forme de
 | |
|    * tableau et retourner la valeur correspondante
 | |
|    *
 | |
|    * si $keys est vide ou null, retourner $default
 | |
|    */
 | |
|   static final function _pget(?array $array, ?array $keys, $default=null) {
 | |
|     if (!$keys) return $default;
 | |
|     $value = $array;
 | |
|     $first = true;
 | |
|     foreach($keys as $key) {
 | |
|       if ($key === "" && $first) {
 | |
|         # une chaine vide en première position est ignorée
 | |
|         continue;
 | |
|       } elseif (is_array($value)) {
 | |
|         if (!array_key_exists($key, $value)) return $default;
 | |
|         $value = $value[$key];
 | |
|       } elseif ($value instanceof ArrayAccess) {
 | |
|         if (!$value->offsetExists($key)) return $default;
 | |
|         $value = $value->offsetGet($key);
 | |
|       } else {
 | |
|         return $default;
 | |
|       }
 | |
|       $first = false;
 | |
|     }
 | |
|     return $value;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Parcourir les enfants de $array avec le chemin $keys qui est de la forme
 | |
|    * key[.keys...] et retourner la valeur correspondante
 | |
|    *
 | |
|    * si $keys === null, retourner $default
 | |
|    */
 | |
|   static final function pget_s(?array $array, ?string $keys, $default=null) {
 | |
|     if ($keys === null) return $default;
 | |
|     $keys = explode(".", $keys);
 | |
|     return self::_pget($array, $keys, $default);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Parcourir les enfants de $array avec le chemin $keys fourni sous forme de
 | |
|    * tableau et retourner la valeur correspondante
 | |
|    *
 | |
|    * si $keys est nul ou vide, retourner $default
 | |
|    */
 | |
|   static final function pget_a(?array $array, ?array $keys, $default=null) {
 | |
|     if (!$keys) return $default;
 | |
|     $keys = implode(".", $keys);
 | |
|     $keys = explode(".", $keys);
 | |
|     return self::_pget($array, $keys, $default);
 | |
|   }
 | |
| 
 | |
|   /** obtenir la valeur correspondant au chemin $keys dans $array */
 | |
|   static final function pget($array, $keys, $default=null) {
 | |
|     if ($keys === null) return $default;
 | |
|     elseif (is_array($keys)) return self::pget_a($array, $keys, $default);
 | |
|     else return self::pget_s($array, strval($keys), $default);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Modifier la valeur au chemin $keys fourni sous forme de tableau
 | |
|    *
 | |
|    * utiliser la clé "" (chaine vide) en dernière position pour rajouter à la fin, e.g
 | |
|    * - _pset($array, [""], $value) est équivalent à $array[] = $value
 | |
|    * - _pset($array, ["a", "b", ""], $value) est équivalent à $array["a"]["b"][] = $value
 | |
|    * la clé "" n'a pas de propriété particulière quand elle n'est pas en dernière position
 | |
|    *
 | |
|    * si $keys est vide ou null, $array est remplacé par $value
 | |
|    */
 | |
|   static final function _pset(&$array, ?array $keys, $value): void {
 | |
|     if (!$keys) {
 | |
|       $array = $value;
 | |
|       return;
 | |
|     }
 | |
|     self::ensure_array($array);
 | |
|     $current =& $array;
 | |
|     $last = count($keys) - 1;
 | |
|     $i = 0;
 | |
|     foreach ($keys as $key) {
 | |
|       if ($i == $last) break;
 | |
|       if ($current instanceof ArrayAccess) {
 | |
|         if (!$current->offsetExists($key)) $current->offsetSet($key, []);
 | |
|         $current =& $current->offsetGet($key);
 | |
|         if ($current === null) {
 | |
|           $current = [];
 | |
|         } elseif (!is_array($current) && !($current instanceof ArrayAccess)) {
 | |
|           $current = [$current];
 | |
|         }
 | |
|       } else {
 | |
|         self::ensure_array($current[$key]);
 | |
|         $current =& $current[$key];
 | |
|       }
 | |
|       $i++;
 | |
|     }
 | |
| 
 | |
|     if ($key === "") $current[] = $value;
 | |
|     else $current[$key] = $value;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Modifier la valeur au chemin $keys qui est de la forme key[.keys...]
 | |
|    *
 | |
|    * utiliser la clé "" (chaine vide) en dernière position pour rajouter à la fin, e.g
 | |
|    * - pset($array, "", $value) est équivalent à $array[] = $value
 | |
|    * - pset($array, "a.b.", $value) est équivalent à $array["a"]["b"][] = $value
 | |
|    * la clé "" n'a pas de propriété particulière quand elle n'est pas en dernière position
 | |
|    *
 | |
|    * si $keys est null, $array est remplacé par $value
 | |
|    */
 | |
|   static final function pset_s(&$array, ?string $keys, $value): void {
 | |
|     if ($keys === null) {
 | |
|       $array = $value;
 | |
|     } else {
 | |
|       $keys = explode(".", $keys);
 | |
|       self::_pset($array, $keys, $value);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Modifier la valeur au chemin $keys fourni sous forme de tableau
 | |
|    *
 | |
|    * utiliser la clé "" (chaine vide) en dernière position pour rajouter à la fin, e.g
 | |
|    * - pset($array, "", $value) est équivalent à $array[] = $value
 | |
|    * - pset($array, "a.b.", $value) est équivalent à $array["a"]["b"][] = $value
 | |
|    * la clé "" n'a pas de propriété particulière quand elle n'est pas en dernière position
 | |
|    *
 | |
|    * si $keys est null, $array est remplacé par $value
 | |
|    */
 | |
|   static final function pset_a(&$array, ?array $keys, $value): void {
 | |
|     if (!$keys) {
 | |
|       $array = $value;
 | |
|     } else {
 | |
|       $keys = implode(".", $keys);
 | |
|       $keys = explode(".", $keys);
 | |
|       self::_pset($array, $keys, $value);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /** modifier la valeur au chemin de clé $keys dans le tableau $array */
 | |
|   static final function pset(&$array, $keys, $value): void {
 | |
|     if ($keys === null) $array = $value;
 | |
|     elseif (is_array($keys)) self::pset_a($array, $keys, $value);
 | |
|     else self::pset_s($array, strval($keys), $value);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * supprimer la valeur au chemin $keys fourni sous forme de tableau
 | |
|    *
 | |
|    * si $array vaut null ou false, sa valeur est inchangée.
 | |
|    * $keys est vide ou null, $array devient null
 | |
|    */
 | |
|   static final function _pdel(&$array, ?array $keys): void {
 | |
|     if ($array === false || $array === null) return;
 | |
|     if (!$keys) {
 | |
|       $array = null;
 | |
|       return;
 | |
|     }
 | |
|     self::ensure_array($array);
 | |
|     $current =& $array;
 | |
|     $last = count($keys) - 1;
 | |
|     $i = 0;
 | |
|     foreach ($keys as $key) {
 | |
|       if ($i == $last) break;
 | |
|       if ($current instanceof ArrayAccess) {
 | |
|         if (!$current->offsetExists($key)) break;
 | |
|       } elseif (is_array($current)) {
 | |
|         if (!self::has($current, $key)) break;
 | |
|       } else {
 | |
|         break;
 | |
|       }
 | |
|       $current =& $current[$key];
 | |
|       $i++;
 | |
|     }
 | |
|     if ($i == $last) {
 | |
|       if ($current instanceof ArrayAccess) {
 | |
|         $current->offsetUnset($key);
 | |
|       } elseif (is_array($current)) {
 | |
|         unset($current[$key]);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * supprimer la valeur au chemin $keys qui est de la forme key[.keys...]
 | |
|    *
 | |
|    * si $array vaut null ou false, sa valeur est inchangée.
 | |
|    * si $keys est null, $array devient null
 | |
|    */
 | |
|   static final function pdel_s(&$array, ?string $keys): void {
 | |
|     if ($array === false || $array === null) return;
 | |
|     if ($keys === null) {
 | |
|       $array = null;
 | |
|     } else {
 | |
|       $keys = explode(".", $keys);
 | |
|       self::_pdel($array, $keys);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * supprimer la valeur au chemin $keys fourni sous forme de tableau
 | |
|    *
 | |
|    * si $array vaut null ou false, sa valeur est inchangée.
 | |
|    * si $keys est vide ou null, $array devient null
 | |
|    */
 | |
|   static final function pdel_a(&$array, ?array $keys): void {
 | |
|     if ($array === false || $array === null) return;
 | |
|     if (!$keys) {
 | |
|       $array = null;
 | |
|     } else {
 | |
|       $keys = implode(".", $keys);
 | |
|       $keys = explode(".", $keys);
 | |
|       self::_pdel($array, $keys);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /** supprimer la valeur au chemin de clé $keys dans $array */
 | |
|   static final function pdel(&$array, $keys): void {
 | |
|     if ($array === false || $array === null) return;
 | |
|     if ($keys === null) $array = null;
 | |
|     elseif (is_array($keys)) self::pdel_a($array, $keys);
 | |
|     else self::pdel_s($array, strval($keys));
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
|   ## map
 | |
| 
 | |
|   static final function map(?array $array, callable $func): ?array {
 | |
|     if ($array === null) return null;
 | |
|     return array_map($func, $array);
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
|   ## filter
 | |
| 
 | |
|   static final function filter_n(?array $array): ?array { return SL::filter_n($array); }
 | |
|   static final function filter_z(?array $array): ?array { return SL::filter_z($array); }
 | |
|   static final function filter_f(?array $array): ?array { return SL::filter_f($array); }
 | |
|   static final function filter_f2(?array $array): ?array { return SL::filter_pf($array); }
 | |
| 
 | |
|   #############################################################################
 | |
|   ## Tests sur les valeurs
 | |
| 
 | |
|   static final function any_v(?array $array, $value, bool $strict=true): bool {
 | |
|     if ($strict) return SL::any_same($array, $value);
 | |
|     else return SL::any_equals($array, $value);
 | |
|   }
 | |
|   static final function all_v(?array $array, $value, bool $strict=true): bool {
 | |
|     if ($strict) return SL::all_same($array, $value);
 | |
|     else return SL::all_equals($array, $value);
 | |
|   }
 | |
|   static final function any_nv(?array $array, $value, bool $strict=true): bool {
 | |
|     if ($strict) return SL::any_not_same($array, $value);
 | |
|     else return SL::any_not_equals($array, $value);
 | |
|   }
 | |
|   static final function all_nv(?array $array, $value, bool $strict=true): bool {
 | |
|     if ($strict) return SL::all_not_same($array, $value);
 | |
|     else return SL::all_not_equals($array, $value);
 | |
|   }
 | |
|   static final function any_z(?array $array): bool { return SL::any_z($array); }
 | |
|   static final function all_z(?array $array): bool { return SL::all_z($array); }
 | |
|   static final function any_nz(?array $array): bool { return SL::any_nz($array); }
 | |
|   static final function all_nz(?array $array): bool { return SL::all_nz($array); }
 | |
|   static final function any_n(?array $array): bool { return SL::any_n($array); }
 | |
|   static final function all_n(?array $array): bool { return SL::all_n($array); }
 | |
|   static final function any_nn(?array $array): bool { return SL::any_nn($array); }
 | |
|   static final function all_nn(?array $array): bool { return SL::all_nn($array); }
 | |
|   static final function any_f(?array $array): bool { return SL::any_f($array); }
 | |
|   static final function all_f(?array $array): bool { return SL::all_f($array); }
 | |
|   static final function any_t(?array $array): bool { return SL::any_t($array); }
 | |
|   static final function all_t(?array $array): bool { return SL::all_t($array); }
 | |
|   static final function any_f2(?array $array): bool { return SL::any_pf($array); }
 | |
|   static final function all_f2(?array $array): bool { return SL::all_pf($array); }
 | |
|   static final function any_t2(?array $array): bool { return SL::any_pt($array); }
 | |
|   static final function all_t2(?array $array): bool { return SL::all_pt($array); }
 | |
| 
 | |
|   #############################################################################
 | |
|   ## Fonctions avancées
 | |
| 
 | |
|   /** dans le tableau $array, "renommer" les clés selon le tableau $key_map */
 | |
|   static function map_keys(?array &$array, ?array $key_map): void {
 | |
|     if ($array === null || $key_map === null) return;
 | |
|     foreach ($key_map as $from => $to) {
 | |
|       if (array_key_exists($from, $array)) {
 | |
|         $array[$to] = $array[$from];
 | |
|         unset($array[$from]);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   private static $flattener;
 | |
| 
 | |
|   /**
 | |
|    * Applatir le tableau $array
 | |
|    *
 | |
|    * Pour chaque élément avec une clé séquentielle:
 | |
|    * - si c'est un tableau, l'applatir puis rajouter ses éléments tels quels au
 | |
|    * résultat
 | |
|    * - sinon ajouter l'élément tel quel
 | |
|    *
 | |
|    * Pour chaque élément avec une clé associative:
 | |
|    * - si la valeur n'existe pas déjà, elle est rajoutée telle quelle
 | |
|    * - si la valeur source est null ou false, la valeur destination n'est pas
 | |
|    * modifiée
 | |
|    * - sinon, les deux valeurs sont transformées en tableau le cas échéant.
 | |
|    * si $flattenValue == true, alors le tableau source est applati au préalable.
 | |
|    * puis cette nouvelle valeur est fusionnée avec array_merge() dans la valeur
 | |
|    * précédente.
 | |
|    */
 | |
|   static final function flatten(?array &$array, bool $flattenValue=true): void {
 | |
|     if (self::$flattener === null) self::$flattener = new Flattener();
 | |
|     self::$flattener->flatten($array, $flattenValue);
 | |
|   }
 | |
| 
 | |
|   /** retourner le tableau $array applati */
 | |
|   static final function flattened(?array $array, bool $flattenValue=true): array {
 | |
|     self::flatten($array, $flattenValue);
 | |
|     return $array;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Extraire d'un tableau les clés séquentielles
 | |
|    *
 | |
|    * Retourner $seq où $seq est un tableau avec uniquement les valeurs des clés
 | |
|    * séquentielles. S'il n'existe aucune clé séquentielle retourner $default.
 | |
|    *
 | |
|    * Par exemple: extract_seq(["a", "b" => "c"]) retourne ["a"]
 | |
|    */
 | |
|   static final function extract_seq(?array $array, ?array $default=null): ?array {
 | |
|     $seq = null;
 | |
|     if ($array !== null) {
 | |
|       $index = 0;
 | |
|       foreach ($array as $key => $value) {
 | |
|         if ($key === $index) {
 | |
|           $seq[] = $value;
 | |
|           $index++;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     if ($seq === null) $seq = $default;
 | |
|     return $seq;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Extraire d'un tableau les clés associatives
 | |
|    *
 | |
|    * Retourner une liste $assoc où $assoc est un tableau avec uniquement les
 | |
|    * valeurs des clés associatives. S'il n'existe aucune clé associative,
 | |
|    * retourner $default.
 | |
|    *
 | |
|    * Par exemple: split_assoc(["a", "b" => "c"]) retourne ["b" => "c"]
 | |
|    */
 | |
|   static final function extract_assoc(?array $array, ?array $default=null): ?array {
 | |
|     $assoc = null;
 | |
|     if ($array !== null) {
 | |
|       $index = 0;
 | |
|       foreach ($array as $key => $value) {
 | |
|         if ($key === $index) $index++;
 | |
|         else $assoc[$key] = $value;
 | |
|       }
 | |
|     }
 | |
|     if ($assoc === null) $assoc = $default;
 | |
|     return $assoc;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Extraire d'un tableau les clés séquentielles et les clés associatives
 | |
|    *
 | |
|    * Retourner une liste [$seq, $assoc] où $seq est un tableau avec uniquement
 | |
|    * les valeurs des clés séquentielles et $assoc est un tableau avec uniquement
 | |
|    * les valeurs des clés associatives. S'il n'existe aucune clé séquentielle
 | |
|    * (resp. aucune clé associative), $seq (resp. $assoc) vaut null.
 | |
|    *
 | |
|    * Par exemple: split_assoc(["a", "b" => "c"]) retourne [["a"], ["b" => "c"]]
 | |
|    */
 | |
|   static final function split_assoc(?array $array): array {
 | |
|     $seq = null;
 | |
|     $assoc = null;
 | |
|     if ($array !== null) {
 | |
|       $i = 0;
 | |
|       foreach ($array as $key => $value) {
 | |
|         if ($key === $i) {
 | |
|           $seq[] = $value;
 | |
|           $i++;
 | |
|         } else {
 | |
|           $assoc[$key] = $value;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     return [$seq, $assoc];
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Joindre en un seul tableau un tableau avec des clés séquentielles et un
 | |
|    * tableau avec des clés associatives.
 | |
|    *
 | |
|    * Si $seq_first==true, les clés séquentielles arrivent d'abord, ensuite les
 | |
|    * clés associatives. Sinon, ce sont les clés associatives qui arrivent d'abord
 | |
|    */
 | |
|   static final function merge_assoc(?array &$array, ?array $seq, ?array $assoc, bool $seq_first=false): void {
 | |
|     if ($seq === null && $assoc === null) $array = [];
 | |
|     elseif ($seq === null) $array = $assoc;
 | |
|     elseif ($assoc === null) $array = $seq;
 | |
|     elseif ($seq_first) $array = array_merge($seq, $assoc);
 | |
|     else $array = array_merge($assoc, $seq);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Construire un sous-ensemble du tableau $array en sélectionnant les clés
 | |
|    * mentionnées dans $keys
 | |
|    * .. si $keys === null, retourner $array
 | |
|    * .. sinon, $keys est un tableau avec des clés séquentielles ou associatives.
 | |
|    * chacune des clés séquentielles est prise telle quelle. les clés associatives
 | |
|    * permettent de renommer les clés
 | |
|    *
 | |
|    * soit $array = ["a" => 1, "b" => 2, "c" => 3]
 | |
|    * alors select($array, ["a", "b" => "x"])
 | |
|    * retourne ["a" => 1, "x" => 2]
 | |
|    */
 | |
|   static final function select(?array $array, ?array $keys, $default=null): array {
 | |
|     if ($array === null) $array = [];
 | |
|     if ($keys === null) return $array;
 | |
| 
 | |
|     $index = 0;
 | |
|     $result = [];
 | |
|     foreach ($keys as $key => $tkey) {
 | |
|       if ($key === $index) {
 | |
|         # clé séquentielle
 | |
|         $value = self::get($array, $tkey, $default);
 | |
|         $index++;
 | |
|       } else {
 | |
|         # clé associative
 | |
|         $value = self::get($array, $key, $default);
 | |
|       }
 | |
|       $result[$tkey] = $value;
 | |
|     }
 | |
|     return $result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * construire un sous-ensemble du tableau $array en sélectionnant les clés
 | |
|    * mentionnées dans $keys.
 | |
|    * .. si $keys === null, retourner $array
 | |
|    * .. sinon, $keys est un tableau avec des clés séquentielles ou associatives.
 | |
|    * pour chacune des clés séquentielles, la valeur est une clé pour récupérer
 | |
|    * la valeur dans $array
 | |
|    * pour chacune des clés associatives, la valeur est celle fournie
 | |
|    */
 | |
|   static final function select_replace(?array $array, ?array $keys, $default=null): array {
 | |
|     if ($array === null) $array = [];
 | |
|     if ($keys === null) return $array;
 | |
| 
 | |
|     $index = 0;
 | |
|     $result = [];
 | |
|     foreach ($keys as $key => $value) {
 | |
|       if ($key === $index) {
 | |
|         # clé séquentielle
 | |
|         $result[$value] = self::get($array, $value, $default);
 | |
|         $index++;
 | |
|       } else {
 | |
|         # clé associative
 | |
|         $result[$key] = $value;
 | |
|       }
 | |
|     }
 | |
|     return $result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link select_replace()} mais $keys est applati d'abord
 | |
|    *
 | |
|    * par exemple:
 | |
|    * ~~~php
 | |
|    * $src = ["a" => 1, "b" => 2, "c" => 3];
 | |
|    * $dest = A::select($src, ["a", "x" => 9, ["z" => 8, "c"], "d"]);
 | |
|    * # $dest === ["a" => 1, "x" => 9, "z" => 8, "c" => 3, "d" => null]
 | |
|    * ~~~
 | |
|    */
 | |
|   static final function select_replace2(?array $array, ?array $keys, $default=null): array {
 | |
|     if ($array === null) $array = [];
 | |
|     if ($keys === null) return $array;
 | |
|     self::flatten($keys);
 | |
|     return self::select_replace($array, $keys, $default);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * construire un sous-ensemble du tableau $array en sélectionnant les clés
 | |
|    * mentionnées dans $keys.
 | |
|    * .. si $keys === null, retourner $array
 | |
|    * .. sinon, $keys est un tableau avec des clés séquentielles ou associatives.
 | |
|    * pour chacune des clés séquentielles, la valeur est une clé pour récupérer
 | |
|    * la valeur dans $array
 | |
|    * pour chacune des clés associatives, la valeur fournie est prise par défaut
 | |
|    * si la valeur correspondante n'existe pas ou vaut false dans $array
 | |
|    */
 | |
|   static final function select_default(?array $array, ?array $keys, $default=null): array {
 | |
|     if ($array === null) $array = [];
 | |
|     if ($keys === null) return $array;
 | |
| 
 | |
|     $index = 0;
 | |
|     $result = [];
 | |
|     foreach ($keys as $key => $value) {
 | |
|       if ($key === $index) {
 | |
|         # clé séquentielle
 | |
|         $result[$value] = self::get($array, $value, $default);
 | |
|         $index++;
 | |
|       } else {
 | |
|         # clé associative
 | |
|         if (array_key_exists($key, $array)) {
 | |
|           $arrayValue = $array[$key];
 | |
|           if ($arrayValue !== false) $value = $arrayValue;
 | |
|         }
 | |
|         $result[$key] = $value;
 | |
|       }
 | |
|     }
 | |
|     return $result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * comme {@link select_default()} mais $keys est applati d'abord
 | |
|    *
 | |
|    * par exemple:
 | |
|    * ~~~php
 | |
|    * $src = ["a" => 1, "b" => 2, "c" => 3];
 | |
|    * $dest = A::select($src, ["a", "x" => 9, ["z" => 8, "c"], "d"]);
 | |
|    * # $dest === ["a" => 1, "x" => 9, "z" => 8, "c" => 3, "d" => null]
 | |
|    * ~~~
 | |
|    */
 | |
|   static final function select_default2(?array $array, ?array $keys, $default=null): array {
 | |
|     if ($array === null) $array = [];
 | |
|     if ($keys === null) return $array;
 | |
|     self::flatten($keys);
 | |
|     return self::select_default($array, $keys, $default);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Construire un sous-ensemble du tableau $array en sélectionnant les clés
 | |
|    * mentionnées dans $includes et pas mentionnées dans $excludes
 | |
|    *
 | |
|    * soit $array = ["a" => 1, "b" => 2, "c" => 3, "d" => 4]
 | |
|    * alors xselect($array, ["a", "b" => "x"], ["d"])
 | |
|    * retourne ["a" => 1, "x" => 2]
 | |
|    */
 | |
|   static final function xselect_keys(?array $array, ?array $includes, ?array $excludes, $default=null): array {
 | |
|     if ($array === null) $array = [];
 | |
|     if ($includes === null) {
 | |
|       if ($excludes === null) return $array;
 | |
|       else $includes = array_keys($array);
 | |
|     }
 | |
| 
 | |
|     $index = 0;
 | |
|     $result = [];
 | |
|     foreach ($includes as $fromkey => $tokey) {
 | |
|       if ($fromkey === $index) {
 | |
|         # clé séquentielle
 | |
|         $index++;
 | |
|         $fromkey = $tokey;
 | |
|       }
 | |
|       if ($excludes !== null && in_array($fromkey, $excludes)) continue;
 | |
|       $value = self::get($array, $fromkey, $default);
 | |
|       $result[$tokey] = $value;
 | |
|     }
 | |
|     return $result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Construire un sous-ensemble du tableau $array en sélectionnant les valeurs
 | |
|    * mentionnées dans $includes et pas mentionnées dans $excludes
 | |
|    *
 | |
|    * soit $array = ["a" => 1, "b" => 2, "c" => 3, "d" => 4]
 | |
|    * alors xselect($array, ["a", "b" => "x"], ["d"])
 | |
|    * retourne ["a" => 1, "x" => 2]
 | |
|    */
 | |
|   static final function xselect(?array $array, ?array $includes, ?array $excludes, $default=null): array {
 | |
|     if ($array === null) $array = [];
 | |
|     if ($includes === null && $excludes === null) return $array;
 | |
| 
 | |
|     $result = [];
 | |
|     foreach ($array as $key => $value) {
 | |
|       if ($excludes !== null && in_array($value, $excludes)) continue;
 | |
|       if ($includes !== null && !in_array($value, $includes)) continue;
 | |
|       $result[$key] = $value;
 | |
|     }
 | |
|     return $result;
 | |
|   }
 | |
| }
 |