$value) { if (is_int($key)) return true; } return false; } /** * tester si $array est une liste, c'est à dire un tableau non null avec * uniquement des clés numériques séquentielles commençant à zéro * * NB: is_list(null) === false * et is_list([]) === true */ static final function is_list(?array $array): bool { if ($array === null) return false; $index = -1; foreach ($array as $key => $value) { ++$index; if ($key !== $index) return false; } return true; } /** * tester si $array contient la clé $key * * @param array|ArrayAccess $array */ static final function has($array, $key): bool { if (is_array($array)) { return array_key_exists($key, $array); } elseif ($array instanceof ArrayAccess) { return $array->offsetExists($key); } return false; } /** * retourner $array[$key] ou $default si la clé n'existe pas * * @param array|ArrayAccess $array */ static final function get($array, $key, $default=null) { if (is_array($array)) { if (array_key_exists($key, $array)) return $array[$key]; } elseif ($array instanceof ArrayAccess) { if ($array->offsetExists($key)) return $array->offsetGet($key); } return $default; } /** * si $array est un array ou une instance de ArrayAccess, créer ou modifier * l'élément dont la clé est $key * * @param array|ArrayAccess $array */ static final function set(&$array, $key, $value): void { if (is_array($array) || $array === null) { if ($key === null) $array[] = $value; else $array[$key] = $value; } elseif ($array instanceof ArrayAccess) { $array->offsetSet($key, $value); } } /** * si $array est un array ou une instance de ArrayAccess, supprimer l'élément * dont la clé est $key * * @param array|ArrayAccess $array */ static final function del(&$array, $key): void { if (is_array($array)) { unset($array[$key]); } elseif ($array instanceof ArrayAccess) { $array->offsetUnset($key); } } /** retourner le nombre d'éléments de $array */ static final function count(?array $array): int { return $array !== null? count($array): 0; } /** retourner la liste des clés de $array */ static final function keys(?array $array): array { return $array !== null? array_keys($array): []; } /** * retourner la première valeur de $array ou $default si le tableau est null * ou vide */ static final function first($array, $default=null) { if (is_array($array)) return $array[array_key_first($array)]; return $default; } ############################################################################# /** * Fusionner tous les tableaux spécifiés. Les valeurs null sont ignorées. * Retourner null si aucun tableau n'est fourni ou s'ils étaient tous null. */ static final function merge(...$arrays): ?array { $merges = []; foreach ($arrays as $array) { self::ensure_narray($array); if ($array !== null) $merges[] = $array; } return $merges? array_merge(...$merges): null; } ############################################################################# /** * vérifier que le chemin $keys existe dans le tableau $array * * si $keys est vide ou null, retourner true */ static final function phas($array, $pkey): bool { # optimisations if ($pkey === null || $pkey === []) { return true; } elseif (is_int($pkey) || (is_string($pkey) && strpos($pkey, ".") === false)) { return self::has($array, $pkey); } elseif (!is_array($pkey)) { $pkey = explode(".", strval($pkey)); } # phas $first = true; foreach($pkey 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; } static final function each_phas($array, ?array $pkeys): array { $result = []; if ($pkeys !== null) { foreach ($pkeys as $pkey) { $result[] = self::phas($array, $pkey); } } return $result; } /** * obtenir la valeur correspondant au chemin $keys dans $array * * si $keys est vide ou null, retourner $default */ static final function pget($array, $pkey, $default=null) { # optimisations if ($pkey === null || $pkey === []) return $default; elseif ($pkey === "") return $array; elseif (is_int($pkey) || (is_string($pkey) && strpos($pkey, ".") === false)) { return self::get($array, $pkey, $default); } elseif (!is_array($pkey)) { $pkey = explode(".", strval($pkey)); } # pget $value = $array; $first = true; foreach($pkey 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; } static final function each_pget($array, ?array $pkeys): array { $result = []; if ($pkeys !== null) { foreach ($pkeys as $key => $pkey) { $result[$key] = self::pget($array, $pkey); } } return $result; } /** * modifier la valeur au chemin de clé $keys dans le tableau $array * * 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, $pkey, $value): void { # optimisations if ($pkey === null || $pkey === []) { $array = $value; return; } elseif ($pkey === "") { $array[] = $value; return; } elseif (is_int($pkey) || (is_string($pkey) && strpos($pkey, ".") === false)) { self::set($array, $pkey, $value); return; } elseif (!is_array($pkey)) { $pkey = explode(".", strval($pkey)); } # pset self::ensure_array($array); $current =& $array; $key = null; $last = count($pkey) - 1; $i = 0; foreach ($pkey 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; } static final function each_pset(&$array, ?array $values): void { if ($values !== null) { foreach ($values as $pkey => $value) { self::pset($array, $pkey, $value); } } } /** * supprimer la valeur au chemin de clé $keys dans $array * * si $array vaut null ou false, sa valeur est inchangée. * si $keys est vide ou null, $array devient null */ static final function pdel(&$array, $pkey): void { # optimisations if ($array === null || $array === false) { return; } elseif ($pkey === null || $pkey === []) { $array = null; return; } elseif (is_int($pkey) || (is_string($pkey) && strpos($pkey, ".") === false)) { self::del($array, $pkey); return; } elseif (!is_array($pkey)) { $pkey = explode(".", strval($pkey)); } # pdel self::ensure_array($array); $current =& $array; $key = null; $last = count($pkey) - 1; $i = 0; foreach ($pkey as $key) { if ($i == $last) break; if ($current instanceof ArrayAccess) { if (!$current->offsetExists($key)) break; } elseif (is_array($current)) { if (!array_key_exists($key, $current)) break; } else { break; } $current =& $current[$key]; $i++; } if ($i == $last) { if ($current instanceof ArrayAccess) { $current->offsetUnset($key); } elseif (is_array($current)) { unset($current[$key]); } } } static final function each_pdel(&$array, ?array $pkeys): void { if ($pkeys !== null) { foreach ($pkeys as $pkey) { self::pdel($array, $pkey); } } } ############################################################################# /** * retourner le tableau $array en "renommant" les clés selon le tableau * $mappings qui contient des associations de la forme [$from => $to] */ static function rekey(?array $array, ?array $mappings): ?array { if ($array === null || $mappings === null) return $array; $mapped = []; foreach ($array as $key => $value) { if (array_key_exists($key, $mappings)) $key = $mappings[$key]; $mapped[$key] = $value; } return $mapped; } ############################################################################# /** tester si tous les éléments du tableau satisfont la condition */ static final function all_if(?array $array, callable $cond): bool { if ($array !== null) { foreach ($array as $value) { if (!$cond($value)) return false; } } return true; } static final function all_z(?array $array): bool { return self::all_if($array, [cv::class, "z"]);} static final function all_nz(?array $array): bool { return self::all_if($array, [cv::class, "nz"]);} static final function all_n(?array $array): bool { return self::all_if($array, [cv::class, "n"]);} static final function all_nn(?array $array): bool { return self::all_if($array, [cv::class, "nn"]);} static final function all_t(?array $array): bool { return self::all_if($array, [cv::class, "t"]);} static final function all_f(?array $array): bool { return self::all_if($array, [cv::class, "f"]);} static final function all_pt(?array $array): bool { return self::all_if($array, [cv::class, "pt"]);} static final function all_pf(?array $array): bool { return self::all_if($array, [cv::class, "pf"]);} static final function all_equals(?array $array, $value): bool { return self::all_if($array, cv::equals($value)); } static final function all_not_equals(?array $array, $value): bool { return self::all_if($array, cv::not_equals($value)); } static final function all_same(?array $array, $value): bool { return self::all_if($array, cv::same($value)); } static final function all_not_same(?array $array, $value): bool { return self::all_if($array, cv::not_same($value)); } #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** tester si au moins un élément du tableau satisfait la condition */ static final function any_if(?array $array, callable $cond): bool { if ($array !== null) { foreach ($array as $value) { if ($cond($value)) return true; } } return false; } static final function any_z(?array $array): bool { return self::any_if($array, [cv::class, "z"]);} static final function any_nz(?array $array): bool { return self::any_if($array, [cv::class, "nz"]);} static final function any_n(?array $array): bool { return self::any_if($array, [cv::class, "n"]);} static final function any_nn(?array $array): bool { return self::any_if($array, [cv::class, "nn"]);} static final function any_t(?array $array): bool { return self::any_if($array, [cv::class, "t"]);} static final function any_f(?array $array): bool { return self::any_if($array, [cv::class, "f"]);} static final function any_pt(?array $array): bool { return self::any_if($array, [cv::class, "pt"]);} static final function any_pf(?array $array): bool { return self::any_if($array, [cv::class, "pf"]);} static final function any_equals(?array $array, $value): bool { return self::any_if($array, cv::equals($value)); } static final function any_not_equals(?array $array, $value): bool { return self::any_if($array, cv::not_equals($value)); } static final function any_same(?array $array, $value): bool { return self::any_if($array, cv::same($value)); } static final function any_not_same(?array $array, $value): bool { return self::any_if($array, cv::not_same($value)); } #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static final function filter_if(?array $array, callable $cond): ?array { if ($array === null) return null; $filtered = []; $index = 0; foreach ($array as $key => $value) { if (!$cond($value)) { if ($key === $index) { $index++; $filtered[] = $value; } else { $filtered[$key] = $value; } } elseif ($key === $index) { $index++; } } return $filtered; } static final function filter_z(?array $array): ?array { return self::filter_if($array, [cv::class, "z"]);} static final function filter_nz(?array $array): ?array { return self::filter_if($array, [cv::class, "nz"]);} static final function filter_n(?array $array): ?array { return self::filter_if($array, [cv::class, "n"]);} static final function filter_nn(?array $array): ?array { return self::filter_if($array, [cv::class, "nn"]);} static final function filter_t(?array $array): ?array { return self::filter_if($array, [cv::class, "t"]);} static final function filter_f(?array $array): ?array { return self::filter_if($array, [cv::class, "f"]);} static final function filter_pt(?array $array): ?array { return self::filter_if($array, [cv::class, "pt"]);} static final function filter_pf(?array $array): ?array { return self::filter_if($array, [cv::class, "pf"]);} static final function filter_equals(?array $array, $value): ?array { return self::filter_if($array, cv::equals($value)); } static final function filter_not_equals(?array $array, $value): ?array { return self::filter_if($array, cv::not_equals($value)); } static final function filter_same(?array $array, $value): ?array { return self::filter_if($array, cv::same($value)); } static final function filter_not_same(?array $array, $value): ?array { return self::filter_if($array, cv::not_same($value)); } ############################################################################# static final function sorted(?array $array, int $flags=SORT_REGULAR, bool $assoc=false): ?array { if ($array === null) return null; if ($assoc) asort($array, $flags); else sort($array, $flags); return $array; } static final function ksorted(?array $array, int $flags=SORT_REGULAR): ?array { if ($array === null) return null; ksort($array, $flags); return $array; } /** * retourner une fonction permettant de trier un tableau sur les clés * spécifiées. * * - les clés ayant le préfixe '+' ou le suffixe '|asc' indiquent un tri * ascendant * - les clés ayant le préfixe '-' ou le suffixe '|desc' indiquent un tri * descendant * - sinon, par défaut, le tri est ascendant */ static final function compare(array $keys): callable { return function ($a, $b) use ($keys) { foreach ($keys as $key) { if (str::del_prefix($key, "+")) $w = 1; elseif (str::del_prefix($key, "-")) $w = -1; elseif (str::del_suffix($key, "|asc")) $w = 1; elseif (str::del_suffix($key, "|desc")) $w = -1; else $w = 1; if ($c = $w * cv::compare(cl::get($a, $key), cl::get($b, $key))) { return $c; } } return 0; }; } static final function usorted(?array $array, array $keys, bool $assoc=false): ?array { if ($array === null) return null; if ($assoc) uasort($array, self::compare($keys)); else usort($array, self::compare($keys)); return $array; } }