From 189c7aba6832fac661a2741cb67c234dbdcbf056 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Tue, 18 Mar 2025 13:30:02 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- src/schema/Schema.php | 7 ++ src/schema/TODO.md | 7 +- src/schema/Wrapper.php | 17 +++ src/schema/WrapperContext.php | 16 ++- src/schema/_assoc/AssocResult.php | 33 +++--- src/schema/_assoc/AssocSchema.php | 25 ++++- src/schema/_assoc/AssocWrapper.php | 113 ++++++++++---------- src/schema/_assoc/AssocWrapperContext.php | 21 ++++ src/schema/_list/ListSchema.php | 17 ++- src/schema/_scalar/ScalarResult.php | 5 +- src/schema/_scalar/ScalarSchema.php | 12 +++ src/schema/_scalar/ScalarWrapper.php | 59 ++++------ tests/wip/schema/_assoc/AssocSchemaTest.php | 5 +- 13 files changed, 206 insertions(+), 131 deletions(-) create mode 100644 src/schema/_assoc/AssocWrapperContext.php diff --git a/src/schema/Schema.php b/src/schema/Schema.php index 55f1304..a7721f4 100644 --- a/src/schema/Schema.php +++ b/src/schema/Schema.php @@ -269,6 +269,13 @@ abstract class Schema implements ArrayAccess { return $this->_definition; } + /** + * retourner la liste des clés valides pour l'accès aux valeurs et résultats + */ + abstract function getKeys(): array; + + abstract function getSchema($key): Schema; + /** retourner true si le schéma est de nature tableau associatif */ function isAssoc(?AssocSchema &$schema=null): bool { return false; } /** retourner true si le schéma est de nature liste */ diff --git a/src/schema/TODO.md b/src/schema/TODO.md index 46e80ce..73b4aca 100644 --- a/src/schema/TODO.md +++ b/src/schema/TODO.md @@ -1,15 +1,12 @@ # nulib\schema -* plus de {key} ni {orig} dans messages - * les messages standard ne sont utilisés que s'il n'y a pas de message dans - l'exception - * si instance de UserException, prendre le message "non technique" pour - résultat * valeurs composite/computed * analyse / vérification de la valeur complète après calcul du résultat, si tous les résultats sont bons * calcul des valeurs composites/computed par une fonction avant/après l'analyse globale si résultat ok + * fonction getter_func, setter_func, deleter_func pour les propriétés de type + computed * 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() diff --git a/src/schema/Wrapper.php b/src/schema/Wrapper.php index d8cafed..f567702 100644 --- a/src/schema/Wrapper.php +++ b/src/schema/Wrapper.php @@ -3,6 +3,7 @@ namespace nur\sery\wip\schema; use ArrayAccess; use IteratorAggregate; +use nulib\php\func; use nur\sery\wip\schema\_assoc\AssocWrapper; use nur\sery\wip\schema\_list\ListWrapper; use nur\sery\wip\schema\_scalar\ScalarWrapper; @@ -67,6 +68,22 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { /** supprimer la valeur */ abstract function unset(): self; + protected function _format(WrapperContext $context, $format=null): string { + $value = $context->input->get($context->valueKey); + /** @var func $formatterFunc */ + $formatterFunc = $context->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 ??= $context->schema->format; + return $formatterFunc->invoke([$value, $format, $context, $this]); + } else { + # on assume que le type a été initialisé avec le format de la définition + # le cas échéant + return $context->type->format($value, $format); + } + } + /** formatter la valeur pour affichage */ abstract function format($format=null): string; diff --git a/src/schema/WrapperContext.php b/src/schema/WrapperContext.php index 071ed7b..440b8c6 100644 --- a/src/schema/WrapperContext.php +++ b/src/schema/WrapperContext.php @@ -5,21 +5,29 @@ use nur\sery\wip\schema\input\Input; use nur\sery\wip\schema\types\IType; class WrapperContext { - function __construct(Schema $schema, Wrapper $wrapper, ?Input $input, $valueKey, Result $result) { + function __construct(Schema $schema, ?Input $input, $valueKey, ?Result $result, ?array $params) { + $this->params = $params; + $this->verifix = $params["verifix"] ?? true; + $this->throw = $params["throw"] ?? null; + $this->schema = $schema; - $this->wrapper = $wrapper; + //$this->wrapper = $wrapper; if ($input !== null) $this->input = $input; $this->valueKey = $valueKey; $this->type = null; - $this->result = $result; + if ($result !== null) $this->result = $result; $this->origValue = null; $this->value = null; } + public ?array $params; + public bool $verifix; + public ?bool $throw; + /** schéma de la valeur */ public Schema $schema; /** instance de Wrapper associé à ce contexte */ - public Wrapper $wrapper; + //public Wrapper $wrapper; /** source et destination de la valeur */ public Input $input; /** @var string|int|null clé de la valeur dans le tableau destination */ diff --git a/src/schema/_assoc/AssocResult.php b/src/schema/_assoc/AssocResult.php index 8ac0c77..76a2a74 100644 --- a/src/schema/_assoc/AssocResult.php +++ b/src/schema/_assoc/AssocResult.php @@ -5,41 +5,38 @@ use nulib\ValueException; use nur\sery\wip\schema\Result; class AssocResult extends Result { - function __construct(Result $arrayResult, array &$keyResults) { - $this->arrayResult = $arrayResult; - $this->keyResults =& $keyResults; - $this->result =& $this->arrayResult; + function __construct(AssocWrapperContext $context) { + $this->context = $context; parent::__construct(); } function isAssoc(?AssocResult &$result=null): bool { $result = $this; return true;} - protected Result $arrayResult; - - /** @var Result[] */ - protected array $keyResults; + protected AssocWrapperContext $context; function getKeys(): array { - return array_keys($this->keyResults); + return $this->context->keys; } protected Result $result; - function select($key): Result { + function select($key): AssocResult { + $context = $this->context; 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); + $this->result = $context->arrayResult; + return $this; } + $wrapper = $context->keyWrappers[$key] ?? null; + if ($wrapper === null) throw ValueException::invalid_key($key); + $this->result = $wrapper->getResult(); return $this; } function reset(): void { - $this->arrayResult->reset(); - foreach ($this->keyResults as $result) { - $result->reset(); + $context = $this->context; + $context->arrayResult->reset(); + foreach ($context->keyWrappers as $wrapper) { + $wrapper->getResult()->reset(); } } diff --git a/src/schema/_assoc/AssocSchema.php b/src/schema/_assoc/AssocSchema.php index cabcc38..017a6fd 100644 --- a/src/schema/_assoc/AssocSchema.php +++ b/src/schema/_assoc/AssocSchema.php @@ -3,6 +3,8 @@ namespace nur\sery\wip\schema\_assoc; use nulib\cl; use nulib\ref\schema\ref_schema; +use nulib\ValueException; +use nur\sery\wip\schema\_scalar\ScalarWrapper; use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\Wrapper; @@ -50,6 +52,11 @@ class AssocSchema extends Schema { self::_ensure_schema_instances($definition); } $this->definition = $definition; + $keys = []; + foreach ($definition["schema"] as $key => $schema) { + if (!$schema["computed"]) $keys[] = $key; + } + $this->keys = $keys; } function isAssoc(?AssocSchema &$schema=null): bool { @@ -57,12 +64,28 @@ class AssocSchema extends Schema { return true; } + protected array $keys; + + function getKeys(): array { + return $this->keys; + } + + function getSchema($key): Schema { + if ($key === null) return $this; + $schema = $this->definition["schema"][$key] ?? null; + if ($schema === null) throw ValueException::invalid_key($key); + return $schema; + } + protected function newWrapper(): AssocWrapper { return new AssocWrapper($this); } function getWrapper(&$array=null, $arrayKey=null, ?Wrapper &$wrapper=null): AssocWrapper { + # si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception + # cf le code similaire dans ScalarWrapper::__construct() + $verifix = $array !== null || $wrapper !== null; if (!($wrapper instanceof AssocWrapper)) $wrapper = $this->newWrapper(); - return $wrapper->reset($array, $arrayKey); + return $wrapper->reset($array, $arrayKey, $verifix); } } diff --git a/src/schema/_assoc/AssocWrapper.php b/src/schema/_assoc/AssocWrapper.php index ea2981c..5ce88b1 100644 --- a/src/schema/_assoc/AssocWrapper.php +++ b/src/schema/_assoc/AssocWrapper.php @@ -3,59 +3,43 @@ 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; +use nur\sery\wip\schema\WrapperContext; class AssocWrapper extends Wrapper { function __construct(AssocSchema $schema, &$array=null, $arrayKey=null, ?array $params=null) { - $verifix = $params["verifix"] ?? true; - $throw = $params["throw"] ?? null; + $definitionSchema = $schema->getDefinition()["schema"]; + $keys = $schema->getKeys(); + $keyTypes = []; + $keyWrappers = []; + foreach ($keys as $key) { + $keyTypes[$key] = null; + $keyWrappers[$key] = $schema->getSchema($key)->getWrapper(); + } + $this->context = $context = new AssocWrapperContext($schema, null, null, null, $params); + $context->arrayResult = new ScalarResult(); + $context->keys = $keys; + $context->keyTypes = $keyTypes; + $context->keyWrappers = $keyWrappers; + $context->result = new AssocResult($context); + + $throw = $context->throw; 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(); + $context->throw = $throw ?? false; $this->reset($array, $arrayKey); - $this->throw = $throw ?? true; + $context->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; + /** @var AssocWrapperContext */ + protected WrapperContext $context; protected function newInput(&$value): Input { return new Input($value); @@ -64,73 +48,84 @@ class AssocWrapper extends Wrapper { 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; + $context = $this->context; + $context->input = $input; + $context->valueKey = $arrayKey; $this->analyze(); - if ($verifix ?? $this->verifix) $this->verifix(); + if ($verifix ?? $context->verifix) $this->verifix(); return $this; } function getKeys(): array { - return $this->keys; + return $this->context->keys; } - function select($key=null): ScalarWrapper { - $wrapper = $this->wrappers[$key] ?? null; - if ($key !== null) return $wrapper; - throw ValueException::invalid_key($key); + /** @param string|int|null $key */ + function select($key=null): Wrapper { + if ($key === null) return $this; + $wrapper = $this->context->keyWrappers[$key] ?? null; + if ($wrapper === null) throw ValueException::invalid_key($key); + return $wrapper; } - /** @param Result[] $results */ - function verifix(?bool $throw=null, ?array &$results=null): bool { + protected function analyze(): int { + return 0; #XXX } + function verifix(?bool $throw=null): bool { + return false; #XXX + } function getResult(): AssocResult { - return $this->result; + /** @var AssocResult $result */ + $result = $this->context->result; + return $result; } function isPresent(): bool { - return $this->result->present; + return $this->context->result->present; } function getType(): IType { - return $this->arrayType; + return $this->context->type; } function isAvailable(): bool { - return $this->result->available; + return $this->context->result->available; } function isValid(): bool { - return $this->result->valid; + return $this->context->result->valid; } function isNormalized(): bool { - return $this->result->normalized; + return $this->context->result->normalized; } function get($default=null) { - if ($this->result->available) return $this->input->get($this->arrayKey); + $context = $this->context; + if ($context->result->available) return $context->input->get($context->valueKey); else return $default; } function set($value, ?bool $verifix=null): AssocWrapper { - $this->input->set($value, $this->arrayKey); + $context = $this->context; + $context->input->set($value, $context->valueKey); $this->analyze(); - if ($verifix ?? $this->verifix) $this->verifix(); + if ($verifix ?? $context->verifix) $this->verifix(); return $this; } function unset(?bool $verifix=null): AssocWrapper { - $this->input->unset($this->arrayKey); + $context = $this->context; + $context->input->unset($context->valueKey); $this->analyze(); - if ($verifix ?? $this->verifix) $this->verifix(); + if ($verifix ?? $context->verifix) $this->verifix(); return $this; } function format($format = null): string { - // TODO: Implement format() method. + return $this->_format($this->context, $format); } function ensureKeys(): bool { diff --git a/src/schema/_assoc/AssocWrapperContext.php b/src/schema/_assoc/AssocWrapperContext.php new file mode 100644 index 0000000..ed25436 --- /dev/null +++ b/src/schema/_assoc/AssocWrapperContext.php @@ -0,0 +1,21 @@ +newWrapper(); - return $wrapper->reset($value, $valueKey); + return $wrapper->reset($value, $valueKey, $verifix); } } diff --git a/src/schema/_scalar/ScalarResult.php b/src/schema/_scalar/ScalarResult.php index 572d936..cc28149 100644 --- a/src/schema/_scalar/ScalarResult.php +++ b/src/schema/_scalar/ScalarResult.php @@ -15,7 +15,7 @@ class ScalarResult extends Result { function isScalar(?ScalarResult &$result=null): bool { $result = $this; return true; } function getKeys(): array { - return [null]; + return ScalarSchema::KEYS; } function select($key): Result { @@ -23,8 +23,7 @@ class ScalarResult extends Result { return $this; } - /** @var array */ - protected $result; + protected array $result; function reset(): void { $this->result = array_merge( diff --git a/src/schema/_scalar/ScalarSchema.php b/src/schema/_scalar/ScalarSchema.php index 44e2ac5..8ff264b 100644 --- a/src/schema/_scalar/ScalarSchema.php +++ b/src/schema/_scalar/ScalarSchema.php @@ -2,6 +2,7 @@ namespace nur\sery\wip\schema\_scalar; use nulib\ref\schema\ref_schema; +use nulib\ValueException; use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\types\IType; use nur\sery\wip\schema\Wrapper; @@ -66,6 +67,17 @@ class ScalarSchema extends Schema { return true; } + const KEYS = [null]; + + function getKeys(): array { + return self::KEYS; + } + + function getSchema($key): Schema { + if ($key !== null) throw ValueException::invalid_key($key); + return $this; + } + protected function newWrapper(): ScalarWrapper { return new ScalarWrapper($this); } diff --git a/src/schema/_scalar/ScalarWrapper.php b/src/schema/_scalar/ScalarWrapper.php index 5bcb847..3ff48fb 100644 --- a/src/schema/_scalar/ScalarWrapper.php +++ b/src/schema/_scalar/ScalarWrapper.php @@ -12,28 +12,23 @@ 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; + $this->context = $context = new WrapperContext($schema, null, null, new ScalarResult(), $params); + + $throw = $context->throw; 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->context = new WrapperContext($schema, $this, null, null, new ScalarResult()); - $this->verifix = $verifix; - $this->throw = $throw ?? false; + $context->throw = $throw ?? false; $this->reset($value, $valueKey); - $this->throw = $throw ?? true; + $context->throw = $throw ?? true; } function isScalar(?ScalarWrapper &$wrapper=null): bool { $wrapper = $this; return true; } protected WrapperContext $context; - protected bool $verifix; - - protected bool $throw; - protected function newInput(&$value): Input { return new Input($value); } @@ -41,16 +36,17 @@ class ScalarWrapper extends Wrapper { function reset(&$value, $valueKey=null, ?bool $verifix=null): Wrapper { if ($value instanceof Input) $input = $value; else $input = $this->newInput($value); - $this->context->input = $input; - $this->context->valueKey = $valueKey; - $this->context->type = null; + $context = $this->context; + $context->input = $input; + $context->valueKey = $valueKey; + $context->type = null; $this->analyze(); - if ($verifix ?? $this->verifix) $this->verifix(); + if ($verifix ?? $context->verifix) $this->verifix(); return $this; } function getKeys(): array { - return [null]; + return ScalarSchema::KEYS; } /** @param string|int|null $key */ @@ -164,7 +160,7 @@ class ScalarWrapper extends Wrapper { /** @var func $analyzerFunc */ $analyzerFunc = $schema->analyzerFunc; - if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context]); + if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context, $this]); else $what = $this->analyze0(); if ($what !== ref_analyze::STRING) return $what; @@ -172,7 +168,7 @@ class ScalarWrapper extends Wrapper { try { /** @var func $extractorFunc */ $extractorFunc = $schema->extractorFunc; - if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context]); + if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context, $this]); else $extracted = $context->type->extract($value); $context->value = $extracted; } catch (ValueException $e) { @@ -183,7 +179,7 @@ class ScalarWrapper extends Wrapper { try { /** @var func $parserFunc */ $parserFunc = $schema->parserFunc; - if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context]); + if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context, $this]); else $parsed = $context->type->parse($extracted); $context->value = $parsed; } catch (ValueException $e) { @@ -209,7 +205,6 @@ class ScalarWrapper extends Wrapper { /** @var ScalarResult $result */ $result = $context->result; - $verifix = false; $modified = false; if ($result->resultAvailable) { @@ -238,16 +233,15 @@ class ScalarWrapper extends Wrapper { /** @var func $normalizerFunc */ $normalizerFunc = $schema->normalizerFunc; if ($normalizerFunc !== null) { - $context = new WrapperContext($schema, $this, $input, $valueKey, $result); $orig = $value; - $value = $normalizerFunc->invoke([$orig, $context]); + $value = $normalizerFunc->invoke([$orig, $context, $this]); $modified = $value !== $orig; } else { - $modified = $this->type->verifix($value, $result, $schema); + $modified = $context->type->verifix($value, $result, $schema); } if ($result->valid) $input->set($value, $valueKey); } - if (!$result->valid) $result->throw($throw ?? $this->throw); + if (!$result->valid) $result->throw($throw ?? $context->throw); return $modified; } @@ -287,7 +281,7 @@ class ScalarWrapper extends Wrapper { $context = $this->context; $context->input->set($value, $context->valueKey); $this->analyze(); - if ($verifix ?? $this->verifix) $this->verifix(); + if ($verifix ?? $context->verifix) $this->verifix(); return $this; } @@ -295,24 +289,11 @@ class ScalarWrapper extends Wrapper { $context = $this->context; $context->input->unset($context->valueKey); $this->analyze(); - if ($verifix ?? $this->verifix) $this->verifix(); + if ($verifix ?? $context->verifix) $this->verifix(); return $this; } function format($format=null): string { - $context = $this->context; - $value = $context->input->get($context->valueKey); - /** @var func $formatterFunc */ - $formatterFunc = $context->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 ??= $context->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); - } + return $this->_format($this->context, $format); } } diff --git a/tests/wip/schema/_assoc/AssocSchemaTest.php b/tests/wip/schema/_assoc/AssocSchemaTest.php index 4ac9fca..8f78dd4 100644 --- a/tests/wip/schema/_assoc/AssocSchemaTest.php +++ b/tests/wip/schema/_assoc/AssocSchemaTest.php @@ -90,6 +90,9 @@ class AssocSchemaTest extends TestCase { "name" => "c", "pkey" => "c", "header" => "c", ], ]), $schema->getDefinition()); - yaml::dump($schema->getDefinition()); + //yaml::dump($schema->getDefinition()); + + $wrapper = $schema->getWrapper(); + $wrapper->getKeys(); } }