More stable Exceptions API

This commit is contained in:
Alexandre Gomes Gaigalas 2010-12-03 19:36:46 -02:00
parent 7c889b2d4b
commit 248fbdcf4f
45 changed files with 226 additions and 213 deletions

View file

@ -0,0 +1,31 @@
<?php
namespace Respect\Validation\Exceptions;
class AbstractCompositeException extends ValidationException
{
const INVALID_NONE= 'AbstractComposite_1';
const INVALID_SOME= 'AbstractComposite_2';
public static $defaultTemplates = array(
self::INVALID_NONE => 'None of the rules passed',
self::INVALID_SOME => '%2$d rules did not passed',
);
public function chooseTemplate($input, $numFailed, $numRules)
{
if ($numFailed === $numRules)
return static::INVALID_NONE;
else
return static::INVALID_SOME;
}
public function renderMessage()
{
if (1 === count($this->related)) {
$this->message = $this->related[0]->getMessage();
} else {
parent::renderMessage();
}
}
}

View file

@ -2,11 +2,12 @@
namespace Respect\Validation\Exceptions;
class AllOfException extends ValidationException
class AllOfException extends AbstractCompositeException
{
const INVALID_ALLOF= 'AllOf_1';
public static $defaultTemplates = array(
self::INVALID_ALLOF => '%d of the %d required rules did not passed',
self::INVALID_NONE => 'None of %3$d required rules passed',
self::INVALID_SOME => '%2$d of %3$d required rules did not passed',
);
}

View file

@ -11,4 +11,12 @@ class AlnumException extends ValidationException
self::INVALID_ALNUM_CHARS => '"%s" does not contain only letters, digits and "%s"'
);
public function chooseTemplate($input, $additionalCharacters=null)
{
if (empty($additionalCharacters))
return self::INVALID_ALNUM;
else
return self::INVALID_ALNUM_CHARS;
}
}

View file

@ -11,4 +11,12 @@ class AlphaException extends ValidationException
self::INVALID_ALPHA_CHARS => '"%s" does not contain only letters and "%s"'
);
public function chooseTemplate($input, $additionalCharacters=null)
{
if (empty($additionalCharacters))
return self::INVALID_ALPHA;
else
return self::INVALID_ALPHA_CHARS;
}
}

View file

@ -2,11 +2,12 @@
namespace Respect\Validation\Exceptions;
class AtLeastException extends ValidationException
class AtLeastException extends AbstractCompositeException
{
const INVALID_ATLEAST = 'AtLeast_1';
public static $defaultTemplates = array(
self::INVALID_ATLEAST => '%d of the %d mandatory rules are invalid',
self::INVALID_NONE => 'None of %3$d required rules passed',
self::INVALID_SOME => '%2$d of %3$d required rules did not passed',
);
}

View file

@ -6,11 +6,21 @@ class BetweenException extends ValidationException
{
const INVALID_LESS= 'Between_1';
const INVALID_MORE= 'Between_2';
const INVALID_BOUNDS= 'Between_3';
const INVALID_BOTH= 'Between_3';
public static $defaultTemplates = array(
self::INVALID_LESS => '"%s" is less than "%s"',
self::INVALID_MORE => '"%s" is more than "%s"',
self::INVALID_BOUNDS => '"%s" is out of bounds',
self::INVALID_LESS => '"%s" is less than "%2$s"',
self::INVALID_MORE => '"%s" is more than "%3$s"',
self::INVALID_BOTH => '"%s" is less than "%s" and more than "%s"',
);
public function chooseTemplate($input, $min, $max, $isMinValid, $isMaxValid)
{
if (!$isMinValid && !$isMaxValid)
return self::INVALID_BOTH;
if (!$isMinValid)
return self::INVALID_LESS;
if (!$isMaxValid)
return self::INVALID_MORE;
}
}

View file

