Browse Source

Merge branch 'master' into 3.next

mark_story 8 years ago
parent
commit
4253fe7219

+ 1 - 1
.travis.yml

@@ -75,7 +75,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

+ 1 - 1
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

+ 1 - 1
src/Auth/DefaultPasswordHasher.php

@@ -74,6 +74,6 @@ class DefaultPasswordHasher extends AbstractPasswordHasher
      */
     public function needsRehash($password)
     {
-        return password_needs_rehash($password, $this->_config['hashType']);
+        return password_needs_rehash($password, $this->_config['hashType'], $this->_config['hashOptions']);
     }
 }

+ 24 - 24
src/Console/Shell.php

@@ -702,13 +702,8 @@ class Shell
      */
     public function err($message = null, $newlines = 1)
     {
-        if (is_array($message)) {
-            foreach ($message as $k => $v) {
-                $message[$k] = '<error>' . $v . '</error>';
-            }
-        } else {
-            $message = '<error>' . $message . '</error>';
-        }
+        $messageType = 'error';
+        $message = $this->wrapMessageWithType($messageType, $message);
 
         return $this->_io->err($message, $newlines);
     }
@@ -724,13 +719,8 @@ class Shell
      */
     public function info($message = null, $newlines = 1, $level = Shell::NORMAL)
     {
-        if (is_array($message)) {
-            foreach ($message as $k => $v) {
-                $message[$k] = '<info>' . $v . '</info>';
-            }
-        } else {
-            $message = '<info>' . $message . '</info>';
-        }
+        $messageType = 'info';
+        $message = $this->wrapMessageWithType($messageType, $message);
 
         return $this->out($message, $newlines, $level);
     }
@@ -745,13 +735,8 @@ class Shell
      */
     public function warn($message = null, $newlines = 1)
     {
-        if (is_array($message)) {
-            foreach ($message as $k => $v) {
-                $message[$k] = '<warning>' . $v . '</warning>';
-            }
-        } else {
-            $message = '<warning>' . $message . '</warning>';
-        }
+        $messageType = 'warning';
+        $message = $this->wrapMessageWithType($messageType, $message);
 
         return $this->_io->err($message, $newlines);
     }
@@ -767,15 +752,30 @@ class Shell
      */
     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. <warning>
+     *
+     * @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] = '<success>' . $v . '</success>';
+                $message[$k] = "<$messageType>" . $v . "</$messageType>";
             }
         } else {
-            $message = '<success>' . $message . '</success>';
+            $message = "<$messageType>" . $message . "</$messageType>";
         }
 
-        return $this->out($message, $newlines, $level);
+        return $message;
     }
 
     /**

+ 27 - 1
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\Query;
 use Cake\Database\Schema\SqlserverSchema;
 use Cake\Database\SqlDialectTrait;
 use Cake\Database\SqlserverCompiler;
+use Cake\Database\ValueBinder;
 use PDO;
 
 /**
@@ -102,7 +104,31 @@ trait SqlserverDialectTrait
     protected function _pagingSubquery($original, $limit, $offset)
     {
         $field = '_cake_paging_._cake_page_rownum_';
-        $order = $original->clause('order') ?: new OrderByExpression('(SELECT NULL)');
+
+        if ($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)');
+        }
 
         $query = clone $original;
         $query->select([

+ 5 - 5
src/Http/ServerRequest.php

@@ -638,7 +638,7 @@ class ServerRequest implements ArrayAccess, ServerRequestInterface
      * @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)
     {
@@ -895,7 +895,7 @@ class ServerRequest implements ArrayAccess, ServerRequestInterface
 
     /**
      * 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.
@@ -2214,7 +2214,7 @@ class ServerRequest implements ArrayAccess, ServerRequestInterface
      * @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)
     {
@@ -2226,7 +2226,7 @@ class ServerRequest implements ArrayAccess, ServerRequestInterface
      *
      * @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)
     {
@@ -2242,7 +2242,7 @@ class ServerRequest implements ArrayAccess, ServerRequestInterface
      *
      * @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)
     {

+ 1 - 1
src/I18n/Translator.php

@@ -174,7 +174,7 @@ class Translator implements TranslatorInterface
         // 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']);

+ 3 - 2
src/TestSuite/IntegrationTestCase.php

@@ -578,11 +578,12 @@ abstract class IntegrationTestCase extends TestCase
         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),

+ 1 - 1
src/Validation/Validator.php

@@ -1645,7 +1645,7 @@ class Validator implements ArrayAccess, IteratorAggregate, Countable
      * @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)

+ 15 - 0
tests/TestCase/Auth/DefaultPasswordHasherTest.php

@@ -36,4 +36,19 @@ class DefaultPasswordHasherTest extends TestCase
         $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));
+    }
 }

+ 30 - 0
tests/TestCase/Database/QueryTest.php

@@ -2248,6 +2248,36 @@ class QueryTest extends TestCase
     }
 
     /**
+     * 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
      *

+ 12 - 0
tests/TestCase/I18n/I18nTest.php

@@ -845,4 +845,16 @@ class I18nTest extends TestCase
         $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);
+    }
 }

+ 18 - 0
tests/TestCase/TestSuite/IntegrationTestCaseTest.php

@@ -575,6 +575,24 @@ class IntegrationTestCaseTest extends IntegrationTestCase
     }
 
     /**
+     * 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.
      *
      * @return void

+ 1 - 1
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"