importation initiale

This commit is contained in:
Jephté Clain 2023-10-03 04:17:01 +04:00
commit b20a040c70
16 changed files with 3115 additions and 0 deletions

10
.composer.yaml Normal file
View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 mode: yaml -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
composer_php_min: '7.3'
composer_php_max: '8.0'
composer_registry: pubdocker.univ-reunion.fr
composer_image: image/phpbuilder:d10
require:
branch:
master:
develop:
wip:

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
/sbin/composer.phar -delta

18
.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
/.idea/**/workspace.xml
/.idea/**/tasks.xml
/.idea/**/usage.statistics.xml
/.idea/**/dictionaries
/.idea/**/shelf
/.idea/**/contentModel.xml
/.idea/**/dataSources/
/.idea/**/dataSources.ids
/.idea/**/dataSources.local.xml
/.idea/**/sqlDataSources.xml
/.idea/**/dynamic.xml
/.idea/**/uiDesigner.xml
/.idea/**/dbnavigator.xml
/.phpunit.result.cache
.~lock*#
.*.swp
/vendor/

1
bin/composer Symbolic link
View File

@ -0,0 +1 @@
runphp

52
bin/runphp Executable file
View File

@ -0,0 +1,52 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
MYDIR="$(dirname -- "$0")"; MYNAME="$(basename -- "$0")"
function die() { echo 1>&2 "ERROR: $*"; exit 1; }
case "$MYNAME" in
runphp) ;;
composer)
if [ -f "$MYDIR/composer.phar" ]; then
set -- "$MYDIR/composer.phar" "$@"
elif [ -f "$MYDIR/../sbin/composer.phar" ]; then
set -- "$MYDIR/../sbin/composer.phar" "$@"
elif [ -f "/usr/bin/composer" ]; then
set -- "/usr/bin/composer" "$@"
else
set -- "" "$@"
fi
;;
*) die "$MYNAME: nom de script invalide";;
esac
function runphp_help() {
echo "$MYNAME: lance un programme PHP en sélectionnant une version en particulier
USAGE
$MYNAME [options] <SCRIPT.php> [args...]
OPTIONS
-s, --min PHP_MIN
-m, --max PHP_MAX
-i, --image IMAGE"
}
SOPTS=+smi
LOPTS=help,php-min,min,php-max,max,image
args="$(getopt -n runphp -o "$SOPTS" -l "$LOPTS" -- "$@")" || exit 1; eval "set -- $args"
while [ $# -gt 0 ]; do
case "$1" in
--) shift; break;;
--help) runphp_help; exit 0;;
*) die "$1: option non configurée";;
esac
shift
done
script="$1"; shift
[ -n "$script" ] || die "vous devez spécifier le script à lancer"
[ -f "$script" ] || die "$script: script introuvable"
scriptdir="$(dirname -- "$script")"
scritname="$(basename -- "$script")"

35
composer.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "nucore/php",
"type": "library",
"description": "fonctions et classes essentielles",
"repositories": [
{
"type": "composer",
"url": "https://repos.univ-reunion.fr/composer"
}
],
"require": {
"php": ">=7.3"
},
"require-dev": {
"mur/tests": "7.3"
},
"autoload": {
"psr-4": {
"nucore\\": "php/src_base"
}
},
"autoload-dev": {
"psr-4": {
"nucore\\": "php/tests"
}
},
"scripts": {
},
"authors": [
{
"name": "Jephte Clain",
"email": "Jephte.Clain@univ-reunion.fr"
}
]
}

1785
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

5
php/run-tests Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
MYDIR="$(dirname -- "$0")"
VENDOR="$MYDIR/../vendor"
"$VENDOR/bin/phpunit" --bootstrap "$VENDOR/autoload.php" "$@" "$MYDIR/tests"

View File

@ -0,0 +1,31 @@
<?php
namespace nucore;
use Exception;
/**
* 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 Exception {
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);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace nucore;
use Exception;
/**
* Class StateException: indiquer que l'état dans lequel on se trouve est
* inattendu: il s'agit donc d'un bug
*/
class StateException extends Exception {
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);
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace nucore;
use Exception;
/**
* Class ValueException: indiquer qu'une valeur est invalide
*/
class ValueException extends Exception {
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)."]";
} 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, " is null"));
}
static final function invalid($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 forbidden($value=null, ?string $kind=null, ?string $prefix=null, ?string $message=null): self {
return new static(self::message($value, $message, $kind, $prefix, " is forbidden"));
}
}

