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

add IsCountable validator, to enforce type and count elements. #157

Closed
wants to merge 51 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
0ca7d66
Merge branch 'hotfix/150' into develop
Xerkus Mar 17, 2017
dfca59b
2.9.0 readiness
Xerkus Mar 17, 2017
0cfbeba
Add changelog for next maintainance version
Xerkus Mar 17, 2017
55e55e0
Bumped to next dev version (2.10.0)
Xerkus Mar 17, 2017
479f57c
add IsCountable validator, to enforce type and count elements.
abacaphiliac Mar 25, 2017
5ba3cdd
Merge branch 'hotfix/validators-service-config' into develop
weierophinney May 17, 2017
6bb4397
Merge branch 'hotfix/159' into develop
weierophinney May 17, 2017
9ec7f1a
Merge branch 'hotfix/94' into develop
weierophinney May 17, 2017
149f6af
Merge branch 'hotfix/105' into develop
weierophinney May 17, 2017
979bd6d
Merge branch 'hotfix/132' into develop
weierophinney May 17, 2017
b23cd62
Merge branch 'hotfix/154' into develop
weierophinney May 17, 2017
7b52198
Merge branch 'hotfix/162' into develop
weierophinney May 17, 2017
fd8c3ed
Merge branch 'hotfix/163' into develop
weierophinney May 17, 2017
687eb4a
CS fixes
weierophinney May 17, 2017
7c1ee5e
Merge branch 'hotfix/166' into develop
weierophinney May 17, 2017
6c60c41
Merge branch 'hotfix/code128' into develop
weierophinney May 17, 2017
af39b1a
Merge branch 'hotfix/update-tld' into develop
weierophinney May 17, 2017
609c866
2.9.1 readiness
weierophinney May 17, 2017
388400f
Bumped version
weierophinney May 17, 2017
33591b6
Fix #165 - Use is_readable() instead of stream_resolve_include_path()
larsnystrom May 23, 2017
9d26add
Adapt .travis.yml to recent ZF standards
Slamdunk Jun 23, 2017
92035a4
Travis: drop HVVM & add PHP 7.2
Slamdunk Jun 23, 2017
1a0717c
Prepare to PHP 7.2
Slamdunk Jun 23, 2017
407ee84
In PHP < 7.2 `0 === count(null)`
Slamdunk Jun 23, 2017
6500014
Merge branch 'hotfix/173' into develop
weierophinney Jul 19, 2017
bf7be0d
Merge branch 'hotfix/hostname-idn-update' into develop
weierophinney Jul 19, 2017
f012441
Merge branch 'hotfix/180' into develop
weierophinney Jul 19, 2017
d7b8d55
Merge branch 'hotfix/travis' into develop
weierophinney Jul 19, 2017
9a7c40c
Merge branch 'hotfix/174' into develop
weierophinney Jul 20, 2017
13f88a4
2.9.2 readiness
weierophinney Jul 20, 2017
58da575
Updated hostname validator TLD list
weierophinney Jul 20, 2017
83aa8e5
Merge pull request #169 from larsnystrom/hotfix/165
weierophinney Jul 25, 2017
4416265
Updates `@return` annotations for fluent interfaces
weierophinney Jul 25, 2017
ddcb7cd
Adds CHANGELOG for #169
weierophinney Jul 25, 2017
e44ce14
Merge branch 'feature/169' into develop
weierophinney Jul 25, 2017
7b3c9ac
Updated Hostname validator TLD list
weierophinney Jul 25, 2017
8d4989d
Merge branch 'hotfix/hostname-tld' into develop
weierophinney Jul 25, 2017
809093c
Merge pull request #175 from Slamdunk/php_72
weierophinney Jul 25, 2017
d0316a1
Provides CS fix for update_hostname_validator
weierophinney Jul 25, 2017
6120357
Provides formatting of complex conditional
weierophinney Jul 25, 2017
af974c9
Adds CHANGELOG for #175
weierophinney Jul 25, 2017
93601c7
Merge branch 'feature/175' into develop
weierophinney Jul 25, 2017
e7355ab
Updates zend-session requirement
weierophinney Jul 25, 2017
f527802
address upstream feedback: remove setters, prefer count over min and …
abacaphiliac Jul 26, 2017
9924700
Adds CHANGELOG for #185
weierophinney Jul 26, 2017
b83a91f
Merge branch 'feature/zend-session-php-7.2-compat' into develop
weierophinney Jul 26, 2017
1dc79e6
Merge pull request #157 from abacaphiliac/master
weierophinney Jul 26, 2017
74b4711
Adds license docblocks to new class files
weierophinney Jul 26, 2017
88831e1
Imports `Countable` into `IsCountable` class file
weierophinney Jul 26, 2017
97d297c
Updates IsCountable to not allow passing both a count and a min or ma…
weierophinney Jul 26, 2017
03c12fd
Adds documentation for #157
weierophinney Jul 26, 2017
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
150 changes: 150 additions & 0 deletions src/IsCountable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

namespace Zend\Validator;

use Zend\Validator\Exception\RuntimeException;

