diff --git a/readme.md b/readme.md index f93a43f..e291596 100644 --- a/readme.md +++ b/readme.md @@ -314,3 +314,18 @@ $schema = Expect::arrayOf('string') $processor->process($schema, ['a', 'b']); // it passes, 2 is even number $processor->process($schema, ['a', 'b', 'c']); // error, 3 is not even number ``` + +Or + +```php +$schema = Expect::string()->assert('is_file'); // file must exist +``` + +You can add custom description for every assert. This description will be part of error message. + +```php +$schema = Expect::arrayOf('string') + ->assert(function ($v) { return count($v) % 2 === 0; }, 'Even items in array'); + +$processor->process($schema, ['a', 'b', 'c']); // Failed assertion "Even items in array" for option with value array. +``` diff --git a/src/Schema/Elements/Base.php b/src/Schema/Elements/Base.php index a02b09a..f5f653e 100644 --- a/src/Schema/Elements/Base.php +++ b/src/Schema/Elements/Base.php @@ -27,7 +27,7 @@ trait Base /** @var callable|null */ private $before; - /** @var callable[] */ + /** @var array[] */ private $asserts = []; /** @var string|null */ @@ -62,9 +62,9 @@ public function castTo(string $type): self } - public function assert(callable $handler): self + public function assert(callable $handler, string $description = null): self { - $this->asserts[] = $handler; + $this->asserts[] = [$handler, $description]; return $this; } @@ -110,9 +110,9 @@ private function doFinalize($value, Context $context) } } - foreach ($this->asserts as $i => $assert) { - if (!$assert($value)) { - $expected = is_string($assert) ? "$assert()" : "#$i"; + foreach ($this->asserts as $i => [$handler, $description]) { + if (!$handler($value)) { + $expected = $description ? ('"' . $description . '"') : (is_string($handler) ? "$handler()" : "#$i"); $context->addError("Failed assertion $expected for option %path% with value " . static::formatValue($value) . '.'); return; } diff --git a/src/Schema/Elements/Type.php b/src/Schema/Elements/Type.php index f4586d9..67bdebe 100644 --- a/src/Schema/Elements/Type.php +++ b/src/Schema/Elements/Type.php @@ -135,8 +135,13 @@ public function complete($value, Context $context) $value = []; // is unable to distinguish null from array in NEON } + $cleanValue = $value; + if (is_array($cleanValue) && isset($cleanValue[Helpers::PREVENT_MERGING])) { + unset($cleanValue[Helpers::PREVENT_MERGING]); + } + $expected = $this->type . ($this->range === [null, null] ? '' : ':' . implode('..', $this->range)); - if (!$this->doValidate($value, $expected, $context)) { + if (!$this->doValidate($cleanValue, $expected, $context)) { return; } if ($this->pattern !== null && !preg_match("\x01^(?:$this->pattern)$\x01Du", $value)) { @@ -150,7 +155,7 @@ public function complete($value, Context $context) if ($this->items) { $errCount = count($context->errors); - foreach ($value as $key => $val) { + foreach ($cleanValue as $key => $val) { $context->path[] = $key; $value[$key] = $this->items->complete($val, $context); array_pop($context->path); diff --git a/tests/Schema/Expect.assert.phpt b/tests/Schema/Expect.assert.phpt index 7d419bc..49d23c8 100644 --- a/tests/Schema/Expect.assert.phpt +++ b/tests/Schema/Expect.assert.phpt @@ -34,3 +34,20 @@ test(function () { // multiple assertions Assert::same('123', (new Processor)->process($schema, '123')); }); + + +test(function () { // multiple assertions with custom descriptions + $schema = Expect::string() + ->assert('ctype_digit', 'Is number') + ->assert(function ($s) { return strlen($s) >= 3; }, 'Minimal lenght'); + + checkValidationErrors(function () use ($schema) { + (new Processor)->process($schema, ''); + }, ["Failed assertion \"Is number\" for option with value ''."]); + + checkValidationErrors(function () use ($schema) { + (new Processor)->process($schema, '1'); + }, ["Failed assertion \"Minimal lenght\" for option with value '1'."]); + + Assert::same('123', (new Processor)->process($schema, '123')); +}); diff --git a/tests/Schema/Expect.list.phpt b/tests/Schema/Expect.list.phpt index e58bac2..d8175bd 100644 --- a/tests/Schema/Expect.list.phpt +++ b/tests/Schema/Expect.list.phpt @@ -3,6 +3,7 @@ declare(strict_types=1); use Nette\Schema\Expect; +use Nette\Schema\Helpers; use Nette\Schema\Processor; use Tester\Assert; @@ -45,6 +46,8 @@ test(function () { // merging Assert::same([1, 2, 3, 'a', 'b', 'c'], (new Processor)->process($schema, ['a', 'b', 'c'])); Assert::same([1, 2, 3], (new Processor)->process($schema, null)); + + Assert::same(['a', 'b', 'c'], (new Processor)->process($schema, ['a', 'b', 'c', Helpers::PREVENT_MERGING => true])); }); @@ -64,6 +67,8 @@ test(function () { // merging & other items validation ]); Assert::same([1, 2, 3], (new Processor)->process($schema, null)); + + Assert::same(['a', 'b', 'c'], (new Processor)->process($schema, ['a', 'b', 'c', Helpers::PREVENT_MERGING => true])); });