From 718b42e15fb75c03a9e2904723e4f8daddaa220e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Fri, 21 Jul 2017 17:03:29 +0200 Subject: [PATCH 01/12] Reduce redundant message wrapping code Refers to https://github.com/cakephp/cakephp/pull/10925 --- src/Console/Shell.php | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Console/Shell.php b/src/Console/Shell.php index f8827fee060..e9d4058af41 100644 --- a/src/Console/Shell.php +++ b/src/Console/Shell.php @@ -665,13 +665,8 @@ public function out($message = null, $newlines = 1, $level = Shell::NORMAL) */ public function err($message = null, $newlines = 1) { - if (is_array($message)) { - foreach ($message as $k => $v) { - $message[$k] = '' . $v . ''; - } - } else { - $message = '' . $message . ''; - } + $messageType = 'error'; + $message = $this->wrapMessageWithType($messageType, $message); return $this->_io->err($message, $newlines); } @@ -687,13 +682,8 @@ public function err($message = null, $newlines = 1) */ public function info($message = null, $newlines = 1, $level = Shell::NORMAL) { - if (is_array($message)) { - foreach ($message as $k => $v) { - $message[$k] = '' . $v . ''; - } - } else { - $message = '' . $message . ''; - } + $messageType = 'info'; + $message = $this->wrapMessageWithType($messageType, $message); return $this->out($message, $newlines, $level); } @@ -708,13 +698,8 @@ public function info($message = null, $newlines = 1, $level = Shell::NORMAL) */ public function warn($message = null, $newlines = 1) { - if (is_array($message)) { - foreach ($message as $k => $v) { - $message[$k] = '' . $v . ''; - } - } else { - $message = '' . $message . ''; - } + $messageType = 'warning'; + $message = $this->wrapMessageWithType($messageType, $message); return $this->_io->err($message, $newlines); } @@ -729,16 +714,31 @@ public function warn($message = null, $newlines = 1) * @see https://book.cakephp.org/3.0/en/console-and-shells.html#Shell::out */ public function success($message = null, $newlines = 1, $level = Shell::NORMAL) + { + $messageType = 'success'; + $message = $this->wrapMessageWithType($messageType, $message); + + return $this->out($message, $newlines, $level); + } + + /** + * Wraps a message with a given message type, e.g. + * + * @param string $messageType The message type, e.g. "warning". + * @param string|array $message The message to wrap. + * @return array|string The message wrapped with the given message type. + */ + protected function wrapMessageWithType($messageType, $message) { if (is_array($message)) { foreach ($message as $k => $v) { - $message[$k] = '' . $v . ''; + $message[$k] = "<$messageType>" . $v . ""; } } else { - $message = '' . $message . ''; + $message = "<$messageType>" . $message . ""; } - return $this->out($message, $newlines, $level); + return $message; } /** From c48a34880e578eb869a435e80088a0265e66c141 Mon Sep 17 00:00:00 2001 From: antograssiot Date: Sun, 23 Jul 2017 22:36:51 +0200 Subject: [PATCH 02/12] Fix a typo in the annotation --- src/Http/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Response.php b/src/Http/Response.php index 50790df9846..5fdeebcf6d1 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -1184,7 +1184,7 @@ public function withCharset($charset) * Sets the correct headers to instruct the client to not cache the response * * @return void - * @deprected 3.4.0 Use withDisabledCache() instead. + * @deprecated 3.4.0 Use withDisabledCache() instead. */ public function disableCache() { From 2759482cbda4f840c88d6d8c15137435eb992b0f Mon Sep 17 00:00:00 2001 From: Mike Fellows Date: Tue, 25 Jul 2017 12:14:19 -0700 Subject: [PATCH 03/12] Fix pagination ordering on calculated columns on SQL Server. On older versions of SQL Server, the pagination is accomplished using a ROW_NUMBER() OVER clause. Unfortunately, OVER does not support the use of column aliases. But for calculated columns the only practical way to specify their use in ordering is with their alias, this currently causes a SQL error. This fix will substitute the calculation's SQL syntax in place of the column alias before generating the OVER clause for calculated columns, fixing the bug. --- .../Dialect/SqlserverDialectTrait.php | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Database/Dialect/SqlserverDialectTrait.php b/src/Database/Dialect/SqlserverDialectTrait.php index 03be72cacf7..f894836b368 100644 --- a/src/Database/Dialect/SqlserverDialectTrait.php +++ b/src/Database/Dialect/SqlserverDialectTrait.php @@ -14,6 +14,7 @@ */ namespace Cake\Database\Dialect; +use Cake\Database\ExpressionInterface; use Cake\Database\Expression\FunctionExpression; use Cake\Database\Expression\OrderByExpression; use Cake\Database\Expression\UnaryExpression; @@ -21,6 +22,7 @@ use Cake\Database\Schema\SqlserverSchema; use Cake\Database\SqlDialectTrait; use Cake\Database\SqlserverCompiler; +use Cake\Database\ValueBinder; use PDO; /** @@ -102,7 +104,31 @@ public function _version() protected function _pagingSubquery($original, $limit, $offset) { $field = '_cake_paging_._cake_page_rownum_'; - $order = $original->clause('order') ?: new OrderByExpression('(SELECT NULL)'); + + if ($original->clause('order')) { + $order = clone $original->clause('order'); + } else { + $order = new OrderByExpression('(SELECT NULL)'); + } + + // SQL server does not support column aliases in OVER clauses. But for + // calculated columns the alias is the only practical identifier to use + // when specifying the order. So if a column alias is specified in the + // order clause, and the value of that alias is an expression, change + // the alias into what it represents by setting the clause's key to be + // the SQL representation of its value. The UnaryExpression creation + // below will then do the right thing and use the calculation in the + // ROW_NUMBER() OVER clause instead of the alias. + $select = $original->clause('select'); + $order->iterateParts(function ($direction, &$orderBy) use ($select) { + if (isset($select[$orderBy])) { + if ($select[$orderBy] instanceof ExpressionInterface) { + $orderBy = $select[$orderBy]->sql(new ValueBinder()); + } + } + + return $direction; + }); $query = clone $original; $query->select([ From c23acbdef79d4dfd58256122453659aad4a3879d Mon Sep 17 00:00:00 2001 From: Joris Vaesen Date: Wed, 26 Jul 2017 12:50:42 +0200 Subject: [PATCH 04/12] DefaultPasswordHasher with changed options enables needsRehash --- src/Auth/DefaultPasswordHasher.php | 2 +- tests/TestCase/Auth/DefaultPasswordHasherTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Auth/DefaultPasswordHasher.php b/src/Auth/DefaultPasswordHasher.php index 1700a57da0c..dc9420cdd29 100644 --- a/src/Auth/DefaultPasswordHasher.php +++ b/src/Auth/DefaultPasswordHasher.php @@ -74,6 +74,6 @@ public function check($password, $hashedPassword) */ public function needsRehash($password) { - return password_needs_rehash($password, $this->_config['hashType']); + return password_needs_rehash($password, $this->_config['hashType'], $this->_config['hashOptions']); } } diff --git a/tests/TestCase/Auth/DefaultPasswordHasherTest.php b/tests/TestCase/Auth/DefaultPasswordHasherTest.php index 309bcc9e4c0..0f2978a1cba 100644 --- a/tests/TestCase/Auth/DefaultPasswordHasherTest.php +++ b/tests/TestCase/Auth/DefaultPasswordHasherTest.php @@ -36,4 +36,19 @@ public function testNeedsRehash() $password = $hasher->hash('foo'); $this->assertFalse($hasher->needsRehash($password)); } + + /** + * Tests that when the hash options change, the password needs + * to be rehashed + * + * @return void + */ + public function testNeedsRehashWithDifferentOptions() + { + $defaultHasher = new DefaultPasswordHasher(['hashType' => PASSWORD_BCRYPT, 'hashOptions' => ['cost' => 10]]); + $updatedHasher = new DefaultPasswordHasher(['hashType' => PASSWORD_BCRYPT, 'hashOptions' => ['cost' => 12]]); + $password = $defaultHasher->hash('foo'); + + $this->assertTrue($updatedHasher->needsRehash($password)); + } } From 74bb8737f784976610773d7f5e5b8ab977e51ada Mon Sep 17 00:00:00 2001 From: Mike Fellows Date: Wed, 26 Jul 2017 09:26:16 -0700 Subject: [PATCH 05/12] Change previous SQL Server order fix to create a new order clause. Avoid using pass by reference. --- .../Dialect/SqlserverDialectTrait.php | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Database/Dialect/SqlserverDialectTrait.php b/src/Database/Dialect/SqlserverDialectTrait.php index f894836b368..a58161c0861 100644 --- a/src/Database/Dialect/SqlserverDialectTrait.php +++ b/src/Database/Dialect/SqlserverDialectTrait.php @@ -106,30 +106,30 @@ protected function _pagingSubquery($original, $limit, $offset) $field = '_cake_paging_._cake_page_rownum_'; if ($original->clause('order')) { - $order = clone $original->clause('order'); + // SQL server does not support column aliases in OVER clauses. But + // the only practical way to specify the use of calculated columns + // is with their alias. So substitute the select SQL in place of + // any column aliases for those entries in the order clause. + $select = $original->clause('select'); + $order = new OrderByExpression(); + $original + ->clause('order') + ->iterateParts(function ($direction, $orderBy) use ($select, $order) { + $key = $orderBy; + if (isset($select[$orderBy]) && + $select[$orderBy] instanceof ExpressionInterface + ) { + $key = $select[$orderBy]->sql(new ValueBinder()); + } + $order->add([$key => $direction]); + + // Leave original order clause unchanged. + return $orderBy; + }); } else { $order = new OrderByExpression('(SELECT NULL)'); } - // SQL server does not support column aliases in OVER clauses. But for - // calculated columns the alias is the only practical identifier to use - // when specifying the order. So if a column alias is specified in the - // order clause, and the value of that alias is an expression, change - // the alias into what it represents by setting the clause's key to be - // the SQL representation of its value. The UnaryExpression creation - // below will then do the right thing and use the calculation in the - // ROW_NUMBER() OVER clause instead of the alias. - $select = $original->clause('select'); - $order->iterateParts(function ($direction, &$orderBy) use ($select) { - if (isset($select[$orderBy])) { - if ($select[$orderBy] instanceof ExpressionInterface) { - $orderBy = $select[$orderBy]->sql(new ValueBinder()); - } - } - - return $direction; - }); - $query = clone $original; $query->select([ '_cake_page_rownum_' => new UnaryExpression('ROW_NUMBER() OVER', $order) From 8fb97c5bdfda592cc615717ab9059aec2d9f81c3 Mon Sep 17 00:00:00 2001 From: Mike Fellows Date: Wed, 26 Jul 2017 15:42:16 -0700 Subject: [PATCH 06/12] Add test for selecting paginated results, order by calculated column --- tests/TestCase/Database/QueryTest.php | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/TestCase/Database/QueryTest.php b/tests/TestCase/Database/QueryTest.php index 85209b6f93d..3b9eefca91c 100644 --- a/tests/TestCase/Database/QueryTest.php +++ b/tests/TestCase/Database/QueryTest.php @@ -2247,6 +2247,36 @@ public function testSelectPage() $this->assertEquals(25, $query->clause('offset')); } + /** + * Test selecting rows using the page() method and ordering the results + * by a calculated column. + * + * @return void + */ + public function testSelectPageWithOrder() + { + $this->loadFixtures('Comments'); + $query = new Query($this->connection); + $result = $query + ->select([ + 'id', + 'ids_added' => $query->newExpr()->add('(user_id + article_id)') + ]) + ->from('comments') + ->order(['ids_added' => 'asc']) + ->limit(2) + ->page(3) + ->execute(); + $this->assertCount(2, $result); + $this->assertEquals( + [ + ['id' => '6', 'ids_added' => '4'], + ['id' => '2', 'ids_added' => '5'] + ], + $result->fetchAll('assoc') + ); + } + /** * Tests that Query objects can be included inside the select clause * and be used as a normal field, including binding any passed parameter From 44d001ced09887c3040697beda05fa4585522549 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 27 Jul 2017 10:50:09 -0500 Subject: [PATCH 07/12] Fixed bug causing requests with queries to be invalidated The SecurityComponent would fail at _validatePost because the query arguments were not encoded when the tokens were generated in the IntegrationTestCase --- src/TestSuite/IntegrationTestCase.php | 5 +++-- .../TestSuite/IntegrationTestCaseTest.php | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/TestSuite/IntegrationTestCase.php b/src/TestSuite/IntegrationTestCase.php index 7d230afa6dd..519bbf3679f 100644 --- a/src/TestSuite/IntegrationTestCase.php +++ b/src/TestSuite/IntegrationTestCase.php @@ -547,11 +547,12 @@ protected function _buildRequest($url, $method, $data) list ($url, $query) = $this->_url($url); $tokenUrl = $url; + parse_str($query, $queryData); + if ($query) { - $tokenUrl .= '?' . $query; + $tokenUrl .= '?' . http_build_query($queryData); } - parse_str($query, $queryData); $props = [ 'url' => $url, 'post' => $this->_addTokens($tokenUrl, $data), diff --git a/tests/TestCase/TestSuite/IntegrationTestCaseTest.php b/tests/TestCase/TestSuite/IntegrationTestCaseTest.php index b1fb558925f..10396aa4046 100644 --- a/tests/TestCase/TestSuite/IntegrationTestCaseTest.php +++ b/tests/TestCase/TestSuite/IntegrationTestCaseTest.php @@ -534,6 +534,24 @@ public function testPostSecuredFormWithQuery() $this->assertResponseContains('Request was accepted'); } + /** + * Test posting to a secured form action with a query that has a part that + * will be encoded by the security component + * + * @return void + */ + public function testPostSecuredFormWithUnencodedQuery() + { + $this->enableSecurityToken(); + $data = [ + 'title' => 'Some title', + 'body' => 'Some text' + ]; + $this->post('/posts/securePost?foo=/', $data); + $this->assertResponseOk(); + $this->assertResponseContains('Request was accepted'); + } + /** * Test posting to a secured form action action. * From 0c7daf1a3c6bdf4168315fec26e496057fa06c3d Mon Sep 17 00:00:00 2001 From: Bernat Arlandis Date: Thu, 27 Jul 2017 12:52:39 +0200 Subject: [PATCH 08/12] Fix empty translations --- src/I18n/Translator.php | 2 +- tests/TestCase/I18n/I18nTest.php | 12 ++++++++++++ tests/test_app/TestApp/Locale/en/default.po | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/I18n/Translator.php b/src/I18n/Translator.php index 2d890edfe18..294e74748de 100644 --- a/src/I18n/Translator.php +++ b/src/I18n/Translator.php @@ -174,7 +174,7 @@ protected function resolveContext($key, $message, array $vars) // No or missing context, fallback to the key/first message if ($context === null) { if (isset($message['_context'][''])) { - return $message['_context']['']; + return $message['_context'][''] === '' ? $key : $message['_context']['']; } return current($message['_context']); diff --git a/tests/TestCase/I18n/I18nTest.php b/tests/TestCase/I18n/I18nTest.php index a8775f495fc..d01c72437eb 100644 --- a/tests/TestCase/I18n/I18nTest.php +++ b/tests/TestCase/I18n/I18nTest.php @@ -844,4 +844,16 @@ public function testFallbackTranslatorWithFactory() $this->assertEquals('Le moo', $translator->translate('Cow')); $this->assertEquals('Le bark', $translator->translate('Dog')); } + + /** + * Tests the __() function on empty translations + * + * @return void + */ + public function testEmptyTranslationString() + { + I18n::defaultFormatter('sprintf'); + $result = __('No translation needed'); + $this->assertEquals('No translation needed', $result); + } } diff --git a/tests/test_app/TestApp/Locale/en/default.po b/tests/test_app/TestApp/Locale/en/default.po index 83ccdb14db6..5f6bf34104b 100644 --- a/tests/test_app/TestApp/Locale/en/default.po +++ b/tests/test_app/TestApp/Locale/en/default.po @@ -32,7 +32,7 @@ msgstr "This is a multiline translation\n" "This is the third line.\n" "This is the forth line. (translated)" -msgid "No Translation needed" +msgid "No translation needed" msgstr "" msgid "test" From c5f114c7ee8142618d8c05e2f9a20793dccbad1c Mon Sep 17 00:00:00 2001 From: ADmad Date: Mon, 31 Jul 2017 21:58:54 +0530 Subject: [PATCH 09/12] Bump up to phpstan 0.8. --- .travis.yml | 2 +- phpstan.neon | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 698b2822799..fadfdd8bd4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,7 @@ script: - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION != 7.0 ]]; then vendor/bin/phpunit; fi - if [[ $PHPCS = 1 ]]; then composer cs-check; fi - - if [[ $PHPSTAN = 1 ]]; then composer require --dev phpstan/phpstan:^0.7 && vendor/bin/phpstan analyse -c phpstan.neon -l 1 src; fi + - if [[ $PHPSTAN = 1 ]]; then composer require --dev phpstan/phpstan:^0.8 && vendor/bin/phpstan analyse -c phpstan.neon -l 1 src; fi after_success: - if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.0 ]]; then bash <(curl -s https://codecov.io/bash); fi diff --git a/phpstan.neon b/phpstan.neon index ed288813e84..85658cd7d12 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -15,7 +15,7 @@ parameters: - '#Access to an undefined property Cake\\[a-zA-Z0-9_\\]+::\$_validViewOptions#' - '#Access to an undefined property Cake\\Database\\Driver::\$_connection#' - '#Constant XC_TYPE_VAR not found#' - - '#Call to static method id\(\) on an unknown class PHPUnit_Runner_Version#' + - '#Class PHPUnit_Runner_Version not found and could not be autoloaded#' earlyTerminatingMethodCalls: Cake\Shell\Shell: - abort From 41ee62533d61f1727b8b95c0a7528b867410c509 Mon Sep 17 00:00:00 2001 From: saeid Date: Tue, 1 Aug 2017 02:04:51 +0430 Subject: [PATCH 10/12] Update deprecation suggestions --- src/Http/ServerRequest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Http/ServerRequest.php b/src/Http/ServerRequest.php index 726fcff966b..b3f87aedcc8 100644 --- a/src/Http/ServerRequest.php +++ b/src/Http/ServerRequest.php @@ -626,7 +626,7 @@ public function __get($name) * @param string $name The property being accessed. * @return bool Existence * @deprecated 3.4.0 Accessing routing parameters through __isset will removed in 4.0.0. - * Use param() instead. + * Use getParam() instead. */ public function __isset($name) { @@ -883,7 +883,7 @@ public static function addDetector($name, $callable) /** * Add parameters to the request's parsed parameter set. This will overwrite any existing parameters. - * This modifies the parameters available through `$request->params`. + * This modifies the parameters available through `$request->getParam()`. * * @param array $params Array of parameters to merge in * @return $this The current object, you can chain this method. @@ -2124,7 +2124,7 @@ public function offsetGet($name) * @param string $name Name of the key being written * @param mixed $value The value being written. * @return void - * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use withParam() or param() instead. + * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use withParam() instead. */ public function offsetSet($name, $value) { @@ -2136,7 +2136,7 @@ public function offsetSet($name, $value) * * @param string $name thing to check. * @return bool - * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use getParam() or param() instead. + * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use getParam() instead. */ public function offsetExists($name) { @@ -2152,7 +2152,7 @@ public function offsetExists($name) * * @param string $name Name to unset. * @return void - * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use withParam() or param() instead. + * @deprecated 3.4.0 The ArrayAccess methods will be removed in 4.0.0. Use withParam() instead. */ public function offsetUnset($name) { From e5be62241953ee90f3ec121fce3bf8074518d4be Mon Sep 17 00:00:00 2001 From: Mark Story Date: Tue, 1 Aug 2017 21:15:12 -0400 Subject: [PATCH 11/12] Update version number to 3.4.12 --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 09e99c78796..451abd9f00c 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -16,4 +16,4 @@ // @license https://opensource.org/licenses/mit-license.php MIT License // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -3.4.11 +3.4.12 From 68c56713c90397bba8d9dd531c2be946fa2c1045 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Wed, 2 Aug 2017 14:24:47 +0200 Subject: [PATCH 12/12] Refer to correct method --- src/Validation/Validator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Validation/Validator.php b/src/Validation/Validator.php index 53ccc759706..040d5e06682 100644 --- a/src/Validation/Validator.php +++ b/src/Validation/Validator.php @@ -1580,7 +1580,7 @@ public function isArray($field, $message = null, $when = null) * @param string|null $message The error message when the rule fails. * @param string|callable|null $when Either 'create' or 'update' or a callable that returns * true when the validation rule should be applied. - * @see \Cake\Validation\Validation::color() + * @see \Cake\Validation\Validation::hexColor() * @return $this */ public function hexColor($field, $message = null, $when = null)