diff --git a/src/schema/Result.php b/src/schema/Result.php index 46f08ee..21c2b7f 100644 --- a/src/schema/Result.php +++ b/src/schema/Result.php @@ -2,9 +2,6 @@ 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; use Throwable; /** @@ -37,10 +34,6 @@ abstract class Result implements IteratorAggregate { $this->reset(); } - 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 * objet diff --git a/src/schema/Schema.php b/src/schema/Schema.php index 4d1a422..aef756d 100644 --- a/src/schema/Schema.php +++ b/src/schema/Schema.php @@ -98,18 +98,18 @@ abstract class Schema implements ArrayAccess { return false; } - protected static function _normalize_definition(&$definition, $definitionKey=null): void { + protected static function _normalize_definition(&$definition, $definitionKey=null, ?array $natureMetaschema=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) { + foreach (array_keys(ref_schema::VALUE_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]; + $definition[$key] = ref_schema::VALUE_METASCHEMA[$key][1]; } } } @@ -161,6 +161,12 @@ abstract class Schema implements ArrayAccess { # nature $nature = $definition[""]; tarray::ensure_array($nature); + $natureMetaschema ??= ref_schema::NATURE_METASCHEMA; + foreach (array_keys($natureMetaschema) as $key) { + if (!array_key_exists($key, $nature)) { + $nature[$key] = $natureMetaschema[$key][1]; + } + } $definition[""] = $nature; # name, pkey, header $name = $definition["name"]; diff --git a/src/schema/TODO.md b/src/schema/TODO.md index b41abbe..745781f 100644 --- a/src/schema/TODO.md +++ b/src/schema/TODO.md @@ -2,8 +2,8 @@ * faire PropertyAccess * si l'argument de Input() est un objet, utiliser PropertyAccess au lieu de KeyAccess -* possibilité de forcer l'un ou l'autre -* ensureKeys() et orderKeys() se fait au niveau de access + * possibilité de forcer l'un ou l'autre (paramètre type=value|array|object) +* ensureKeys() et orderKeys() se fait au niveau de access (ou input?) * valeurs composite/computed * analyse / vérification de la valeur complète après calcul du résultat, si diff --git a/src/schema/Wrapper.php b/src/schema/Wrapper.php index 7d6361f..4cf6a9a 100644 --- a/src/schema/Wrapper.php +++ b/src/schema/Wrapper.php @@ -23,14 +23,17 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { $this->context->resetParams($params); } - protected function afterModify(?array $params, bool $reset=true): void { + protected function resetContext($resetSelectedKey): void { $context = $this->context; - if ($reset) { - $context->type = null; - $context->result->reset(); - $context->analyzed = false; - $context->normalized = false; - } + $context->type = null; + $context->result->reset(); + $context->analyzed = false; + $context->normalized = false; + } + + protected function afterModify(?array $params, $resetSelectedKey=false): void { + $context = $this->context; + $this->resetContext($resetSelectedKey); if ($params["analyze"] ?? $context->analyze) { $this->analyze($params); } @@ -51,7 +54,15 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { * @param ?array $params paramètres spécifique à cet appel, qui peuvent être * différent des paramètres par défaut */ - abstract function reset(&$value, $valueKey=null, ?array $params=null): self; + function reset(&$value, $valueKey=null, ?array $params=null): Wrapper { + $context = $this->context; + if ($value instanceof Input) $input = $value; + else $input = $this->newInput($value); + $context->input = $input; + $context->valueKey = $valueKey; + $this->afterModify($params, true); + return $this; + } /** analyser la valeur */ abstract protected function _analyze(?array $params): int; @@ -119,50 +130,50 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { * cette fonction doit être appelée après {@link set()} ou {@link unset()} et * après que le wrapper aie été sélectionné avec {@link select()} */ - function getResult(): Result { + function getResult($key=false): Result { return $this->context->result; } /** retourner true si la valeur existe */ - function isPresent(): bool { - return $this->context->result->present; + function isPresent($key=false): bool { + return $this->getResult()->present; } /** retourner le type associé à la valeur */ - function getType(): IType { + function getType($key=false): IType { return $this->context->type; } /** retourner true si la valeur est disponible */ - function isAvailable(): bool { - return $this->context->result->available; + function isAvailable($key=false): bool { + return $this->getResult()->available; } /** retourner true si la valeur est valide */ - function isValid(): bool { - return $this->context->result->valid; + function isValid($key=false): bool { + return $this->getResult()->valid; } /** retourner true si la valeur est dans sa forme normalisée */ - function isNormalized(): bool { - return $this->context->result->normalized; + function isNormalized($key=false): bool { + return $this->getResult()->normalized; } - function get($default=null) { + function get($default=null, $key=false) { $context = $this->context; - if ($context->result->available) return $context->input->get($context->valueKey); - else return $default; + if (!$context->result->available) return $default; + return $context->input->get($context->valueKey); } - function set($value, ?array $params=null): self { + function set($value, ?array $params=null, $key=false): self { $context = $this->context; $context->input->set($value, $context->valueKey); $this->afterModify($params); return $this; } - function unset(?array $params=null): self { + function unset(?array $params=null, $key=false): self { $context = $this->context; $context->input->unset($context->valueKey); $this->afterModify($params); @@ -186,7 +197,7 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { } /** formatter la valeur pour affichage */ - function format($format=null): string { + function format($format=null, $key=false): string { return $this->_format($this->context, $format); } @@ -198,14 +209,14 @@ abstract class Wrapper implements ArrayAccess, IteratorAggregate { } function offsetGet($offset) { - return $this->select($offset)->get(); + return $this->get(null, $offset); } function offsetSet($offset, $value): void { - $this->select($offset)->set($value); + $this->set($value, null, $offset); } function offsetUnset($offset): void { - $this->select($offset)->unset(); + $this->unset(null, $offset); } } diff --git a/src/schema/WrapperContext.php b/src/schema/WrapperContext.php index 6e523e4..d144939 100644 --- a/src/schema/WrapperContext.php +++ b/src/schema/WrapperContext.php @@ -12,6 +12,7 @@ class WrapperContext { $this->valueKey = $valueKey; $this->origValue = null; $this->value = null; + $this->selectedKey = null; $this->type = null; $this->result = null; } diff --git a/src/schema/_assoc/AssocResult.php b/src/schema/_assoc/AssocResult.php deleted file mode 100644 index 76a2a74..0000000 --- a/src/schema/_assoc/AssocResult.php +++ /dev/null @@ -1,50 +0,0 @@ -context = $context; - parent::__construct(); - } - - function isAssoc(?AssocResult &$result=null): bool { $result = $this; return true;} - - protected AssocWrapperContext $context; - - function getKeys(): array { - return $this->context->keys; - } - - protected Result $result; - - function select($key): AssocResult { - $context = $this->context; - if ($key === null) { - $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 { - $context = $this->context; - $context->arrayResult->reset(); - foreach ($context->keyWrappers as $wrapper) { - $wrapper->getResult()->reset(); - } - } - - function __get(string $name) { - return $this->result[$name]; - } - - function __set(string $name, $value): void { - $this->result[$name] = $value; - } -} diff --git a/src/schema/_assoc/AssocSchema.php b/src/schema/_assoc/AssocSchema.php index d7bb88e..e49d794 100644 --- a/src/schema/_assoc/AssocSchema.php +++ b/src/schema/_assoc/AssocSchema.php @@ -11,8 +11,11 @@ use nur\sery\wip\schema\Wrapper; * Class AssocSchema */ class AssocSchema extends Schema { - /** @var array meta-schema d'un schéma de nature tableau associatif */ - const METASCHEMA = ref_schema::ASSOC_METASCHEMA; + //const METASCHEMA = ref_schema::VALUE_METASCHEMA; + //const NATURE_METASCHEMA = [ + // ...ref_schema::NATURE_METASCHEMA, + // ...ref_schema::ASSOC_NATURE_METASCHEMA, + //]; /** * indiquer si $definition est une définition de schéma de nature tableau @@ -37,7 +40,8 @@ class AssocSchema extends Schema { "schema" => $definition, ]; } - self::_normalize_definition($definition, $definitionKey); + $natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::ASSOC_NATURE_METASCHEMA); + self::_normalize_definition($definition, $definitionKey, $natureMetaschema); self::_ensure_nature($definition, "assoc", "array"); return $definition; } @@ -83,9 +87,9 @@ class AssocSchema extends Schema { function getWrapper(&$value=null, $valueKey=null, ?array $params=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 = $value !== null || $wrapper !== null; + $dontNormalize = $value === null && $wrapper === null; if (!($wrapper instanceof AssocWrapper)) $wrapper = $this->newWrapper(); if ($params !== null) $wrapper->resetParams($params); - return $wrapper->reset($value, $valueKey, $verifix); + return $wrapper->reset($value, $valueKey, $dontNormalize? ["normalize" => false]: null); } } diff --git a/src/schema/_assoc/AssocWrapper.php b/src/schema/_assoc/AssocWrapper.php index f7a3a38..029d5b7 100644 --- a/src/schema/_assoc/AssocWrapper.php +++ b/src/schema/_assoc/AssocWrapper.php @@ -4,12 +4,13 @@ namespace nur\sery\wip\schema\_assoc; use nulib\ValueException; use nur\sery\wip\schema\_scalar\ScalarResult; 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) { + function __construct(AssocSchema $schema, &$value=null, $valueKey=null, ?array $params=null) { $keys = $schema->getKeys(); $keyTypes = []; $keyWrappers = []; @@ -22,16 +23,17 @@ class AssocWrapper extends Wrapper { $context->keys = $keys; $context->keyTypes = $keyTypes; $context->keyWrappers = $keyWrappers; - $context->result = new AssocResult($context); + $context->result = $context->arrayResult; - $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; - } + # calculer manuellemet throw ici parce que WrapperContext le met à true par + # défaut. on veut pouvoir mettre temporairement throw à false si jamais il + # n'est pas spécifié par l'utilisateur + $throw = $params["throw"] ?? null; + # Si $value est null, ne pas lancer d'exception, parce qu'on considère que + # c'est une initialisation sans conséquences + if ($throw === null && $value !== null) $throw = true; $context->throw = $throw ?? false; - $this->reset($array, $arrayKey); + $this->reset($value, $valueKey); $context->throw = $throw ?? true; } @@ -40,35 +42,103 @@ class AssocWrapper extends Wrapper { /** @var AssocWrapperContext */ protected WrapperContext $context; - function reset(&$array, $arrayKey=null, ?array $params=null): Wrapper { - if ($array instanceof Input) $input = $array; - else $input = $this->newInput($array); + protected function resetContext($resetSelectedKey): void { $context = $this->context; - $context->input = $input; - $context->valueKey = $arrayKey; - $this->afterModify($params); - return $this; + $context->arrayResult->reset(); + foreach ($context->keyWrappers as $wrapper) { + $wrapper->getResult()->reset(); + } + $context->analyzed = false; + $context->normalized = false; + if ($resetSelectedKey) { + $context->selectedKey = null; + $context->type = $context->arrayType; + $context->result = $context->arrayResult; + } } function getKeys(): array { return $this->context->keys; } - /** @param string|int|null $key */ - function select($key=null): Wrapper { - #XXX il faut que context contiennent les informations pour la clé sélectionnée - if ($key === null) return $this; - $wrapper = $this->context->keyWrappers[$key] ?? null; + protected function _getWrapper($key): Wrapper { + $wrapper = $context->keyWrappers[$key] ?? null; if ($wrapper === null) throw ValueException::invalid_key($key); return $wrapper; } - function isNormalized(): bool { - return $this->context->result->normalized; + /** @param string|int|null $key */ + function select($key=null): Wrapper { + $context = $this->context; + if ($key === null) { + $context->selectedKey = null; + $context->type = $context->arrayType; + $context->result = $context->arrayResult; + return $this; + } + $wrapper = $this->_getWrapper($key); + $context->selectedKey = $key; + $context->type = $wrapper->getType(); + $context->result = $wrapper->getResult(); + return $wrapper; } - function ensureKeys(): bool { + protected function _analyze(?array $params): int { + return -1; } - function orderKeys(): bool { + + protected function _normalize(?array $params): bool { + return false; + } + + function getResult($key=false): Result { + if ($key === false) return $this->context->result; + elseif ($key === null) return $this->context->arrayResult; + else return $this->_getWrapper($key)->getResult(); + } + + function getType($key=false): IType { + if ($key === false) return $this->context->type; + elseif ($key === null) return $this->context->arrayType; + else return $this->_getWrapper($key)->getType(); + } + + function get($default=null, $key=false) { + $context = $this->context; + if (!$context->arrayResult->available) return $default; + if ($key === false) $key = $context->selectedKey; + if ($key === null) return $context->input->get($context->valueKey); + else return $this->_getWrapper($key)->get($default); + } + + function set($value, ?array $params=null, $key=false): Wrapper { + $context = $this->context; + if ($key === false) $key = $context->selectedKey; + if ($key === null) { + $context->input->set($value, $context->valueKey); + $this->afterModify($params); + } else { + $this->_getWrapper($key)->set($value); + } + return $this; + } + + function unset(?array $params=null, $key=false): Wrapper { + $context = $this->context; + if ($key === false) $key = $context->selectedKey; + if ($key === null) { + $context->input->unset($context->valueKey); + $this->afterModify($params); + } else { + $this->_getWrapper($key)->unset(); + } + return $this; + } + + function format($format=null, $key=false): string { + $context = $this->context; + if ($key === false) $key = $context->selectedKey; + if ($key === null) return $this->_format($context, $format); + else return $this->_getWrapper($key)->format($format); } } diff --git a/src/schema/_assoc/AssocWrapperContext.php b/src/schema/_assoc/AssocWrapperContext.php index ed25436..44fee29 100644 --- a/src/schema/_assoc/AssocWrapperContext.php +++ b/src/schema/_assoc/AssocWrapperContext.php @@ -2,13 +2,22 @@ namespace nur\sery\wip\schema\_assoc; use nur\sery\wip\schema\_scalar\ScalarResult; +use nur\sery\wip\schema\input\Input; +use nur\sery\wip\schema\Schema; use nur\sery\wip\schema\types\IType; use nur\sery\wip\schema\Wrapper; use nur\sery\wip\schema\WrapperContext; class AssocWrapperContext extends WrapperContext { + function __construct(Schema $schema, ?Input $input, $valueKey, ?array $params) { + parent::__construct($schema, $input, $valueKey, $params); + $this->arrayType = null; + $this->arrayResult = null; + } + + public ?IType $arrayType; /** résultat de l'analyse du tableau */ - public ScalarResult $arrayResult; + public ?ScalarResult $arrayResult; /** liste des clés valides */ public array $keys; diff --git a/src/schema/_scalar/ScalarSchema.php b/src/schema/_scalar/ScalarSchema.php index 1621e5b..d726ebb 100644 --- a/src/schema/_scalar/ScalarSchema.php +++ b/src/schema/_scalar/ScalarSchema.php @@ -1,6 +1,7 @@ context; - if ($value instanceof Input) $input = $value; - else $input = $this->newInput($value); - $context->input = $input; - $context->valueKey = $valueKey; - $this->afterModify($params); - return $this; - } - function getKeys(): array { return ScalarSchema::KEYS; } diff --git a/tests/wip/schema/_assoc/AssocSchemaTest.php b/tests/wip/schema/_assoc/AssocSchemaTest.php index 384f5d6..a082855 100644 --- a/tests/wip/schema/_assoc/AssocSchemaTest.php +++ b/tests/wip/schema/_assoc/AssocSchemaTest.php @@ -7,7 +7,14 @@ use nur\sery\wip\schema\_scalar\ScalarSchemaTest; class AssocSchemaTest extends TestCase { const NULL_SCHEMA = [ - "" => ["assoc"], + "" => [ + "assoc", + "compute_func" => null, + "validate_func" => null, + "ensure_array" => false, + "ensure_keys" => true, + "ensure_order" => true, + ], "schema" => null, "type" => [null], "default" => null, diff --git a/tests/wip/schema/_scalar/ScalarSchemaTest.php b/tests/wip/schema/_scalar/ScalarSchemaTest.php index a5b6cee..5742fb8 100644 --- a/tests/wip/schema/_scalar/ScalarSchemaTest.php +++ b/tests/wip/schema/_scalar/ScalarSchemaTest.php @@ -19,7 +19,11 @@ class ScalarSchemaTest extends TestCase { "messages" => null, "formatter_func" => null, "format" => null, - "" => ["scalar"], + "" => [ + "scalar", + "compute_func" => null, + "validate_func" => null, + ], "schema" => null, "name" => null, "pkey" => null,