modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2025-03-06 12:27:59 +04:00
parent a705dd61c4
commit e1b4ef4cc3
17 changed files with 280 additions and 59 deletions

View File

@ -1,5 +1,7 @@
# nulib\schema # nulib\schema
* tenir compte de la valeur par défaut, qui *doit* être du bon type
* type vaut soit une instance de IType, soit une tableau de types
* dans la définition, `[type]` est remplacé par l'instance de IType lors de sa * dans la définition, `[type]` est remplacé par l'instance de IType lors de sa
résolution? --> NON, sauf si c'est un type union résolution? --> NON, sauf si c'est un type union
* newInput dans Schema * newInput dans Schema

View File

@ -6,6 +6,7 @@ use nulib\ref\schema\ref_schema;
use nulib\ref\schema\ref_types; use nulib\ref\schema\ref_types;
use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\Schema;
use nur\sery\wip\schema\SchemaException; use nur\sery\wip\schema\SchemaException;
use nur\sery\wip\schema\types;
use nur\sery\wip\schema\types\tarray; use nur\sery\wip\schema\types\tarray;
use nur\sery\wip\schema\types\tbool; use nur\sery\wip\schema\types\tbool;
use nur\sery\wip\schema\types\tcallable; use nur\sery\wip\schema\types\tcallable;
@ -103,7 +104,7 @@ class ScalarSchema extends Schema {
# type # type
$types = []; $types = [];
$deftype = $definition["type"]; $deftype = $definition["type"];
$nullable = $definition["nullable"]; $nullable = $definition["nullable"] ?? false;
if ($deftype === null) { if ($deftype === null) {
$types[] = null; $types[] = null;
$nullable = true; $nullable = true;
@ -164,6 +165,20 @@ class ScalarSchema extends Schema {
tarray::ensure_narray($definition["messages"]); tarray::ensure_narray($definition["messages"]);
tcallable::ensure_ncallable($definition["formatter_func"]); tcallable::ensure_ncallable($definition["formatter_func"]);
tbool::ensure_nbool($definition["composite"]); tbool::ensure_nbool($definition["composite"]);
# s'il n'y a qu'une seul type, l'instancier tout de suite
if (count($types) == 1 && $types[0] !== null) {
foreach ($types as $key => $name) {
if ($key === 0) {
$params = null;
} else {
$params = $name;
$name = $key;
}
$definition["type"] = [types::get($nullable, $name, $params, $definition)];
}
}
return $definition; return $definition;
} }

View File

@ -71,57 +71,65 @@ class ScalarWrapper extends Wrapper {
/** analyser la valeur et résoudre son type */ /** analyser la valeur et résoudre son type */
function _analyze(AnalyzerContext $context): int { function _analyze(AnalyzerContext $context): int {
$schema = $this->schema; /** @var ScalarSchema $schema */
$input = $this->input; $schema = $context->schema;
$destKey = $this->destKey; $input = $context->input;
$result = $this->result; $destKey = $context->destKey;
/** @var ScalarResult $result */
$result = $context->result;
if (!$input->isPresent($destKey)) return $result->setMissing($schema); if (!$input->isPresent($destKey)) return $result->setMissing($schema);
$haveType = false; $schemaTypes = $schema->type;
$types = []; if (count($schemaTypes) == 1 && $schemaTypes[0] instanceof IType) {
$type = $firstType = null; $type = $schemaTypes[0];
$haveValue = false; } else {
$value = null; # type union
# d'abord chercher un type pour lequel c'est une valeur normalisée $haveType = false;
$index = 0; $types = [];
foreach ($schema->type as $key => $name) { $type = $firstType = null;
if ($key === $index) { $value = null;
$index++; # d'abord chercher un type pour lequel c'est une valeur normalisée
$params = null; $index = 0;
} else { $haveValue = false;
$params = $name; foreach ($schemaTypes as $key => $name) {
$name = $key; if ($key === $index) {
} $index++;
$type = types::get($name, $params, $this->schema->getDefinition()); $params = null;
if ($firstType === null) $firstType = $type; } else {
$types[] = $type; $params = $name;
if ($type->isAvailable($input, $destKey)) { $name = $key;
if (!$haveValue) {
$value = $input->get($destKey);
$haveValue = true;
} }
if ($type->isValid($value, $normalized) && $normalized) { $type = types::get($schema->nullable, $name, $params, $this->schema->getDefinition());
$haveType = true; if ($firstType === null) $firstType = $type;
$this->type = $type; $types[] = $type;
break; if ($type->isAvailable($input, $destKey)) {
if (!$haveValue) {
$value = $input->get($destKey);
$haveValue = true;
}
if ($type->isValid($value, $normalized) && $normalized) {
$haveType = true;
break;
}
} }
} }
}
if (!$haveType) {
# ensuite chercher un type pour lequel la valeur est valide # ensuite chercher un type pour lequel la valeur est valide
foreach ($types as $type) { if (!$haveType) {
if ($type->isAvailable($input, $destKey) && $type->isValid($value)) { foreach ($types as $type) {
$haveType = true; if ($type->isAvailable($input, $destKey) && $type->isValid($value)) {
$this->type = $type; $haveType = true;
break; break;
}
} }
} }
# sinon prendre le premier type
if (!$haveType) {
$type = $firstType;
}
} }
$context->type = $this->type = $type;
# sinon prendre le premier type
if (!$haveType) $type = $this->type = $firstType;
$context->type = $type;
if (!$type->isAvailable($input, $destKey)) return $result->setUnavailable($schema); if (!$type->isAvailable($input, $destKey)) return $result->setUnavailable($schema);
$value = $input->get($destKey); $value = $input->get($destKey);

View File

@ -25,16 +25,16 @@ class types {
return self::$registry; return self::$registry;
} }
static function get(string $name, ?array $params=null, ?array $definition=null): IType { static function get(bool $nullable, string $name, ?array $params=null, ?array $definition=null): IType {
return self::registry()->get($name, $params, $definition); return self::registry()->get($nullable, $name, $params, $definition);
} }
static function rawstring(): trawstring { return self::get("rawstring"); } static function rawstring(bool $nullable=true): trawstring { return self::get($nullable, "rawstring"); }
static function string(): tstring { return self::get("string"); } static function string(bool $nullable=true): tstring { return self::get($nullable, "string"); }
static function text(): ttext { return self::get("text"); } static function text(bool $nullable=true): ttext { return self::get($nullable, "text"); }
static function bool(): tbool { return self::get("bool"); } static function bool(bool $nullable=true): tbool { return self::get($nullable, "bool"); }
static function int(): tint { return self::get("int"); } static function int(bool $nullable=true): tint { return self::get($nullable, "int"); }
static function float(): tfloat { return self::get("float"); } static function float(bool $nullable=true): tfloat { return self::get($nullable, "float"); }
static function array(): tarray { return self::get("array"); } static function array(bool $nullable=true): tarray { return self::get($nullable, "array"); }
static function callable(): tcallable { return self::get("callable"); } static function callable(bool $nullable=true): tcallable { return self::get($nullable, "callable"); }
} }

View File

@ -10,6 +10,54 @@ use nur\sery\wip\schema\Schema;
* Interface IType: un type de données * Interface IType: un type de données
*/ */
interface IType { interface IType {
/**
* @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($destKey) est-elle disponible? */ /** la donnée $input($destKey) est-elle disponible? */
function isAvailable(Input $input, $destKey): bool; function isAvailable(Input $input, $destKey): bool;
@ -54,4 +102,29 @@ interface IType {
* garantie d'être du bon type * garantie d'être du bon type
*/ */
function format($value, $format=null): string; 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

@ -28,14 +28,15 @@ class Registry {
/** @var IType[] */ /** @var IType[] */
protected $types; protected $types;
function get(string $name, ?array $params=null, ?array $definition=null): IType { function get(bool $nullable, string $name, ?array $params=null, ?array $definition=null): IType {
$class = self::TYPES[$name]; $class = self::TYPES[$name];
$params = cl::merge($class::get_params_from_definition($definition), $params); $params = cl::merge($class::get_params_from_definition($definition), $params);
if ($params !== null) { if ($params !== null) {
return func::with([$class, false, $params])->invoke(); return func::with([$class, false, $nullable, $params])->invoke();
} }
if ($nullable) $name = "?$name";
$type = cl::get($this->types, $name); $type = cl::get($this->types, $name);
if ($type === null) $type = $this->types[$name] = new $class(); if ($type === null) $type = $this->types[$name] = new $class($nullable);
return $type; return $type;
} }
} }

View File

@ -1,19 +1,44 @@
<?php <?php
namespace nur\sery\wip\schema\types; namespace nur\sery\wip\schema\types;
use nulib\StateException;
use nur\prop;
use nur\sery\wip\schema\input\Input; use nur\sery\wip\schema\input\Input;
use nur\str;
abstract class _tsimple implements IType { abstract class _tsimple implements IType {
static function get_params_from_definition(?array $definition): ?array { static function get_params_from_definition(?array $definition): ?array {
return null; return null;
} }
function __construct(?array $params=null) { function __construct(bool $nullable, ?array $params=null) {
$this->nullable = $nullable;
$this->params = $params; $this->params = $params;
} }
protected bool $nullable;
protected ?array $params; protected ?array $params;
function getPhpType(bool $allowNullable=true): ?string {
$phpType = $this->getClass();
if ($phpType === "mixed") return null;
if ($this->nullable && $allowNullable) $phpType = "?$phpType";
return $phpType;
}
function get2States(): array {
throw StateException::not_implemented();
}
function is3States(): bool {
return false;
}
function get3States(): array {
throw StateException::not_implemented();
}
function isAvailable(Input $input, $destKey): bool { function isAvailable(Input $input, $destKey): bool {
return $input->isAvailable($destKey) && $input->get($destKey) !== false; return $input->isAvailable($destKey) && $input->get($destKey) !== false;
} }
@ -25,4 +50,34 @@ abstract class _tsimple implements IType {
function extract(string $value): string { function extract(string $value): string {
return $value; return $value;
} }
function is2States(): bool {
return false;
}
#############################################################################
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,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

@ -29,6 +29,10 @@ class tarray extends _tstring {
if ($array !== null) self::ensure_array($array); if ($array !== null) self::ensure_array($array);
} }
function getClass(): string {
return "array";
}
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_array($value); $normalized = is_array($value);
return is_scalar($value) || is_array($value); return is_scalar($value) || is_array($value);

View File

@ -3,6 +3,7 @@ namespace nur\sery\wip\schema\types;
use nulib\cl; use nulib\cl;
use nulib\ValueException; use nulib\ValueException;
use nur\prop;
use nur\sery\wip\schema\_scalar\ScalarResult; use nur\sery\wip\schema\_scalar\ScalarResult;
use nur\sery\wip\schema\_scalar\ScalarSchema; use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\input\Input; use nur\sery\wip\schema\input\Input;
@ -51,6 +52,26 @@ class tbool extends _tformatable {
if ($bool !== null) self::ensure_bool($bool); if ($bool !== null) self::ensure_bool($bool);
} }
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, $destKey): bool { function isAvailable(Input $input, $destKey): bool {
return $input->isAvailable($destKey); return $input->isAvailable($destKey);
} }
@ -124,4 +145,8 @@ class tbool extends _tformatable {
} }
return $value? $format[0]: $format[1]; return $value? $format[0]: $format[1];
} }
function getGetterName(string $name): string {
return prop::get_getter_name($name, !$this->nullable);
}
} }

