From 862cec3ce47f6ff2456053d6026681f1cc442ecf Mon Sep 17 00:00:00 2001 From: Farhad Date: Thu, 23 Jun 2016 08:23:09 +0000 Subject: [PATCH 01/58] fixed a bug where empty $email was passed to idn_to_utf8 checked if $email is not empty before passing it to idn_to_utf8 --- src/EmailAddress.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/EmailAddress.php b/src/EmailAddress.php index 4ce2bd8ab..475462609 100644 --- a/src/EmailAddress.php +++ b/src/EmailAddress.php @@ -547,6 +547,9 @@ protected function idnToAscii($email) */ protected function idnToUtf8($email) { + if(strlen($email) == 0) + return $email; + if (extension_loaded('intl')) { // The documentation does not clarify what kind of failure // can happen in idn_to_utf8. One can assume if the source From ab625c947d0df0d496d5d27f0d72c92175dc6fc6 Mon Sep 17 00:00:00 2001 From: Farhad Date: Thu, 23 Jun 2016 08:37:57 +0000 Subject: [PATCH 02/58] Update EmailAddress.php --- src/EmailAddress.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EmailAddress.php b/src/EmailAddress.php index 475462609..10118a71b 100644 --- a/src/EmailAddress.php +++ b/src/EmailAddress.php @@ -547,7 +547,7 @@ protected function idnToAscii($email) */ protected function idnToUtf8($email) { - if(strlen($email) == 0) + if (strlen($email) == 0) return $email; if (extension_loaded('intl')) { From dcbb971da74a865fd3aa2f657512644284e5ff7f Mon Sep 17 00:00:00 2001 From: Farhad Date: Thu, 23 Jun 2016 08:49:49 +0000 Subject: [PATCH 03/58] Update EmailAddress.php --- src/EmailAddress.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EmailAddress.php b/src/EmailAddress.php index 10118a71b..9c36e918d 100644 --- a/src/EmailAddress.php +++ b/src/EmailAddress.php @@ -547,8 +547,9 @@ protected function idnToAscii($email) */ protected function idnToUtf8($email) { - if (strlen($email) == 0) + if (strlen($email) == 0) { return $email; + } if (extension_loaded('intl')) { // The documentation does not clarify what kind of failure From 6ebf8251f504a850431467896ccb22c5de278f91 Mon Sep 17 00:00:00 2001 From: Francis Gonzales Date: Mon, 18 Jul 2016 11:47:52 -0500 Subject: [PATCH 04/58] Update Code128.php Avoiding warning in the last character https://github.com/zendframework/zend-barcode/issues/28 --- src/Barcode/Code128.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Barcode/Code128.php b/src/Barcode/Code128.php index 245960073..d17e7d7ce 100644 --- a/src/Barcode/Code128.php +++ b/src/Barcode/Code128.php @@ -180,7 +180,10 @@ public function hasValidCharacters($value) break; } - $value = $strWrapper->substr($value, 1, null); + if (!empty($value) && $value != '') { + $value = $strWrapper->substr($value, 1, null); + } + $read = $set; } From 5551b3d698b1eae7502936e406cc280019c2052f Mon Sep 17 00:00:00 2001 From: Francis Gonzales Date: Mon, 18 Jul 2016 11:54:40 -0500 Subject: [PATCH 05/58] Update Code128.php --- src/Barcode/Code128.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Barcode/Code128.php b/src/Barcode/Code128.php index d17e7d7ce..da4172f75 100644 --- a/src/Barcode/Code128.php +++ b/src/Barcode/Code128.php @@ -180,7 +180,7 @@ public function hasValidCharacters($value) break; } - if (!empty($value) && $value != '') { + if (!empty($value) && strlen($value) > 1) { $value = $strWrapper->substr($value, 1, null); } From 5b27d44db8f8d3b49d04184bea967d05ad97bb19 Mon Sep 17 00:00:00 2001 From: Mehmet Korkmaz Date: Mon, 20 Mar 2017 16:57:52 +0300 Subject: [PATCH 06/58] Credit card number lengths are extended. fixes #153 Source: https://en.wikipedia.org/wiki/Payment_card_number --- src/CreditCard.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CreditCard.php b/src/CreditCard.php index 4d36759da..6bf514bdd 100644 --- a/src/CreditCard.php +++ b/src/CreditCard.php @@ -83,14 +83,14 @@ class CreditCard extends AbstractValidator self::AMERICAN_EXPRESS => [15], self::DINERS_CLUB => [14], self::DINERS_CLUB_US => [16], - self::DISCOVER => [16], + self::DISCOVER => [16, 19], self::JCB => [15, 16], self::LASER => [16, 17, 18, 19], self::MAESTRO => [12, 13, 14, 15, 16, 17, 18, 19], self::MASTERCARD => [16], self::SOLO => [16, 18, 19], self::UNIONPAY => [16, 17, 18, 19], - self::VISA => [16], + self::VISA => [13, 16, 19], ]; /** From 479f57cb6c68d4481e2dca6c2001b0cf0203cc5a Mon Sep 17 00:00:00 2001 From: Tim Younger Date: Sat, 25 Mar 2017 13:32:27 -0700 Subject: [PATCH 07/58] add IsCountable validator, to enforce type and count elements. --- src/IsCountable.php | 150 +++++++++++++++++++++++++++++++++++++++ test/IsCountableTest.php | 88 +++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 src/IsCountable.php create mode 100644 test/IsCountableTest.php diff --git a/src/IsCountable.php b/src/IsCountable.php new file mode 100644 index 000000000..ea9ba81aa --- /dev/null +++ b/src/IsCountable.php @@ -0,0 +1,150 @@ + "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]); + } + + $count = count($value); + + if (is_numeric($this->options['count'])) { + 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) + { + $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; + } +} diff --git a/test/IsCountableTest.php b/test/IsCountableTest.php new file mode 100644 index 000000000..953faf57a --- /dev/null +++ b/test/IsCountableTest.php @@ -0,0 +1,88 @@ +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())); + 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 + */ + 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()); + } +} From 1016df475aea43c8205e44bfdf9854dd7971251d Mon Sep 17 00:00:00 2001 From: Geert Eltink Date: Wed, 29 Mar 2017 23:48:06 +0200 Subject: [PATCH 08/58] Enable Travis CI slack notifications --- .travis.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 53cbbd2ca..2cde30890 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,7 @@ matrix: - php: hhvm env: - DEPS=latest - - php: hhvm + - php: hhvm env: - DEPS=locked - SERVICE_MANAGER_VERSION="^2.7.5" @@ -111,5 +111,14 @@ after_success: - if [[ $DEPLOY_DOCS == "true" ]]; then echo "Preparing to build and deploy documentation" ; ./zf-mkdoc-theme/deploy.sh ; echo "Completed deploying documentation" ; fi notifications: - irc: "irc.freenode.org#zftalk.dev" email: false + irc: + channels: + - "irc.freenode.org#zftalk.dev" + on_success: change + on_failure: always + slack: + rooms: + - secure: "ujQTv4jUDjnWAUME3w2VoPyKPBwbCGa7b1YMKxOt4PWxjWuyIUDY743fnmcQUqkX+CUXz9qJHG124RyZQ6UUowG/NZDttp7lppU/bIGJ/K0MuVmpBVwb8y/6rDoRj37V4n8WqAHGevjlRet3E5+gl91PFuSpN5JSj4efI8MlgUF6mqIIZUOifq2yNTZ9MXrG2qojIN4o6G4gttfwUR/3Ah1nD/ZtQBLA7pTd31/UNwtZMQ4IbGmcCMpdUADbQDr24VubjzTJfweSBoAu8Xf3IPPdR5AEfdRvuT1tGYPP4YxmvHxpTH1wF3mCX6b6ubUFMlpbqE50y/v4Mlva+2jXvcZ9Lt/Fs1Hz4pR3P3mM8EJjtj55cXWm+MSBqPBN7SX6AnkYB/OznuzqbCvt5Te09fm++1REYGnxkLxCnwI9GN2sKS7Tr8NxUCZyi9d4sVh7KUnrwFGVAGpViBTeglq+epoClcwupLK1E2m8IjrjUHTYG6NCE1QF/1NrrwuFBUIQuxPj/uE4oZcb8Tmiz9ilGFw/JbMR6WKzYzDSk2GrkJjUpa8Pn570kDL3otfJImPnEOxN73m9jle6P3laVJP9/A5Vm9C86S0aghAaswxCZB7Fql0Pl01WAB1kufqQBM/euFWGB0bQ9TFWoOENZDfD9zGyTkeZq3mgZ6DgU7ft6FA=" + on_success: change + on_failure: always From 1e92244c1b1642d93ee910138e8b69c4603fd882 Mon Sep 17 00:00:00 2001 From: codisart Date: Sun, 26 Mar 2017 17:28:06 +0200 Subject: [PATCH 09/58] [BUG] Initialize variable to prevent error when not set And do refactoring --- src/EmailAddress.php | 9 ++------- test/EmailAddressTest.php | 6 +++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/EmailAddress.php b/src/EmailAddress.php index 7f06c2267..71e6c0f8d 100644 --- a/src/EmailAddress.php +++ b/src/EmailAddress.php @@ -511,6 +511,7 @@ public function isValid($value) } // Match hostname part + $hostname = false; if ($this->options['useDomainCheck']) { $hostname = $this->validateHostnamePart(); } @@ -518,13 +519,7 @@ public function isValid($value) $local = $this->validateLocalPart(); // If both parts valid, return true - if ($local && $length) { - if (($this->options['useDomainCheck'] && $hostname) || ! $this->options['useDomainCheck']) { - return true; - } - } - - return false; + return ($local && $length) && (! $this->options['useDomainCheck'] || $hostname); } /** diff --git a/test/EmailAddressTest.php b/test/EmailAddressTest.php index a13f3e0df..87a6af31b 100644 --- a/test/EmailAddressTest.php +++ b/test/EmailAddressTest.php @@ -685,6 +685,11 @@ public function testNotSetHostnameValidator() $this->assertInstanceOf(Hostname::class, $hostname); } + public function testIsMxSupported() + { + $validator = new EmailAddress(['useMxCheck' => true, 'allow' => Hostname::ALLOW_ALL]); + $this->assertInternalType('bool', $validator->isMxSupported()); + } /** * Test getMXRecord */ @@ -692,7 +697,6 @@ public function testGetMXRecord() { $this->skipIfOnlineTestsDisabled(); - $validator = new EmailAddress(['useMxCheck' => true, 'allow' => Hostname::ALLOW_ALL]); $validator = new EmailAddress(['useMxCheck' => true, 'allow' => Hostname::ALLOW_ALL]); if (! $validator->isMxSupported()) { From 7f8ab4da886d650ed6e096d4d53d8c1cc25f4704 Mon Sep 17 00:00:00 2001 From: Aleksei Khudiakov Date: Thu, 6 Apr 2017 06:18:43 +1000 Subject: [PATCH 10/58] Add support for internationalized email address local part --- src/EmailAddress.php | 19 +++++++++++++++++++ test/EmailAddressTest.php | 4 +++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/EmailAddress.php b/src/EmailAddress.php index 7f06c2267..6fc89c619 100644 --- a/src/EmailAddress.php +++ b/src/EmailAddress.php @@ -9,6 +9,8 @@ namespace Zend\Validator; +use UConverter; + class EmailAddress extends AbstractValidator { const INVALID = 'emailAddressInvalid'; @@ -342,6 +344,8 @@ protected function validateLocalPart() $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e'; if (preg_match('/^[' . $atext . ']+(\x2e+[' . $atext . ']+)*$/', $this->localPart)) { $result = true; + } elseif ($this->validateInternationalizedLocalPart()) { + $result = true; } else { // Try quoted string format (RFC 5321 Chapter 4.1.2) @@ -360,6 +364,21 @@ protected function validateLocalPart() return $result; } + protected function validateInternationalizedLocalPart() + { + if (extension_loaded('intl') + && false === UConverter::transcode($this->localPart, 'UTF-8', 'UTF-8') + ) { + // invalid utf? + return false; + } + $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e'; + // RFC 6532 extends atext to include non-ascii utf + // @see https://tools.ietf.org/html/rfc6532#section-3.1 + $uatext = $atext . '\x{80}-\x{FFFF}'; + return (bool) preg_match('/^[' . $uatext . ']+(\x2e+[' . $uatext . ']+)*$/u', $this->localPart); + } + /** * Returns the found MX Record information after validation including weight for further processing * diff --git a/test/EmailAddressTest.php b/test/EmailAddressTest.php index a13f3e0df..ece216988 100644 --- a/test/EmailAddressTest.php +++ b/test/EmailAddressTest.php @@ -235,6 +235,9 @@ public function validEmailAddresses() ]; if (extension_loaded('intl')) { + $return['иван@письмо.рф'] = ['иван@письмо.рф']; + $return['öäü@ä-umlaut.de'] = ['öäü@ä-umlaut.de']; + $return['frédéric@domain.com'] = ['frédéric@domain.com']; $return['bob@тест.рф'] = ['bob@тест.рф']; $return['bob@xn--e1aybc.xn--p1ai'] = ['bob@xn--e1aybc.xn--p1ai']; } @@ -277,7 +280,6 @@ public function invalidEmailAddresses() 'bob @ domain.com' => ['bob @ domain.com'], 'Abc..123@example.com' => ['Abc..123@example.com'], '"bob%jones@domain.com' => ['"bob%jones@domain.com'], - 'иван@письмо.рф' => ['иван@письмо.рф'], 'multiline' => ['bob @domain.com'], From 43dbb2d61d6c5d354dd7e1bca6e35180ff68abe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Kuzmanovi=C4=87?= Date: Mon, 10 Apr 2017 15:32:54 +0200 Subject: [PATCH 11/58] IDN for .hr Added expression to validate .hr IDN domains --- src/Hostname.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Hostname.php b/src/Hostname.php index 4fa57ce35..709eabe79 100644 --- a/src/Hostname.php +++ b/src/Hostname.php @@ -1629,6 +1629,7 @@ class Hostname extends AbstractValidator * (.ES) Spain https://www.nic.es/media/2008-05/1210147705287.pdf * (.FI) Finland http://www.ficora.fi/en/index/palvelut/fiverkkotunnukset/aakkostenkaytto.html * (.GR) Greece https://grweb.ics.forth.gr/CharacterTable1_en.jsp + * (.HR) Croatia https://www.dns.hr/en/portal/files/Odluka-1,2alfanum-dijak.pdf * (.HU) Hungary http://www.domain.hu/domain/English/szabalyzat/szabalyzat.html * (.IL) Israel http://www.isoc.org.il/domains/il-domain-rules.html * (.INFO) International http://www.nic.info/info/idn @@ -1688,6 +1689,7 @@ class Hostname extends AbstractValidator 'FI' => [1 => '/^[\x{002d}0-9a-zäåö]{1,63}$/iu'], 'GR' => [1 => '/^[\x{002d}0-9a-zΆΈΉΊΌΎ-ΡΣ-ώἀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼῂῃῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲῳῴῶ-ῼ]{1,63}$/iu'], 'HK' => 'Hostname/Cn.php', + 'HR' => [1 => '/^[\x{002d}0-9a-zžćčđš]{1,63}$/iu'], 'HU' => [1 => '/^[\x{002d}0-9a-záéíóöúüőű]{1,63}$/iu'], 'IL' => [1 => '/^[\x{002d}0-9\x{05D0}-\x{05EA}]{1,63}$/iu', 2 => '/^[\x{002d}0-9a-z]{1,63}$/i'], From 0d562d0bcb07c4795b7156a0b02dc8435f766654 Mon Sep 17 00:00:00 2001 From: max-zu Date: Wed, 12 Apr 2017 15:12:28 +0300 Subject: [PATCH 12/58] Update Iban.php add ibanRegExp for Belarus. 'BY' => 'BY[0-9]{2}[A-Z0-9]{4}[0-9]{4}[A-Z0-9]{16}' https://www.nbrb.by/engl/payment/IBANBIC --- src/Iban.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Iban.php b/src/Iban.php index cbc35b757..2fd89f06e 100644 --- a/src/Iban.php +++ b/src/Iban.php @@ -75,6 +75,7 @@ class Iban extends AbstractValidator 'BG' => 'BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8}', 'BH' => 'BH[0-9]{2}[A-Z]{4}[A-Z0-9]{14}', 'BR' => 'BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z][A-Z0-9]', + 'BY' => 'BY[0-9]{2}[A-Z0-9]{4}[0-9]{4}[A-Z0-9]{16}' 'CH' => 'CH[0-9]{2}[0-9]{5}[A-Z0-9]{12}', 'CR' => 'CR[0-9]{2}[0-9]{3}[0-9]{14}', 'CY' => 'CY[0-9]{2}[0-9]{3}[0-9]{5}[A-Z0-9]{16}', From a2fcedde56b39846ba50fcb3d48a727e1619d627 Mon Sep 17 00:00:00 2001 From: max-zu Date: Wed, 12 Apr 2017 15:38:58 +0300 Subject: [PATCH 13/58] Update Iban.php --- src/Iban.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Iban.php b/src/Iban.php index 2fd89f06e..926573b13 100644 --- a/src/Iban.php +++ b/src/Iban.php @@ -75,7 +75,7 @@ class Iban extends AbstractValidator 'BG' => 'BG[0-9]{2}[A-Z]{4}[0-9]{4}[0-9]{2}[A-Z0-9]{8}', 'BH' => 'BH[0-9]{2}[A-Z]{4}[A-Z0-9]{14}', 'BR' => 'BR[0-9]{2}[0-9]{8}[0-9]{5}[0-9]{10}[A-Z][A-Z0-9]', - 'BY' => 'BY[0-9]{2}[A-Z0-9]{4}[0-9]{4}[A-Z0-9]{16}' + 'BY' => 'BY[0-9]{2}[A-Z0-9]{4}[0-9]{4}[A-Z0-9]{16}', 'CH' => 'CH[0-9]{2}[0-9]{5}[A-Z0-9]{12}', 'CR' => 'CR[0-9]{2}[0-9]{3}[0-9]{14}', 'CY' => 'CY[0-9]{2}[0-9]{3}[0-9]{5}[A-Z0-9]{16}', From 3fc1e6614672eb7dbba816677c5a660a953bb563 Mon Sep 17 00:00:00 2001 From: Philippe Jung Date: Tue, 25 Apr 2017 10:05:32 +0100 Subject: [PATCH 14/58] Ref #7629 Closes opened file with finfo to avoid segfault on specific PHP versions --- src/File/MimeType.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/File/MimeType.php b/src/File/MimeType.php index 3c09469a5..68e7dd8e3 100644 --- a/src/File/MimeType.php +++ b/src/File/MimeType.php @@ -385,6 +385,7 @@ public function isValid($value, $file = null) $this->type = null; if (! empty($this->finfo)) { $this->type = finfo_file($this->finfo, $file); + finfo_close($this->finfo); } } From cd6b79ce97f8dd084b9baa574b8726f909264830 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 16 May 2017 15:32:31 -0500 Subject: [PATCH 15/58] Ensure `validators` config is honored in non-zend-mvc contexts Per https://discourse.zendframework.com/t/validatormanager-not-calling-custom-validator-factory/109/5?u=matthew the `validators` config key is not honored currently unless the application is within a zend-mvc context. This is due to the fact that `Zend\Validator\Module` wires configuration for the `Zend\ModuleManager\Listener\ServiceListener` in order to push merged service configuration into the plugin during bootstrap; no similar logic is available when not in a zend-mvc context, however. This patch fixes that situation by modifying the `ValidatorPluginManagerFactory` to do the following: - If a `ServiceListener` service exists, it returns the plugin manager immediately (old behavior). - Otherwise, it checks for the `config` service, and, if found, a `validators` key with an array value. When found, it feeds that value to a `Zend\ServiceManager\Config` instance and uses that to configure the plugin manager before returning it. --- src/ValidatorPluginManagerFactory.php | 26 +++++- test/ValidatorPluginManagerFactoryTest.php | 102 +++++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/ValidatorPluginManagerFactory.php b/src/ValidatorPluginManagerFactory.php index 2a3ffd4e6..e3575af90 100644 --- a/src/ValidatorPluginManagerFactory.php +++ b/src/ValidatorPluginManagerFactory.php @@ -8,6 +8,7 @@ namespace Zend\Validator; use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Config; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; @@ -27,7 +28,30 @@ class ValidatorPluginManagerFactory implements FactoryInterface */ public function __invoke(ContainerInterface $container, $name, array $options = null) { - return new ValidatorPluginManager($container, $options ?: []); + $pluginManager = new ValidatorPluginManager($container, $options ?: []); + + // If this is in a zend-mvc application, the ServiceListener will inject + // merged configuration during bootstrap. + if ($container->has('ServiceListener')) { + return $pluginManager; + } + + // If we do not have a config service, nothing more to do + if (! $container->has('config')) { + return $pluginManager; + } + + $config = $container->get('config'); + + // If we do not have validators configuration, nothing more to do + if (! isset($config['validators']) || ! is_array($config['validators'])) { + return $pluginManager; + } + + // Wire service configuration for validators + (new Config($config['validators']))->configureServiceManager($pluginManager); + + return $pluginManager; } /** diff --git a/test/ValidatorPluginManagerFactoryTest.php b/test/ValidatorPluginManagerFactoryTest.php index 06ac39b5b..eb2651d16 100644 --- a/test/ValidatorPluginManagerFactoryTest.php +++ b/test/ValidatorPluginManagerFactoryTest.php @@ -9,6 +9,7 @@ use Interop\Container\ContainerInterface; use PHPUnit\Framework\TestCase; +use Zend\Validator\Digits; use Zend\Validator\ValidatorInterface; use Zend\Validator\ValidatorPluginManager; use Zend\Validator\ValidatorPluginManagerFactory; @@ -70,4 +71,105 @@ public function testFactoryConfiguresPluginManagerUnderServiceManagerV2() $validators = $factory->createService($container->reveal()); $this->assertSame($validator, $validators->get('test')); } + + public function testConfiguresValidatorServicesWhenFound() + { + $validator = $this->prophesize(ValidatorInterface::class)->reveal(); + $config = [ + 'validators' => [ + 'aliases' => [ + 'test' => Digits::class, + ], + 'factories' => [ + 'test-too' => function ($container) use ($validator) { + return $validator; + }, + ], + ], + ]; + + $container = $this->prophesize(ServiceLocatorInterface::class); + $container->willImplement(ContainerInterface::class); + + $container->has('ServiceListener')->willReturn(false); + $container->has('config')->willReturn(true); + $container->get('config')->willReturn($config); + $container->has('MvcTranslator')->willReturn(false); // necessary due to default initializers + + $factory = new ValidatorPluginManagerFactory(); + $validators = $factory($container->reveal(), 'FormElementManager'); + + $this->assertInstanceOf(ValidatorPluginManager::class, $validators); + $this->assertTrue($validators->has('test')); + $this->assertInstanceOf(Digits::class, $validators->get('test')); + $this->assertTrue($validators->has('test-too')); + $this->assertSame($validator, $validators->get('test-too')); + } + + public function testDoesNotConfigureValidatorServicesWhenServiceListenerPresent() + { + $validator = $this->prophesize(ValidatorInterface::class)->reveal(); + $config = [ + 'validators' => [ + 'aliases' => [ + 'test' => Digits::class, + ], + 'factories' => [ + 'test-too' => function ($container) use ($validator) { + return $validator; + }, + ], + ], + ]; + + $container = $this->prophesize(ServiceLocatorInterface::class); + $container->willImplement(ContainerInterface::class); + + $container->has('ServiceListener')->willReturn(true); + $container->has('config')->shouldNotBeCalled(); + $container->get('config')->shouldNotBeCalled(); + $container->has('MvcTranslator')->willReturn(false); // necessary due to default initializers + + $factory = new ValidatorPluginManagerFactory(); + $validators = $factory($container->reveal(), 'FormElementManager'); + + $this->assertInstanceOf(ValidatorPluginManager::class, $validators); + $this->assertFalse($validators->has('test')); + $this->assertFalse($validators->has('test-too')); + } + + public function testDoesNotConfigureValidatorServicesWhenConfigServiceNotPresent() + { + $validator = $this->prophesize(ValidatorInterface::class)->reveal(); + $container = $this->prophesize(ServiceLocatorInterface::class); + $container->willImplement(ContainerInterface::class); + + $container->has('ServiceListener')->willReturn(false); + $container->has('config')->willReturn(false); + $container->get('config')->shouldNotBeCalled(); + $container->has('MvcTranslator')->willReturn(false); // necessary due to default initializers + + $factory = new ValidatorPluginManagerFactory(); + $validators = $factory($container->reveal(), 'FormElementManager'); + + $this->assertInstanceOf(ValidatorPluginManager::class, $validators); + } + + public function testDoesNotConfigureValidatorServicesWhenConfigServiceDoesNotContainValidatorsConfig() + { + $validator = $this->prophesize(ValidatorInterface::class)->reveal(); + $container = $this->prophesize(ServiceLocatorInterface::class); + $container->willImplement(ContainerInterface::class); + + $container->has('ServiceListener')->willReturn(false); + $container->has('config')->willReturn(true); + $container->get('config')->willReturn(['foo' => 'bar']); + $container->has('MvcTranslator')->willReturn(false); // necessary due to default initializers + + $factory = new ValidatorPluginManagerFactory(); + $validators = $factory($container->reveal(), 'FormElementManager'); + + $this->assertInstanceOf(ValidatorPluginManager::class, $validators); + $this->assertFalse($validators->has('foo')); + } } From 4b9fca2c65a186c292c3e3df228007158ea5e491 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 16 May 2017 15:37:51 -0500 Subject: [PATCH 16/58] Use `ValidatorManager`, not `FormElementManager`, as requested service name Doesn't affect tests, but it removes a WTF. --- test/ValidatorPluginManagerFactoryTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/ValidatorPluginManagerFactoryTest.php b/test/ValidatorPluginManagerFactoryTest.php index eb2651d16..6ae0ba88c 100644 --- a/test/ValidatorPluginManagerFactoryTest.php +++ b/test/ValidatorPluginManagerFactoryTest.php @@ -97,7 +97,7 @@ public function testConfiguresValidatorServicesWhenFound() $container->has('MvcTranslator')->willReturn(false); // necessary due to default initializers $factory = new ValidatorPluginManagerFactory(); - $validators = $factory($container->reveal(), 'FormElementManager'); + $validators = $factory($container->reveal(), 'ValidatorManager'); $this->assertInstanceOf(ValidatorPluginManager::class, $validators); $this->assertTrue($validators->has('test')); @@ -131,7 +131,7 @@ public function testDoesNotConfigureValidatorServicesWhenServiceListenerPresent( $container->has('MvcTranslator')->willReturn(false); // necessary due to default initializers $factory = new ValidatorPluginManagerFactory(); - $validators = $factory($container->reveal(), 'FormElementManager'); + $validators = $factory($container->reveal(), 'ValidatorManager'); $this->assertInstanceOf(ValidatorPluginManager::class, $validators); $this->assertFalse($validators->has('test')); @@ -150,7 +150,7 @@ public function testDoesNotConfigureValidatorServicesWhenConfigServiceNotPresent $container->has('MvcTranslator')->willReturn(false); // necessary due to default initializers $factory = new ValidatorPluginManagerFactory(); - $validators = $factory($container->reveal(), 'FormElementManager'); + $validators = $factory($container->reveal(), 'ValidatorManager'); $this->assertInstanceOf(ValidatorPluginManager::class, $validators); } @@ -167,7 +167,7 @@ public function testDoesNotConfigureValidatorServicesWhenConfigServiceDoesNotCon $container->has('MvcTranslator')->willReturn(false); // necessary due to default initializers $factory = new ValidatorPluginManagerFactory(); - $validators = $factory($container->reveal(), 'FormElementManager'); + $validators = $factory($container->reveal(), 'ValidatorManager'); $this->assertInstanceOf(ValidatorPluginManager::class, $validators); $this->assertFalse($validators->has('foo')); From e29ddafef5e287feeaa36f302a63918f48b76b27 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 16 May 2017 15:44:10 -0500 Subject: [PATCH 17/58] Remove unnecessary mocks - No ValidatorInterface instance was needed for last two test cases --- test/ValidatorPluginManagerFactoryTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/ValidatorPluginManagerFactoryTest.php b/test/ValidatorPluginManagerFactoryTest.php index 6ae0ba88c..e91e43465 100644 --- a/test/ValidatorPluginManagerFactoryTest.php +++ b/test/ValidatorPluginManagerFactoryTest.php @@ -140,7 +140,6 @@ public function testDoesNotConfigureValidatorServicesWhenServiceListenerPresent( public function testDoesNotConfigureValidatorServicesWhenConfigServiceNotPresent() { - $validator = $this->prophesize(ValidatorInterface::class)->reveal(); $container = $this->prophesize(ServiceLocatorInterface::class); $container->willImplement(ContainerInterface::class); @@ -157,7 +156,6 @@ public function testDoesNotConfigureValidatorServicesWhenConfigServiceNotPresent public function testDoesNotConfigureValidatorServicesWhenConfigServiceDoesNotContainValidatorsConfig() { - $validator = $this->prophesize(ValidatorInterface::class)->reveal(); $container = $this->prophesize(ServiceLocatorInterface::class); $container->willImplement(ContainerInterface::class); From 803b79139ff03b98a48a3bd5b849ff48ae6ea44b Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 16:00:32 -0500 Subject: [PATCH 18/58] Added CHANGELOG for #168 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cad5eaf0f..d36f2f3d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,11 @@ All notable changes to this project will be documented in this file, in reverse ### Fixed -- Nothing. +- [#168](https://github.com/zendframework/zend-validator/pull/168) fixes how the + `ValidatorPluginManagerFactory` factory initializes the plugin manager instance, + ensuring it is injecting the relevant configuration from the `config` service + and thus seeding it with configured validator services. This means + that the `validators` configuration will now be honored in non-zend-mvc contexts. ## 2.9.0 - 2017-03-17 From d35e36456d4e2f4c9cde63739349177faa1310dd Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 16:02:31 -0500 Subject: [PATCH 19/58] Do not notify IRC any more. --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2cde30890..44b0eb45e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -112,11 +112,6 @@ after_success: notifications: email: false - irc: - channels: - - "irc.freenode.org#zftalk.dev" - on_success: change - on_failure: always slack: rooms: - secure: "ujQTv4jUDjnWAUME3w2VoPyKPBwbCGa7b1YMKxOt4PWxjWuyIUDY743fnmcQUqkX+CUXz9qJHG124RyZQ6UUowG/NZDttp7lppU/bIGJ/K0MuVmpBVwb8y/6rDoRj37V4n8WqAHGevjlRet3E5+gl91PFuSpN5JSj4efI8MlgUF6mqIIZUOifq2yNTZ9MXrG2qojIN4o6G4gttfwUR/3Ah1nD/ZtQBLA7pTd31/UNwtZMQ4IbGmcCMpdUADbQDr24VubjzTJfweSBoAu8Xf3IPPdR5AEfdRvuT1tGYPP4YxmvHxpTH1wF3mCX6b6ubUFMlpbqE50y/v4Mlva+2jXvcZ9Lt/Fs1Hz4pR3P3mM8EJjtj55cXWm+MSBqPBN7SX6AnkYB/OznuzqbCvt5Te09fm++1REYGnxkLxCnwI9GN2sKS7Tr8NxUCZyi9d4sVh7KUnrwFGVAGpViBTeglq+epoClcwupLK1E2m8IjrjUHTYG6NCE1QF/1NrrwuFBUIQuxPj/uE4oZcb8Tmiz9ilGFw/JbMR6WKzYzDSk2GrkJjUpa8Pn570kDL3otfJImPnEOxN73m9jle6P3laVJP9/A5Vm9C86S0aghAaswxCZB7Fql0Pl01WAB1kufqQBM/euFWGB0bQ9TFWoOENZDfD9zGyTkeZq3mgZ6DgU7ft6FA=" From 5586da983d736c2f1a491bd86685736803139ea7 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 16:15:56 -0500 Subject: [PATCH 20/58] Added CHANGELOG for #154 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d36f2f3d1..432187a24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ All notable changes to this project will be documented in this file, in reverse - Nothing. +### Changes + +- [#154](https://github.com/zendframework/zend-validator/pull/154) updates the + `CreditCard` validator to allow 19 digit Discover card values, and 13 and 19 + digit Visa card values, which are now allowed (see + https://en.wikipedia.org/wiki/Payment_card_number). + ### Deprecated - Nothing. From a769a4c0d277062e111d22f455920b29d940f340 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 16:18:15 -0500 Subject: [PATCH 21/58] Added CHANGELOG for #162 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 432187a24..9021c76fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ All notable changes to this project will be documented in this file, in reverse `CreditCard` validator to allow 19 digit Discover card values, and 13 and 19 digit Visa card values, which are now allowed (see https://en.wikipedia.org/wiki/Payment_card_number). +- [#162](https://github.com/zendframework/zend-validator/pull/162) updates the + `Hostname` validator to support `.hr` (Croatia) IDN domains. ### Deprecated From 28adfffc50ef012da9e6537b61d4099d2d1f4d4c Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 16:20:08 -0500 Subject: [PATCH 22/58] Added CHANGELOG for #163 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9021c76fa..75fbc41c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ All notable changes to this project will be documented in this file, in reverse https://en.wikipedia.org/wiki/Payment_card_number). - [#162](https://github.com/zendframework/zend-validator/pull/162) updates the `Hostname` validator to support `.hr` (Croatia) IDN domains. +- [#163](https://github.com/zendframework/zend-validator/pull/163) updates the + `Iban` validator to support Belarus. ### Deprecated From 620705731181d38428417d0bfc7f618ed19887fe Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 16:21:03 -0500 Subject: [PATCH 23/58] CS fixes per phpcs --- src/Barcode/Code128.php | 4 ++-- src/EmailAddress.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Barcode/Code128.php b/src/Barcode/Code128.php index 21861de98..613b8a98d 100644 --- a/src/Barcode/Code128.php +++ b/src/Barcode/Code128.php @@ -180,10 +180,10 @@ public function hasValidCharacters($value) break; } - if (!empty($value) && strlen($value) > 1) { + if (! empty($value) && strlen($value) > 1) { $value = $strWrapper->substr($value, 1, null); } - + $read = $set; } diff --git a/src/EmailAddress.php b/src/EmailAddress.php index c9c750d78..d089f84c0 100644 --- a/src/EmailAddress.php +++ b/src/EmailAddress.php @@ -545,7 +545,7 @@ protected function idnToUtf8($email) if (strlen($email) == 0) { return $email; } - + if (extension_loaded('intl')) { // The documentation does not clarify what kind of failure // can happen in idn_to_utf8. One can assume if the source From cb230086608eeec3424fed11a6dcb425a9b9028c Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 16:58:52 -0500 Subject: [PATCH 24/58] Revert "Merge pull request #105 from FraGoTe/patch-1" This reverts commit 1fbb4fdf675769615a99726629076facac411854, reversing changes made to 95c93865633bc65209806d1eba3c00a60383c5db. The revert was performed as it introduced an infinite loop in the `BarcodeTest::testCODE128()` test case. Conflicts: src/Barcode/Code128.php --- src/Barcode/Code128.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Barcode/Code128.php b/src/Barcode/Code128.php index 613b8a98d..2daee6d1a 100644 --- a/src/Barcode/Code128.php +++ b/src/Barcode/Code128.php @@ -180,10 +180,7 @@ public function hasValidCharacters($value) break; } - if (! empty($value) && strlen($value) > 1) { - $value = $strWrapper->substr($value, 1, null); - } - + $value = $strWrapper->substr($value, 1, null); $read = $set; } From f4fcf425a17014c4098cba7f77fcb9ed7abd20ce Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 17:05:06 -0500 Subject: [PATCH 25/58] Updated TLD list --- src/Hostname.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Hostname.php b/src/Hostname.php index 709eabe79..12e65c7f6 100644 --- a/src/Hostname.php +++ b/src/Hostname.php @@ -69,7 +69,7 @@ class Hostname extends AbstractValidator /** * Array of valid top-level-domains - * IanaVersion 2017031600 + * IanaVersion 2017051700 * * @see ftp://data.iana.org/TLD/tlds-alpha-by-domain.txt List of all TLDs by domain * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs @@ -662,6 +662,7 @@ class Hostname extends AbstractValidator 'hosting', 'hot', 'hoteles', + 'hotels', 'hotmail', 'house', 'how', @@ -920,7 +921,6 @@ class Hostname extends AbstractValidator 'msd', 'mt', 'mtn', - 'mtpc', 'mtr', 'mu', 'museum', @@ -1003,7 +1003,6 @@ class Hostname extends AbstractValidator 'orange', 'org', 'organic', - 'orientexpress', 'origins', 'osaka', 'otsuka', @@ -1132,6 +1131,7 @@ class Hostname extends AbstractValidator 'rs', 'rsvp', 'ru', + 'rugby', 'ruhr', 'run', 'rw', From d44a1ab143c8604b98c036eade1601d5fdb1ee94 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 17:06:05 -0500 Subject: [PATCH 26/58] Added date to 2.9.1 in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75fbc41c9..1cd730588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 2.9.1 - TBD +## 2.9.1 - 2017-05-17 ### Added From e0eb5cfc2eca0919c48fc3c0b0870a60ed1c0b0d Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 17 May 2017 17:07:11 -0500 Subject: [PATCH 27/58] Bumped to next dev version (2.9.2) --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cd730588..ef0225836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 2.9.2 - TBD + +### Added + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 2.9.1 - 2017-05-17 ### Added From 33591b684a405dd07617afbd0a86b4dcb5b6caa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Nystr=C3=B6m?= Date: Tue, 23 May 2017 14:41:09 +0200 Subject: [PATCH 28/58] Fix #165 - Use is_readable() instead of stream_resolve_include_path() --- src/File/Crc32.php | 4 ++-- src/File/ExcludeExtension.php | 2 +- src/File/ExcludeMimeType.php | 2 +- src/File/Extension.php | 2 +- src/File/FilesSize.php | 12 ++++++------ src/File/Hash.php | 4 ++-- src/File/ImageSize.php | 10 +++++----- src/File/Md5.php | 4 ++-- src/File/MimeType.php | 6 +++--- src/File/Sha1.php | 4 ++-- src/File/Size.php | 14 +++++++------- src/File/WordCount.php | 12 ++++++------ 12 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/File/Crc32.php b/src/File/Crc32.php index db8aac806..ba5d1c448 100644 --- a/src/File/Crc32.php +++ b/src/File/Crc32.php @@ -104,12 +104,12 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_FOUND); return false; } - $hashes = array_unique(array_keys($this->getHash())); + $hashes = array_unique(array_keys($this->getHash())); $filehash = hash_file('crc32', $file); if ($filehash === false) { $this->error(self::NOT_DETECTED); diff --git a/src/File/ExcludeExtension.php b/src/File/ExcludeExtension.php index e06830788..1d1cb4218 100644 --- a/src/File/ExcludeExtension.php +++ b/src/File/ExcludeExtension.php @@ -59,7 +59,7 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_FOUND); return false; } diff --git a/src/File/ExcludeMimeType.php b/src/File/ExcludeMimeType.php index 2f6b5771e..66c8eadb6 100644 --- a/src/File/ExcludeMimeType.php +++ b/src/File/ExcludeMimeType.php @@ -63,7 +63,7 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_READABLE); return false; } diff --git a/src/File/Extension.php b/src/File/Extension.php index 6f2287840..0918474bf 100644 --- a/src/File/Extension.php +++ b/src/File/Extension.php @@ -196,7 +196,7 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_FOUND); return false; } diff --git a/src/File/FilesSize.php b/src/File/FilesSize.php index 237f417cd..079c7a291 100644 --- a/src/File/FilesSize.php +++ b/src/File/FilesSize.php @@ -103,12 +103,12 @@ public function isValid($value, $file = null) 'Value array must be in $_FILES format' ); } - $file = $files; + $file = $files; $files = $files['tmp_name']; } // Is file readable ? - if (empty($files) || false === stream_resolve_include_path($files)) { + if (empty($files) || false === is_readable($files)) { $this->throwError($file, self::NOT_READABLE); continue; } @@ -128,10 +128,10 @@ public function isValid($value, $file = null) if (($max !== null) && ($max < $size)) { if ($this->getByteString()) { $this->options['max'] = $this->toByteString($max); - $this->size = $this->toByteString($size); + $this->size = $this->toByteString($size); $this->throwError($file, self::TOO_BIG); $this->options['max'] = $max; - $this->size = $size; + $this->size = $size; } else { $this->throwError($file, self::TOO_BIG); } @@ -142,10 +142,10 @@ public function isValid($value, $file = null) if (($min !== null) && ($size < $min)) { if ($this->getByteString()) { $this->options['min'] = $this->toByteString($min); - $this->size = $this->toByteString($size); + $this->size = $this->toByteString($size); $this->throwError($file, self::TOO_SMALL); $this->options['min'] = $min; - $this->size = $size; + $this->size = $size; } else { $this->throwError($file, self::TOO_SMALL); } diff --git a/src/File/Hash.php b/src/File/Hash.php index e28e8afb6..b1a5fa484 100644 --- a/src/File/Hash.php +++ b/src/File/Hash.php @@ -90,8 +90,8 @@ public function setHash($options) * Adds the hash for one or multiple files * * @param string|array $options - * @return Hash Provides a fluent interface * @throws Exception\InvalidArgumentException + * @return Hash Provides a fluent interface */ public function addHash($options) { @@ -148,7 +148,7 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_FOUND); return false; } diff --git a/src/File/ImageSize.php b/src/File/ImageSize.php index 023a86b40..bb7433766 100644 --- a/src/File/ImageSize.php +++ b/src/File/ImageSize.php @@ -124,8 +124,8 @@ public function getMinWidth() * Sets the minimum allowed width * * @param int $minWidth - * @return ImageSize Provides a fluid interface * @throws Exception\InvalidArgumentException When minwidth is greater than maxwidth + * @return ImageSize Provides a fluid interface */ public function setMinWidth($minWidth) { @@ -154,8 +154,8 @@ public function getMaxWidth() * Sets the maximum allowed width * * @param int $maxWidth - * @return ImageSize Provides a fluid interface * @throws Exception\InvalidArgumentException When maxwidth is less than minwidth + * @return ImageSize Provides a fluid interface */ public function setMaxWidth($maxWidth) { @@ -184,8 +184,8 @@ public function getMinHeight() * Sets the minimum allowed height * * @param int $minHeight - * @return ImageSize Provides a fluid interface * @throws Exception\InvalidArgumentException When minheight is greater than maxheight + * @return ImageSize Provides a fluid interface */ public function setMinHeight($minHeight) { @@ -214,8 +214,8 @@ public function getMaxHeight() * Sets the maximum allowed height * * @param int $maxHeight - * @return ImageSize Provides a fluid interface * @throws Exception\InvalidArgumentException When maxheight is less than minheight + * @return ImageSize Provides a fluid interface */ public function setMaxHeight($maxHeight) { @@ -351,7 +351,7 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_READABLE); return false; } diff --git a/src/File/Md5.php b/src/File/Md5.php index 2a88e4084..c36b8a457 100644 --- a/src/File/Md5.php +++ b/src/File/Md5.php @@ -104,12 +104,12 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_FOUND); return false; } - $hashes = array_unique(array_keys($this->getHash())); + $hashes = array_unique(array_keys($this->getHash())); $filehash = hash_file('md5', $file); if ($filehash === false) { $this->error(self::NOT_DETECTED); diff --git a/src/File/MimeType.php b/src/File/MimeType.php index 68e7dd8e3..4a4ed819f 100644 --- a/src/File/MimeType.php +++ b/src/File/MimeType.php @@ -176,10 +176,10 @@ public function getMagicFile() * if false, the default MAGIC file from PHP will be used * * @param string $file - * @return MimeType Provides fluid interface * @throws Exception\RuntimeException When finfo can not read the magicfile * @throws Exception\InvalidArgumentException * @throws Exception\InvalidMagicMimeFileException + * @return MimeType Provides fluid interface */ public function setMagicFile($file) { @@ -291,8 +291,8 @@ public function setMimeType($mimetype) * Adds the mimetypes * * @param string|array $mimetype The mimetypes to add for validation - * @return MimeType Provides a fluent interface * @throws Exception\InvalidArgumentException + * @return MimeType Provides a fluent interface */ public function addMimeType($mimetype) { @@ -363,7 +363,7 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(static::NOT_READABLE); return false; } diff --git a/src/File/Sha1.php b/src/File/Sha1.php index 2a59039c2..bc3929d1f 100644 --- a/src/File/Sha1.php +++ b/src/File/Sha1.php @@ -104,12 +104,12 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_FOUND); return false; } - $hashes = array_unique(array_keys($this->getHash())); + $hashes = array_unique(array_keys($this->getHash())); $filehash = hash_file('sha1', $file); if ($filehash === false) { $this->error(self::NOT_DETECTED); diff --git a/src/File/Size.php b/src/File/Size.php index d8ac7481b..b698f8417 100644 --- a/src/File/Size.php +++ b/src/File/Size.php @@ -136,8 +136,8 @@ public function getMin($raw = false) * For example: 2000, 2MB, 0.2GB * * @param int|string $min The minimum file size - * @return Size Provides a fluent interface * @throws Exception\InvalidArgumentException When min is greater than max + * @return Size Provides a fluent interface */ public function setMin($min) { @@ -181,8 +181,8 @@ public function getMax($raw = false) * For example: 2000, 2MB, 0.2GB * * @param int|string $max The maximum file size - * @return Size Provides a fluent interface * @throws Exception\InvalidArgumentException When max is smaller than min + * @return Size Provides a fluent interface */ public function setMax($max) { @@ -253,7 +253,7 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_FOUND); return false; } @@ -270,10 +270,10 @@ public function isValid($value, $file = null) if (($min !== null) && ($size < $min)) { if ($this->getByteString()) { $this->options['min'] = $this->toByteString($min); - $this->size = $this->toByteString($size); + $this->size = $this->toByteString($size); $this->error(self::TOO_SMALL); $this->options['min'] = $min; - $this->size = $size; + $this->size = $size; } else { $this->error(self::TOO_SMALL); } @@ -283,10 +283,10 @@ public function isValid($value, $file = null) if (($max !== null) && ($max < $size)) { if ($this->getByteString()) { $this->options['max'] = $this->toByteString($max); - $this->size = $this->toByteString($size); + $this->size = $this->toByteString($size); $this->error(self::TOO_BIG); $this->options['max'] = $max; - $this->size = $size; + $this->size = $size; } else { $this->error(self::TOO_BIG); } diff --git a/src/File/WordCount.php b/src/File/WordCount.php index 61145597a..a725de394 100644 --- a/src/File/WordCount.php +++ b/src/File/WordCount.php @@ -28,8 +28,8 @@ class WordCount extends AbstractValidator * @var array Error message templates */ protected $messageTemplates = [ - self::TOO_MUCH => "Too many words, maximum '%max%' are allowed but '%count%' were counted", - self::TOO_LESS => "Too few words, minimum '%min%' are expected but '%count%' were counted", + self::TOO_MUCH => "Too many words, maximum '%max%' are allowed but '%count%' were counted", + self::TOO_LESS => "Too few words, minimum '%min%' are expected but '%count%' were counted", self::NOT_FOUND => "File is not readable or does not exist", ]; @@ -75,7 +75,7 @@ class WordCount extends AbstractValidator public function __construct($options = null) { if (1 < func_num_args()) { - $args = func_get_args(); + $args = func_get_args(); $options = [ 'min' => array_shift($args), 'max' => array_shift($args), @@ -103,8 +103,8 @@ public function getMin() * Sets the minimum word count * * @param int|array $min The minimum word count - * @return WordCount Provides a fluent interface * @throws Exception\InvalidArgumentException When min is greater than max + * @return WordCount Provides a fluent interface */ public function setMin($min) { @@ -141,8 +141,8 @@ public function getMax() * Sets the maximum file count * * @param int|array $max The maximum word count - * @return WordCount Provides a fluent interface * @throws Exception\InvalidArgumentException When max is smaller than min + * @return WordCount Provides a fluent interface */ public function setMax($max) { @@ -194,7 +194,7 @@ public function isValid($value, $file = null) $this->setValue($filename); // Is file readable ? - if (empty($file) || false === stream_resolve_include_path($file)) { + if (empty($file) || false === is_readable($file)) { $this->error(self::NOT_FOUND); return false; } From 9c1077551fc7327704610b3a52ac721820b4caaa Mon Sep 17 00:00:00 2001 From: NathanS Date: Thu, 15 Jun 2017 10:01:06 -0700 Subject: [PATCH 29/58] Corrected Date validator format docs --- doc/book/validators/date.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/book/validators/date.md b/doc/book/validators/date.md index 133792677..83de95a40 100644 --- a/doc/book/validators/date.md +++ b/doc/book/validators/date.md @@ -25,7 +25,7 @@ $validator->isValid('10.10.2000'); // returns false `Zend\Validator\Date` also supports custom date formats. When you want to validate such a date, use the `format` option. This option accepts any format -allowed by the PHP [date()](http://php.net/date) function. +allowed by the PHP [DateTime::createFromFormat()](http://php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters) method. ```php $validator = new Zend\Validator\Date(['format' => 'Y']); From 9d26add4d02faa225f5a328bd77df7f9503aec9b Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Fri, 23 Jun 2017 11:46:00 +0200 Subject: [PATCH 30/58] Adapt .travis.yml to recent ZF standards --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 44b0eb45e..7b6c5acc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ branches: cache: directories: - - $HOME/.composer/cache + - $HOME/.composer/ - $HOME/.local - zf-mkdoc-theme @@ -91,7 +91,7 @@ install: - travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs - if [[ $TRAVIS_PHP_VERSION =~ ^5.6 ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi - - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi + - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update $COMPOSER_ARGS --prefer-lowest --prefer-stable ; fi - if [[ $SERVICE_MANAGER_VERSION != '' ]]; then travis_retry composer require --dev --no-update $COMPOSER_ARGS "zendframework/zend-servicemanager:$SERVICE_MANAGER_VERSION" ; fi - if [[ $SERVICE_MANAGER_VERSION == '' ]]; then travis_retry composer require --dev --no-update $COMPOSER_ARGS "zendframework/zend-servicemanager:^3.0.3" ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi @@ -104,12 +104,12 @@ script: - if [[ $EXECUTE_HOSTNAME_CHECK == "true" && $TRAVIS_PULL_REQUEST == "false" ]]; then php bin/update_hostname_validator.php --check-only; fi - if [[ $DEPLOY_DOCS == "true" && "$TRAVIS_TEST_RESULT" == "0" ]]; then wget -O theme-installer.sh "https://raw.githubusercontent.com/zendframework/zf-mkdoc-theme/master/theme-installer.sh" ; chmod 755 theme-installer.sh ; ./theme-installer.sh ; fi -after_script: - - if [[ $TEST_COVERAGE == 'true' ]]; then composer upload-coverage ; fi - after_success: - if [[ $DEPLOY_DOCS == "true" ]]; then echo "Preparing to build and deploy documentation" ; ./zf-mkdoc-theme/deploy.sh ; echo "Completed deploying documentation" ; fi +after_script: + - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer upload-coverage ; fi + notifications: email: false slack: From 92035a47e70a0dc726698e0eac902653b8587256 Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Fri, 23 Jun 2017 11:46:47 +0200 Subject: [PATCH 31/58] Travis: drop HVVM & add PHP 7.2 --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7b6c5acc3..44944de12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,21 +67,21 @@ matrix: - php: 7.1 env: - DEPS=latest - - php: hhvm + - php: nightly env: - DEPS=lowest - - php: hhvm + - php: nightly env: - DEPS=locked - - php: hhvm + - php: nightly env: - DEPS=latest - - php: hhvm + - php: nightly env: - DEPS=locked - SERVICE_MANAGER_VERSION="^2.7.5" allow_failures: - - php: hhvm + - php: nightly before_install: - if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi From 1a0717cd4f2e74ff947c3e068181718a186b37f2 Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Fri, 23 Jun 2017 11:57:39 +0200 Subject: [PATCH 32/58] Prepare to PHP 7.2 --- bin/update_hostname_validator.php | 4 +++- src/EmailAddress.php | 4 ++-- src/File/Upload.php | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/bin/update_hostname_validator.php b/bin/update_hostname_validator.php index fab52c24e..49070cbb5 100644 --- a/bin/update_hostname_validator.php +++ b/bin/update_hostname_validator.php @@ -177,7 +177,9 @@ function getNewValidTlds($string) function getPunycodeDecoder() { if (function_exists('idn_to_utf8')) { - return 'idn_to_utf8'; + return function($domain) { + return idn_to_utf8($domain, 0, INTL_IDNA_VARIANT_UTS46); + }; } $hostnameValidator = new Hostname(); diff --git a/src/EmailAddress.php b/src/EmailAddress.php index d089f84c0..55c450eef 100644 --- a/src/EmailAddress.php +++ b/src/EmailAddress.php @@ -530,7 +530,7 @@ public function isValid($value) protected function idnToAscii($email) { if (extension_loaded('intl')) { - return (idn_to_ascii($email) ?: $email); + return (idn_to_ascii($email, 0, INTL_IDNA_VARIANT_UTS46) ?: $email); } return $email; } @@ -553,7 +553,7 @@ protected function idnToUtf8($email) // the source string in those cases. // But not when the source string is long enough. // Thus we default to source string ourselves. - return idn_to_utf8($email) ?: $email; + return idn_to_utf8($email, 0, INTL_IDNA_VARIANT_UTS46) ?: $email; } return $email; } diff --git a/src/File/Upload.php b/src/File/Upload.php index 55025f633..724a8987d 100644 --- a/src/File/Upload.php +++ b/src/File/Upload.php @@ -9,6 +9,7 @@ namespace Zend\Validator\File; +use Countable; use Zend\Validator\AbstractValidator; use Zend\Validator\Exception; @@ -109,7 +110,7 @@ public function getFiles($file = null) */ public function setFiles($files = []) { - if (count($files) === 0) { + if ((is_array($files) || $files instanceof Countable) && count($files) === 0) { $this->options['files'] = $_FILES; } else { $this->options['files'] = $files; From 407ee843496ebed629e3816f01d1c4f53f9ae230 Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Fri, 23 Jun 2017 16:49:15 +0200 Subject: [PATCH 33/58] In PHP < 7.2 `0 === count(null)` --- src/File/Upload.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/File/Upload.php b/src/File/Upload.php index 724a8987d..a721dfe3e 100644 --- a/src/File/Upload.php +++ b/src/File/Upload.php @@ -110,7 +110,7 @@ public function getFiles($file = null) */ public function setFiles($files = []) { - if ((is_array($files) || $files instanceof Countable) && count($files) === 0) { + if (null === $files || ((is_array($files) || $files instanceof Countable) && count($files) === 0)) { $this->options['files'] = $_FILES; } else { $this->options['files'] = $files; From 4b61f11d053ac26a8e3414d7cbd5da0fbdd31377 Mon Sep 17 00:00:00 2001 From: codisart Date: Mon, 19 Jun 2017 23:17:44 +0200 Subject: [PATCH 34/58] Fix Between validator behavior return false when validating a string with a numeric range return false when validating a numeric value with a string range --- src/Between.php | 26 ++++++++++++- test/BetweenTest.php | 91 +++++++++++++++++++++++++++++++------------- 2 files changed, 90 insertions(+), 27 deletions(-) diff --git a/src/Between.php b/src/Between.php index 386393dbd..6f99ea81a 100644 --- a/src/Between.php +++ b/src/Between.php @@ -17,6 +17,13 @@ class Between extends AbstractValidator const NOT_BETWEEN = 'notBetween'; const NOT_BETWEEN_STRICT = 'notBetweenStrict'; + /** + * Retain if min and max are numeric values. Allow to not compare string and numeric types + * + * @var boolean + */ + private $numeric; + /** * Validation failure message template definitions * @@ -81,7 +88,17 @@ public function __construct($options = null) 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"); + throw new Exception\InvalidArgumentException("Missing option : 'min' and 'max' have to be given"); + } + + if (is_numeric($options['min']) && is_numeric($options['max'])) { + $this->numeric = true; + } elseif (is_string($options['min']) && is_string($options['max'])) { + $this->numeric = false; + } else { + throw new Exception\InvalidArgumentException( + "Invalid options : 'min' and 'max' should be of the same scalar type" + ); } parent::__construct($options); @@ -162,6 +179,13 @@ public function setInclusive($inclusive) */ public function isValid($value) { + if ($this->numeric && ! is_numeric($value)) { + return false; + } + if (! $this->numeric && ! is_string($value)) { + return false; + } + $this->setValue($value); if ($this->getInclusive()) { diff --git a/test/BetweenTest.php b/test/BetweenTest.php index bf86e4e5f..caceecf00 100644 --- a/test/BetweenTest.php +++ b/test/BetweenTest.php @@ -18,37 +18,76 @@ */ class BetweenTest extends TestCase { + public function providerBasic() + { + return [ + [ + 'min' => 1, + 'max' => 100, + 'inclusive' => true, + 'expected' => true, + 'values' => [1, 10, 100], + ], + [ + 'min' => 1, + 'max' => 100, + 'inclusive' => true, + 'expected' => false, + 'values' => [0, 0.99, 100.01, 101], + ], + [ + 'min' => 1, + 'max' => 100, + 'inclusive' => false, + 'expected' => false, + 'values' => [0, 1, 100, 101], + ], + [ + 'min' => 'a', + 'max' => 'z', + 'inclusive' => true, + 'expected' => true, + 'values' => ['a', 'b', 'y', 'z'], + ], + [ + 'min' => 'a', + 'max' => 'z', + 'inclusive' => false, + 'expected' => false, + 'values' => ['!', 'a', 'z'], + ], + [ + 'min' => 0, + 'max' => 99999999, + 'inclusive' => true, + 'expected' => false, + 'values' => ['asdasd', 'q'], + ], + [ + 'min' => 'a', + 'max' => 'zzzzz', + 'inclusive' => true, + 'expected' => false, + 'values' => [0, 10, 548], + ], + ]; + } /** * Ensures that the validator follows expected behavior * + * @dataProvider providerBasic * @return void */ - public function testBasic() + public function testBasic($min, $max, $inclusive, $expected, $values) { - /** - * The elements of each array are, in order: - * - minimum - * - maximum - * - inclusive - * - expected validation result - * - array of test input values - */ - $valuesExpected = [ - [1, 100, true, true, [1, 10, 100]], - [1, 100, true, false, [0, 0.99, 100.01, 101]], - [1, 100, false, false, [0, 1, 100, 101]], - ['a', 'z', true, true, ['a', 'b', 'y', 'z']], - ['a', 'z', false, false, ['!', 'a', 'z']] - ]; - foreach ($valuesExpected as $element) { - $validator = new Between(['min' => $element[0], 'max' => $element[1], 'inclusive' => $element[2]]); - foreach ($element[4] as $input) { - $this->assertEquals( - $element[3], - $validator->isValid($input), - 'Failed values: ' . $input . ":" . implode("\n", $validator->getMessages()) - ); - } + $validator = new Between(['min' => $min, 'max' => $max, 'inclusive' => $inclusive]); + + foreach ($values as $input) { + $this->assertEquals( + $expected, + $validator->isValid($input), + 'Failed values: ' . $input . ":" . implode("\n", $validator->getMessages()) + ); } } @@ -117,7 +156,7 @@ public function testEqualsMessageVariables() public function testMissingMinOrMax(array $args) { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Missing option. 'min' and 'max' have to be given"); + $this->expectExceptionMessage("Missing option : 'min' and 'max' have to be given"); new Between($args); } From d3a3be6a2c15c10810a992aa357fb416c7a3726f Mon Sep 17 00:00:00 2001 From: Thomas Cantonnet Date: Tue, 18 Jul 2017 15:00:13 +0200 Subject: [PATCH 35/58] Fix BC break introduced by 3fc1e66 --- src/File/MimeType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/File/MimeType.php b/src/File/MimeType.php index 68e7dd8e3..b479e8015 100644 --- a/src/File/MimeType.php +++ b/src/File/MimeType.php @@ -385,7 +385,7 @@ public function isValid($value, $file = null) $this->type = null; if (! empty($this->finfo)) { $this->type = finfo_file($this->finfo, $file); - finfo_close($this->finfo); + unset($this->finfo); } } From 39a64e601397c7dabda2205be15a1b15fab542ad Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 19 Jul 2017 17:00:05 -0500 Subject: [PATCH 36/58] Updates Hostname IDN list --- src/Hostname.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Hostname.php b/src/Hostname.php index 12e65c7f6..bfe4a444d 100644 --- a/src/Hostname.php +++ b/src/Hostname.php @@ -69,7 +69,7 @@ class Hostname extends AbstractValidator /** * Array of valid top-level-domains - * IanaVersion 2017051700 + * IanaVersion 2017071801 * * @see ftp://data.iana.org/TLD/tlds-alpha-by-domain.txt List of all TLDs by domain * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs @@ -144,6 +144,7 @@ class Hostname extends AbstractValidator 'aq', 'aquarelle', 'ar', + 'arab', 'aramco', 'archi', 'army', @@ -479,6 +480,7 @@ class Hostname extends AbstractValidator 'estate', 'esurance', 'et', + 'etisalat', 'eu', 'eurovision', 'eus', @@ -613,6 +615,7 @@ class Hostname extends AbstractValidator 'gratis', 'green', 'gripe', + 'grocery', 'group', 'gs', 'gt', @@ -853,6 +856,7 @@ class Hostname extends AbstractValidator 'man', 'management', 'mango', + 'map', 'market', 'marketing', 'markets', @@ -876,6 +880,7 @@ class Hostname extends AbstractValidator 'men', 'menu', 'meo', + 'merckmsd', 'metlife', 'mg', 'mh', @@ -1028,6 +1033,7 @@ class Hostname extends AbstractValidator 'pg', 'ph', 'pharmacy', + 'phd', 'philips', 'phone', 'photo', @@ -1173,6 +1179,7 @@ class Hostname extends AbstractValidator 'scot', 'sd', 'se', + 'search', 'seat', 'secure', 'security', @@ -1444,13 +1451,16 @@ class Hostname extends AbstractValidator 'कॉम', 'セール', '佛山', + 'ಭಾರತ', '慈善', '集团', '在线', '한국', + 'ଭାରତ', '大众汽车', '点看', 'คอม', + 'ভাৰত', 'ভারত', '八卦', 'موقع', @@ -1504,7 +1514,9 @@ class Hostname extends AbstractValidator 'クラウド', 'ભારત', '通販', + 'भारतम्', 'भारत', + 'भारोत', '网店', 'संगठन', '餐厅', @@ -1525,15 +1537,18 @@ class Hostname extends AbstractValidator 'ارامكو', 'ایران', 'العليان', + 'اتصالات', 'امارات', 'بازار', 'پاکستان', 'الاردن', 'موبايلي', + 'بارت', 'بھارت', 'المغرب', 'ابوظبي', 'السعودية', + 'ڀارت', 'كاثوليك', 'سودان', 'همراه', @@ -1544,6 +1559,7 @@ class Hostname extends AbstractValidator '政府', 'شبكة', 'بيتك', + 'عرب', 'გე', '机构', '组织机构', @@ -1560,6 +1576,7 @@ class Hostname extends AbstractValidator 'ελ', '世界', '書籍', + 'ഭാരതം', 'ਭਾਰਤ', '网址', '닷넷', From a56abf95acb27eb39945c9947436088af618dc04 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 19 Jul 2017 17:03:22 -0500 Subject: [PATCH 37/58] Adds CHANGELOG for #180 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef0225836..cedd2b7a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,10 @@ All notable changes to this project will be documented in this file, in reverse ### Fixed -- Nothing. +- [#180](https://github.com/zendframework/zend-validator/pull/180) fixes how + `Zend\Validator\File\MimeType` "closes" the open FileInfo handle for the file + being validated, using `unset()` instead of `finfo_close()`; this resolves a + segfault that occurs on older PHP versions. ## 2.9.1 - 2017-05-17 From 5de3f0d4ba962757b5cb7199bda34a793ebb8c5f Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 19 Jul 2017 17:12:06 -0500 Subject: [PATCH 38/58] Updates Travis configuration to remove unneeded functionality - Removes targets with SERVICEMANAGER; these are covered in the lowest/locked/latest strategy. - Removes documentation deployment functionality; happens via github hooks now. - Removes slack notification; happens via github hooks now. --- .travis.yml | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/.travis.yml b/.travis.yml index 44b0eb45e..af1558b78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,6 @@ branches: cache: directories: - $HOME/.composer/cache - - $HOME/.local - - zf-mkdoc-theme env: global: @@ -19,11 +17,6 @@ env: - COVERAGE_DEPS="satooshi/php-coveralls" - LEGACY_DEPS="phpunit/phpunit" - TESTS_ZEND_VALIDATOR_ONLINE_ENABLED=true - - SITE_URL=https://zendframework.github.io/zend-validator - - GH_USER_NAME="Matthew Weier O'Phinney" - - GH_USER_EMAIL=matthew@weierophinney.net - - GH_REF=github.com/zendframework/zend-validator.git - - secure="SoUsUxBFCuC0rVQyDJ/+IB38glC5WeWvg0XxtNj79di7wsQ92Jofp6Uu3NJBB8H1+at1pHetphRm4N+GPQmZGMFTG7LyF5u8duV8t4nDpAz5WfoP1y0IyacP6IrWzANeszOTZ04dlHu3dBdHusNpNxxUHl97bSx4XQUAm2GUTqNkuXNgQJFAAxx91jb5txG4W8KeMnfRm9jeDHP17BCnBMaSkYEXeLpHkYa9wA4lBJ7ZD6LuSC+MhrJCtREBTsWKLJY6xeBjRorUug+uCrNyArPtcOAaOLMSDJ1XIi3L5/Q7HdoldV7aC3V5HjNlpdIEFl33IGiCOyictFCpT1KaKx7TL8zDTMCiqe0cCyfTnq28lzULz2hXg0Kov7BFcRr2Ht/1f96RgrakWQiYTmk+C3YYYA16Fb+MndkMI3WH7WI0suC+5nhPdGl53MCWsd5x2+dDk/ifB/VvxHdGhhgxzAxsYJ41gV/LlzjbCQJNDCnTaL/GHCTUGJEPgwLrn2W52uZx6VggE9wl5z4XkiPqBy6zAAdwF55RRJgCxFttGOMVGdegFLHTf6+13S4sEImNmyVTeuJBZEHxaYRJ21wweOocjC2StKC9V54uPysDcEYwhu8WOsYU34fQdpMx3OHfPmXvhNGqoZ1rVsd5HM0QZZMT+7SI0r3UNKxrPC8LEAU=" matrix: include: @@ -35,12 +28,6 @@ matrix: - DEPS=locked - EXECUTE_HOSTNAME_CHECK=true - TEST_COVERAGE=true - - DEPLOY_DOCS="$(if [[ $TRAVIS_BRANCH == 'master' && $TRAVIS_PULL_REQUEST == 'false' ]]; then echo -n 'true' ; else echo -n 'false' ; fi)" - - PATH="$HOME/.local/bin:$PATH" - - php: 5.6 - env: - - DEPS=locked - - SERVICE_MANAGER_VERSION="^2.7.5" - php: 5.6 env: - DEPS=latest @@ -51,10 +38,6 @@ matrix: env: - DEPS=locked - CS_CHECK=true - - php: 7 - env: - - DEPS=locked - - SERVICE_MANAGER_VERSION="^2.7.5" - php: 7 env: - DEPS=latest @@ -76,10 +59,6 @@ matrix: - php: hhvm env: - DEPS=latest - - php: hhvm - env: - - DEPS=locked - - SERVICE_MANAGER_VERSION="^2.7.5" allow_failures: - php: hhvm @@ -92,8 +71,6 @@ install: - if [[ $TRAVIS_PHP_VERSION =~ ^5.6 ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi - - if [[ $SERVICE_MANAGER_VERSION != '' ]]; then travis_retry composer require --dev --no-update $COMPOSER_ARGS "zendframework/zend-servicemanager:$SERVICE_MANAGER_VERSION" ; fi - - if [[ $SERVICE_MANAGER_VERSION == '' ]]; then travis_retry composer require --dev --no-update $COMPOSER_ARGS "zendframework/zend-servicemanager:^3.0.3" ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi - stty cols 120 - COLUMNS=120 composer show @@ -102,18 +79,9 @@ script: - if [[ $TEST_COVERAGE == 'true' ]]; then composer test-coverage ; else composer test ; fi - if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi - if [[ $EXECUTE_HOSTNAME_CHECK == "true" && $TRAVIS_PULL_REQUEST == "false" ]]; then php bin/update_hostname_validator.php --check-only; fi - - if [[ $DEPLOY_DOCS == "true" && "$TRAVIS_TEST_RESULT" == "0" ]]; then wget -O theme-installer.sh "https://raw.githubusercontent.com/zendframework/zf-mkdoc-theme/master/theme-installer.sh" ; chmod 755 theme-installer.sh ; ./theme-installer.sh ; fi after_script: - if [[ $TEST_COVERAGE == 'true' ]]; then composer upload-coverage ; fi -after_success: - - if [[ $DEPLOY_DOCS == "true" ]]; then echo "Preparing to build and deploy documentation" ; ./zf-mkdoc-theme/deploy.sh ; echo "Completed deploying documentation" ; fi - notifications: email: false - slack: - rooms: - - secure: "ujQTv4jUDjnWAUME3w2VoPyKPBwbCGa7b1YMKxOt4PWxjWuyIUDY743fnmcQUqkX+CUXz9qJHG124RyZQ6UUowG/NZDttp7lppU/bIGJ/K0MuVmpBVwb8y/6rDoRj37V4n8WqAHGevjlRet3E5+gl91PFuSpN5JSj4efI8MlgUF6mqIIZUOifq2yNTZ9MXrG2qojIN4o6G4gttfwUR/3Ah1nD/ZtQBLA7pTd31/UNwtZMQ4IbGmcCMpdUADbQDr24VubjzTJfweSBoAu8Xf3IPPdR5AEfdRvuT1tGYPP4YxmvHxpTH1wF3mCX6b6ubUFMlpbqE50y/v4Mlva+2jXvcZ9Lt/Fs1Hz4pR3P3mM8EJjtj55cXWm+MSBqPBN7SX6AnkYB/OznuzqbCvt5Te09fm++1REYGnxkLxCnwI9GN2sKS7Tr8NxUCZyi9d4sVh7KUnrwFGVAGpViBTeglq+epoClcwupLK1E2m8IjrjUHTYG6NCE1QF/1NrrwuFBUIQuxPj/uE4oZcb8Tmiz9ilGFw/JbMR6WKzYzDSk2GrkJjUpa8Pn570kDL3otfJImPnEOxN73m9jle6P3laVJP9/A5Vm9C86S0aghAaswxCZB7Fql0Pl01WAB1kufqQBM/euFWGB0bQ9TFWoOENZDfD9zGyTkeZq3mgZ6DgU7ft6FA=" - on_success: change - on_failure: always From 3cda0ad559b13feb50b2087d72b94baee8d043ec Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 20 Jul 2017 11:21:59 -0500 Subject: [PATCH 39/58] Updates `providerBasic` to do one value per set Splits each "values" argument into discrete sets. Names each set based on behavior being tested. --- test/BetweenTest.php | 145 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 123 insertions(+), 22 deletions(-) diff --git a/test/BetweenTest.php b/test/BetweenTest.php index caceecf00..8054d080e 100644 --- a/test/BetweenTest.php +++ b/test/BetweenTest.php @@ -21,54 +21,152 @@ class BetweenTest extends TestCase public function providerBasic() { return [ - [ + 'inclusive-int-valid-floor' => [ 'min' => 1, 'max' => 100, 'inclusive' => true, 'expected' => true, - 'values' => [1, 10, 100], + 'value' => 1, ], - [ + 'inclusive-int-valid-between' => [ + 'min' => 1, + 'max' => 100, + 'inclusive' => true, + 'expected' => true, + 'value' => 10, + ], + 'inclusive-int-valid-ceiling' => [ + 'min' => 1, + 'max' => 100, + 'inclusive' => true, + 'expected' => true, + 'value' => 100, + ], + 'inclusive-int-invaild-below' => [ 'min' => 1, 'max' => 100, 'inclusive' => true, 'expected' => false, - 'values' => [0, 0.99, 100.01, 101], + 'value' => 0, ], - [ + 'inclusive-int-invalid-below-fractional' => [ + 'min' => 1, + 'max' => 100, + 'inclusive' => true, + 'expected' => false, + 'value' => 0.99, + ], + 'inclusive-int-invalid-above-fractional' => [ + 'min' => 1, + 'max' => 100, + 'inclusive' => true, + 'expected' => false, + 'value' => 100.01, + ], + 'inclusive-int-invalid-above' => [ + 'min' => 1, + 'max' => 100, + 'inclusive' => true, + 'expected' => false, + 'value' => 101, + ], + 'exclusive-int-invalid-below' => [ 'min' => 1, 'max' => 100, 'inclusive' => false, 'expected' => false, - 'values' => [0, 1, 100, 101], + 'value' => 0, ], - [ + 'exclusive-int-invalid-floor' => [ + 'min' => 1, + 'max' => 100, + 'inclusive' => false, + 'expected' => false, + 'value' => 1, + ], + 'exclusive-int-invalid-ceiling' => [ + 'min' => 1, + 'max' => 100, + 'inclusive' => false, + 'expected' => false, + 'value' => 100, + ], + 'exclusive-int-invalid-above' => [ + 'min' => 1, + 'max' => 100, + 'inclusive' => false, + 'expected' => false, + 'value' => 101, + ], + 'inclusive-string-valid-floor' => [ 'min' => 'a', 'max' => 'z', 'inclusive' => true, 'expected' => true, - 'values' => ['a', 'b', 'y', 'z'], + 'value' => 'a', ], - [ + 'inclusive-string-valid-between' => [ + 'min' => 'a', + 'max' => 'z', + 'inclusive' => true, + 'expected' => true, + 'value' => 'm', + ], + 'inclusive-string-valid-ceiling' => [ + 'min' => 'a', + 'max' => 'z', + 'inclusive' => true, + 'expected' => true, + 'value' => 'z', + ], + 'exclusive-string-invalid-out-of-range' => [ 'min' => 'a', 'max' => 'z', 'inclusive' => false, 'expected' => false, - 'values' => ['!', 'a', 'z'], + 'value' => '!', ], - [ + 'exclusive-string-invalid-floor' => [ + 'min' => 'a', + 'max' => 'z', + 'inclusive' => false, + 'expected' => false, + 'value' => 'a', + ], + 'exclusive-string-invalid-ceiling' => [ + 'min' => 'a', + 'max' => 'z', + 'inclusive' => false, + 'expected' => false, + 'value' => 'z', + ], + 'inclusive-int-invalid-string' => [ 'min' => 0, 'max' => 99999999, 'inclusive' => true, 'expected' => false, - 'values' => ['asdasd', 'q'], + 'value' => 'asdasd', ], - [ + 'inclusive-int-invalid-char' => [ + 'min' => 0, + 'max' => 99999999, + 'inclusive' => true, + 'expected' => false, + 'value' => 'q', + ], + 'inclusive-string-invalid-zero' => [ + 'min' => 'a', + 'max' => 'zzzzz', + 'inclusive' => true, + 'expected' => false, + 'value' => 0, + ], + 'inclusive-string-invalid-non-zero' => [ 'min' => 'a', 'max' => 'zzzzz', 'inclusive' => true, 'expected' => false, - 'values' => [0, 10, 548], + 'value' => 10, ], ]; } @@ -76,19 +174,22 @@ public function providerBasic() * Ensures that the validator follows expected behavior * * @dataProvider providerBasic + * @param int|float|string $min + * @param int|float|string $max + * @param bool $inclusive + * @param bool $expected + * @param mixed $value * @return void */ - public function testBasic($min, $max, $inclusive, $expected, $values) + public function testBasic($min, $max, $inclusive, $expected, $value) { $validator = new Between(['min' => $min, 'max' => $max, 'inclusive' => $inclusive]); - foreach ($values as $input) { - $this->assertEquals( - $expected, - $validator->isValid($input), - 'Failed values: ' . $input . ":" . implode("\n", $validator->getMessages()) - ); - } + $this->assertSame( + $expected, + $validator->isValid($value), + 'Failed value: ' . $value . ':' . implode("\n", $validator->getMessages()) + ); } /** From d2b56362f3c37d487127548a3ae11014afb5127b Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 20 Jul 2017 11:33:17 -0500 Subject: [PATCH 40/58] Provides validation error messages for Between validator type mismatch Adds two new validation error messages, one for when a numeric value is validated against non-numeric min/max values, and one for when a string value is validated against numeric min/max values. --- src/Between.php | 17 ++++++++++++----- test/BetweenTest.php | 26 ++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/Between.php b/src/Between.php index 6f99ea81a..05a74bcca 100644 --- a/src/Between.php +++ b/src/Between.php @@ -16,6 +16,8 @@ class Between extends AbstractValidator { const NOT_BETWEEN = 'notBetween'; const NOT_BETWEEN_STRICT = 'notBetweenStrict'; + const VALUE_NOT_NUMERIC = 'valueNotNumeric'; + const VALUE_NOT_STRING = 'valueNotString'; /** * Retain if min and max are numeric values. Allow to not compare string and numeric types @@ -31,7 +33,10 @@ class Between extends AbstractValidator */ protected $messageTemplates = [ self::NOT_BETWEEN => "The input is not between '%min%' and '%max%', inclusively", - self::NOT_BETWEEN_STRICT => "The input is not strictly between '%min%' and '%max%'" + self::NOT_BETWEEN_STRICT => "The input is not strictly between '%min%' and '%max%'", + self::VALUE_NOT_NUMERIC => "The min ('%min%') and max ('%max%') values are numeric, but the input is not", + self::VALUE_NOT_STRING => "The min ('%min%') and max ('%max%') values are non-numeric strings, " + . "but the input is not a string", ]; /** @@ -88,7 +93,7 @@ public function __construct($options = null) 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"); + throw new Exception\InvalidArgumentException("Missing option: 'min' and 'max' have to be given"); } if (is_numeric($options['min']) && is_numeric($options['max'])) { @@ -97,7 +102,7 @@ public function __construct($options = null) $this->numeric = false; } else { throw new Exception\InvalidArgumentException( - "Invalid options : 'min' and 'max' should be of the same scalar type" + "Invalid options: 'min' and 'max' should be of the same scalar type" ); } @@ -179,15 +184,17 @@ public function setInclusive($inclusive) */ public function isValid($value) { + $this->setValue($value); + if ($this->numeric && ! is_numeric($value)) { + $this->error(self::VALUE_NOT_NUMERIC); return false; } if (! $this->numeric && ! is_string($value)) { + $this->error(self::VALUE_NOT_STRING); return false; } - $this->setValue($value); - if ($this->getInclusive()) { if ($this->getMin() > $value || $value > $this->getMax()) { $this->error(self::NOT_BETWEEN); diff --git a/test/BetweenTest.php b/test/BetweenTest.php index 8054d080e..478c12c96 100644 --- a/test/BetweenTest.php +++ b/test/BetweenTest.php @@ -257,7 +257,7 @@ public function testEqualsMessageVariables() public function testMissingMinOrMax(array $args) { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Missing option : 'min' and 'max' have to be given"); + $this->expectExceptionMessage("Missing option: 'min' and 'max' have to be given"); new Between($args); } @@ -280,7 +280,7 @@ public function testConstructorCanAcceptInclusiveParameter() $this->assertFalse($validator->getInclusive()); } - public function testConstructWithTravesableOptions() + public function testConstructWithTraversableOptions() { $options = new \ArrayObject(['min' => 1, 'max' => 10, 'inclusive' => false]); $validator = new Between($options); @@ -288,4 +288,26 @@ public function testConstructWithTravesableOptions() $this->assertTrue($validator->isValid(5)); $this->assertFalse($validator->isValid(10)); } + + public function testStringValidatedAgainstNumericMinAndMaxIsInvalidAndReturnsAFailureMessage() + { + $validator = new Between(['min' => 1, 'max' => 10]); + $this->assertFalse($validator->isValid('a')); + $messages = $validator->getMessages(); + $this->assertContains( + 'The min (\'1\') and max (\'10\') values are numeric, but the input is not', + $messages + ); + } + + public function testNumericValidatedAgainstStringMinAndMaxIsInvalidAndReturnsAFailureMessage() + { + $validator = new Between(['min' => 'a', 'max' => 'z']); + $this->assertFalse($validator->isValid(10)); + $messages = $validator->getMessages(); + $this->assertContains( + 'The min (\'a\') and max (\'z\') values are non-numeric strings, but the input is not a string', + $messages + ); + } } From 8729b71d2005f5a2bcfabad9ca862756a7a444f2 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 20 Jul 2017 11:35:04 -0500 Subject: [PATCH 41/58] Added CHANGELOG for #174 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cedd2b7a7..1256ce9fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,11 @@ All notable changes to this project will be documented in this file, in reverse `Zend\Validator\File\MimeType` "closes" the open FileInfo handle for the file being validated, using `unset()` instead of `finfo_close()`; this resolves a segfault that occurs on older PHP versions. +- [#174](https://github.com/zendframework/zend-validator/pull/174) fixes how + `Zend\Validator\Between` handles two situations: (1) when a non-numeric value + is validated against numeric min/max values, and (2) when a numeric value is + validated against non-numeric min/max values. Previously, these incorrectly + validated as true; now they are marked invalid. ## 2.9.1 - 2017-05-17 From 754c62fc4052ef9bb0f931a92dd0433ad307187a Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 20 Jul 2017 11:44:47 -0500 Subject: [PATCH 42/58] Adds date for 2.9.2 to CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1256ce9fa..38975104b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 2.9.2 - TBD +## 2.9.2 - 2017-07-20 ### Added From b91f8e358210e166a94a100d4a350d24f21cdd8e Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 20 Jul 2017 11:48:55 -0500 Subject: [PATCH 43/58] Updated hostname validator TLD list --- src/Hostname.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hostname.php b/src/Hostname.php index bfe4a444d..7fedac45f 100644 --- a/src/Hostname.php +++ b/src/Hostname.php @@ -69,7 +69,7 @@ class Hostname extends AbstractValidator /** * Array of valid top-level-domains - * IanaVersion 2017071801 + * IanaVersion 2017072000 * * @see ftp://data.iana.org/TLD/tlds-alpha-by-domain.txt List of all TLDs by domain * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs From 4416265d5b53fc6f9b5c4faf61afc212b2cb8e8b Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 25 Jul 2017 08:58:47 -0500 Subject: [PATCH 44/58] Updates `@return` annotations for fluent interfaces Should return `self` for fluent interfaces, class name when returning new instance (immutable classes). --- src/File/Crc32.php | 4 ++-- src/File/Extension.php | 6 +++--- src/File/Hash.php | 4 ++-- src/File/ImageSize.php | 16 ++++++++-------- src/File/MimeType.php | 10 +++++----- src/File/Size.php | 6 +++--- src/File/WordCount.php | 4 ++-- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/File/Crc32.php b/src/File/Crc32.php index ba5d1c448..126d63d56 100644 --- a/src/File/Crc32.php +++ b/src/File/Crc32.php @@ -56,7 +56,7 @@ public function getCrc32() * Sets the crc32 hash for one or multiple files * * @param string|array $options - * @return Crc32 Provides a fluent interface + * @return self Provides a fluent interface */ public function setCrc32($options) { @@ -68,7 +68,7 @@ public function setCrc32($options) * Adds the crc32 hash for one or multiple files * * @param string|array $options - * @return Crc32 Provides a fluent interface + * @return self Provides a fluent interface */ public function addCrc32($options) { diff --git a/src/File/Extension.php b/src/File/Extension.php index 0918474bf..e502dc993 100644 --- a/src/File/Extension.php +++ b/src/File/Extension.php @@ -100,7 +100,7 @@ public function getCase() * Sets the case to use * * @param bool $case - * @return Extension Provides a fluent interface + * @return self Provides a fluent interface */ public function setCase($case) { @@ -124,7 +124,7 @@ public function getExtension() * Sets the file extensions * * @param string|array $extension The extensions to validate - * @return Extension Provides a fluent interface + * @return self Provides a fluent interface */ public function setExtension($extension) { @@ -137,7 +137,7 @@ public function setExtension($extension) * Adds the file extensions * * @param string|array $extension The extensions to add for validation - * @return Extension Provides a fluent interface + * @return self Provides a fluent interface */ public function addExtension($extension) { diff --git a/src/File/Hash.php b/src/File/Hash.php index b1a5fa484..f48c2c921 100644 --- a/src/File/Hash.php +++ b/src/File/Hash.php @@ -76,7 +76,7 @@ public function getHash() * Sets the hash for one or multiple files * * @param string|array $options - * @return Hash Provides a fluent interface + * @return self Provides a fluent interface */ public function setHash($options) { @@ -91,7 +91,7 @@ public function setHash($options) * * @param string|array $options * @throws Exception\InvalidArgumentException - * @return Hash Provides a fluent interface + * @return self Provides a fluent interface */ public function addHash($options) { diff --git a/src/File/ImageSize.php b/src/File/ImageSize.php index bb7433766..62e1d1f7a 100644 --- a/src/File/ImageSize.php +++ b/src/File/ImageSize.php @@ -125,7 +125,7 @@ public function getMinWidth() * * @param int $minWidth * @throws Exception\InvalidArgumentException When minwidth is greater than maxwidth - * @return ImageSize Provides a fluid interface + * @return self Provides a fluid interface */ public function setMinWidth($minWidth) { @@ -155,7 +155,7 @@ public function getMaxWidth() * * @param int $maxWidth * @throws Exception\InvalidArgumentException When maxwidth is less than minwidth - * @return ImageSize Provides a fluid interface + * @return self Provides a fluid interface */ public function setMaxWidth($maxWidth) { @@ -185,7 +185,7 @@ public function getMinHeight() * * @param int $minHeight * @throws Exception\InvalidArgumentException When minheight is greater than maxheight - * @return ImageSize Provides a fluid interface + * @return self Provides a fluid interface */ public function setMinHeight($minHeight) { @@ -215,7 +215,7 @@ public function getMaxHeight() * * @param int $maxHeight * @throws Exception\InvalidArgumentException When maxheight is less than minheight - * @return ImageSize Provides a fluid interface + * @return self Provides a fluid interface */ public function setMaxHeight($maxHeight) { @@ -274,7 +274,7 @@ public function getImageHeight() * Sets the minimum image size * * @param array $options The minimum image dimensions - * @return ImageSize Provides a fluent interface + * @return self Provides a fluent interface */ public function setImageMin($options) { @@ -286,7 +286,7 @@ public function setImageMin($options) * Sets the maximum image size * * @param array|\Traversable $options The maximum image dimensions - * @return ImageSize Provides a fluent interface + * @return self Provides a fluent interface */ public function setImageMax($options) { @@ -298,7 +298,7 @@ public function setImageMax($options) * Sets the minimum and maximum image width * * @param array $options The image width dimensions - * @return ImageSize Provides a fluent interface + * @return self Provides a fluent interface */ public function setImageWidth($options) { @@ -312,7 +312,7 @@ public function setImageWidth($options) * Sets the minimum and maximum image height * * @param array $options The image height dimensions - * @return ImageSize Provides a fluent interface + * @return self Provides a fluent interface */ public function setImageHeight($options) { diff --git a/src/File/MimeType.php b/src/File/MimeType.php index 7bac70f7f..3a9d42e6f 100644 --- a/src/File/MimeType.php +++ b/src/File/MimeType.php @@ -179,7 +179,7 @@ public function getMagicFile() * @throws Exception\RuntimeException When finfo can not read the magicfile * @throws Exception\InvalidArgumentException * @throws Exception\InvalidMagicMimeFileException - * @return MimeType Provides fluid interface + * @return self Provides fluid interface */ public function setMagicFile($file) { @@ -216,7 +216,7 @@ public function setMagicFile($file) * Disables usage of MagicFile * * @param $disable boolean False disables usage of magic file - * @return MimeType Provides fluid interface + * @return self Provides fluid interface */ public function disableMagicFile($disable) { @@ -249,7 +249,7 @@ public function getHeaderCheck() * Note that this is unsafe and therefor the default value is false * * @param bool $headerCheck - * @return MimeType Provides fluid interface + * @return self Provides fluid interface */ public function enableHeaderCheck($headerCheck = true) { @@ -278,7 +278,7 @@ public function getMimeType($asArray = false) * Sets the mimetypes * * @param string|array $mimetype The mimetypes to validate - * @return MimeType Provides a fluent interface + * @return self Provides a fluent interface */ public function setMimeType($mimetype) { @@ -292,7 +292,7 @@ public function setMimeType($mimetype) * * @param string|array $mimetype The mimetypes to add for validation * @throws Exception\InvalidArgumentException - * @return MimeType Provides a fluent interface + * @return self Provides a fluent interface */ public function addMimeType($mimetype) { diff --git a/src/File/Size.php b/src/File/Size.php index b698f8417..80a9701b0 100644 --- a/src/File/Size.php +++ b/src/File/Size.php @@ -137,7 +137,7 @@ public function getMin($raw = false) * * @param int|string $min The minimum file size * @throws Exception\InvalidArgumentException When min is greater than max - * @return Size Provides a fluent interface + * @return self Provides a fluent interface */ public function setMin($min) { @@ -182,7 +182,7 @@ public function getMax($raw = false) * * @param int|string $max The maximum file size * @throws Exception\InvalidArgumentException When max is smaller than min - * @return Size Provides a fluent interface + * @return self Provides a fluent interface */ public function setMax($max) { @@ -216,7 +216,7 @@ protected function getSize() * Set current size * * @param int $size - * @return Size + * @return self */ protected function setSize($size) { diff --git a/src/File/WordCount.php b/src/File/WordCount.php index a725de394..cbe9ce451 100644 --- a/src/File/WordCount.php +++ b/src/File/WordCount.php @@ -104,7 +104,7 @@ public function getMin() * * @param int|array $min The minimum word count * @throws Exception\InvalidArgumentException When min is greater than max - * @return WordCount Provides a fluent interface + * @return self Provides a fluent interface */ public function setMin($min) { @@ -142,7 +142,7 @@ public function getMax() * * @param int|array $max The maximum word count * @throws Exception\InvalidArgumentException When max is smaller than min - * @return WordCount Provides a fluent interface + * @return self Provides a fluent interface */ public function setMax($max) { From ddcb7cd779af1250b4116a148564c720a314aef1 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 25 Jul 2017 09:01:28 -0500 Subject: [PATCH 45/58] Adds CHANGELOG for #169 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d3dfa088..597c7fbb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ All notable changes to this project will be documented in this file, in reverse - Nothing. +### Changed + +- [#169](https://github.com/zendframework/zend-validator/pull/169) modifies how + the various `File` validators check for readable files. Previously, they used + `stream_resolve_include_path`, which led to false negative checks when the + files did not exist within an `include_path` (which is often the case within a + web application). These now use `is_readable()` instead. + ### Deprecated - Nothing. From 7b3c9acb5575a0fcf84206eab79fcf79d3cb94b5 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 25 Jul 2017 09:02:18 -0500 Subject: [PATCH 46/58] Updated Hostname validator TLD list --- src/Hostname.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hostname.php b/src/Hostname.php index 7fedac45f..1e88b9894 100644 --- a/src/Hostname.php +++ b/src/Hostname.php @@ -69,7 +69,7 @@ class Hostname extends AbstractValidator /** * Array of valid top-level-domains - * IanaVersion 2017072000 + * IanaVersion 2017072500 * * @see ftp://data.iana.org/TLD/tlds-alpha-by-domain.txt List of all TLDs by domain * @see http://www.iana.org/domains/root/db/ Official list of supported TLDs From d0316a1e0d60ef2c33a551b3cd06be1228580dfc Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 25 Jul 2017 09:07:28 -0500 Subject: [PATCH 47/58] Provides CS fix for update_hostname_validator - Whitespace following `function` keyword --- bin/update_hostname_validator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/update_hostname_validator.php b/bin/update_hostname_validator.php index 49070cbb5..69043d681 100644 --- a/bin/update_hostname_validator.php +++ b/bin/update_hostname_validator.php @@ -177,7 +177,7 @@ function getNewValidTlds($string) function getPunycodeDecoder() { if (function_exists('idn_to_utf8')) { - return function($domain) { + return function ($domain) { return idn_to_utf8($domain, 0, INTL_IDNA_VARIANT_UTS46); }; } From 612035786724cd3c4b0d9b166914ac3321dbd688 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 25 Jul 2017 09:09:03 -0500 Subject: [PATCH 48/58] Provides formatting of complex conditional - Uses newlines and indentation to group conditional statements --- src/File/Upload.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/File/Upload.php b/src/File/Upload.php index a721dfe3e..33b664b3a 100644 --- a/src/File/Upload.php +++ b/src/File/Upload.php @@ -110,7 +110,10 @@ public function getFiles($file = null) */ public function setFiles($files = []) { - if (null === $files || ((is_array($files) || $files instanceof Countable) && count($files) === 0)) { + if (null === $files + || ((is_array($files) || $files instanceof Countable) + && count($files) === 0) + ) { $this->options['files'] = $_FILES; } else { $this->options['files'] = $files; From af974c96a2ceca74ce529400702b77dee6e02d90 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 25 Jul 2017 09:10:30 -0500 Subject: [PATCH 49/58] Adds CHANGELOG for #175 --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 597c7fbb6..eaa64bf1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ All notable changes to this project will be documented in this file, in reverse ### Added -- Nothing. +- [#175](https://github.com/zendframework/zend-validator/pull/175) adds support + for PHP 7.2 (conditionally, as PHP 7.2 is currently in beta1). ### Changed @@ -22,7 +23,8 @@ All notable changes to this project will be documented in this file, in reverse ### Removed -- Nothing. +- [#175](https://github.com/zendframework/zend-validator/pull/175) removes + support for HHVM. ### Fixed From e7355ab1dcc9183719fb6efcb71e9ac5642d96c9 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 25 Jul 2017 10:45:20 -0500 Subject: [PATCH 50/58] Updates zend-session requirement Updates the zend-session requirement to v2.8+, to ensure compatibility with PHP 7.2. --- composer.json | 4 ++-- composer.lock | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 560ed56bb..6b52eca70 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "zendframework/zend-i18n": "^2.6", "zendframework/zend-math": "^2.6", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-session": "^2.6.2", + "zendframework/zend-session": "^2.8", "zendframework/zend-uri": "^2.5", "phpunit/PHPUnit": "^6.0.8 || ^5.7.15", "zendframework/zend-coding-standard": "~1.0.0" @@ -38,7 +38,7 @@ "zendframework/zend-math": "Zend\\Math component, required by the Csrf validator", "zendframework/zend-i18n-resources": "Translations of validator messages", "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", - "zendframework/zend-session": "Zend\\Session component, required by the Csrf validator", + "zendframework/zend-session": "Zend\\Session component, ^2.8; required by the Csrf validator", "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" }, "minimum-stability": "dev", diff --git a/composer.lock b/composer.lock index dca2092e7..e47398518 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "6926a47b4f62c328d7c9d74767654cb9", + "content-hash": "592dc52b9037dcdb0de561eced941e30", "packages": [ { "name": "container-interop/container-interop", @@ -2169,29 +2169,29 @@ }, { "name": "zendframework/zend-session", - "version": "2.7.3", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-session.git", - "reference": "346e9709657b81a5d53d70ce754730a26d1f02f2" + "reference": "b1486c382decc241de8b1c7778eaf2f0a884f67d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/346e9709657b81a5d53d70ce754730a26d1f02f2", - "reference": "346e9709657b81a5d53d70ce754730a26d1f02f2", + "url": "https://api.github.com/repos/zendframework/zend-session/zipball/b1486c382decc241de8b1c7778eaf2f0a884f67d", + "reference": "b1486c382decc241de8b1c7778eaf2f0a884f67d", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", + "php": "^7.0 || ^5.6", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "require-dev": { "container-interop/container-interop": "^1.1", - "fabpot/php-cs-fixer": "1.7.*", "mongodb/mongodb": "^1.0.1", - "phpunit/phpunit": "~4.0", + "phpunit/phpunit": "^6.0.8 || ^5.7.15", "zendframework/zend-cache": "^2.6.1", + "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-db": "^2.7", "zendframework/zend-http": "^2.5.4", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", @@ -2208,8 +2208,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "2.8-dev", + "dev-develop": "2.9-dev" }, "zf": { "component": "Zend\\Session", @@ -2231,7 +2231,7 @@ "session", "zf2" ], - "time": "2016-07-05T18:32:50+00:00" + "time": "2017-06-19T21:31:39+00:00" }, { "name": "zendframework/zend-uri", From f527802116967f8288b35caa57cad4f7ace0526b Mon Sep 17 00:00:00 2001 From: Tim Younger Date: Tue, 25 Jul 2017 20:01:06 -0700 Subject: [PATCH 51/58] address upstream feedback: remove setters, prefer count over min and max in setOptions which is used by the constructor, use getter methods instead of options field in isValid, report NOT_COUNTABLE as a validation error rather than throwing an exception, and use `$this->` instead of `self::` for assertions in test case. --- src/IsCountable.php | 61 ++++++----------------- test/IsCountableTest.php | 103 +++++++++++++++++++++++++-------------- 2 files changed, 83 insertions(+), 81 deletions(-) diff --git a/src/IsCountable.php b/src/IsCountable.php index ea9ba81aa..07b007084 100644 --- a/src/IsCountable.php +++ b/src/IsCountable.php @@ -2,8 +2,6 @@ namespace Zend\Validator; -use Zend\Validator\Exception\RuntimeException; - class IsCountable extends AbstractValidator { const NOT_COUNTABLE = 'notCountable'; @@ -45,23 +43,32 @@ class IsCountable extends AbstractValidator 'max' => null, ]; + public function setOptions($options = []) + { + if (is_array($options) && isset($options['count'])) { + unset($options['min'], $options['max']); + } + + return parent::setOptions($options); + } + /** * Returns true if and only if $value is countable (and the count validates against optional values). * - * @param string $value + * @param iterable $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]); + $this->error(self::NOT_COUNTABLE); + return false; } $count = count($value); - if (is_numeric($this->options['count'])) { - if ($count != $this->options['count']) { + if (is_numeric($this->getCount())) { + if ($count != $this->getCount()) { $this->error(self::NOT_EQUALS); return false; } @@ -69,12 +76,12 @@ public function isValid($value) return true; } - if (is_numeric($this->options['max']) && $count > $this->options['max']) { + if (is_numeric($this->getMax()) && $count > $this->getMax()) { $this->error(self::GREATER_THAN); return false; } - if (is_numeric($this->options['min']) && $count < $this->options['min']) { + if (is_numeric($this->getMin()) && $count < $this->getMin()) { $this->error(self::LESS_THAN); return false; } @@ -92,18 +99,6 @@ public function getCount() return $this->options['count']; } - /** - * Sets the count option - * - * @param int $count - * @return self Provides a fluent interface - */ - public function setCount($count) - { - $this->options['count'] = $count; - return $this; - } - /** * Returns the min option * @@ -114,18 +109,6 @@ 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 * @@ -135,16 +118,4 @@ 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; - } } diff --git a/test/IsCountableTest.php b/test/IsCountableTest.php index 953faf57a..c69397afe 100644 --- a/test/IsCountableTest.php +++ b/test/IsCountableTest.php @@ -7,82 +7,113 @@ 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); + $sut = new IsCountable([ + 'min' => 1, + 'max' => 10, + ]); - self::assertTrue($this->sut->isValid(['Foo']), json_encode($this->sut->getMessages())); - self::assertCount(0, $this->sut->getMessages()); + $this->assertTrue($sut->isValid(['Foo']), json_encode($sut->getMessages())); + $this->assertCount(0, $sut->getMessages()); } public function testIteratorIsValid() { - self::assertTrue($this->sut->isValid(new \SplQueue()), json_encode($this->sut->getMessages())); - self::assertCount(0, $this->sut->getMessages()); + $sut = new IsCountable(); + + $this->assertTrue($sut->isValid(new \SplQueue()), json_encode($sut->getMessages())); + $this->assertCount(0, $sut->getMessages()); } public function testValidEquals() { - $this->sut->setCount(1); + $sut = new IsCountable([ + 'count' => 1, + ]); - self::assertTrue($this->sut->isValid(['Foo'])); - self::assertCount(0, $this->sut->getMessages()); + $this->assertTrue($sut->isValid(['Foo'])); + $this->assertCount(0, $sut->getMessages()); } public function testValidMax() { - $this->sut->setMax(1); + $sut = new IsCountable([ + 'max' => 1, + ]); - self::assertTrue($this->sut->isValid(['Foo'])); - self::assertCount(0, $this->sut->getMessages()); + $this->assertTrue($sut->isValid(['Foo'])); + $this->assertCount(0, $sut->getMessages()); } public function testValidMin() { - $this->sut->setMin(1); + $sut = new IsCountable([ + 'min' => 1, + ]); - self::assertTrue($this->sut->isValid(['Foo'])); - self::assertCount(0, $this->sut->getMessages()); + $this->assertTrue($sut->isValid(['Foo'])); + $this->assertCount(0, $sut->getMessages()); } public function testInvalidNotEquals() { - $this->sut->setCount(2); + $sut = new IsCountable([ + 'count' => 2, + ]); - self::assertFalse($this->sut->isValid(['Foo'])); - self::assertCount(1, $this->sut->getMessages()); + $this->assertFalse($sut->isValid(['Foo'])); + $this->assertCount(1, $sut->getMessages()); } - /** - * @expectedException \Zend\Validator\Exception\RuntimeException - */ public function testInvalidType() { - $this->sut->isValid(new \stdClass()); + $sut = new IsCountable(); + + $this->assertFalse($sut->isValid(new \stdClass())); + $this->assertCount(1, $sut->getMessages()); } public function testInvalidExceedsMax() { - $this->sut->setMax(1); + $sut = new IsCountable([ + 'max' => 1, + ]); - self::assertFalse($this->sut->isValid(['Foo', 'Bar'])); - self::assertCount(1, $this->sut->getMessages()); + $this->assertFalse($sut->isValid(['Foo', 'Bar'])); + $this->assertCount(1, $sut->getMessages()); } public function testInvalidExceedsMin() { - $this->sut->setMin(2); + $sut = new IsCountable([ + 'min' => 2, + ]); + + $this->assertFalse($sut->isValid(['Foo'])); + $this->assertCount(1, $sut->getMessages()); + } - self::assertFalse($this->sut->isValid(['Foo'])); - self::assertCount(1, $this->sut->getMessages()); + public function testExactCountOverridesMinAndMax() + { + $sut = new IsCountable([ + 'count' => 1, + 'min' => 2, + 'max' => 3, + ]); + + $this->assertSame(1, $sut->getCount()); + $this->assertNull($sut->getMin()); + $this->assertNull($sut->getMax()); + + $sut->setOptions([ + 'count' => 4, + 'min' => 5, + 'max' => 6, + ]); + + $this->assertSame(4, $sut->getCount()); + $this->assertNull($sut->getMin()); + $this->assertNull($sut->getMax()); } } From 99247005e1c7a9ce80e37545fb30aa937abedaab Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 26 Jul 2017 10:23:08 -0500 Subject: [PATCH 52/58] Adds CHANGELOG for #185 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaa64bf1d..c04abd6e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ All notable changes to this project will be documented in this file, in reverse files did not exist within an `include_path` (which is often the case within a web application). These now use `is_readable()` instead. +- [#185](https://github.com/zendframework/zend-validator/pull/185) updates the + zend-session requirement (during development, and in the suggestions) to 2.8+, + to ensure compatibility with the upcoming PHP 7.2 release. + ### Deprecated - Nothing. From 74b4711166ca96825715558067aecd36992b9aee Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 26 Jul 2017 10:26:22 -0500 Subject: [PATCH 53/58] Adds license docblocks to new class files --- src/IsCountable.php | 5 +++++ test/IsCountableTest.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/IsCountable.php b/src/IsCountable.php index 07b007084..eb49e07e5 100644 --- a/src/IsCountable.php +++ b/src/IsCountable.php @@ -1,4 +1,9 @@ Date: Wed, 26 Jul 2017 10:26:59 -0500 Subject: [PATCH 54/58] Imports `Countable` into `IsCountable` class file --- src/IsCountable.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/IsCountable.php b/src/IsCountable.php index eb49e07e5..653e33444 100644 --- a/src/IsCountable.php +++ b/src/IsCountable.php @@ -7,6 +7,8 @@ namespace Zend\Validator; +use Countable; + class IsCountable extends AbstractValidator { const NOT_COUNTABLE = 'notCountable'; @@ -65,7 +67,7 @@ public function setOptions($options = []) */ public function isValid($value) { - if (! (is_array($value) || $value instanceof \Countable)) { + if (! (is_array($value) || $value instanceof Countable)) { $this->error(self::NOT_COUNTABLE); return false; } From 97d297c117bd047add23538ab5413a4cfe22f933 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 26 Jul 2017 10:49:22 -0500 Subject: [PATCH 55/58] Updates IsCountable to not allow passing both a count and a min or max value Since there is no way to clear a `count`, this is the only way to ensure that behavior remains as expected when merging options from disparate sources. --- src/IsCountable.php | 73 ++++++++++++++++++++++++++++++++++++++-- test/IsCountableTest.php | 63 +++++++++++++++++++++------------- 2 files changed, 111 insertions(+), 25 deletions(-) diff --git a/src/IsCountable.php b/src/IsCountable.php index 653e33444..ab553f83f 100644 --- a/src/IsCountable.php +++ b/src/IsCountable.php @@ -9,6 +9,21 @@ use Countable; +/** + * Validate that a value is countable and the count meets expectations. + * + * The validator has five specific behaviors: + * + * - You can determine if a value is countable only + * - You can test if the value is an exact count + * - You can test if the value is greater than a minimum count value + * - You can test if the value is greater than a maximum count value + * - You can test if the value is between the minimum and maximum count values + * + * When creating the instance or calling `setOptions()`, if you specify a + * "count" option, specifying either "min" or "max" leads to an inconsistent + * state and, as such will raise an Exception\InvalidArgumentException. + */ class IsCountable extends AbstractValidator { const NOT_COUNTABLE = 'notCountable'; @@ -52,8 +67,14 @@ class IsCountable extends AbstractValidator public function setOptions($options = []) { - if (is_array($options) && isset($options['count'])) { - unset($options['min'], $options['max']); + foreach (['count', 'min', 'max'] as $option) { + if (! is_array($options) || ! isset($options[$option])) { + continue; + } + + $method = sprintf('set%s', ucfirst($option)); + $this->$method($options[$option]); + unset($options[$option]); } return parent::setOptions($options); @@ -125,4 +146,52 @@ public function getMax() { return $this->options['max']; } + + /** + * @param mixed $value + * @return void + * @throws Exception\InvalidArgumentException if either a min or max option + * was previously set. + */ + private function setCount($value) + { + if (isset($this->options['min']) || isset($this->options['max'])) { + throw new Exception\InvalidArgumentException( + 'Cannot set count; conflicts with either a min or max option previously set' + ); + } + $this->options['count'] = $value; + } + + /** + * @param mixed $value + * @return void + * @throws Exception\InvalidArgumentException if either a count or max option + * was previously set. + */ + private function setMin($value) + { + if (isset($this->options['count'])) { + throw new Exception\InvalidArgumentException( + 'Cannot set count; conflicts with either a count option previously set' + ); + } + $this->options['min'] = $value; + } + + /** + * @param mixed $value + * @return void + * @throws Exception\InvalidArgumentException if either a count or min option + * was previously set. + */ + private function setMax($value) + { + if (isset($this->options['count'])) { + throw new Exception\InvalidArgumentException( + 'Cannot set count; conflicts with either a count option previously set' + ); + } + $this->options['max'] = $value; + } } diff --git a/test/IsCountableTest.php b/test/IsCountableTest.php index a5d877ecb..2084b52c7 100644 --- a/test/IsCountableTest.php +++ b/test/IsCountableTest.php @@ -9,9 +9,49 @@ use PHPUnit\Framework\TestCase; use Zend\Validator\IsCountable; +use Zend\Validator\Exception; class IsCountableTest extends TestCase { + public function conflictingOptionsProvider() + { + return [ + 'count-min' => [['count' => 10, 'min' => 1]], + 'count-max' => [['count' => 10, 'max' => 10]], + ]; + } + + /** + * @dataProvider conflictingOptionsProvider + */ + public function testConstructorRaisesExceptionWhenProvidedConflictingOptions(array $options) + { + $this->expectException(Exception\InvalidArgumentException::class); + $this->expectExceptionMessage('conflicts'); + new IsCountable($options); + } + + public function conflictingSecondaryOptionsProvider() + { + return [ + 'count-min' => [['count' => 10], ['min' => 1]], + 'count-max' => [['count' => 10], ['max' => 10]], + ]; + } + + /** + * @dataProvider conflictingSecondaryOptionsProvider + */ + public function testSetOptionsRaisesExceptionWhenProvidedOptionConflictingWithCurrentSettings( + array $originalOptions, + array $secondaryOptions + ) { + $validator = new IsCountable($originalOptions); + $this->expectException(Exception\InvalidArgumentException::class); + $this->expectExceptionMessage('conflicts'); + $validator->setOptions($secondaryOptions); + } + public function testArrayIsValid() { $sut = new IsCountable([ @@ -98,27 +138,4 @@ public function testInvalidExceedsMin() $this->assertFalse($sut->isValid(['Foo'])); $this->assertCount(1, $sut->getMessages()); } - - public function testExactCountOverridesMinAndMax() - { - $sut = new IsCountable([ - 'count' => 1, - 'min' => 2, - 'max' => 3, - ]); - - $this->assertSame(1, $sut->getCount()); - $this->assertNull($sut->getMin()); - $this->assertNull($sut->getMax()); - - $sut->setOptions([ - 'count' => 4, - 'min' => 5, - 'max' => 6, - ]); - - $this->assertSame(4, $sut->getCount()); - $this->assertNull($sut->getMin()); - $this->assertNull($sut->getMax()); - } } From 03c12fd5d117bf5802f2c8b01b411f8949c518b1 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 26 Jul 2017 11:09:02 -0500 Subject: [PATCH 56/58] Adds documentation for #157 --- doc/book/validators/is-countable.md | 107 ++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 108 insertions(+) create mode 100644 doc/book/validators/is-countable.md diff --git a/doc/book/validators/is-countable.md b/doc/book/validators/is-countable.md new file mode 100644 index 000000000..bfd66c123 --- /dev/null +++ b/doc/book/validators/is-countable.md @@ -0,0 +1,107 @@ +# IsCountable Validator + +- **Since 2.10.0** + +`Zend\Validator\IsCountable` allows you to validate that a value can be counted +(i.e., it's an array or implements `Countable`), and, optionally: + +- the exact count of the value +- the minimum count of the value +- the maximum count of the value + +Specifying either of the latter two is inconsistent with the first, and, as +such, the validator does not allow setting both a count and a minimum or maximum +value. You may, however specify both minimum and maximum values, in which case +the validator operates similar to the [Between validator](between.md). + +## Supported options + +The following options are supported for `Zend\Validator\IsCountable`: + +- `count`: Defines if the validation should look for a specific, exact count for + the value provided. +- `max`: Sets the maximum value for the validation; if the count of the value is + greater than the maximum, validation fails.. +- `min`: Sets the minimum value for the validation; if the count of the value is + lower than the minimum, validation fails. + +## Default behaviour + +Given no options, the validator simply tests to see that the value may be +counted (i.e., it's an array or `Countable` instance): + +```php +$validator = new Zend\Validator\IsCountable(); + +$validator->isValid(10); // false; not an array or Countable +$validator->isValid([10]); // true; value is an array +$validator->isValid(new ArrayObject([10])); // true; value is Countable +$validator->isValid(new stdClass); // false; value is not Countable +``` + +## Specifying an exact count + +You can also specify an exact count; if the value is countable, and its count +matches, the the value is valid. + +```php +$validator = new Zend\Validator\IsCountable(['count' => 3]); + +$validator->isValid([1, 2, 3]); // true; countable, and count is 3 +$validator->isValid(new ArrayObject([1, 2, 3])); // true; countable, and count is 3 +$validator->isValid([1]); // false; countable, but count is 1 +$validator->isValid(new ArrayObject([1])); // false; countable, but count is 1 +``` + +## Specifying a minimum count + +You may specify a minimum count. When you do, the value must be countable, and +greater than or equal to the minimum count you specify in order to be valid. + +```php +$validator = new Zend\Validator\IsCountable(['min' => 2]); + +$validator->isValid([1, 2, 3]); // true; countable, and count is 3 +$validator->isValid(new ArrayObject([1, 2, 3])); // true; countable, and count is 3 +$validator->isValid([1, 2]); // true; countable, and count is 2 +$validator->isValid(new ArrayObject([1, 2])); // true; countable, and count is 2 +$validator->isValid([1]); // false; countable, but count is 1 +$validator->isValid(new ArrayObject([1])); // false; countable, but count is 1 +``` + +## Specifying a maximum count + +You may specify a maximum count. When you do, the value must be countable, and +less than or equal to the maximum count you specify in order to be valid. + +```php +$validator = new Zend\Validator\IsCountable(['max' => 2]); + +$validator->isValid([1, 2, 3]); // false; countable, but count is 3 +$validator->isValid(new ArrayObject([1, 2, 3])); // false; countable, but count is 3 +$validator->isValid([1, 2]); // true; countable, and count is 2 +$validator->isValid(new ArrayObject([1, 2])); // true; countable, and count is 2 +$validator->isValid([1]); // true; countable, and count is 1 +$validator->isValid(new ArrayObject([1])); // true; countable, and count is 1 +``` + +## Specifying both minimum and maximum + +If you specify both a minimum and maximum, the count must be _between_ the two, +inclusively (i.e., it may be the minimum or maximum, and any value between). + +```php +$validator = new Zend\Validator\IsCountable([ + 'min' => 3, + 'max' => 5, +]); + +$validator->isValid([1, 2, 3]); // true; countable, and count is 3 +$validator->isValid(new ArrayObject([1, 2, 3])); // true; countable, and count is 3 +$validator->isValid(range(1, 5)); // true; countable, and count is 5 +$validator->isValid(new ArrayObject(range(1, 5))); // true; countable, and count is 5 +$validator->isValid([1, 2]); // false; countable, and count is 2 +$validator->isValid(new ArrayObject([1, 2])); // false; countable, and count is 2 +$validator->isValid(range(1, 6)); // false; countable, and count is 6 +$validator->isValid(new ArrayObject(range(1, 6))); // false; countable, and count is 6 +``` diff --git a/mkdocs.yml b/mkdocs.yml index f72bf9afb..277caf7ff 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -25,6 +25,7 @@ pages: - InArray: validators/in-array.md - Ip: validators/ip.md - Isbn: validators/isbn.md + - IsCountable: validators/is-countable.md - IsInstanceOf: validators/isinstanceof.md - LessThan: validators/less-than.md - NotEmpty: validators/not-empty.md From e9ac470eb12ad5ce99249c3063acfaeebc68f903 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 26 Jul 2017 11:17:30 -0500 Subject: [PATCH 57/58] Adds CHANGELOG for #157 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c04abd6e6..0a90c6fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ All notable changes to this project will be documented in this file, in reverse - [#175](https://github.com/zendframework/zend-validator/pull/175) adds support for PHP 7.2 (conditionally, as PHP 7.2 is currently in beta1). +- [#157](https://github.com/zendframework/zend-validator/pull/157) adds a new + validator, `IsCountable`, which allows validating: + - if a value is countable + - if a countable value exactly matches a configured count + - if a countable value is greater than a configured minimum count + - if a countable value is less than a configured maximum count + - if a countable value is between configured minimum and maximum counts + ### Changed - [#169](https://github.com/zendframework/zend-validator/pull/169) modifies how From 360379d9670548cae7919f1bf68ae9c01101a577 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Wed, 26 Jul 2017 11:21:50 -0500 Subject: [PATCH 58/58] Refactors `validateInternationalizedLocalPart()` to accept the local part as an argument Allows the method to not rely on instance state. --- src/EmailAddress.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/EmailAddress.php b/src/EmailAddress.php index 41e4c6ace..bd58018dc 100644 --- a/src/EmailAddress.php +++ b/src/EmailAddress.php @@ -344,7 +344,7 @@ protected function validateLocalPart() $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e'; if (preg_match('/^[' . $atext . ']+(\x2e+[' . $atext . ']+)*$/', $this->localPart)) { $result = true; - } elseif ($this->validateInternationalizedLocalPart()) { + } elseif ($this->validateInternationalizedLocalPart($this->localPart)) { $result = true; } else { // Try quoted string format (RFC 5321 Chapter 4.1.2) @@ -364,19 +364,24 @@ protected function validateLocalPart() return $result; } - protected function validateInternationalizedLocalPart() + /** + * @param string $localPart Address local part to validate. + * @return bool + */ + protected function validateInternationalizedLocalPart($localPart) { if (extension_loaded('intl') - && false === UConverter::transcode($this->localPart, 'UTF-8', 'UTF-8') + && false === UConverter::transcode($localPart, 'UTF-8', 'UTF-8') ) { // invalid utf? return false; } + $atext = 'a-zA-Z0-9\x21\x23\x24\x25\x26\x27\x2a\x2b\x2d\x2f\x3d\x3f\x5e\x5f\x60\x7b\x7c\x7d\x7e'; // RFC 6532 extends atext to include non-ascii utf // @see https://tools.ietf.org/html/rfc6532#section-3.1 $uatext = $atext . '\x{80}-\x{FFFF}'; - return (bool) preg_match('/^[' . $uatext . ']+(\x2e+[' . $uatext . ']+)*$/u', $this->localPart); + return (bool) preg_match('/^[' . $uatext . ']+(\x2e+[' . $uatext . ']+)*$/u', $localPart); } /**