<pman>Intégration de la branche rel82-0.4.0

This commit is contained in:
Jephté Clain 2025-03-14 15:46:49 +04:00
commit b364e797ea
97 changed files with 3286 additions and 4791 deletions

View File

@ -2,5 +2,12 @@
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GrazieInspection" enabled="false" level="GRAMMAR_ERROR" enabled_by_default="false" />
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

15
.idea/php.xml generated
View File

@ -10,6 +10,11 @@
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpCodeSniffer">
<phpcs_settings>
<phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="30000" />
</phpcs_settings>
</component>
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
@ -64,6 +69,11 @@
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.4" />
<component name="PhpStan">
<PhpStan_settings>
<phpstan_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="60000" />
</PhpStan_settings>
</component>
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
@ -72,6 +82,11 @@
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" />
</phpunit_settings>
</component>
<component name="Psalm">
<Psalm_settings>
<psalm_fixer_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="60000" />
</Psalm_settings>
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>

View File

@ -9,4 +9,19 @@ TAG_PREFIX=
TAG_SUFFIX=p82
HOTFIX=hotf82-
DIST=
NOAUTO=1
NOAUTO=
AFTER_CREATE_RELEASE='
set -x
pman --composer-select-profile dist
composer u || exit 1
git commit -am "<pman>deps de dist"
true
'
AFTER_MERGE_RELEASE='
set -x
pman --composer-select-profile dev
composer u || exit 1
git commit -am "<pman>deps de dev"
true
'

16
.pman.yml Normal file
View File

@ -0,0 +1,16 @@
# -*- coding: utf-8 mode: yaml -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
composer:
profiles: [ dev, dist ]
dev:
link: true
require-dev:
nulib/php: ^8.2-dev
nulib/spout: ^8.2-dev
nulib/phpss: ^8.2-dev
dist:
link: false
require-dev:
nulib/php: ^0.4.0p82
nulib/spout: ^0.4.0p82
nulib/phpss: ^0.4.0p82

View File

@ -1,3 +1,16 @@
## Release 0.4.0p82 du 14/03/2025-15:46
## Release 0.4.0p74 du 14/03/2025-15:44
* `4b84f11` début assocSchema
* `7227cd7` maj TODO
* `089e487` renommer dest en value; tenir compte de la valeur par défaut
* `fc523bf` instancier type le plus vite possible
* `f8eec57` renommer Value en Wrapper
* `1aa266b` tstring et trawstring
* `1a5ca79` support des colonnes
* `9438aaf` ajout config .pman.yml
## Release 0.3.0p82 du 01/03/2025-13:49
## Release 0.3.0p74 du 01/03/2025-13:44

View File

@ -1 +1 @@
0.3.0
0.4.0

View File

