diff --git a/src/schema/Result.php b/src/schema/Result.php index 4567c02..6dcfb50 100644 --- a/src/schema/Result.php +++ b/src/schema/Result.php @@ -1,30 +1,21 @@ false]; - foreach (static::KEYS as $key) { - if (!array_key_exists($key, $result)) $result[$key] = null; - } - #XXX interpoler [message] avec les clés de $result - $this->result = $result; +abstract class Result { + function __construct() { + $this->reset(); } - /** @var array */ - protected $result; + function isAssoc(?AssocResult &$assoc=null): bool { return false; } + function isList(?ListResult &$list=null): bool { return false; } + function isScalar(?ScalarResult &$scalar=null): bool { return false; } /** * Obtenir la liste des clés valides pour les valeurs accessibles via cet @@ -35,7 +26,5 @@ class Result { /** obtenir un objet pour gérer la valeur spécifiée */ abstract function getResult($key=null): Result; - function __get($name) { - return $this->result[$name]; - } + abstract function reset(): void; } diff --git a/src/schema/Schema.php b/src/schema/Schema.php index 22779ca..82b7fa4 100644 --- a/src/schema/Schema.php +++ b/src/schema/Schema.php @@ -13,14 +13,14 @@ abstract class Schema implements ArrayAccess { * créer si besoin une nouvelle instance de {@link Schema} à partir d'une * définition de schéma */ - static function ns(?Schema &$schema, $definition): self { + static function ns(?Schema &$schema, $definition, $definitionKey=null): self { if ($schema === null) { if (AssocSchema::isa_definition($definition)) { - $schema = new AssocSchema($definition); + $schema = new AssocSchema($definition, $definitionKey); } elseif (ListSchema::isa_definition($definition)) { - $schema = new ListSchema($definition); + $schema = new ListSchema($definition, $definitionKey); } elseif (ScalarSchema::isa_definition($definition)) { - $schema = new ScalarSchema($definition); + $schema = new ScalarSchema($definition, $definitionKey); } else { throw SchemaException::invalid_schema(); } diff --git a/src/schema/SchemaException.php b/src/schema/SchemaException.php index ac57759..e7988d5 100644 --- a/src/schema/SchemaException.php +++ b/src/schema/SchemaException.php @@ -10,15 +10,13 @@ class SchemaException extends Exception { return new static($invalid_schema); } - static final function invalid_type($value, ?string $message=null): self { - $invalid_type = var_export($value, true).": invalid type"; - if ($message !== null) $invalid_type .= ": $message"; - return new static($invalid_type); + static final function invalid_kind(string $kind, $value, ?string $message=null): self { + $invalid_kind = var_export($value, true).": invalid $kind"; + if ($message !== null) $invalid_kind .= ": $message"; + return new static($invalid_kind); } - static final function invalid_callable($value, ?string $message=null): self { - $invalid_callable = var_export($value, true).": invalid callable"; - if ($message !== null) $invalid_callable .= ": $message"; - return new static($invalid_callable); + static final function invalid_type($value, ?string $message=null): self { + return self::invalid_kind("type", $value, $message); } } diff --git a/src/schema/Value.php b/src/schema/Value.php index 47feb68..02a8c54 100644 --- a/src/schema/Value.php +++ b/src/schema/Value.php @@ -3,9 +3,16 @@ namespace nur\sery\schema; use ArrayAccess; use IteratorAggregate; +use nur\sery\schema\_assoc\AssocValue; +use nur\sery\schema\_list\ListValue; +use nur\sery\schema\_scalar\ScalarValue; use nur\sery\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; } + /** spécifier la valeur destination gérée par cet objet */ abstract function reset(&$dest, $destKey=null, bool $verifix=true): self; @@ -28,7 +35,7 @@ abstract class Value implements ArrayAccess, IteratorAggregate { abstract function getType(): IType; /** retourner true si la valeur existe */ - abstract function exists(): bool; + abstract function available(): bool; /** supprimer la valeur */ abstract function unset(): void; diff --git a/src/schema/ValueException.php b/src/schema/ValueException.php new file mode 100644 index 0000000..c210176 --- /dev/null +++ b/src/schema/ValueException.php @@ -0,0 +1,20 @@ +definition = $definition; } diff --git a/src/schema/_list/ListResult.php b/src/schema/_list/ListResult.php index 98873df..6ab00d0 100644 --- a/src/schema/_list/ListResult.php +++ b/src/schema/_list/ListResult.php @@ -4,4 +4,5 @@ namespace nur\sery\schema\_list; use nur\sery\schema\Result; class ListResult extends Result { + function isList(?ListResult &$list=null): bool { $list = $this; return true;} } diff --git a/src/schema/_list/ListSchema.php b/src/schema/_list/ListSchema.php index 4710724..6f47445 100644 --- a/src/schema/_list/ListSchema.php +++ b/src/schema/_list/ListSchema.php @@ -31,12 +31,12 @@ class ListSchema extends Schema { return $count == 1 && $haveIndex0 && is_array($definition[0]); } - static function normalize($definition): array { + static function normalize($definition, $definitionKey=null): array { } - function __construct($definition=null, bool $normalize=true) { + function __construct($definition=null, $definitionKey=null, bool $normalize=true) { if ($definition === null) $definition = static::SCHEMA; - if ($normalize) $definition = self::normalize($definition); + if ($normalize) $definition = self::normalize($definition, $definitionKey); $this->definition = $definition; } diff --git a/src/schema/_scalar/ScalarResult.php b/src/schema/_scalar/ScalarResult.php index 533ac04..6e50a29 100644 --- a/src/schema/_scalar/ScalarResult.php +++ b/src/schema/_scalar/ScalarResult.php @@ -1,7 +1,82 @@ result = array_merge( + array_fill_keys(static::KEYS, null), [ + "missing" => true, + "null" => false, + "valid" => false, + "normalized" => false, + ]); + } + + function __get(string $name) { + return $this->result[$name]; + } + + function __set(string $name, $value): void { + $this->result[$name] = $value; + } + + function setMissing(ScalarSchema $schema, ?string $key=null): int { + $this->missing = true; + if (!$schema->required) { + $this->null = true; + $this->valid = true; + $this->normalized = true; + return ref_analyze::NORMALIZED; + } else { + $message = cl::get($schema->messages, "missing"); + $message = str_replace("{key}", $key, $message); + $this->message = $message; + return ref_analyze::MISSING; + } + } + + function setNull(ScalarSchema $schema, ?string $key=null): int { + $this->missing = true; + if ($schema->nullable) { + $this->valid = true; + $this->normalized = true; + return ref_analyze::NORMALIZED; + } else { + $message = cl::get($schema->messages, "null"); + $message = str_replace("{key}", $key, $message); + $this->message = $message; + return ref_analyze::NULL; + } + } } diff --git a/src/schema/_scalar/ScalarSchema.php b/src/schema/_scalar/ScalarSchema.php index 8f07377..42b3f5d 100644 --- a/src/schema/_scalar/ScalarSchema.php +++ b/src/schema/_scalar/ScalarSchema.php @@ -169,9 +169,9 @@ class ScalarSchema extends Schema { return $definition; } - function __construct($definition=null, bool $normalize=true) { + function __construct($definition=null, $definitionKey=null, bool $normalize=true) { if ($definition === null) $definition = static::SCHEMA; - if ($normalize) $definition = self::normalize($definition); + if ($normalize) $definition = self::normalize($definition, $definitionKey); $this->definition = $definition; } diff --git a/src/schema/_scalar/ScalarValue.php b/src/schema/_scalar/ScalarValue.php index 74564c7..b69b64e 100644 --- a/src/schema/_scalar/ScalarValue.php +++ b/src/schema/_scalar/ScalarValue.php @@ -2,13 +2,16 @@ namespace nur\sery\schema\_scalar; use nur\sery\schema\input\Input; +use nur\sery\schema\ref\ref_analyze; use nur\sery\schema\Result; use nur\sery\schema\types\IType; use nur\sery\schema\Value; +use nur\sery\schema\ValueException; class ScalarValue extends Value { function __construct(ScalarSchema $schema, &$dest=null, $key=null, bool $verifix=true) { $this->schema = $schema; + $this->result = new ScalarResult(); $this->reset($dest, $key, $verifix); } @@ -23,7 +26,10 @@ class ScalarValue extends Value { /** @var string|int clé de la valeur dans le tableau destination */ protected $destKey; - /** @var ?Result résultat de l'analyse de la valeur */ + /** @var IType */ + protected $type; + + /** @var ?ScalarResult résultat de l'analyse de la valeur */ protected $result; function reset(&$dest, $destKey=null, bool $verifix=true): Value { @@ -34,28 +40,53 @@ class ScalarValue extends Value { $this->result = null; #XXX résoudre les types ici? if ($verifix) $this->verifix(); + else $this->_analyze(); return $this; } + function getKeys(): array { + return [null]; + } + + function getValue($key=null): Value { + 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->available()) return $result->setMissing($schema, $destKey); + $value = $input->get($destKey); + if ($value === null) return $result->setNull($schema, $destKey); + #XXX résoudre le type + } + function exists(): bool { - return $this->input->exists($this->destKey); + return $this->input->exits($this->destKey); + } + + function available(): bool { + return $this->input->available($this->destKey); } function get($default=null) { $destKey = $this->destKey; $input = $this->input; - if ($input->exists($destKey)) return $input->get($destKey); + if ($input->available($destKey)) return $input->get($destKey); else return $default; } - function set($value): self { + function set($value, bool $verifix=true): Value { $this->input->set($value, $this->destKey); + if ($verifix) $this->verifix(); return $this; } - /** @var IType */ - protected $type; - function getType(): IType { if ($this->type === null) $this->type = $this->schema->getType($this->destKey); return $this->type; diff --git a/src/schema/input/FormInput.php b/src/schema/input/FormInput.php index 5a48b4d..fcd0b22 100644 --- a/src/schema/input/FormInput.php +++ b/src/schema/input/FormInput.php @@ -12,15 +12,36 @@ namespace nur\sery\schema\input; * */ class FormInput extends Input { + const ALLOW_EMPTY = false; + function exists($key=null): bool { if ($key === null) return false; return array_key_exists($key, $_POST) || array_key_exists($key, $_GET); } + function available($key=null): bool { + if ($key === null) return false; + if (array_key_exists($key, $_POST)) { + return $this->allowEmpty || $_POST[$key] !== ""; + } elseif (array_key_exists($key, $_GET)) { + return $this->allowEmpty || $_GET[$key] !== ""; + } else { + return false; + } + } + function get($key=null) { if ($key === null) return null; - if (array_key_exists($key, $_POST)) return $_POST[$key]; - elseif (array_key_exists($key, $_GET)) return $_GET[$key]; - else return null; + if (array_key_exists($key, $_POST)) { + $value = $_POST[$key]; + if ($value === "" && !$this->allowEmpty) return null; + return $value; + } elseif (array_key_exists($key, $_GET)) { + $value = $_GET[$key]; + if ($value === "" && !$this->allowEmpty) return null; + return $value; + } else { + return null; + } } } diff --git a/src/schema/input/GetInput.php b/src/schema/input/GetInput.php index aa0c8c4..1d74d84 100644 --- a/src/schema/input/GetInput.php +++ b/src/schema/input/GetInput.php @@ -13,9 +13,23 @@ class GetInput extends FormInput { return array_key_exists($key, $_GET); } + function available($key=null): bool { + if ($key === null) return false; + if (array_key_exists($key, $_GET)) { + return $this->allowEmpty || $_GET[$key] !== ""; + } else { + return false; + } + } + function get($key=null) { if ($key === null) return null; - if (array_key_exists($key, $_GET)) return $_GET[$key]; - else return null; + if (array_key_exists($key, $_GET)) { + $value = $_GET[$key]; + if ($value === "" && !$this->allowEmpty) return null; + return $value; + } else { + return null; + } } } diff --git a/src/schema/input/Input.php b/src/schema/input/Input.php index 3a6c7b6..7f19fff 100644 --- a/src/schema/input/Input.php +++ b/src/schema/input/Input.php @@ -4,29 +4,52 @@ namespace nur\sery\schema\input; #XXX available permet de préciser qu'une valeur doit exister et être non vide # pour être considérée. en faire un paramètre, à moins que ce soit au niveau du # type qu'il faille le gérer? +use nulib\cl; + /** * Class Input: accès à une valeur * * cette implémentation lit depuis et écrit dans une référence */ class Input { - function __construct(&$dest=null) { + const ALLOW_EMPTY = true; + + function __construct(&$dest=null, ?array $params=null) { $this->dest =& $dest; + $allowEmpty = cl::get($params, "allow_empty"); + if ($allowEmpty === null) $allowEmpty = static::ALLOW_EMPTY; + $this->allowEmpty = $allowEmpty; } protected $dest; - /** tester si la valeur existe */ - function exists($key=null): bool { + /** tester si la valeur existe sans tenir compte de $allowEmpty */ + function exits($key=null): bool { if ($key === null) return true; - return $this->dest !== null && array_key_exists($key, $this->dest); + $dest = $this->dest; + return $dest !== null && array_key_exists($key, $dest); + } + + /** + * @var bool comment considérer une chaine vide: "" si allowEmpty, null sinon + */ + protected $allowEmpty; + + /** tester si la valeur est disponible en tenant compte de $allowEmpty */ + function available($key=null): bool { + if ($key === null) return true; + $dest = $this->dest; + if ($dest === null || !array_key_exists($key, $dest)) return false; + return $this->allowEmpty || $dest[$key] !== ""; } function get($key=null) { - if ($key === null) return $this->dest; - elseif ($this->dest === null) return null; - elseif (!array_key_exists($key, $this->dest)) return null; - else return $this->dest[$key]; + $dest = $this->dest; + if ($key === null) return $dest; + if ($dest === null || !array_key_exists($key, $dest)) return null; + $value = $dest[$key]; + if ($value === "" && !$this->allowEmpty) return null; + return $value; } function set($value, $key=null): void { diff --git a/src/schema/input/PostInput.php b/src/schema/input/PostInput.php index 80f2b22..9cebe08 100644 --- a/src/schema/input/PostInput.php +++ b/src/schema/input/PostInput.php @@ -13,9 +13,23 @@ class PostInput extends FormInput { return array_key_exists($key, $_POST); } + function available($key=null): bool { + if ($key === null) return false; + if (array_key_exists($key, $_POST)) { + return $this->allowEmpty || $_POST[$key] !== ""; + } else { + return false; + } + } + function get($key=null) { if ($key === null) return null; - if (array_key_exists($key, $_POST)) return $_POST[$key]; - else return null; + if (array_key_exists($key, $_POST)) { + $value = $_POST[$key]; + if ($value === "" && !$this->allowEmpty) return null; + return $value; + } else { + return null; + } } } diff --git a/src/schema/ref/ref_schema.php b/src/schema/ref/ref_schema.php index 3d7f2de..3d443ac 100644 --- a/src/schema/ref/ref_schema.php +++ b/src/schema/ref/ref_schema.php @@ -41,7 +41,7 @@ class ref_schema { ]; const MESSAGES = [ - "absent" => "{key}: Vous devez spécifier cette valeur", + "missing" => "{key}: Vous devez spécifier cette valeur", "null" => "{key}: Cette valeur ne doit pas être nulle", "empty" => "{key}: Cette valeur ne doit pas être vide", "invalid" => "{key}: {orig}: cette valeur est invalide", diff --git a/src/schema/types/tcallable.php b/src/schema/types/tcallable.php index 54d83a4..6293b5a 100644 --- a/src/schema/types/tcallable.php +++ b/src/schema/types/tcallable.php @@ -1,11 +1,11 @@