@ -7,8 +7,16 @@ class HasAttributeException extends ValidationException
const INVALID_HAS_ATTRIBUTE= 'HasAttribute_1';
const INVALID_HAS_ATTRIBUTE_RELATED = 'HasAttribute_2';
public static $defaultTemplates = array(
self::INVALID_HAS_ATTRIBUTE => '"%s" is not present',
self::INVALID_HAS_ATTRIBUTE_RELATED => '"%s" is invalid',
self::INVALID_HAS_ATTRIBUTE => '"%2$s" is not present',
self::INVALID_HAS_ATTRIBUTE_RELATED => '"%2$s" is invalid',
);
public function chooseTemplate($input, $attributeName, $hasTheAttribute)
{
if (!$hasTheAttribute)
return self::INVALID_HAS_ATTRIBUTE;
else
return self::INVALID_HAS_ATTRIBUTE_RELATED;
}
}

View file

@ -7,8 +7,16 @@ class HasKeyException extends ValidationException
const INVALID_HAS_KEY= 'HasKey_1';
const INVALID_HAS_KEY_RELATED = 'HasKey_2';
public static $defaultTemplates = array(
self::INVALID_HAS_KEY => '"%s" is not present',
self::INVALID_HAS_KEY_RELATED => '"%s" is invalid',
self::INVALID_HAS_KEY => '"%2$s" is not present',
self::INVALID_HAS_KEY_RELATED => '"%2$s" is invalid',
);
public function chooseTemplate($input, $attributeName, $hasTheAttribute)
{
if (!$hasTheAttribute)
return self::INVALID_HAS_KEY;
else
return self::INVALID_HAS_KEY_RELATED;
}
}

View file

@ -4,11 +4,9 @@ namespace Respect\Validation\Exceptions;
class HasOptionalAttributeException extends ValidationException
{
const INVALID_HAS_ATTRIBUTE= 'HasAttribute_1';
const INVALID_HAS_ATTRIBUTE_RELATED = 'HasAttribute_2';
const INVALID_HAS_ATTRIBUTE_RELATED = 'HasOptionalAttribute_2';
public static $defaultTemplates = array(
self::INVALID_HAS_ATTRIBUTE => '"%s" is not present',
self::INVALID_HAS_ATTRIBUTE_RELATED => '"%s" is invalid',
self::INVALID_HAS_ATTRIBUTE_RELATED => '"%2$s" is invalid',
);
}

View file

@ -2,11 +2,12 @@
namespace Respect\Validation\Exceptions;
class MostOfException extends ValidationException
class MostOfException extends AbstractCompositeException
{
const INVALID_MOST= 'Most_1';
public static $defaultTemplates = array(
self::INVALID_MOST => '%d of the %d required rules did not passed',
self::INVALID_NONE => 'None of %3$d required rules passed',
self::INVALID_SOME => '%2$d of %3$d required rules did not passed',
);
}

View file

@ -4,9 +4,9 @@ namespace Respect\Validation\Exceptions;
class NoneOfException extends ValidationException
{
const INVALID_NONE= 'None_1';
const INVALID_NONEOF= 'Hexa_1';
public static $defaultTemplates = array(
self::INVALID_NONE => 'None of the %d rules must pass. %d of them passed.',
self::INVALID_NONEOF => 'None of the %3$d must pass. %3$d passed',
);
}

View file

@ -2,11 +2,12 @@
namespace Respect\Validation\Exceptions;
class OneOfException extends ValidationException
class OneOfException extends AbstractCompositeException
{
const INVALID_ONE_OF= 'OneOf_1';
public static $defaultTemplates = array(
self::INVALID_ONE_OF => 'None of the %d rules passed',
self::INVALID_NONE => 'None of the %3$d rules passed',
self::INVALID_SOME => '%2$d of the %3$d rules did not passed',
);
}

View file

@ -4,9 +4,9 @@ namespace Respect\Validation\Exceptions;
class SfException extends ValidationException
{
const INVALID_DF= 'DF_1';
const INVALID_SF= 'Sf_1';
public static $defaultTemplates = array(
self::INVALID_DF => '%s',
self::INVALID_SF => '%s',
);
}

