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 {