Skip to content

Commit

Permalink
Overhauled the validate_pos() method and added many more validate_pos…
Browse files Browse the repository at this point in the history
…() unit tests.
  • Loading branch information
Craig Manley committed Jul 1, 2018
1 parent d1c170a commit 69b2995
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 92 deletions.
59 changes: 41 additions & 18 deletions src/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* @author Craig Manley
* @copyright Copyright © 2016, Craig Manley (www.craigmanley.com)
* @license http://www.opensource.org/licenses/mit-license.php Licensed under MIT
* @version $Id: Validator.php,v 1.4 2018/05/26 22:55:49 cmanley Exp $
* @version $Id: Validator.php,v 1.5 2018/07/01 18:41:02 cmanley Exp $
* @package Validate
*/
namespace Validate;
Expand Down Expand Up @@ -94,7 +94,7 @@
class Validator {

protected $allow_extra = null;
protected $empty_delete = true; # odd one out - normally defaults are false
protected $empty_delete = true; # odd one out - normally defaults are false/negative/null
protected $empty_null = null;
protected $prefix = '';
protected $remove_extra = false;
Expand Down Expand Up @@ -185,7 +185,8 @@ public function specs() {


/**
* Validates the given associative array.
* Validates the given associative array and returns the validated result.
* The result may be mutated depending on the options and specs used.
*
* @param array $args associative array
* @return array
Expand Down Expand Up @@ -254,7 +255,8 @@ public function validate(array $args) {


/**
* Validates a plain positional array of arguments.
* Validates a plain positional array of arguments and returns the validated result.
* The result may be mutated depending on the options and specs used.
* Since all PHP arrays are really associative, this function reindexes the args and the specs.
* Because of this, you can use still use strings for keys in either the args or the specs.
*
Expand All @@ -263,28 +265,49 @@ public function validate(array $args) {
* @throws ValidationException
*/
public function validate_pos(array $args) {
$args = array_values($args); # this make sure that args is a sequential numerically indexed array.
$specs = $this->specs();
if ($specs) {
$specs = array_values($this->specs()->toArray()); # make sure that specs is a sequential numerically indexed array.
}
$args = array_values($args); # this make sure that args is a sequential numerically indexed array.
foreach ($args as $k => &$v) {
if ($this->empty_null && is_string($v) && !strlen($v)) {
$v = null;
$count_args = count($args);
$count_specs = count($specs);

# Handle too many arguments
if ($count_args > $count_specs) {
if (!$this->allow_extra && !$this->remove_extra) {
throw new ValidationException('Too many arguments given (' . $count_args . ') for the number of specs (' . $count_specs . ')');
#throw new ValidationException('Unexpected parameter at index ' . $this->prefix . $k);
}
if ($this->remove_extra) {
array_splice($args, $count_specs);
}
}
$spec = $specs && (is_array($specs) ? array_key_exists($k, $specs) : $specs->offsetExists($k)) ? $specs[$k] : null; # array_key_exists does not work with ArrayAccess objects yet. Perhaps in the future it will.
if (!$spec) {
# note: remove_extra doesn't apply to positional arrays
if (!$this->allow_extra) {
throw new ValidationException('Unexpected parameter at index ' . $this->prefix . $k);
}

# Convert empty strings to null values if so wanted.
if ($this->empty_null) {
foreach ($args as $k => &$v) {
if (is_string($v) && !strlen($v)) {
$v = null;
}
continue;
unset($v);
}
if (!$spec->validate($v)) { # also applies before/after mutators to $v reference
throw new ValidationNamedCheckException($this->prefix . $k, $spec->getLastFailure(), $v);
}

# Validate
if ($specs) {
foreach ($specs as $i => $spec) {
$v = null;
if ($i < $count_args) {
$v =& $args[$i];
}
if (!$spec->validate($v)) { # also applies before/after mutators to $v reference
throw new ValidationNamedCheckException($this->prefix . $i, $spec->getLastFailure(), $v);
}
unset($v);
}
unset($v);
}

return $args;
}
}
231 changes: 157 additions & 74 deletions t/Validator.t.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,9 @@ public function testValidate() {
}
}

public function testValidatePosNamedSpecs() {
public function testValidatePosSpecs() {
$specs = array(
'name' => array(
array(
'type' => 'string',
'max_length' => 2,
'max_length' => 30,
Expand All @@ -179,91 +179,174 @@ public function testValidatePosNamedSpecs() {
}
},
),
'score' => array(
'score' => array( # string keys are allowed too.
'types' => array('float', 'integer'),
'max_value' => 10,
'min_value' => 0,
),
);
$validator = new Validate\Validator(array(
'specs' => $specs,
));
$inputs = array(
'normal' => array('Jane', 7),
'too few params' => array('Jane'),
'too many params' => array('Jane', 7, '', 'bla'),
'invalid 2nd param (index 1)' => array('Mike', 'high'),
'no params' => array(),
);
$tests = array(
array(
'input' => array('Jane', 7),
'expect' => array('JANE', 7),
'expect_exception' => null,
),
array(
'input' => array('Mike', 'high'),
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "types" failed for string value "high"',
'no options' => array(
'options' => array(),
'expects' => array(
'normal' => array(
'expect' => array('JANE', 7),
'expect_exception' => null,
),
'too few params' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "mandatory" failed for NULL value',
),
'too many params' => array(
'expect' => null,
'expect_exception' => 'Too many arguments given (4) for the number of specs (2)',
),
'invalid 2nd param (index 1)' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "types" failed for string value "high"',
),
'no params' => array(
'expect' => null,
'expect_exception' => 'Parameter "0" validation check "mandatory" failed for NULL value',
),
),
),
);
foreach ($tests as $i => $test) {
$input = $test['input'];
$expect = $test['expect'];
$expect_exception = $test['expect_exception'];
$got_exception = null;
$validated_input = null;
try {
$validated_input = $validator->validate_pos($input);
}
catch (Validate\ValidationException $e) {
$got_exception = $e->getMessage();
}
$this->assertEquals($expect, $validated_input, "Test $i validate() returns expected result.");
$this->assertEquals($expect_exception, $got_exception, "Test $i throws the expected exception.");
}
}

public function testValidatePosUnnamedSpecs() {
$specs = array(
array(
'type' => 'string',
'max_length' => 2,
'max_length' => 30,
'after' => function(&$value) {
if (is_string($value)) {
$value = strtoupper($value);
}
},
'allow_extra is true' => array(
'options' => array(
'allow_extra' => true,
),
'expects' => array(
'normal' => array(
'expect' => array('JANE', 7),
'expect_exception' => null,
),
'too few params' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "mandatory" failed for NULL value',
),
'too many params' => array(
'expect' => array('JANE', 7, '', 'bla'),
'expect_exception' => null,
),
'invalid 2nd param (index 1)' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "types" failed for string value "high"',
),
'no params' => array(
'expect' => null,
'expect_exception' => 'Parameter "0" validation check "mandatory" failed for NULL value',
),
),
),
array(
'types' => array('float', 'integer'),
'max_value' => 10,
'min_value' => 0,
'remove_extra is true' => array(
'options' => array(
'remove_extra' => true,
),
'expects' => array(
'normal' => array(
'expect' => array('JANE', 7),
'expect_exception' => null,
),
'too few params' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "mandatory" failed for NULL value',
),
'too many params' => array(
'expect' => array('JANE', 7),
'expect_exception' => null,
),
'invalid 2nd param (index 1)' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "types" failed for string value "high"',
),
'no params' => array(
'expect' => null,
'expect_exception' => 'Parameter "0" validation check "mandatory" failed for NULL value',
),
),
),
);
$validator = new Validate\Validator(array(
'specs' => $specs,
));
$tests = array(
array(
'input' => array('Jane', 7),
'expect' => array('JANE', 7),
'expect_exception' => null,
'allow_extra is true and remove_extra is true' => array(
'options' => array(
'allow_extra' => true,
'remove_extra' => true,
),
'expects' => array(
'normal' => array(
'expect' => array('JANE', 7),
'expect_exception' => null,
),
'too few params' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "mandatory" failed for NULL value',
),
'too many params' => array(
'expect' => array('JANE', 7),
'expect_exception' => null,
),
'invalid 2nd param (index 1)' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "types" failed for string value "high"',
),
'no params' => array(
'expect' => null,
'expect_exception' => 'Parameter "0" validation check "mandatory" failed for NULL value',
),
),
),
array(
'input' => array('Mike', 'high'),
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "types" failed for string value "high"',
'allow_extra is true and empty_null is true' => array(
'options' => array(
'allow_extra' => true,
'empty_null' => true,
),
'expects' => array(
'normal' => array(
'expect' => array('JANE', 7),
'expect_exception' => null,
),
'too few params' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "mandatory" failed for NULL value',
),
'too many params' => array(
'expect' => array('JANE', 7, null, 'bla'),
'expect_exception' => null,
),
'invalid 2nd param (index 1)' => array(
'expect' => null,
'expect_exception' => 'Parameter "1" validation check "types" failed for string value "high"',
),
'no params' => array(
'expect' => null,
'expect_exception' => 'Parameter "0" validation check "mandatory" failed for NULL value',
),
),
),
);
foreach ($tests as $i => $test) {
$input = $test['input'];
$expect = $test['expect'];
$expect_exception = $test['expect_exception'];
$got_exception = null;
$validated_input = null;
try {
$validated_input = $validator->validate_pos($input);
}
catch (Validate\ValidationException $e) {
$got_exception = $e->getMessage();
foreach ($tests as $name => $test) {
$args = $test['options'];
$args['specs'] = $specs;
$validator = new Validate\Validator($args);
foreach ($inputs as $input_name => $input) {
$expect = $test['expects'][$input_name]['expect'];
$expect_exception = $test['expects'][$input_name]['expect_exception'];
$got_exception = null;
$validated_input = null;
try {
$validated_input = $validator->validate_pos($input);
}
catch (Validate\ValidationException $e) {
$got_exception = $e->getMessage();
}
$this->assertEquals($expect, $validated_input, "Test \"$name\"->\"$input_name\" validate() returns expected result.");
$this->assertEquals($expect_exception, $got_exception, "Test \"$name\"->\"$input_name\" throws the expected exception.");
}
$this->assertEquals($expect, $validated_input, "Test $i validate() returns expected result.");
$this->assertEquals($expect_exception, $got_exception, "Test $i throws the expected exception.");
}
}

Expand Down

0 comments on commit 69b2995

Please sign in to comment.