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
* 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
résolution? --> NON, sauf si c'est un type union
* newInput dans Schema

View File

@ -6,6 +6,7 @@ use nulib\ref\schema\ref_schema;
use nulib\ref\schema\ref_types;
use nur\sery\wip\schema\Schema;
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\tbool;
use nur\sery\wip\schema\types\tcallable;
@ -103,7 +104,7 @@ class ScalarSchema extends Schema {
# type
$types = [];
$deftype = $definition["type"];
$nullable = $definition["nullable"];
$nullable = $definition["nullable"] ?? false;
if ($deftype === null) {
$types[] = null;
$nullable = true;
@ -164,6 +165,20 @@ class ScalarSchema extends Schema {
tarray::ensure_narray($definition["messages"]);
tcallable::ensure_ncallable($definition["formatter_func"]);
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;
}

View File

@ -71,57 +71,65 @@ class ScalarWrapper extends Wrapper {
/** analyser la valeur et résoudre son type */
function _analyze(AnalyzerContext $context): int {
$schema = $this->schema;
$input = $this->input;
$destKey = $this->destKey;
$result = $this->result;
/** @var ScalarSchema $schema */
$schema = $context->schema;
$input = $context->input;
$destKey = $context->destKey;
/** @var ScalarResult $result */
$result = $context->result;
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
$index = 0;
foreach ($schema->type as $key => $name) {
if ($key === $index) {
$index++;
$params = null;
} else {
$params = $name;
$name = $key;
}
$type = types::get($name, $params, $this->schema->getDefinition());
if ($firstType === null) $firstType = $type;
$types[] = $type;
if ($type->isAvailable($input, $destKey)) {
if (!$haveValue) {
$value = $input->get($destKey);
$haveValue = true;
$schemaTypes = $schema->type;
if (count($schemaTypes) == 1 && $schemaTypes[0] instanceof IType) {
$type = $schemaTypes[0];
} 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++;
$params = null;
} else {
$params = $name;
$name = $key;
}
if ($type->isValid($value, $normalized) && $normalized) {
$haveType = true;
$this->type = $type;
break;
$type = types::get($schema->nullable, $name, $params, $this->schema->getDefinition());
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;
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;
if (!$haveType) {
foreach ($types as $type) {
if ($type->isAvailable($input, $destKey) && $type->isValid($value)) {
$haveType = true;
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);
$value = $input->get($destKey);

View File

@ -25,16 +25,16 @@ class types {
return self::$registry;
}
static function get(string $name, ?array $params=null, ?array $definition=null): IType {
return self::registry()->get($name, $params, $definition);
static function get(bool $nullable, string $name, ?array $params=null, ?array $definition=null): IType {
return self::registry()->get($nullable, $name, $params, $definition);
}
static function rawstring(): trawstring { return self::get("rawstring"); }
static function string(): tstring { return self::get("string"); }
static function text(): ttext { return self::get("text"); }
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"); }
}

View File

@ -10,6 +10,54 @@ use nur\sery\wip\schema\Schema;
* Interface IType: un type de données
*/
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? */
function isAvailable(Input $input, $destKey): bool;
@ -54,4 +102,29 @@ interface IType {
* 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

@ -28,14 +28,15 @@ class Registry {
/** @var IType[] */
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];
$params = cl::merge($class::get_params_from_definition($definition), $params);
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);
if ($type === null) $type = $this->types[$name] = new $class();
if ($type === null) $type = $this->types[$name] = new $class($nullable);
return $type;
}
}

View File

@ -1,19 +1,44 @@
<?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 {
static function get_params_from_definition(?array $definition): ?array {
return null;
}
function __construct(?array $params=null) {
function __construct(bool $nullable, ?array $params=null) {
$this->nullable = $nullable;
$this->params = $params;
}
protected bool $nullable;
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 {
return $input->isAvailable($destKey) && $input->get($destKey) !== false;
}
@ -25,4 +50,34 @@ abstract class _tsimple implements IType {
function extract(string $value): string {
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);
}
function getClass(): string {
return "array";
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = 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\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;
@ -51,6 +52,26 @@ class tbool extends _tformatable {
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 {
return $input->isAvailable($destKey);
}
@ -124,4 +145,8 @@ class tbool extends _tformatable {
}
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);
}
function getClass(): string {
return func::class;
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_callable($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\Schema;
abstract class tcontent extends _tsimple {
abstract class tcontent extends _tunion {
static function ensure_content(&$content): void {
if ($content === null || $content === false) $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);
}
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);

View File

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

View File

@ -17,7 +17,11 @@ class tint extends _tformatable {
}
//const INT_PATTERN = '/^[-+]?[0-9]+(?:\.[0-9]*)?$/';
function getClass(): string {
return "int";
}
function isValid($value, ?bool &$normalized=null): bool {
$normalized = is_int($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\Schema;
class tkey extends _tsimple {
class tkey extends _tunion {
static function ensure_key(&$key): void {
if ($key === null) $key = "";
elseif ($key === false) $key = 0;
@ -17,6 +17,10 @@ class tkey extends _tsimple {
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);

View File

@ -6,7 +6,7 @@ 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 {
static function ensure_pkey(&$pkey): void {
if ($pkey === null) $pkey = "";
elseif ($pkey === false) $pkey = 0;
@ -22,6 +22,10 @@ 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);

View File

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