Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

[WIP] Refactor to stateless validators #181

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
558 changes: 35 additions & 523 deletions src/AbstractValidator.php

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions src/Barcode.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,7 @@ public function useChecksum($checksum = null)
}

/**
* Defined by Zend\Validator\ValidatorInterface
*
* Returns true if and only if $value contains a valid barcode
* Determine if the given $value contains a valid barcode
*
* @param string $value
* @return bool
Expand Down
152 changes: 52 additions & 100 deletions src/Between.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,19 @@ class Between extends AbstractValidator
];

/**
* Additional variables available for validation failure messages
*
* @var array
* @var bool
*/
protected $messageVariables = [
'min' => ['options' => 'min'],
'max' => ['options' => 'max'],
];
private $inclusive;

/**
* Options for the between validator
*
* @var array
* @var int|float
*/
protected $options = [
'inclusive' => true, // Whether to do inclusive comparisons, allowing equivalence to min and/or max
'min' => 0,
'max' => PHP_INT_MAX,
];
private $max;

/**
* @var int|float
*/
private $min;

/**
* Sets validator options
Expand All @@ -55,127 +49,85 @@ class Between extends AbstractValidator
* 'max' => scalar, maximum border
* 'inclusive' => boolean, inclusive border values
*
* @param array|Traversable $options
*
* @throws Exception\InvalidArgumentException
* @param int|float $min
* @param int|float $max
* @throws Exception\InvalidArgumentException if $min is not numeric
* @throws Exception\InvalidArgumentException if $max is not numeric
*/
public function __construct($options = null)
public function __construct($min = 0, $max = PHP_INT_MAX, bool $inclusive = true)
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
if (! is_numeric($min)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid value for "min"; must be numeric, received %s',
is_object($min) ? get_class($min) : gettype($min)
));
}
if (! is_array($options)) {
$options = func_get_args();
$temp['min'] = array_shift($options);
if (! empty($options)) {
$temp['max'] = array_shift($options);
}

if (! empty($options)) {
$temp['inclusive'] = array_shift($options);
}

$options = $temp;
if (! is_numeric($max)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid value for "max"; must be numeric, received %s',
is_object($max) ? get_class($max) : gettype($max)
));
}

if (count($options) !== 2
&& (! array_key_exists('min', $options) || ! array_key_exists('max', $options))
) {
throw new Exception\InvalidArgumentException("Missing option. 'min' and 'max' have to be given");
}
$this->min = $min;
$this->max = $max;
$this->inclusive = $inclusive;

parent::__construct($options);
$this->messageVariables = [
'min' => $min,
'max' => $max,
];
}

/**
* Returns the min option
*
* @return mixed
* @return int|float
*/
public function getMin()
{
return $this->options['min'];
}

/**
* Sets the min option
*
* @param mixed $min
* @return Between Provides a fluent interface
*/
public function setMin($min)
{
$this->options['min'] = $min;
return $this;
return $this->min;
}

/**
* Returns the max option
*
* @return mixed
* @return int|float
*/
public function getMax()
{
return $this->options['max'];
return $this->max;
}

/**
* Sets the max option
*
* @param mixed $max
* @return Between Provides a fluent interface
*/
public function setMax($max)
public function isInclusive() : bool
{
$this->options['max'] = $max;
return $this;
return $this->inclusive;
}

/**
* Returns the inclusive option
*
* @return bool
* Returns true if and only if $value is between min and max options, inclusively
* if inclusive option is true.
*/
public function getInclusive()
public function validate($value, array $context = []) : Result
{
return $this->options['inclusive'];
return $this->isInclusive()
? $this->validateInclusive($value, $context)
: $this->validateExclusive($value, $context);
}

/**
* Sets the inclusive option
*
* @param bool $inclusive
* @return Between Provides a fluent interface
*/
public function setInclusive($inclusive)
private function validateInclusive($value, array $context) : Result
{
$this->options['inclusive'] = $inclusive;
return $this;
if ($value < $this->getMin() || $value > $this->getMax()) {
return $this->createInvalidResult($value, [self::NOT_BETWEEN]);
}
return ValidatorResult::createValidResult($value);
}

/**
* Returns true if and only if $value is between min and max options, inclusively
* if inclusive option is true.
*
* @param mixed $value
* @return bool
*/
public function isValid($value)
private function validateExclusive($value, array $context) : Result
{
$this->setValue($value);

if ($this->getInclusive()) {
if ($this->getMin() > $value || $value > $this->getMax()) {
$this->error(self::NOT_BETWEEN);
return false;
}
} else {
if ($this->getMin() >= $value || $value >= $this->getMax()) {
$this->error(self::NOT_BETWEEN_STRICT);
return false;
}
if ($value <= $this->getMin() || $value >= $this->getMax()) {
return $this->createInvalidResult($value, [self::NOT_BETWEEN_STRICT]);
}

return true;
return ValidatorResult::createValidResult($value);
}
}
72 changes: 47 additions & 25 deletions src/Bitwise.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,35 +120,27 @@ public function getStrict()
}

