Compare commits

...

5 Commits
dev82 ... dev74

7 changed files with 314 additions and 10 deletions

16
TODO.md

@ -1,5 +1,6 @@
# nulib # nulib/bash
* [nulib/bash](bash/TODO.md)
* runners * runners
* [ ] rnlphp -- lancer un programme php avec la bonne version (+docker le cas échéant) * [ ] rnlphp -- lancer un programme php avec la bonne version (+docker le cas échéant)
* [ ] utilisable en shebang * [ ] utilisable en shebang
@ -12,4 +13,17 @@
* [ ] rnlsh -- lancer un shell avec les librairies bash / lancer un script * [ ] rnlsh -- lancer un shell avec les librairies bash / lancer un script
* MYTRUEDIR, MYTRUENAME, MYTRUESELF -- résoudre les liens symboliques * MYTRUEDIR, MYTRUENAME, MYTRUESELF -- résoudre les liens symboliques
# nulib/php
* [nulib](php/src/TODO.md)
* [nulib\app](php/src/app/TODO.md)
* [nulib\db](php/src/db/TODO.md)
* [nulib\os](php/src/os/TODO.md)
* [nulib\output](php/src/output/TODO.md)
* [nulib\php\time](php/src/php/time/TODO.md)
vrac:
* PID dans les logs
* build --ci
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

