From ab65035181d4f4befeddaaaba795dfa49a3aecf0 Mon Sep 17 00:00:00 2001 From: Andy Wendt Date: Thu, 29 Jan 2015 18:53:20 -0700 Subject: [PATCH] Issue #260: Now using Respect\Validation exceptions only * `ExceptionInterface`: all Respect\Validation\Exceptions implement through inheritance * `ValidatorExceptionInterface`: implemented only by AllOfException. This allows the end users to know when there has been a Validator exception rather than just any of our exceptions. Fixed formatting issues Created two new exception types * Created ValidationExceptionInterface * Created NestedValidationExceptionInterface which extends ValidationExceptionInterface * Renamed from ValidatorExceptionInterface * ValidationException implements ValidationExceptionInterface and ValidationExceptionTest checks for the implementation. * AbstractNestedException implements NestedValidationExceptionInterface and AbstractNestedExceptionTest checks for the implementation. * CheckExceptionsTest now checks all Rule exceptions to make sure they implement ValidationExceptionInterface * ValidatorTest now contains test that shows that only ValidationExceptionInterface can be used reliably with `::check()` * Updated documentation for new exception types * Reworked examples to show how to catch the exception interfaces Minor changes in readme.md and ExceptionInterfaces * Removed `import` statements (hahaha) * Renamed `$e` to `$exception` * `ValidationExceptionInterface` now extends `ExceptionInterface`. Changed `ValidationException` to match --- README.md | 32 ++++++++++---- .../Exceptions/AbstractNestedException.php | 2 +- library/Exceptions/ComponentException.php | 2 +- library/Exceptions/ExceptionInterface.php | 6 +++ .../NestedValidationExceptionInterface.php | 8 ++++ library/Exceptions/ValidationException.php | 3 +- .../ValidationExceptionInterface.php | 7 ++++ .../AbstractNestedExceptionTest.php | 12 ++++-- tests/Exceptions/CheckExceptionsTest.php | 42 ++++++++++++++++++- tests/Exceptions/ValidationExceptionTest.php | 14 ++++++- tests/ValidatorTest.php | 27 +++++++++--- 11 files changed, 132 insertions(+), 23 deletions(-) create mode 100644 library/Exceptions/ExceptionInterface.php create mode 100644 library/Exceptions/NestedValidationExceptionInterface.php create mode 100644 library/Exceptions/ValidationExceptionInterface.php diff --git a/README.md b/README.md index 544a024c..5e97f17e 100755 --- a/README.md +++ b/README.md @@ -108,16 +108,30 @@ $usernameValidator->validate('alexandre gaigalas'); //false $usernameValidator->validate('#$%'); //false ``` +### Exception Types + +* `Respect\Validation\Exceptions\NestedValidationExceptionInterface`: + * Use when calling `assert()`. + * Interface has three methods: `getFullMessage()`, `findMessages()`, and `getMainMessage()`. +* `Respect\Validation\Exceptions\ValidationExceptionInterface`: + * Use when calling `::check()`. + * All `Respect\Validation` validation exceptions implement this interface. + * Interface has one method: `getMainMessage()`; +* `Repect\Validation\Exceptions\ExceptionInterface`: + * All `Respect\Validation\Exceptions` implement this interface. + ### Informative Exceptions When something goes wrong, Validation can tell you exactly what's going on. For this, we use the `assert()` method instead of `validate()`: ```php +use Respect\Validation\Exceptions\NestedValidationExceptionInterface + try { $usernameValidator->assert('really messed up screen#name'); -} catch(DomainException $e) { - echo $e->getFullMessage(); +} catch(NestedValidationExceptionInterface $exception) { + echo $exception->getFullMessage(); } ``` @@ -134,10 +148,12 @@ The text tree is fine, but unusable on a HTML form or something more custom. You `findMessages()` for that: ```php +use Respect\Validation\Exceptions\NestedValidationExceptionInterface + try { $usernameValidator->assert('really messed up screen#name'); -} catch(\InvalidArgumentException $e) { - var_dump($e->findMessages(array('alnum', 'length', 'noWhitespace'))); +} catch(NestedValidationExceptionInterface $exception) { + var_dump($exception->findMessages(array('alnum', 'length', 'noWhitespace'))); } ``` @@ -149,7 +165,7 @@ Getting messages as an array is fine, but sometimes you need to customize them i to present them to the user. This is possible using the `findMessages()` method as well: ```php -$errors = $e->findMessages(array( +$errors = $exception->findMessages(array( 'alnum' => '{{name}} must contain only letters and digits', 'length' => '{{name}} must not have more than 15 chars', 'noWhitespace' => '{{name}} cannot contain spaces' @@ -183,10 +199,12 @@ validation report. There is also a `check()` method that returns an Exception only with the first error found: ```php +use Respect\Validation\Exceptions\ValidationExceptionInterface + try { $usernameValidator->check('really messed up screen#name'); -} catch(\InvalidArgumentException $e) { - echo $e->getMainMessage(); +} catch(ValidationExceptionInterface $exception) { + echo $exception->getMainMessage(); } ``` diff --git a/library/Exceptions/AbstractNestedException.php b/library/Exceptions/AbstractNestedException.php index 1339cce3..8b911ab6 100644 --- a/library/Exceptions/AbstractNestedException.php +++ b/library/Exceptions/AbstractNestedException.php @@ -5,7 +5,7 @@ use RecursiveIteratorIterator; use RecursiveTreeIterator; use Respect\Validation\ExceptionIterator; -class AbstractNestedException extends ValidationException +class AbstractNestedException extends ValidationException implements NestedValidationExceptionInterface { const ITERATE_TREE = 1; const ITERATE_ALL = 2; diff --git a/library/Exceptions/ComponentException.php b/library/Exceptions/ComponentException.php index 55c20f19..5de4655b 100644 --- a/library/Exceptions/ComponentException.php +++ b/library/Exceptions/ComponentException.php @@ -3,6 +3,6 @@ namespace Respect\Validation\Exceptions; use Exception; -class ComponentException extends Exception +class ComponentException extends Exception implements ExceptionInterface { } diff --git a/library/Exceptions/ExceptionInterface.php b/library/Exceptions/ExceptionInterface.php new file mode 100644 index 00000000..3d95411c --- /dev/null +++ b/library/Exceptions/ExceptionInterface.php @@ -0,0 +1,6 @@ +getMock('Respect\Validation\Exceptions\AbstractNestedException'); + $this->assertInstanceOf('Respect\Validation\Exceptions\NestedValidationExceptionInterface', + $abstractNestedException); + } + public function testGetRelatedShouldReturnExceptionAddedByAddRelated() { $composite = new AttributeException; @@ -75,7 +82,7 @@ class AbstractNestedExceptionTest extends \PHPUnit_Framework_TestCase 'security_question' => null, ) ); - } catch (ValidationException $e) { + } catch (NestedValidationExceptionInterface $e) { $messages = $e->findMessages( array('allOf', 'first_name.length') ); @@ -114,7 +121,7 @@ class AbstractNestedExceptionTest extends \PHPUnit_Framework_TestCase 'security_question' => null, ) ); - } catch (ValidationException $e) { + } catch (NestedValidationExceptionInterface $e) { $messages = $e->findMessages( array( 'allOf' => 'Invalid {{name}}', @@ -127,4 +134,3 @@ class AbstractNestedExceptionTest extends \PHPUnit_Framework_TestCase } } } - diff --git a/tests/Exceptions/CheckExceptionsTest.php b/tests/Exceptions/CheckExceptionsTest.php index c98c77be..7fb5b48b 100644 --- a/tests/Exceptions/CheckExceptionsTest.php +++ b/tests/Exceptions/CheckExceptionsTest.php @@ -47,7 +47,7 @@ class CheckExceptionsTest extends \PHPUnit_Framework_TestCase $missingExceptions = array(); foreach ($this->getAllRuleNames() as $ruleName) { - $exceptionClass = 'Respect\\Validation\\Exceptions\\'.$ruleName.'Exception'; + $exceptionClass = $this->buildExceptionClass($ruleName); if (class_exists($exceptionClass)) { continue; } @@ -55,6 +55,44 @@ class CheckExceptionsTest extends \PHPUnit_Framework_TestCase $missingExceptions[] = $ruleName; } - $this->assertEmpty($missingExceptions, 'No exceptions for: '.implode(', ', $missingExceptions)); + $this->assertEmpty($missingExceptions, 'No exceptions for: ' . $this->formatArrayAsString($missingExceptions)); + } + + public function testEveryRuleExceptionImplementsValidationExceptionInterface() + { + $exceptionsNotImplementingInterface = array(); + + foreach ($this->getAllRuleNames() as $ruleName) { + $exceptionClass = $this->buildExceptionClass($ruleName); + $exceptionClassMock = $this->getMock($exceptionClass); + if ($exceptionClassMock instanceof ValidationExceptionInterface) { + continue; + } + + $exceptionsNotImplementingInterface[] = $ruleName; + } + + $this->assertEmpty($exceptionsNotImplementingInterface, + 'ValidationExceptionInterface not implemented in: ' . + $this->formatArrayAsString($exceptionsNotImplementingInterface)); + } + + /** + * @param string $ruleName + * @return string + */ + private function buildExceptionClass($ruleName) + { + $exceptionClass = 'Respect\\Validation\\Exceptions\\' . $ruleName . 'Exception'; + return $exceptionClass; + } + + /** + * @param array $array + * @return string + */ + private function formatArrayAsString(array $array) + { + return implode(', ', $array); } } diff --git a/tests/Exceptions/ValidationExceptionTest.php b/tests/Exceptions/ValidationExceptionTest.php index c005dfaa..f8dea2c3 100644 --- a/tests/Exceptions/ValidationExceptionTest.php +++ b/tests/Exceptions/ValidationExceptionTest.php @@ -3,6 +3,19 @@ namespace Respect\Validation\Exceptions; class ValidationExceptionTest extends \PHPUnit_Framework_TestCase { + public function testItImplementsValidationExceptionInterface() + { + $validationException = new ValidationException(); + $this->assertInstanceOf('Respect\Validation\Exceptions\ValidationExceptionInterface', $validationException); + } + + public function testItDoesNotImplementNestedValidationExceptionInterface() + { + $validationException = new ValidationException(); + $this->assertNotInstanceOf('Respect\Validation\Exceptions\NestedValidationExceptionInterface', + $validationException); + } + /** * @dataProvider providerForFormat */ @@ -98,4 +111,3 @@ class ValidationExceptionTest extends \PHPUnit_Framework_TestCase ); } } - diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index c2b8166f..219c4e09 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -1,6 +1,9 @@ setTemplate('{{name}} is not tasty')->assert('something'); - } catch (\Exception $e) { + } catch (NestedValidationExceptionInterface $e) { $this->assertEquals('"something" is not tasty', $e->getMainMessage()); } } @@ -25,7 +28,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase { try { Validator::callback('is_int')->between(1,2)->setTemplate('{{name}} is not tasty')->assert('something'); - } catch (\Exception $e) { + } catch (NestedValidationExceptionInterface $e) { $this->assertEquals('"something" is not tasty', $e->getMainMessage()); } } @@ -33,7 +36,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase { try { Validator::callback('is_string')->between(1,2)->setTemplate('{{name}} is not tasty')->assert('something'); - } catch (\Exception $e) { + } catch (NestedValidationExceptionInterface $e) { $this->assertEquals('\-"something" is not tasty \-"something" must be greater than 1', $e->getFullMessage()); } @@ -42,7 +45,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase { try { Validator::string()->length(1,15)->assert(''); - } catch (\Exception $e) { + } catch (NestedValidationExceptionInterface $e) { $this->assertEquals('\-These rules must pass for "" \-"" must have a length between 1 and 15', $e->getFullMessage()); } @@ -70,7 +73,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase $usernameValidator = Validator::alnum('_')->length(1,15)->noWhitespace(); try { $usernameValidator->assert('really messed up screen#name'); - } catch (\InvalidArgumentException $e) { + } catch (NestedValidationExceptionInterface $e) { $e->findMessages(array('alnum', 'length', 'noWhitespace')); } } @@ -82,7 +85,7 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase ->key('birthdate', Validator::date()) ->setName("User Subscription Form") ->assert(array('username' => '', 'birthdate' => '')); - } catch (\InvalidArgumentException $e) { + } catch (NestedValidationExceptionInterface $e) { $this->assertEquals('\-These rules must pass for User Subscription Form |-Key username must be valid | \-"" must have a length between 1 and 32 @@ -110,4 +113,16 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase $this->assertSame($validator, $validator->not($validator->notEmpty())); } + + public function testDoNotRelyOnNestedValidationExceptionInterfaceForCheck() + { + $usernameValidator = Validator::alnum('_')->length(1, 15)->noWhitespace(); + try { + $usernameValidator->check('really messed up screen#name'); + } catch (NestedValidationExceptionInterface $e) { + $this->fail('Check used NestedValidationException'); + } catch (ValidationExceptionInterface $e) { + $this->assertTrue(true); + } + } }