diff --git a/src/schema/TODO.md b/src/schema/TODO.md index dea7216..5c1bf19 100644 --- a/src/schema/TODO.md +++ b/src/schema/TODO.md @@ -1,14 +1,19 @@ # nulib\schema +* 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 +* ScalarSchema::from_property() + * dans AssocSchema, support `[key_prefix]` qui permet de spécifier un préfixe commun aux champs dans le tableau destination, e.g ~~~php - $value = Schema::ns($schema, [ + $wrapper = Schema::ns($schema, [ "a" => "?string", "b" => "?int", - ])->newValue(); + ])->newWrapper(); $dest = ["x_a" => 5, "x_b" => "10"], - $value->reset($dest, null, [ + $wrapper->reset($dest, null, [ "key_prefix" => "x_", ]); # $dest vaut ["x_a" => "5", "x_b" => 10]; @@ -19,18 +24,16 @@ alternative: c'est lors de la *définition* du schéma que le préfixe est ajouté e.g ~~~php - $value = Schema::ns($schema, [ + $wrapper = Schema::ns($schema, [ "a" => "?string", "b" => "?int", ], [ "key_prefix" => "x_", - ])->newValue(); + ])->newWrapper(); $dest = ["x_a" => 5, "x_b" => "10"], - $value->reset($dest); + $wrapper->reset($dest); # $dest vaut ["x_a" => "5", "x_b" => 10]; ~~~ -* dans la définition, `[type]` est remplacé par l'instance de IType lors de sa - résolution? * implémenter l'instanciation de types avec des paramètres particuliers. *si* des paramètres sont fournis, le type est instancié avec la signature `IType($typeDefinition, $schemaDefinition)` e.g diff --git a/src/schema/_scalar/ScalarResult.php b/src/schema/_scalar/ScalarResult.php index 1ef71ee..b247aba 100644 --- a/src/schema/_scalar/ScalarResult.php +++ b/src/schema/_scalar/ScalarResult.php @@ -1,11 +1,13 @@ resultAvailable = true; $this->present = true; $this->available = true; $this->null = false; $this->valid = false; - $this->orig = $value; + $this->origValue = $value; $messageKey = $this->messageKey = "invalid"; $message = $this->getMessage($messageKey, $schema); self::replace_key($message, $schema->name); self::replace_orig($message, $schema->orig); + if ($t !== null) { + $tmessage = ValueException::get_message($t); + if ($tmessage) $message .= ": $tmessage"; + } $this->message = $message; return ref_analyze::INVALID; } - function setValid(): int { + function setValid($normalizedValue=null): int { $this->resultAvailable = true; $this->present = true; $this->available = true; $this->null = false; $this->valid = true; + $this->normalizedValue = $normalizedValue; return ref_analyze::VALID; } diff --git a/src/schema/_scalar/ScalarWrapper.php b/src/schema/_scalar/ScalarWrapper.php index 00a40b2..13c3d9f 100644 --- a/src/schema/_scalar/ScalarWrapper.php +++ b/src/schema/_scalar/ScalarWrapper.php @@ -9,22 +9,23 @@ use nur\sery\wip\schema\input\Input; use nur\sery\wip\schema\types; use nur\sery\wip\schema\types\IType; use nur\sery\wip\schema\Wrapper; +use PHPUnit\Event\Code\Throwable; class ScalarWrapper extends Wrapper { function __construct(ScalarSchema $schema, &$dest=null, $destKey=null, ?array $params=null) { - $defaultVerifix = $params["verifix"] ?? true; - $defaultThrow = $params["throw"] ?? null; - if ($dest !== null && $defaultThrow === null) { + $verifix = $params["verifix"] ?? true; + $throw = $params["throw"] ?? null; + if ($dest !== null && $throw === null) { # Si $dest est null, ne pas lancer d'exception, parce qu'on considère que # c'est une initialisation sans conséquences - $defaultThrow = true; + $throw = true; } $this->schema = $schema; - $this->verifix = $defaultVerifix; - $this->throw = $defaultThrow ?? false; + $this->verifix = $verifix; + $this->throw = $throw ?? false; $this->result = new ScalarResult(); $this->reset($dest, $destKey); - $this->throw = $defaultThrow ?? true; + $this->throw = $throw ?? true; } function isScalar(?ScalarWrapper &$scalar=null): bool { $scalar = $this; return true; } @@ -137,19 +138,6 @@ class ScalarWrapper extends Wrapper { } } - function _extract(AnalyzerContext $context): bool { - return $context->type->extract($context->dest); - } - - function _parse(AnalyzerContext $context): bool { - try { - $context->dest = $context->type->parse($context->dest); - return true; - } catch (ValueException $e) { - return false; - } - } - function analyzeExtractParse(): int { $schema = $this->schema; $input = $this->input; @@ -164,42 +152,41 @@ class ScalarWrapper extends Wrapper { else $what = $this->_analyze($context); if ($what !== ref_analyze::STRING) return $what; - /** @var func $extractorFunc */ - $extractorFunc = $schema->extractorFunc; - if ($extractorFunc !== null) $valid = $extractorFunc->invoke([$context]); - else $valid = $this->_extract($context); - if (!$valid) return $result->setInvalid($context->orig, $schema); - if ($context->type->isNull($context->dest)) return $result->setNull($schema); - $extracted = $context->dest; - - /** @var func $parserFunc */ - $parserFunc = $schema->parserFunc; - if ($parserFunc !== null) $valid = $parserFunc->invoke([$context]); - else $valid = $this->_parse($context); - if ($valid) { - $normalized = $context->dest === $context->orig; - if ($normalized) { - $input->set($context->dest, $destKey); - return $result->setNormalized(); - } else { - $input->set($extracted, $destKey); - return $result->setValid(); - } + $dest = $context->dest; + try { + /** @var func $extractorFunc */ + $extractorFunc = $schema->extractorFunc; + if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$dest, $context]); + else $extracted = $context->type->extract($dest); + $context->dest = $extracted; + } catch (ValueException $e) { + return $result->setInvalid($context->orig, $schema, $e); + } + if ($context->type->isNull($extracted)) return $result->setNull($schema); + + try { + /** @var func $parserFunc */ + $parserFunc = $schema->parserFunc; + if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context]); + else $parsed = $context->type->parse($extracted); + $context->dest = $parsed; + } catch (ValueException $e) { + return $result->setInvalid($context->orig, $schema, $e); + } + + $normalized = $parsed === $context->orig; + if ($normalized) { + $input->set($parsed, $destKey); + return $result->setNormalized(); + } else { + $input->set($extracted, $destKey); + return $result->setValid($parsed); } - return $result->setInvalid($context->orig, $schema); } function verifix(?bool $throw=null): bool { - $schema = $this->schema; - $input = $this->input; - $destKey = $this->destKey; $result = $this->result; - $context = new AnalyzerContext($schema, $this, $input, $destKey, $result); - /** @var func $normalizerFunc */ - $normalizerFunc = $schema->normalizerFunc; - if ($normalizerFunc !== null) return $normalizerFunc->invoke([$context]); - - if ($throw === null) $throw = $this->throw; + $destKey = $this->destKey; $verifix = false; $modified = false; if ($result->resultAvailable) { @@ -208,18 +195,37 @@ class ScalarWrapper extends Wrapper { # valeur assimilée à null $this->input->set(null, $destKey); } elseif ($result->valid && !$result->normalized) { - # normaliser la valeur - $verifix = true; + $normalizedValue = $result->normalizedValue; + if ($normalizedValue !== null) { + # la valeur normalisée est disponible + $this->input->set($normalizedValue); + $result->normalizedValue = null; + $modified = true; + } else { + # normaliser la valeur + $verifix = true; + } } } else { $verifix = true; } + if ($verifix) { $value = $this->input->get($destKey); - $modified = $this->type->verifix($value, $result, $this->schema); + $schema = $this->schema; + /** @var func $normalizerFunc */ + $normalizerFunc = $schema->normalizerFunc; + if ($normalizerFunc !== null) { + $context = new AnalyzerContext($schema, $this, $this->input, $destKey, $result); + $orig = $value; + $value = $normalizerFunc->invoke([$orig, $context]); + $modified = $value !== $orig; + } else { + $modified = $this->type->verifix($value, $result, $this->schema); + } if ($result->valid) $this->input->set($value, $destKey); } - if (!$result->valid) $result->throw($throw); + if (!$result->valid) $result->throw($throw ?? $this->throw); return $modified; } diff --git a/src/schema/types/IType.php b/src/schema/types/IType.php index 5fcbda4..f0e564f 100644 --- a/src/schema/types/IType.php +++ b/src/schema/types/IType.php @@ -24,8 +24,12 @@ interface IType { */ function isValid($value, ?bool &$normalized=null): bool; - /** extraire de la chaine la valeur à analyser */ - function extract(string &$value): bool; + /** + * extraire de la chaine la valeur à analyser + * + * @throws ValueException en cas d'erreur d'analyse + */ + function extract(string $value): string; /** * analyser la chaine et retourner la valeur "convertie" diff --git a/src/schema/types/_tsimple.php b/src/schema/types/_tsimple.php index c6d213c..91ded5d 100644 --- a/src/schema/types/_tsimple.php +++ b/src/schema/types/_tsimple.php @@ -22,7 +22,7 @@ abstract class _tsimple implements IType { return $value === null || $value === ""; } - function extract(string &$value): bool { - return true; + function extract(string $value): string { + return $value; } } diff --git a/src/schema/types/_tstring.php b/src/schema/types/_tstring.php index d4928ff..782f2e6 100644 --- a/src/schema/types/_tstring.php +++ b/src/schema/types/_tstring.php @@ -18,9 +18,9 @@ abstract class _tstring extends _tsimple { return $params; } - function extract(string &$value): bool { + function extract(string $value): string { if ($this->params["trim"] ?? static::TRIM) $value = trim($value); if ($this->params["norm_nl"] ?? static::NORM_NL) $value = str::norm_nl($value); - return true; + return $value; } } diff --git a/src/schema/types/tbool.php b/src/schema/types/tbool.php index f89a01a..8e85a01 100644 --- a/src/schema/types/tbool.php +++ b/src/schema/types/tbool.php @@ -60,9 +60,8 @@ class tbool extends _tformatable { return is_scalar($value); } - function extract(string &$value): bool { - $value = trim($value); - return true; + function extract(string $value): string { + return trim($value); } function parse(string $value) { diff --git a/src/schema/types/tfloat.php b/src/schema/types/tfloat.php index 50668ac..47f3956 100644 --- a/src/schema/types/tfloat.php +++ b/src/schema/types/tfloat.php @@ -21,9 +21,8 @@ class tfloat extends _tformatable { return is_scalar($value); } - function extract(string &$value): bool { - $value = trim($value); - return true; + function extract(string $value): string { + return trim($value); } function parse(string $value) { diff --git a/src/schema/types/tint.php b/src/schema/types/tint.php index 395d5b7..51dbeec 100644 --- a/src/schema/types/tint.php +++ b/src/schema/types/tint.php @@ -23,9 +23,8 @@ class tint extends _tformatable { return is_scalar($value); } - function extract(string &$value): bool { - $value = trim($value); - return true; + function extract(string $value): string { + return trim($value); } function parse(string $value) { diff --git a/tests/wip/schema/_scalar/ScalarValueTest.php b/tests/wip/schema/_scalar/ScalarValueTest.php index 60d2f36..1fb9184 100644 --- a/tests/wip/schema/_scalar/ScalarValueTest.php +++ b/tests/wip/schema/_scalar/ScalarValueTest.php @@ -1,7 +1,6 @@ checkVerifix($schema, false, false, null, true, false, true, true); + $this->checkVerifix($schema, false, true, null, true, false, true, true); + $this->checkVerifix($schema, null, false, null, true, true, false, false); $this->checkException($schema, null, true, ValueException::class); @@ -49,9 +51,6 @@ class ScalarValueTest extends TestCase { $this->checkVerifix($schema, " text ", false, " text ", true, true, true, true); $this->checkVerifix($schema, " text ", true, " text ", true, true, true, true); - $this->checkVerifix($schema, false, false, null, true, false, true, true); - $this->checkVerifix($schema, false, true, null, true, false, true, true); - $this->checkVerifix($schema, true, false, true, true, true, true, false); $this->checkVerifix($schema, true, true, "1", true, true, true, false); @@ -95,6 +94,9 @@ class ScalarValueTest extends TestCase { function testString() { $schema = new ScalarSchema("string"); + $this->checkVerifix($schema, false, false, null, true, false, true, true); + $this->checkVerifix($schema, false, true, null, true, false, true, true); + $this->checkVerifix($schema, null, false, null, true, true, false, false); $this->checkException($schema, null, true, ValueException::class); @@ -102,16 +104,13 @@ class ScalarValueTest extends TestCase { $this->checkVerifix($schema, "", true, "", true, true, true, true); $this->checkVerifix($schema, " ", false, "", true, true, true, false); - $this->checkVerifix($schema, " ", true, "", true, true, true, true); + $this->checkVerifix($schema, " ", true, "", true, true, true, false); $this->checkVerifix($schema, "text", false, "text", true, true, true, true); $this->checkVerifix($schema, "text", true, "text", true, true, true, true); $this->checkVerifix($schema, " text ", false, "text", true, true, true, false); - $this->checkVerifix($schema, " text ", true, "text", true, true, true, true); - - $this->checkVerifix($schema, false, false, null, true, false, true, true); - $this->checkVerifix($schema, false, true, null, true, false, true, true); + $this->checkVerifix($schema, " text ", true, "text", true, true, true, false); $this->checkVerifix($schema, true, false, true, true, true, true, false); $this->checkVerifix($schema, true, true, "1", true, true, true, false); @@ -133,11 +132,32 @@ class ScalarValueTest extends TestCase { $this->checkVerifix($schema, false, false, null, true, false, false, false); $this->checkException($schema, false, true, ValueException::class); + + ## Tester allow_empty === false + $inputParams = ["allow_empty" => false]; + $schema = new ScalarSchema("string"); + + $this->checkVerifix($schema, null, false, null, true, true, false, false, $inputParams); + $this->checkException($schema, null, true, ValueException::class, $inputParams); + + $this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams); + $this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams); + + $schema = new ScalarSchema("?string"); + + $this->checkVerifix($schema, null, false, null, true, true, true, true, $inputParams); + $this->checkVerifix($schema, null, true, null, true, true, true, true, $inputParams); + + $this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams); + $this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams); } function testInt() { $schema = new ScalarSchema("int"); + $this->checkVerifix($schema, false, false, null, true, false, true, true); + $this->checkVerifix($schema, false, true, null, true, false, true, true); + $this->checkVerifix($schema, null, false, null, true, true, false, false); $this->checkException($schema, null, true, ValueException::class); @@ -165,9 +185,6 @@ class ScalarValueTest extends TestCase { $this->checkVerifix($schema, "text", false, "text", true, true, false, false); $this->checkException($schema, "text", true, ValueException::class); - $this->checkVerifix($schema, false, false, null, true, false, true, true); - $this->checkVerifix($schema, false, true, null, true, false, true, true); - $this->checkVerifix($schema, true, false, true, true, true, true, false); $this->checkVerifix($schema, true, true, 1, true, true, true, false); @@ -185,5 +202,23 @@ class ScalarValueTest extends TestCase { $this->checkVerifix($schema, false, false, null, true, false, false, false); $this->checkException($schema, false, true, ValueException::class); + + ## Tester allow_empty === false + $inputParams = ["allow_empty" => false]; + $schema = new ScalarSchema("int"); + + $this->checkVerifix($schema, null, false, null, true, true, false, false, $inputParams); + $this->checkException($schema, null, true, ValueException::class, $inputParams); + + $this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams); + $this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams); + + $schema = new ScalarSchema("?int"); + + $this->checkVerifix($schema, null, false, null, true, true, true, true, $inputParams); + $this->checkVerifix($schema, null, true, null, true, true, true, true, $inputParams); + + $this->checkVerifix($schema, "", false, null, true, false, true, true, $inputParams); + $this->checkVerifix($schema, "", true, null, true, false, true, true, $inputParams); } }