diff --git a/src/Field.php b/src/Field.php index 6a0a07e5f771240002f46f65b5fa880bb1faa11a..5dc038c8c9fe096a56b1a2e7aa92fe05826ef36c 100644 --- a/src/Field.php +++ b/src/Field.php @@ -270,7 +270,7 @@ class Field } catch (InvalidValue $e) { $this->filterFailed = true; $this->errors = $e->errors; - return $value; + break; } } @@ -291,12 +291,14 @@ class Field */ public function validate($value, array $context = []) { + $filtered = $this->filter($value, $context); + if ($this->filterFailed) { return false; } try { - $hash = md5(serialize([$value, $context])); + $hash = md5(serialize([$filtered, $context])); if (isset($this->validationCache[$hash])) { return $this->validationCache[$hash]; } @@ -308,7 +310,7 @@ class Field $valid = true; $this->errors = []; foreach ($this->validators as $validator) { - if (!$validator->validate($value, $context)) { + if (!$validator->validate($filtered, $context)) { $valid = false; if ($error = $validator->getError()) { $this->errors[] = $error; diff --git a/src/Filter.php b/src/Filter.php index 9bd9db85a4f6e913850fa8210579d00880873c9f..f8f80d1b52f06217c14040cb23f662025d8d560e 100644 --- a/src/Filter.php +++ b/src/Filter.php @@ -41,7 +41,7 @@ abstract class Filter implements FilterInterface { if (is_string($filter)) { $filter = Filter::fromString($filter); - } elseif (is_callable($filter)) { + } elseif (is_callable($filter) && !$filter instanceof FilterInterface) { $filter = new Filter\Callback($filter); } @@ -81,6 +81,19 @@ abstract class Filter implements FilterInterface return static::create(ucfirst($name), $arguments); } + /** + * Call the filter is an alias for filter + * + * @param mixed $value + * @param array $context + * @return mixed + */ + public function __invoke($value, array $context = []) + { + return $this->filter($value, $context); + } + + /** * Create a filter dynamically * diff --git a/src/FilterInterface.php b/src/FilterInterface.php index 6a1f8cd164a5fd8eecf835c894004dfb1d10bd14..ff730fcb48ba136a2d173d7df3fd75eb36676940 100644 --- a/src/FilterInterface.php +++ b/src/FilterInterface.php @@ -13,6 +13,15 @@ interface FilterInterface */ public function filter($value, array $context = []); + /** + * Call the filter is an alias for filter + * + * @param mixed $value + * @param array $context + * @return mixed + */ + public function __invoke($value, array $context = []); + /** * Assign filter to $field * diff --git a/src/Gate.php b/src/Gate.php index b027132fa8643466e8f05f4c8d4963bff6f705a1..143a78ca233ed0fbd4cae7fbd604ef2abd5df495 100644 --- a/src/Gate.php +++ b/src/Gate.php @@ -210,9 +210,9 @@ class Gate $result = []; foreach ($fields as $k => $field) { - $filtered = $field->filter(isset($this->rawData[$k]) ? $this->rawData[$k] : null, $this->rawData); + $filtered = $field->filter($this->rawData[$k] ?? null, $this->rawData); - if ($validate && !$field->validate($filtered, $this->rawData)) { + if ($validate && !$field->validate($this->rawData[$k] ?? null, $this->rawData)) { if ($field->isRequired()) { $errors = $field->getErrors(); if (count($errors) > 0) { @@ -239,8 +239,9 @@ class Gate * * @param string $key * @return mixed - * @see Gate::getData() + * @see Gate::getData() * @codeCoverageIgnore trivial + * @throws InvalidValue */ public function get(string $key = null) { @@ -252,14 +253,21 @@ class Gate * * @param string $key * @return mixed - * @see Gate::getData() + * @see Gate::getData() * @codeCoverageIgnore trivial + * @throws InvalidValue */ public function __get(string $key) { return $this->getData($key); } + /** + * Validate $data or previously stored data + * + * @param array $data + * @return bool + */ public function validate(array $data = null) { if ($data) { @@ -268,13 +276,12 @@ class Gate $valid = true; $this->errors = []; - $filtered = $this->getData(null, false); foreach ($this->fields as $key => $field) { - if (empty($filtered[$key]) && !$field->isRequired()) { + if (empty($this->rawData[$key]) && !$field->isRequired()) { continue; } - if (!$field->validate($filtered[$key], $this->rawData)) { + if (!$field->validate($this->rawData[$key] ?? null, $this->rawData)) { $valid = false; $this->errors[$key] = $field->getErrors(); } diff --git a/src/Validator.php b/src/Validator.php index 29e526bb7bddd45c4e1002a309bb6c495ccba893..dffb010e491c6b284c38944e7a2d94fc5ecfd31e 100644 --- a/src/Validator.php +++ b/src/Validator.php @@ -57,7 +57,7 @@ abstract class Validator implements ValidatorInterface { if (is_string($validator)) { $validator = static::fromString($validator); - } elseif (is_callable($validator)) { + } elseif (is_callable($validator) && !$validator instanceof ValidatorInterface) { $validator = new Validator\Callback($validator); } @@ -101,6 +101,18 @@ abstract class Validator implements ValidatorInterface return static::create(ucfirst($name), $arguments); } + /** + * Call the validator is an alias for validate + * + * @param mixed $value + * @param array $context + * @return bool + */ + public function __invoke($value, array $context = []): bool + { + return $this->validate($value, $context); + } + /** * Create a validator dynamically * diff --git a/src/ValidatorInterface.php b/src/ValidatorInterface.php index 76ba5d76fde04a46ea06732dbe40949974e2cfd3..3b4fc4c5455fccdc0dfd5b703758ae377084f17f 100644 --- a/src/ValidatorInterface.php +++ b/src/ValidatorInterface.php @@ -13,6 +13,15 @@ interface ValidatorInterface */ public function validate($value, array $context = []): bool; + /** + * Call the validator is an alias for validate + * + * @param mixed $value + * @param array $context + * @return bool + */ + public function __invoke($value, array $context = []): bool; + /** * Assign validator to $field * diff --git a/tests/Field/ValidatorTest.php b/tests/Field/ValidatorTest.php index e1273561327a5b5fde52ef4df13d0e4b084ebbbc..36d46bc0a2fccb212db2f95689703dd9230e8616 100644 --- a/tests/Field/ValidatorTest.php +++ b/tests/Field/ValidatorTest.php @@ -156,4 +156,21 @@ class ValidatorTest extends TestCase $field->addValidator('notEmpty'); $field->validate(' body '); } + + /** @test */ + public function executesFilterBeforeValidation() + { + $field = \Mockery::mock(Field::class)->makePartial(); + $validator = \Mockery::mock(NotEmpty::class)->makePartial(); + $field->addValidator($validator); + + $field->shouldReceive('filter') + ->with('value', []) + ->once()->andReturn(42); + $validator->shouldReceive('validate') + ->with(42, []) + ->once()->andReturn(true); + + $field->validate('value'); + } } diff --git a/tests/Filter/CommonTest.php b/tests/Filter/CommonTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6b480c2c4fa9deb8ea816a9a28ebdd3104d523e4 --- /dev/null +++ b/tests/Filter/CommonTest.php @@ -0,0 +1,19 @@ +<?php + +namespace Verja\Test\Filter; + +use Verja\Filter\Trim; +use Verja\Test\TestCase; + +class CommonTest extends TestCase +{ + /** @test */ + public function filterIsInvokable() + { + $filter = new Trim(); + + $result = $filter(' foo bar '); + + self::assertSame('foo bar', $result); + } +} diff --git a/tests/Gate/GetDataTest.php b/tests/Gate/GetDataTest.php index ffc80978fb5d510b27060565f628153e1bdd0752..637356d7094aab0b1e6046d0588a5491c565451e 100644 --- a/tests/Gate/GetDataTest.php +++ b/tests/Gate/GetDataTest.php @@ -40,7 +40,7 @@ class GetDataTest extends TestCase $gate->addField('username', $field); $field->shouldReceive('filter')->with('john', ['username' => 'john', 'password' => 'abc123']) - ->once()->andReturn('john'); + ->atLeast()->once()->andReturn('john'); $gate->getData(); } @@ -50,10 +50,10 @@ class GetDataTest extends TestCase { $gate = new Gate([ 'username' => 'john@example', 'password' => 'abc123' ]); $field = \Mockery::mock(Field::class)->makePartial(); - $field->shouldReceive('filter')->andReturn('john'); $gate->addField('username', $field); - $field->shouldReceive('validate')->with('john', ['username' => 'john@example', 'password' => 'abc123']) + $field->shouldReceive('validate') + ->with('john@example', ['username' => 'john@example', 'password' => 'abc123']) ->once()->andReturn(true); $gate->getData(); diff --git a/tests/Gate/ValidateTest.php b/tests/Gate/ValidateTest.php index da5db22afef667e4d2ed54e0e12d6245054c7c21..13706deb00458775dd045ec4d21884a10731304d 100644 --- a/tests/Gate/ValidateTest.php +++ b/tests/Gate/ValidateTest.php @@ -58,21 +58,6 @@ class ValidateTest extends TestCase self::assertFalse($result); } - /** @test */ - public function usesFilteredValueForValidation() - { - $field = \Mockery::mock(Field::class)->makePartial(); - $field->shouldReceive('filter')->with('value', [ 'f' => 'value' ])->once()->andReturn(42); - $field->shouldReceive('validate')->with(42, [ 'f' => 'value' ])->once()->andReturn(true); - - $gate = new Gate([ 'f' => 'value' ]); - $gate->addField('f', $field); - - $result = $gate->validate(); - - self::assertTrue($result); - } - /** @test */ public function validatesGivenArray() { diff --git a/tests/Validator/CommonTest.php b/tests/Validator/CommonTest.php new file mode 100644 index 0000000000000000000000000000000000000000..04e1a800aec64c88fe2bb213d5ef08585000b012 --- /dev/null +++ b/tests/Validator/CommonTest.php @@ -0,0 +1,19 @@ +<?php + +namespace Verja\Test\Validator; + +use Verja\Test\TestCase; +use Verja\Validator\NotEmpty; + +class CommonTest extends TestCase +{ + /** @test */ + public function validatorIsInvokable() + { + $validator = new NotEmpty(); + + $result = $validator('foo'); + + self::assertTrue($result); + } +}