16
composer.lock generated

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "4569957a35f86d8a4964d01c7358935c", "content-hash": "71744d15224f445d1aeefe16ec7d1099",
"packages": [ "packages": [
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
@ -301,16 +301,16 @@
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
"version": "1.13.1", "version": "1.13.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/myclabs/DeepCopy.git", "url": "https://github.com/myclabs/DeepCopy.git",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" "reference": "faed855a7b5f4d4637717c2b3863e277116beb36"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", "reference": "faed855a7b5f4d4637717c2b3863e277116beb36",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -349,7 +349,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/myclabs/DeepCopy/issues", "issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3"
}, },
"funding": [ "funding": [
{ {
@ -357,7 +357,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-04-29T12:36:36+00:00" "time": "2025-07-05T12:25:42+00:00"
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
@ -2027,6 +2027,8 @@
"php": "^7.4" "php": "^7.4"
}, },
"platform-dev": { "platform-dev": {
"ext-mbstring": "*",
"ext-iconv": "*",
"ext-posix": "*", "ext-posix": "*",
"ext-pcntl": "*", "ext-pcntl": "*",
"ext-curl": "*", "ext-curl": "*",

5
php/src/TODO.md Normal file

@ -0,0 +1,5 @@
# nulib
* [ ] support de UserException pour ExceptionShadow: distinguer userMessage et techMessage
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

@ -3,5 +3,7 @@
* [ ] ajouter des méthodes normalisées `app::get_cachedir()` et * [ ] ajouter des méthodes normalisées `app::get_cachedir()` et
`app::get_cachefile($name)` avec la valeur par défaut `app::get_cachefile($name)` avec la valeur par défaut
`cachedir = $vardir/cache` `cachedir = $vardir/cache`
* [ ] `app::action()` et `app::step()` appellent automatiquement
`app::_dispatch_signals()`
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

@ -2,6 +2,9 @@
namespace nulib\app; namespace nulib\app;
use nulib\A; use nulib\A;
use nulib\cl;
use nulib\cv;
use nulib\file;
use nulib\str; use nulib\str;
class args { class args {
@ -10,7 +13,10 @@ class args {
* - ["myArg" => $value] devient ["--my-arg", "$value"] * - ["myArg" => $value] devient ["--my-arg", "$value"]
* - ["myOpt" => true] devient ["--my-opt"] * - ["myOpt" => true] devient ["--my-opt"]
* - ["myOpt" => false] est omis * - ["myOpt" => false] est omis
* - les autres valeurs sont prises telles quelles * - les autres valeurs sont transformées en chaines puis ajoutée
*
* ainsi, ["myOpt" => "value", "myArg", "myBool" => true]
* devient ["--my-opt", "value", "myArg", "--my-bool"]
*/ */
static function from_array(?array $array): array { static function from_array(?array $array): array {
$args = []; $args = [];
@ -36,4 +42,205 @@ class args {
} }
return $args; return $args;
} }
private static function tint(string $value): int {
return intval($value);
}
private static function tbool(string $value): bool {
return boolval($value);
}
private static function tarray(string $value): ?array {
if ($value === "") return null;
$tmparray = explode(",", $value);
$array = null;
foreach ($tmparray as $tmpvalue) {
[$tmpkey, $tmpvalue] = str::split_pair($tmpvalue);
if ($tmpvalue === null) cv::swap($tmpkey, $tmpvalue);
if ($tmpkey === null) {
$array[] = $tmpvalue;
} else {
if (str::del_suffix($tmpkey, ":int")) {
$tmpvalue = self::tint($tmpvalue);
} elseif (str::del_suffix($tmpkey, ":bool")) {
$tmpvalue = self::tbool($tmpvalue);
}
$array[$tmpkey] = $tmpvalue;
}
}
return $array;
}
/**
* convertir une liste d'arguments en tableau qui est utilisable comme un
* filtre de base de données ou des données d'une requête REST. les arguments
* peuvent être de la forme:
* - "name=value"
* qui devient dans le tableau ["name" => "value"]
* - "+arg" ou "arg"
* qui devient dans le tableau ["arg" => true]
* - "-arg" ou "~arg"
* qui est stocké dans le tableau $query ["arg" => false]
*
* si $allow_file == true, les formes d'arguments suivantes sont reconnues
* aussi:
* - "name=@file" (1 argument) OU
* "name=@" "file" (2 arguments)
* qui deviennent ["name" => new FileReader(file)]
*/
static function build_query(?array $args, bool $allow_file=true): ?array {
$query = null;
$args ??= [];
$keys = array_keys($args);
$index = 0;
$count = count($keys);
while ($index < $count) {
$arg = $args[$keys[$index++]];
[$name, $value] = str::split_pair($arg, "=");
$checkType = true;
if ($value === null) {
if (str::del_prefix($name, "+")) {
$value = true;
} elseif (str::del_prefix($name, "-") || str::del_prefix($name, "~")) {
$value = false;
} else {
$value = true;
}
} elseif ($allow_file) {
if ($value === "@") {
$value = $args[$keys[$index++]];
$value = file::reader($value);
$checkType = false;
} elseif (substr($value, 0, 1) === "@") {
$value = substr($value, 1);
$value = file::reader($value);
$checkType = false;
}
}
if ($checkType) {
if (str::del_suffix($name, ":int")) {
if (str::del_suffix($name, ":array")) {
$value = array_map([self::class, "tint"], self::tarray($value));
} else {
$value = self::tint($value);
}
} elseif (str::del_suffix($name, ":bool")) {
if (str::del_suffix($name, ":array")) {
$value = array_map([self::class, "tbool"], self::tarray($value));
} else {
$value = self::tbool($value);
}
} elseif (str::del_suffix($name, ":array")) {
$value = self::tarray($value);
if (str::del_suffix($name, ":int")) {
$value = array_map([self::class, "tint"], $value);
} elseif (str::del_suffix($name, ":bool")) {
$value = array_map([self::class, "tbool"], $value);
}
}
}
if (cl::has($query, $name)) {
A::ensure_array($query[$name]);
$query[$name][] = $value;
} else {
$query[$name] = $value;
}
}
return $query;
}
/**
* convertir une liste d'arguments de façon qu'ils soient utilisables pour un
* appel de méthode. les arguments peuvent être de la forme:
* - "name=value"
* qui est stocké dans le tableau $query ["name" => "value"]
* il est possible de forcer le type de la valeur avec l'un des suffixes
* :int, :bool ou :array, e.g
* un entier: "name:int=42"
* un tableau de chaines: "name:array=a,b,c"
* un tableau d'entiers: "name:array:int=1,2,3"
* - "+arg"
* qui est stocké dans le tableau $query ["arg" => true]
* - "-arg" ou "~arg"
* qui est stocké dans le tableau $query ["arg" => false]
* - "array:sval,key:aval,..."
* qui devient l'argument ["sval", "key" => "aval", ...]
* il est possible de forcer le types des éléments avec le préfixe int: ou
* bool: e.g "array:int:1,2,3"
* - "int:value"
* qui devient l'argument intval("value")
* - "bool:value"
* qui devient l'argument boolval("value")
* - "value"
* qui devient l'argument "value"
*
* à la fin, la liste des arguments est retournée [$arguments...]
* si le tableau $query est renseigné, il est en premier dans la liste des
* arguments e.g [$query, $arguments...]
*/
static function build_method_args(?array $args): ?array {
$query = null;
$margs = [];
$args ??= [];
foreach ($args as $arg) {
[$name, $value] = str::split_pair($arg, "=");
if ($value === null) {
if (str::del_prefix($name, "+")) {
$value = true;
} elseif (str::del_prefix($name, "-") || str::del_prefix($name, "~")) {
$value = false;
} elseif (str::del_prefix($name, "int:")) {
$margs[] = self::tint($name);
continue;
} elseif (str::del_prefix($name, "bool:")) {
$margs[] = self::tbool($name);
continue;
} elseif (str::del_prefix($name, "array:")) {
if (str::del_prefix($name, "int:")) {
$map = [self::class, "tint"];
} elseif (str::del_prefix($name, "bool:")) {
$map = [self::class, "tbool"];
} else {
$map = null;
}
$value = self::tarray($name);
if ($map !== null) $value = array_map($map, $value);
$margs[] = $value;
continue;
} else {
$margs[] = $name;
continue;
}
}
if (str::del_suffix($name, ":int")) {
if (str::del_suffix($name, ":array")) {
$value = array_map([self::class, "tint"], self::tarray($value));
} else {
$value = self::tint($value);
}
} elseif (str::del_suffix($name, ":bool")) {
if (str::del_suffix($name, ":array")) {
$value = array_map([self::class, "tbool"], self::tarray($value));
} else {
$value = self::tbool($value);
}
} elseif (str::del_suffix($name, ":array")) {
$value = self::tarray($value);
if (str::del_suffix($name, ":int")) {
$value = array_map([self::class, "tint"], $value);
} elseif (str::del_suffix($name, ":bool")) {
$value = array_map([self::class, "tbool"], $value);
}
}
if (cl::has($query, $name)) {
A::ensure_array($query[$name]);
$query[$name][] = $value;
} else {
$query[$name] = $value;
}
}
if ($query !== null) array_unshift($margs, $query);
return $margs;
}
} }

@ -1,4 +1,4 @@
# TOOD # nulib\output
* dans msg::action($m, function() {}), *bloquer* la marque pour empêcher d'aller * dans msg::action($m, function() {}), *bloquer* la marque pour empêcher d'aller
plus bas que prévu. comme ça s'il y a plusieurs success ou failure dans la plus bas que prévu. comme ça s'il y a plusieurs success ou failure dans la
@ -32,4 +32,10 @@ pour l'UI
peut-être rajouter `ui` (ou `web`?) en plus de say, log, debuglog? peut-être rajouter `ui` (ou `web`?) en plus de say, log, debuglog?
--> ou renommer `say` en `console`, et `ui` en `say` --> ou renommer `say` en `console`, et `ui` en `say`
* [ ] ajouter une option `Application::MSG_SIGNALS` qui fait que
* les méthodes `msg::eXXX()` appellent automatiquement `app::_dispatch_signals()`
* [ ] ajouter une option `Application::MSG_ACTIONS` qui fait que
* `msg::section()` et/ou `msg::title()` appellent automatiquement `app::action()`
* `msg::estep()` appelle automatiquement `app::step()`
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary -*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

@ -23,4 +23,72 @@ class argsTest extends TestCase {
self::assertSame(["x", "1", "2", "3", "y"], args::from_array(["x", [1, 2, 3], "y"])); self::assertSame(["x", "1", "2", "3", "y"], args::from_array(["x", [1, 2, 3], "y"]));
} }
function testBuild_query() {
self::assertSame(null, args::build_query(null));
self::assertSame(null, args::build_query([]));
self::assertSame(["a" => true], args::build_query(["a"]));
self::assertSame(["a" => true], args::build_query(["+a"]));
self::assertSame(["a" => false], args::build_query(["-a"]));
self::assertSame(["a" => false], args::build_query(["~a"]));
self::assertSame(["x" => "a"], args::build_query(["x=a"]));
self::assertSame(["x" => 0], args::build_query(["x:int=0"]));
self::assertSame(["x" => 42], args::build_query(["x:int=42"]));
self::assertSame(["x" => false], args::build_query(["x:bool=0"]));
self::assertSame(["x" => true], args::build_query(["x:bool=42"]));
self::assertSame(["x" => ["a", "b"]], args::build_query(["x:array=a,b"]));
self::assertSame(["x" => [0, 42]], args::build_query(["x:array:int=0,42"]));
self::assertSame(["x" => [0, 42]], args::build_query(["x:int:array=0,42"]));
self::assertSame(["x" => "a", "y" => "b"], args::build_query(["x=a", "y=b"]));
self::assertSame(["x" => ["a", "b"]], args::build_query(["x=a", "x=b"]));
}
function testBuild_method_args() {
self::assertSame([], args::build_method_args(null));
self::assertSame([], args::build_method_args([]));
self::assertSame(["a"], args::build_method_args(["a"]));
self::assertSame(["a", "b"], args::build_method_args(["a", "b"]));
self::assertSame([0], args::build_method_args(["int:0"]));
self::assertSame([42], args::build_method_args(["int:42"]));
# pour le moment, pas de tint
self::assertSame([0], args::build_method_args(["int:"]));
self::assertSame([0], args::build_method_args(["int:truc"]));
self::assertSame([false], args::build_method_args(["bool:0"]));
self::assertSame([true], args::build_method_args(["bool:42"]));
self::assertSame([false], args::build_method_args(["bool:"]));
self::assertSame([true], args::build_method_args(["bool:truc"]));
# pour le moment, pas de tbool
self::assertSame([true], args::build_method_args(["bool:false"]));
self::assertSame([true], args::build_method_args(["bool:true"]));
self::assertSame([["a", "b"]], args::build_method_args(["array:a,b"]));
self::assertSame([["x" => "a", "y" => "b"]], args::build_method_args(["array:x:a,y:b"]));
# pour le moment, pas de tint
self::assertSame([[0, 42, 0, 0]], args::build_method_args(["array:int:0,42,,truc"]));
self::assertSame([["x" => 0, "y" => 42]], args::build_method_args(["array:int:x:0,y:42"]));
# pour le moment, pas de tbool
self::assertSame([[false, true, false, true, true, true]], args::build_method_args(["array:bool:0,42,,truc,false,true"]));
self::assertSame([["x" => false, "y" => true]], args::build_method_args(["array:bool:x:0,y:42"]));
self::assertSame([["a" => true]], args::build_method_args(["+a"]));
self::assertSame([["a" => false]], args::build_method_args(["-a"]));
self::assertSame([["a" => false]], args::build_method_args(["~a"]));
self::assertSame([["x" => "a"]], args::build_method_args(["x=a"]));
self::assertSame([["x" => 0]], args::build_method_args(["x:int=0"]));
self::assertSame([["x" => 42]], args::build_method_args(["x:int=42"]));
self::assertSame([["x" => false]], args::build_method_args(["x:bool=0"]));
self::assertSame([["x" => true]], args::build_method_args(["x:bool=42"]));
self::assertSame([["x" => ["a", "b"]]], args::build_method_args(["x:array=a,b"]));
self::assertSame([["x" => [0, 42]]], args::build_method_args(["x:array:int=0,42"]));
self::assertSame([["x" => [0, 42]]], args::build_method_args(["x:int:array=0,42"]));
self::assertSame([["x" => "a", "y" => "b"], "a", "b"], args::build_method_args(["x=a", "a", "y=b", "b"]));
self::assertSame([["x" => ["a", "b"]]], args::build_method_args(["x=a", "x=b"]));
}
} }