View file

@ -6,9 +6,21 @@ class StringLengthException extends ValidationException
{
const INVALID_LESS= 'StringLength_1';
const INVALID_MORE= 'StringLength_2';
const INVALID_BOTH= 'StringLength_3';
public static $defaultTemplates = array(
self::INVALID_LESS => '"%s" is shorter than the specified minimum of %n characters',
self::INVALID_MORE => '"%s" is longer than the specified minimum of %n characters',
self::INVALID_LESS => '"%s" is shorter than "%s"',
self::INVALID_MORE => '"%s" is longer than "%s"',
self::INVALID_BOTH => '"%s" is shorter than "%s" and longer than "%s"',
);
public function chooseTemplate($input, $min, $max, $isMinValid, $isMaxValid)
{
if (!$isMinValid && !$isMaxValid)
return self::INVALID_BOTH;
if (!$isMinValid)
return self::INVALID_LESS;
if (!$isMaxValid)
return self::INVALID_MORE;
}
}

View file

@ -3,6 +3,7 @@
namespace Respect\Validation\Exceptions;
use \InvalidArgumentException;
use \Exception;
class ValidationException extends InvalidArgumentException
{
@ -14,6 +15,15 @@ class ValidationException extends InvalidArgumentException
protected $related = array();
protected $params = array();
public static function create()
{
$instance = new static;
$params = func_get_args();
if (!empty($params))
$instance->configure($params);
return $instance;
}
public function configure()
{
$this->params = array_map(
@ -21,7 +31,9 @@ class ValidationException extends InvalidArgumentException
return is_object($mixed) ? get_class($mixed) : strval($mixed);
}, func_get_args()
);
$this->useTemplate($this->chooseTemplate($this->params));
$this->useTemplate(
call_user_func_array(array($this, 'chooseTemplate'), $this->params)
);
$this->renderMessage();
return $this;
}
@ -36,18 +48,30 @@ class ValidationException extends InvalidArgumentException
$sprintfParams = $this->params;
array_unshift($sprintfParams, $this->messageTemplate);
$this->message = call_user_func_array('sprintf', $sprintfParams);
$relatedMessages = array();
foreach ($this->related as $n => $related) {
$relatedMessage = "-" . $related->getMessage();
$relatedMessage = str_replace("\n", "\n ", $relatedMessage);
$relatedMessages[] = $relatedMessage;
}
if (!empty($relatedMessages))
$this->message .= "\n" . implode("\n", $relatedMessages);
}
public function setRelated(array $relatedExceptions)
{
foreach ($relatedExceptions as $related)
$this->addRelated($related);
$this->addRelated($related, false);
$this->renderMessage();
return $this;
}
public function addRelated(ValidationException $related)
public function addRelated(Exception $related, $render=true)
{
$this->related[] = $related;
if ($render)
$this->renderMessage();
return $this;
}
public function useTemplate($code)
@ -55,9 +79,4 @@ class ValidationException extends InvalidArgumentException
$this->messageTemplate = @static::$defaultTemplates[$code];
}
public function useParams($params)
{
}
}

View file

@ -15,16 +15,14 @@ abstract class AbstractRule implements Validatable
return $this->validate($input);
}
public function createException()
{
return new ValidationException;
}
public function getException()
{
if (is_null($this->exception))
$this->exception = $this->createException();
return $this->exception;
}
public function setException()
{
}
}

View file

@ -18,19 +18,15 @@ class AllOf extends AbstractComposite
));
}
public function createException()
{
return new AllOfException;
}
public function assert($input)
{
$exceptions = $this->validateRules($input);
if (!empty($exceptions))
throw $this
->getException()
->configure($input, count($exceptions), count($this->rules))
->setRelated($exceptions);
throw $this->getException() ? : AllOfException::create()
->setRelated($exceptions)
->configure(
$input, count($exceptions), count($this->rules)
);
return true;
}