View File

@ -18,6 +18,10 @@ class tcallable extends _tsimple {
if ($callable !== null) self::ensure_callable($callable); if ($callable !== null) self::ensure_callable($callable);
} }
function getClass(): string {
return func::class;
}
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_callable($value); $normalized = is_callable($value);
return func::check($value); return func::check($value);

View File

@ -7,7 +7,7 @@ use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result; use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\Schema;
abstract class tcontent extends _tsimple { abstract class tcontent extends _tunion {
static function ensure_content(&$content): void { static function ensure_content(&$content): void {
if ($content === null || $content === false) $content = []; if ($content === null || $content === false) $content = [];
elseif (!is_string($content) && !is_array($content)) $content = strval($content); elseif (!is_string($content) && !is_array($content)) $content = strval($content);
@ -17,6 +17,10 @@ abstract class tcontent extends _tsimple {
if ($content !== null) self::ensure_content($content); if ($content !== null) self::ensure_content($content);
} }
function getClass(): string {
return "string|array";
}
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value) || is_array($value); $normalized = is_string($value) || is_array($value);
return is_scalar($value) || is_array($value); return is_scalar($value) || is_array($value);

View File

@ -16,6 +16,10 @@ class tfloat extends _tformatable {
if ($float !== null) self::ensure_float($float); if ($float !== null) self::ensure_float($float);
} }
function getClass(): string {
return "float";
}
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_float($value); $normalized = is_float($value);
return is_scalar($value); return is_scalar($value);

View File

@ -17,7 +17,11 @@ class tint extends _tformatable {
} }
//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 { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_int($value); $normalized = is_int($value);
return is_scalar($value); return is_scalar($value);