class IsCountable extends AbstractValidator
{
const NOT_COUNTABLE = 'notCountable';
const NOT_EQUALS = 'notEquals';
const GREATER_THAN = 'greaterThan';
const LESS_THAN = 'lessThan';

/**
* Validation failure message template definitions
*
* @var array
*/
protected $messageTemplates = [
self::NOT_COUNTABLE => "The input must be an array or an instance of \\Countable",
self::NOT_EQUALS => "The input count must equal '%count%'",
self::GREATER_THAN => "The input count must be less than '%max%', inclusively",
self::LESS_THAN => "The input count must be greater than '%min%', inclusively",
];

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

/**
* Options for the between validator
*
* @var array
*/
protected $options = [
'count' => null,
'min' => null,
'max' => null,
];

/**
* Returns true if and only if $value is countable (and the count validates against optional values).
*
* @param string $value
* @return bool
* @throws Exception\RuntimeException
*/
public function isValid($value)
{
if (! (is_array($value) || $value instanceof \Countable)) {
throw new RuntimeException($this->messageTemplates[self::NOT_COUNTABLE]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid input is not an exceptional case, and should instead be reported as a validation error. Use the $this->error construct and return false in this particular case.

}

$count = count($value);

if (is_numeric($this->options['count'])) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend using your get*() methods for these, instead of direct access to the $options property. The rationale is that this implementation detail may change for version 3 (see the v3 RFC), and refactoring would require finding these references throughout the class.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it looks like the validator will have different behavior based on whether count, min, or max are set; in fact, it appears that these should be mutually exclusive. As such, I wonder if the constructor and/or setOptions() should have some logic to prevent cases where one is already set/discovered, and an attempt is made to set a different option?

if ($count != $this->options['count']) {
$this->error(self::NOT_EQUALS);
return false;
}

return true;
}

if (is_numeric($this->options['max']) && $count > $this->options['max']) {
$this->error(self::GREATER_THAN);
return false;
}

if (is_numeric($this->options['min']) && $count < $this->options['min']) {
$this->error(self::LESS_THAN);
return false;
}

return true;
}

/**
* Returns the count option
*
* @return mixed
*/
public function getCount()
{
return $this->options['count'];
}

/**
* Sets the count option
*
* @param int $count
* @return self Provides a fluent interface
*/
public function setCount($count)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the class offer setters? Once one of either count, min, or max is set, I'd argue that the class shouldn't allow setting one of the other options, as that indicates a change in behavior, and/or an unexpected behavior (e.g., if count was previously set, but setMin() or setMax() is called later, it will still only honor the count setting).

{
$this->options['count'] = $count;
return $this;
}

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

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

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

/**
* Sets the max option
*
* @param int $max
* @return self Provides a fluent interface
*/
public function setMax($max)
{
$this->options['max'] = $max;
return $this;
}
}
88 changes: 88 additions & 0 deletions test/IsCountableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace ZendTest\Validator;

use PHPUnit\Framework\TestCase;
use Zend\Validator\IsCountable;

class IsCountableTest extends TestCase
{
/** @var IsCountable */
private $sut;

protected function setUp()
{
$this->sut = new IsCountable();
}

public function testArrayIsValid()
{
$this->sut->setMin(1);
$this->sut->setMax(10);

self::assertTrue($this->sut->isValid(['Foo']), json_encode($this->sut->getMessages()));
Copy link
Member

@froschdesign froschdesign Jul 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use $this-> instead of self::, this is the preferred way. Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ocramius thinks something else 😜

self::assertCount(0, $this->sut->getMessages());
}

public function testIteratorIsValid()
{
self::assertTrue($this->sut->isValid(new \SplQueue()), json_encode($this->sut->getMessages()));
self::assertCount(0, $this->sut->getMessages());
}

public function testValidEquals()
{
$this->sut->setCount(1);

self::assertTrue($this->sut->isValid(['Foo']));
self::assertCount(0, $this->sut->getMessages());
}

public function testValidMax()
{
$this->sut->setMax(1);

self::assertTrue($this->sut->isValid(['Foo']));
self::assertCount(0, $this->sut->getMessages());
}

public function testValidMin()
{
$this->sut->setMin(1);

self::assertTrue($this->sut->isValid(['Foo']));
self::assertCount(0, $this->sut->getMessages());
}

public function testInvalidNotEquals()
{
$this->sut->setCount(2);

self::assertFalse($this->sut->isValid(['Foo']));
self::assertCount(1, $this->sut->getMessages());
}

/**
* @expectedException \Zend\Validator\Exception\RuntimeException
Copy link
Member

@froschdesign froschdesign Jul 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*/
public function testInvalidType()
{
$this->sut->isValid(new \stdClass());
}

public function testInvalidExceedsMax()
{
$this->sut->setMax(1);

self::assertFalse($this->sut->isValid(['Foo', 'Bar']));
self::assertCount(1, $this->sut->getMessages());
}

public function testInvalidExceedsMin()
{
$this->sut->setMin(2);

self::assertFalse($this->sut->isValid(['Foo']));
self::assertCount(1, $this->sut->getMessages());
}
}