467
php/src_base/cl.php Normal file
View File

@ -0,0 +1,467 @@
<?php
namespace nucore;
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 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 {
if ($pkey !== null && !is_array($pkey)) {
$pkey = explode(".", strval($pkey));
}
if ($pkey === null || $pkey === []) return true;
$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) {
if ($pkey !== null && !is_array($pkey)) {
$pkey = explode(".", strval($pkey));
}
if ($pkey === null || $pkey === []) return true;
$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 {
if ($pkey !== null && !is_array($pkey)) {
$pkey = explode(".", strval($pkey));
}
if ($pkey === null || $pkey === []) {
$array = $value;
return;
}
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 $keys fourni sous forme de tableau
*/
static final function pdel_a(&$array, ?array $pkey): void {
}
/**
* 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 {
if ($array === false || $array === null) return;
if ($pkey !== null && !is_array($pkey)) {
$pkey = explode(".", strval($pkey));
}
if ($pkey === null || $pkey === []) {
$array = null;
return;
}
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 (cstr::del_prefix($key, "+")) $w = 1;
elseif (cstr::del_prefix($key, "-")) $w = -1;
elseif (cstr::del_suffix($key, "|asc")) $w = 1;
elseif (cstr::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;
}
}

367
php/src_base/cstr.php Normal file
View File

@ -0,0 +1,367 @@
<?php
namespace nucore;
/**
* Class cstr: gestion des chaines de caractère "simples"
*/
class cstr {
/**
* 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 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, ?array $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, ?array $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($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);
}
}

216
php/src_base/cv.php Normal file
View File

@ -0,0 +1,216 @@
<?php
namespace nucore;
/**
* 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($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($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;
}
}

56
php/tests/cstrTest.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace nucore;
use mur\tests\TestCase;
class cstrTest extends TestCase {
function testSplit_pair() {
self::assertSame([null, null], cstr::split_pair(null));
self::assertSame(["", null], cstr::split_pair(""));
self::assertSame(["first", null], cstr::split_pair("first"));
self::assertSame(["first", ""], cstr::split_pair("first:"));
self::assertSame(["first", "second"], cstr::split_pair("first:second"));
self::assertSame(["first", "second:third"], cstr::split_pair("first:second:third"));
}
function testXxxSuffix() {
$s = "blahSUF";
self::assertTrue(cstr::del_suffix($s, "SUF"));
self::assertSame("blah", $s);
self::assertFalse(cstr::del_suffix($s, "SUF"));
self::assertTrue(cstr::add_suffix($s, "SUF"));
self::assertSame("blahSUF", $s);
self::assertFalse(cstr::add_suffix($s, "SUF"));
}
function testXxxPrefix() {
$s = "PREblah";
self::assertTrue(cstr::del_prefix($s, "PRE"));
self::assertSame("blah", $s);
self::assertFalse(cstr::del_prefix($s, "PRE"));
self::assertTrue(cstr::add_prefix($s, "PRE"));
self::assertSame("PREblah", $s);
self::assertFalse(cstr::add_prefix($s, "PRE"));
}
function testUs2camel() {
# avec délimiteur
self::assertSame("myCamel", cstr::us2camel("my_camel"));
self::assertSame("myCamel", cstr::us2camel("MY_CAMEL"));
self::assertSame("myCamel", cstr::us2camel("My_Camel"));
self::assertSame("myCamel", cstr::us2camel("mY_cAMEL"));
self::assertSame("myCamel", cstr::us2camel("my-camel"));
self::assertSame("myCamel", cstr::us2camel("my camel"));
self::assertSame("myCamel", cstr::us2camel("my\tcamel"));
self::assertSame("myCamel", cstr::us2camel("my\rcamel"));
self::assertSame("myCamel", cstr::us2camel("my\ncamel"));
self::assertSame("myCamel", cstr::us2camel("my\fcamel"));
self::assertSame("myCamel", cstr::us2camel("my\vcamel"));
# sans délimiteur
self::assertSame("myCamel", cstr::us2camel("myCamel"));
self::assertSame("MyCamel", cstr::us2camel("MyCamel"));
self::assertSame("MyCaMEL", cstr::us2camel("MyCaMEL"));
}
}

BIN
sbin/composer.phar Executable file

Binary file not shown.