View file

@ -20,11 +20,6 @@ class Alnum extends AbstractRule
$this->additionalChars = $additionalChars;
}
public function createException()
{
return new AlnumException;
}
public function validate($input)
{
return is_string($input) && preg_match(
@ -36,8 +31,7 @@ class Alnum extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : AlnumException::create()
->configure($input, $this->additionalChars);
return true;
}

View file

@ -20,11 +20,6 @@ class Alpha extends AbstractRule
$this->additionalChars = $additionalChars;
}
public function createException()
{
return new AlphaException;
}
public function validate($input)
{
return is_string($input) && preg_match(
@ -36,8 +31,7 @@ class Alpha extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : AlphaException::create()
->configure($input, $this->additionalChars);
return true;
}

View file

@ -20,18 +20,12 @@ class AtLeast extends AbstractComposite
$validators = $this->getRules();
$exceptions = $this->validateRules($input);
if ($this->howMany > (count($validators) - count($exceptions)))
throw $this
->getException()
->configure($input, count($exceptions), $this->howMany)
->setRelated($exceptions);
throw $this->getException() ? : AtLeastException::create()
->configure($input, count($exceptions), $this->howMany)
->setRelated($exceptions);
return true;
}
public function createException()
{
return new AtLeastException;
}
public function validate($input)
{
$validators = $this->getRules();
@ -41,7 +35,8 @@ class AtLeast extends AbstractComposite
$v->check($input);
$pass++;
} catch (ValidationException $e) {
//no need to do anything here. We just wanna count
//how many rules passed
}
if ($pass >= $this->howMany)
return true;
@ -64,10 +59,9 @@ class AtLeast extends AbstractComposite
if ($pass >= $this->howMany)
return true;
if (count($exceptions) > (count($validators) - $this->howMany))
throw $this
->getException()
->configure($input, count($exceptions), $this->howMany)
->setRelated($exceptions);
throw $this->getException() ? : AtLeastException::create()
->setRelated($exceptions)
->configure($input, count($exceptions), $this->howMany);
}
return false;
}

View file

@ -36,11 +36,6 @@ class Between extends AbstractRule
}
}
public function createException()
{
return new BetweenException;
}
public function validateMin($input)
{
return is_null($this->min) || $input >= $this->min;
@ -65,9 +60,10 @@ class Between extends AbstractRule
$validMin = $this->validateMin($input);
$validMax = $this->validateMax($input);
if (!$validMin || !$validMax)
throw $this
->getException()
->configure($input, $this->min, $this->max, $validMin, $validMax);
throw $this->getException() ? : BetweenException::create()
->configure(
$input, $this->min, $this->max, $validMin, $validMax
);
return true;
}

View file

@ -20,11 +20,6 @@ class Callback extends AbstractRule
$this->callback = $callback;
}
public function createException()
{
return new CallbackException;
}
public function validate($input)
{
return call_user_func($this->callback, $input);
@ -33,8 +28,7 @@ class Callback extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : CallbackException::create()
->configure($input, $this->callback);
return true;
}

View file

@ -33,16 +33,10 @@ class Date extends AbstractRule
return date($this->format, strtotime($input)) == $input;
}
public function createException()
{
return new DateException;
}
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : DateException::create()
->configure($input, $this->format);
return true;
}

View file