/**
* Returns true if and only if $value is between min and max options, inclusively
* if inclusive option is true.
* Validates successfully if and only if $value is between min and max
* options, inclusively if inclusive option is true.
*
* @param mixed $value
* @return bool
* @throws Exception\RuntimeException for unrecognized operators.
*/
public function isValid($value)
public function validate($value, array $context = []) : Result
{
$this->setValue($value);

if (self::OP_AND === $this->operator) {
if ($this->strict) {
// All the bits set in value must be set in control
$this->error(self::NOT_AND_STRICT);

return (bool) (($this->control & $value) == $value);
} else {
// At least one of the bits must be common between value and control
$this->error(self::NOT_AND);

return (bool) ($this->control & $value);
}
} elseif (self::OP_XOR === $this->operator) {
$this->error(self::NOT_XOR);

return (bool) (($this->control ^ $value) === ($this->control | $value));
switch ($this->operator) {
case (self::OP_AND):
return $this->validateAndOperation($value);
case (self::OP_OR):
return $this->validateOrOperation($value);
default:
throw Exception\RuntimeException(sprintf(
'%s instance has unrecognized operator "%s"; must be one of "%s" or "%s"',
get_class($this),
var_export($this->operator, true),
self::OP_AND,
self::OP_OR
));
}

return false;
}

/**
Expand Down Expand Up @@ -189,4 +181,34 @@ public function setStrict($strict)

return $this;
}

/**
* @param mixed $value
*/
private function validateAndOperation($value) : Result
{
if ($this->strict) {
// All the bits set in value must be set in control
$this->error(self::NOT_AND_STRICT);

return ($this->control & $value) == $value
? ValidatorResult::createValidResult($value)
: $this->createInvalidResult($value, [self::NOT_AND_STRICT]);
}

// At least one of the bits must be common between value and control
return (bool) ($this->control & $value)
? ValidatorResult::createValidResult($value)
: $this->createInvalidResult($value, [self::NOT_AND]);
}

/**
* @param mixed $value
*/
private function validateOrOperation($value) : Result
{
return ($this->control ^ $value) === ($this->control | $value)
? ValidatorResult::createValidResult($value)
: $this->createInvalidResult($value, [self::NOT_XOR]);
}
}
29 changes: 13 additions & 16 deletions src/Callback.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

namespace Zend\Validator;

use Throwable;

class Callback extends AbstractValidator
{
/**
Expand Down Expand Up @@ -108,20 +110,19 @@ public function setCallbackOptions($options)
* Returns true if and only if the set callback returns
* for the provided $value
*
* @param mixed $value
* @param mixed $context Additional context to provide to the callback
* @return bool
* @throws Exception\InvalidArgumentException
* @throws Exception\InvalidArgumentException if no callback present
* @throws Exception\InvalidArgumentException if callback is not callable
*/
public function isValid($value, $context = null)
public function validate($value, $context = null) : Result
{
$this->setValue($value);

$options = $this->getCallbackOptions();
$callback = $this->getCallback();
if (empty($callback)) {
throw new Exception\InvalidArgumentException('No callback given');
}
if (! is_callable($callback)) {
throw new Exception\InvalidArgumentException('Invalid callback given; not callable');
}

$args = [$value];
if (empty($options) && ! empty($context)) {
Expand All @@ -136,15 +137,11 @@ public function isValid($value, $context = null)
}

try {
if (! call_user_func_array($callback, $args)) {
$this->error(self::INVALID_VALUE);
return false;
}
} catch (\Exception $e) {
$this->error(self::INVALID_CALLBACK);
return false;
return (bool) $callback(...$args)
? ValidatorResult::createValidResult($value)
: $this->createInvalidResult($value, [self::INVALID_VALUE]);
} catch (Throwable $e) {
return $this->createInvalidResult($value, [self::INVALID_CALLBACK]);
}

return true;
}
}
5 changes: 1 addition & 4 deletions src/EmailAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -479,10 +479,7 @@ protected function splitEmailParts($value)
}

/**
* Defined by Zend\Validator\ValidatorInterface
*
* Returns true if and only if $value is a valid email address
* according to RFC2822
* Determine if the given $value is a valid email address per RFC 2822.
*
* @link http://www.ietf.org/rfc/rfc2822.txt RFC2822
* @link http://www.columbia.edu/kermit/ascii.html US-ASCII characters
Expand Down
Loading