View File

@ -6,7 +6,7 @@ use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result; use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\Schema;
class tkey extends _tsimple { class tkey extends _tunion {
static function ensure_key(&$key): void { static function ensure_key(&$key): void {
if ($key === null) $key = ""; if ($key === null) $key = "";
elseif ($key === false) $key = 0; elseif ($key === false) $key = 0;
@ -17,6 +17,10 @@ class tkey extends _tsimple {
if ($key !== null) self::ensure_key($key); if ($key !== null) self::ensure_key($key);
} }
function getClass(): string {
return "string|int";
}
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value) || is_int($value); $normalized = is_string($value) || is_int($value);
return is_scalar($value); return is_scalar($value);

View File

@ -6,7 +6,7 @@ use nur\sery\wip\schema\_scalar\ScalarSchema;
use nur\sery\wip\schema\Result; use nur\sery\wip\schema\Result;
use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\Schema;
class tpkey extends _tsimple { class tpkey extends _tunion {
static function ensure_pkey(&$pkey): void { static function ensure_pkey(&$pkey): void {
if ($pkey === null) $pkey = ""; if ($pkey === null) $pkey = "";
elseif ($pkey === false) $pkey = 0; elseif ($pkey === false) $pkey = 0;
@ -22,6 +22,10 @@ class tpkey extends _tsimple {
if ($pkey !== null) self::ensure_pkey($pkey); if ($pkey !== null) self::ensure_pkey($pkey);
} }
function getClass(): string {
return "string|int|array";
}
function isValid($value, ?bool &$normalized=null): bool { function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_string($value) || is_int($value) || is_array($value); $normalized = is_string($value) || is_int($value) || is_array($value);
return is_scalar($value) || is_array($value); return is_scalar($value) || is_array($value);

View File

@ -18,6 +18,10 @@ class trawstring extends _tstring {
if ($string !== null) self::ensure_string($string); if ($string !== null) self::ensure_string($string);
} }
function getClass(): string {
return "string";
}
function isNull($value): bool { function isNull($value): bool {
return $value === null; return $value === null;
} }