@ -8,11 +8,6 @@ use Respect\Validation\Exceptions\DigitsException;
class Digits extends AbstractRule
{
public function createException()
{
return new DigitsException;
}
public function validate($input)
{
return ctype_digit((string) $input);
@ -21,9 +16,7 @@ class Digits extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
->configure($input);
throw $this->getException() ? : DigitsException::create($input);
return true;
}

View file

@ -7,11 +7,6 @@ use Respect\Validation\Exceptions\FloatException;
class Float extends AbstractRule
{
public function createException()
{
return new FloatException;
}
public function validate($input)
{
@ -21,8 +16,7 @@ class Float extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : FloatException::create()
->configure($input);
return true;
}

View file

@ -26,11 +26,6 @@ class HasAttribute extends AllOf
$this->addRule($attributeValidator);
}
public function createException()
{
return new HasAttributeException(HasAttributeException::INVALID_HAS_ATTRIBUTE);
}
protected function getAttributeValue($input)
{
$propertyMirror = new ReflectionProperty($input, $this->attribute);
@ -56,13 +51,12 @@ class HasAttribute extends AllOf
$this->getAttributeValue($input)
);
} catch (ReflectionException $e) {
throw $this
->getException()
->configure($input, $this->attribute);
throw $this->getException() ? : HasAttributeException::create()
->configure($input, $this->attribute, false);
} catch (ValidationException $e) {
throw $this
->getException()
->configure($input, $this->attribute);
throw $this->getException() ? : HasAttributeException::create()
->addRelated($e)
->configure($input, $this->attribute, true);
}
return true;
}

View file

@ -7,6 +7,7 @@ use Respect\Validation\Exceptions\HasKeyException;
use Respect\Validation\Rules\All;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Validator;
use Respect\Validation\Exceptions\ValidationException;
class HasKey extends AllOf
{
@ -23,11 +24,6 @@ class HasKey extends AllOf
if (!is_null($valueValidator))
$this->addRule($valueValidator);
}
public function createException()
{
return new HasKeyException;
}
public function validate($input)
{
@ -37,11 +33,18 @@ class HasKey extends AllOf
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
->configure($input, $this->key);
return parent::validate(@$input[$this->key]);
$keyExists = array_key_exists($this->key, $input);
try {
parent::assert(@$input[$this->key]);
} catch (ValidationException $e) {
throw $this->exception ? : HasKeyException::create()
->configure($input, $this->key, $keyExists)
->addRelated($e);
}
if (!$keyExists)
throw $this->exception ? : HasKeyException::create()
->configure($input, $this->key, false);
return true;
}
public function check($input)

View file

@ -30,16 +30,10 @@ class HasOptionalAttribute extends HasAttribute
} catch (ReflectionException $e) {
return true;
} catch (ValidationException $e) {
throw $this
->getException()
->configure($input, $this->attribute);
throw $this->getException() ? : HasOptionalAttributeException::create()
->configure($input, $this->attribute);
}
return true;
}
public function createException()
{
return new HasOptionalAttributeException;
}
}

View file

@ -14,9 +14,4 @@ class HasOptionalKey extends HasKey
|| parent::validate($input[$this->key]);
}
public function createException()
{
return new HasOptionalKeyException;
}
}

View file

@ -7,6 +7,7 @@ use Respect\Validation\Exceptions\HexaException;
class Hexa extends AbstractRule
{
public function createException()
{
return new HexaException;
@ -20,8 +21,7 @@ class Hexa extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : HexaException::create()
->configure($input);
return true;
}

View file

@ -28,8 +28,7 @@ class Instance extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : InstanceException::create()
->configure($input, $this->instance);
return true;
}

View file

@ -28,8 +28,7 @@ class Ip extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : IpException::create()
->configure($input);
return true;
}

View file

@ -12,10 +12,5 @@ class MostOf extends AtLeast
$this->howMany = ceil(func_num_args() / 2);
$this->addRules(func_get_args());
}
public function createException()
{
return new MostOfException;
}
}

View file

@ -21,8 +21,7 @@ class NoWhitespace extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : NoWhitespaceException::create()
->configure($input);
return true;
}

View file

@ -29,10 +29,9 @@ class NoneOf extends AbstractComposite
$numRules = count($this->getRules());
$numExceptions = count($exceptions);
if ($numRules !== $numExceptions)
throw $this
->getException()
->configure($input, $numRules, $numExceptions)
->setRelated($exceptions);
throw $this->getException() ? : NoneOfException::create()
->configure($input, $numExceptions, $numRules)
->setRelated($exceptions);
return true;
}

