intégrer tout nulib

This commit is contained in:
Jephté Clain 2024-04-05 08:31:49 +04:00
parent 5732020909
commit e8b7ca91af
56 changed files with 1538 additions and 58 deletions

View File

@ -123,8 +123,8 @@ args=(
-u[n|m] package|Class [DEST]
Il y a deux types d'évolutions possibles:
* mettre en nurserie
* arriver à maturation
* mettre en nurserie (-n)
* arriver à maturation (-m)
Pour chaque type d'évolution, il y a deux actions possibles:
* copier (-c)

View File

@ -13,7 +13,6 @@
}
],
"require": {
"nulib/php": "dev-wip",
"symfony/yaml": "^5.0",
"php": ">=7.4"
},

36
src/AccessException.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace nur\sery;
/**
* Class AccessException: indiquer que la resource ou l'objet auquel on veut
* accéder n'est pas accessible. il s'agit donc d'une erreur de l'utilisateur
*/
class AccessException extends UserException {
static final function read_only(?string $dest=null, ?string $prefix=null): self {
if ($prefix) $prefix = "$prefix: ";
if ($dest === null) $dest = "this property";
$message = "$dest is read-only";
return new static($prefix.$message);
}
static final function immutable_object(?string $dest=null, ?string $prefix=null): self {
if ($prefix) $prefix = "$prefix: ";
if ($dest === null) $dest = "this object";
$message = "$dest is immutable";
return new static($prefix.$message);
}
static final function not_allowed(?string $action=null, ?string $prefix=null): self {
if ($prefix) $prefix = "$prefix: ";
if ($action === null) $action = "this operation";
$message = "$action is not allowed";
return new static($prefix.$message);
}
static final function not_accessible(?string $dest=null, ?string $prefix=null): self {
if ($prefix) $prefix = "$prefix: ";
if ($dest === null) $dest = "this resource";
$message = "$dest is not accessible";
return new static($prefix.$message);
}
}

19
src/DataException.php Normal file
View File

@ -0,0 +1,19 @@
<?php
namespace nur\sery;
use RuntimeException;
/**
* Class DataException: exception générique concernant l'accès à des données
*/
class DataException extends RuntimeException {
static final function no_more_data(): self {
return new self("no more data");
}
static final function ensure_not_eof($data, bool $throw=true, $eof=false) {
if (!$throw) return null;
elseif ($data !== $eof) return $data;
else throw self::no_more_data();
}
}

101
src/ExceptionShadow.php Normal file
View File

@ -0,0 +1,101 @@
<?php
namespace nur\sery;
use Throwable;
/**
* Class ExceptionShadow: une classe qui capture les informations d'une
* exception afin de pouvoir les sérialiser
*/
class ExceptionShadow {
protected static function extract_trace(array $trace): array {
$frames = [];
foreach ($trace as $frame) {
$file = cl::get($frame, "file");
$line = cl::get($frame, "line");
$class = cl::get($frame, "class");
$function = cl::get($frame, "function");
$type = cl::get($frame, "type");
$frames[] = [
"file" => $file,
"line" => $line,
"class" => $class,
"object" => null,
"type" => $type,
"function" => $function,
"args" => [],
];
}
return $frames;
}
function __construct(Throwable $exception) {
$this->class = get_class($exception);
$this->message = $exception->getMessage();
$this->code = $exception->getCode();
$this->file = $exception->getFile();
$this->line = $exception->getLine();
$this->trace = self::extract_trace($exception->getTrace());
$previous = $exception->getPrevious();
if ($previous !== null) $this->previous = new static($previous);
}
/** @var string */
protected $class;
function getClass(): string {
return $this->class;
}
/** @var string */
protected $message;
function getMessage(): string {
return $this->message;
}
/** @var mixed */
protected $code;
function getCode() {
return $this->code;
}
/** @var string */
protected $file;
function getFile(): string {
return $this->file;
}
/** @var int */
protected $line;
function getLine(): int {
return $this->line;
}
/** @var array */
protected $trace;
function getTrace(): array {
return $this->trace;
}
function getTraceAsString(): string {
$lines = [];
foreach ($this->trace as $index => $frame) {
$lines[] = "#$index $frame[file]($frame[line]): $frame[class]$frame[type]$frame[function]()";
}
$index++;
$lines[] = "#$index {main}";
return implode("\n", $lines);
}
/** @var ExceptionShadow */
protected $previous;
function getPrevious(): ?ExceptionShadow {
return $this->previous;
}
}

22
src/ExitException.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace nur\sery;
use Throwable;
/**
* Class ExitException: une exception qui indique que l'application souhaite
* quitter normalement, avec éventuellement un code d'erreur.
*/
class ExitException extends UserException {
function __construct(int $exitcode=0, $user_message=null, Throwable $previous=null) {
parent::__construct($user_message, null, $exitcode, $previous);
}
function isError(): bool {
return $this->getCode() !== 0;
}
function haveMessage(): bool {
return $this->getUserMessage() !== null;
}
}

