Browse Source

Merge branch 'master' into 3.next

Mark Story 8 years ago
parent
commit
c20de0f344

+ 7 - 0
.stickler.yml

@@ -1,6 +1,13 @@
+---
 linters:
   phpcs:
     standard: CakePHP
     extensions: 'php,ctp'
+    fixer: true
+
 branches:
     ignore: ['2.x', '2.next']
+
+fixers:
+  enable: true
+  workflow: commit

+ 2 - 2
LICENSE.txt

@@ -1,7 +1,7 @@
 The MIT License (MIT)
 
 CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org)
-Copyright (c) 2005-2017, Cake Software Foundation, Inc. (https://cakefoundation.org)
+Copyright (c) 2005-2018, Cake Software Foundation, Inc. (https://cakefoundation.org)
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -19,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+SOFTWARE.

+ 1 - 1
src/Cache/CacheEngine.php

@@ -255,7 +255,7 @@ abstract class CacheEngine
 
         $prefix = '';
         if ($this->_groupPrefix) {
-            $prefix = vsprintf($this->_groupPrefix, $this->groups());
+            $prefix = md5(implode('_', $this->groups()));
         }
 
         $key = preg_replace('/[\s]+/', '_', strtolower(trim(str_replace([DIRECTORY_SEPARATOR, '/', '.'], '_', (string)$key))));

+ 2 - 2
src/Database/composer.json

@@ -25,12 +25,12 @@
     },
     "require": {
         "php": ">=5.6.0",
+        "cakephp/cache": "^3.0.0",
         "cakephp/core": "^3.0.0",
         "cakephp/datasource": "^3.0.0"
     },
     "suggest": {
-        "cakephp/log": "Require this if you want to use the built-in query logger",
-        "cakephp/cache": "Require this if you decide to use the schema caching feature"
+        "cakephp/log": "Require this if you want to use the built-in query logger"
     },
     "autoload": {
         "psr-4": {

+ 1 - 1
src/Datasource/EntityInterface.php

@@ -36,7 +36,7 @@ use JsonSerializable;
  * @method $this setAccess($property, $set)
  * @method bool isAccessible($property)
  * @method $this setSource($source)
- * @method array getSource()
+ * @method string getSource()
  * @method array extractOriginal(array $properties)
  * @method array extractOriginalChanged(array $properties)
  *

+ 3 - 2
src/Datasource/composer.json

@@ -28,8 +28,9 @@
         "cakephp/core": "^3.0.0"
     },
     "suggest": {
-        "cakephp/utility": "Require this if you decide to use EntityTrait",
-        "cakephp/collection": "Require this if you decide to use ResultSetInterface"
+        "cakephp/utility": "If you decide to use EntityTrait.",
+        "cakephp/collection": "If you decide to use ResultSetInterface.",
+        "cakephp/cache": "If you decide to use Query caching."
     },
     "autoload": {
         "psr-4": {

+ 5 - 5
src/ORM/Behavior/TreeBehavior.php

@@ -936,13 +936,13 @@ class TreeBehavior extends Behavior
      */
     protected function _scope($query)
     {
-        $config = $this->getConfig();
+        $scope = $this->getConfig('scope');
 
-        if (is_array($config['scope'])) {
-            return $query->where($config['scope']);
+        if (is_array($scope)) {
+            return $query->where($scope);
         }
-        if (is_callable($config['scope'])) {
-            return $config['scope']($query);
+        if (is_callable($scope)) {
+            return $scope($query);
         }
 
         return $query;

+ 18 - 1
src/ORM/README.md

@@ -32,7 +32,8 @@ ConnectionManager::setConfig('default', [
 	'database' => 'test',
 	'username' => 'root',
 	'password' => 'secret',
-	'cacheMetadata' => false // If set to `true` you need to install the optional "cakephp/cache" package.
+	'cacheMetadata' => true,
+	'quoteIdentifiers' => false,
 ]);
 ```
 
@@ -114,6 +115,22 @@ $article = $articles->get(2);
 $articles->delete($article);
 ```
 
+## Meta Data Cache
+
+It is recommended to enable meta data cache for production systems to avoid performance issues.
+For e.g. file system strategy your bootstrap file could look like this:
+```php
+use Cake\Cache\Engine\FileEngine;
+
+$cacheConfig = [
+   'className' => FileEngine::class,
+   'duration' => '+1 year',
+   'serialize' => true,
+   'prefix'    => 'orm_',
+],
+Cache::setConfig('_cake_model_', $cacheConfig);
+```
+
 ## Additional Documentation
 
 Consult [the CakePHP ORM documentation](https://book.cakephp.org/3.0/en/orm.html)

+ 5 - 0
src/Routing/Route/Route.php

@@ -488,6 +488,11 @@ class Route
             $route['_ext'] = $ext;
         }
 
+        // pass the name if set
+        if (isset($this->options['_name'])) {
+            $route['_name'] = $this->options['_name'];
+        }
+
         // restructure 'pass' key route params
         if (isset($this->options['pass'])) {
             $j = count($this->options['pass']);

+ 2 - 1
src/Routing/Router.php

@@ -768,7 +768,8 @@ class Router
             $params['requested'],
             $params['return'],
             $params['_Token'],
-            $params['_matchedRoute']
+            $params['_matchedRoute'],
+            $params['_name']
         );
         $params = array_merge($params, $pass);
         if (!empty($url)) {

+ 3 - 3
src/Template/Error/missing_action.ctp

@@ -22,9 +22,9 @@ if (!empty($plugin)) {
 }
 $prefixNs = '';
 if (!empty($prefix)) {
-    $prefix = Inflector::camelize($prefix);
-    $prefixNs = '\\' . $prefix;
-    $prefix .= DIRECTORY_SEPARATOR;
+    $prefix = array_map('\Cake\Utility\Inflector::camelize', explode('/', $prefix));
+    $prefixNs = '\\' . implode('\\', $prefix);
+    $prefix = implode(DIRECTORY_SEPARATOR, $prefix) . DIRECTORY_SEPARATOR;
 }
 
 // Controller MissingAction support

+ 2 - 3
src/Validation/Validation.php

@@ -281,12 +281,11 @@ class Validation
     /**
      * Used to compare 2 numeric values.
      *
-     * @param string $check1 if string is passed for, a string must also be passed for $check2
-     *    used as an array it must be passed as ['check1' => value, 'operator' => 'value', 'check2' => value]
+     * @param string $check1 The left value to compare.
      * @param string $operator Can be either a word or operand
      *    is greater >, is less <, greater or equal >=
      *    less or equal <=, is less <, equal to ==, not equal !=
-     * @param int $check2 only needed if $check1 is a string
+     * @param int $check2 The right value to compare.
      * @return bool Success
      */
     public static function comparison($check1, $operator, $check2)

+ 7 - 1
src/View/Helper/PaginatorHelper.php

@@ -536,7 +536,7 @@ class PaginatorHelper extends Helper
         }
 
         $url = array_filter($url, function ($value) {
-            return ($value || is_numeric($value));
+            return ($value || is_numeric($value) || $value === false);
         });
         $url = array_merge($url, $options);
 
@@ -553,6 +553,12 @@ class PaginatorHelper extends Helper
         if (!empty($paging['scope'])) {
             $scope = $paging['scope'];
             $currentParams = $this->_config['options']['url'];
+
+            if (isset($url['#'])) {
+                $currentParams['#'] = $url['#'];
+                unset($url['#']);
+            }
+
             // Merge existing query parameters in the scope.
             if (isset($currentParams['?'][$scope]) && is_array($currentParams['?'][$scope])) {
                 $url += $currentParams['?'][$scope];

+ 16 - 2
tests/TestCase/ORM/Behavior/TreeBehaviorTest.php

@@ -197,7 +197,7 @@ class TreeBehaviorTest extends TestCase
      *
      * @return void
      */
-    public function testCallableScoping()
+    public function testScopeCallable()
     {
         $table = TableRegistry::get('MenuLinkTrees');
         $table->addBehavior('Tree', [
@@ -208,7 +208,6 @@ class TreeBehaviorTest extends TestCase
         $count = $table->childCount($table->get(1), false);
         $this->assertEquals(4, $count);
     }
-
     /**
      * Tests the find('children') method
      *
@@ -235,6 +234,21 @@ class TreeBehaviorTest extends TestCase
     }
 
     /**
+     * Tests the find('children') plus scope=null
+     *
+     * @return void
+     */
+    public function testScopeNull()
+    {
+        $table = TableRegistry::get('MenuLinkTrees');
+        $table->addBehavior('Tree');
+        $table->behaviors()->get('Tree')->setConfig('scope', null);
+
+        $nodes = $table->find('children', ['for' => 1, 'direct' => true])->all();
+        $this->assertEquals([2, 3], $nodes->extract('id')->toArray());
+    }
+
+    /**
      * Tests that find('children') will throw an exception if the node was not found
      *
      * @return void

+ 61 - 0
tests/TestCase/Routing/RouteCollectionTest.php

@@ -128,6 +128,67 @@ class RouteCollectionTest extends TestCase
     }
 
     /**
+     * Test parsing routes with and without _name.
+     *
+     * @return void
+     */
+    public function testParseWithNameParameter()
+    {
+        $routes = new RouteBuilder($this->collection, '/b', ['key' => 'value']);
+        $routes->connect('/', ['controller' => 'Articles']);
+        $routes->connect('/:id', ['controller' => 'Articles', 'action' => 'view']);
+        $routes->connect('/media/search/*', ['controller' => 'Media', 'action' => 'search'], ['_name' => 'media_search']);
+
+        $result = $this->collection->parse('/b/');
+        $expected = [
+            'controller' => 'Articles',
+            'action' => 'index',
+            'pass' => [],
+            'plugin' => null,
+            'key' => 'value',
+            '_matchedRoute' => '/b',
+        ];
+        $this->assertEquals($expected, $result);
+
+        $result = $this->collection->parse('/b/the-thing?one=two');
+        $expected = [
+            'controller' => 'Articles',
+            'action' => 'view',
+            'id' => 'the-thing',
+            'pass' => [],
+            'plugin' => null,
+            'key' => 'value',
+            '?' => ['one' => 'two'],
+            '_matchedRoute' => '/b/:id',
+        ];
+        $this->assertEquals($expected, $result);
+
+        $result = $this->collection->parse('/b/media/search');
+        $expected = [
+            'key' => 'value',
+            'pass' => [],
+            'plugin' => null,
+            'controller' => 'Media',
+            'action' => 'search',
+            '_matchedRoute' => '/b/media/search/*',
+            '_name' => 'media_search'
+        ];
+        $this->assertEquals($expected, $result);
+
+        $result = $this->collection->parse('/b/media/search/thing');
+        $expected = [
+            'key' => 'value',
+            'pass' => ['thing'],
+            'plugin' => null,
+            'controller' => 'Media',
+            'action' => 'search',
+            '_matchedRoute' => '/b/media/search/*',
+            '_name' => 'media_search'
+        ];
+        $this->assertEquals($expected, $result);
+    }
+
+    /**
      * Test that parse decodes URL data before matching.
      * This is important for multibyte URLs that pass through URL rewriting.
      *

+ 1 - 1
tests/TestCase/Shell/RoutesShellTest.php

@@ -117,7 +117,7 @@ class RoutesShellTest extends ConsoleIntegrationTestCase
         $this->assertOutputContainsRow([
             'testName',
             '/tests/index',
-            '{"action":"index","pass":[],"controller":"Tests","plugin":null}'
+            '{"action":"index","pass":[],"controller":"Tests","plugin":null,"_name":"testName"}'
         ]);
     }
 

+ 47 - 2
tests/TestCase/View/Helper/PaginatorHelperTest.php

@@ -683,7 +683,6 @@ class PaginatorHelperTest extends TestCase
      */
     public function testSortAdminLinks()
     {
-        Configure::write('Routing.prefixes', ['admin']);
         Router::reload();
         Router::connect('/admin/:controller/:action/*', ['prefix' => 'admin']);
 
@@ -818,7 +817,6 @@ class PaginatorHelperTest extends TestCase
      */
     public function testGenerateUrlWithPrefixes()
     {
-        Configure::write('Routing.prefixes', ['members']);
         Router::reload();
         Router::connect('/members/:controller/:action/*', ['prefix' => 'members']);
         Router::connect('/:controller/:action/*');
@@ -882,6 +880,49 @@ class PaginatorHelperTest extends TestCase
     }
 
     /**
+     * test URL generation can leave prefix routes
+     *
+     * @return void
+     */
+    public function testGenerateUrlWithPrefixesLeavePrefix()
+    {
+        Router::reload();
+        Router::connect('/members/:controller/:action/*', ['prefix' => 'members']);
+        Router::connect('/:controller/:action/*');
+
+        $request = new ServerRequest([
+            'params' => [
+                'prefix' => 'members',
+                'controller' => 'posts',
+                'action' => 'index',
+                'plugin' => null,
+                'paging' => [
+                    'Articles' => ['page' => 2, 'prevPage' => true]
+                ]
+            ],
+            'webroot' => '/'
+        ]);
+        Router::setRequestInfo($request);
+        $this->Paginator->request = $request;
+
+        $result = $this->Paginator->generateUrl();
+        $expected = '/members/posts/index?page=2';
+        $this->assertEquals($expected, $result);
+
+        $result = $this->Paginator->generateUrl(['prefix' => 'members']);
+        $expected = '/members/posts/index?page=2';
+        $this->assertEquals($expected, $result);
+
+        $result = $this->Paginator->generateUrl(['prefix' => false]);
+        $expected = '/posts/index?page=2';
+        $this->assertEquals($expected, $result);
+
+        $this->Paginator->options(['url' => ['prefix' => false]]);
+        $result = $this->Paginator->generateUrl();
+        $this->assertEquals($expected, $result, 'Setting prefix in options should work too.');
+    }
+
+    /**
      * test generateUrl with multiple pagination
      *
      * @return void
@@ -939,6 +980,10 @@ class PaginatorHelperTest extends TestCase
         $result = $this->Paginator->generateUrl(['sort' => 'name']);
         $expected = '/posts/index?article%5Bpage%5D=3&amp;article%5Bsort%5D=name';
         $this->assertEquals($expected, $result);
+
+        $result = $this->Paginator->generateUrl(['#' => 'foo']);
+        $expected = '/posts/index?article%5Bpage%5D=3#foo';
+        $this->assertEquals($expected, $result);
     }
 
     /**