View file

@ -23,8 +23,7 @@ class NotEmpty extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : NotEmptyException::create()
->configure($input);
return true;
}

View file

@ -21,8 +21,7 @@ class NullValue extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : NullValueException::create()
->configure($input);
return true;
}

View file

@ -21,8 +21,7 @@ class Numeric extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : NumericException::create()
->configure($input);
return true;
}

View file

@ -7,6 +7,7 @@ use Respect\Validation\Exceptions\ObjectException;
class Object extends AbstractRule
{
public function createException()
{
return new ObjectException;
@ -20,8 +21,7 @@ class Object extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : ObjectException::create()
->configure($input);
return true;
}

View file

@ -17,10 +17,9 @@ class OneOf extends AbstractComposite
$validators = $this->getRules();
$exceptions = $this->validateRules($input);
if (count($exceptions) === count($validators))
throw $this
->getException()
->configure(count($validators))
->setRelated($exceptions);
throw $this->getException() ? : OneOfException::create()
->configure($input, count($exceptions), count($validators))
->setRelated($exceptions);
return true;
}

View file

@ -28,8 +28,7 @@ class Regex extends AbstractRule
public function assert($input)
{
if (!$this->validate($input))
throw $this
->getException()
throw $this->getException() ? : RegexException::create()
->configure($input, $this->regex);
return true;
}

View file

@ -28,11 +28,6 @@ class Sf extends AbstractRule
$this->constraint = $sfMirrorConstraint->newInstance();
}
public function createException()
{
return new SfException;
}
public function validate($input)
{
$validatorName = 'Symfony\Component\Validator\Constraints\\'
@ -51,8 +46,7 @@ class Sf extends AbstractRule
'',
$input
);
throw $this
->getException()
throw $this->getException() ? : SfException::create()
->configure($violation->getMessage());
}
return true;

View file

@ -40,11 +40,6 @@ class StringLength extends AbstractRule
}
}
public function createException()
{
return new StringLengthException;
}
public function validateMin($input)
{
$input = mb_strlen($input);
@ -67,11 +62,10 @@ class StringLength extends AbstractRule
$validMin = $this->validateMin($input);
$validMax = $this->validateMax($input);
if (!$validMin || !$validMax)
throw $this
->getException()
->configure(
$input, $validMin, $validMax, $this->min, $this->max
);
throw $this->getException() ? : StringLengthException::create()
->configure(
$input, $this->min, $this->max, $validMin, $validMax
);
return true;
}

View file

@ -40,11 +40,10 @@ class Zend extends AbstractRule
if (!$this->validate($input)) {
$exceptions = array();
foreach ($this->zendValidator->getMessages() as $m) {
$exceptions[] = $this->getException()->configure($m);
$exceptions[] = ZendException::create()->configure($m);
}
throw $this
->getException()
->configure($exceptions);
throw $this->getException() ? : ZendException::create()
->configure($exceptions);
}
return true;
}

View file

@ -68,24 +68,25 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase
$validator = Validator::object()
->oneOf(
Validator::hasAttribute('screen_name',
Validator::alnum('_')->noWhitespace()),
Validator::hasAttribute('id',
Validator::numeric()
->between(1, 15))
Validator::hasAttribute(
'screen_name', Validator::alnum('_')->noWhitespace()
),
Validator::hasAttribute(
'id', Validator::numeric()->between(1, 15)
)
)
->hasAttribute('created_at', Validator::date())
->hasAttribute('name', $v160 = Validator::stringLength(1, 160))
->hasAttribute('sex',
Validator::allOf(
Validator::oneOf(
Validator::hexa(), Validator::float(), Validator::numeric()
))
->hasOptionalAttribute('description', $v160)
->hasOptionalAttribute('location', $v160);
try {
$validator->assert($target);
} catch (InvalidException $e) {
echo $e->message();
} catch (Exceptions\ValidationException $e) {
echo $e->getMessage();
}
}