23
src/StateException.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace nur\sery;
use LogicException;
/**
* Class StateException: indiquer que l'état dans lequel on se trouve est
* inattendu: il s'agit donc d'un bug
*/
class StateException extends LogicException {
static final function not_implemented(?string $method=null, ?string $prefix=null): self {
if ($method === null) $method = "this method";
$message = "$method is not implemented";
if ($prefix) $prefix = "$prefix: ";
return new static($prefix.$message);
}
static final function unexpected_state(?string $prefix=null): self {
$message = "unexpected state";
if ($prefix) $prefix = "$prefix: ";
return new static($prefix.$message);
}
}

12
src/StopException.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace nur\sery;
use Exception;
/**
* Class StopException: une exception qui par convention est envoyée à un
* générateur pour indiquer qu'il doit s'arrêter "proprement". cela peut être
* utilisé pour implémenter des itérateur "closeable"
*/
class StopException extends Exception {
}

89
src/UserException.php Normal file
View File

@ -0,0 +1,89 @@
<?php
namespace nur\sery;
use RuntimeException;
use Throwable;
/**
* Class UserException: une exception qui peut en plus contenir un message
* utilisateur
*/
class UserException extends RuntimeException {
/** @param Throwable|ExceptionShadow $e */
static function get_user_message($e): ?string {
if ($e instanceof self) return $e->getUserMessage();
else return null;
}
/** @param Throwable|ExceptionShadow $e */
static final function get_user_summary($e): string {
$parts = [];
$first = true;
while ($e !== null) {
$message = self::get_user_message($e);
if (!$message) $message = "(no message)";
if ($first) $first = false;
else $parts[] = "caused by ";
$parts[] = get_class($e) . ": " . $message;
$e = $e->getPrevious();
}
return implode(", ", $parts);
}
/** @param Throwable|ExceptionShadow $e */
static function get_message($e): ?string {
$message = $e->getMessage();
if (!$message && $e instanceof self) $message = $e->getUserMessage();
return $message;
}
/** @param Throwable|ExceptionShadow $e */
static final function get_summary($e): string {
$parts = [];
$first = true;
while ($e !== null) {
$message = self::get_message($e);
if (!$message) $message = "(no message)";
if ($first) $first = false;
else $parts[] = "caused by ";
if ($e instanceof ExceptionShadow) $class = $e->getClass();
else $class = get_class($e);
$parts[] = "$class: $message";
$e = $e->getPrevious();
}
return implode(", ", $parts);
}
/** @param Throwable|ExceptionShadow $e */
static final function get_traceback($e): string {
$tbs = [];
$previous = false;
while ($e !== null) {
if (!$previous) {
$efile = $e->getFile();
$eline = $e->getLine();
$tbs[] = "at $efile($eline)";
} else {
$tbs[] = "~~ caused by: " . self::get_summary($e);
}
$tbs[] = $e->getTraceAsString();
$e = $e->getPrevious();
$previous = true;
#XXX il faudrait ne pas réinclure les lignes communes aux exceptions qui
# ont déjà été affichées
}
return implode("\n", $tbs);
}
function __construct($user_message, $tech_message=null, $code=0, ?Throwable $previous=null) {
$this->userMessage = $user_message;
if ($tech_message === null) $tech_message = $user_message;
parent::__construct($tech_message, $code, $previous);
}
protected $userMessage;
function getUserMessage(): ?string {
return $this->userMessage;
}
}

65
src/ValueException.php Normal file
View File

@ -0,0 +1,65 @@
<?php
namespace nur\sery;
/**
* Class ValueException: indiquer qu'une valeur est invalide
*/
class ValueException extends UserException {
private static function value($value): string {
if (is_object($value)) {
return "<".get_class($value).">";
} elseif (is_array($value)) {
$values = $value;
$parts = [];
foreach ($values as $value) {
$parts[] = self::value($value);
}
return "[" . implode(", ", $parts) . "]";
} elseif (is_string($value)) {
return $value;
} else {
return var_export($value, true);
}
}
private static function message($value, ?string $message, ?string $kind, ?string $prefix, ?string $suffix): string {
if ($kind === null) $kind = "value";
if ($message === null) $message = "$kind$suffix";
if ($value !== null) {
$value = self::value($value);
if ($prefix) $prefix = "$prefix: $value";
else $prefix = $value;
}
if ($prefix) $prefix = "$prefix: ";
return $prefix.$message;
}
static final function null(?string $kind=null, ?string $prefix=null, ?string $message=null): self {
return new static(self::message(null, $message, $kind, $prefix, " should not be null"));
}
static final function invalid_kind($value=null, ?string $kind=null, ?string $prefix=null, ?string $message=null): self {
return new static(self::message($value, $message, $kind, $prefix, " is invalid"));
}
static final function invalid_key($value, ?string $prefix=null, ?string $message=null): self {
return self::invalid_kind($value, "key", $prefix, $message);
}
static final function invalid_value($value, ?string $prefix=null, ?string $message=null): self {
return self::invalid_kind($value, "value", $prefix, $message);
}
static final function invalid_type($value, string $expected_type): self {
return new static(self::message($value, null, "type", null, " is invalid, expected $expected_type"));
}
static final function invalid_class($class, string $expected_class): self {
if (is_object($class)) $class = get_class($class);
return new static(self::message($class, null, "class", null, " is invalid, expected $expected_class"));
}
static final function forbidden($value=null, ?string $kind=null, ?string $prefix=null, ?string $message=null): self {
return new static(self::message($value, $message, $kind, $prefix, " is forbidden"));
}
}

