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; } }