@ -18,9 +18,9 @@
"php": "^8.2"
},
"require-dev": {
"nulib/php": "^0.3.0p82",
"nulib/spout": "^0.3.0p82",
"nulib/phpss": "^0.3.0p82",
"nulib/php": "^0.4.0p82",
"nulib/spout": "^0.4.0p82",
"nulib/phpss": "^0.4.0p82",
"nulib/tests": "^8.2",
"ext-posix": "*",
"ext-pcntl": "*",

1017
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\wip\php\access;
use nur\sery\cl;
use nulib\cl;
/**
* Class AbstractAccess: implémentation par défaut pour des instances standard

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\wip\php\access;
use nur\sery\cl;
use nulib\cl;
/**
* Class FormAccess: accès à une valeur de $_POST puis $_GET, dans cet ordre

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\wip\php\access;
use nur\sery\cl;
use nulib\cl;
/**
* Class GetAccess: accès à une valeur de $_GET

View File

@ -5,10 +5,10 @@ namespace nur\sery\wip\php\access;
* Interface IAccess: abstraction d'un accès complet à une valeur
*/
interface IAccess extends IGetter, ISetter, IDeleter {
/** incrémenter la valeur */
/** incrémenter la valeur et la retourner */
function inc(): int;
/** décrémenter la valeur */
/** décrémenter la valeur et la retourner */
function dec(bool $allowNegative=false): int;
/**

View File

@ -2,14 +2,14 @@
namespace nur\sery\wip\php\access;
use ArrayAccess;
use nur\sery\cl;
use nulib\cl;
/**
* Class KeyAccess: accès à une valeur d'une clé dans un tableau
*/
class KeyAccess extends AbstractAccess {
function __construct(&$dest, $key, ?array $params=null) {
$this->dest =& $dest;
function __construct(&$array, $key, ?array $params=null) {
$this->array =& $array;
$this->key = $key;
$this->allowNull = $params["allow_null"] ?? true;
$this->allowFalse = $params["allow_false"] ?? false;
@ -17,10 +17,10 @@ class KeyAccess extends AbstractAccess {
}
/** @var array|ArrayAccess */
protected $dest;
protected $array;
function reset(&$dest): self {
$this->dest =& $dest;
function reset(&$array): self {
$this->array =& $array;
return $this;
}
@ -36,12 +36,12 @@ class KeyAccess extends AbstractAccess {
function exists(): bool {
$key = $this->key;
if ($key === null) return false;
return cl::has($this->dest, $key);
return cl::has($this->array, $key);
}
function available(): bool {
if (!$this->exists()) return false;
$value = cl::get($this->dest, $this->key);
$value = cl::get($this->array, $this->key);
if ($value === null) return $this->allowNull;
if ($value === false) return $this->allowFalse;
if ($value === "") return $this->allowEmpty;
@ -50,7 +50,7 @@ class KeyAccess extends AbstractAccess {
function get($default=null) {
if ($this->key === null) return $default;
$value = cl::get($this->dest, $this->key, $default);
$value = cl::get($this->array, $this->key, $default);
if ($value === null && !$this->allowNull) return $default;
if ($value === false && !$this->allowFalse) return $default;
if ($value === "" && !$this->allowEmpty) return $default;
@ -59,11 +59,11 @@ class KeyAccess extends AbstractAccess {
function set($value): void {
if ($this->key === null) return;
cl::set($this->dest, $this->key, $value);
cl::set($this->array, $this->key, $value);
}
function del(): void {
if ($this->key === null) return;
cl::del($this->dest, $this->key);
cl::del($this->array, $this->key);
}
}

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\wip\php\access;
use nur\sery\cl;
use nulib\cl;
/**
* Class PostAccess: accès à une valeur de $_POST

View File

@ -5,18 +5,18 @@ namespace nur\sery\wip\php\access;
* Class ValueAccess: accès à une valeur unitaire
*/
class ValueAccess extends AbstractAccess {
function __construct(&$dest, ?array $params=null) {
$this->dest =& $dest;
function __construct(&$value, ?array $params=null) {
$this->value =& $value;
$this->allowNull = $params["allow_null"] ?? false;
$this->allowFalse = $params["allow_false"] ?? true;
$this->allowEmpty = $params["allow_empty"] ?? true;
}
/** @var mixed */
protected $dest;
protected $value;
function reset(&$dest): self {
$this->dest =& $dest;
function reset(&$value): self {
$this->value =& $value;
return $this;
}
@ -27,19 +27,19 @@ class ValueAccess extends AbstractAccess {
protected bool $allowEmpty;
function exists(): bool {
return $this->allowNull || $this->dest !== null;
return $this->allowNull || $this->value !== null;
}
function available(): bool {
if (!$this->exists()) return false;
$value = $this->dest;
$value = $this->value;
if ($value === false) return $this->allowFalse;
if ($value === "") return $this->allowEmpty;
return true;
}
function get($default=null) {
$value = $this->dest;
$value = $this->value;
if ($value === null && !$this->allowNull) return $default;
if ($value === false && !$this->allowFalse) return $default;
if ($value === "" && !$this->allowEmpty) return $default;
@ -47,10 +47,10 @@ class ValueAccess extends AbstractAccess {
}
function set($value): void {
$this->dest = $value;
$this->value = $value;
}
function del(): void {
$this->dest = null;
$this->value = null;
}
}

View File

@ -2,6 +2,7 @@
namespace nur\sery\wip\php\coll;
use Iterator;
use IteratorAggregate;
use nulib\cl;
use nulib\php\func;
use nur\sery\wip\php\iter;
@ -33,7 +34,7 @@ class Cursor implements Iterator {
* alors retourner le tableau
* ["a" => $row["a"], "b" => $row["x"], "c" => "y", "d" => null]
*/
private static function map_row(array $row, ?array $map): array {
protected static function map_row(array $row, ?array $map): array {
if ($map === null) return $row;
$index = 0;
$mapped = [];
@ -48,7 +49,7 @@ class Cursor implements Iterator {
$mapped[$key] = $func->invoke([$value, $key, $row]);
} else {
if ($value === null) $mapped[$key] = null;
else $mapped[$key] = cl::get($row, $key);
else $mapped[$key] = cl::get($row, $value);
}
}
return $mapped;
@ -67,7 +68,7 @@ class Cursor implements Iterator {
* - une valeur associative $key => $value indique que la clé correspondante
* doit exiter avec la valeur spécifiée
*/
private static function filter_row(array $row, $filter): bool {
protected static function filter_row(array $row, $filter): bool {
if ($filter === null) return false;
if (!is_array($filter)) $filter = [$filter];
if (!$filter) return false;
@ -79,12 +80,12 @@ class Cursor implements Iterator {
if (!array_key_exists($value, $row)) return false;
} elseif (is_bool($value)) {
if ($value) {
if (!array_key_exists($value, $row)) return false;
if (!array_key_exists($key, $row)) return false;
} else {
if (array_key_exists($value, $row)) return false;
if (array_key_exists($key, $row)) return false;
}
} else {
if (!array_key_exists($value, $row)) return false;
if (!array_key_exists($key, $row)) return false;
if ($row[$key] !== $value) return false;
}
}
@ -114,6 +115,11 @@ class Cursor implements Iterator {
$this->rowsGenerator = $rowsGenerator;
$this->rowsFunc = $rowsFunc;
$this->cols = $params["cols"] ?? null;
$colsFunc = $params["cols_func"] ?? null;
if ($colsFunc !== null) $colsFunc = func::with($colsFunc);
$this->colsFunc = $colsFunc;
$map = $params["map"] ?? null;
$mapFunc = $params["map_func"] ?? null;
if ($mapFunc !== null) {
@ -140,16 +146,16 @@ class Cursor implements Iterator {
/** un générateur de lignes */
private ?Traversable $rowsGenerator;
/** une fonction de signature <code>function(Cursor):?iterable</code> */
/** une fonction de signature <code>function(Cursor): ?iterable</code> */
private ?func $rowsFunc;
/** une fonction de signature <code>function(Cursor):?array</code> */
/** une fonction de signature <code>function(Cursor): ?array</code> */
private ?func $colsFunc;
/** une fonction de signature <code>function(Cursor):?array</code> */
/** une fonction de signature <code>function(Cursor): ?array</code> */
private ?func $mapFunc;
/** une fonction de signature <code>function(Cursor):bool</code> */
/** une fonction de signature <code>function(Cursor): bool</code> */
private ?func $filterFunc;
protected ?iterable $rows;
@ -173,6 +179,10 @@ class Cursor implements Iterator {
return null;
}
protected function cols(): ?array {
return $this->row !== null? array_keys($this->row): null;
}
protected function filter(): bool {
return false;
}
@ -185,20 +195,24 @@ class Cursor implements Iterator {
# Iterator
function rewind() {
$this->cols = null;
$this->index = 0;
$this->origIndex = 0;
$this->key = null;
$this->raw = null;
$this->row = null;
if ($this->rowsGenerator !== null) {
$this->rows = $this->rowsGenerator;
$this->rows->rewind();
$rows = $this->rowsGenerator;
if ($rows instanceof IteratorAggregate) $rows = $rows->getIterator();
$rows->rewind();
$this->rows = $rows;
} else {
$this->rows = $this->rowsFunc->invoke();
}
}
function valid() {
function valid(): bool {
$cols = $this->colsFunc;
$filter = $this->filterFunc;
$map = $this->mapFunc;
while ($valid = iter::valid($this->rows)) {
@ -210,6 +224,10 @@ class Cursor implements Iterator {
if (!$filtered) {
if ($map === null) $this->row = $this->map();
else $this->row = $map->invoke([$this]);
if ($this->cols === null) {
if ($cols === null) $this->cols = $this->cols();
else $this->cols = $cols->invoke([$this]);
}
break;
} else {
iter::next($this->rows);
@ -219,6 +237,7 @@ class Cursor implements Iterator {
if (!$valid) {
iter::close($this->rows);
$this->rows = null;
$this->cols = null;
$this->index = -1;
$this->origIndex = -1;
$this->key = null;
@ -228,7 +247,7 @@ class Cursor implements Iterator {
return $valid;
}
function current() {
function current(): ?array {
return $this->row;
}

View File

@ -0,0 +1,30 @@
<?php
namespace nur\sery\wip\schema;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\types\IType;
class AnalyzerContext {
function __construct(Schema $schema, Wrapper $wrapper, Input $input, $valueKey, Result $result) {
$this->schema = $schema;
$this->wrapper = $wrapper;
$this->input = $input;
$this->result = $result;
$this->type = null;
$this->origValue = null;
$this->value = null;
$this->valueKey = $valueKey;
}
public Schema $schema;
public Wrapper $wrapper;
public Input $input;
public Result $result;
public ?IType $type;
/** @var mixed */
public $origValue;
/** @var mixed */
public $value;
/** @var int|string|null */
public $valueKey;
}

View File

@ -1,70 +1,60 @@
# nur\sery\schema
# nulib\schema
objet: s'assurer que des données soit dans un type particulier, en les
convertissant si nécessaire. la source de ces données peut-être diverse:
formulaire web, résultat d'une requête SQL, flux CSV, etc.
les classes de ce package permettent de s'assurer que des données soit dans un
type particulier, en les convertissant si nécessaire. la source de ces données
peut-être diverse: formulaire web, résultat d'une requête SQL, flux CSV, etc.
les données dont on peut modéliser le schéma sont de 3 types:
* scalaire
* tableau associatif
* liste (tableau séquentiel ou associatif d'éléments du même type)
les données dont on peut modéliser le schéma sont de 3 types: scalaire, tableau
associatif ou liste. la nature du schéma (la valeur de la clé `"""`) indique le
type de donnée modélisée
* donnée scalaire
chaque type de données a une syntaxe spécifique pour la définition du schéma.
## Nature de schéma
Un schéma se présente sous la forme d'un tableau associatif avec des clés qui
dépendent de la nature du schéma. La nature du schéma est indiquée avec la clé
`""` (chaine vide), e.g
~~~php
const SCHEMA = [
"" => NATURE,
];
~~~
La nature indique le type de données représenté par le schéma.
* nature scalaire: modélise une donnée scalaire
forme courante:
~~~php
const SCALAR_SCHEMA = [
$type, [$default, $title, ...]
];
~~~
forme normalisée:
~~~php
const SCALAR_SCHEMA = [
$type, [$default, $title, ...]
"" => "scalar",
];
~~~
Si le type est "array" ou "?array", on peut préciser le schéma de la donnée
~~~php
const SCALAR_SCHEMA = [
"?array", [$default, $title, ...]
"" => "scalar",
"schema" => NAKED_SCHEMA,
];
~~~
* nature tableau associatif: modélise un tableau associatif (le tableau peut
avoir des clés numériques ou chaines --> seules les clés décrites par le
schéma sont validées)
* tableau associatif
le tableau modélisé peut avoir des clés numériques ou chaines --> seules les
clés décrites par le schéma sont validées
forme courante:
~~~php
const ASSOC_SCHEMA = [
KEY => VALUE_SCHEMA,
...
"" => "assoc",
];
~~~
la nature "tableau associatif" est du sucre syntaxique pour une valeur
scalaire de type "?array" dont on précise le schéma
forme normalisée:
~~~php
// la valeur ci-dessus est strictement équivalent à
const ASSOC_SCHEMA = [
"?array",
"" => "scalar",
"?array", [$default, $title, ...]
"" => "assoc",
"schema" => [
KEY => VALUE_SCHEMA,
...
],
];
~~~
* liste (tableau d'éléments du même type)
le tableau modélisé peut avoir des clés numériques ou chaines --> on ne
modélise ni le type ni la valeur des clés
* nature liste: modélise une liste de valeurs du même type (le tableau peut
avoir des clés numériques ou chaines --> on ne modélise ni le type ni la
valeur des clés)
forme courante:
~~~php
const LIST_SCHEMA = [[
ITEM_SCHEMA,
]];
~~~
forme normalisée:
~~~php
const LIST_SCHEMA = [
"?array", [$default, $title, ...]
@ -85,7 +75,8 @@ const SCALAR_SCHEMA = [
"required" => "la valeur est-elle requise? si oui, elle doit exister",
"nullable" => "si la valeur existe, peut-elle être nulle?",
"desc" => "description de la valeur",
"checker_func" => "une fonction qui vérifie une valeur et la classifie",
"analyzer_func" => "XXX",
"extractor_func" => "XXX",
"parser_func" => "une fonction qui analyse une chaine pour produire la valeur",
"messages" => "messages à afficher en cas d'erreur d'analyse",
"formatter_func" => "une fonction qui formatte la valeur pour affichage",
@ -118,9 +109,9 @@ nature scalaire si:
* c'est un tableau avec un élément à l'index 0, ainsi que d'autres éléments,
e.g `["string", null, "required" => true]`
message indique les messages à afficher en cas d'erreur d'analyse. les clés sont
normalisées et correspondent à différents états de la valeur tels qu'analysés
par `checker_func`
`messages` indique les messages à afficher en cas d'erreur d'analyse. les clés
sont normalisées et correspondent à différents états de la valeur tels
qu'analysés par `analyzer_func`
~~~php
const MESSAGE_SCHEMA = [
"missing" => "message si la valeur n'existe pas dans la source et qu'elle est requise",
@ -133,23 +124,27 @@ const MESSAGE_SCHEMA = [
## Schéma d'un tableau associatif
Dans sa forme *non normalisée*, un tableau associatif est généralement modélisé
de cette manière:
~~~php
const ASSOC_SCHEMA = [
KEY => VALUE_SCHEMA,
...
"" => "assoc",
];
~~~
où chaque occurrence de `KEY => VALUE_SCHEMA` définit le schéma de la valeur
dont la clé est `KEY`
Si la nature du schéma n'est pas spécifiée, on considère que c'est un schéma de
nature associative si:
Dans la forme courante, on considère que c'est un schéma de nature associative si:
* c'est un tableau uniquement associatif avec aucun élément séquentiel, e.g
`["name" => "string", "age" => "int"]`
La forme normalisée est
~~~php
const ASSOC_SCHEMA = [
"?array",
"" => "assoc",
"schema" => [
KEY => VALUE_SCHEMA,
...
],
];
~~~
le type "?array" ou "array" indique si la liste est nullable ou non. la valeur
par défaut est "?array"
chaque occurrence de `KEY => VALUE_SCHEMA` définit le schéma de la valeur dont
la clé est `KEY`
VALUE_SCHEMA peut-être n'importe quel schéma valide, qui sera analysé
récursivement, avec cependant l'ajout de quelques clés supplémentaires:
* description de la valeur dans le contexte du tableau
@ -171,14 +166,11 @@ récursivement, avec cependant l'ajout de quelques clés supplémentaires:
## Schéma d'une liste (tableau séquentiel ou associatif d'éléments du même type)
Dans sa forme *non normalisée*, une liste est généralement modélisée de cette
manière:
~~~php
const LIST_SCHEMA = [ITEM_SCHEMA];
~~~
où ITEM_SCHEMA est le schéma des éléments de la liste
Dans la forme courante, on considère que c'est un schéma de nature liste si:
* c'est un tableau avec un unique élément de type tableau à l'index 0, e.g
`[["string", null, "required" => true]]`
Pour information, la forme normalisée est plutôt de la forme
La forme normalisée est
~~~php
const LIST_SCHEMA = [
"?array",
@ -189,9 +181,4 @@ const LIST_SCHEMA = [
le type "?array" ou "array" indique si la liste est nullable ou non. la valeur
par défaut est "?array"
Si la nature du schéma n'est pas spécifiée, on considère que c'est un schéma de
nature liste si:
* c'est un tableau avec un unique élément de type tableau à l'index 0, e.g
`[["string", null, "required" => true]]`
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary

View File

@ -1,21 +1,42 @@
<?php
namespace nur\sery\wip\schema;
use IteratorAggregate;
use nur\sery\wip\schema\_assoc\AssocResult;
use nur\sery\wip\schema\_list\ListResult;
use nur\sery\wip\schema\_scalar\ScalarResult;
/**
* Class Result: résultat de l'analyse ou de la normalisation d'une valeur
*
* @property bool $resultAvailable le résultat est-il disponible?
* @property bool $present la valeur existe-t-elle?
* @property bool $available si la valeur existe, est-elle disponible?
* @property bool $null si la valeur est disponible, est-elle nulle?
* @property bool $valid si la valeur est disponible, est-elle valide?
* @property bool $normalized si la valeur est valide, est-elle normalisée?
* @property string|null $messageKey clé de message si la valeur n'est pas valide
* @property string|null $message message si la valeur n'est pas valide
* @property string|null $origValue valeur originale avant extraction et analyse
* @property mixed|null $normalizedValue la valeur normalisée si elle est
* disponible, null sinon. ce champ est utilisé comme optimisation si la valeur
* normalisée a déjà été calculée
*/
abstract class Result {
abstract class Result implements IteratorAggregate {
const KEYS = [
"resultAvailable",
"present", "available", "null", "valid", "normalized",
"messageKey", "message",
"origValue", "normalizedValue",
];
function __construct() {
$this->reset();
}
function isAssoc(?AssocResult &$assoc=null): bool { return false; }
function isList(?ListResult &$list=null): bool { return false; }
function isScalar(?ScalarResult &$scalar=null): bool { return false; }
function isAssoc(?AssocResult &$result=null): bool { return false; }
function isList(?ListResult &$result=null): bool { return false; }
function isScalar(?ScalarResult &$result=null): bool { return false; }
/**
* Obtenir la liste des clés valides pour les valeurs accessibles via cet
@ -23,8 +44,21 @@ abstract class Result {
*/
abstract function getKeys(): array;
/** obtenir un objet pour gérer la valeur spécifiée */
abstract function getResult($key=null): Result;
/**
* sélectionner le résultat associé à la clé spécifiée
*
* @param string|int|null $key
* @return Result $this
*/
abstract function select($key): Result;
function getIterator() {
foreach ($this->getKeys() as $key) {
yield $key => $this->select($key);
}
$this->select(null);
}
/** réinitialiser tous les objets résultats accessibles via cet objet */
abstract function reset(): void;
}

View File

@ -2,15 +2,24 @@
namespace nur\sery\wip\schema;
use ArrayAccess;
use nur\sery\AccessException;
use nur\sery\cl;
use nulib\AccessException;
use nulib\cl;
use nulib\ref\schema\ref_schema;
use nulib\ref\schema\ref_types;
use nur\sery\wip\schema\_assoc\AssocSchema;
use nur\sery\wip\schema\_list\ListSchema;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\types\tarray;
use nur\sery\wip\schema\types\tbool;
use nur\sery\wip\schema\types\tcallable;
use nur\sery\wip\schema\types\tcontent;
use nur\sery\wip\schema\types\tpkey;
use nur\sery\wip\schema\types\trawstring;
abstract class Schema implements ArrayAccess {
/**
* créer si besoin une nouvelle instance de {@link Schema} à partir d'une
* créer le cas échéant une nouvelle instance de {@link Schema} à partir d'une
* définition de schéma
*
* - si $schema est une instance de schéma, la retourner
@ -18,18 +27,18 @@ abstract class Schema implements ArrayAccess {
* l'instance de Schema nouvelle créée
* - sinon, prendre $definition comme définition
*/
static function ns(&$schema, $definition=null, $definitionKey=null): self {
static function ns(&$schema, $definition=null, $definitionKey=null, bool $normalize=true): self {
if (is_array($schema)) {
$definition = $schema;
$schema = null;
}
if ($schema === null) {
if (AssocSchema::isa_definition($definition)) {
$schema = new AssocSchema($definition, $definitionKey);
$schema = new AssocSchema($definition, $definitionKey, $normalize);
} elseif (ListSchema::isa_definition($definition)) {
$schema = new ListSchema($definition, $definitionKey);
$schema = new ListSchema($definition, $definitionKey, $normalize);
} elseif (ScalarSchema::isa_definition($definition)) {
$schema = new ScalarSchema($definition, $definitionKey);
$schema = new ScalarSchema($definition, $definitionKey, $normalize);
} else {
throw SchemaException::invalid_schema();
}
@ -38,17 +47,189 @@ abstract class Schema implements ArrayAccess {
}
/**
* Créer si besoin une nouvelle instance de {@link Value} qui référence la
* variable $dest (si $destKey===null) ou $dest[$destKey] si $destKey n'est
* pas null
* Créer une nouvelle instance de {@link Wrapper} qui référence la
* variable $value (si $valueKey===null) ou $value[$valueKey] si $valueKey
* n'est pas null
*/
static function nv(?Value &$destv=null, &$dest=null, $destKey=null, &$schema=null, $definition=null): Value {
static function nw(&$value=null, $valueKey=null, &$schema=null, $definition=null, ?Wrapper &$wrapper=null): Wrapper {
if ($definition === null) {
# bien que techniquement, $definition peut être null (il s'agit alors du
# schéma d'un scalaire quelconque), on ne l'autorise pas ici
throw SchemaException::invalid_schema("definition is required");
}
return self::ns($schema, $definition)->newValue($destv, $dest, $destKey);
return self::ns($schema, $definition)->getWrapper($value, $valueKey, $wrapper);
}
protected static function have_nature(array $definition, ?string &$nature=null): bool {
$definitionNature = $definition[""] ?? null;
if (is_string($definitionNature)) {
$nature = $definitionNature;
return true;
}
if (is_array($definitionNature)
&& array_key_exists(0, $definitionNature)
&& is_string($definitionNature[0])) {
$nature = $definitionNature;
return true;
}
return false;
}
protected static function _normalize(&$definition, $definitionKey=null): void {
if (!is_array($definition)) $definition = [$definition];
# s'assurer que toutes les clés existent avec leur valeur par défaut
$index = 0;
foreach (array_keys(ref_schema::SCALAR_METASCHEMA) as $key) {
if (!array_key_exists($key, $definition)) {
if (array_key_exists($index, $definition)) {
$definition[$key] = $definition[$index];
unset($definition[$index]);
$index++;
} else {
$definition[$key] = ref_schema::SCALAR_METASCHEMA[$key][1];
}
}
}
# réordonner les clés numériques
if (cl::have_num_keys($definition)) {
$keys = array_keys($definition);
$index = 0;
foreach ($keys as $key) {
if (!is_int($key)) continue;
if ($key !== $index) {
$definition[$index] = $definition[$key];
unset($definition[$key]);
}
$index++;
}
}
# type
$types = [];
$deftype = $definition["type"];
$nullable = $definition["nullable"] ?? false;
if ($deftype === null) {
$types[] = null;
$nullable = true;
} else {
if (!is_array($deftype)) {
if (!is_string($deftype)) throw SchemaException::invalid_type($deftype);
$deftype = explode("|", $deftype);
}
foreach ($deftype as $type) {
if ($type !== null) $type = trim($type);
if ($type === null || $type === "null") {
$nullable = true;
continue;
}
if (!is_string($type)) throw SchemaException::invalid_type($type);
if (substr($type, 0, 1) == "?") {
$type = substr($type, 1);
$nullable = true;
}
if ($type === "") throw SchemaException::invalid_type($type);
$type = cl::get(ref_types::ALIASES, $type, $type);
$types = array_merge($types, explode("|", $type));
}
if (!$types) throw SchemaException::invalid_schema("scalar: type is required");
$types = array_keys(array_fill_keys($types, true));
}
$definition["type"] = $types;
$definition["nullable"] = $nullable;
# nature
$nature = $definition[""];
tarray::ensure_array($nature);
$definition[""] = $nature;
# name, pkey, header
$name = $definition["name"];
$pkey = $definition["pkey"];
$header = $definition["header"];
if ($name === null) $name = $definitionKey;
trawstring::ensure_nstring($name);
tpkey::ensure_npkey($pkey);
trawstring::ensure_nstring($header);
if ($pkey === null) $pkey = $name;
if ($header === null) $header = $name;
$definition["name"] = $name;
$definition["pkey"] = $pkey;
$definition["header"] = $header;
# autres éléments
tarray::ensure_narray($definition["schema"]);
trawstring::ensure_nstring($definition["title"]);
tbool::ensure_bool($definition["required"]);
tbool::ensure_bool($definition["nullable"]);
tcontent::ensure_ncontent($definition["desc"]);
tcallable::ensure_ncallable($definition["analyzer_func"]);
tcallable::ensure_ncallable($definition["extractor_func"]);
tcallable::ensure_ncallable($definition["parser_func"]);
tcallable::ensure_ncallable($definition["normalizer_func"]);
tarray::ensure_narray($definition["messages"]);
tcallable::ensure_ncallable($definition["formatter_func"]);
tbool::ensure_nbool($definition["composite"]);
switch ($nature[0] ?? null) {
case "assoc":
foreach ($definition["schema"] as $key => &$keydef) {
self::_normalize($keydef, $key);
}; unset($keydef);
break;
case "list":
self::_normalize($definition["schema"]);
break;
}
}
protected static function _ensure_nature(array $definition, string $expectedNature, ?string $expectedType=null): void {
$nature = $definition[""];
if (!array_key_exists(0, $nature) || $nature[0] !== $expectedNature) {
throw SchemaException::invalid_schema("$nature: invalid nature. expected $expectedNature");
}
if ($expectedType !== null) {
$types = $definition["type"];
if (count($types) !== 1 || $types[0] !== $expectedType) {
throw new SchemaException("{$types[O]}: invalide type. expected $expectedType");
}
}
}
protected static function _ensure_type(array &$definition): void {
$types = $definition["type"];
$nullable = $definition["nullable"];
# s'il n'y a qu'une seul type, l'instancier tout de suite
if (is_array($types) && count($types) == 1 && $types[0] !== null) {
foreach ($types as $key => $name) {
if ($key === 0) {
$args = null;
} else {
$args = $name;
$name = $key;
}
$definition["type"] = types::get($nullable, $name, $args, $definition);
}
}
switch ($definition[""][0]) {
case "assoc":
foreach ($definition["schema"] as &$keydef) {
self::_ensure_type($keydef);
}; unset($keydef);
break;
case "list":
self::_ensure_type($definition["schema"]);
break;
}
}
protected static function _ensure_schema_instances(array &$definition): void {
switch ($definition[""][0]) {
case "assoc":
foreach ($definition["schema"] as &$keydef) {
self::_ensure_schema_instances($keydef);
Schema::ns($keydef, null, null, false);
}; unset($keydef);
break;
case "list":
Schema::ns($definition["schema"], null, null, false);
break;
}
}
/**
@ -57,17 +238,22 @@ abstract class Schema implements ArrayAccess {
*/
const SCHEMA = null;
/** @var array */
protected $definition;
protected array $_definition;
protected array $definition;
function getDefinition(): array {
return $this->_definition;
}
/** retourner true si le schéma est de nature tableau associatif */
function isAssoc(?AssocSchema &$assoc=null): bool { return false; }
function isAssoc(?AssocSchema &$schema=null): bool { return false; }
/** retourner true si le schéma est de nature liste */
function isList(?ListSchema &$list=null): bool { return false; }
function isList(?ListSchema &$schema=null): bool { return false; }
/** retourner true si le schéma est de nature scalaire */
function isScalar(?ScalarSchema &$scalar=null): bool { return false; }
function isScalar(?ScalarSchema &$schema=null): bool { return false; }
abstract function newValue(?Value &$destv=null, &$dest=null, $destKey=null): Value;
abstract function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): Wrapper;
#############################################################################
# key & properties

View File

@ -1,19 +1,36 @@
# nur\sery\schema
# nulib\schema
* tdate et tdatetime. qu'en est-il des autres classes (delay, etc.)
* possibilité de spécifier le format de la date à analyser
* ScalarSchema::from_property()
* possibilité de spécifier un type via sa classe, e.g
~~~php
Schema::ns($schema, [
MyType::class, null, "une valeur de type MyType"
]);
~~~
MyType doit implémenter IType
* type générique construit à partir d'un nom de classe, e.g
~~~php
Schema::ns($schema, [
MyClass::class, null, "une valeur de type MyClass"
]);
~~~
MyClass ne doit pas implémenter IType, et le type correspondant est créé avec
`new tgeneric(MyClass::class)`
* implémenter support `analyzer_func`, `extractor_func`, `parser_func`,
`normalizer_func`, `formatter_func`
* dans AssocSchema, support `[key_prefix]` qui permet de spécifier un préfixe
commun aux champs dans le tableau destination, e.g
~~~php
$value = Schema::ns($schema, [
$wrapper = Schema::ns($schema, [
"a" => "?string",
"b" => "?int",
])->newValue();
$dest = ["x_a" => 5, "x_b" => "10"],
$value->reset($dest, null, [
])->newWrapper();
$value = ["x_a" => 5, "x_b" => "10"],
$wrapper->reset($value, null, [
"key_prefix" => "x_",
]);
# $dest vaut ["x_a" => "5", "x_b" => 10];
# $value vaut ["x_a" => "5", "x_b" => 10];
~~~
définir si le préfixe doit être spécifié sur le schéma ou sur la valeur...
actuellement, le code ne permet pas de définir de tels paramètres...
@ -21,48 +38,16 @@
alternative: c'est lors de la *définition* du schéma que le préfixe est ajouté
e.g
~~~php
$value = Schema::ns($schema, [
$wrapper = Schema::ns($schema, [
"a" => "?string",
"b" => "?int",
], [
"key_prefix" => "x_",
])->newValue();
$dest = ["x_a" => 5, "x_b" => "10"],
$value->reset($dest);
# $dest vaut ["x_a" => "5", "x_b" => 10];
])->newWrapper();
$value = ["x_a" => 5, "x_b" => "10"],
$wrapper->reset($value);
# $value vaut ["x_a" => "5", "x_b" => 10];
~~~
* dans la définition, `[type]` est remplacé par l'instance de IType lors de sa
résolution?
* implémenter l'instanciation de types avec des paramètres particuliers. *si*
des paramètres sont fournis, le type est instancié avec la signature
`IType($typeDefinition, $schemaDefinition)` e.g
~~~php
const SCHEMA = ["type", default, "required" => true];
# le type est instancié comme suit:
$type = new ttype();
const SCHEMA = [[["type", ...]], default, "required" => true];
# le type est instancié comme suit:
# le tableau peut être une liste ou associatif, c'est au type de décider ce
# qu'il en fait
$type = new ttype(["type", ...], SCHEMA);
~~~
* ajouter à IType les méthodes getName() pour le nom officiel du type,
getAliases() pour les alias supportés, et getClass() pour la définition de la
classe dans les méthodes et propriétés
getName() et getAliases() sont juste pour information, ils ne sont pas utilisés
lors de la résolution du type effectif.
* si cela a du sens, dans AssocSchema, n'instancier les schémas de chaque clé qu'à la demande.
l'idée est de ne pas perdre du temps à instancier un schéma qui ne serait pas utilisé
on pourrait avoir d'une manière générale quelque chose comme:
~~~
Schema::ensure(&$schema, ?array $def=null, $defKey=null): Schema;
~~~
* si $schema est une instance de Schema, la retourner
* si c'est un array, c'est une définition et il faut la remplacer par l'instance de Schema correspondant
* sinon, prendre $def comme définition
$key est la clé si $schema est dans un autre schema
* actuellement, pour un schéma associatif, si on normalise un tableau séquentiel,
chaque valeur correspond à la clé de même rang, eg. pour un schéma
~~~php

View File

@ -3,18 +3,18 @@ namespace nur\sery\wip\schema;
use ArrayAccess;
use IteratorAggregate;
use nur\sery\wip\schema\_assoc\AssocValue;
use nur\sery\wip\schema\_list\ListValue;
use nur\sery\wip\schema\_scalar\ScalarValue;
use nur\sery\wip\schema\_assoc\AssocWrapper;
use nur\sery\wip\schema\_list\ListWrapper;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\types\IType;
abstract class Value implements ArrayAccess, IteratorAggregate {
function isAssoc(?AssocValue &$assoc=null): bool { return false; }
function isList(?ListValue &$list=null): bool { return false; }
function isScalar(?ScalarValue &$scalar=null): bool { return false; }
abstract class Wrapper implements ArrayAccess, IteratorAggregate {
function isAssoc(?AssocWrapper &$wrapper=null): bool { return false; }
function isList(?ListWrapper &$wrapper=null): bool { return false; }
function isScalar(?ScalarWrapper &$wrapper=null): bool { return false; }
/** spécifier la valeur destination gérée par cet objet */
abstract function reset(&$dest, $destKey=null, ?bool $verifix=null): self;
abstract function reset(&$value, $valueKey=null, ?bool $verifix=null): self;
/**
* Obtenir la liste des clés valides pour les valeurs accessibles via cet
@ -22,13 +22,19 @@ abstract class Value implements ArrayAccess, IteratorAggregate {
*/
abstract function getKeys(): array;
/** obtenir un objet pour gérer la valeur spécifiée */
abstract function getValue($key=null): Value;
/**
* sélectionner le wrapper associé à la clé spécifiée
*
* @param string|int|null $key
* @return Wrapper $this
*/
abstract function select($key): Wrapper;
function getIterator() {
foreach ($this->getKeys() as $key) {
yield $key => $this->getValue($key);
yield $key => $this->select($key);
}
$this->select(null);
}
/**
@ -72,14 +78,14 @@ abstract class Value implements ArrayAccess, IteratorAggregate {
}
function offsetGet($offset) {
return $this->getValue($offset);
return $this->select($offset);
}
function offsetSet($offset, $value): void {
$this->getValue($offset)->set($value);
$this->select($offset)->set($value);
}
function offsetUnset($offset): void {
$this->getValue($offset)->unset();
$this->select($offset)->unset();
}
}

View File

@ -1,8 +1,53 @@
<?php
namespace nur\sery\wip\schema\_assoc;
use nulib\ValueException;
use nur\sery\wip\schema\Result;
class AssocResult extends Result {
function isAssoc(?AssocResult &$assoc=null): bool { $assoc = $this; return true;}
function __construct(Result $arrayResult, array &$keyResults) {
$this->arrayResult = $arrayResult;
$this->keyResults =& $keyResults;
$this->result =& $this->arrayResult;
parent::__construct();
}
function isAssoc(?AssocResult &$result=null): bool { $result = $this; return true;}
protected Result $arrayResult;
/** @var Result[] */
protected array $keyResults;
function getKeys(): array {
return array_keys($this->keyResults);
}
protected Result $result;
function select($key): Result {
if ($key === null) {
$this->result =& $this->arrayResult;
} elseif (array_key_exists($key, $this->keyResults)) {
$this->result =& $this->keyResults[$key];
} else {
throw ValueException::invalid_key($key);
}
return $this;
}
function reset(): void {
$this->arrayResult->reset();
foreach ($this->keyResults as $result) {
$result->reset();
}
}
function __get(string $name) {
return $this->result[$name];
}
function __set(string $name, $value): void {
$this->result[$name] = $value;
}
}

View File

@ -1,10 +1,10 @@
<?php
namespace nur\sery\wip\schema\_assoc;
use nur\sery\cl;
use nur\sery\ref\schema\ref_schema;
use nulib\cl;
use nulib\ref\schema\ref_schema;
use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\Value;
use nur\sery\wip\schema\Wrapper;
/**
* Class AssocSchema
@ -20,36 +20,49 @@ class AssocSchema extends Schema {
static function isa_definition($definition): bool {
if (!is_array($definition)) return false;
# nature explicitement spécifiée
if (array_key_exists("", $definition)) {
$nature = $definition[""];
if ($nature === "assoc") return true;
if (is_array($nature)
&& array_key_exists(0, $nature)
&& $nature[0] === "assoc") {
return true;
}
return false;
if (self::have_nature($definition, $nature)) {
return $nature === "assoc";
}
# un tableau associatif
# tableau associatif
return !cl::have_num_keys($definition);
}
static function normalize($definition, $definitionKey=null): array {
if (!is_array($definition)) $definition = [$definition];
if (!self::have_nature($definition)) {
$definition = [
"?array",
"" => "assoc",
"schema" => $definition,
];
}
self::_normalize($definition, $definitionKey);
self::_ensure_nature($definition, "assoc", "array");
return $definition;
}
function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
if ($definition === null) $definition = static::SCHEMA;
if ($normalize) $definition = self::normalize($definition, $definitionKey);
if ($normalize) {
$definition = self::normalize($definition, $definitionKey);
$this->_definition = $definition;
self::_ensure_type($definition);
self::_ensure_schema_instances($definition);
}
$this->definition = $definition;
}
function isAssoc(?AssocSchema &$assoc=null): bool {
$assoc = $this;
function isAssoc(?AssocSchema &$schema=null): bool {
$schema = $this;
return true;
}
function newValue(?Value &$destv=null, &$dest=null, $destKey=null): AssocValue {
if ($destv instanceof AssocValue) return $destv->reset($dest, $destKey);
else return ($destv = new AssocValue($this, $dest, $destKey));
protected function newWrapper(): AssocWrapper {
return new AssocWrapper($this);
}
function getWrapper(&$array=null, $arrayKey=null, ?Wrapper &$wrapper=null): AssocWrapper {
if (!($wrapper instanceof AssocWrapper)) $wrapper = $this->newWrapper();
return $wrapper->reset($array, $arrayKey);
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace nur\sery\wip\schema\_assoc;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Value;
class AssocValue extends Value {
function isAssoc(?AssocValue &$assoc=null): bool { $assoc = $this; return true; }
function ensureKeys(): bool {
}
function orderKeys(): bool {
}
/** @param Result[] $results */
function verifix(bool $throw=true, ?array &$results=null): bool {
}
}

View File

@ -0,0 +1,140 @@
<?php
namespace nur\sery\wip\schema\_assoc;
use nulib\ValueException;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Wrapper;
class AssocWrapper extends Wrapper {
function __construct(AssocSchema $schema, &$array=null, $arrayKey=null, ?array $params=null) {
$verifix = $params["verifix"] ?? true;
$throw = $params["throw"] ?? null;
if ($array !== null && $throw === null) {
# Si $value est null, ne pas lancer d'exception, parce qu'on considère que
# c'est une initialisation sans conséquences
$throw = true;
}
$this->schema = $schema;
$this->verifix = $verifix;
$this->throw = $throw ?? false;
$this->result = new AssocResult();
$this->reset($array, $arrayKey);
$this->throw = $throw ?? true;
}
function isAssoc(?AssocWrapper &$wrapper=null): bool { $wrapper = $this; return true; }
protected bool $verifix;
protected bool $throw;
/** schéma de ce tableau */
protected AssocSchema $schema;
/** source et destination de la valeur */
protected Input $input;
/** @var string|int|null clé du tableau dans le tableau destination */
protected $arrayKey;
protected IType $arrayType;
protected ScalarResult $arrayResult;
/** @var IType[] */
protected array $keyTypes;
/** @var Result[] */
protected array $keyResults;
protected AssocResult $result;
protected ?array $keys;
protected ?array $wrappers;
protected function newInput(&$value): Input {
return new Input($value);
}
function reset(&$array, $arrayKey=null, ?bool $verifix=null): Wrapper {
if ($array instanceof Input) $input = $array;
else $input = $this->newInput($array);
$this->input = $input;
$this->arrayKey = $arrayKey;
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function getKeys(): array {
return $this->keys;
}
function select($key=null): ScalarWrapper {
$wrapper = $this->wrappers[$key] ?? null;
if ($key !== null) return $wrapper;
throw ValueException::invalid_key($key);
}
/** @param Result[] $results */
function verifix(?bool $throw=null, ?array &$results=null): bool {
}
function getResult(): AssocResult {
return $this->result;
}
function isPresent(): bool {
return $this->result->present;
}
function getType(): IType {
return $this->arrayType;
}
function isAvailable(): bool {
return $this->result->available;
}
function isValid(): bool {
return $this->result->valid;
}
function isNormalized(): bool {
return $this->result->normalized;
}
function get($default=null) {
if ($this->result->available) return $this->input->get($this->arrayKey);
else return $default;
}
function set($value, ?bool $verifix=null): AssocWrapper {
$this->input->set($value, $this->arrayKey);
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function unset(?bool $verifix=null): AssocWrapper {
$this->input->unset($this->arrayKey);
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function format($format = null): string {
// TODO: Implement format() method.
}
function ensureKeys(): bool {
}
function orderKeys(): bool {
}
}

View File

@ -1,8 +1,53 @@
<?php
namespace nur\sery\wip\schema\_list;
use nulib\ValueException;
use nur\sery\wip\schema\Result;
class ListResult extends Result {
function isList(?ListResult &$list=null): bool { $list = $this; return true;}
function __construct(Result $arrayResult, array &$keyResults) {
$this->arrayResult = $arrayResult;
$this->keyResults =& $keyResults;
$this->result =& $this->arrayResult;
parent::__construct();
}
function isList(?ListResult &$result=null): bool { $result = $this; return true;}
protected Result $arrayResult;
/** @var Result[] */
protected array $keyResults;
function getKeys(): array {
return array_keys($this->keyResults);
}
protected Result $result;
function select($key): Result {
if ($key === null) {
$this->result =& $this->arrayResult;
} elseif (array_key_exists($key, $this->keyResults)) {
$this->result =& $this->keyResults[$key];
} else {
throw ValueException::invalid_key($key);
}
return $this;
}
function reset(): void {
$this->arrayResult->reset();
foreach ($this->keyResults as $result) {
$result->reset();
}
}
function __get(string $name) {
return $this->result[$name];
}
function __set(string $name, $value): void {
$this->result[$name] = $value;
}
}

View File

@ -1,9 +1,9 @@
<?php
namespace nur\sery\wip\schema\_list;
use nur\sery\ref\schema\ref_schema;
use nulib\ref\schema\ref_schema;
use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\Value;
use nur\sery\wip\schema\Wrapper;
class ListSchema extends Schema {
/** @var array meta-schema d'un schéma de nature liste */
@ -16,15 +16,8 @@ class ListSchema extends Schema {
static function isa_definition($definition): bool {
if (!is_array($definition)) return false;
# nature explicitement spécifiée
if (array_key_exists("", $definition)) {
$nature = $definition[""];
if ($nature === "list") return true;
if (is_array($nature)
&& array_key_exists(0, $nature)
&& $nature[0] === "list") {
return true;
}
return false;
if (self::have_nature($definition, $nature)) {
return $nature === "list";
}
# un unique élément tableau à l'index 0
$count = count($definition);
@ -33,21 +26,41 @@ class ListSchema extends Schema {
}
static function normalize($definition, $definitionKey=null): array {
if (!is_array($definition)) $definition = [$definition];
if (!self::have_nature($definition)) {
$definition = [
"?array",
"" => "list",
"schema" => $definition[0],
];
}
self::_normalize($definition, $definitionKey);
self::_ensure_nature($definition, "list", "array");
return $definition;
}
function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
if ($definition === null) $definition = static::SCHEMA;
if ($normalize) $definition = self::normalize($definition, $definitionKey);
if ($normalize) {
$definition = self::normalize($definition, $definitionKey);
$this->_definition = $definition;
self::_ensure_type($definition);
self::_ensure_schema_instances($definition);
}
$this->definition = $definition;
}
function isList(?ListSchema &$list=null): bool {
$list = $this;
function isList(?ListSchema &$schema=null): bool {
$schema = $this;
return true;
}
function newValue(?Value &$destv=null, &$dest=null, $destKey=null): ListValue {
if ($destv instanceof ListValue) return $destv->reset($dest, $destKey);
else return ($destv = new ListValue($this, $dest, $destKey));
protected function newWrapper(): ListWrapper {
return new ListWrapper($this);
}
function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ListWrapper {
if (!($wrapper instanceof ListWrapper)) $wrapper = $this->newWrapper();
return $wrapper->reset($value, $valueKey);
}
}

View File

@ -2,10 +2,10 @@
namespace nur\sery\wip\schema\_list;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Value;
use nur\sery\wip\schema\Wrapper;
class ListValue extends Value {
function isList(?ListValue &$list=null): bool { $list = $this; return true; }
abstract/*XXX*/ class ListWrapper extends Wrapper {
function isList(?ListWrapper &$wrapper=null): bool { $wrapper = $this; return true; }
function ensureKeys(): bool {
}

View File

@ -1,36 +1,27 @@
<?php
namespace nur\sery\wip\schema\_scalar;
use nur\sery\cl;
use nur\sery\ref\schema\ref_analyze;
use nur\sery\ref\schema\ref_schema;
use nur\sery\ValueException;
use Exception;
use nulib\cl;
use nulib\ref\schema\ref_analyze;
use nulib\ref\schema\ref_schema;
use nulib\ValueException;
use nur\sery\wip\schema\Result;
use Throwable;
/**
* Class ScalarResult: résultat de l'analyse ou de la normalisation d'une valeur
*
* @property bool $resultAvailable le résultat est-il disponible?
* @property bool $present la valeur existe-t-elle?
* @property bool $available si la valeur existe, est-elle disponible?
* @property bool $null si la valeur est disponible, est-elle nulle?
* @property bool $valid si la valeur est disponible, est-elle valide?
* @property bool $normalized si la valeur est valide, est-elle normalisée?
* @property string|null $orig valeur originale avant analyse avec parse()
* @property string|null $message message si la valeur n'est pas valide
*/
class ScalarResult extends Result {
const KEYS = ["resultAvailable", "present", "available", "null", "valid", "normalized", "orig", "message"];
function isScalar(?ScalarResult &$scalar=null): bool { $scalar = $this; return true; }
function isScalar(?ScalarResult &$result=null): bool { $result = $this; return true; }
function getKeys(): array {
return [null];
}
function getResult($key=null): Result {
if ($key === null) return $this;
else throw ValueException::invalid_key($key);
function select($key): Result {
if ($key !== null) throw ValueException::invalid_key($key);
return $this;
}
/** @var array */
@ -65,8 +56,8 @@ class ScalarResult extends Result {
}
}
protected static function replace_orig(string &$message, $orig): void {
$message = str_replace("{orig}", strval($orig), $message);
protected static function replace_orig(string &$message, $origValue): void {
$message = str_replace("{orig}", strval($origValue), $message);
}
protected function getMessage(string $key, ScalarSchema $schema): string {
@ -85,7 +76,8 @@ class ScalarResult extends Result {
$this->normalized = true;
return ref_analyze::NORMALIZED;
} else {
$message = $this->getMessage("missing", $schema);
$messageKey = $this->messageKey = "missing";
$message = $this->getMessage($messageKey, $schema);
self::replace_key($message, $schema->name);
$this->message = $message;
return ref_analyze::MISSING;
@ -102,7 +94,8 @@ class ScalarResult extends Result {
$this->normalized = true;
return ref_analyze::NORMALIZED;
} else {
$message = $this->getMessage("unavailable", $schema);
$messageKey = $this->messageKey = "unavailable";
$message = $this->getMessage($messageKey, $schema);
self::replace_key($message, $schema->name);
$this->message = $message;
return ref_analyze::UNAVAILABLE;
@ -119,33 +112,40 @@ class ScalarResult extends Result {
$this->normalized = true;
return ref_analyze::NORMALIZED;
} else {
$message = $this->getMessage("null", $schema);
$messageKey = $this->messageKey = "null";
$message = $this->getMessage($messageKey, $schema);
self::replace_key($message, $schema->name);
$this->message = $message;
return ref_analyze::NULL;
}
}
function setInvalid($value, ScalarSchema $schema): int {
function setInvalid($value, ScalarSchema $schema, ?Throwable $t=null): int {
$this->resultAvailable = true;
$this->present = true;
$this->available = true;
$this->null = false;
$this->valid = false;
$this->orig = $value;
$message = $this->getMessage("invalid", $schema);
$this->origValue = $value;
$messageKey = $this->messageKey = "invalid";
$message = $this->getMessage($messageKey, $schema);
self::replace_key($message, $schema->name);
self::replace_orig($message, $schema->orig);
if ($t !== null) {
$tmessage = ValueException::get_message($t);
if ($tmessage) $message .= ": $tmessage";
}
$this->message = $message;
return ref_analyze::INVALID;
}
function setValid(): int {
function setValid($normalizedValue=null): int {
$this->resultAvailable = true;
$this->present = true;
$this->available = true;
$this->null = false;
$this->valid = true;
$this->normalizedValue = $normalizedValue;
return ref_analyze::VALID;
}

View File

@ -1,23 +1,15 @@
<?php
namespace nur\sery\wip\schema\_scalar;
use nur\sery\cl;
use nur\sery\ref\schema\ref_schema;
use nur\sery\ref\schema\ref_types;
use nulib\ref\schema\ref_schema;
use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\SchemaException;
use nur\sery\wip\schema\types\tarray;
use nur\sery\wip\schema\types\tbool;
use nur\sery\wip\schema\types\tcallable;
use nur\sery\wip\schema\types\tcontent;
use nur\sery\wip\schema\types\tpkey;
use nur\sery\wip\schema\types\tstring;
use nur\sery\wip\schema\Value;
use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Wrapper;
/**
* Class ScalarSchema
*
* @property-read array $type
* @property-read array|IType $type
* @property-read mixed $default
* @property-read string|null $title
* @property-read bool $required
@ -31,6 +23,7 @@ use nur\sery\wip\schema\Value;
* @property-read callable|null $formatterFunc
* @property-read mixed $format
* @property-read array $nature
* @property-read array|null $schema
* @property-read string|int|null $name
* @property-read string|array|null $pkey
* @property-read string|null $header
@ -72,111 +65,34 @@ class ScalarSchema extends Schema {
}
static function normalize($definition, $definitionKey=null): array {
if (!is_array($definition)) $definition = [$definition];
# s'assurer que toutes les clés existent avec leur valeur par défaut
$index = 0;
foreach (array_keys(self::METASCHEMA) as $key) {
if (!array_key_exists($key, $definition)) {
if (array_key_exists($index, $definition)) {
$definition[$key] = $definition[$index];
unset($definition[$index]);
$index++;
} else {
$definition[$key] = self::METASCHEMA[$key][1];
}
}
}
# réordonner les clés numériques
if (cl::have_num_keys($definition)) {
$keys = array_keys($definition);
$index = 0;
foreach ($keys as $key) {
if (!is_int($key)) continue;
$definition[$index] = $definition[$key];
unset($definition[$key]);
$index++;
}
}
# type
$types = [];
$deftype = $definition["type"];
$nullable = $definition["nullable"];
if ($deftype === null) {
$types[] = null;
$nullable = true;
} else {
if (!is_array($deftype)) {
if (!is_string($deftype)) throw SchemaException::invalid_type($deftype);
$deftype = explode("|", $deftype);
}
foreach ($deftype as $type) {
if ($type === null || $type === "null") {
$nullable = true;
continue;
}
if (!is_string($type)) throw SchemaException::invalid_type($type);
if (substr($type, 0, 1) == "?") {
$type = substr($type, 1);
$nullable = true;
}
if ($type === "") throw SchemaException::invalid_type($type);
$type = cl::get(ref_types::ALIASES, $type, $type);
$types = array_merge($types, explode("|", $type));
}
if (!$types) throw SchemaException::invalid_schema("scalar: type is required");
$types = array_keys(array_fill_keys($types, true));
}
$definition["type"] = $types;
$definition["nullable"] = $nullable;
# nature
$nature = $definition[""];
tarray::ensure_array($nature);
if (!array_key_exists(0, $nature) || $nature[0] !== "scalar") {
throw SchemaException::invalid_schema("expected scalar nature");
}
$definition[""] = $nature;
# name, pkey, header
$name = $definition["name"];
$pkey = $definition["pkey"];
$header = $definition["header"];
if ($name === null) $name = $definitionKey;
tstring::ensure_nstring($name);
tpkey::ensure_npkey($pkey);
tstring::ensure_nstring($header);
if ($pkey === null) $pkey = $name;
if ($header === null) $header = $name;
$definition["name"] = $name;
$definition["pkey"] = $pkey;
$definition["header"] = $header;
# autres éléments
tstring::ensure_nstring($definition["title"]);
tbool::ensure_bool($definition["required"]);
tbool::ensure_bool($definition["nullable"]);
tcontent::ensure_ncontent($definition["desc"]);
tcallable::ensure_ncallable($definition["analyzer_func"]);
tcallable::ensure_ncallable($definition["extractor_func"]);
tcallable::ensure_ncallable($definition["parser_func"]);
tcallable::ensure_ncallable($definition["normalizer_func"]);
tarray::ensure_narray($definition["messages"]);
tcallable::ensure_ncallable($definition["formatter_func"]);
tbool::ensure_nbool($definition["composite"]);
self::_normalize($definition, $definitionKey);
self::_ensure_nature($definition, "scalar");
return $definition;
}
function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
if ($definition === null) $definition = static::SCHEMA;
if ($normalize) $definition = self::normalize($definition, $definitionKey);
if ($normalize) {
$definition = self::normalize($definition, $definitionKey);
$this->_definition = $definition;
self::_ensure_type($definition);
self::_ensure_schema_instances($definition);
}
$this->definition = $definition;
}
function isScalar(?ScalarSchema &$scalar=null): bool {
$scalar = $this;
function isScalar(?ScalarSchema &$schema=null): bool {
$schema = $this;
return true;
}
function newValue(?Value &$destv=null, &$dest=null, $destKey=null): ScalarValue {
if ($destv instanceof ScalarValue) return $destv->reset($dest, $destKey);
else return ($destv = new ScalarValue($this, $dest, $destKey));
protected function newWrapper(): ScalarWrapper {
return new ScalarWrapper($this);
}
function getWrapper(&$value=null, $valueKey=null, ?Wrapper &$wrapper=null): ScalarWrapper {
if (!($wrapper instanceof ScalarWrapper)) $wrapper = $this->newWrapper();
return $wrapper->reset($value, $valueKey);
}
#############################################################################

View File

@ -1,198 +0,0 @@
<?php
namespace nur\sery\wip\schema\_scalar;
use nur\sery\ref\schema\ref_analyze;
use nur\sery\ValueException;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\types;
use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Value;
class ScalarValue extends Value {
function __construct(ScalarSchema $schema, &$dest=null, $destKey=null, bool $defaultVerifix=true, ?bool $defaultThrow=null) {
if ($dest !== null && $defaultThrow = null) {
# Si $dest est null, ne pas lancer d'exception, parce qu'on considère que
# c'est une initialisation sans conséquences
$defaultThrow = true;
}
$this->schema = $schema;
$this->defaultVerifix = $defaultVerifix;
$this->defaultThrow = $defaultThrow !== null? $defaultThrow: false;
$this->result = new ScalarResult();
$this->reset($dest, $destKey);
$this->defaultThrow = $defaultThrow !== null? $defaultThrow: true;
}
function isScalar(?ScalarValue &$scalar=null): bool { $scalar = $this; return true; }
/** @var ScalarSchema schéma de cette valeur */
protected $schema;
/** @var Input source et destination de la valeur */
protected $input;
/** @var string|int|null clé de la valeur dans le tableau destination */
protected $destKey;
/** @var bool */
protected $defaultVerifix;
/** @var bool */
protected $defaultThrow;
/** @var IType type de la valeur après analyse */
protected $type;
/** @var ?ScalarResult résultat de l'analyse de la valeur */
protected $result;
function reset(&$dest, $destKey=null, ?bool $verifix=null): Value {
if ($dest instanceof Input) $input = $dest;
else $input = new Input($dest);
$this->input = $input;
$this->destKey = $destKey;
$this->type = null;
$this->_analyze();
if ($verifix == null) $verifix = $this->defaultVerifix;
if ($verifix) $this->verifix();
return $this;
}
function getKeys(): array {
return [null];
}
function getValue($key=null): ScalarValue {
if ($key === null) return $this;
throw ValueException::invalid_key($key);
}
/** analyser la valeur et résoudre son type */
function _analyze(): int {
$schema = $this->schema;
$input = $this->input;
$destKey = $this->destKey;
$result = $this->result;
$result->reset();
if (!$input->isPresent($destKey)) return $result->setMissing($schema);
$haveType = false;
$types = [];
$type = $firstType = null;
$haveValue = false;
$value = null;
# d'abord chercher un type pour lequel c'est une valeur normalisée
foreach ($schema->type as $name) {
$type = types::get($name);
if ($firstType === null) $firstType = $type;
$types[] = $type;
if ($type->isAvailable($input, $destKey)) {
if (!$haveValue) {
$value = $input->get($destKey);
$haveValue = true;
}
if ($type->isValid($value, $normalized) && $normalized) {
$haveType = true;
$this->type = $type;
break;
}
}
}
if (!$haveType) {
# ensuite chercher un type pour lequel la valeur est valide
foreach ($types as $type) {
if ($type->isAvailable($input, $destKey) && $type->isValid($value)) {
$haveType = true;
$this->type = $type;
break;
}
}
}
# sinon prendre le premier type
if (!$haveType) $type = $this->type = $firstType;
if (!$type->isAvailable($input, $destKey)) return $result->setUnavailable($schema);
$value = $input->get($destKey);
if ($type->isNull($value)) return $result->setNull($schema);
if ($type->isValid($value, $normalized)) {
if ($normalized) return $result->setNormalized();
else return $result->setValid();
}
if (is_string($value)) return ref_analyze::STRING;
else return $result->setInvalid($value, $schema);
}
function verifix(?bool $throw=null): bool {
if ($throw === null) $throw = $this->defaultThrow;
$destKey = $this->destKey;
$verifix = false;
$result =& $this->result;
$modified = false;
if ($result->resultAvailable) {
if ($result->null) {
# forcer la valeur null, parce que la valeur actuelle est peut-être une
# valeur assimilée à null
$this->input->set(null, $destKey);
} elseif ($result->valid && !$result->normalized) {
# normaliser la valeur
$verifix = true;
}
} else {
$verifix = true;
}
if ($verifix) {
$value = $this->input->get($destKey);
$modified = $this->type->verifix($value, $result, $this->schema);
if ($result->valid) $this->input->set($value, $destKey);
}
if (!$result->valid) $result->throw($throw);
return $modified;
}
function getResult(): ScalarResult {
return $this->result;
}
function isPresent(): bool {
return $this->result->present;
}
function getType(): IType {
return $this->type;
}
function isAvailable(): bool {
return $this->result->available;
}
function isValid(): bool {
return $this->result->valid;
}
function isNormalized(): bool {
return $this->result->normalized;
}
function get($default=null) {
if ($this->result->available) return $this->input->get($this->destKey);
else return $default;
}
function set($value, ?bool $verifix=null): ScalarValue {
$this->input->set($value, $this->destKey);
$this->_analyze();
if ($verifix === null) $verifix = $this->defaultVerifix;
if ($verifix) $this->verifix();
return $this;
}
function unset(?bool $verifix=null): ScalarValue {
$this->input->unset($this->destKey);
$this->_analyze();
if ($verifix === null) $verifix = $this->defaultVerifix;
if ($verifix) $this->verifix();
return $this;
}
function format($format=null): string {
return $this->type->format($this->input->get($this->destKey), $format);
}
}

View File

@ -0,0 +1,317 @@
<?php
namespace nur\sery\wip\schema\_scalar;
use nulib\php\func;
use nulib\ref\schema\ref_analyze;
use nulib\ValueException;
use nur\sery\wip\schema\AnalyzerContext;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\types;
use nur\sery\wip\schema\types\IType;
use nur\sery\wip\schema\Wrapper;
class ScalarWrapper extends Wrapper {
function __construct(ScalarSchema $schema, &$value=null, $valueKey=null, ?array $params=null) {
$verifix = $params["verifix"] ?? true;
$throw = $params["throw"] ?? null;
if ($value !== null && $throw === null) {
# Si $value est null, ne pas lancer d'exception, parce qu'on considère que
# c'est une initialisation sans conséquences
$throw = true;
}
$this->verifix = $verifix;
$this->throw = $throw ?? false;
$this->schema = $schema;
$this->result = new ScalarResult();
$this->reset($value, $valueKey);
$this->throw = $throw ?? true;
}
function isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; }
protected bool $verifix;
protected bool $throw;
/** schéma de cette valeur */
protected ScalarSchema $schema;
/** source et destination de la valeur */
protected Input $input;
/** @var string|int|null clé de la valeur dans le tableau destination */
protected $valueKey;
/** type de la valeur après analyse */
protected ?IType $type;
/** résultat de l'analyse de la valeur */
protected ScalarResult $result;
protected function newInput(&$value): Input {
return new Input($value);
}
function reset(&$value, $valueKey=null, ?bool $verifix=null): Wrapper {
if ($value instanceof Input) $input = $value;
else $input = $this->newInput($value);
$this->input = $input;
$this->valueKey = $valueKey;
$this->type = null;
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function getKeys(): array {
return [null];
}
/** @param string|int|null $key */
function select($key): ScalarWrapper {
if ($key !== null) throw ValueException::invalid_key($key);
return $this;
}
/** analyser la valeur et résoudre son type */
protected function analyze0(AnalyzerContext $context): int {
/** @var ScalarSchema $schema */
$schema = $context->schema;
$input = $context->input;
$valueKey = $context->valueKey;
/** @var ScalarResult $result */
$result = $context->result;
$default = $schema->default;
if (!$input->isPresent($valueKey)) {
if ($default !== null) {
$input->set($default, $valueKey);
return $result->setNormalized();
} else {
return $result->setMissing($schema);
}
}
$schemaTypes = $schema->type;
if ($schemaTypes instanceof IType) {
$type = $schemaTypes;
} else {
# type union
$haveType = false;
$types = [];
$type = $firstType = null;
$value = null;
# d'abord chercher un type pour lequel c'est une valeur normalisée
$index = 0;
$haveValue = false;
foreach ($schemaTypes as $key => $name) {
if ($key === $index) {
$index++;
$args = null;
} else {
$args = $name;
$name = $key;
}
$type = types::get($schema->nullable, $name, $args, $this->schema->getDefinition());
if ($firstType === null) $firstType = $type;
$types[] = $type;
if ($type->isAvailable($input, $valueKey)) {
if (!$haveValue) {
$value = $input->get($valueKey);
$haveValue = true;
}
if ($type->isValid($value, $normalized) && $normalized) {
$haveType = true;
break;
}
}
}
# ensuite chercher un type pour lequel la valeur est valide
if (!$haveType) {
foreach ($types as $type) {
if ($type->isAvailable($input, $valueKey) && $type->isValid($value)) {
$haveType = true;
break;
}
}
}
# sinon prendre le premier type
if (!$haveType) {
$type = $firstType;
}
}
$context->type = $this->type = $type;
if (!$type->isAvailable($input, $valueKey)) {
if ($default !== null) {
$input->set($default, $valueKey);
return $result->setNormalized();
} else {
return $result->setUnavailable($schema);
}
}
$value = $input->get($valueKey);
$context->origValue = $context->value = $value;
if ($type->isNull($value)) {
return $result->setNull($schema);
} elseif (is_string($value)) {
return ref_analyze::STRING;
} elseif ($type->isValid($value, $normalized)) {
if ($normalized) return $result->setNormalized();
else return $result->setValid();
} else {
return $result->setInvalid($value, $schema);
}
}
protected function analyze(): int {
$schema = $this->schema;
$input = $this->input;
$valueKey = $this->valueKey;
$result = $this->result;
$result->reset();
$context = new AnalyzerContext($schema, $this, $input, $valueKey, $result);
/** @var func $analyzerFunc */
$analyzerFunc = $schema->analyzerFunc;
if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context]);
else $what = $this->analyze0($context);
if ($what !== ref_analyze::STRING) return $what;
$value = $context->value;
try {
/** @var func $extractorFunc */
$extractorFunc = $schema->extractorFunc;
if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context]);
else $extracted = $context->type->extract($value);
$context->value = $extracted;
} catch (ValueException $e) {
return $result->setInvalid($context->origValue, $schema, $e);
}
if ($context->type->isNull($extracted)) return $result->setNull($schema);
try {
/** @var func $parserFunc */
$parserFunc = $schema->parserFunc;
if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context]);
else $parsed = $context->type->parse($extracted);
$context->value = $parsed;
} catch (ValueException $e) {
return $result->setInvalid($context->origValue, $schema, $e);
}
$normalized = $parsed === $context->origValue;
if ($normalized) {
$input->set($parsed, $valueKey);
return $result->setNormalized();
} else {
$input->set($extracted, $valueKey);
return $result->setValid($parsed);
}
}
function verifix(?bool $throw=null): bool {
$result = $this->result;
$valueKey = $this->valueKey;
$verifix = false;
$modified = false;
if ($result->resultAvailable) {
if ($result->null) {
# forcer la valeur null, parce que la valeur actuelle est peut-être une
# valeur assimilée à null
$this->input->set(null, $valueKey);
} elseif ($result->valid && !$result->normalized) {
$normalizedValue = $result->normalizedValue;
if ($normalizedValue !== null) {
# la valeur normalisée est disponible
$this->input->set($normalizedValue);
$result->normalizedValue = null;
$modified = true;
} else {
# normaliser la valeur
$verifix = true;
}
}
} else {
$verifix = true;
}
if ($verifix) {
$value = $this->input->get($valueKey);
$schema = $this->schema;
/** @var func $normalizerFunc */
$normalizerFunc = $schema->normalizerFunc;
if ($normalizerFunc !== null) {
$context = new AnalyzerContext($schema, $this, $this->input, $valueKey, $result);
$orig = $value;
$value = $normalizerFunc->invoke([$orig, $context]);
$modified = $value !== $orig;
} else {
$modified = $this->type->verifix($value, $result, $this->schema);
}
if ($result->valid) $this->input->set($value, $valueKey);
}
if (!$result->valid) $result->throw($throw ?? $this->throw);
return $modified;
}
function getResult(): ScalarResult {
return $this->result;
}
function isPresent(): bool {
return $this->result->present;
}
function getType(): IType {
return $this->type;
}
function isAvailable(): bool {
return $this->result->available;
}
function isValid(): bool {
return $this->result->valid;
}
function isNormalized(): bool {
return $this->result->normalized;
}
function get($default=null) {
if ($this->result->available) return $this->input->get($this->valueKey);
else return $default;
}
function set($value, ?bool $verifix=null): ScalarWrapper {
$this->input->set($value, $this->valueKey);
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function unset(?bool $verifix=null): ScalarWrapper {
$this->input->unset($this->valueKey);
$this->analyze();
if ($verifix ?? $this->verifix) $this->verifix();
return $this;
}
function format($format=null): string {
$value = $this->input->get($this->valueKey);
/** @var func $formatterFunc */
$formatterFunc = $this->schema->formatterFunc;
if ($formatterFunc !== null) {
# la fonction formatter n'a pas forcément accès au format de la définition
# le lui fournir ici
$format ??= $this->schema->format;
return $formatterFunc->invoke([$value, $format]);
} else {
# on assume que le type a été initialisé avec le format de la définition
# le cas échéant
return $this->type->format($value, $format);
}
}
}

View File

@ -25,7 +25,7 @@ class FormInput extends Input {
}
protected function access($key): IAccess {
return $this->keyAccess[$key] ??= new ShadowAccess($this->formAccess($key), new KeyAccess($this->dest, $key, [
return $this->keyAccess[$key] ??= new ShadowAccess($this->formAccess($key), new KeyAccess($this->value, $key, [
"allow_empty" => $this->allowEmpty,
]));
}

View File

@ -13,13 +13,13 @@ use nur\sery\wip\php\access\ValueAccess;
class Input {
const ALLOW_EMPTY = true;
function __construct(&$dest=null, ?array $params=null) {
$this->dest =& $dest;
function __construct(&$value=null, ?array $params=null) {
$this->value =& $value;
$this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY;
}
/** @var mixed */
protected $dest;
protected $value;
/**
* @var bool comment considérer une chaine vide: "" si allowEmpty, null sinon
@ -31,12 +31,12 @@ class Input {
protected function access($key): IAccess {
if ($key === null) {
return $this->valueAccess ??= new ValueAccess($this->dest, [
return $this->valueAccess ??= new ValueAccess($this->value, [
"allow_null" => true,
"allow_empty" => $this->allowEmpty,
]);
} else {
return $this->keyAccess[$key] ??= new KeyAccess($this->dest, $key, [
return $this->keyAccess[$key] ??= new KeyAccess($this->value, $key, [
"allow_empty" => $this->allowEmpty,
]);
}

View File

@ -6,9 +6,16 @@ use nur\sery\wip\schema\types\Registry;
use nur\sery\wip\schema\types\tarray;
use nur\sery\wip\schema\types\tbool;
use nur\sery\wip\schema\types\tcallable;
use nur\sery\wip\schema\types\tcontent;
use nur\sery\wip\schema\types\tfloat;
use nur\sery\wip\schema\types\tint;
use nur\sery\wip\schema\types\tkey;
use nur\sery\wip\schema\types\tmixed;
use nur\sery\wip\schema\types\tpkey;
use nur\sery\wip\schema\types\traw;
use nur\sery\wip\schema\types\trawstring;
use nur\sery\wip\schema\types\tstring;
use nur\sery\wip\schema\types\ttext;
/**
* Class types: classe outil pour gérer le registre de types
@ -24,14 +31,21 @@ class types {
return self::$registry;
}
static function get(string $name): IType {
return self::registry()->get($name);
static function get(bool $nullable, ?string $name, ?array $args=null, ?array $definition=null): IType {
return self::registry()->get($nullable, $name, $args, $definition);
}
static function string(): tstring { return self::get("string"); }
static function bool(): tbool { return self::get("bool"); }
static function int(): tint { return self::get("int"); }
static function float(): tfloat { return self::get("float"); }
static function array(): tarray { return self::get("array"); }
static function callable(): tcallable { return self::get("callable"); }
static function rawstring(bool $nullable=true): trawstring { return self::get($nullable, "rawstring"); }
static function string(bool $nullable=true): tstring { return self::get($nullable, "string"); }
static function text(bool $nullable=true): ttext { return self::get($nullable, "text"); }
static function bool(bool $nullable=true): tbool { return self::get($nullable, "bool"); }
static function int(bool $nullable=true): tint { return self::get($nullable, "int"); }
static function float(bool $nullable=true): tfloat { return self::get($nullable, "float"); }
static function array(bool $nullable=true): tarray { return self::get($nullable, "array"); }
static function callable(bool $nullable=true): tcallable { return self::get($nullable, "callable"); }
static function raw(bool $nullable=true): traw { return self::get($nullable, "raw"); }
static function mixed(bool $nullable=true): tmixed { return self::get($nullable, "mixed"); }
static function key(bool $nullable=true): tkey { return self::get($nullable, "key"); }
static function pkey(bool $nullable=true): tpkey { return self::get($nullable, "pkey"); }
static function content(bool $nullable=true): tcontent { return self::get($nullable, "content"); }
}

View File

@ -1,6 +1,7 @@
<?php
namespace nur\sery\wip\schema\types;
use nulib\ValueException;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
@ -9,25 +10,130 @@ use nur\sery\wip\schema\Schema;
* Interface IType: un type de données
*/
interface IType {
/** la donnée $input($destKey) est-elle disponible? */
function isAvailable(Input $input, $destKey): bool;
/**
* obtenir, pour information, le nom officiel de ce type, utilisable dans une
* définition de schéma
*/
function getName(): string;
/** obtenir la liste des aliases valides pour ce type */
function getAliases(): array;
/**
* @return string la classe des objets gérés par ce format: le type attendu
* par {@link format()} et le type retourné par {@link verifix()}
*
* Les valeurs "mixed", "bool", "float", "int", "string" et "array" peuvent
* aussi être retournées, bien qu'elles ne soient pas à proprement parler des
* classes.
*
* La valeur "mixed" signifie qu'il peut s'agir de n'importe quelle classe
* et/ou que la valeur ne peut pas être déterminée à l'avance.
*/
function getClass(): string;
/**
* comme {@link getClass()} mais peut être utilisé directement comme type dans
* une déclaration PHP. notamment, si le type est nullable et que
* $allowNullable==true, il y a "?" devant le nom.
*
* Par exemple:
* - pour un type chaine nullable, {@link getClass()} retournerait "string"
* alors que cette méthode retournerait "?string".
* - pour un type mixed, {@link getClass()} retournerait "mixed" alors que
* cette méthode retournerait null
*/
function getPhpType(bool $allowNullable=true): ?string;
/**
* indiquer si c'est le type d'une valeur qui ne peut prendre que 2 états: une
* "vraie" et une "fausse"
*/
function is2States(): bool;
/**
* Si {@link is2States()} est vrai, retourner les deux valeurs [faux, vrai]
*/
function get2States(): array;
/**
* indiquer si c'est le type d'une valeur qui ne peut prendre que 3 états: une
* "vraie", une "fausse", et une "indéterminée"
*/
function is3States(): bool;
/**
* Si {@link is3States()} est vrai, retourner les 3 valeurs [faux, vrai, undef]
*/
function get3States(): array;
/** la donnée $input($valueKey) est-elle disponible? */
function isAvailable(Input $input, $valueKey): bool;
/** la valeur $value est-elle nulle? */
function isNull($value): bool;
/** la valeur $value est-elle valide et normalisée le cas échéant? */
/**
* la valeur $value est-elle valide et normalisée le cas échéant?
*
* NB: si $value est un string. elle doit avoir déjà été traitée au préalable
* par extract() et parse()
*/
function isValid($value, ?bool &$normalized=null): bool;
/**
* extraire de la chaine la valeur à analyser
*
* @throws ValueException en cas d'erreur d'analyse
*/
function extract(string $value): string;
/**
* analyser la chaine et retourner la valeur "convertie"
*
* @throws ValueException en cas d'erreur d'analyse
*/
function parse(string $value);
/**
* analyser, corriger éventuellement et normaliser la valeur
*
* NB: si $value est un string. elle doit avoir déjà été traitée au préalable
* par extract() et parse()
*
* si la valeur était déjà normalisée, ou si une erreur s'est produite,
* retourner false.
*/
function verifix(&$value, Result &$result, Schema $schema): bool;
function verifix(&$value, Result $result, Schema $schema): bool;
/**
* formatter la valeur pour affichage. $value est garanti d'être du bon type
* formatter la valeur pour affichage. si $value n'est pas null, elle est
* garantie d'être du bon type
*/
function format($value, $format=null): string;
#############################################################################
/** @return string le nom d'un getter pour une valeur de ce type */
function getGetterName(string $name): string;
/** @return string le nom d'un setter pour une valeur de ce type */
function getSetterName(string $name): string;
/** @return string le nom d'un deleter pour une valeur de ce type */
function getDeleterName(string $name): string;
/**
* @return string le nom d'une constante de classe pour une valeur de ce type
*/
function getClassConstName(string $name): string;
/**
* @return string le nom d'une propriété d'une classe pour une valeur de ce
* type
*/
function getObjectPropertyName(string $name): string;
/** @return string le nom d'une clé d'un tableau pour une valeur de ce type */
function getArrayKeyName(string $name): string;
}

View File

@ -1,11 +1,14 @@
<?php
namespace nur\sery\wip\schema\types;
use nur\sery\cl;
use nulib\cl;
use nulib\php\func;
class Registry {
const TYPES = [
"rawstring" => trawstring::class,
"string" => tstring::class,
"text" => ttext::class,
"bool" => tbool::class, "boolean" => tbool::class,
"int" => tint::class, "integer" => tint::class,
"float" => tfloat::class, "flt" => tfloat::class,
@ -13,6 +16,8 @@ class Registry {
"array" => tarray::class,
"callable" => tcallable::class,
# types spéciaux
"raw" => tmixed::class,
"mixed" => tmixed::class,
"key" => tkey::class,
"pkey" => tpkey::class,
"content" => tcontent::class,
@ -25,12 +30,25 @@ class Registry {
/** @var IType[] */
protected $types;
function get(string $name): IType {
$type = cl::get($this->types, $name);
if ($type === null) {
$class = self::TYPES[$name];
$type = $this->types[$name] = new $class();
function get(bool $nullable, ?string $name, ?array $args=null, ?array $definition=null): IType {
$name ??= "raw";
$class = self::TYPES[$name];
if (cl::is_list($args)) {
$key = array_key_last($args);
$params = $args[$key];
unset($args[$key]);
} else {
$params = $args;
$args = null;
}
$params = cl::merge($class::get_params_from_definition($definition), $params);
if ($args || $params !== null) {
$args ??= [];
return func::with([$class, false, ...$args, $nullable, $params])->invoke();
}
if ($nullable) $name = "?$name";
$type = cl::get($this->types, $name);
if ($type === null) $type = $this->types[$name] = new $class($nullable);
return $type;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace nur\sery\wip\schema\types;
abstract class _tformatable extends _tsimple {
const FORMAT = null;
static function get_params_from_definition(?array $definition): ?array {
$params = null;
$format = $definition["format"] ?? null;
if ($format !== null) $params["format"] = $format;
return $params;
}
function format($value, $format=null): string {
$format ??= $this->params["format"] ?? static::FORMAT;
if ($format !== null) return sprintf($format, $value);
else return strval($value);
}
}

View File

@ -1,14 +1,99 @@
<?php
namespace nur\sery\wip\schema\types;
use nulib\StateException;
use nur\prop;
use nur\sery\wip\schema\input\Input;
use nur\str;
abstract class _tsimple implements IType {
function isAvailable(Input $input, $destKey): bool {
return $input->isAvailable($destKey) && $input->get($destKey) !== false;
const NAME = null;
const ALIASES = [];
static function get_params_from_definition(?array $definition): ?array {
return null;
}
/**
* $nullable et $params doivent toujours être les derniers arguments du
* constructeur
*/
function __construct(bool $nullable, ?array $params=null) {
$this->nullable = $nullable;
$this->params = $params;
}
protected bool $nullable;
protected ?array $params;
function getName(): string {
return static::NAME;
}
function getAliases(): array {
return static::ALIASES;
}
function getPhpType(bool $allowNullable=true): ?string {
$phpType = $this->getClass();
if ($phpType === "mixed") return null;
if ($this->nullable && $allowNullable) $phpType = "?$phpType";
return $phpType;
}
function is2States(): bool {
return false;
}
function get2States(): array {
throw StateException::not_implemented();
}
function is3States(): bool {
return false;
}
function get3States(): array {
throw StateException::not_implemented();
}
function isAvailable(Input $input, $valueKey): bool {
return $input->isAvailable($valueKey) && $input->get($valueKey) !== false;
}
function isNull($value): bool {
return $value === null || (is_string($value) && trim($value) === "");
return $value === null || $value === "";
}
function extract(string $value): string {
return $value;
}
#############################################################################
function getGetterName(string $name): string {
return prop::get_getter_name($name);
}
function getSetterName(string $name): string {
return prop::get_setter_name($name);
}
function getDeleterName(string $name): string {
return prop::get_deletter_name($name);
}
function getClassConstName(string $name): string {
return strtoupper($name);
}
function getObjectPropertyName(string $name): string {
return str::us2camel($name);
}
function getArrayKeyName(string $name): string {
return $name;
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace nur\sery\wip\schema\types;
use nulib\str;
abstract class _tstring extends _tsimple {
/** @var bool faut-il trimmer la valeur */
const TRIM = false;
/** @var bool faut-il normaliser les caractères fin de ligne */
const NORM_NL = false;
static function get_params_from_definition(?array $definition): ?array {
$params = null;
$trim = $definition["trim"] ?? null;
if ($trim !== null) $params["trim"] = $trim;
$normNl = $definition["norm_nl"] ?? null;
if ($normNl !== null) $params["norm_nl"] = $normNl;
return $params;
}
function extract(string $value): string {
if ($this->params["trim"] ?? static::TRIM) $value = trim($value);
if ($this->params["norm_nl"] ?? static::NORM_NL) $value = str::norm_nl($value);
return $value;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\wip\schema\types;
abstract class _tunion extends _tsimple {
function getPhpType(bool $allowNullable=true): ?string {
# assumer mixed pour le moment
#XXX à terme, lister les types de l'union
return null;
}
}

View File

@ -1,11 +1,28 @@
<?php
namespace nur\sery\wip\schema\types;
use nur\sery\cl;
use nulib\cl;
use nulib\ValueException;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
class tarray extends _tsimple {
class tarray extends _tstring {
const NAME = "array";
const SPLIT_PATTERN = '/\s+/';
const FORMAT = " ";
public static function get_params_from_definition(?array $definition): ?array {
$params = parent::get_params_from_definition($definition);
$splitPattern = $definition["split_pattern"] ?? null;
if ($splitPattern !== null) $params["split_pattern"] = $splitPattern;
$format = $definition["format"] ?? null;
if ($format !== null) $params["format"] = $format;
return $params;
}
static function ensure_array(&$array): void {
if (!is_array($array)) $array = cl::with($array);
}
@ -14,14 +31,47 @@ class tarray extends _tsimple {
if ($array !== null) self::ensure_array($array);
}
function getClass(): string {
return "array";
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_array($value);
return is_scalar($value) || is_array($value);
}
function verifix(&$value, Result &$result, Schema $schema): bool {
function parse(string $value) {
$pattern = $this->params["split_pattern"] ?? static::SPLIT_PATTERN;
return preg_split($pattern, $value);
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
if (is_array($value)) {
$result->setNormalized();
return false;
} elseif (is_string($value)) {
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
} elseif (is_scalar($value)) {
$value = cl::with($value);
$result->setValid();
return true;
}
$result->setInvalid($value, $schema);
return false;
}
function format($value, $format=null): string {
if ($value === null) return "";
$format ??= $this->params["format"] ?? static::FORMAT;
return implode($format, $value);
}
}

View File

@ -1,25 +1,28 @@
<?php
namespace nur\sery\wip\schema\types;
use nur\sery\cl;
use nur\sery\ValueException;
use nulib\cl;
use nulib\ValueException;
use nur\prop;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
class tbool extends _tsimple {
class tbool extends _tformatable {
const NAME = "bool";
const ALIASES = ["boolean"];
/** liste de valeurs chaines à considérer comme 'OUI' */
const YES_VALUES = [
# IMPORTANT: ordonner par taille décroissante pour compatibilité avec parse()
"true", "vrai", "yes", "oui",
"t", "v", "y", "o", "1",
];
/** liste de valeurs chaines à considérer comme 'NON' */
const NO_VALUES = [
# IMPORTANT: ordonner par taille décroissante pour compatibilité avec parse()
"false", "faux", "non", "no",
"f", "n", "0",
];
@ -53,41 +56,67 @@ class tbool extends _tsimple {
if ($bool !== null) self::ensure_bool($bool);
}
function isAvailable(Input $input, $destKey): bool {
return $input->isAvailable($destKey);
function getClass(): string {
return "bool";
}
function is2States(): bool {
return !$this->nullable;
}
function get2States(): array {
return [false, true];
}
function is3States(): bool {
return $this->nullable;
}
function get3States(): array {
return [false, true, null];
}
function isAvailable(Input $input, $valueKey): bool {
return $input->isAvailable($valueKey);
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_bool($value);
if (is_string($value)) {
$value = trim($value);
$valid = self::is_yes($value) || self::is_no($value);
} else {
$valid = is_scalar($value);
}
return $valid;
return is_scalar($value);
}
function extract(string $value): string {
return trim($value);
}
function parse(string $value) {
if (self::is_yes($value)) return true;
elseif (self::is_no($value)) return false;
throw new ValueException("une valeur booléenne est attendue");
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result &$result, Schema $schema): bool {
function verifix(&$value, Result $result, Schema $schema): bool {
if (is_bool($value)) {
$result->setNormalized();
return false;
} elseif (is_string($value)) {
$bool = trim($value);
if (self::is_yes($bool)) $value = true;
elseif (self::is_no($bool)) $value = false;
else return $result->setInvalid($value, $schema);
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
} elseif (is_scalar($value)) {
$value = boolval($value);
} else {
return $result->setInvalid($value, $schema);
$result->setValid();
return true;
}
$result->setValid();
return true;
$result->setInvalid($value, $schema);
return false;
}
const OUINON_FORMAT = ["Oui", "Non", false];
@ -105,10 +134,10 @@ class tbool extends _tsimple {
"oz" => self::OZ_FORMAT,
];
const DEFAULT_FORMAT = self::OUINON_FORMAT;
const FORMAT = self::OUINON_FORMAT;
function format($value, $format=null): string {
if ($format === null) $format = static::DEFAULT_FORMAT;
$format ??= $this->params["format"] ?? static::FORMAT;
if (is_string($format)) {
$oformat = $format;
$format = cl::get(self::FORMATS, strtolower($oformat));
@ -120,4 +149,8 @@ class tbool extends _tsimple {
}
return $value? $format[0]: $format[1];
}
function getGetterName(string $name): string {
return prop::get_getter_name($name, !$this->nullable);
}
}

View File

@ -1,27 +1,66 @@
<?php
namespace nur\sery\wip\schema\types;
use nur\sery\php\nur_func;
use nur\sery\ValueException;
use Exception;
use nulib\php\func;
use nulib\ValueException;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
use stdClass;
class tcallable extends _tsimple {
const NAME = "callable";
const ALIASES = ["func", "function"];
static function ensure_callable(&$callable): void {
if (!is_callable($callable)) throw ValueException::invalid_type($callable, "callable");
$callable = func::ensure($callable);
}
static function ensure_ncallable(&$callable): void {
if ($callable !== null) self::ensure_callable($callable);
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_callable($value);
return nur_func::check_func($value, stdClass::class);
function getClass(): string {
return func::class;
}
function verifix(&$value, Result &$result, Schema $schema): bool {
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_callable($value);
return func::check($value);
}
function parse(string $value) {
try {
return func::ensure($value);
} catch (Exception $e) {
throw new ValueException(null, null, 0, $e);
}
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
if ($value instanceof func) {
$result->setNormalized();
return false;
} elseif (is_callable($value)) {
$value = func::with($value);
$result->setNormalized();
return true;
} elseif (is_string($value)) {
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
}
$result->setInvalid($value, $schema);
return false;
}
function format($value, $format=null): string {

View File

@ -1,26 +1,56 @@
<?php
namespace nur\sery\wip\schema\types;
use nulib\php\content\c;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
abstract class tcontent extends _tsimple {
abstract class tcontent extends _tunion {
const NAME = "content";
static function ensure_content(&$content): void {
if (!is_string($content) && !is_array($content)) $content = strval($content);
if ($content === null || $content === false) $content = [];
elseif (!is_string($content) && !is_array($content)) $content = strval($content);
}
static function ensure_ncontent(&$content): void {
if ($content !== null) self::ensure_content($content);
}
function getClass(): string {
return "string|array";
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value) || is_array($value);
return is_scalar($value) || is_array($value);
}
function verifix(&$value, Result &$result, Schema $schema): bool {
function parse(string $value) {
return $value;
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
if (is_string($value) || is_array($value)) {
$result->setNormalized();
return false;
} elseif (is_scalar($value)) {
$value = strval($value);
$result->setValid();
return true;
} else {
$result->setInvalid($value, $schema);
return false;
}
}
function format($value, $format=null): string {
return c::to_string($value);
}
}

View File

@ -1,12 +1,17 @@
<?php
namespace nur\sery\wip\schema\types;
use nulib\ValueException;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
class tfloat extends _tsimple {
class tfloat extends _tformatable {
const NAME = "float";
const ALIASES = ["flt", "double", "dbl"];
static function ensure_float(&$float): void {
if (!is_float($float)) $float = floatval($float);
}
@ -15,36 +20,46 @@ class tfloat extends _tsimple {
if ($float !== null) self::ensure_float($float);
}
function getClass(): string {
return "float";
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_float($value);
if (is_string($value)) $valid = is_numeric(trim($value));
else $valid = is_scalar($value);
return $valid;
return is_scalar($value);
}
function extract(string $value): string {
return trim($value);
}
function parse(string $value) {
$value = str_replace(",", ".", trim($value));
if (is_numeric($value)) return floatval($value);
throw new ValueException("une valeur numérique flottante est attendue");
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result &$result, Schema $schema): bool {
function verifix(&$value, Result $result, Schema $schema): bool {
if (is_float($value)) {
$result->setNormalized();
return false;
} elseif (is_string($value)) {
$float = trim($value);
if (is_numeric($float)) $value = floatval($float);
else return $result->setInvalid($value, $schema);
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
} elseif (is_scalar($value)) {
$value = floatval($value);
} else {
return $result->setInvalid($value, $schema);
$result->setValid();
return true;
}
$result->setValid();
return true;
}
function format($value, $format=null): string {
if ($format !== null) return sprintf($format, $value);
else return strval($value);
$result->setInvalid($value, $schema);
return false;
}
}

View File

@ -1,12 +1,17 @@
<?php
namespace nur\sery\wip\schema\types;
use nulib\ValueException;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
class tint extends _tsimple {
class tint extends _tformatable {
const NAME = "int";
const ALIASES = ["integer"];
static function ensure_int(&$int): void {
if (!is_int($int)) $int = intval($int);
}
@ -15,38 +20,48 @@ class tint extends _tsimple {
if ($int !== null) self::ensure_int($int);
}
const INT_PATTERN = '/^[-+]?[0-9]+(?:\.[0-9]*)?$/';
//const INT_PATTERN = '/^[-+]?[0-9]+(?:\.[0-9]*)?$/';
function getClass(): string {
return "int";
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_int($value);
if (is_string($value)) $valid = is_numeric(trim($value));
else $valid = is_scalar($value);
return $valid;
return is_scalar($value);
}
function extract(string $value): string {
return trim($value);
}
function parse(string $value) {
$value = str_replace(",", ".", trim($value));
if (is_numeric($value)) return intval($value);
throw new ValueException("une valeur numérique entière est attendue");
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result &$result, Schema $schema): bool {
function verifix(&$value, Result $result, Schema $schema): bool {
if (is_int($value)) {
$result->setNormalized();
return false;
} elseif (is_string($value)) {
$int = trim($value);
if (is_numeric($int)) $value = intval($int);
else return $result->setInvalid($value, $schema);
try {
$value = $this->parse($value);
$result->setValid();
return true;
} catch (ValueException $e) {
}
} elseif (is_scalar($value)) {
$value = intval($value);
} else {
return $result->setInvalid($value, $schema);
$result->setValid();
return true;
}
$result->setValid();
return true;
}
function format($value, $format=null): string {
if ($format !== null) return sprintf($format, $value);
else return strval($value);
$result->setInvalid($value, $schema);
return false;
}
}

View File

@ -1,26 +1,56 @@
<?php
namespace nur\sery\wip\schema\types;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
class tkey extends _tsimple {
class tkey extends _tunion {
const NAME = "key";
static function ensure_key(&$key): void {
if (!is_string($key) && !is_int($key)) $key = strval($key);
if ($key === null) $key = "";
elseif ($key === false) $key = 0;
elseif (!is_string($key) && !is_int($key)) $key = strval($key);
}
static function ensure_nkey(&$key): void {
if ($key !== null) self::ensure_key($key);
}
function getClass(): string {
return "string|int";
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value) || is_int($value);
return is_scalar($value);
}
function verifix(&$value, Result &$result, Schema $schema): bool {
function parse(string $value) {
return $value;
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
if (is_string($value) || is_int($value)) {
$result->setNormalized();
return false;
} elseif (is_scalar($value)) {
$value = strval($value);
$result->setValid();
return true;
} else {
$result->setInvalid($value, $schema);
return false;
}
}
function format($value, $format=null): string {
return strval($value);
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace nur\sery\wip\schema\types;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\input\Input;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
class tmixed extends _tsimple {
const NAME = "mixed";
function getClass(): string {
return "mixed";
}
function isAvailable(Input $input, $valueKey): bool {
return $input->isAvailable($valueKey);
}
public function isNull($value): bool {
return $value === null;
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = true;
return true;
}
function parse(string $value) {
return $value;
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
$result->setNormalized();
return false;
}
function format($value, $format=null): string {
return var_export($value, true);
}
}

View File

@ -1,17 +1,22 @@
<?php
namespace nur\sery\wip\schema\types;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
class tpkey extends _tsimple {
class tpkey extends _tunion {
const NAME = "pkey";
static function ensure_pkey(&$pkey): void {
if (!is_string($pkey) && !is_int($pkey) && !is_array($pkey)) $pkey = strval($pkey);
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) {
tkey::ensure_key($key);
};
unset($key);
}; unset($key);
}
}
@ -19,14 +24,39 @@ class tpkey extends _tsimple {
if ($pkey !== null) self::ensure_pkey($pkey);
}
function getClass(): string {
return "string|int|array";
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value) || is_int($value) || is_array($value);
return is_scalar($value) || is_array($value);
}
function verifix(&$value, Result &$result, Schema $schema): bool {
function parse(string $value) {
return $value;
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
if (is_string($value) || is_int($value) || is_array($value)) {
$result->setNormalized();
return false;
} elseif (is_scalar($value)) {
$value = strval($value);
$result->setValid();
return true;
} else {
$result->setInvalid($value, $schema);
return false;
}
}
function format($value, $format=null): string {
if (is_array($value)) return implode(".", $value);
else return strval($value);
}
}

16
src/schema/types/traw.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace nur\sery\wip\schema\types;
use nur\sery\wip\schema\input\Input;
class traw extends tmixed {
const NAME = "raw";
function isAvailable(Input $input, $valueKey): bool {
return true;
}
public function isNull($value): bool {
return false;
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace nur\sery\wip\schema\types;
use nulib\str;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
class trawstring extends _tstring {
const NAME = "rawstring";
static function ensure_string(&$string): void {
if (!is_string($string)) $string = strval($string);
if (static::TRIM) $string = trim($string);
if (static::NORM_NL) $string = str::norm_nl($string);
}
static function ensure_nstring(&$string): void {
if ($string !== null) self::ensure_string($string);
}
function getClass(): string {
return "string";
}
function isNull($value): bool {
return $value === null;
}
function isValid($value, ?bool &$normalized=null): bool {
if (is_string($value)) {
$normalized = true;
return true;
}
return is_scalar($value);
}
function parse(string $value) {
return $value;
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result $result, Schema $schema): bool {
if (is_string($value)) {
$result->setNormalized();
return false;
} elseif (is_scalar($value)) {
$value = strval($value);
$result->setValid();
return true;
} else {
$result->setInvalid($value, $schema);
return false;
}
}
function format($value, $format=null): string {
return strval($value);
}
}

View File

@ -1,48 +1,8 @@
<?php
namespace nur\sery\wip\schema\types;
use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema;
class tstring extends trawstring {
const NAME = "string";
class tstring extends _tsimple {
static function ensure_string(&$string): void {
if (!is_string($string)) $string = strval($string);
}
static function ensure_nstring(&$string): void {
if ($string !== null) self::ensure_string($string);
}
function isNull($value): bool {
return $value === null;
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value);
return is_scalar($value);
}
/**
* @var ScalarResult $result
* @var ScalarSchema $schema
*/
function verifix(&$value, Result &$result, Schema $schema): bool {
if (is_string($value)) {
$result->setNormalized();
return false;
} elseif (is_scalar($value)) {
$value = strval($value);
$result->setValid();
return true;
} else {
$result->setInvalid($value, $schema);
return false;
}
}
function format($value, $format=null): string {
return strval($value);
}
const TRIM = true;
}

View File

@ -0,0 +1,9 @@
<?php
namespace nur\sery\wip\schema\types;
class ttext extends trawstring {
const NAME = "text";
const TRIM = true;
const NORM_NL = true;
}

View File

@ -1,9 +1,10 @@
<?php
namespace nur\sery\wip\web\content;
use nur\sery\A;
use nur\sery\php\content\c;
use nur\sery\php\content\IContent;
use nulib\A;
use nulib\php\content\c;
use nulib\php\content\IContent;
class Tag implements IContent {
function __construct(string $tag, $content=null) {

View File

@ -1,26 +0,0 @@
<?php
namespace nur\sery\app;
use nulib\tests\TestCase;
use nur\sery\app\args;
class argsTest extends TestCase {
function testFrom_array() {
self::assertSame([], args::from_array(null));
self::assertSame([], args::from_array([]));
self::assertSame([], args::from_array([false]));
self::assertSame(["x"], args::from_array(["x", false]));
self::assertSame(["--opt"], args::from_array(["--opt"]));
self::assertSame(["--opt", "value"], args::from_array(["--opt", "value"]));
self::assertSame([], args::from_array(["opt" => false]));
self::assertSame(["--opt"], args::from_array(["opt" => true]));
self::assertSame(["--opt", "value"], args::from_array(["opt" => "value"]));
self::assertSame(["--opt", "42"], args::from_array(["opt" => 42]));
self::assertSame(["--opt", "1", "2", "3", "--"], args::from_array(["opt" => [1, 2, 3]]));
self::assertSame(["x", "1", "2", "3", "y"], args::from_array(["x", [1, 2, 3], "y"]));
}
}

View File

@ -1,132 +0,0 @@
<?php
namespace nur\sery {
use nulib\tests\TestCase;
use nur\sery\impl\config;
use nur\sery\impl\myapp;
use nur\sery\impl\MyApplication1;
use nur\sery\impl\MyApplication2;
class appTest extends TestCase {
function testWith() {
$projdir = config::get_projdir();
$cwd = getcwd();
myapp::reset();
$app1 = myapp::with(MyApplication1::class);
self::assertSame([
"projdir" => $projdir,
"vendor" => [
"bindir" => "$projdir/vendor/bin",
"autoload" => "$projdir/vendor/autoload.php",
],
"appcode" => "nur-sery",
"cwd" => $cwd,
"datadir" => "$projdir/devel",
"etcdir" => "$projdir/devel/etc",
"vardir" => "$projdir/devel/var",
"logdir" => "$projdir/devel/log",
"profile" => "devel",
"appgroup" => null,
"name" => "my-application1",
"title" => null,
], $app1->getParams());
$app2 = myapp::with(MyApplication2::class, $app1);
self::assertSame([
"projdir" => $projdir,
"vendor" => [
"bindir" => "$projdir/vendor/bin",
"autoload" => "$projdir/vendor/autoload.php",
],
"appcode" => "nur-sery",
"cwd" => $cwd,
"datadir" => "$projdir/devel",
"etcdir" => "$projdir/devel/etc",
"vardir" => "$projdir/devel/var",
"logdir" => "$projdir/devel/log",
"profile" => "devel",
"appgroup" => null,
"name" => "my-application2",
"title" => null,
], $app2->getParams());
}
function testInit() {
$projdir = config::get_projdir();
$cwd = getcwd();
myapp::reset();
myapp::init(MyApplication1::class);
self::assertSame([
"projdir" => $projdir,
"vendor" => [
"bindir" => "$projdir/vendor/bin",
"autoload" => "$projdir/vendor/autoload.php",
],
"appcode" => "nur-sery",
"cwd" => $cwd,
"datadir" => "$projdir/devel",
"etcdir" => "$projdir/devel/etc",
"vardir" => "$projdir/devel/var",
"logdir" => "$projdir/devel/log",
"profile" => "devel",
"appgroup" => null,
"name" => "my-application1",
"title" => null,
], myapp::get()->getParams());
myapp::init(MyApplication2::class);
self::assertSame([
"projdir" => $projdir,
"vendor" => [
"bindir" => "$projdir/vendor/bin",
"autoload" => "$projdir/vendor/autoload.php",
],
"appcode" => "nur-sery",
"cwd" => $cwd,
"datadir" => "$projdir/devel",
"etcdir" => "$projdir/devel/etc",
"vardir" => "$projdir/devel/var",
"logdir" => "$projdir/devel/log",
"profile" => "devel",
"appgroup" => null,
"name" => "my-application2",
"title" => null,
], myapp::get()->getParams());
}
}
}
namespace nur\sery\impl {
use nur\sery\app\cli\Application;
use nur\sery\os\path;
use nur\sery\app;
class config {
const PROJDIR = __DIR__.'/..';
static function get_projdir(): string {
return path::abspath(self::PROJDIR);
}
}
class myapp extends app {
static function reset(): void {
self::$app = null;
}
}
class MyApplication1 extends Application {
const PROJDIR = config::PROJDIR;
function main() {
}
}
class MyApplication2 extends Application {
const PROJDIR = null;
function main() {
}
}
}

View File

@ -1 +0,0 @@
/capacitor.db*

View File

@ -1,344 +0,0 @@
<?php
namespace nur\sery\db\sqlite;
use nulib\tests\TestCase;
use nur\sery\cl;
use nur\sery\db\Capacitor;
use nur\sery\db\CapacitorChannel;
class SqliteStorageTest extends TestCase {
function _testChargeStrings(SqliteStorage $storage, ?string $channel) {
$storage->reset($channel);
$storage->charge($channel, "first");
$storage->charge($channel, "second");
$storage->charge($channel, "third");
$items = cl::all($storage->discharge($channel, false));
self::assertSame(["first", "second", "third"], $items);
}
function _testChargeArrays(SqliteStorage $storage, ?string $channel) {
$storage->reset($channel);
$storage->charge($channel, ["id" => 10, "name" => "first"]);
$storage->charge($channel, ["name" => "second", "id" => 20]);
$storage->charge($channel, ["name" => "third", "id" => "30"]);
}
function testChargeStrings() {
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
$this->_testChargeStrings($storage, null);
$storage->close();
}
function testChargeArrays() {
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
$storage->addChannel(new class extends CapacitorChannel {
const NAME = "arrays";
const COLUMN_DEFINITIONS = ["id" => "integer"];
function getItemValues($item): ?array {
return ["id" => $item["id"] ?? null];
}
});
$this->_testChargeStrings($storage, "strings");
$this->_testChargeArrays($storage, "arrays");
$storage->close();
}
function testEach() {
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
const NAME = "each";
const COLUMN_DEFINITIONS = [
"age" => "integer",
"done" => "integer default 0",
];
function getItemValues($item): ?array {
return [
"age" => $item["age"],
];
}
});
$capacitor->reset();
$capacitor->charge(["name" => "first", "age" => 5]);
$capacitor->charge(["name" => "second", "age" => 10]);
$capacitor->charge(["name" => "third", "age" => 15]);
$capacitor->charge(["name" => "fourth", "age" => 20]);
$setDone = function ($item, $row, $suffix=null) {
$updates = ["done" => 1];
if ($suffix !== null) {
$item["name"] .= $suffix;
$updates["item"] = $item;
}
return $updates;
};
$capacitor->each(["age" => [">", 10]], $setDone, ["++"]);
$capacitor->each(["done" => 0], $setDone, null);
Txx(cl::all($capacitor->discharge(false)));
$capacitor->close();
self::assertTrue(true);
}
function testPrimayKey() {
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
const NAME = "pk";
const COLUMN_DEFINITIONS = [
"id_" => "varchar primary key",
"done" => "integer default 0",
];
function getItemValues($item): ?array {
return [
"id_" => $item["numero"],
];
}
});
$capacitor->charge(["numero" => "a", "name" => "first", "age" => 5]);
$capacitor->charge(["numero" => "b", "name" => "second", "age" => 10]);
$capacitor->charge(["numero" => "c", "name" => "third", "age" => 15]);
$capacitor->charge(["numero" => "d", "name" => "fourth", "age" => 20]);
sleep(2);
$capacitor->charge(["numero" => "b", "name" => "second", "age" => 100]);
$capacitor->charge(["numero" => "d", "name" => "fourth", "age" => 200]);
$capacitor->close();
self::assertTrue(true);
}
function testSum() {
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
const NAME = "sum";
const COLUMN_DEFINITIONS = [
"a__" => "varchar",
"b__" => "varchar",
"b__sum_" => self::SUM_DEFINITION,
];
function getItemValues($item): ?array {
return [
"a" => $item["a"],
"b" => $item["b"],
];
}
});
$capacitor->reset();
$capacitor->charge(["a" => null, "b" => null]);
$capacitor->charge(["a" => "first", "b" => "second"]);
Txx("=== all");
/** @var Sqlite $sqlite */
$sqlite = $capacitor->getStorage()->db();
Txx(cl::all($sqlite->all([
"select",
"from" => $capacitor->getChannel()->getTableName(),
])));
Txx("=== each");
$capacitor->each(null, function ($item, $values) {
Txx($values);
});
$capacitor->close();
self::assertTrue(true);
}
function testEachValues() {
# tester que values contient bien toutes les valeurs de la ligne
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
const NAME = "each_values";
const COLUMN_DEFINITIONS = [
"name" => "varchar primary key",
"age" => "integer",
"done" => "integer default 0",
"notes" => "text",
];
function getItemValues($item): ?array {
return [
"name" => $item["name"],
"age" => $item["age"],
];
}
});
$capacitor->reset();
$capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $values, ?array $pvalues) {
self::assertSame("first", $item["name"]);
self::assertSame(5, $item["age"]);
self::assertnotnull($values);
self::assertSame(["name", "age", "item", "item__sum_", "created_", "modified_"], array_keys($values));
self::assertSame([
"name" => "first",
"age" => 5,
"item" => $item,
], cl::select($values, ["name", "age", "item"]));
self::assertNull($pvalues);
});
$capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) {
self::assertSame("first", $item["name"]);
self::assertSame(10, $item["age"]);
self::assertnotnull($values);
self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 0,
"notes" => null,
"item" => $item,
], cl::select($values, ["name", "age", "done", "notes", "item"]));
self::assertNotNull($pvalues);
self::assertSame([
"name" => "first",
"age" => 5,
"done" => 0,
"notes" => null,
"item" => ["name" => "first", "age" => 5],
], cl::select($pvalues, ["name", "age", "done", "notes", "item"]));
});
$capacitor->each(null, function($item, ?array $values) {
self::assertSame("first", $item["name"]);
self::assertSame(10, $item["age"]);
self::assertnotnull($values);
self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 0,
"notes" => null,
"item" => $item,
], cl::select($values, ["name", "age", "done", "notes", "item"]));
return [
"done" => 1,
"notes" => "modified",
];
});
$capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) {
self::assertSame("first", $item["name"]);
self::assertSame(10, $item["age"]);
self::assertnotnull($values);
self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 1,
"notes" => "modified",
"item" => $item,
], cl::select($values, ["name", "age", "done", "notes", "item"]));
self::assertNotNull($pvalues);
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 1,
"notes" => "modified",
"item" => $item,
], cl::select($pvalues, ["name", "age", "done", "notes", "item"]));
});
$capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $values, ?array $pvalues) {
self::assertSame("first", $item["name"]);
self::assertSame(20, $item["age"]);
self::assertnotnull($values);
self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
self::assertSame([
"name" => "first",
"age" => 20,
"done" => 1,
"notes" => "modified",
"item" => $item,
], cl::select($values, ["name", "age", "done", "notes", "item"]));
self::assertNotNull($pvalues);
self::assertSame([
"name" => "first",
"age" => 10,
"done" => 1,
"notes" => "modified",
"item" => ["name" => "first", "age" => 10],
], cl::select($pvalues, ["name", "age", "done", "notes", "item"]));
});
}
function testSetItemNull() {
# tester le forçage de $îtem à null pour économiser la place
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
const NAME = "set_item_null";
const COLUMN_DEFINITIONS = [
"name" => "varchar primary key",
"age" => "integer",
"done" => "integer default 0",
"notes" => "text",
];
function getItemValues($item): ?array {
return [
"name" => $item["name"],
"age" => $item["age"],
];
}
});
$capacitor->reset();
$nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $values, ?array $pvalues) {
self::assertSame([
"name" => "first", "age" => 5,
"item" => $item,
], cl::select($values, ["name", "age", "item"]));
return ["item" => null];
});
self::assertSame(1, $nbModified);
sleep(1);
# nb: on met des sleep() pour que la date de modification soit systématiquement différente
$nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) {
self::assertSame([
"name" => "first", "age" => 10,
"item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc",
], cl::select($values, ["name", "age", "item", "item__sum_"]));
self::assertSame([
"name" => "first", "age" => 5,
"item" => null, "item__sum_" => null,
], cl::select($pvalues, ["name", "age", "item", "item__sum_"]));
return ["item" => null];
});
self::assertSame(1, $nbModified);
sleep(1);
# pas de modification ici
$nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) {
self::assertSame([
"name" => "first", "age" => 10,
"item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc",
], cl::select($values, ["name", "age", "item", "item__sum_"]));
self::assertSame([
"name" => "first", "age" => 10,
"item" => null, "item__sum_" => null,
], cl::select($pvalues, ["name", "age", "item", "item__sum_"]));
return ["item" => null];
});
self::assertSame(0, $nbModified);
sleep(1);
$nbModified = $capacitor->charge(["name" => "first", "age" => 20], function ($item, ?array $values, ?array $pvalues) {
self::assertSame([
"name" => "first", "age" => 20,
"item" => $item, "item__sum_" => "001b91982b4e0883b75428c0eb28573a5dc5f7a5",
], cl::select($values, ["name", "age", "item", "item__sum_"]));
self::assertSame([
"name" => "first", "age" => 10,
"item" => null, "item__sum_" => null,
], cl::select($pvalues, ["name", "age", "item", "item__sum_"]));
return ["item" => null];
});
self::assertSame(1, $nbModified);
sleep(1);
}
}

View File

@ -1,146 +0,0 @@
<?php
namespace nur\sery\db\sqlite;
use Exception;
use nulib\tests\TestCase;
class SqliteTest extends TestCase {
const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)";
const INSERT_JEPHTE = "insert into person(nom, prenom, age) values ('clain', 'jephte', 50)";
const INSERT_JEAN = "insert into person(nom, prenom, age) values ('payet', 'jean', 32)";
function testMigration() {
$sqlite = new Sqlite(":memory:", [
"migrate" => [
self::CREATE_PERSON,
self::INSERT_JEPHTE,
],
]);
self::assertSame("clain", $sqlite->get("select nom, age from person"));
self::assertSame([
"nom" => "clain",
"age" => 50,
], $sqlite->get("select nom, age from person", null, true));
$sqlite->exec(self::INSERT_JEAN);
self::assertSame("payet", $sqlite->get("select nom, age from person where nom = 'payet'"));
self::assertSame([
"nom" => "payet",
"age" => 32,
], $sqlite->get("select nom, age from person where nom = 'payet'", null, true));
self::assertSame([
["key" => "0", "value" => self::CREATE_PERSON, "done" => 1],
["key" => "1", "value" => self::INSERT_JEPHTE, "done" => 1],
], iterator_to_array($sqlite->all("select key, value, done from _migration")));
}
function testException() {
$sqlite = new Sqlite(":memory:");
self::assertException(Exception::class, [$sqlite, "exec"], "prout");
self::assertException(SqliteException::class, [$sqlite, "exec"], ["prout"]);
}
protected function assertInserted(Sqlite $sqlite, array $row, array $query): void {
$sqlite->exec($query);
self::assertSame($row, $sqlite->one("select * from mapping where i = :i", [
"i" => $query["values"]["i"],
]));
}
function testInsert() {
$sqlite = new Sqlite(":memory:", [
"migrate" => "create table mapping (i integer, s varchar)",
]);
$sqlite->exec(["insert into mapping", "values" => ["i" => 1, "s" => "un"]]);
$sqlite->exec(["insert mapping", "values" => ["i" => 2, "s" => "deux"]]);
$sqlite->exec(["insert into", "into" => "mapping", "values" => ["i" => 3, "s" => "trois"]]);
$sqlite->exec(["insert", "into" => "mapping", "values" => ["i" => 4, "s" => "quatre"]]);
$sqlite->exec(["insert into mapping(i)", "values" => ["i" => 5, "s" => "cinq"]]);
$sqlite->exec(["insert into (i)", "into" => "mapping", "values" => ["i" => 6, "s" => "six"]]);
$sqlite->exec(["insert into mapping(i) values ()", "values" => ["i" => 7, "s" => "sept"]]);
$sqlite->exec(["insert into mapping(i) values (8)", "values" => ["i" => 42, "s" => "whatever"]]);
$sqlite->exec(["insert into mapping(i, s) values (9, 'neuf')", "values" => ["i" => 43, "s" => "garbage"]]);
$sqlite->exec(["insert into mapping", "cols" => ["i"], "values" => ["i" => 10, "s" => "dix"]]);
self::assertSame([
["i" => 1, "s" => "un"],
["i" => 2, "s" => "deux"],
["i" => 3, "s" => "trois"],
["i" => 4, "s" => "quatre"],
["i" => 5, "s" => null/*"cinq"*/],
["i" => 6, "s" => null/*"six"*/],
["i" => 7, "s" => null/*"sept"*/],
["i" => 8, "s" => null/*"huit"*/],
["i" => 9, "s" => "neuf"],
["i" => 10, "s" => null/*"dix"*/],
], iterator_to_array($sqlite->all("select * from mapping")));
}
function testSelect() {
$sqlite = new Sqlite(":memory:", [
"migrate" => "create table user (name varchar, amount integer)",
]);
$sqlite->exec(["insert into user", "values" => ["name" => "jclain1", "amount" => 1]]);
$sqlite->exec(["insert into user", "values" => ["name" => "jclain2", "amount" => 2]]);
$sqlite->exec(["insert into user", "values" => ["name" => "jclain5", "amount" => 5]]);
$sqlite->exec(["insert into user", "values" => ["name" => "fclain7", "amount" => 7]]);
$sqlite->exec(["insert into user", "values" => ["name" => "fclain9", "amount" => 9]]);
$sqlite->exec(["insert into user", "values" => ["name" => "fclain10", "amount" => 10]]);
self::assertSame([
"name" => "jclain1",
"amount" => 1,
], $sqlite->one("select * from user where name = 'jclain1'"));
self::assertSame([
"name" => "jclain1",
"amount" => 1,
], $sqlite->one(["select * from user where name = 'jclain1'"]));
self::assertSame([
"name" => "jclain1",
"amount" => 1,
], $sqlite->one(["select from user where name = 'jclain1'"]));
self::assertSame([
"name" => "jclain1",
"amount" => 1,
], $sqlite->one(["select from user where", "where" => ["name = 'jclain1'"]]));
self::assertSame([
"name" => "jclain1",
"amount" => 1,
], $sqlite->one(["select from user", "where" => ["name = 'jclain1'"]]));
self::assertSame([
"name" => "jclain1",
"amount" => 1,
], $sqlite->one(["select", "from" => "user", "where" => ["name = 'jclain1'"]]));
self::assertSame([
"name" => "jclain1",
"amount" => 1,
], $sqlite->one(["select", "from" => "user", "where" => ["name" => "jclain1"]]));
self::assertSame([
"name" => "jclain1",
], $sqlite->one(["select name", "from" => "user", "where" => ["name" => "jclain1"]]));
self::assertSame([
"name" => "jclain1",
], $sqlite->one(["select", "cols" => "name", "from" => "user", "where" => ["name" => "jclain1"]]));
self::assertSame([
"name" => "jclain1",
], $sqlite->one(["select", "cols" => ["name"], "from" => "user", "where" => ["name" => "jclain1"]]));
self::assertSame([
"plouf" => "jclain1",
], $sqlite->one(["select", "cols" => ["plouf" => "name"], "from" => "user", "where" => ["name" => "jclain1"]]));
}
function testSelectGroupBy() {
$sqlite = new Sqlite(":memory:", [
"migrate" => "create table user (name varchar, amount integer)",
]);
$sqlite->exec(["insert into user", "values" => ["name" => "jclain1", "amount" => 1]]);
$sqlite->exec(["insert into user", "values" => ["name" => "jclain2", "amount" => 1]]);
$sqlite->exec(["insert into user", "values" => ["name" => "jclain5", "amount" => 2]]);
$sqlite->exec(["insert into user", "values" => ["name" => "fclain7", "amount" => 2]]);
$sqlite->exec(["insert into user", "values" => ["name" => "fclain9", "amount" => 2]]);
$sqlite->exec(["insert into user", "values" => ["name" => "fclain10", "amount" => 3]]);
self::assertSame([
["count" => 2],
], iterator_to_array($sqlite->all(["select count(name) as count from user", "group by" => ["amount"], "having" => ["count(name) = 2"]])));
}
}

View File

@ -1,125 +0,0 @@
<?php
namespace nur\sery\db\sqlite;
use PHPUnit\Framework\TestCase;
class _queryTest extends TestCase {
function testParseConds(): void {
$sql = $params = null;
_query_base::parse_conds(null, $sql, $params);
self::assertNull($sql);
self::assertNull($params);
$sql = $params = null;
_query_base::parse_conds([], $sql, $params);
self::assertNull($sql);
self::assertNull($params);
$sql = $params = null;
_query_base::parse_conds(["col" => null], $sql, $params);
self::assertSame(["col is null"], $sql);
self::assertNull($params);
$sql = $params = null;
_query_base::parse_conds(["col = 'value'"], $sql, $params);
self::assertSame(["col = 'value'"], $sql);
self::assertNull($params);
$sql = $params = null;
_query_base::parse_conds([["col = 'value'"]], $sql, $params);
self::assertSame(["col = 'value'"], $sql);
self::assertNull($params);
$sql = $params = null;
_query_base::parse_conds(["int" => 42, "string" => "value"], $sql, $params);
self::assertSame(["(int = :int and string = :string)"], $sql);
self::assertSame(["int" => 42, "string" => "value"], $params);
$sql = $params = null;
_query_base::parse_conds(["or", "int" => 42, "string" => "value"], $sql, $params);
self::assertSame(["(int = :int or string = :string)"], $sql);
self::assertSame(["int" => 42, "string" => "value"], $params);
$sql = $params = null;
_query_base::parse_conds([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params);
self::assertSame(["((int = :int and string = :string) and (int = :int2 and string = :string2))"], $sql);
self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params);
$sql = $params = null;
_query_base::parse_conds(["int" => ["is null"], "string" => ["<>", "value"]], $sql, $params);
self::assertSame(["(int is null and string <> :string)"], $sql);
self::assertSame(["string" => "value"], $params);
$sql = $params = null;
_query_base::parse_conds(["col" => ["between", "lower", "upper"]], $sql, $params);
self::assertSame(["col between :col and :col2"], $sql);
self::assertSame(["col" => "lower", "col2" => "upper"], $params);
$sql = $params = null;
_query_base::parse_conds(["col" => ["in", "one"]], $sql, $params);
self::assertSame(["col in (:col)"], $sql);
self::assertSame(["col" => "one"], $params);
$sql = $params = null;
_query_base::parse_conds(["col" => ["in", ["one", "two"]]], $sql, $params);
self::assertSame(["col in (:col, :col2)"], $sql);
self::assertSame(["col" => "one", "col2" => "two"], $params);
$sql = $params = null;
_query_base::parse_conds(["col" => ["=", ["one", "two"]]], $sql, $params);
self::assertSame(["col = :col and col = :col2"], $sql);
self::assertSame(["col" => "one", "col2" => "two"], $params);
$sql = $params = null;
_query_base::parse_conds(["or", "col" => ["=", ["one", "two"]]], $sql, $params);
self::assertSame(["col = :col or col = :col2"], $sql);
self::assertSame(["col" => "one", "col2" => "two"], $params);
$sql = $params = null;
_query_base::parse_conds(["col" => ["<>", ["one", "two"]]], $sql, $params);
self::assertSame(["col <> :col and col <> :col2"], $sql);
self::assertSame(["col" => "one", "col2" => "two"], $params);
$sql = $params = null;
_query_base::parse_conds(["or", "col" => ["<>", ["one", "two"]]], $sql, $params);
self::assertSame(["col <> :col or col <> :col2"], $sql);
self::assertSame(["col" => "one", "col2" => "two"], $params);
}
function testParseValues(): void {
$sql = $params = null;
_query_base::parse_set_values(null, $sql, $params);
self::assertNull($sql);
self::assertNull($params);
$sql = $params = null;
_query_base::parse_set_values([], $sql, $params);
self::assertNull($sql);
self::assertNull($params);
$sql = $params = null;
_query_base::parse_set_values(["col = 'value'"], $sql, $params);
self::assertSame(["col = 'value'"], $sql);
self::assertNull($params);
$sql = $params = null;
_query_base::parse_set_values([["col = 'value'"]], $sql, $params);
self::assertSame(["col = 'value'"], $sql);
self::assertNull($params);
$sql = $params = null;
_query_base::parse_set_values(["int" => 42, "string" => "value"], $sql, $params);
self::assertSame(["int = :int", "string = :string"], $sql);
self::assertSame(["int" => 42, "string" => "value"], $params);
$sql = $params = null;
_query_base::parse_set_values(["int" => 42, "string" => "value"], $sql, $params);
self::assertSame(["int = :int", "string = :string"], $sql);
self::assertSame(["int" => 42, "string" => "value"], $params);
$sql = $params = null;
_query_base::parse_set_values([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params);
self::assertSame(["int = :int", "string = :string", "int = :int2", "string = :string2"], $sql);
self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params);
}
}

View File

@ -1,63 +0,0 @@
<?php
namespace nur\sery\file\base;
use nur\sery\file\FileReader;
use PHPUnit\Framework\TestCase;
class FileReaderTest extends TestCase {
function testIgnoreBom() {
# la lecture avec et sans BOM doit être identique
## sans BOM
$reader = new FileReader(__DIR__ . '/impl/sans_bom.txt');
self::assertSame("0123456789", $reader->fread(10));
self::assertSame(10, $reader->ftell());
$reader->seek(30);
self::assertSame("abcdefghij", $reader->fread(10));
self::assertSame(40, $reader->ftell());
$reader->seek(10);
self::assertSame("ABCDEFGHIJ", $reader->fread(10));
self::assertSame(20, $reader->ftell());
$reader->seek(40);
self::assertSame("0123456789\n", $reader->getContents());
$reader->close();
## avec BOM
$reader = new FileReader(__DIR__ . '/impl/avec_bom.txt');
self::assertSame("0123456789", $reader->fread(10));
self::assertSame(10, $reader->ftell());
$reader->seek(30);
self::assertSame("abcdefghij", $reader->fread(10));
self::assertSame(40, $reader->ftell());
$reader->seek(10);
self::assertSame("ABCDEFGHIJ", $reader->fread(10));
self::assertSame(20, $reader->ftell());
$reader->seek(40);
self::assertSame("0123456789\n", $reader->getContents());
$reader->close();
}
function testCsvAutoParams() {
$reader = new FileReader(__DIR__ . '/impl/msexcel.csv');
self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
self::assertNull($reader->fgetcsv());
$reader->close();
$reader = new FileReader(__DIR__ . '/impl/ooffice.csv');
self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
self::assertNull($reader->fgetcsv());
$reader->close();
$reader = new FileReader(__DIR__ . '/impl/weird.tsv');
self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
self::assertNull($reader->fgetcsv());
$reader->close();
$reader = new FileReader(__DIR__ . '/impl/avec_bom.csv');
self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
self::assertNull($reader->fgetcsv());
$reader->close();
}
}

View File

@ -1,2 +0,0 @@
nom,prenom,age
clain,jephte,50
1 nom prenom age
2 clain jephte 50

View File

@ -1 +0,0 @@
0123456789ABCDEFGHIJ0123456789abcdefghij0123456789

View File

@ -1,2 +0,0 @@
nom;prenom;age
clain;jephte;50
1 nom prenom age
2 clain jephte 50

View File

@ -1,2 +0,0 @@
nom,prenom,age
clain,jephte,50
1 nom prenom age
2 clain jephte 50

View File

@ -1 +0,0 @@
0123456789ABCDEFGHIJ0123456789abcdefghij0123456789

View File

@ -1,2 +0,0 @@
nom prenom age
clain jephte 50
1 nom prenom age
2 clain jephte 50

File diff suppressed because it is too large Load Diff

View File

@ -1,292 +0,0 @@
<?php
namespace {
function func36(): int { return 36; }
function func_m1($a): array { return [$a]; }
function func_o1($b=9): array { return [$b]; }
function func_v(...$c): array { return [...$c]; }
function func_m1o1($a, $b=9): array { return [$a, $b]; }
function func_m1v($a, ...$c): array { return [$a, ...$c]; }
function func_m1o1v($a, $b=9, ...$c): array { return [$a, $b, ...$c]; }
function func_o1v($b=9, ...$c): array { return [$b, ...$c]; }
}
namespace nur\sery\php {
use nulib\tests\TestCase;
class nur_funcTest extends TestCase {
function testIs_static() {
self::assertFalse(nur_func::is_static(null));
self::assertFalse(nur_func::is_static(""));
self::assertFalse(nur_func::is_static("::"));
self::assertFalse(nur_func::is_static("xxx::"));
self::assertFalse(nur_func::is_static([]));
self::assertFalse(nur_func::is_static([""]));
self::assertFalse(nur_func::is_static([null, ""]));
self::assertFalse(nur_func::is_static(["xxx", ""]));
self::assertTrue(nur_func::is_static("::xxx"));
self::assertTrue(nur_func::is_static(["xxx"]));
self::assertTrue(nur_func::is_static([null, "yyy"]));
self::assertTrue(nur_func::is_static(["xxx", "yyy"]));
self::assertTrue(nur_func::is_static([null, "yyy", "aaa"]));
self::assertTrue(nur_func::is_static(["xxx", "yyy", "aaa"]));
}
function testFix_static() {
$class = "class";
$func = "::xxx";
nur_func::fix_static($func, $class);
self::assertSame("class::xxx", $func);
$func = ["xxx"];
nur_func::fix_static($func, $class);
self::assertSame(["class", "xxx"], $func);
$func = [null, "yyy"];
nur_func::fix_static($func, $class);
self::assertSame(["class", "yyy"], $func);
$func = ["xxx", "yyy"];
nur_func::fix_static($func, $class);
self::assertSame(["xxx", "yyy"], $func);
$func = [null, "yyy", "aaa"];
nur_func::fix_static($func, $class);
self::assertSame(["class", "yyy", "aaa"], $func);
$func = ["xxx", "yyy", "aaa"];
nur_func::fix_static($func, $class);
self::assertSame(["xxx", "yyy", "aaa"], $func);
}
function testIs_method() {
self::assertFalse(nur_func::is_method(null));
self::assertFalse(nur_func::is_method(""));
self::assertFalse(nur_func::is_method("->"));
self::assertFalse(nur_func::is_method([]));
self::assertFalse(nur_func::is_method([""]));
self::assertFalse(nur_func::is_method([null, "->"]));
self::assertFalse(nur_func::is_method(["xxx", "->"]));
self::assertTrue(nur_func::is_method("->xxx"));
self::assertTrue(nur_func::is_method(["->xxx"]));
self::assertTrue(nur_func::is_method([null, "->yyy"]));
self::assertTrue(nur_func::is_method(["xxx", "->yyy"]));
self::assertTrue(nur_func::is_method([null, "->yyy", "aaa"]));
self::assertTrue(nur_func::is_method(["xxx", "->yyy", "aaa"]));
}
function testFix_method() {
$object = new \stdClass();
$func= "->xxx";
nur_func::fix_method($func, $object);
self::assertSame([$object, "xxx"], $func);
$func= ["->xxx"];
nur_func::fix_method($func, $object);
self::assertSame([$object, "xxx"], $func);
$func= [null, "->yyy"];
nur_func::fix_method($func, $object);
self::assertSame([$object, "yyy"], $func);
$func= ["xxx", "->yyy"];
nur_func::fix_method($func, $object);
self::assertSame([$object, "yyy"], $func);
$func= [null, "->yyy", "aaa"];
nur_func::fix_method($func, $object);
self::assertSame([$object, "yyy", "aaa"], $func);
$func= ["xxx", "->yyy", "aaa"];
nur_func::fix_method($func, $object);
self::assertSame([$object, "yyy", "aaa"], $func);
}
function testCall() {
self::assertSame(36, nur_func::call("func36"));
self::assertSame(12, nur_func::call(TC::class."::method"));
self::assertSame(12, nur_func::call([TC::class, "method"]));
$closure = function() {
return 21;
};
self::assertSame(21, nur_func::call($closure));
}
function test_prepare_fill() {
# vérifier que les arguments sont bien remplis, en fonction du fait qu'ils
# soient obligatoires, facultatifs ou variadiques
# m1
self::assertSame([null], nur_func::call("func_m1"));
self::assertSame([null], nur_func::call("func_m1", null));
self::assertSame([null], nur_func::call("func_m1", null, null));
self::assertSame([null], nur_func::call("func_m1", null, null, null));
self::assertSame([null], nur_func::call("func_m1", null, null, null, null));
self::assertSame([1], nur_func::call("func_m1", 1));
self::assertSame([1], nur_func::call("func_m1", 1, 2));
self::assertSame([1], nur_func::call("func_m1", 1, 2, 3));
self::assertSame([1], nur_func::call("func_m1", 1, 2, 3, 4));
# o1
self::assertSame([9], nur_func::call("func_o1"));
self::assertSame([null], nur_func::call("func_o1", null));
self::assertSame([null], nur_func::call("func_o1", null, null));
self::assertSame([null], nur_func::call("func_o1", null, null, null));
self::assertSame([null], nur_func::call("func_o1", null, null, null, null));
self::assertSame([1], nur_func::call("func_o1", 1));
self::assertSame([1], nur_func::call("func_o1", 1, 2));
self::assertSame([1], nur_func::call("func_o1", 1, 2, 3));
self::assertSame([1], nur_func::call("func_o1", 1, 2, 3, 4));
# v
self::assertSame([], nur_func::call("func_v"));
self::assertSame([null], nur_func::call("func_v", null));
self::assertSame([null, null], nur_func::call("func_v", null, null));
self::assertSame([null, null, null], nur_func::call("func_v", null, null, null));
self::assertSame([null, null, null, null], nur_func::call("func_v", null, null, null, null));
self::assertSame([1], nur_func::call("func_v", 1));
self::assertSame([1, 2], nur_func::call("func_v", 1, 2));
self::assertSame([1, 2, 3], nur_func::call("func_v", 1, 2, 3));
self::assertSame([1, 2, 3, 4], nur_func::call("func_v", 1, 2, 3, 4));
# m1o1
self::assertSame([null, 9], nur_func::call("func_m1o1"));
self::assertSame([null, 9], nur_func::call("func_m1o1", null));
self::assertSame([null, null], nur_func::call("func_m1o1", null, null));
self::assertSame([null, null], nur_func::call("func_m1o1", null, null, null));
self::assertSame([null, null], nur_func::call("func_m1o1", null, null, null, null));
self::assertSame([1, 9], nur_func::call("func_m1o1", 1));
self::assertSame([1, 2], nur_func::call("func_m1o1", 1, 2));
self::assertSame([1, 2], nur_func::call("func_m1o1", 1, 2, 3));
self::assertSame([1, 2], nur_func::call("func_m1o1", 1, 2, 3, 4));
# m1v
self::assertSame([null], nur_func::call("func_m1v"));
self::assertSame([null], nur_func::call("func_m1v", null));
self::assertSame([null, null], nur_func::call("func_m1v", null, null));
self::assertSame([null, null, null], nur_func::call("func_m1v", null, null, null));
self::assertSame([null, null, null, null], nur_func::call("func_m1v", null, null, null, null));
self::assertSame([1], nur_func::call("func_m1v", 1));
self::assertSame([1, 2], nur_func::call("func_m1v", 1, 2));
self::assertSame([1, 2, 3], nur_func::call("func_m1v", 1, 2, 3));
self::assertSame([1, 2, 3, 4], nur_func::call("func_m1v", 1, 2, 3, 4));
# m1o1v
self::assertSame([null, 9], nur_func::call("func_m1o1v"));
self::assertSame([null, 9], nur_func::call("func_m1o1v", null));
self::assertSame([null, null], nur_func::call("func_m1o1v", null, null));
self::assertSame([null, null, null], nur_func::call("func_m1o1v", null, null, null));
self::assertSame([null, null, null, null], nur_func::call("func_m1o1v", null, null, null, null));
self::assertSame([1, 9], nur_func::call("func_m1o1v", 1));
self::assertSame([1, 2], nur_func::call("func_m1o1v", 1, 2));
self::assertSame([1, 2, 3], nur_func::call("func_m1o1v", 1, 2, 3));
self::assertSame([1, 2, 3, 4], nur_func::call("func_m1o1v", 1, 2, 3, 4));
# o1v
self::assertSame([9], nur_func::call("func_o1v"));
self::assertSame([null], nur_func::call("func_o1v", null));
self::assertSame([null, null], nur_func::call("func_o1v", null, null));
self::assertSame([null, null, null], nur_func::call("func_o1v", null, null, null));
self::assertSame([null, null, null, null], nur_func::call("func_o1v", null, null, null, null));
self::assertSame([1], nur_func::call("func_o1v", 1));
self::assertSame([1, 2], nur_func::call("func_o1v", 1, 2));
self::assertSame([1, 2, 3], nur_func::call("func_o1v", 1, 2, 3));
self::assertSame([1, 2, 3, 4], nur_func::call("func_o1v", 1, 2, 3, 4));
}
function testCall_all() {
$c1 = new C1();
$c2 = new C2();
$c3 = new C3();
self::assertSameValues([11, 12], nur_func::call_all(C1::class));
self::assertSameValues([11, 12, 21, 22], nur_func::call_all($c1));
self::assertSameValues([13, 11, 12], nur_func::call_all(C2::class));
self::assertSameValues([13, 23, 11, 12, 21, 22], nur_func::call_all($c2));
self::assertSameValues([111, 13, 12], nur_func::call_all(C3::class));
self::assertSameValues([111, 121, 13, 23, 12, 22], nur_func::call_all($c3));
$options = "conf";
self::assertSameValues([11], nur_func::call_all(C1::class, $options));
self::assertSameValues([11, 21], nur_func::call_all($c1, $options));
self::assertSameValues([11], nur_func::call_all(C2::class, $options));
self::assertSameValues([11, 21], nur_func::call_all($c2, $options));
self::assertSameValues([111], nur_func::call_all(C3::class, $options));
self::assertSameValues([111, 121], nur_func::call_all($c3, $options));
$options = ["prefix" => "conf"];
self::assertSameValues([11], nur_func::call_all(C1::class, $options));
self::assertSameValues([11, 21], nur_func::call_all($c1, $options));
self::assertSameValues([11], nur_func::call_all(C2::class, $options));
self::assertSameValues([11, 21], nur_func::call_all($c2, $options));
self::assertSameValues([111], nur_func::call_all(C3::class, $options));
self::assertSameValues([111, 121], nur_func::call_all($c3, $options));
self::assertSameValues([11, 12], nur_func::call_all($c1, ["include" => "x"]));
self::assertSameValues([11, 21], nur_func::call_all($c1, ["include" => "y"]));
self::assertSameValues([11, 12, 21], nur_func::call_all($c1, ["include" => ["x", "y"]]));
self::assertSameValues([21, 22], nur_func::call_all($c1, ["exclude" => "x"]));
self::assertSameValues([12, 22], nur_func::call_all($c1, ["exclude" => "y"]));
self::assertSameValues([22], nur_func::call_all($c1, ["exclude" => ["x", "y"]]));
self::assertSameValues([12], nur_func::call_all($c1, ["include" => "x", "exclude" => "y"]));
}
function testCons() {
$obj1 = nur_func::cons(WoCons::class, 1, 2, 3);
self::assertInstanceOf(WoCons::class, $obj1);
$obj2 = nur_func::cons(WithEmptyCons::class, 1, 2, 3);
self::assertInstanceOf(WithEmptyCons::class, $obj2);
$obj3 = nur_func::cons(WithCons::class, 1, 2, 3);
self::assertInstanceOf(WithCons::class, $obj3);
self::assertSame(1, $obj3->first);
}
}
class WoCons {
}
class WithEmptyCons {
function __construct() {
}
}
class WithCons {
public $first;
function __construct($first) {
$this->first = $first;
}
}
class TC {
static function method() {
return 12;
}
}
class C1 {
static function confps1_xy() {
return 11;
}
static function ps2_x() {
return 12;
}
function confp1_y() {
return 21;
}
function p2() {
return 22;
}
}
class C2 extends C1 {
static function ps3() {
return 13;
}
function p3() {
return 23;
}
}
class C3 extends C2 {
static function confps1_xy() {
return 111;
}
function confp1_y() {
return 121;
}
}
}

View File

@ -1,85 +0,0 @@
<?php
namespace nur\sery\php\time;
use DateTimeZone;
use nulib\tests\TestCase;
class DateTest extends TestCase {
protected static function dt(string $datetime): Date {
return new Date($datetime, new DateTimeZone("Indian/Reunion"));
}
function testDate() {
$date = self::dt("2024-04-05 09:15:23");
self::assertSame("05/04/2024", $date->format());
self::assertSame("05/04/2024", strval($date));
self::assertSame(2024, $date->year);
self::assertSame(4, $date->month);
self::assertSame(5, $date->day);
self::assertSame(0, $date->hour);
self::assertSame(0, $date->minute);
self::assertSame(0, $date->second);
self::assertSame(5, $date->wday);
self::assertSame(14, $date->wnum);
self::assertSame("+04:00", $date->timezone);
self::assertSame("05/04/2024 00:00:00", $date->datetime);
self::assertSame("05/04/2024", $date->date);
}
function testClone() {
$date = self::dt("now");
$clone = Date::clone($date);
self::assertInstanceOf(DateTime::class, $clone);
}
function testConstruct() {
$y = date("Y");
self::assertSame("05/04/$y", strval(new Date("5/4")));
self::assertSame("05/04/2024", strval(new Date("5/4/24")));
self::assertSame("05/04/2024", strval(new Date("5/4/2024")));
self::assertSame("05/04/2024", strval(new Date("05/04/2024")));
self::assertSame("05/04/2024", strval(new Date("20240405")));
self::assertSame("05/04/2024", strval(new Date("240405")));
self::assertSame("05/04/2024", strval(new Date("20240405T091523")));
self::assertSame("05/04/2024", strval(new Date("20240405T091523Z")));
self::assertSame("05/04/2024", strval(new Date("5/4/2024 9:15:23")));
self::assertSame("05/04/2024", strval(new Date("5/4/2024 9.15.23")));
self::assertSame("05/04/2024", strval(new Date("5/4/2024 9:15")));
self::assertSame("05/04/2024", strval(new Date("5/4/2024 9.15")));
self::assertSame("05/04/2024", strval(new Date("5/4/2024 9h15")));
self::assertSame("05/04/2024", strval(new Date("5/4/2024 09:15:23")));
self::assertSame("05/04/2024", strval(new Date("5/4/2024 09:15")));
self::assertSame("05/04/2024", strval(new Date("5/4/2024 09h15")));
}
function testCompare() {
$a = new Date("10/02/2024");
$b = new Date("15/02/2024");
$c = new Date("20/02/2024");
$a2 = new Date("10/02/2024");
$b2 = new Date("15/02/2024");
$c2 = new Date("20/02/2024");
self::assertTrue($a == $a2);
self::assertFalse($a === $a2);
self::assertTrue($b == $b2);
self::assertTrue($c == $c2);
self::assertFalse($a < $a);
self::assertTrue($a < $b);
self::assertTrue($a < $c);
self::assertTrue($a <= $a);
self::assertTrue($a <= $b);
self::assertTrue($a <= $c);
self::assertFalse($c > $c);
self::assertTrue($c > $b);
self::assertTrue($c > $a);
self::assertTrue($c >= $c);
self::assertTrue($c >= $b);
self::assertTrue($c >= $a);
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace nur\sery\php\time;
use DateTimeZone;
use nulib\tests\TestCase;
class DateTimeTest extends TestCase {
protected static function dt(string $datetime): DateTime {
return new DateTime($datetime, new DateTimeZone("Indian/Reunion"));
}
function testDateTime() {
$date = self::dt("2024-04-05 09:15:23");
self::assertEquals("05/04/2024 09:15:23", $date->format());
self::assertEquals("05/04/2024 09:15:23", strval($date));
self::assertSame(2024, $date->year);
self::assertSame(4, $date->month);
self::assertSame(5, $date->day);
self::assertSame(9, $date->hour);
self::assertSame(15, $date->minute);
self::assertSame(23, $date->second);
self::assertSame(5, $date->wday);
self::assertSame(14, $date->wnum);
self::assertEquals("+04:00", $date->timezone);
self::assertSame("05/04/2024 09:15:23", $date->datetime);
self::assertSame("05/04/2024", $date->date);
self::assertSame("20240405", $date->Ymd);
self::assertSame("20240405T091523", $date->YmdHMS);
self::assertSame("20240405T091523+04:00", $date->YmdHMSZ);
}
function testDateTimeZ() {
$date = new DateTime("20240405T091523Z");
self::assertSame("20240405T091523", $date->YmdHMS);
self::assertSame("20240405T091523Z", $date->YmdHMSZ);
}
function testClone() {
$date = self::dt("now");
$clone = DateTime::clone($date);
self::assertInstanceOf(DateTime::class, $clone);
}
function testConstruct() {
$y = date("Y");
self::assertSame("05/04/$y 00:00:00", strval(new DateTime("5/4")));
self::assertSame("05/04/2024 00:00:00", strval(new DateTime("5/4/24")));
self::assertSame("05/04/2024 00:00:00", strval(new DateTime("5/4/2024")));
self::assertSame("05/04/2024 00:00:00", strval(new DateTime("05/04/2024")));
self::assertSame("05/04/2024 00:00:00", strval(new DateTime("20240405")));
self::assertSame("05/04/2024 00:00:00", strval(new DateTime("240405")));
self::assertSame("05/04/2024 09:15:23", strval(new DateTime("20240405T091523")));
self::assertSame("05/04/2024 09:15:23", strval(new DateTime("20240405T091523Z")));
self::assertSame("05/04/2024 09:15:23", strval(new DateTime("5/4/2024 9:15:23")));
self::assertSame("05/04/2024 09:15:23", strval(new DateTime("5/4/2024 9.15.23")));
self::assertSame("05/04/2024 09:15:00", strval(new DateTime("5/4/2024 9:15")));
self::assertSame("05/04/2024 09:15:00", strval(new DateTime("5/4/2024 9.15")));
self::assertSame("05/04/2024 09:15:00", strval(new DateTime("5/4/2024 9h15")));
self::assertSame("05/04/2024 09:15:23", strval(new DateTime("5/4/2024 09:15:23")));
self::assertSame("05/04/2024 09:15:00", strval(new DateTime("5/4/2024 09:15")));
self::assertSame("05/04/2024 09:15:00", strval(new DateTime("5/4/2024 09h15")));
}
function testCompare() {
$a = new DateTime("10/02/2024");
$a2 = new DateTime("10/02/2024 8:30");
$a3 = new DateTime("10/02/2024 15:45");
$b = new DateTime("15/02/2024");
$b2 = new DateTime("15/02/2024 8:30");
$b3 = new DateTime("15/02/2024 15:45");
$x = new DateTime("10/02/2024");
$x2 = new DateTime("10/02/2024 8:30");
$x3 = new DateTime("10/02/2024 15:45");
self::assertTrue($a == $x);
self::assertFalse($a === $x);
self::assertTrue($a2 == $x2);
self::assertTrue($a3 == $x3);
self::assertFalse($a < $a);
self::assertTrue($a < $a2);
self::assertTrue($a < $a3);
self::assertTrue($a < $b);
self::assertTrue($a < $b2);
self::assertTrue($a < $b3);
self::assertTrue($a <= $a);
self::assertTrue($a <= $a2);
self::assertTrue($a <= $a3);
self::assertTrue($a <= $b);
self::assertTrue($a <= $b2);
self::assertTrue($a <= $b3);
self::assertTrue($b > $a);
self::assertTrue($b > $a2);
self::assertTrue($b > $a3);
self::assertFalse($b > $b);
self::assertFalse($b > $b2);
self::assertFalse($b > $b3);
self::assertTrue($b >= $a);
self::assertTrue($b >= $a2);
self::assertTrue($b >= $a3);
self::assertTrue($b >= $b);
self::assertFalse($b >= $b2);
self::assertFalse($b >= $b3);
}
}

View File

@ -1,83 +0,0 @@
<?php
namespace nur\sery\php\time;
use DateTimeZone;
use nulib\tests\TestCase;
class DelayTest extends TestCase {
protected static function dt(string $datetime): DateTime {
return new DateTime($datetime, new DateTimeZone("Indian/Reunion"));
}
function testDelay() {
$from = self::dt("2024-04-05 09:15:23");
$delay = new Delay(10, $from);
self::assertEquals(self::dt("2024-04-05 09:15:33"), $delay->getDest());
$delay = new Delay("10", $from);
self::assertEquals(self::dt("2024-04-05 09:15:33"), $delay->getDest());
$delay = new Delay("10s", $from);
self::assertEquals(self::dt("2024-04-05 09:15:33"), $delay->getDest());
$delay = new Delay("s", $from);
self::assertEquals(self::dt("2024-04-05 09:15:24"), $delay->getDest());
$delay = new Delay("5m", $from);
self::assertEquals(self::dt("2024-04-05 09:20:00"), $delay->getDest());
$delay = new Delay("5m0", $from);
self::assertEquals(self::dt("2024-04-05 09:20:00"), $delay->getDest());
$delay = new Delay("5m2", $from);
self::assertEquals(self::dt("2024-04-05 09:20:02"), $delay->getDest());
$delay = new Delay("m", $from);
self::assertEquals(self::dt("2024-04-05 09:16:00"), $delay->getDest());
$delay = new Delay("5h", $from);
self::assertEquals(self::dt("2024-04-05 14:00:00"), $delay->getDest());
$delay = new Delay("5h0", $from);
self::assertEquals(self::dt("2024-04-05 14:00:00"), $delay->getDest());
$delay = new Delay("5h2", $from);
self::assertEquals(self::dt("2024-04-05 14:02:00"), $delay->getDest());
$delay = new Delay("h", $from);
self::assertEquals(self::dt("2024-04-05 10:00:00"), $delay->getDest());
$delay = new Delay("5d", $from);
self::assertEquals(self::dt("2024-04-10 05:00:00"), $delay->getDest());
$delay = new Delay("5d2", $from);
self::assertEquals(self::dt("2024-04-10 02:00:00"), $delay->getDest());
$delay = new Delay("5d0", $from);
self::assertEquals(self::dt("2024-04-10 00:00:00"), $delay->getDest());
$delay = new Delay("d", $from);
self::assertEquals(self::dt("2024-04-06 05:00:00"), $delay->getDest());
$delay = new Delay("2w", $from);
self::assertEquals(self::dt("2024-04-21 05:00:00"), $delay->getDest());
$delay = new Delay("2w2", $from);
self::assertEquals(self::dt("2024-04-21 02:00:00"), $delay->getDest());
$delay = new Delay("2w0", $from);
self::assertEquals(self::dt("2024-04-21 00:00:00"), $delay->getDest());
$delay = new Delay("w", $from);
self::assertEquals(self::dt("2024-04-07 05:00:00"), $delay->getDest());
}
function testElapsed() {
$delay = new Delay(5);
sleep(2);
self::assertFalse($delay->isElapsed());
sleep(5);
self::assertTrue($delay->isElapsed());
}
}

View File

@ -1,111 +0,0 @@
<?php
namespace nur\sery\schema\types;
use Exception;
use nulib\tests\TestCase;
use nur\sery\schema\_scalar\ScalarValue;
use nur\sery\schema\Schema;
class boolTest extends TestCase {
function commonTests($destv, &$dest, callable $destvSetter): void {
$destv->set(true);
self::assertSame(true, $destv->get());
self::assertSame(true, $dest);
self::assertSame("Oui", $destv->format());
self::assertSame("Oui", $destv->format("OuiNonNull"));
self::assertSame("O", $destv->format("ON"));
self::assertSame("O", $destv->format("ONN"));
$destv->set(false);
self::assertSame(false, $destv->get());
self::assertSame(false, $dest);
self::assertSame("Non", $destv->format());
self::assertSame("Non", $destv->format("OuiNonNull"));
self::assertSame("N", $destv->format("ON"));
self::assertSame("N", $destv->format("ONN"));
$destv->set("yes");
self::assertSame(true, $destv->get());
$destv->set(" yes ");
self::assertSame(true, $destv->get());
$destv->set("12");
self::assertSame(true, $destv->get());
$destv->set(12);
self::assertSame(true, $destv->get());
$destv->set("no");
self::assertSame(false, $destv->get());
$destv->set(" no ");
self::assertSame(false, $destv->get());
$destv->set("0");
self::assertSame(false, $destv->get());
$destv->set(0);
self::assertSame(false, $destv->get());
$destv->set(12.34);
self::assertSame(true, $destv->get());
self::assertException(Exception::class, $destvSetter("a"));
self::assertException(Exception::class, $destvSetter([]));
self::assertException(Exception::class, $destvSetter(["a"]));
}
function testBool() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, "bool");
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
self::assertException(Exception::class, $destvSetter(null));
self::assertException(Exception::class, $destvSetter(""));
self::assertException(Exception::class, $destvSetter(" "));
$this->commonTests($destv, $dest, $destvSetter);
}
function testNbool() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, "?bool");
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
$destv->set(null);
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("Non", $destv->format());
self::assertSame("", $destv->format("OuiNonNull"));
self::assertSame("N", $destv->format("ON"));
self::assertSame("", $destv->format("ONN"));
$destv->set("");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("Non", $destv->format());
self::assertSame("", $destv->format("OuiNonNull"));
self::assertSame("N", $destv->format("ON"));
self::assertSame("", $destv->format("ONN"));
$destv->set(" ");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("Non", $destv->format());
self::assertSame("", $destv->format("OuiNonNull"));
self::assertSame("N", $destv->format("ON"));
self::assertSame("", $destv->format("ONN"));
$this->commonTests($destv, $dest, $destvSetter);
}
}

View File

@ -1,139 +0,0 @@
<?php
namespace nur\sery\schema\types;
use Exception;
use nulib\tests\TestCase;
use nur\sery\schema\_scalar\ScalarValue;
use nur\sery\schema\Schema;
class floatTest extends TestCase {
function commonTests($destv, &$dest, callable $destvSetter): void {
$destv->set(12);
self::assertSame(12.0, $destv->get());
self::assertSame(12.0, $dest);
self::assertSame("12", $destv->format());
self::assertSame("0012", $destv->format("%04u"));
$destv->set("12");
self::assertSame(12.0, $destv->get());
$destv->set(" 12 ");
self::assertSame(12.0, $destv->get());
$destv->set(12.34);
self::assertSame(12.34, $destv->get());
$destv->set(true);
self::assertSame(1.0, $destv->get());
self::assertException(Exception::class, $destvSetter("a"));
self::assertException(Exception::class, $destvSetter([]));
self::assertException(Exception::class, $destvSetter(["a"]));
}
function testFloat() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, "float");
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
self::assertException(Exception::class, $destvSetter(null));
self::assertException(Exception::class, $destvSetter(""));
self::assertException(Exception::class, $destvSetter(" "));
// valeur non requise donc retourne null
$destv->set(false);
self::assertNull($destv->get());
$this->commonTests($destv, $dest, $destvSetter);
}
function testRequiredFloat() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, [
"float", null,
"required" => true,
]);
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
self::assertException(Exception::class, $destvSetter(null));
self::assertException(Exception::class, $destvSetter(""));
self::assertException(Exception::class, $destvSetter(" "));
// valeur requise donc lance une exception
self::assertException(Exception::class, $destvSetter(false));
$this->commonTests($destv, $dest, $destvSetter);
}
function testNfloat() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, "?float");
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
$destv->set(null);
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
$destv->set("");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
$destv->set(" ");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
// valeur non requise donc retourne null
$destv->set(false);
self::assertNull($destv->get());
$this->commonTests($destv, $dest, $destvSetter);
}
function testRequiredNfloat() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, [
"?float", null,
"required" => true,
]);
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
$destv->set(null);
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
$destv->set("");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
$destv->set(" ");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
// valeur requise donc lance une exception
self::assertException(Exception::class, $destvSetter(false));
$this->commonTests($destv, $dest, $destvSetter);
}
}

View File

@ -1,139 +0,0 @@
<?php
namespace nur\sery\schema\types;
use Exception;
use nulib\tests\TestCase;
use nur\sery\schema\_scalar\ScalarValue;
use nur\sery\schema\Schema;
class intTest extends TestCase {
function commonTests($destv, &$dest, callable $destvSetter): void {
$destv->set(12);
self::assertSame(12, $destv->get());
self::assertSame(12, $dest);
self::assertSame("12", $destv->format());
self::assertSame("0012", $destv->format("%04u"));
$destv->set("12");
self::assertSame(12, $destv->get());
$destv->set(" 12 ");
self::assertSame(12, $destv->get());
$destv->set(12.34);
self::assertSame(12, $destv->get());
$destv->set(true);
self::assertSame(1, $destv->get());
self::assertException(Exception::class, $destvSetter("a"));
self::assertException(Exception::class, $destvSetter([]));
self::assertException(Exception::class, $destvSetter(["a"]));
}
function testInt() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, "int");
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
self::assertException(Exception::class, $destvSetter(null));
self::assertException(Exception::class, $destvSetter(""));
self::assertException(Exception::class, $destvSetter(" "));
// valeur non requise donc retourne null
$destv->set(false);
self::assertNull($destv->get());
$this->commonTests($destv, $dest, $destvSetter);
}
function testRequiredInt() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, [
"int", null,
"required" => true,
]);
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
self::assertException(Exception::class, $destvSetter(null));
self::assertException(Exception::class, $destvSetter(""));
self::assertException(Exception::class, $destvSetter(" "));
// valeur requise donc lance une exception
self::assertException(Exception::class, $destvSetter(false));
$this->commonTests($destv, $dest, $destvSetter);
}
function testNint() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, "?int");
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
$destv->set(null);
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
$destv->set("");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
$destv->set(" ");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
// valeur non requise donc retourne null
$destv->set(false);
self::assertNull($destv->get());
$this->commonTests($destv, $dest, $destvSetter);
}
function testRequiredNint() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, [
"?int", null,
"required" => true,
]);
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
$destv->set(null);
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
$destv->set("");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
$destv->set(" ");
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
// valeur requise donc lance une exception
self::assertException(Exception::class, $destvSetter(false));
$this->commonTests($destv, $dest, $destvSetter);
}
}

View File

@ -1,123 +0,0 @@
<?php
namespace nur\sery\schema\types;
use Exception;
use nulib\tests\TestCase;
use nur\sery\wip\schema\_scalar\ScalarValue;
use nur\sery\wip\schema\Schema;
class strTest extends TestCase {
function commonTests($destv, &$dest, callable $destvSetter): void {
$destv->set("");
self::assertSame("", $destv->get());
self::assertSame("", $dest);
$destv->set(" ");
self::assertSame(" ", $destv->get());
self::assertSame(" ", $dest);
$destv->set("a");
self::assertSame("a", $destv->get());
self::assertSame("a", $dest);
$destv->set("12");
self::assertSame("12", $destv->get());
$destv->set(" 12 ");
self::assertSame(" 12 ", $destv->get());
$destv->set(12);
self::assertSame("12", $destv->get());
$destv->set(12.34);
self::assertSame("12.34", $destv->get());
$destv->set(true);
self::assertSame("1", $destv->get());
self::assertException(Exception::class, $destvSetter([]));
self::assertException(Exception::class, $destvSetter(["a"]));
}
function testStr() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, "string");
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
self::assertException(Exception::class, $destvSetter(null));
// valeur non requise donc retourne null
$destv->set(false);
self::assertNull($destv->get());
$this->commonTests($destv, $dest, $destvSetter);
}
function testRequiredStr() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, [
"string", null,
"required" => true,
]);
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
self::assertException(Exception::class, $destvSetter(null));
// valeur requise donc lance une exception
self::assertException(Exception::class, $destvSetter(false));
$this->commonTests($destv, $dest, $destvSetter);
}
function testNstr() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, "?string");
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
$destv->set(null);
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
// valeur non requise donc retourne null
$destv->set(false);
self::assertNull($destv->get());
$this->commonTests($destv, $dest, $destvSetter);
}
function testRequiredNstr() {
/** @var ScalarValue $destv */
Schema::nv($destv, $dest, null, $schema, [
"?string", null,
"required" => true,
]);
$destvSetter = function($value) use($destv) {
return function() use($destv, $value) {
$destv->set($value);
};
};
$destv->set(null);
self::assertNull($destv->get());
self::assertNull($dest);
self::assertSame("", $destv->format());
// valeur requise donc lance une exception
self::assertException(Exception::class, $destvSetter(false));
$this->commonTests($destv, $dest, $destvSetter);
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace nur\sery\schema\types;
use nulib\tests\TestCase;
use nur\sery\schema\_scalar\ScalarValue;
use nur\sery\schema\Schema;
class unionTest extends TestCase {
function testUnionTypes() {
## l'ordre des types doit être respecté
# string puis int
/** @var ScalarValue $siv */
Schema::nv($siv, $si, null, $sis, "string|int");
$siv->set("12");
self::assertSame("12", $si);
$siv->set(12);
self::assertSame(12, $si);
# int puis string
Schema::nv($isv, $is, null, $iss, "int|string");
$isv->set("12");
self::assertSame("12", $is);
$isv->set(12);
self::assertSame(12, $is);
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace nur\sery;
use nulib\tests\TestCase;
class strTest extends TestCase {
function testCamel2us() {
self::assertSame("a", str::camel2us("a"));
self::assertSame("aa", str::camel2us("aa"));
self::assertSame("aaa", str::camel2us("aaa"));
self::assertSame("a", str::camel2us("A"));
self::assertSame("aa", str::camel2us("Aa"));
self::assertSame("aa", str::camel2us("AA"));
self::assertSame("aaa", str::camel2us("Aaa"));
self::assertSame("aaa", str::camel2us("AAA"));
self::assertSame("a_aa", str::camel2us("AAa"));
self::assertSame("a_b", str::camel2us("aB"));
self::assertSame("aa_bb", str::camel2us("aaBb"));
self::assertSame("aaa_bbb", str::camel2us("aaaBbb"));
self::assertSame("aa_bb", str::camel2us("AaBb"));
self::assertSame("aaa_bbb", str::camel2us("AaaBbb"));
self::assertSame("_aaa", str::camel2us("_aaa"));
self::assertSame("__aaa_bbb", str::camel2us("__aaaBbb"));
self::assertSame("___aaa_bbb", str::camel2us("___AaaBbb"));
}
}

View File

@ -1,200 +0,0 @@
<?php
namespace nur\sery\web;
use PHPUnit\Framework\TestCase;
class uploadsTest extends TestCase {
const _FILES = [
# name="simple"
'simple' => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
# name=multiple[], name=multiple[]
'multiple' => [
'name' => [
0 => '',
1 => '',
],
'type' => [
0 => '',
1 => '',
],
'tmp_name' => [
0 => '',
1 => '',
],
'error' => [
0 => 4,
1 => 4,
],
'size' => [
0 => 0,
1 => 0,
],
],
# name=onelevel[a], name=onelevel[b]
'onelevel' => [
'name' => [
'a' => '',
'b' => '',
],
'type' => [
'a' => '',
'b' => '',
],
'tmp_name' => [
'a' => '',
'b' => '',
],
'error' => [
'a' => 4,
'b' => 4,
],
'size' => [
'a' => 0,
'b' => 0,
],
],
# name=multiplelevel[a][], name=multiplelevel[a][], name=multiplelevel[b][], name=multiplelevel[b][]
'multiplelevel' => [
'name' => [
'a' => [
0 => '',
1 => '',
],
'b' => [
0 => '',
1 => '',
],
],
'type' => [
'a' => [
0 => '',
1 => '',
],
'b' => [
0 => '',
1 => '',
],
],
'tmp_name' => [
'a' => [
0 => '',
1 => '',
],
'b' => [
0 => '',
1 => '',
],
],
'error' => [
'a' => [
0 => 4,
1 => 4,
],
'b' => [
0 => 4,
1 => 4,
],
],
'size' => [
'a' => [
0 => 0,
1 => 0,
],
'b' => [
0 => 0,
1 => 0,
],
],
],
];
const PARSED = [
# name="simple"
'simple' => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
# name=multiple[], name=multiple[]
'multiple' => [
0 => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
1 => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
],
# name=onelevel[a], name=onelevel[b]
'onelevel' => [
'a' => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
'b' => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
],
# name=multiplelevel[a][], name=multiplelevel[a][], name=multiplelevel[b][], name=multiplelevel[b][]
'multiplelevel' => [
'a' => [
0 => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
1 => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
],
'b' => [
0 => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
1 => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => 4,
'size' => 0,
],
],
],
];
function test_files() {
self::assertSame(self::PARSED, uploads::_files(self::_FILES));
}
}

View File

@ -1,8 +1,7 @@
<?php
namespace nur\sery\php\access;
namespace nur\sery\wip\php\access;
use nulib\tests\TestCase;
use nur\sery\wip\php\access\KeyAccess;
use stdClass;
class KeyAccessTest extends TestCase {

View File

@ -1,8 +1,7 @@
<?php
namespace nur\sery\php\access;
namespace nur\sery\wip\php\access;
use nulib\tests\TestCase;
use nur\sery\wip\php\access\ValueAccess;
use stdClass;
class ValueAccessTest extends TestCase {

View File

@ -13,6 +13,43 @@ class CursorTest extends TestCase {
msg::set_messenger(new StdMessenger());
}
function test_map_row() {
$cursor = new class extends Cursor {
function mapRow(array $row, ?array $map): array {
return self::map_row($row, $map);
}
};
$row = ["a" => 1, "b" => 2, "c" => 3, "x" => 99];
$map = ["a", "b" => "x", "c" => function() { return "y"; }, "d" => null];
self::assertSame([
"a" => $row["a"],
"b" => $row["x"],
"c" => "y",
"d" => null
], $cursor->mapRow($row, $map));
}
function test_filter_row() {
$cursor = new class extends Cursor {
function filterRow(array $row, $filter): bool {
return self::filter_row($row, $filter);
}
};
$row = ["a" => 1, "b" => 2, "c" => 3, "x" => 99];
self::assertTrue($cursor->filterRow($row, "a"));
self::assertTrue($cursor->filterRow($row, ["a"]));
self::assertTrue($cursor->filterRow($row, ["a" => true]));
self::assertFalse($cursor->filterRow($row, ["a" => false]));
self::assertTrue($cursor->filterRow($row, ["a" => 1]));
self::assertFalse($cursor->filterRow($row, ["a" => 2]));
self::assertFalse($cursor->filterRow($row, "z"));
self::assertFalse($cursor->filterRow($row, ["z"]));
self::assertFalse($cursor->filterRow($row, ["z" => true]));
self::assertTrue($cursor->filterRow($row, ["z" => false]));
self::assertFalse($cursor->filterRow($row, ["z" => 1]));
}
const SCALARS = [0, 1, 2, 3, 4];
function generator() {
@ -41,6 +78,15 @@ class CursorTest extends TestCase {
return cl::all($c);
});
$c = new Cursor(null, [
"rows" => $this->generator(),
]);
self::assertSame([[0], [1], [2], [3], [4]], cl::all($c));
self::assertException(Exception::class, function() use ($c) {
// pas possible de rewind un générateur
return cl::all($c);
});
$c = new Cursor(null, [
"rows_func" => function() {
return self::SCALARS;

View File

@ -1,7 +1,8 @@
<?php
namespace nur\sery\php\content;
namespace nur\sery\wip\php\content;
use nur\sery\php\content\impl\html;
use nulib\php\content\c;
use nur\sery\wip\php\content\impl\html;
use nur\sery\wip\web\content\v;
use PHPUnit\Framework\TestCase;

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\php\content\impl;
namespace nur\sery\wip\php\content\impl;
use nur\sery\php\content\IContent;
use nulib\php\content\IContent;
class AContent implements IContent {
function getContent(): iterable {

View File

@ -1,7 +1,7 @@
<?php
namespace nur\sery\php\content\impl;
namespace nur\sery\wip\php\content\impl;
use nur\sery\php\content\IPrintable;
use nulib\php\content\IPrintable;
class APrintable implements IPrintable {
function print(): void {

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\php\content\impl;
namespace nur\sery\wip\php\content\impl;
use nur\sery\php\content\c;
use nur\sery\php\content\IContent;
use nulib\php\content\c;
use nulib\php\content\IContent;
class ATag implements IContent {
function __construct(string $tag, $content=null) {

View File

@ -1,5 +1,5 @@
<?php
namespace nur\sery\php\content\impl;
namespace nur\sery\wip\php\content\impl;
class html {
const H1 = [self::class, "h1"];

View File

@ -0,0 +1,95 @@
<?php
namespace nur\sery\wip\schema\_assoc;
use nulib\ext\yaml;
use nulib\tests\TestCase;
use nur\sery\wip\schema\_scalar\ScalarSchemaTest;
class AssocSchemaTest extends TestCase {
const NULL_SCHEMA = [
"" => ["assoc"],
"schema" => null,
"type" => [null],
"default" => null,
"title" => null,
"required" => false,
"nullable" => true,
"desc" => null,
"analyzer_func" => null,
"extractor_func" => null,
"parser_func" => null,
"normalizer_func" => null,
"messages" => null,
"formatter_func" => null,
"format" => null,
"name" => null,
"pkey" => null,
"header" => null,
"composite" => null,
];
static function schema(array $definition, array $keyDefinitions): array {
$definition = array_merge(self::NULL_SCHEMA, $definition, ["schema" => []]);
foreach ($keyDefinitions as $key => $keydef) {
$definition["schema"][$key] = array_merge(ScalarSchemaTest::NULL_SCHEMA, $keydef);
}; unset($subdef);
return $definition;
}
function testNormalize() {
self::assertSame(self::schema([
"type" => ["array"], "nullable" => true,
], [
"a" => [
"type" => ["string"], "nullable" => false,
"name" => "a", "pkey" => "a", "header" => "a",
],
]), AssocSchema::normalize(["a" => "string"]));
self::assertSame(self::schema([
"type" => ["array"], "nullable" => true,
], [
"a" => [
"type" => ["string"], "nullable" => false,
"name" => "a", "pkey" => "a", "header" => "a",
],
"b" => [
"type" => ["int"], "nullable" => false,
"name" => "b", "pkey" => "b", "header" => "b",
],
"c" => [
"type" => ["bool"], "nullable" => false,
"name" => "c", "pkey" => "c", "header" => "c",
],
]), AssocSchema::normalize([
"a" => "string",
"b" => "int",
"c" => "bool",
]));
}
function testConstructor() {
$schema = new AssocSchema([
"a" => "string",
"b" => "int",
"c" => "bool",
]);
self::assertSame(self::schema([
"type" => ["array"], "nullable" => true,
], [
"a" => [
"type" => ["string"], "nullable" => false,
"name" => "a", "pkey" => "a", "header" => "a",
],
"b" => [
"type" => ["int"], "nullable" => false,
"name" => "b", "pkey" => "b", "header" => "b",
],
"c" => [
"type" => ["bool"], "nullable" => false,
"name" => "c", "pkey" => "c", "header" => "c",
],
]), $schema->getDefinition());
yaml::dump($schema->getDefinition());
}
}

View File

@ -1,8 +1,7 @@
<?php
namespace nur\sery\schema\_scalar;
namespace nur\sery\wip\schema\_scalar;
use nulib\tests\TestCase;
use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\SchemaException;
class ScalarSchemaTest extends TestCase {
@ -21,6 +20,7 @@ class ScalarSchemaTest extends TestCase {
"formatter_func" => null,
"format" => null,
"" => ["scalar"],
"schema" => null,
"name" => null,
"pkey" => null,
"header" => null,

View File

@ -0,0 +1,294 @@
<?php
namespace nur\sery\wip\schema\_scalar;
use nulib\tests\TestCase;
use nulib\ValueException;
use nur\sery\wip\schema\input\Input;
use stdClass;
class ScalarWrapperTest extends TestCase {
function checkValue(ScalarWrapper $wrapper, $value, bool $present, bool $available, bool $valid, bool $normalized): void {
self::assertSame($value, $wrapper->get(), "value");
self::assertSame($present, $wrapper->isPresent(), "present");
self::assertSame($available, $wrapper->isAvailable(), "available");
self::assertSame($valid, $wrapper->isValid(), "valid");
self::assertSame($normalized, $wrapper->isNormalized(), "normalized");
}
function checkVerifix(ScalarSchema $schema, $orig, bool $verifix, $value, bool $present, bool $available, bool $valid, bool $normalized, ?array $inputParams=null): void {
$wrapper = $schema->getWrapper();
if ($inputParams !== null) $input = new Input($orig, $inputParams);
else $input = $orig;
$wrapper->reset($input, null, $verifix);
$this->checkValue($wrapper, $value, $present, $available, $valid, $normalized);
}
function checkException(ScalarSchema $schema, $orig, bool $verifix, string $exceptionClass, ?array $inputParams=null) {
$wrapper = $schema->getWrapper();
if ($inputParams !== null) $orig = new Input($orig, $inputParams);
self::assertException($exceptionClass, function() use ($wrapper, &$orig, $verifix) {
$wrapper->reset($orig, null, $verifix);
});
}
function testRaw() {
$schema = new ScalarSchema();
$this->checkVerifix($schema, false, false, false, true, true, true, true);
$this->checkVerifix($schema, false, true, false, true, true, true, true);
$this->checkVerifix($schema, null, false, null, true, true, true, true);
$this->checkVerifix($schema, null, true, null, true, true, true, true);
$obj = new stdClass();
$this->checkVerifix($schema, $obj, false, $obj, true, true, true, true);
$this->checkVerifix($schema, $obj, true, $obj, true, true, true, true);
$schema = new ScalarSchema("raw");
$this->checkVerifix($schema, false, false, false, true, true, true, true);
$this->checkVerifix($schema, false, true, false, true, true, true, true);
$this->checkVerifix($schema, null, false, null, true, true, true, true);
$this->checkVerifix($schema, null, true, null, true, true, true, true);
$obj = new stdClass();
$this->checkVerifix($schema, $obj, false, $obj, true, true, true, true);
$this->checkVerifix($schema, $obj, true, $obj, true, true, true, true);
}
function testMixed() {
$schema = new ScalarSchema("mixed");
$this->checkVerifix($schema, false, false, false, true, true, true, true);
$this->checkVerifix($schema, false, true, false, true, true, true, true);
$this->checkVerifix($schema, null, false, null, true, true, false, false);
$this->checkException($schema, null, true, ValueException::class);
$obj = new stdClass();
$this->checkVerifix($schema, $obj, false, $obj, true, true, true, true);
$this->checkVerifix($schema, $obj, true, $obj, true, true, true, true);
$schema = new ScalarSchema("?mixed");
$this->checkVerifix($schema, false, false, false, true, true, true, true);
$this->checkVerifix($schema, false, true, false, true, true, true, true);
$this->checkVerifix($schema, null, false, null, true, true, true, true);
$this->checkVerifix($schema, null, true, null, true, true, true, true);
$obj = new stdClass();
$this->checkVerifix($schema, $obj, false, $obj, true, true, true, true);
$this->checkVerifix($schema, $obj, true, $obj, true, true, true, true);
}
function testRawstring() {
$schema = new ScalarSchema("rawstring");
$this->checkVerifix($schema, false, false, null, true, false, true, true);
$this->checkVerifix($schema, false, true, null, true, false, true, true);
$this->checkVerifix($schema, null, false, null, true, true, false, false);
$this->checkException($schema, null, true, ValueException::class);
$this->checkVerifix($schema, "", false, "", true, true, true, true);
$this->checkVerifix($schema, "", true, "", true, true, true, true);
$this->checkVerifix($schema, " ", false, " ", true, true, true, true);
$this->checkVerifix($schema, " ", true, " ", true, true, true, true);
$this->checkVerifix($schema, "text", false, "text", true, true, true, true);
$this->checkVerifix($schema, "text", true, "text", true, true, true, true);
$this->checkVerifix($schema, " text ", false, " text ", true, true, true, true);
$this->checkVerifix($schema, " text ", true, " text ", true, true, true, true);
$this->checkVerifix($schema, true, false, true, true, true, true, false);
$this->checkVerifix($schema, true, true, "1", true, true, true, false);
$this->checkVerifix($schema, 42, false, 42, true, true, true, false);
$this->checkVerifix($schema, 42, true, "42", true, true, true, false);
$this->checkVerifix($schema, [], false, [], true, true, false, false);
$this->checkException($schema, [], true, ValueException::class);
## Tester valeur par défaut
$schema = new ScalarSchema(["rawstring", null]);
$this->checkVerifix($schema, false, false, null, true, false, true, true);
$this->checkVerifix($schema, false, true, null, true, false, true, true);
$this->checkVerifix($schema, null, false, null, true, true, false, false);
$this->checkException($schema, null, true, ValueException::class);
$schema = new ScalarSchema(["rawstring", "default"]);
$this->checkVerifix($schema, false, false, "default", true, true, true, true);
$this->checkVerifix($schema, false, true, "default", true, true, true, true);
$this->checkVerifix($schema, null, false, null, true, true, false, false);
$this->checkException($schema, null, true, ValueException::class);
## Tester nullable
$schema = new ScalarSchema("?rawstring");
$this->checkVerifix($schema, null, false, null, true, true, true, true);
$this->checkVerifix($schema, null, true, null, true, true, true, true);
## Tester required
$schema = new ScalarSchema(["rawstring", "required" => true]);
$this->checkVerifix($schema, false, false, null, true, false, false, false);
$this->checkException($schema, false, true, ValueException::class);
## Tester allow_empty === false
$inputParams = ["allow_empty" => false];
$schema = new ScalarSchema("rawstring");
$this->checkVerifix($schema, null, false, null, true, true, false, false, $inputParams);
$this->checkException($schema, null, true, ValueException::class, $inputParams);
$this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams);
$this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams);
$schema = new ScalarSchema("?rawstring");
$this->checkVerifix($schema, null, false, null, true, true, true, true, $inputParams);
$this->checkVerifix($schema, null, true, null, true, true, true, true, $inputParams);
$this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams);
$this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams);
}
function testString() {
$schema = new ScalarSchema("string");
$this->checkVerifix($schema, false, false, null, true, false, true, true);
$this->checkVerifix($schema, false, true, null, true, false, true, true);
$this->checkVerifix($schema, null, false, null, true, true, false, false);
$this->checkException($schema, null, true, ValueException::class);
$this->checkVerifix($schema, "", false, "", true, true, true, true);
$this->checkVerifix($schema, "", true, "", true, true, true, true);
$this->checkVerifix($schema, " ", false, "", true, true, true, false);
$this->checkVerifix($schema, " ", true, "", true, true, true, false);
$this->checkVerifix($schema, "text", false, "text", true, true, true, true);
$this->checkVerifix($schema, "text", true, "text", true, true, true, true);
$this->checkVerifix($schema, " text ", false, "text", true, true, true, false);
$this->checkVerifix($schema, " text ", true, "text", true, true, true, false);
$this->checkVerifix($schema, true, false, true, true, true, true, false);
$this->checkVerifix($schema, true, true, "1", true, true, true, false);
$this->checkVerifix($schema, 42, false, 42, true, true, true, false);
$this->checkVerifix($schema, 42, true, "42", true, true, true, false);
$this->checkVerifix($schema, [], false, [], true, true, false, false);
$this->checkException($schema, [], true, ValueException::class);
## Tester nullable
$schema = new ScalarSchema("?string");
$this->checkVerifix($schema, null, false, null, true, true, true, true);
$this->checkVerifix($schema, null, true, null, true, true, true, true);
## Tester required
$schema = new ScalarSchema(["string", "required" => true]);
$this->checkVerifix($schema, false, false, null, true, false, false, false);
$this->checkException($schema, false, true, ValueException::class);
## Tester allow_empty === false
$inputParams = ["allow_empty" => false];
$schema = new ScalarSchema("string");
$this->checkVerifix($schema, null, false, null, true, true, false, false, $inputParams);
$this->checkException($schema, null, true, ValueException::class, $inputParams);
$this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams);
$this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams);
$schema = new ScalarSchema("?string");
$this->checkVerifix($schema, null, false, null, true, true, true, true, $inputParams);
$this->checkVerifix($schema, null, true, null, true, true, true, true, $inputParams);
$this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams);
$this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams);
}
function testInt() {
$schema = new ScalarSchema("int");
$this->checkVerifix($schema, false, false, null, true, false, true, true);
$this->checkVerifix($schema, false, true, null, true, false, true, true);
$this->checkVerifix($schema, null, false, null, true, true, false, false);
$this->checkException($schema, null, true, ValueException::class);
$this->checkVerifix($schema, 42, false, 42, true, true, true, true);
$this->checkVerifix($schema, 42, true, 42, true, true, true, true);
$this->checkVerifix($schema, "42", false, "42", true, true, true, false);
$this->checkVerifix($schema, "42", true, 42, true, true, true, false);
$this->checkVerifix($schema, "42.5", false, "42.5", true, true, true, false);
$this->checkVerifix($schema, "42.5", true, 42, true, true, true, false);
$this->checkVerifix($schema, "42,5", false, "42,5", true, true, true, false);
$this->checkVerifix($schema, "42,5", true, 42, true, true, true, false);
$this->checkVerifix($schema, " 42 ", false, "42", true, true, true, false);
$this->checkVerifix($schema, " 42 ", true, 42, true, true, true, false);
$this->checkVerifix($schema, "", false, "", true, true, false, false);
$this->checkException($schema, "", true, ValueException::class);
$this->checkVerifix($schema, " ", false, " ", true, true, false, false);
$this->checkException($schema, " ", true, ValueException::class);
$this->checkVerifix($schema, "text", false, "text", true, true, false, false);
$this->checkException($schema, "text", true, ValueException::class);
$this->checkVerifix($schema, true, false, true, true, true, true, false);
$this->checkVerifix($schema, true, true, 1, true, true, true, false);
$this->checkVerifix($schema, [], false, [], true, true, false, false);
$this->checkException($schema, [], true, ValueException::class);
## Tester nullable
$schema = new ScalarSchema("?int");
$this->checkVerifix($schema, null, false, null, true, true, true, true);
$this->checkVerifix($schema, null, true, null, true, true, true, true);
## Tester required
$schema = new ScalarSchema(["int", "required" => true]);
$this->checkVerifix($schema, false, false, null, true, false, false, false);
$this->checkException($schema, false, true, ValueException::class);
## Tester allow_empty === false
$inputParams = ["allow_empty" => false];
$schema = new ScalarSchema("int");
$this->checkVerifix($schema, null, false, null, true, true, false, false, $inputParams);
$this->checkException($schema, null, true, ValueException::class, $inputParams);
$this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams);
$this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams);
$schema = new ScalarSchema("?int");
$this->checkVerifix($schema, null, false, null, true, true, true, true, $inputParams);
$this->checkVerifix($schema, null, true, null, true, true, true, true, $inputParams);
$this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams);
$this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams);
}
}

View File

@ -0,0 +1,111 @@
<?php
namespace nur\sery\wip\schema\types;
use Exception;
use nulib\tests\TestCase;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\Schema;
class boolTest extends TestCase {
function commonTests($wrapper, &$value, callable $wrapperSetter): void {
$wrapper->set(true);
self::assertSame(true, $wrapper->get());
self::assertSame(true, $value);
self::assertSame("Oui", $wrapper->format());
self::assertSame("Oui", $wrapper->format("OuiNonNull"));
self::assertSame("O", $wrapper->format("ON"));
self::assertSame("O", $wrapper->format("ONN"));
$wrapper->set(false);
self::assertSame(false, $wrapper->get());
self::assertSame(false, $value);
self::assertSame("Non", $wrapper->format());
self::assertSame("Non", $wrapper->format("OuiNonNull"));
self::assertSame("N", $wrapper->format("ON"));
self::assertSame("N", $wrapper->format("ONN"));
$wrapper->set("yes");
self::assertSame(true, $wrapper->get());
$wrapper->set(" yes ");
self::assertSame(true, $wrapper->get());
$wrapper->set("12");
self::assertSame(true, $wrapper->get());
$wrapper->set(12);
self::assertSame(true, $wrapper->get());
$wrapper->set("no");
self::assertSame(false, $wrapper->get());
$wrapper->set(" no ");
self::assertSame(false, $wrapper->get());
$wrapper->set("0");
self::assertSame(false, $wrapper->get());
$wrapper->set(0);
self::assertSame(false, $wrapper->get());
$wrapper->set(12.34);
self::assertSame(true, $wrapper->get());
self::assertException(Exception::class, $wrapperSetter("a"));
self::assertException(Exception::class, $wrapperSetter([]));
self::assertException(Exception::class, $wrapperSetter(["a"]));
}
function testBool() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, "bool", $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
self::assertException(Exception::class, $wrapperSetter(null));
self::assertException(Exception::class, $wrapperSetter(""));
self::assertException(Exception::class, $wrapperSetter(" "));
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testNbool() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, "?bool", $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
$wrapper->set(null);
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("Non", $wrapper->format());
self::assertSame("", $wrapper->format("OuiNonNull"));
self::assertSame("N", $wrapper->format("ON"));
self::assertSame("", $wrapper->format("ONN"));
$wrapper->set("");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("Non", $wrapper->format());
self::assertSame("", $wrapper->format("OuiNonNull"));
self::assertSame("N", $wrapper->format("ON"));
self::assertSame("", $wrapper->format("ONN"));
$wrapper->set(" ");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("Non", $wrapper->format());
self::assertSame("", $wrapper->format("OuiNonNull"));
self::assertSame("N", $wrapper->format("ON"));
self::assertSame("", $wrapper->format("ONN"));
$this->commonTests($wrapper, $value, $wrapperSetter);
}
}

View File

@ -0,0 +1,139 @@
<?php
namespace nur\sery\wip\schema\types;
use Exception;
use nulib\tests\TestCase;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\Schema;
class floatTest extends TestCase {
function commonTests($wrapper, &$value, callable $wrapperSetter): void {
$wrapper->set(12);
self::assertSame(12.0, $wrapper->get());
self::assertSame(12.0, $value);
self::assertSame("12", $wrapper->format());
self::assertSame("0012", $wrapper->format("%04u"));
$wrapper->set("12");
self::assertSame(12.0, $wrapper->get());
$wrapper->set(" 12 ");
self::assertSame(12.0, $wrapper->get());
$wrapper->set(12.34);
self::assertSame(12.34, $wrapper->get());
$wrapper->set(true);
self::assertSame(1.0, $wrapper->get());
self::assertException(Exception::class, $wrapperSetter("a"));
self::assertException(Exception::class, $wrapperSetter([]));
self::assertException(Exception::class, $wrapperSetter(["a"]));
}
function testFloat() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, "float", $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
self::assertException(Exception::class, $wrapperSetter(null));
self::assertException(Exception::class, $wrapperSetter(""));
self::assertException(Exception::class, $wrapperSetter(" "));
// valeur non requise donc retourne null
$wrapper->set(false);
self::assertNull($wrapper->get());
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testRequiredFloat() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, [
"float", null,
"required" => true,
], $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
self::assertException(Exception::class, $wrapperSetter(null));
self::assertException(Exception::class, $wrapperSetter(""));
self::assertException(Exception::class, $wrapperSetter(" "));
// valeur requise donc lance une exception
self::assertException(Exception::class, $wrapperSetter(false));
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testNfloat() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, "?float", $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
$wrapper->set(null);
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
$wrapper->set("");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
$wrapper->set(" ");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
// valeur non requise donc retourne null
$wrapper->set(false);
self::assertNull($wrapper->get());
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testRequiredNfloat() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, [
"?float", null,
"required" => true,
], $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
$wrapper->set(null);
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
$wrapper->set("");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
$wrapper->set(" ");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
// valeur requise donc lance une exception
self::assertException(Exception::class, $wrapperSetter(false));
$this->commonTests($wrapper, $value, $wrapperSetter);
}
}

View File

@ -0,0 +1,139 @@
<?php
namespace nur\sery\wip\schema\types;
use Exception;
use nulib\tests\TestCase;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\Schema;
class intTest extends TestCase {
function commonTests($wrapper, &$value, callable $wrapperSetter): void {
$wrapper->set(12);
self::assertSame(12, $wrapper->get());
self::assertSame(12, $value);
self::assertSame("12", $wrapper->format());
self::assertSame("0012", $wrapper->format("%04u"));
$wrapper->set("12");
self::assertSame(12, $wrapper->get());
$wrapper->set(" 12 ");
self::assertSame(12, $wrapper->get());
$wrapper->set(12.34);
self::assertSame(12, $wrapper->get());
$wrapper->set(true);
self::assertSame(1, $wrapper->get());
self::assertException(Exception::class, $wrapperSetter("a"));
self::assertException(Exception::class, $wrapperSetter([]));
self::assertException(Exception::class, $wrapperSetter(["a"]));
}
function testInt() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, "int", $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
self::assertException(Exception::class, $wrapperSetter(null));
self::assertException(Exception::class, $wrapperSetter(""));
self::assertException(Exception::class, $wrapperSetter(" "));
// valeur non requise donc retourne null
$wrapper->set(false);
self::assertNull($wrapper->get());
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testRequiredInt() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, [
"int", null,
"required" => true,
], $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
self::assertException(Exception::class, $wrapperSetter(null));
self::assertException(Exception::class, $wrapperSetter(""));
self::assertException(Exception::class, $wrapperSetter(" "));
// valeur requise donc lance une exception
self::assertException(Exception::class, $wrapperSetter(false));
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testNint() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, "?int", $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
$wrapper->set(null);
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
$wrapper->set("");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
$wrapper->set(" ");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
// valeur non requise donc retourne null
$wrapper->set(false);
self::assertNull($wrapper->get());
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testRequiredNint() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, [
"?int", null,
"required" => true,
], $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
$wrapper->set(null);
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
$wrapper->set("");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
$wrapper->set(" ");
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
// valeur requise donc lance une exception
self::assertException(Exception::class, $wrapperSetter(false));
$this->commonTests($wrapper, $value, $wrapperSetter);
}
}

View File

@ -0,0 +1,123 @@
<?php
namespace nur\sery\wip\schema\types;
use Exception;
use nulib\tests\TestCase;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\Schema;
class strTest extends TestCase {
function commonTests($wrapper, &$value, callable $wrapperSetter): void {
$wrapper->set("");
self::assertSame("", $wrapper->get());
self::assertSame("", $value);
$wrapper->set(" ");
self::assertSame("", $wrapper->get());
self::assertSame("", $value);
$wrapper->set("a");
self::assertSame("a", $wrapper->get());
self::assertSame("a", $value);
$wrapper->set("12");
self::assertSame("12", $wrapper->get());
$wrapper->set(" 12 ");
self::assertSame("12", $wrapper->get());
$wrapper->set(12);
self::assertSame("12", $wrapper->get());
$wrapper->set(12.34);
self::assertSame("12.34", $wrapper->get());
$wrapper->set(true);
self::assertSame("1", $wrapper->get());
self::assertException(Exception::class, $wrapperSetter([]));
self::assertException(Exception::class, $wrapperSetter(["a"]));
}
function testStr() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, "string", $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
self::assertException(Exception::class, $wrapperSetter(null));
// valeur non requise donc retourne null
$wrapper->set(false);
self::assertNull($wrapper->get());
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testRequiredStr() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, [
"string", null,
"required" => true,
], $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
self::assertException(Exception::class, $wrapperSetter(null));
// valeur requise donc lance une exception
self::assertException(Exception::class, $wrapperSetter(false));
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testNstr() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, "?string", $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
$wrapper->set(null);
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
// valeur non requise donc retourne null
$wrapper->set(false);
self::assertNull($wrapper->get());
$this->commonTests($wrapper, $value, $wrapperSetter);
}
function testRequiredNstr() {
/** @var ScalarWrapper $wrapper */
Schema::nw($value, null, $schema, [
"?string", null,
"required" => true,
], $wrapper);
$wrapperSetter = function($value) use($wrapper) {
return function() use($wrapper, $value) {
$wrapper->set($value);
};
};
$wrapper->set(null);
self::assertNull($wrapper->get());
self::assertNull($value);
self::assertSame("", $wrapper->format());
// valeur requise donc lance une exception
self::assertException(Exception::class, $wrapperSetter(false));
$this->commonTests($wrapper, $value, $wrapperSetter);
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace nur\sery\wip\schema\types;
use nulib\tests\TestCase;
use nur\sery\wip\schema\_scalar\ScalarWrapper;
use nur\sery\wip\schema\Schema;
class unionTest extends TestCase {
function testUnionTypes() {
## l'ordre des types doit être respecté
# string puis int
/** @var ScalarWrapper $siw */
Schema::nw($si, null, $sis, "string|int", $siw);
$siw->set("12");
self::assertSame("12", $si);
$siw->set(12);
self::assertSame(12, $si);
# int puis string
/** @var ScalarWrapper $isw */
Schema::nw($is, null, $iss, "int|string", $isw);
$isw->set("12");
self::assertSame("12", $is);
$isw->set(12);
self::assertSame(12, $is);
}
}