<pman>Intégration de la branche rel74-0.7.0

This commit is contained in:
Jephté Clain 2025-10-22 18:34:02 +04:00
commit 64ab6c70e4
29 changed files with 1026 additions and 46 deletions

View File

@ -1,3 +1,11 @@
## Release 0.7.0p74 du 22/10/2025-18:33
* `5e41e7d` gestion simplifiée des schéma
* `efb4f03` importation Hour et Time et d'autres vclasses
* `4f0d6d4` function isa() pour les vclasses
* `cfb8f0d` corriger la prise en compte des configurations de branche
* nombreuses autres modifications non documentées ici
## Release 0.6.1p82 du 03/06/2025-10:22
## Release 0.6.1p74 du 03/06/2025-10:21

View File

@ -1 +1 @@
0.6.1
0.7.0

View File

@ -366,7 +366,8 @@ function load_config() {
ac_set_tmpfile config
git show "$ConfigBranch:.pman.conf" >"$config" 2>/dev/null
[ -s "$config" ] || die "$ConfigBranch: aucune configuration trouvée sur cette branche" || return
source "$config"
ConfigFile="$config"
source "$ConfigFile"
fi
elif [ -f .pman.conf ]; then
ConfigFile="$(pwd)/.pman.conf"
@ -681,9 +682,7 @@ function _create_upstream_action() {
# faire une copie de la configuration actuelle
local config; ac_set_tmpfile config
set -x; ls -l "$ConfigFile" #XXX
cp "$ConfigFile" "$config"
set +x #XXX
einfo "Création de la branche $RefBranch"
git checkout --orphan "$RefBranch" || die

View File

@ -9,4 +9,4 @@ args=(
)
parse_args "$@"; set -- "${args[@]}"
exec "$MYDIR/ptool" -fupstream -Bdev82 -m --tech-merge ${dev74:+-a "git checkout dev74"} "$@"
exec "$MYDIR/ptool" -fupstream -Bdev82 --tech-merge ${dev74:+-a "git checkout dev74"} "$@"

View File

@ -221,6 +221,9 @@ if check_gitdir; then
set_pman_vars "$merge_dir"
load_branches current
loaded_config=1
# remettre à zéro ConfigFile pour ne pas influencer le rechargement plus bas
oConfigFile="$ConfigFile"
ConfigFile=
else
set_pman_vars "$merge_dir"
fi
@ -377,6 +380,9 @@ if [ -z "$loaded_config" -o -n "$chdir" -o -n "$ConfigFile" -o -n "$ConfigBranch
load_config
set_pman_vars "$merge_dir"
load_branches current
else
# restaurer ConfigFile précédent
ConfigFile="$oConfigFile"
fi
resolve_should_push quiet

View File

@ -6,38 +6,26 @@ use nulib\cl;
use nulib\cv;
use nulib\exceptions;
use nulib\output\msg;
use nulib\php\types\vbool;
use nulib\str;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
class mailer {
private static function is_bool(&$value): bool {
if ($value === null) {
return false;
} elseif (is_bool($value)) {
if ($value === null) return false;
elseif (is_bool($value)) return true;
elseif (is_numeric($value)) {
$value = $value != 0;
return true;
} elseif (is_int($value)) {
$value = boolval($value);
} elseif (vbool::is_yes($value)) {
$value = true;
return true;
} elseif ($value === "" || vbool::is_no($value)) {
$value = false;
return true;
} else {
switch (strval($value)) {
case "":
case "0":
case "no":
case "off":
case "false":
$value = false;
return true;
case "1":
case "yes":
case "on":
case "true":
$value = true;
return true;
default:
return false;
}
}
return false;
}
private static function get_bool($value): bool {
@ -120,19 +108,14 @@ class mailer {
$mailer->SMTPDebug = $debug;
# auth, username, password
$username = $params["username"] ?? null;
$username ??= cv::vn(getenv("NULIB_MAIL_USERNAME"));
$password = $params["password"] ?? null;
$password ??= cv::vn(getenv("NULIB_MAIL_PASSWORD"));
$auth = $params["auth"] ?? null;
$auth ??= cv::vn(getenv("NULIB_MAIL_AUTH"));
$auth ??= $username !== null && $password !== null;
$mailer->SMTPAuth = self::get_bool($auth);
$mailer->Username = $username;
$mailer->Password = $password;
# secure
$secure = $params["secure"] ?? null;
$secure ??= cv::vn(getenv("NULIB_MAIL_SECURE"));
$secure ??= false;
$secure = $params["secure"] ?? false;
if (self::is_bool($secure)) {
if (!$secure) {
$mailer->SMTPSecure = "";

16
php/src/php/time/Hour.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace nulib\php\time;
class Hour extends Time {
const UNIT = self::UNIT_MINUTES;
const FORMAT = "%hh%M";
function format(?string $format=null): string {
$string = parent::format($format);
if (substr($string, -3) == "h00") {
$string = substr($string, 0, -2);
}
return $string;
}
}

288
php/src/php/time/Time.php Normal file
View File

@ -0,0 +1,288 @@
<?php
namespace nulib\php\time;
use DateTimeInterface;
use nulib\exceptions;
use nulib\php\types\vbool;
use nulib\php\types\vint;
/**
* Class Time: une heure allant de 0h à 24h inclus.
*
* la seule utilisation autorisée de "24h" est comme borne supérieure pour une
* plage horaire.
*/
class Time {
const UNIT_HOURS = 3600;
const UNIT_MINUTES = 60;
const UNIT_SECONDS = 1;
const PATTERNS = [
'/^(\d+)[:.h](\d+)(?:[:.](\d+))?$/',
'/^(\d+)h(?:(\d+)(?:[:.](\d+))?)?$/'
];
/** @var string format par défaut pour l'affichage */
const FORMAT = "%H:%M:%S";
static function with($time): self {
if ($time instanceof static) return $time;
else return new static($time);
}
static function withn($time): ?self {
if ($time === null) return null;
elseif ($time instanceof static) return $time;
else return new static($time);
}
static function isa_time($time): bool {
if ($time === null) return false;
if ($time instanceof self) return true;
if (is_int($time)) return true;
if (is_string($time)) {
foreach (self::PATTERNS as $pattern) {
if (preg_match($pattern, $time)) return true;
}
}
if ($time instanceof DateTimeInterface) return true;
if (is_array($time) && count($time) == 3) return true;
return false;
}
protected static function s_now(): int {
$hms = date('His');
return intval(substr($hms, 0, 2)) * 3600
+ intval(substr($hms, 2, 2)) * 60
+ intval(substr($hms, 4, 2));
}
protected static function s_get($time, int $unit=self::UNIT_SECONDS): int {
if ($time === null) {
return 0;
} elseif ($time instanceof self) {
return $time->getSeconds();
} elseif (is_int($time)) {
return $time * $unit;
} elseif (is_string($time)) {
$matched = false;
foreach (self::PATTERNS as $pattern) {
if (preg_match($pattern, $time, $ms)) {
$matched = true;
break;
}
}
if ($matched) {
$h = $ms[1];
$m = $ms[2] ?? 0;
$s = $ms[3] ?? 0;
return intval($h) * 3600 + intval($m) * 60 + intval($s);
}
} elseif ($time instanceof DateTimeInterface) {
$hms = $time->format('His');
return intval(substr($hms, 0, 2)) * 3600
+ intval(substr($hms, 2, 2)) * 60
+ intval(substr($hms, 4, 2));
} elseif (is_array($time) && count($time) == 3) {
[$h, $m, $s] = $time;
return $h * 3600 + $m * 60 + $s;
}
throw exceptions::invalid_value($time, "time");
}
protected static function s_adjust(int &$seconds, int $unit=self::UNIT_SECONDS, ?int $step=null): int {
if ($step !== null) $unit *= $step;
$adjust = $seconds % $unit;
if ($seconds < 0) $adjust = -$adjust;
$seconds -= $adjust;
return $seconds;
}
protected static function s_wrap_start(int &$seconds): int {
while ($seconds < 0) $seconds += 86400;
if ($seconds >= 86400) $seconds %= 86400;
return $seconds;
}
protected static function s_wrap_end(int &$seconds): int {
while ($seconds < 0) $seconds += 86400;
if ($seconds > 86400) $seconds %= 86400;
return $seconds;
}
/**
* @var int la valeur de l'unité en secondes, pour le constructeur et les
* méthodes {@link setu()}, {@link addu()} et {@link subu()}
*/
const UNIT = self::UNIT_SECONDS;
/**
* @var int|null un nombre d'unité dont l'heure doit être multiple. par
* exemple, si l'unité est la minute, une valeur 5 permet d'avoir des heures
* qui vont de 5 en 5 minutes (0h00, 0h05, 0h10, etc.)
*/
const STEP = null;
/** @var bool s'il faut garder les heures dans la plage [0, 24h] */
const WRAP = true;
function __construct($time=null, ?array $params=null) {
$this->unit = vint::with($params["unit"] ?? static::UNIT);
$this->step = vint::withn($params["step"] ?? self::STEP);
$this->wrap = vbool::with($params["wrap"] ?? self::WRAP);
if ($time === null) $seconds = self::s_now();
else $seconds = self::s_get($time, $this->unit);
$this->setSeconds($seconds);
}
protected int $unit;
protected ?int $step;
protected bool $wrap;
protected int $seconds;
function getSeconds(): int {
return $this->seconds;
}
/**
* mettre à jour cet objet avec le nombre de secondes spécifié
*
* le nombre effectif de secondes est calculé en tenant compte de l'unité
* actuelle
*
* @return int le nombre de seconde effectif, après correction
*/
protected function setSeconds(?int $seconds): int {
$this->seconds = $seconds ?? self::s_now();
return $this->afterUpdate();
}
protected function afterUpdate(): int {
self::s_adjust($this->seconds, $this->unit, $this->step);
if ($this->wrap) $this->wrapEnd();
return $this->seconds;
}
/**
* wrapper l'heure pour la garder dans la plage [0h, 24h[ ce qui la rend
* propice à l'utilisation comme borne inférieure d'une période
*/
function wrapStart(): self {
self::s_wrap_start($this->seconds);
return $this;
}
/**
* wrapper l'heure pour la garder dans la plage [0h, 24h] ce qui la rend
* propice à l'utilisation comme borne supérieure d'une période
*/
function wrapEnd(): self {
self::s_wrap_end($this->seconds);
return $this;
}
/** formatter cette heure pour affichage */
function format(?string $format=null): string {
if ($format === null) $format = static::FORMAT;
$v = $this->seconds;
$h = intdiv($v, 3600); $v = $v % 3600;
$m = intdiv($v, 60);
$s = $v % 60;
$searches = [
"%H", "%h",
"%M", "%m",
"%S", "%s",
"%%",
];
$replaces = [
sprintf("%02u", $h), strval($h),
sprintf("%02u", $m), strval($m),
sprintf("%02u", $s), strval($s),
"%",
];
return str_replace($searches, $replaces, $format);
}
function __toString(): string {
return $this->format();
}
/** @return int le nombre d'unités */
function getu(): int {
return intdiv($this->seconds, $this->unit);
}
/** créer une nouvelle heure avec le nombre d'unités spécifiées */
function setu(int $count): self {
$this->setSeconds($count * $this->unit);
return $this;
}
/** créer une nouvelle heure en ajoutant à cette heure le nombre d'unités spécifiées */
function addu(int $count=1): self {
$count *= $this->unit;
if ($this->step !== null) $count *= $this->step;
$this->setSeconds($this->seconds + $count);
return $this;
}
/** créer une nouvelle heure en soustrayant à cette heure le nombre d'unités spécifiées */
function subu(int $count=1): self {
$count *= $this->unit;
if ($this->step !== null) $count *= $this->step;
$this->setSeconds($this->seconds - $count);
return $this;
}
/** forcer cette heure à l'heure spécifiée */
function set($time): self {
if ($time === null) $seconds = self::s_now();
else $seconds = self::with($time)->getSeconds();
$this->setSeconds($seconds);
return $this;
}
/** ajouter à cette heure l'heure spécifiée */
function add($time): self {
if ($time !== null) {
$this->setSeconds($this->seconds + self::with($time)->getSeconds());
}
return $this;
}
/** soustraire à cette heure l'heure spécifiée */
function sub($time): self {
if ($time !== null) {
$this->setSeconds($this->seconds - self::with($time)->getSeconds());
}
return $this;
}
/**
* comparer avec l'heure spécifiée. retourner une valeur négative, égale à
* zéro ou positive suivant le résultat de la comparaison
*/
function compare($time): int {
if ($time === null) return 1;
else return $this->seconds - self::with($time)->getSeconds();
}
/**
* tester si cette heure est avant ou égale à l'heure spécifiée
*/
function before($other): bool {
if ($other === null) return false;
else return $this->seconds <= self::with($other)->getSeconds();
}
/**
* tester si cette heure est après ou égale à l'heure spécifiée
*/
function after($other): bool {
if ($other === null) return true;
else return $this->seconds >= self::with($other)->getSeconds();
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
use nulib\str;
class _schema {
/** @var array association type simple / vclass */
const VCLASSES = [
"rawstring" => vrawstring::class,
"string" => vstring::class,
"text" => vtext::class,
"bool" => vbool::class,
"int" => vint::class,
"float" => vfloat::class,
"array" => varray::class,
"func" => vfunc::class,
"raw" => vraw::class,
"mixed" => vmixed::class,
"key" => vkey::class,
"pkey" => vpkey::class,
"content" => vcontent::class,
"datetime" => vdatetime::class,
"date" => vdate::class,
"time" => vtime::class,
];
static function get_types($schema, &$default = null, ?bool &$required = null): array {
if (is_array($schema)) {
$types = $schema["type"] ?? $schema[0] ?? null;
$default = $schema["default"] ?? $schema[1] ?? null;
$required = vbool::with($schema["required"] ?? false);
} elseif (is_string($schema)) {
$types = $schema;
$default = null;
$required = false;
} else {
throw exceptions::invalid_value($schema, "schema");
}
if (is_string($types)) {
$types = explode(",", $types);
} elseif (!is_array($types)) {
throw exceptions::invalid_value($types, "types");
}
return $types;
}
static function get_vclass(array $types, ?bool &$nullable): ?string {
foreach ($types as $type) {
$vclass = self::VCLASSES[$type] ?? null;
if ($vclass !== null) {
$nullable = str::del_prefix($type, "?");
return $vclass;
}
}
return null;
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
/**
* Class assoc: gestion simplifiée de schémas associatifs
*
* seuls les types simples sont reconnus
*/
class assoc {
/** indiquer si $array est conforme au schéma */
public static function check_schema(?array $array, array $schema, bool $strict = false): bool {
foreach ($schema as $key => $kschema) {
$types = _schema::get_types($kschema, $default, $required);
$exists = array_key_exists($key, $array);
if (!$exists) {
if ($required) return false;
else continue;
}
$vclass = _schema::get_vclass($types, $nullable);
# le test échoue si le type n'est pas supporté
if ($vclass === null) return false;
$value = $array[$key];
if ($value === null) {
if (!$nullable) return false;
} else {
if (!$vclass::isa($value, $strict)) return false;
}
}
return true;
}
/**
* s'assurer que $array est conforme au schéma
* - les clés ne sont pas créées si elles n'existent pas, sauf si la valeur
* est requise ou si une valeur par défaut non nulle est fournie
*/
public static function ensure_schema(&$array, array $schema): bool {
$ensured = true;
foreach ($schema as $key => $kschema) {
$types = _schema::get_types($kschema, $default, $required);
$exists = array_key_exists($key, $array);
if (!$exists) {
if ($required) {
if ($default !== null) {
$array[$key] = $default;
} else {
throw exceptions::missing_value(null, "array", "$key est obligatoire");
}
} elseif ($default !== null) {
$array[$key] = $default;
}
continue;
}
$vclass = _schema::get_vclass($types, $nullable);
# le test échoue si le type n'est pas supporté
if ($vclass === null) {
$ensured = false;
continue;
}
$value = $array[$key];
if ($nullable) {
$array[$key] = $vclass::withn($value);
} else {
if ($value === null) $value = $default;
$array[$key] = $vclass::with($value);
}
}
return $ensured;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
use nulib\str;
/**
* Class scalar: gestion simplifiée de schémas de valeurs simples
*/
class scalar {
/** indiquer si $value est conforme au schéma */
static function check_schema($value, $schema, bool $strict=false): bool {
$types = _schema::get_types($schema);
$vclass = _schema::get_vclass($types, $nullable);
# le test échoue si le type n'est pas supporté
if ($vclass === null) return false;
if ($value === null) return $nullable;
return $vclass::isa($value, $strict);
}
static function ensure_schema(&$value, $schema): bool {
$types = _schema::get_types($schema, $default);
$vclass = _schema::get_vclass($types, $nullable);
# la conversion échoue si le type n'est pas supporté
if ($vclass === null) return false;
if ($nullable) {
$value = $vclass::withn($value);
} else {
if ($value === null) $value = $default;
$value = $vclass::with($value);
}
return true;
}
}

View File

@ -2,10 +2,19 @@
namespace nulib\php\types;
use nulib\cl;
use nulib\exceptions;
class varray {
static function isa($value, bool $strict=false) : bool {
if ($strict) return is_array($value);
else return is_scalar($value);
}
static function ensure(&$array): void {
if (!is_array($array)) $array = cl::with($array);
if (is_array($array)) return;
if ($array === null || $array === false) $array = [];
elseif (is_scalar($array)) $array = cl::with($array);
else throw exceptions::invalid_value($array, "array");
}
static function ensuren(&$array): void {

View File

@ -1,6 +1,8 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
class vbool {
/** liste de valeurs chaines à considérer comme 'OUI' */
public const YES_VALUES = [
@ -30,12 +32,25 @@ class vbool {
return in_array($value, self::NO_VALUES, true);
}
static function isa($value, bool $strict=false) : bool {
if ($strict) return is_bool($value);
elseif (is_string($value)) return self::is_yes($value) || self::is_no($value);
else return is_scalar($value);
}
static function ensure(&$bool): void {
if (is_string($bool)) {
if (is_bool($bool)) return;
if ($bool === null) {
$bool = false;
} elseif (is_string($bool)) {
if (self::is_yes($bool)) $bool = true;
elseif (self::is_no($bool)) $bool = false;
else throw exceptions::invalid_value($bool, "boolean");
} elseif (is_scalar($bool)) {
$bool = boolval($bool);
} else {
throw exceptions::invalid_value($bool, "boolean");
}
if (!is_bool($bool)) $bool = boolval($bool);
}
static function ensuren(&$bool): void {

View File

@ -1,10 +1,28 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
use nulib\php\content\IContent;
use nulib\php\content\IPrintable;
class vcontent {
static function isa($value, bool $strict=false) : bool {
if ($strict) return is_array($value) || is_string($value);
return is_array($value) || is_scalar($value) ||
$value instanceof IContent || $value instanceof IPrintable;
}
static function ensure(&$content): void {
if ($content === null || $content === false) $content = [];
elseif (!is_string($content) && !is_array($content)) $content = strval($content);
if (is_array($content) || is_string($content)) return;
if ($content === null || $content === false) {
$content = [];
} elseif (is_scalar($content)) {
$content = [$content];
} elseif ($content instanceof IContent || $content instanceof IPrintable) {
$content = [$content];
} else {
throw exceptions::invalid_value($content, "content");
}
}
static function ensuren(&$content): void {

View File

@ -0,0 +1,29 @@
<?php
namespace nulib\php\types;
use nulib\php\time\Date;
class vdate {
static function isa($value, bool $strict=false) : bool {
if ($strict) return $value instanceof Date;
else return Date::isa_date($value);
}
static function ensure(&$date): void {
$date = Date::with($date);
}
static function ensuren(&$date): void {
$date = Date::withn($date);
}
static function with($value): Date {
self::ensure($value);
return $value;
}
static function withn($value): ?Date {
self::ensuren($value);
return $value;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace nulib\php\types;
use nulib\php\time\DateTime;
class vdatetime {
static function isa($value, bool $strict=false) : bool {
if ($strict) return $value instanceof DateTime;
else return DateTime::isa_datetime($value);
}
static function ensure(&$datetime): void {
$datetime = DateTime::with($datetime);
}
static function ensuren(&$datetime): void {
$datetime = DateTime::withn($datetime);
}
static function with($value): DateTime {
self::ensure($value);
return $value;
}
static function withn($value): ?DateTime {
self::ensuren($value);
return $value;
}
}

View File

@ -1,9 +1,19 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
class vfloat {
static function isa($value, bool $strict=false) : bool {
if ($strict) return is_float($value);
else return is_numeric($value);
}
static function ensure(&$float): void {
if (!is_float($float)) $float = floatval($float);
if (is_float($float)) return;
if ($float === null || $float === false) $float = 0.0;
elseif (is_numeric($float)) $float = floatval($float);
else throw exceptions::invalid_value($float, "float");
}
static function ensuren(&$float): void {

View File

@ -4,6 +4,11 @@ namespace nulib\php\types;
use nulib\php\func;
class vfunc {
static function isa($value, bool $strict=false) : bool {
if ($strict) return $value instanceof func;
else return func::is_callable($value);
}
static function ensure(&$func): void {
$func = func::ensure($func);
}

View File

@ -1,9 +1,19 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
class vint {
static function isa($value, bool $strict=false) : bool {
if ($strict) return is_int($value);
else return is_numeric($value);
}
static function ensure(&$int): void {
if (!is_int($int)) $int = intval($int);
if (is_int($int)) return;
if ($int === null || $int === false) $int = 0.0;
elseif (is_numeric($int)) $int = intval($int);
else throw exceptions::invalid_value($int, "int");
}
static function ensuren(&$int): void {

View File

@ -1,11 +1,20 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
class vkey {
static function isa($value, bool $strict=false) : bool {
if ($strict) return is_int($value) || is_string($value);
else return is_scalar($value);
}
static function ensure(&$key): void {
if (is_int($key) || is_string($key)) return;
if ($key === null) $key = "";
elseif ($key === false) $key = 0;
elseif (!is_string($key) && !is_int($key)) $key = strval($key);
elseif (is_scalar($key)) $key = strval($key);
else throw exceptions::invalid_value($key, "key");
}
static function ensuren(&$key): void {

View File

@ -0,0 +1,22 @@
<?php
namespace nulib\php\types;
class vmixed {
static function isa($value, bool $strict=false) : bool {
return true;
}
static function ensure(&$mixed): void {
}
static function ensuren(&$mixed): void {
}
static function with($value) {
return $value;
}
static function withn($value) {
return $value;
}
}

View File

@ -1,17 +1,28 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
class vpkey {
static function isa($value, bool $strict=false) : bool {
if ($strict) return is_array($value) || is_int($value) || is_string($value);
else return is_array($value) || is_scalar($value);
}
static function ensure(&$pkey): void {
if ($pkey === null) $pkey = "";
elseif ($pkey === false) $pkey = 0;
elseif (!is_string($pkey) && !is_int($pkey) && !is_array($pkey)) $pkey = strval($pkey);
if (is_array($pkey)) {
foreach ($pkey as &$key) {
vkey::ensure($key);
};
unset($key);
return;
} elseif (is_int($pkey) || is_string($pkey)) {
return;
}
if ($pkey === null) $pkey = "";
elseif ($pkey === false) $pkey = 0;
elseif (is_scalar($pkey)) $pkey = strval($pkey);
else throw exceptions::invalid_value($pkey, "pkey");
}
static function ensuren(&$pkey): void {

View File

@ -0,0 +1,22 @@
<?php
namespace nulib\php\types;
class vraw {
static function isa($value, bool $strict=false) : bool {
return true;
}
static function ensure(&$raw): void {
}
static function ensuren(&$raw): void {
}
static function with($value) {
return $value;
}
static function withn($value) {
return $value;
}
}

View File

@ -1,6 +1,7 @@
<?php
namespace nulib\php\types;
use nulib\exceptions;
use nulib\str;
class vrawstring {
@ -9,8 +10,19 @@ class vrawstring {
/** @var bool faut-il normaliser les caractères fin de ligne */
const NORM_NL = false;
static function isa($value, bool $strict=false) : bool {
if ($strict) return is_string($value);
else return is_scalar($value);
}
static function ensure(&$string): void {
if (!is_string($string)) $string = strval($string);
if (!is_string($string)) {
if (is_scalar($string)) {
$string = strval($string);
} else {
throw exceptions::invalid_value($string, "string");
}
}
if (static::TRIM) $string = trim($string);
if (static::NORM_NL) $string = str::norm_nl($string);
}

View File

@ -0,0 +1,30 @@
<?php
namespace nulib\php\types;
use nulib\php\time\DateTime;
use nulib\php\time\Time;
class vtime {
static function isa($value, bool $strict=false) : bool {
if ($strict) return $value instanceof Time;
else return Time::isa_time($value);
}
static function ensure(&$time): void {
$time = Time::with($time);
}
static function ensuren(&$time): void {
$time = Time::withn($time);
}
static function with($value): Time {
self::ensure($value);
return $value;
}
static function withn($value): ?Time {
self::ensuren($value);
return $value;
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace nulib\php\time;
use nulib\tests\TestCase;
use nulib\ValueException;
class HourTest extends TestCase {
function testParse() {
self::assertSame("8h", strval(new Hour("08:00:00")));
self::assertSame("8h", strval(new Hour("8:0")));
self::assertSame("8h", strval(new Hour("8.0")));
self::assertSame("8h", strval(new Hour("8h")));
self::assertSame("8h15", strval(new Hour("08:15:00")));
self::assertSame("8h15", strval(new Hour("8:15")));
self::assertSame("8h15", strval(new Hour("8.15")));
self::assertSame("8h15", strval(new Hour("8h15")));
self::assertSame("8h15", strval(new Hour("08:15:23")));
self::assertSame("8h15", strval(new Hour("8:15.23")));
self::assertSame("8h15", strval(new Hour("8.15.23")));
self::assertSame("8h15", strval(new Hour("8h15.23")));
self::assertSame("31h11", strval(new Hour("30:70:80", [
"wrap" => false,
])));
self::assertSame("7h11", strval(new Hour("30:70:80")));
self::assertException(ValueException::class, function() {
return strval(new Hour("bad format"));
});
}
function testFormat() {
self::assertSame("0h", (new Hour(0))->format());
self::assertSame("0h56", (new Hour(56))->format());
self::assertSame("2h", (new Hour(120))->format());
self::assertSame("23h59", (new Hour(1439))->format());
self::assertSame("24h", (new Hour(1440))->format());
self::assertSame("0h01", (new Hour(1441))->format());
}
function testStep() {
$h = new Hour(null, [
"step" => 5,
]);
$h->setu(10); self::assertSame("0h10", strval($h));
$h->setu(12); self::assertSame("0h10", strval($h));
$h->setu(15); self::assertSame("0h15", strval($h));
$h->setu(17); self::assertSame("0h15", strval($h));
$h->set("8h");
$h->addu(); self::assertSame("8h05", strval($h));
$h->addu(); self::assertSame("8h10", strval($h));
$h->addu(); self::assertSame("8h15", strval($h));
$h->subu(); self::assertSame("8h10", strval($h));
$h->subu(); self::assertSame("8h05", strval($h));
$h->subu(); self::assertSame("8h", strval($h));
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace nulib\php\time;
use nulib\tests\TestCase;
use nulib\ValueException;
class TimeTest extends TestCase {
function testParse() {
self::assertSame("08:00:00", strval(new Time("08:00:00")));
self::assertSame("08:00:00", strval(new Time("8:0")));
self::assertSame("08:00:00", strval(new Time("8.0")));
self::assertSame("08:00:00", strval(new Time("8h")));
self::assertSame("08:15:00", strval(new Time("08:15:00")));
self::assertSame("08:15:00", strval(new Time("8:15")));
self::assertSame("08:15:00", strval(new Time("8.15")));
self::assertSame("08:15:00", strval(new Time("8h15")));
self::assertSame("08:15:23", strval(new Time("08:15:23")));
self::assertSame("08:15:23", strval(new Time("8:15.23")));
self::assertSame("08:15:23", strval(new Time("8.15.23")));
self::assertSame("08:15:23", strval(new Time("8h15.23")));
self::assertSame("31:11:20", strval(new Time("30:70:80", [
"wrap" => false,
])));
self::assertSame("07:11:20", strval(new Time("30:70:80")));
self::assertException(ValueException::class, function() {
return strval(new Time("bad format"));
});
}
function testGetu() {
self::assertSame(0, (new Time(0))->getu());
self::assertSame(86399, (new Time(-1))->getu());
self::assertSame(1, (new Time(1))->getu());
self::assertSame(120, (new Time(120))->getu());
self::assertSame(86400, (new Time(86400))->getu());
self::assertSame(0, (new Time(-86400))->getu());
}
function testAddu() {
$t = new Time(0);
$t->addu(0); self::assertSame(0, $t->getu());
$t->addu(10); self::assertSame(10, $t->getu());
$t->addu(10); self::assertSame(20, $t->getu());
$t->addu(100); self::assertSame(120, $t->getu());
$t->addu(86280); self::assertSame(86400, $t->getu());
$t->addu(86400); self::assertSame(0, $t->getu());
$t->addu(-86400); self::assertSame(0, $t->getu());
}
function testSubu() {
$t = new Time(0);
$t->subu(-86400); self::assertSame(86400, $t->getu());
$t->subu(86400); self::assertSame(0, $t->getu());
$t = new Time(120);
$t->subu(100); self::assertSame(20, $t->getu());
$t->subu(10); self::assertSame(10, $t->getu());
$t->subu(10); self::assertSame(0, $t->getu());
$t->subu(0); self::assertSame(0, $t->getu());
}
function testFormat() {
self::assertSame("00:00:00", (new Time(0))->format());
self::assertSame("00:00:56", (new Time(56))->format());
self::assertSame("00:02:00", (new Time(120))->format());
self::assertSame("23:59:59", (new Time(86399))->format());
self::assertSame("24:00:00", (new Time(86400))->format());
self::assertSame("00:00:01", (new Time(86401))->format());
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace nulib\php\types;
use nulib\tests\TestCase;
use nulib\ValueException;
class assocTest extends TestCase {
static function assertEnsureOk(array $schema, ?array $input, ?array $output) {
assoc::ensure_schema($input, $schema);
self::assertSame($output, $input);
}
static function assertEnsureFail(array $schema, ?array $input, ?array $output) {
self::assertException(ValueException::class, function() use ($input, $schema) {
assoc::ensure_schema($input, $schema);
});
}
function testEnsureSchema() {
self::assertEnsureOk([
"string" => "string",
"int" => "int",
], [
"string" => 5,
"int" => "42",
], [
"string" => "5",
"int" => 42,
]);
}
const SCHEMA_S = ["s" => "string"];
const SCHEMA_SD = ["s" => ["string", null]];
const SCHEMA_SND = ["s" => ["string", "not null"]];
const SCHEMA_RS = ["s" => ["string", "required" => true]];
const SCHEMA_RSD = ["s" => ["string", null, "required" => true]];
const SCHEMA_RSND = ["s" => ["string", "not null", "required" => true]];
const SCHEMA_NS = ["s" => "?string"];
const SCHEMA_NSD = ["s" => ["?string", null]];
const SCHEMA_NSND = ["s" => ["?string", "not null"]];
const SCHEMA_NRS = ["s" => ["?string", "required" => true]];
const SCHEMA_NRSD = ["s" => ["?string", null, "required" => true]];
const SCHEMA_NRSND = ["s" => ["?string", "not null", "required" => true]];
function testEnsureSchema2() {
self::assertEnsureOk(self::SCHEMA_S, [], []);
self::assertEnsureFail(self::SCHEMA_S, ["s" => null], ["s" => null]);
self::assertEnsureOk(self::SCHEMA_S, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_NS, [], []);
self::assertEnsureOk(self::SCHEMA_NS, ["s" => null], ["s" => null]);
self::assertEnsureOk(self::SCHEMA_NS, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_SD, [], []);
self::assertEnsureFail(self::SCHEMA_SD, ["s" => null], ["s" => null]);
self::assertEnsureOk(self::SCHEMA_SD, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_NSD, [], []);
self::assertEnsureOk(self::SCHEMA_NSD, ["s" => null], ["s" => null]);
self::assertEnsureOk(self::SCHEMA_NSD, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_SND, [], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_SND, ["s" => null], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_SND, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_NSND, [], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_NSND, ["s" => null], ["s" => null]);
self::assertEnsureOk(self::SCHEMA_NSND, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureFail(self::SCHEMA_RS, [], []);
self::assertEnsureFail(self::SCHEMA_RS, ["s" => null], []);
self::assertEnsureOk(self::SCHEMA_RS, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureFail(self::SCHEMA_NRS, [], []);
self::assertEnsureOk(self::SCHEMA_NRS, ["s" => null], ["s" => null]);
self::assertEnsureOk(self::SCHEMA_NRS, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureFail(self::SCHEMA_RSD, [], []);
self::assertEnsureFail(self::SCHEMA_RSD, ["s" => null], []);
self::assertEnsureOk(self::SCHEMA_RSD, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureFail(self::SCHEMA_NRSD, [], []);
self::assertEnsureOk(self::SCHEMA_NRSD, ["s" => null], ["s" => null]);
self::assertEnsureOk(self::SCHEMA_NRSD, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_RSND, [], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_RSND, ["s" => null], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_RSND, ["s" => "not null"], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_NRSND, [], ["s" => "not null"]);
self::assertEnsureOk(self::SCHEMA_NRSND, ["s" => null], ["s" => null]);
self::assertEnsureOk(self::SCHEMA_NRSND, ["s" => "not null"], ["s" => "not null"]);
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace nulib\php\types;
use nulib\tests\TestCase;
use nulib\ValueException;
class scalarTest extends TestCase {
function testCheckSchema() {
self::assertTrue(scalar::check_schema(5, "int"));
self::assertTrue(scalar::check_schema(5, "string"));
self::assertTrue(scalar::check_schema(5, "float"));
self::assertTrue(scalar::check_schema(5, "int", true));
self::assertFalse(scalar::check_schema(5, "string", true));
self::assertFalse(scalar::check_schema(5, "float", true));
self::assertTrue(scalar::check_schema("5", "int"));
self::assertTrue(scalar::check_schema("5", "string"));
self::assertTrue(scalar::check_schema("5", "float"));
self::assertFalse(scalar::check_schema("5", "int", true));
self::assertTrue(scalar::check_schema("5", "string", true));
self::assertFalse(scalar::check_schema("5", "float", true));
self::assertFalse(scalar::check_schema("bad format", "int"));
}
function testEnsureSchema() {
$v = 5; scalar::ensure_schema($v, "int");
self::assertSame(5, $v);
$v = "5"; scalar::ensure_schema($v, "int");
self::assertSame(5, $v);
$v = "5.0"; scalar::ensure_schema($v, "int");
self::assertSame(5, $v);
$v = 5.0; scalar::ensure_schema($v, "int");
self::assertSame(5, $v);
$v = 5; scalar::ensure_schema($v, "string");
self::assertSame("5", $v);
$v = "5"; scalar::ensure_schema($v, "string");
self::assertSame("5", $v);
$v = "5.0"; scalar::ensure_schema($v, "string");
self::assertSame("5.0", $v);
$v = 5.0; scalar::ensure_schema($v, "string");
self::assertSame("5", $v);
$v = 5; scalar::ensure_schema($v, "float");
self::assertSame(5.0, $v);
$v = "5"; scalar::ensure_schema($v, "float");
self::assertSame(5.0, $v);
$v = "5.0"; scalar::ensure_schema($v, "float");
self::assertSame(5.0, $v);
$v = 5.0; scalar::ensure_schema($v, "float");
self::assertSame(5.0, $v);
self::assertException(ValueException::class, function() {
$v = "bad format"; scalar::ensure_schema($v, "int");
});
}
}