From 8c529c433e44714c28183754ea59e7aa5c059e07 Mon Sep 17 00:00:00 2001 From: Henrique Moody Date: Thu, 24 May 2018 07:47:09 +0200 Subject: [PATCH] Refactor ValidationException Make the ValidationException a little bit less mutable than before. All its dependencies are now passed into the constructor. This commit also make the Factory pass the translator to the exceptions allowing to define the translator before the exception gets created. This change is not the ideal one, later I would like to not need the Singleton from the Factory to do that, but for now it seems like a good approach. One more thing that this commit does is to introduce the "id" for Exceptions. Key can be either the defined "name" or the name of the rule that throwed the exception. This method will be handy to identify exceptions better. Signed-off-by: Henrique Moody --- docs/README.md | 16 +- library/Exceptions/AlphaException.php | 2 +- library/Exceptions/AttributeException.php | 2 +- library/Exceptions/CreditCardException.php | 2 +- library/Exceptions/DateTimeException.php | 2 +- .../Exceptions/GroupedValidationException.php | 2 +- library/Exceptions/IpException.php | 2 +- library/Exceptions/KeySetException.php | 2 +- library/Exceptions/KeyValueException.php | 2 +- library/Exceptions/LengthException.php | 2 +- library/Exceptions/MaxException.php | 2 +- library/Exceptions/MinException.php | 2 +- library/Exceptions/NotBlankException.php | 8 +- library/Exceptions/NotEmptyException.php | 8 +- library/Exceptions/NotOptionalException.php | 8 +- library/Exceptions/NullableException.php | 8 +- library/Exceptions/OptionalException.php | 4 +- library/Exceptions/SizeException.php | 2 +- library/Exceptions/ValidationException.php | 286 +++++++----------- library/Exceptions/VideoUrlException.php | 4 +- library/Factory.php | 56 +++- library/Rules/Base.php | 4 +- library/Rules/KeyValue.php | 3 +- library/Rules/Not.php | 7 +- library/Validator.php | 2 +- tests/integration/exception_update.phpt | 8 +- tests/integration/translator-assert.phpt | 8 +- tests/integration/translator-check.phpt | 8 +- tests/library/RuleTestCase.php | 19 +- tests/unit/Exceptions/CheckExceptionsTest.php | 13 +- .../NestedValidationExceptionTest.php | 16 +- .../Exceptions/ValidationExceptionTest.php | 192 +++++++++--- tests/unit/FactoryTest.php | 28 +- tests/unit/Rules/AbstractRuleTest.php | 2 +- tests/unit/Rules/BaseTest.php | 2 +- 35 files changed, 417 insertions(+), 317 deletions(-) diff --git a/docs/README.md b/docs/README.md index 40477e52..3d96de0d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -132,18 +132,14 @@ $usernameValidator->validate('#$%'); //false - All validation exceptions extend this class - Available methods: - `getMessage()`; - - `setMode($mode)`; - - `setName($name)`; - - `setParam($name, $value)`; - - `setTemplate($template)`; - - more... + - `updateMode($mode)`; + - `updateTemplate($template)`; - `Respect\Validation\Exceptions\NestedValidationException`: - Extends the `Respect\Validation\Exceptions\ValidationException` class - Usually thrown when the `assert()` fails - Available methods: - `getFullMessage()`; - `getMessages()`; - - more... ## Informative exceptions @@ -199,20 +195,18 @@ Array You're also able to translate your message to another language with Validation. The only thing one must do is to define the param `translator` as a callable that -will handle the translation: +will handle the translation overwriting the default factory: ```php -$exception->setParam('translator', 'gettext'); +Factory::setDefaultInstance(new Factory([], [], 'gettext')); ``` The example above uses `gettext()` but you can use any other callable value, like `[$translator, 'trans']` or `you_custom_function()`. -After that, if you call `getMessage()` or `getFullMessage()` (for nested), +After that, if you call `getMessage()`, `getMessages()`, or `getFullMessage()`, the message will be translated. -Note that `getMessage()` will keep the original message. - ## Custom rules You can also create and use your own rules. To do this, you will need to create diff --git a/library/Exceptions/AlphaException.php b/library/Exceptions/AlphaException.php index 2cf61d2e..522a9c7c 100644 --- a/library/Exceptions/AlphaException.php +++ b/library/Exceptions/AlphaException.php @@ -28,7 +28,7 @@ class AlphaException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { return $this->getParam('additionalChars') ? static::EXTRA : static::STANDARD; } diff --git a/library/Exceptions/AttributeException.php b/library/Exceptions/AttributeException.php index 6e131193..e9e95a15 100644 --- a/library/Exceptions/AttributeException.php +++ b/library/Exceptions/AttributeException.php @@ -29,7 +29,7 @@ class AttributeException extends NestedValidationException implements NonOmissib ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { return $this->getParam('hasReference') ? static::INVALID : static::NOT_PRESENT; } diff --git a/library/Exceptions/CreditCardException.php b/library/Exceptions/CreditCardException.php index 982fd745..8eace636 100644 --- a/library/Exceptions/CreditCardException.php +++ b/library/Exceptions/CreditCardException.php @@ -28,7 +28,7 @@ class CreditCardException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { if (!$this->getParam('brand')) { return static::STANDARD; diff --git a/library/Exceptions/DateTimeException.php b/library/Exceptions/DateTimeException.php index dd29d237..08db8a29 100644 --- a/library/Exceptions/DateTimeException.php +++ b/library/Exceptions/DateTimeException.php @@ -38,7 +38,7 @@ final class DateTimeException extends ValidationException /** * {@inheritdoc} */ - public function chooseTemplate(): string + protected function chooseTemplate(): string { return $this->getParam('format') ? static::FORMAT : static::STANDARD; } diff --git a/library/Exceptions/GroupedValidationException.php b/library/Exceptions/GroupedValidationException.php index 817f3655..3dbfe758 100644 --- a/library/Exceptions/GroupedValidationException.php +++ b/library/Exceptions/GroupedValidationException.php @@ -29,7 +29,7 @@ class GroupedValidationException extends NestedValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { $numRules = $this->getParam('passed'); $numFailed = $this->getRelated()->count(); diff --git a/library/Exceptions/IpException.php b/library/Exceptions/IpException.php index 4ca458b9..9c833b28 100644 --- a/library/Exceptions/IpException.php +++ b/library/Exceptions/IpException.php @@ -28,7 +28,7 @@ class IpException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { if (!$this->getParam('networkRange')) { return static::STANDARD; diff --git a/library/Exceptions/KeySetException.php b/library/Exceptions/KeySetException.php index a8762a38..c7304a22 100644 --- a/library/Exceptions/KeySetException.php +++ b/library/Exceptions/KeySetException.php @@ -36,7 +36,7 @@ class KeySetException extends GroupedValidationException implements NonOmissible /** * {@inheritdoc} */ - public function chooseTemplate(): string + protected function chooseTemplate(): string { if (0 === $this->getRelated()->count()) { return static::STRUCTURE; diff --git a/library/Exceptions/KeyValueException.php b/library/Exceptions/KeyValueException.php index 5d2ae955..1df28017 100644 --- a/library/Exceptions/KeyValueException.php +++ b/library/Exceptions/KeyValueException.php @@ -28,7 +28,7 @@ class KeyValueException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { return $this->getParam('component') ? static::COMPONENT : static::STANDARD; } diff --git a/library/Exceptions/LengthException.php b/library/Exceptions/LengthException.php index 6328067e..f470bb30 100644 --- a/library/Exceptions/LengthException.php +++ b/library/Exceptions/LengthException.php @@ -35,7 +35,7 @@ class LengthException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { if (!$this->getParam('minValue')) { return static::GREATER; diff --git a/library/Exceptions/MaxException.php b/library/Exceptions/MaxException.php index c22e25cb..c78ffc63 100644 --- a/library/Exceptions/MaxException.php +++ b/library/Exceptions/MaxException.php @@ -28,7 +28,7 @@ class MaxException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { return $this->getParam('inclusive') ? static::INCLUSIVE : static::STANDARD; } diff --git a/library/Exceptions/MinException.php b/library/Exceptions/MinException.php index 491e8734..abab852b 100644 --- a/library/Exceptions/MinException.php +++ b/library/Exceptions/MinException.php @@ -28,7 +28,7 @@ class MinException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { return $this->getParam('inclusive') ? static::INCLUSIVE : static::STANDARD; } diff --git a/library/Exceptions/NotBlankException.php b/library/Exceptions/NotBlankException.php index db897784..1553fd9c 100644 --- a/library/Exceptions/NotBlankException.php +++ b/library/Exceptions/NotBlankException.php @@ -28,8 +28,12 @@ class NotBlankException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { - return $this->hasName() ? static::NAMED : static::STANDARD; + if ($this->getParam('input') || $this->getParam('name')) { + return self::NAMED; + } + + return self::STANDARD; } } diff --git a/library/Exceptions/NotEmptyException.php b/library/Exceptions/NotEmptyException.php index 4274987f..6fb60454 100644 --- a/library/Exceptions/NotEmptyException.php +++ b/library/Exceptions/NotEmptyException.php @@ -32,8 +32,12 @@ final class NotEmptyException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { - return $this->hasName() ? static::NAMED : static::STANDARD; + if ($this->getParam('input') || $this->getParam('name')) { + return self::NAMED; + } + + return self::STANDARD; } } diff --git a/library/Exceptions/NotOptionalException.php b/library/Exceptions/NotOptionalException.php index bbe97e80..a691cdca 100644 --- a/library/Exceptions/NotOptionalException.php +++ b/library/Exceptions/NotOptionalException.php @@ -28,8 +28,12 @@ class NotOptionalException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { - return $this->hasName() ? static::NAMED : static::STANDARD; + if ($this->getParam('input') || $this->getParam('name')) { + return self::NAMED; + } + + return self::STANDARD; } } diff --git a/library/Exceptions/NullableException.php b/library/Exceptions/NullableException.php index 2149ac01..94865b69 100644 --- a/library/Exceptions/NullableException.php +++ b/library/Exceptions/NullableException.php @@ -37,8 +37,12 @@ final class NullableException extends ValidationException /** * {@inheritdoc} */ - public function chooseTemplate(): string + protected function chooseTemplate(): string { - return $this->hasName() ? static::NAMED : static::STANDARD; + if ($this->getParam('input') || $this->getParam('name')) { + return self::NAMED; + } + + return self::STANDARD; } } diff --git a/library/Exceptions/OptionalException.php b/library/Exceptions/OptionalException.php index 5d684716..54f6f627 100644 --- a/library/Exceptions/OptionalException.php +++ b/library/Exceptions/OptionalException.php @@ -28,8 +28,8 @@ class OptionalException extends ValidationException ], ]; - public function chooseTemplate(): string + protected function chooseTemplate(): string { - return $this->hasName() ? static::NAMED : static::STANDARD; + return $this->getParam('name') ? static::NAMED : static::STANDARD; } } diff --git a/library/Exceptions/SizeException.php b/library/Exceptions/SizeException.php index 09d85a2d..89cb5866 100644 --- a/library/Exceptions/SizeException.php +++ b/library/Exceptions/SizeException.php @@ -43,7 +43,7 @@ class SizeException extends NestedValidationException /** * {@inheritdoc} */ - public function chooseTemplate(): string + protected function chooseTemplate(): string { if (!$this->getParam('minValue')) { return static::GREATER; diff --git a/library/Exceptions/ValidationException.php b/library/Exceptions/ValidationException.php index 77fe02a0..7d5d000c 100644 --- a/library/Exceptions/ValidationException.php +++ b/library/Exceptions/ValidationException.php @@ -14,10 +14,15 @@ declare(strict_types=1); namespace Respect\Validation\Exceptions; use InvalidArgumentException; -use function in_array; -use function is_numeric; +use function call_user_func; use function Respect\Stringifier\stringify; +/** + * Default exception class for rule validations. + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ class ValidationException extends InvalidArgumentException implements Exception { public const MODE_DEFAULT = 'default'; @@ -38,14 +43,114 @@ class ValidationException extends InvalidArgumentException implements Exception ], ]; - protected $id = 'validation'; - protected $mode = self::MODE_DEFAULT; - protected $name = ''; - protected $template = ''; - protected $params = []; - private $customTemplate = false; + /** + * @var mixed + */ + private $input; - public static function format($template, array $vars = []) + /** + * @var string + */ + private $id; + + /** + * @var string + */ + private $mode = self::MODE_DEFAULT; + + /** + * @var array + */ + private $params = []; + + /** + * @var callable + */ + private $translator; + + /** + * @var string + */ + private $template; + + public function __construct($input, string $id, array $params, callable $translator) + { + $this->input = $input; + $this->id = $id; + $this->params = $params; + $this->translator = $translator; + $this->template = $this->chooseTemplate(); + $this->message = $this->createMessage(); + } + + public function getId(): string + { + return $this->id; + } + + public function getParams(): array + { + return $this->params; + } + + public function getParam($name) + { + return $this->params[$name] ?? null; + } + + public function updateMode(string $mode): void + { + $this->mode = $mode; + $this->message = $this->createMessage(); + } + + public function updateTemplate(string $template): void + { + $this->template = $template; + $this->message = $this->createMessage(); + } + + public function updateParams(array $params): void + { + $this->params = $params; + $this->message = $this->createMessage(); + } + + public function hasCustomTemplate(): bool + { + return false === isset(static::$defaultTemplates[$this->mode][$this->template]); + } + + public function __toString(): string + { + return $this->getMessage(); + } + + protected function chooseTemplate(): string + { + return key(static::$defaultTemplates[$this->mode]); + } + + private function createMessage(): string + { + $template = $this->createTemplate($this->mode, $this->template); + $params = $this->getParams(); + $params['name'] = $params['name'] ?? stringify($this->input); + $params['input'] = $this->input; + + return $this->format($template, $params); + } + + private function createTemplate(string $mode, string $template): string + { + if (isset(static::$defaultTemplates[$mode][$template])) { + $template = static::$defaultTemplates[$mode][$template]; + } + + return call_user_func($this->translator, $template); + } + + private function format($template, array $vars = []): string { return preg_replace_callback( '/{{(\w+)}}/', @@ -64,167 +169,4 @@ class ValidationException extends InvalidArgumentException implements Exception $template ); } - - public function __toString() - { - return $this->getMainMessage(); - } - - public function chooseTemplate(): string - { - return key(static::$defaultTemplates[$this->mode]); - } - - public function configure($name, array $params = []) - { - $this->setName($name); - $this->setId($this->guessId()); - $this->setParams($params); - - return $this; - } - - public function getName() - { - return $this->name; - } - - protected function hasName(): bool - { - $name = $this->getName(); - if (is_numeric($name)) { - return (bool) (float) $name; - } - - return !in_array($name, ['`FALSE`', '`NULL`', '`{ }`', '""', '']); - } - - public function getId() - { - return $this->id; - } - - public function getMainMessage() - { - $vars = $this->getParams(); - $vars['name'] = $this->getName(); - $template = $this->getTemplate(); - if (isset($vars['translator']) && is_callable($vars['translator'])) { - $template = call_user_func($vars['translator'], $template); - } - - return static::format($template, $vars); - } - - public function getParam($name) - { - return $this->hasParam($name) ? $this->params[$name] : false; - } - - public function getParams() - { - return $this->params; - } - - public function getTemplate() - { - if (!empty($this->template)) { - return $this->template; - } - - return $this->template = $this->buildTemplate(); - } - - public function hasParam($name) - { - return isset($this->params[$name]); - } - - public function setId($id) - { - $this->id = $id; - - return $this; - } - - public function setName($name) - { - $this->name = $name; - - return $this; - } - - public function setMode($mode) - { - $this->mode = $mode; - $this->template = $this->buildTemplate(); - - $this->buildMessage(); - - return $this; - } - - public function setParam($key, $value) - { - $this->params[$key] = $value; - - $this->buildMessage(); - - return $this; - } - - public function setParams(array $params) - { - foreach ($params as $key => $value) { - $this->params[$key] = $value; - } - - $this->buildMessage(); - - return $this; - } - - public function hasCustomTemplate() - { - return true === $this->customTemplate; - } - - public function setTemplate($template) - { - $this->customTemplate = true; - if (isset(static::$defaultTemplates[$this->mode][$template])) { - $template = static::$defaultTemplates[$this->mode][$template]; - } - $this->template = $template; - - $this->buildMessage(); - - return $this; - } - - private function buildMessage(): void - { - $this->message = $this->getMainMessage(); - } - - protected function buildTemplate() - { - $templateKey = $this->chooseTemplate(); - - return static::$defaultTemplates[$this->mode][$templateKey]; - } - - public function guessId() - { - if (!empty($this->id) && 'validation' != $this->id) { - return $this->id; - } - - $pieces = explode('\\', get_called_class()); - $exceptionClassShortName = end($pieces); - $ruleClassShortName = str_replace('Exception', '', $exceptionClassShortName); - $ruleName = lcfirst($ruleClassShortName); - - return $ruleName; - } } diff --git a/library/Exceptions/VideoUrlException.php b/library/Exceptions/VideoUrlException.php index 82b81cff..79dd3098 100644 --- a/library/Exceptions/VideoUrlException.php +++ b/library/Exceptions/VideoUrlException.php @@ -31,9 +31,9 @@ class VideoUrlException extends ValidationException /** * {@inheritdoc} */ - public function chooseTemplate(): string + protected function chooseTemplate(): string { - if (false !== $this->getParam('service')) { + if ($this->getParam('service')) { return self::SERVICE; } diff --git a/library/Factory.php b/library/Factory.php index 40b5a349..ce15bbc7 100644 --- a/library/Factory.php +++ b/library/Factory.php @@ -14,6 +14,7 @@ declare(strict_types=1); namespace Respect\Validation; use ReflectionClass; +use ReflectionException; use ReflectionObject; use Respect\Validation\Exceptions\ComponentException; use Respect\Validation\Exceptions\InvalidClassException; @@ -22,7 +23,7 @@ use function array_map; use function array_merge; use function array_unique; use function class_exists; -use function Respect\Stringifier\stringify; +use function lcfirst; /** * Factory of objects. @@ -58,6 +59,11 @@ final class Factory */ private $exceptionsNamespaces = []; + /** + * @var callable + */ + private $translator; + /** * Initializes the factory with the defined namespaces. * @@ -67,10 +73,14 @@ final class Factory * @param string[] $rulesNamespaces * @param string[] $exceptionsNamespaces */ - public function __construct(array $rulesNamespaces, array $exceptionsNamespaces) - { + public function __construct( + array $rulesNamespaces, + array $exceptionsNamespaces, + callable $translator + ) { $this->rulesNamespaces = $this->filterNamespaces($rulesNamespaces, self::DEFAULT_RULES_NAMESPACES); $this->exceptionsNamespaces = $this->filterNamespaces($exceptionsNamespaces, self::DEFAULT_EXCEPTIONS_NAMESPACES); + $this->translator = $translator; } /** @@ -91,7 +101,13 @@ final class Factory public static function getDefaultInstance(): self { if (null === self::$defaultInstance) { - self::$defaultInstance = new self(self::DEFAULT_RULES_NAMESPACES, self::DEFAULT_EXCEPTIONS_NAMESPACES); + self::$defaultInstance = new self( + self::DEFAULT_RULES_NAMESPACES, + self::DEFAULT_EXCEPTIONS_NAMESPACES, + function (string $message): string { + return $message; + } + ); } return self::$defaultInstance; @@ -137,18 +153,21 @@ final class Factory { $reflection = new ReflectionObject($validatable); $ruleName = $reflection->getShortName(); - $name = $validatable->getName() ?: stringify($input); $params = ['input' => $input] + $extraParams + $this->extractPropertiesValues($validatable, $reflection); + $id = lcfirst($ruleName); + if ($validatable->getName()) { + $id = $params['name'] = $validatable->getName(); + } foreach ($this->exceptionsNamespaces as $namespace) { $exceptionName = sprintf('%s\\%sException', $namespace, $ruleName); if (!class_exists($exceptionName)) { continue; } - return $this->createValidationException($exceptionName, $name, $params); + return $this->createValidationException($exceptionName, $id, $input, $params); } - return $this->createValidationException(ValidationException::class, $name, $params); + return $this->createValidationException(ValidationException::class, $id, $input, $params); } /** @@ -159,6 +178,7 @@ final class Factory * @param string $parentName * * @throws InvalidClassException + * @throws ReflectionException * * @return ReflectionClass */ @@ -204,17 +224,22 @@ final class Factory * Creates a Validation exception. * * @param string $exceptionName - * @param mixed $name + * @param string $id + * @param mixed $input * @param array $params * + * @throws InvalidClassException + * @throws ReflectionException + * * @return ValidationException */ - private function createValidationException(string $exceptionName, $name, array $params): ValidationException + private function createValidationException(string $exceptionName, string $id, $input, array $params): ValidationException { - $exception = $this->createReflectionClass($exceptionName, ValidationException::class)->newInstance(); - $exception->configure($name, $params); + /* @var ValidationException $exception */ + $exception = $this->createReflectionClass($exceptionName, ValidationException::class) + ->newInstance($input, $id, $params, $this->translator); if (isset($params['template'])) { - $exception->setTemplate($params['template']); + $exception->updateTemplate($params['template']); } return $exception; @@ -232,7 +257,12 @@ final class Factory foreach ($reflection->getProperties() as $property) { $property->setAccessible(true); - $values[$property->getName()] = $property->getValue($validatable); + $propertyValue = $property->getValue($validatable); + if (null === $propertyValue) { + continue; + } + + $values[$property->getName()] = $propertyValue; } if (($parentReflection = $reflection->getParentClass())) { diff --git a/library/Rules/Base.php b/library/Rules/Base.php index 37c83476..b671af08 100644 --- a/library/Rules/Base.php +++ b/library/Rules/Base.php @@ -13,7 +13,7 @@ declare(strict_types=1); namespace Respect\Validation\Rules; -use Respect\Validation\Exceptions\BaseException; +use Respect\Validation\Exceptions\ComponentException; class Base extends AbstractRule { @@ -28,7 +28,7 @@ class Base extends AbstractRule $max = mb_strlen($this->chars); if (!is_numeric($base) || $base > $max) { - throw new BaseException(sprintf('a base between 1 and %s is required', $max)); + throw new ComponentException(sprintf('a base between 1 and %s is required', $max)); } $this->base = $base; } diff --git a/library/Rules/KeyValue.php b/library/Rules/KeyValue.php index a1b03fb5..3412fcdd 100644 --- a/library/Rules/KeyValue.php +++ b/library/Rules/KeyValue.php @@ -60,8 +60,9 @@ class KeyValue extends AbstractRule $params[$key] = $this->baseKey; } + $params['name'] = $this->comparedKey; - $exception->configure($this->comparedKey, $params); + $exception->updateParams($params); return $exception; } diff --git a/library/Rules/Not.php b/library/Rules/Not.php index accf2055..84f8c0f6 100644 --- a/library/Rules/Not.php +++ b/library/Rules/Not.php @@ -48,9 +48,10 @@ class Not extends AbstractRule $rule = $this->absorbAllOf($rule, $input); } - throw $rule - ->reportError($input) - ->setMode(ValidationException::MODE_NEGATIVE); + $exception = $rule->reportError($input); + $exception->updateMode(ValidationException::MODE_NEGATIVE); + + throw $exception; } private function absorbAllOf(AllOf $rule, $input) diff --git a/library/Validator.php b/library/Validator.php index 1dda6756..7cb1c94e 100644 --- a/library/Validator.php +++ b/library/Validator.php @@ -170,7 +170,7 @@ class Validator extends AllOf parent::check($input); } catch (ValidationException $exception) { if (1 == count($this->getRules()) && $this->template) { - $exception->setTemplate($this->template); + $exception->updateTemplate($this->template); } throw $exception; diff --git a/tests/integration/exception_update.phpt b/tests/integration/exception_update.phpt index bc6c77f4..486f9895 100644 --- a/tests/integration/exception_update.phpt +++ b/tests/integration/exception_update.phpt @@ -3,14 +3,16 @@ require 'vendor/autoload.php'; +use Respect\Validation\Factory; use Respect\Validation\Validator as v; +Factory::setDefaultInstance(new Factory([], [], function (string $message): string { + return '{{name}} não deve conter letras (a-z) ou dígitos (0-9)'; +})); + try { v::not(v::alnum())->check('abc123'); } catch (Exception $exception) { - $exception->setParam('translator', function () { - return '{{name}} não deve conter letras (a-z) ou dígitos (0-9)'; - }); echo $exception->getMessage(); } ?> diff --git a/tests/integration/translator-assert.phpt b/tests/integration/translator-assert.phpt index d32dfcbd..e59fbc5a 100644 --- a/tests/integration/translator-assert.phpt +++ b/tests/integration/translator-assert.phpt @@ -3,10 +3,10 @@ require 'vendor/autoload.php'; use Respect\Validation\Exceptions\NestedValidationException; +use Respect\Validation\Factory; use Respect\Validation\Validator; -function translatorCallback($message) -{ +Factory::setDefaultInstance(new Factory([], [], function (string $message): string { $messages = [ 'All of the required rules must pass for {{name}}' => 'Todas as regras requeridas devem passar para {{name}}', '{{name}} must be of type string' => '{{name}} deve ser do tipo string', @@ -14,13 +14,11 @@ function translatorCallback($message) ]; return $messages[$message]; -} +})); try { Validator::stringType()->length(2, 15)->assert(0); } catch (NestedValidationException $exception) { - $exception->setParam('translator', 'translatorCallback'); - echo $exception->getFullMessage(); } ?> diff --git a/tests/integration/translator-check.phpt b/tests/integration/translator-check.phpt index 430d79a4..fd8edb60 100644 --- a/tests/integration/translator-check.phpt +++ b/tests/integration/translator-check.phpt @@ -3,22 +3,20 @@ require 'vendor/autoload.php'; use Respect\Validation\Exceptions\ValidationException; +use Respect\Validation\Factory; use Respect\Validation\Validator; -function translatorCallback($message) -{ +Factory::setDefaultInstance(new Factory([], [], function (string $message): string { $messages = [ '{{name}} must be of type string' => '{{name}} deve ser do tipo string', ]; return $messages[$message]; -} +})); try { Validator::stringType()->length(2, 15)->check(0); } catch (ValidationException $exception) { - $exception->setParam('translator', 'translatorCallback'); - echo $exception->getMessage(); } ?> diff --git a/tests/library/RuleTestCase.php b/tests/library/RuleTestCase.php index 8a409264..d8cdf3d8 100644 --- a/tests/library/RuleTestCase.php +++ b/tests/library/RuleTestCase.php @@ -16,6 +16,7 @@ namespace Respect\Validation\Test; use PHPUnit\Framework\TestCase; use Respect\Validation\Exceptions\ValidationException; use Respect\Validation\Validatable; +use function sprintf; /** * Abstract class to create TestCases for Rules. @@ -88,14 +89,28 @@ abstract class RuleTestCase extends TestCase ->method('assert') ->willReturn($expectedResult); } else { + $checkException = new ValidationException( + 'validatable', + 'input', + [], + 'trim' + ); + $checkException->updateTemplate(sprintf('Exception for %s:check() method', $mockClassName)); $validatableMocked ->expects($this->any()) ->method('check') - ->willThrowException(new ValidationException('Exception for '.$mockClassName.':check() method')); + ->willThrowException($checkException); + $assertException = new ValidationException( + 'validatable', + 'input', + [], + 'trim' + ); + $assertException->updateTemplate(sprintf('Exception for %s:assert() method', $mockClassName)); $validatableMocked ->expects($this->any()) ->method('assert') - ->willThrowException(new ValidationException('Exception for '.$mockClassName.':assert() method')); + ->willThrowException($assertException); } return $validatableMocked; diff --git a/tests/unit/Exceptions/CheckExceptionsTest.php b/tests/unit/Exceptions/CheckExceptionsTest.php index 5818251f..099e9f01 100644 --- a/tests/unit/Exceptions/CheckExceptionsTest.php +++ b/tests/unit/Exceptions/CheckExceptionsTest.php @@ -61,16 +61,9 @@ class CheckExceptionsTest extends TestCase sprintf('Expected exception class to exist: %s.', $ruleName) ); - $expectedMessage = 'Test exception message.'; - $exceptionObject = new $exceptionClass($expectedMessage); - self::assertInstanceOf( - 'Exception', - $exceptionObject, - 'Every exception should extend an Exception class.' - ); - self::assertInstanceOf( - ValidationException::class, - $exceptionObject, + $reflectionClass = new ReflectionClass($exceptionClass); + self::assertTrue( + $reflectionClass->isSubclassOf(ValidationException::class), 'Every Respect/Validation exception must extend ValidationException.' ); } diff --git a/tests/unit/Exceptions/NestedValidationExceptionTest.php b/tests/unit/Exceptions/NestedValidationExceptionTest.php index 891a4dad..2d78bd99 100644 --- a/tests/unit/Exceptions/NestedValidationExceptionTest.php +++ b/tests/unit/Exceptions/NestedValidationExceptionTest.php @@ -15,20 +15,12 @@ namespace Respect\Validation\Exceptions; use PHPUnit\Framework\TestCase; -/** - * phpunit has an issue with mocking exceptions when in HHVM: - * https://github.com/sebastianbergmann/phpunit-mock-objects/issues/207. - */ -class PrivateNestedValidationException extends NestedValidationException -{ -} - class NestedValidationExceptionTest extends TestCase { public function testGetRelatedShouldReturnExceptionAddedByAddRelated(): void { - $composite = new AttributeException(); - $node = new IntValException(); + $composite = new AttributeException('input', 'id', [], 'trim'); + $node = new IntValException('input', 'id', [], 'trim'); $composite->addRelated($node); self::assertEquals(1, count($composite->getRelated(true))); self::assertContainsOnly($node, $composite->getRelated()); @@ -36,8 +28,8 @@ class NestedValidationExceptionTest extends TestCase public function testAddingTheSameInstanceShouldAddJustASingleReference(): void { - $composite = new AttributeException(); - $node = new IntValException(); + $composite = new AttributeException('input', 'id', [], 'trim'); + $node = new IntValException('input', 'id', [], 'trim'); $composite->addRelated($node); $composite->addRelated($node); $composite->addRelated($node); diff --git a/tests/unit/Exceptions/ValidationExceptionTest.php b/tests/unit/Exceptions/ValidationExceptionTest.php index 195bc7e8..02884880 100644 --- a/tests/unit/Exceptions/ValidationExceptionTest.php +++ b/tests/unit/Exceptions/ValidationExceptionTest.php @@ -15,62 +15,180 @@ namespace Respect\Validation\Exceptions; use PHPUnit\Framework\TestCase; -class ValidationExceptionTest extends TestCase +/** + * @group core + * @covers \Respect\Validation\Exceptions\ValidationException + * + * @author Alexandre Gomes Gaigalas + * @author Henrique Moody + */ +final class ValidationExceptionTest extends TestCase { - public function testItImplementsExceptionInterface(): void + /** + * @test + */ + public function itShouldImplementException(): void { - $validationException = new ValidationException(); - self::assertInstanceOf(Exception::class, $validationException); + $sut = new ValidationException('input', 'id', [], 'trim'); + + self::assertInstanceOf(Exception::class, $sut); } /** - * @dataProvider providerForFormat + * @test */ - public function testFormatShouldReplacePlaceholdersProperly($template, $result, $vars): void + public function itShouldRetrieveId(): void { + $id = 'my id'; + $sut = new ValidationException('input', $id, [], 'trim'); + + self::assertSame($id, $sut->getId()); + } + + /** + * @test + */ + public function itShouldRetrieveParams(): void + { + $params = ['foo' => true, 'bar' => 23]; + + $sut = new ValidationException('input', 'id', $params, 'trim'); + + self::assertSame($params, $sut->getParams()); + } + + /** + * @test + */ + public function itShouldRetrieveASingleParameter(): void + { + $name = 'any name'; + $value = 'any value'; + + $sut = new ValidationException('input', 'id', [$name => $value], 'trim'); + + self::assertSame($value, $sut->getParam($name)); + } + + /** + * @test + */ + public function itShouldReturnNullWhenParameterCanNotBeFound(): void + { + $sut = new ValidationException('input', 'id', [], 'trim'); + + self::assertNull($sut->getParam('foo')); + } + + /** + * @test + */ + public function itShouldHaveADefaultTemplate(): void + { + $sut = new ValidationException('input', 'id', [], 'trim'); + + self::assertSame('"input" must be valid', $sut->getMessage()); + } + + /** + * @test + */ + public function itShouldUpdateMode(): void + { + $sut = new ValidationException('input', 'id', [], 'trim'); + $sut->updateMode(ValidationException::MODE_NEGATIVE); + + self::assertSame('"input" must not be valid', $sut->getMessage()); + } + + /** + * @test + */ + public function itShouldUpdateTemplate(): void + { + $template = 'This is my new template'; + + $sut = new ValidationException('input', 'id', [], 'trim'); + $sut->updateTemplate($template); + + self::assertEquals($template, $sut->getMessage()); + } + + /** + * @test + */ + public function itShouldTellWhenHasAsCustomUpdateTemplate(): void + { + $sut = new ValidationException('input', 'id', [], 'trim'); + + self::assertFalse($sut->hasCustomTemplate()); + + $sut->updateTemplate('This is my new template'); + + self::assertTrue($sut->hasCustomTemplate()); + } + + /** + * @test + */ + public function itShouldUseTranslator(): void + { + $template = ' This is my new template '; + $expected = trim($template); + + $sut = new ValidationException('input', 'id', [], 'trim'); + $sut->updateTemplate($template); + + self::assertEquals($expected, $sut->getMessage()); + } + + /** + * @test + */ + public function itShouldReplacePlaceholders(): void + { + $sut = new ValidationException('foo', 'id', ['bar' => 1, 'baz' => 2], 'trim'); + $sut->updateTemplate('{{name}} {{bar}} {{baz}}'); + self::assertEquals( - $result, - ValidationException::format($template, $vars) + '"foo" 1 2', + $sut->getMessage() ); } - public function testGetMainMessageShouldApplyTemplatePlaceholders(): void + /** + * @test + */ + public function itShouldKeepPlaceholdersThatCanNotReplace(): void { - $sampleValidationException = new ValidationException(); - $sampleValidationException->configure('foo', ['bar' => 1, 'baz' => 2]); - $sampleValidationException->setTemplate('{{name}} {{bar}} {{baz}}'); + $sut = new ValidationException('foo', 'id', ['foo' => 1], 'trim'); + $sut->updateTemplate('{{name}} {{foo}} {{bar}}'); + self::assertEquals( - 'foo 1 2', - $sampleValidationException->getMainMessage() + '"foo" 1 {{bar}}', + $sut->getMessage() ); } - public function testSettingTemplates(): void + /** + * @test + */ + public function itShouldUpdateParams(): void { - $x = new ValidationException(); - $x->configure('bar'); - $x->setTemplate('foo'); - self::assertEquals('foo', $x->getTemplate()); + $sut = new ValidationException('input', 'id', ['foo' => 1], 'trim'); + $sut->updateTemplate('{{foo}}'); + $sut->updateParams(['foo' => 2]); + + self::assertEquals('2', $sut->getMessage()); } - public function providerForFormat() + /** + * @test + */ + public function itShouldConvertToString(): void { - return [ - [ - '{{foo}} {{bar}} {{baz}}', - '"hello" "world" "respect"', - ['foo' => 'hello', 'bar' => 'world', 'baz' => 'respect'], - ], - [ - '{{foo}} {{bar}} {{baz}}', - '"hello" {{bar}} "respect"', - ['foo' => 'hello', 'baz' => 'respect'], - ], - [ - '{{foo}} {{bar}} {{baz}}', - '"hello" {{bar}} "respect"', - ['foo' => 'hello', 'bot' => 111, 'baz' => 'respect'], - ], - ]; + $sut = new ValidationException('input', 'id', [], 'trim'); + + self::assertSame('"input" must be valid', (string) $sut); } } diff --git a/tests/unit/FactoryTest.php b/tests/unit/FactoryTest.php index 7ea78ba0..4feb8fc5 100644 --- a/tests/unit/FactoryTest.php +++ b/tests/unit/FactoryTest.php @@ -37,7 +37,7 @@ final class FactoryTest extends TestCase */ public function shouldCreateARuleByNameBasedOnNamespace(): void { - $factory = new Factory([self::TEST_RULES_NAMESPACE], []); + $factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim'); self::assertInstanceOf(Valid::class, $factory->rule('valid')); } @@ -47,7 +47,7 @@ final class FactoryTest extends TestCase */ public function shouldLookUpToAllNamespacesUntilRuleIsFound(): void { - $factory = new Factory([__NAMESPACE__, self::TEST_RULES_NAMESPACE], []); + $factory = new Factory([__NAMESPACE__, self::TEST_RULES_NAMESPACE], [], 'trim'); self::assertInstanceOf(Valid::class, $factory->rule('valid')); } @@ -59,7 +59,7 @@ final class FactoryTest extends TestCase { $constructorArguments = [true, false, true, false]; - $factory = new Factory([self::TEST_RULES_NAMESPACE], []); + $factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim'); $rule = $factory->rule('stub', $constructorArguments); self::assertSame($constructorArguments, $rule->validations); @@ -70,7 +70,7 @@ final class FactoryTest extends TestCase */ public function shouldThrowsAnExceptionWhenRuleIsInvalid(): void { - $factory = new Factory([self::TEST_RULES_NAMESPACE], []); + $factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim'); $this->expectException(InvalidClassException::class); $this->expectExceptionMessage(sprintf('"%s" must be an instance of "%s"', Invalid::class, Validatable::class)); @@ -83,7 +83,7 @@ final class FactoryTest extends TestCase */ public function shouldThrowsAnExceptionWhenRuleIsNotInstantiable(): void { - $factory = new Factory([self::TEST_RULES_NAMESPACE], []); + $factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim'); $this->expectException(InvalidClassException::class); $this->expectExceptionMessage(sprintf('"%s" must be instantiable', AbstractClass::class)); @@ -96,7 +96,7 @@ final class FactoryTest extends TestCase */ public function shouldThrowsAnExceptionWhenRuleIsNotFound(): void { - $factory = new Factory([self::TEST_RULES_NAMESPACE], []); + $factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim'); $this->expectException(ComponentException::class); $this->expectExceptionMessage('"notFoundRule" is not a valid rule name'); @@ -109,7 +109,7 @@ final class FactoryTest extends TestCase */ public function shouldCreateExceptionBasedOnRule(): void { - $factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE]); + $factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE], 'trim'); $rule = new Stub(); $input = 2; @@ -122,7 +122,7 @@ final class FactoryTest extends TestCase */ public function shouldLookUpToAllNamespacesUntilExceptionIsCreated(): void { - $factory = new Factory([], [__NAMESPACE__, self::TEST_EXCEPTIONS_NAMESPACE]); + $factory = new Factory([], [__NAMESPACE__, self::TEST_EXCEPTIONS_NAMESPACE], 'trim'); $rule = new Stub(); $input = 2; @@ -135,7 +135,7 @@ final class FactoryTest extends TestCase */ public function shouldCreateValidationExceptionWhenExceptionIsNotFound(): void { - $factory = new Factory([], []); + $factory = new Factory([], [], 'trim'); $input = 'input'; $rule = new Stub(); @@ -147,7 +147,7 @@ final class FactoryTest extends TestCase */ public function shouldSetInputAsParameterOfCreatedException(): void { - $factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE]); + $factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE], 'trim'); $rule = new Stub(); $input = 2; @@ -162,7 +162,7 @@ final class FactoryTest extends TestCase */ public function shouldPassPropertiesToCreatedException(): void { - $factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE]); + $factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE], 'trim'); $validations = [true, false, true, true]; $rule = new Stub(...$validations); @@ -178,7 +178,7 @@ final class FactoryTest extends TestCase */ public function shouldSetTemplateWhenTemplateKeyIsDefined(): void { - $factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE]); + $factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE], 'trim'); $extraParams = [ 'template' => 'This is my template', @@ -190,7 +190,7 @@ final class FactoryTest extends TestCase $exception = $factory->exception($rule, $input, $extraParams); - self::assertSame($extraParams['template'], $exception->getTemplate()); + self::assertSame($extraParams['template'], $exception->getMessage()); } /** @@ -206,7 +206,7 @@ final class FactoryTest extends TestCase */ public function shouldBeAbleToOverwriteDefaultInstance(): void { - $factory = new Factory([], []); + $factory = new Factory([], [], 'trim'); $defaultInstance = Factory::getDefaultInstance(); diff --git a/tests/unit/Rules/AbstractRuleTest.php b/tests/unit/Rules/AbstractRuleTest.php index a53e62b9..c3295ca2 100644 --- a/tests/unit/Rules/AbstractRuleTest.php +++ b/tests/unit/Rules/AbstractRuleTest.php @@ -101,7 +101,7 @@ class AbstractRuleTest extends TestCase ->expects($this->once()) ->method('reportError') ->with($input) - ->will($this->throwException(new ValidationException())); + ->will($this->throwException(new ValidationException($input, 'abstract', [], 'trim'))); $abstractRuleMock->assert($input); } diff --git a/tests/unit/Rules/BaseTest.php b/tests/unit/Rules/BaseTest.php index a02eb2fb..cf74a097 100644 --- a/tests/unit/Rules/BaseTest.php +++ b/tests/unit/Rules/BaseTest.php @@ -46,7 +46,7 @@ class BaseTest extends TestCase /** * @dataProvider providerForExceptionBase - * @expectedException \Respect\Validation\Exceptions\BaseException + * @expectedException \Respect\Validation\Exceptions\ComponentException */ public function testExceptionBase($base, $input): void {