509
src/cl.php Normal file
View File

@ -0,0 +1,509 @@
<?php
namespace nur\sery;
use ArrayAccess;
use Traversable;
/**
* Class cl: gestion de tableau de valeurs scalaires
*/
class cl {
/** retourner un array non null à partir de $array */
static final function with($array): array {
if (is_array($array)) return $array;
elseif ($array === null || $array === false) return [];
elseif ($array instanceof Traversable) return iterator_to_array($array);
else return [$array];
}
/** retourner un array à partir de $array, ou null */
static final function withn($array): ?array {
if (is_array($array)) return $array;
elseif ($array === null || $array === false) return null;
elseif ($array instanceof Traversable) return iterator_to_array($array);
else return [$array];
}
/**
* s'assurer que $array est un array non null. retourner true si $array n'a
* pas été modifié (s'il était déjà un array), false sinon.
*/
static final function ensure_array(&$array): bool {
if (is_array($array)) return true;
elseif ($array === null || $array === false) $array = [];
elseif ($array instanceof Traversable) $array = iterator_to_array($array);
else $array = [$array];
return false;
}
/**
* s'assurer que $array est un array s'il est non null. retourner true si
* $array n'a pas été modifié (s'il était déjà un array ou s'il valait null).
*/
static final function ensure_narray(&$array): bool {
if ($array === null || is_array($array)) return true;
elseif ($array === false) $array = [];
elseif ($array instanceof Traversable) $array = iterator_to_array($array);
else $array = [$array];
return false;
}
/** tester si $array a au moins une clé numérique */
static final function have_num_keys(?array $array): bool {
if ($array === null) return false;
foreach ($array as $key => $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): [];
}
#############################################################################
/**
* 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;
}
}

216
src/cv.php Normal file
View File

@ -0,0 +1,216 @@
<?php
namespace nur\sery;
/**
* Class cv: gestion de valeurs scalaires
*/
class cv {
/** tester si $value vaut null ou false */
static final function z($value): bool {
return $value === false || $value === null;
}
/** tester si $value ne vaut ni null ni false */
static final function nz($value): bool {
return $value !== false && $value !== null;
}
/** tester si $value vaut null */
static final function n($value): bool {
return $value === null;
}
/** tester si $value n'est pas null */
static final function nn($value): bool {
return $value !== null;
}
/** tester si $value est vraie (y compris la chaine "0") */
static final function t($value): bool {
return $value || $value === "0";
}
/** tester si $value est fausse (cela n'inclue pas la chaine "0") */
static final function f($value): bool {
return !$value && $value !== "0";
}
/** tester si $value est vraie selon les règles de PHP. */
static final function pt($value): bool {
return boolval($value);
}
/** tester si $value est fausse selon les règles de PHP. */
static final function pf($value): bool {
return !$value;
}
/**
* contruire une fonction qui retourne vrai si on lui passe en argument une
* valeur égale à $value
*/
static final function equals($value): callable {
return function ($arg) use($value) { return $arg == $value; };
}
/**
* contruire une fonction qui retourne vrai si on lui passe en argument une
* valeur qui n'est pas égale à $value
*/
static final function not_equals($value): callable {
return function ($arg) use($value) { return $arg != $value; };
}
/**
* contruire une fonction qui retourne vrai si on lui passe en argument une
* valeur strictement égale à $value
*/
static final function same($value): callable {
return function ($arg) use($value) { return $arg === $value; };
}
/**
* contruire une fonction qui retourne vrai si on lui passe en argument une
* valeur qui n'est pas strictement égale à $value
*/
static final function not_same($value): callable {
return function ($arg) use($value) { return $arg !== $value; };
}
#############################################################################
/** mettre à jour $dest avec $value si $cond($value) est vrai */
static final function set_if(&$dest, $value, callable $cond) {
if ($cond($value)) $dest = $value;
return $dest;
}
static final function set_z(&$dest, $value) { return self::set_if($dest, $value, [self::class, "z"]);}
static final function set_nz(&$dest, $value) { return self::set_if($dest, $value, [self::class, "nz"]);}
static final function set_n(&$dest, $value) { return self::set_if($dest, $value, [self::class, "n"]);}
static final function set_nn(&$dest, $value) { return self::set_if($dest, $value, [self::class, "nn"]);}
static final function set_t(&$dest, $value) { return self::set_if($dest, $value, [self::class, "t"]);}
static final function set_f(&$dest, $value) { return self::set_if($dest, $value, [self::class, "f"]);}
static final function set_pt(&$dest, $value) { return self::set_if($dest, $value, [self::class, "pt"]);}
static final function set_pf(&$dest, $value) { return self::set_if($dest, $value, [self::class, "pf"]);}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/** mettre à jour $dest avec $value si $cond($dest) est vrai */
static final function update_if(&$dest, $value, callable $cond) {
if ($cond($dest)) $dest = $value;
return $dest;
}
static final function update_z(&$dest, $value) { return self::update_if($dest, $value, [self::class, "z"]);}
static final function update_nz(&$dest, $value) { return self::update_if($dest, $value, [self::class, "nz"]);}
static final function update_n(&$dest, $value) { return self::update_if($dest, $value, [self::class, "n"]);}
static final function update_nn(&$dest, $value) { return self::update_if($dest, $value, [self::class, "nn"]);}
static final function update_t(&$dest, $value) { return self::update_if($dest, $value, [self::class, "t"]);}
static final function update_f(&$dest, $value) { return self::update_if($dest, $value, [self::class, "f"]);}
static final function update_pt(&$dest, $value) { return self::update_if($dest, $value, [self::class, "pt"]);}
static final function update_pf(&$dest, $value) { return self::update_if($dest, $value, [self::class, "pf"]);}
#############################################################################
/** retourner $value si elle est vraie (y compris la chaine "0"), false sinon */
static final function vf($value) {
return $value || $value === "0"? $value: false;
}
/** retourner $value si elle est vraie (y compris la chaine "0"), null sinon */
static final function vn($value) {
return $value || $value === "0"? $value: null;
}
/**
* retourner la première valeur vraie (y compris la chaine "0")
*
* si toutes les valeurs sont fausses, retourner la dernière valeur de la
* liste, ou null si la liste est vide
*/
static final function firstv(...$values) {
$value = null;
foreach ($values as $value) {
if ($value || $value === "0") return $value;
}
return $value;
}
/**
* retourner la première valeur vraie selon les règles de PHP.
*
* si toutes les valeurs sont fausses, retourner la dernière valeur de la
* liste, ou null si la liste est vide
*/
static final function firstpv(...$values) {
$value = null;
foreach ($values as $value) {
if ($value) return $value;
}
return $value;
}
#############################################################################
/** vérifier si $value est un booléen, sinon retourner null */
static final function check_bool($value): ?bool {
return is_bool($value)? $value: null;
}
/** vérifier si $value est un integer, sinon retourner null */
static final function check_int($value): ?int {
return is_int($value)? $value: null;
}
/** vérifier si $value est un string, sinon retourner null */
static final function check_string($value): ?string {
return is_string($value)? $value: null;
}
/** vérifier si $value est un array, sinon retourner null */
static final function check_array($value): ?array {
return is_array($value)? $value: null;
}
/**
* retourner [$index, $key] initialisés chacun en fonction du type de $value
* (index est numérique, key est une chaine)
*
* lever une exception si $value n'est d'aucun de ces types
*/
static final function check_key($value, ?string $prefix=null, bool $throw_exception=true): array {
$index = is_int($value)? $value : null;
$key = is_string($value)? $value : null;
if ($index === null && $key === null && $throw_exception) {
throw ValueException::invalid_kind($value, "key", $prefix);
} else {
return [$index, $key];
}
}
/**
* retourner [$bool, $string, $array] initialisés chacun en fonction du type
* de $value.
*
* @throws ValueException si $value n'est d'aucun de ces types
*/
static final function check_bsa($value, ?string $prefix=null, bool $throw_exception=true): array {
$bool = is_bool($value)? $value : null;
$string = is_string($value)? $value : null;
$array = is_array($value)? $value : null;
if ($bool === null && $string === null && $array === null && $throw_exception) {
throw ValueException::invalid_kind($value, "value", $prefix);
} else {
return [$bool, $string, $array];
}
}
#############################################################################
/** retourner -1, 0 ou 1 en fonction de l'ordre relatif entre $a et $b */
static final function compare($a, $b): int {
if ($a === $b) return 0;
if ($a < $b) return -1;
if ($a > $b) return 1;
return 0;
}
}

View File

@ -1,4 +1,4 @@
# nulib\os
# nur\sery\os
Ce package contient des services permettant d'interagir avec le système d'exploitation

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\os\file;
use nulib\ValueException;
use nur\sery\ValueException;
class SharedFile extends FileWriter {
const USE_LOCKING = true;

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\os\file;
use nulib\str;
use nulib\ValueException;
use nur\sery\str;
use nur\sery\ValueException;
use nur\sery\os\EOFException;
use nur\sery\os\IOException;
use nur\sery\php\iter\AbstractIterator;

View File

@ -1,7 +1,7 @@
<?php # -*- coding: utf-8 mode: php -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
namespace nur\sery\os;
use nulib\str;
use nur\sery\str;
/**
* Class path: manipulation de chemins du système de fichiers

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\os;
use nulib\cl;
use nur\sery\cl;
use RuntimeException;
class sh {

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\output;
use nulib\str;
use nulib\ValueException;
use nur\sery\str;
use nur\sery\ValueException;
/**
* Class _messenger: classe de base pour say, log et msg

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\output;
use nulib\ValueException;
use nur\sery\ValueException;
use nur\sery\output\std\ProxyMessenger;
/**

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\output;
use nulib\ValueException;
use nur\sery\ValueException;
use nur\sery\output\std\ProxyMessenger;
/**

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\output;
use nulib\ValueException;
use nur\sery\ValueException;
use nur\sery\output\std\ProxyMessenger;
/**

View File

@ -2,9 +2,9 @@
namespace nur\sery\output\std;
use Exception;
use nulib\cl;
use nulib\ExceptionShadow;
use nulib\UserException;
use nur\sery\cl;
use nur\sery\ExceptionShadow;
use nur\sery\UserException;
use nur\sery\output\IMessenger;
use Throwable;

View File

@ -2,7 +2,7 @@
namespace nur\sery\output\std;
use Exception;
use nulib\cl;
use nur\sery\cl;
use nur\sery\os\file\Stream;
use nur\sery\php\content\content;
use nur\sery\php\content\IContent;

View File

@ -1,4 +1,4 @@
# nulib\php
# nur\sery\php
Ce package contient des services généraux spécifiques à PHP

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\php\coll;
use nulib\cl;
use nur\sery\cl;
/**
* Class AutoArray: un tableau dont les éléments peuvent être accédés comme des

View File

@ -4,7 +4,7 @@ namespace nur\sery\php\coll;
use ArrayAccess;
use Countable;
use Iterator;
use nulib\cl;
use nur\sery\cl;
/**
* Class BaseArray: implémentation de base d'un objet array-like, qui peut aussi

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\php\content;
use nulib\cl;
use nur\sery\cl;
use nur\sery\php\func;
/**

View File

@ -2,8 +2,8 @@
namespace nur\sery\php;
use Closure;
use nulib\cl;
use nulib\ValueException;
use nur\sery\cl;
use nur\sery\ValueException;
use nur\sery\ref\php\ref_func;
use nur\sery\schema\Schema;
use ReflectionClass;

View File

@ -3,7 +3,7 @@ namespace nur\sery\php\iter;
use Exception;
use Iterator;
use nulib\DataException;
use nur\sery\DataException;
use nur\sery\php\ICloseable;
/**

View File

@ -1,4 +1,4 @@
# nulib\schema
# nur\sery\schema
objet: s'assurer que des données soit dans un type particulier, en les
convertissant si nécessaire. la source de ces données peut-être diverse:

View File

@ -3,8 +3,8 @@ namespace nur\sery\schema;
use ArrayAccess;
use LogicException;
use nulib\AccessException;
use nulib\cl;
use nur\sery\AccessException;
use nur\sery\cl;
use nur\sery\schema\_assoc\AssocSchema;
use nur\sery\schema\_list\ListSchema;
use nur\sery\schema\_scalar\ScalarSchema;

View File

@ -1,4 +1,4 @@
# nulib\schema
# nur\sery\schema
* implémenter support `analyzer_func`, `extractor_func`, `parser_func`,
`normalizer_func`, `formatter_func`

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\schema\_assoc;
use nulib\cl;
use nur\sery\cl;
use nur\sery\ref\schema\ref_schema;
use nur\sery\schema\_scalar\ScalarValue;
use nur\sery\schema\input\Input;

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\schema\_scalar;
use nulib\cl;
use nulib\ValueException;
use nur\sery\cl;
use nur\sery\ValueException;
use nur\sery\ref\schema\ref_analyze;
use nur\sery\ref\schema\ref_schema;
use nur\sery\schema\Result;

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\schema\_scalar;
use nulib\cl;
use nur\sery\cl;
use nur\sery\ref\schema\ref_schema;
use nur\sery\ref\schema\ref_types;
use nur\sery\schema\Schema;

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\schema\_scalar;
use nulib\ValueException;
use nur\sery\ValueException;
use nur\sery\schema\input\Input;
use nur\sery\ref\schema\ref_analyze;
use nur\sery\schema\Result;

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\schema\input;
use nulib\cl;
use nur\sery\cl;
/**
* Class Input: accès à une valeur

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\schema\types;
use nulib\cl;
use nur\sery\cl;
class Registry {
const TYPES = [

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\schema\types;
use nulib\cl;
use nur\sery\cl;
abstract class tarray implements IType {
static function ensure_array(&$array): void {

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\schema\types;
use nulib\cl;
use nulib\ValueException;
use nur\sery\cl;
use nur\sery\ValueException;
use nur\sery\schema\_scalar\ScalarResult;
use nur\sery\schema\_scalar\ScalarSchema;
use nur\sery\schema\input\Input;

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\schema\types;
use nulib\ValueException;
use nur\sery\ValueException;
abstract class tcallable implements IType {
static function ensure_callable(&$callable): void {

389
src/str.php Normal file
View File

@ -0,0 +1,389 @@
<?php
namespace nur\sery;
use nur\base;
/**
* Class str: gestion des chaines de caractère "simples"
*/
class str {
/**
* Retourner $s converti en chaine non nulle, ou "" si $s est fausse (cela
* n'inclue pas la chaine "0")
*/
static final function with($s): string {
if (!is_string($s)) {
if (!$s) return "";
else $s = strval($s);
}
return $s;
}
/**
* Retourner $s converti en chaine non nulle, ou "" si $s est fausse selon les
* règles de PHP
*/
static final function pwith($s): string {
if (!is_string($s)) {
if (!$s) return "";
else $s = strval($s);
}
return $s?: "";
}
/**
* tronquer si nécessaire $s à la valeur $length.
* la chaine $suffix est rajoutée le cas échéant de façon que la taille
* totale n'excède pas $length caractères.
*
* si $ellips est true et que le troncage est nécessaire, remplacer les 3
* derniers caractères par "..."
*/
static final function trunc(?string $s, int $length, bool $ellips=false, ?string $suffix=null): ?string {
if ($s === null) return null;
if ($suffix !== null) $length -= strlen($suffix);
if (strlen($s) > $length) {
if ($ellips && $length > 3) $s = substr($s, 0, $length - 3)."...";
else $s = substr($s, 0, $length);
}
if ($suffix !== null) $s .= $suffix;
return $s;
}
/** trimmer $s */
static final function trim(?string $s): ?string {
if ($s === null) return null;
else return trim($s);
}
/** trimmer $s à gauche */
static final function ltrim(?string $s): ?string {
if ($s === null) return null;
else return ltrim($s);
}
/** trimmer $s à droite */
static final function rtrim(?string $s): ?string {
if ($s === null) return null;
else return rtrim($s);
}
static final function left(?string $s, int $size): ?string {
if ($s === null) return null;
else return str_pad($s, $size);
}
static final function right(?string $s, int $size): ?string {
if ($s === null) return null;
else return str_pad($s, $size, " ", STR_PAD_LEFT);
}
static final function center(?string $s, int $size): ?string {
if ($s === null) return null;
else return str_pad($s, $size, " ", STR_PAD_BOTH);
}
static final function pad0(?string $s, int $size): ?string {
if ($s === null) return null;
else return str_pad($s, $size, "0", STR_PAD_LEFT);
}
static final function lower(?string $s): ?string {
if ($s === null) return null;
else return strtolower($s);
}
static final function lower1(?string $s): ?string {
if ($s === null) return null;
else return lcfirst($s);
}
static final function upper(?string $s): ?string {
if ($s === null) return null;
else return strtoupper($s);
}
static final function upper1(?string $s): ?string {
if ($s === null) return null;
else return ucfirst($s);
}
static final function upperw(?string $s, ?string $delimiters=null): ?string {
if ($s === null) return null;
if ($delimiters !== null) return ucwords($s, $delimiters);
else return ucwords($s, " _-\t\r\n\f\v");
}
protected static final function _starts_with(string $prefix, string $s, ?int $min_len=null): bool {
if ($prefix === $s) return true;
$len = strlen($prefix);
if ($min_len !== null && ($len < $min_len || $len > strlen($s))) return false;
return $len == 0 || $prefix === substr($s, 0, $len);
}
/**
* tester si $s commence par $prefix
* par exemple:
* - starts_with("", "whatever") est true
* - starts_with("fi", "first") est true
* - starts_with("no", "yes") est false
*
* si $min_len n'est pas null, c'est la longueur minimum requise de $prefix
* pour qu'on teste la correspondance. dans le cas contraire, la valeur de
* retour est toujours false, sauf s'il y a égalité. e.g
* - starts_with("a", "abc", 2) est false
* - starts_with("a", "a", 2) est true
*/
static final function starts_with(?string $prefix, ?string $s, ?int $min_len=null): bool {
if ($s === null || $prefix === null) return false;
else return self::_starts_with($prefix, $s, $min_len);
}
/** Retourner $s sans le préfixe $prefix s'il existe */
static final function without_prefix(?string $prefix, ?string $s): ?string {
if ($s === null || $prefix === null) return $s;
if (self::_starts_with($prefix, $s)) $s = substr($s, strlen($prefix));
return $s;
}
/**
* modifier $s en place pour supprimer le préfixe $prefix s'il existe
*
* retourner true si le préfixe a été enlevé.
*/
static final function del_prefix(?string &$s, ?string $prefix): bool {
if ($s === null || !self::_starts_with($prefix, $s)) return false;
$s = self::without_prefix($prefix, $s);
return true;
}
/**
* Retourner $s avec le préfixe $prefix
*
* Si $unless_exists, ne pas ajouter le préfixe s'il existe déjà
*/
static final function with_prefix(?string $prefix, ?string $s, ?string $sep=null, bool $unless_exists=false): ?string {
if ($s === null || $prefix === null) return $s;
if (!self::_starts_with($prefix, $s) || !$unless_exists) $s = $prefix.$sep.$s;
return $s;
}
/**
* modifier $s en place pour ajouter le préfixe $prefix
*
* retourner true si le préfixe a été ajouté.
*/
static final function add_prefix(?string &$s, ?string $prefix, bool $unless_exists=true): bool {
if (($s === null || self::_starts_with($prefix, $s)) && $unless_exists) return false;
$s = self::with_prefix($prefix, $s, null, $unless_exists);
return true;
}
protected static final function _ends_with(string $suffix, string $s, ?int $min_len=null): bool {
if ($suffix === $s) return true;
$len = strlen($suffix);
if ($min_len !== null && ($len < $min_len || $len > strlen($s))) return false;
return $len == 0 || $suffix === substr($s, -$len);
}
/**
* tester si $string se termine par $suffix
* par exemple:
* - ends_with("", "whatever") est true
* - ends_with("st", "first") est true
* - ends_with("no", "yes") est false
*
* si $min_len n'est pas null, c'est la longueur minimum requise de $prefix
* pour qu'on teste la correspondance. dans le cas contraire, la valeur de
* retour est toujours false, sauf s'il y a égalité. e.g
* - ends_with("c", "abc", 2) est false
* - ends_with("c", "c", 2) est true
*/
static final function ends_with(?string $suffix, ?string $s, ?int $min_len=null): bool {
if ($s === null || $suffix === null) return false;
else return self::_ends_with($suffix, $s, $min_len);
}
/** Retourner $s sans le suffixe $suffix s'il existe */
static final function without_suffix(?string $suffix, ?string $s): ?string {
if ($s === null || $suffix === null) return $s;
if (self::_ends_with($suffix, $s)) $s = substr($s, 0, -strlen($suffix));
return $s;
}
/**
* modifier $s en place pour supprimer le suffixe $suffix s'il existe
*
* retourner true si le suffixe a été enlevé.
*/
static final function del_suffix(?string &$s, ?string $suffix): bool {
if ($s === null || !self::_ends_with($suffix, $s)) return false;
$s = self::without_suffix($suffix, $s);
return true;
}
/**
* Retourner $s avec le suffixe $suffix
*
* Si $unless_exists, ne pas ajouter le suffixe s'il existe déjà
*/
static final function with_suffix(?string $suffix, ?string $s, ?string $sep=null, bool $unless_exists=false): ?string {
if ($s === null || $suffix === null) return $s;
if (!self::_ends_with($suffix, $s) || !$unless_exists) $s = $s.$sep.$suffix;
return $s;
}
/**
* modifier $s en place pour ajouter le suffixe $suffix
*
* retourner true si le suffixe a été ajouté.
*/
static final function add_suffix(?string &$s, ?string $suffix, bool $unless_exists=true): bool {
if (($s === null || self::_ends_with($suffix, $s)) && $unless_exists) return false;
$s = self::with_suffix($suffix, $s, null, $unless_exists);
return true;
}
/** splitter $s en deux chaines séparées par $sep */
static final function split_pair(?string $s, string $sep=":"): array {
if ($s === null) return [null, null];
$parts = explode($sep, $s, 2);
if ($parts === false) return [null, null];
if (count($parts) < 2) $parts[] = null;
return $parts;
}
/** retourner $line sans son caractère de fin de ligne */
static final function strip_nl(?string $line): ?string {
if ($line === null) return null;
if (substr($line, -2) == "\r\n") {
$line = substr($line, 0, -2);
} elseif (substr($line, -1) == "\n") {
$line = substr($line, 0, -1);
} elseif (substr($line, -1) == "\r") {
$line = substr($line, 0, -1);
}
return $line;
}
/**
* normaliser le caractère de fin de ligne: tous les occurrences de [CR]LF et CR sont remplacées par LF
*/
static final function norm_nl(?string $s): ?string {
if ($s === null) return null;
$s = str_replace("\r\n", "\n", $s);
$s = str_replace("\r", "\n", $s);
return $s;
}
/**
* joindre les éléments de $parts comme avec implode(), mais en ignorant les
* valeurs fausses (cela n'inclue pas la chaine "0")
*
* pour chaque valeur du tableau avec une clé associative, c'est la clé qui
* est utilisée mais uniquement si la valeur est vraie
*/
static final function join(string $glue, ?iterable $values): ?string {
if ($values === null) return null;
$pieces = [];
$index = 0;
foreach ($values as $key => $value) {
if (is_array($value)) $value = self::join($glue, $value);
if ($key === $index) {
$index++;
if (cv::t($value)) $pieces[] = $value;
} elseif (cv::t($value)) {
$pieces[] = $key;
}
}
return implode($glue, $pieces);
}
/**
* comme {@link join()} mais en ignorant les valeurs fausses selon les règles
* de PHP
*/
static final function pjoin(string $glue, ?iterable $values): ?string {
if ($values === null) return null;
$pieces = [];
$index = 0;
foreach ($values as $key => $value) {
if (is_array($value)) $value = self::join($glue, $value);
if ($key === $index) {
$index++;
if ($value) $pieces[] = $value;
} elseif ($value) {
$pieces[] = $key;
}
}
return implode($glue, $pieces);
}
const CAMEL_PATTERN0 = '/([A-Z0-9]+)$/A';
const CAMEL_PATTERN1 = '/([A-Z0-9]+)[A-Z]/A';
const CAMEL_PATTERN2 = '/([^A-Z]+)/A';
const CAMEL_PATTERN3 = '/([A-Z][^A-Z]*)/A';
/**
* convertir une chaine de la forme "camelCase" en "under_score". le premier
* ensemble de caractères en majuscule est considéré comme étant en minuscule
*
* par exemple:
* - 'myCamelCase' devient 'my_camel_case'
* - 'AValue' devient 'a_value'
* - 'UPPERValue' devient 'upper_value'
* - 'UPPER' devient 'upper'
* - 'aXYZ' devient 'a_x_y_z'
*
* $delimiter est le séparateur en sortie ('_' par défaut)
* $upper indique s'il faut transformer le résultat en majuscule
*/
static final function camel2us(?string $camel, bool $upper=false, string $delimiter="_"): ?string {
if ($camel === null || $camel === "") return $camel;
$parts = [];
if (preg_match(self::CAMEL_PATTERN0, $camel, $vs, PREG_OFFSET_CAPTURE)) {
# que des majuscules
} elseif (preg_match(self::CAMEL_PATTERN1, $camel, $vs, PREG_OFFSET_CAPTURE)) {
# préfixe en majuscule
} elseif (preg_match(self::CAMEL_PATTERN2, $camel, $vs, PREG_OFFSET_CAPTURE)) {
# préfixe en minuscule
} else {
throw ValueException::invalid_kind($camel, "camel string");
}
$parts[] = strtolower($vs[1][0]);
$index = intval($vs[1][1]) + strlen($vs[1][0]);
while (preg_match(self::CAMEL_PATTERN3, $camel, $vs, PREG_OFFSET_CAPTURE, $index)) {
$parts[] = strtolower($vs[1][0]);
$index = intval($vs[1][1]) + strlen($vs[1][0]);
}
$us = implode($delimiter, $parts);
if ($upper) $us = strtoupper($us);
return $us;
}
const US_PATTERN = '/([ _\-\t\r\n\f\v])/';
/**
* convertir une chaine de la forme "under_score" en "camelCase"
*
* par exemple, 'my_camel_case' devient 'myCamelCalse'
* et 'UPPER_VALUE' devient 'upperValue'
*
* si la chaine de départ ne contient pas de delimiter, e.g 'myValue', elle
* est retournée inchangée
*/
static final function us2camel(?string $us, ?string $delimiters=null): ?string {
if ($us === null || $us === "") return $us;
if ($delimiters === null) $pattern = self::US_PATTERN;
else $pattern = '/(['.preg_quote($delimiters).'])/';
$parts = preg_split($pattern, $us);
$count = count($parts);
if ($count == 1) return $us;
for ($i = 0; $i < $count; $i++) {
$part = strtolower($parts[$i]);
if ($i > 0) $part = ucfirst($part);
$parts[$i] = $part;
}
return implode("", $parts);
}
}

View File

@ -2,7 +2,7 @@
namespace nur\sery\values;
use ArrayAccess;
use nulib\cl;
use nur\sery\cl;
/**
* Class akey: accéder aux valeurs d'un tableau

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\values;
use nulib\cl;
use nulib\str;
use nur\sery\cl;
use nur\sery\str;
use nur\sery\php\func;
use ReflectionClass;
use ReflectionException;

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\values;
use nulib\cl;
use nur\sery\cl;
use ReflectionClass;
use ReflectionException;

View File

@ -2,7 +2,7 @@
namespace nur\sery\values;
use ArrayAccess;
use nulib\str;
use nur\sery\str;
/**
* Class valm: frontend pour accéder à une propriété d'un objet (par une méthode

View File

@ -2,7 +2,7 @@
namespace nur\sery\values;
use ArrayAccess;
use nulib\str;
use nur\sery\str;
/**
* Class valx: frontend pour accéder à une propriété d'un objet ou à la clé

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\web\ext;
use nulib\UserException;
use nur\sery\UserException;
use Throwable;
class CurlException extends UserException {

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\web;
use nulib\cv;
use nur\sery\cv;
/**
* Class http: des outils pour parler le protocole HTTP dans le cadre de la

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\web\params;
use nulib\str;
use nur\sery\str;
/**
* Class F: gestion unifiée des paramètres POST + GET

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\web\params;
use nulib\cl;
use nulib\str;
use nur\sery\cl;
use nur\sery\str;
/**
* Class G: gestion des paramètres $_GET

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\web\params;
use nulib\cl;
use nulib\str;
use nur\sery\cl;
use nur\sery\str;
/**
* Class P: gestion des paramètres $_POST

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\web\params;
use nulib\cl;
use nulib\str;
use nur\sery\cl;
use nur\sery\str;
/**
* Class R: gestion des paramètres $_REQUEST (GET + POST + COOKIES)

View File

@ -2,7 +2,7 @@
namespace nur\sery\wip\cli;
use Exception;
use nulib\ExitException;
use nur\sery\ExitException;
use nur\sery\output\msg;
use nur\sery\output\std\StdMessenger;

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\wip\cli;
use nulib\UserException;
use nur\sery\UserException;
/**
* Class ArgsException: exception lancée quand il y a une erreur dans l'analyse

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\wip\cli;
use nulib\str;
use nur\sery\str;
use nur\A;
use nur\sery\php\func;
use nur\sery\ref\cli\ref_args;