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 diff --git a/CHANGELOG.md b/CHANGELOG.md index ef0225836..1256ce9fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,15 @@ 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. +- [#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 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']); diff --git a/src/Between.php b/src/Between.php index 386393dbd..05a74bcca 100644 --- a/src/Between.php +++ b/src/Between.php @@ -16,6 +16,15 @@ 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 + * + * @var boolean + */ + private $numeric; /** * Validation failure message template definitions @@ -24,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", ]; /** @@ -81,7 +93,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); @@ -164,6 +186,15 @@ 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; + } + if ($this->getInclusive()) { if ($this->getMin() > $value || $value > $this->getMax()) { $this->error(self::NOT_BETWEEN); 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); } } 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 'ελ', '世界', '書籍', + 'ഭാരതം', 'ਭਾਰਤ', '网址', '닷넷', diff --git a/test/BetweenTest.php b/test/BetweenTest.php index bf86e4e5f..478c12c96 100644 --- a/test/BetweenTest.php +++ b/test/BetweenTest.php @@ -18,38 +18,178 @@ */ class BetweenTest extends TestCase { + public function providerBasic() + { + return [ + 'inclusive-int-valid-floor' => [ + 'min' => 1, + 'max' => 100, + 'inclusive' => true, + 'expected' => true, + '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, + '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, + '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, + '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, + '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, + '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, + 'value' => 10, + ], + ]; + } /** * 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() + public function testBasic($min, $max, $inclusive, $expected, $value) { - /** - * 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]); + + $this->assertSame( + $expected, + $validator->isValid($value), + 'Failed value: ' . $value . ':' . implode("\n", $validator->getMessages()) + ); } /** @@ -117,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); } @@ -140,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); @@ -148,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 + ); + } }