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