Browse Source

Merge branch '4.next' into 5.x

ADmad 4 years ago
parent
commit
7ad8fa599b

+ 1 - 1
composer.json

@@ -95,7 +95,7 @@
             "TestPluginTwo\\": "tests/test_app/Plugin/TestPluginTwo/src/",
             "Company\\TestPluginThree\\": "tests/test_app/Plugin/Company/TestPluginThree/src/",
             "Company\\TestPluginThree\\Test\\": "tests/test_app/Plugin/Company/TestPluginThree/tests/",
-            "ParentPlugin\\": "tests/test_app/Plugin/ParentPlugin/src/"
+            "Named\\": "tests/test_app/Plugin/Named/src/"
         }
     },
     "scripts": {

+ 4 - 0
src/Cache/Engine/FileEngine.php

@@ -223,6 +223,10 @@ class FileEngine extends CacheEngine
         $path = $this->_File->getRealPath();
         unset($this->_File);
 
+        if ($path === false) {
+            return false;
+        }
+
         // phpcs:disable
         return @unlink($path);
         // phpcs:enable

+ 2 - 0
src/Controller/Controller.php

@@ -527,6 +527,7 @@ class Controller implements EventListenerInterface, EventDispatcherInterface
      *  - `only`: (array|string) Only run the middleware for specified actions.
      *  - `except`: (array|string) Run the middleware for all actions except the specified ones.
      * @return void
+     * @since 4.3.0
      * @psalm-param array{only?: array|string, except?: array|string} $options
      */
     public function middleware(MiddlewareInterface|Closure|string $middleware, array $options = []): void
@@ -541,6 +542,7 @@ class Controller implements EventListenerInterface, EventDispatcherInterface
      * Get middleware to be applied for this controller.
      *
      * @return array
+     * @since 4.3.0
      */
     public function getMiddleware(): array
     {

+ 19 - 6
src/Core/PluginCollection.php

@@ -115,7 +115,7 @@ class PluginCollection implements Iterator, Countable
      * This method is not part of the official public API as plugins with
      * no plugin class are being phased out.
      *
-     * @param string $name The plugin name to locate a path for. Will return '' when a plugin cannot be found.
+     * @param string $name The plugin name to locate a path for.
      * @return string
      * @throws \Cake\Core\Exception\MissingPluginException when a plugin path cannot be resolved.
      * @internal
@@ -246,15 +246,28 @@ class PluginCollection implements Iterator, Countable
         }
 
         $config += ['name' => $name];
-        /** @var class-string<\Cake\Core\PluginInterface> $className */
-        $className = str_replace('/', '\\', $name) . '\\' . 'Plugin';
+        $namespace = str_replace('/', '\\', $name);
+
+        $className = $namespace . '\\' . 'Plugin';
+        // Check for [Vendor/]Foo/Plugin class
         if (!class_exists($className)) {
-            $className = BasePlugin::class;
-            if (empty($config['path'])) {
-                $config['path'] = $this->findPath($name);
+            $pos = strpos($name, '/');
+            if ($pos === false) {
+                $className = $namespace . '\\' . $name . 'Plugin';
+            } else {
+                $className = $namespace . '\\' . substr($name, $pos + 1) . 'Plugin';
+            }
+
+            // Check for [Vendor/]Foo/FooPlugin
+            if (!class_exists($className)) {
+                $className = BasePlugin::class;
+                if (empty($config['path'])) {
+                    $config['path'] = $this->findPath($name);
+                }
             }
         }
 
+        /** @var class-string<\Cake\Core\PluginInterface> $className */
         return new $className($config);
     }
 

+ 20 - 23
src/ORM/Association/BelongsToMany.php

@@ -21,7 +21,6 @@ use Cake\Database\Expression\IdentifierExpression;
 use Cake\Database\Expression\QueryExpression;
 use Cake\Database\ExpressionInterface;
 use Cake\Datasource\EntityInterface;
-use Cake\Datasource\ResultSetInterface;
 use Cake\ORM\Association;
 use Cake\ORM\Association\Loader\SelectWithPivotLoader;
 use Cake\ORM\Query;
@@ -1187,35 +1186,33 @@ class BelongsToMany extends Association
                 $target = $this->getTarget();
 
                 $foreignKey = (array)$this->getForeignKey();
-                /** @psalm-suppress PossiblyFalseArgument getForeignKey() returns false */
+                $assocForeignKey = (array)$junction->getAssociation($target->getAlias())->getForeignKey();
                 $prefixedForeignKey = array_map([$junction, 'aliasField'], $foreignKey);
 
                 $junctionPrimaryKey = (array)$junction->getPrimaryKey();
-                $assocForeignKey = (array)$junction->getAssociation($target->getAlias())->getForeignKey();
+                $junctionQueryAlias = $junction->getAlias() . '__matches';
 
-                /** @psalm-suppress InvalidScalarArgument getForeignKey() returns false */
-                $keys = array_combine($foreignKey, $prefixedForeignKey);
+                $keys = $matchesConditions = [];
                 foreach (array_merge($assocForeignKey, $junctionPrimaryKey) as $key) {
-                    /** @psalm-suppress PossiblyFalseArgument getForeignKey() returns false */
-                    $keys[$key] = $junction->aliasField($key);
+                    $aliased = $junction->aliasField($key);
+                    $keys[$key] = $aliased;
+                    $matchesConditions[$aliased] = new IdentifierExpression($junctionQueryAlias . '.' . $key);
                 }
 
-                $existing = $this->_appendJunctionJoin($this->find())
-                    ->select($junction)
-                    ->where(array_combine($prefixedForeignKey, $primaryValue))
-                    ->formatResults(function (ResultSetInterface $results) use ($junction) {
-                        $junctionEntity = $junction->getEntityClass();
-                        $junctionAlias = $junction->getAlias();
-
-                        // Extract data for the junction entity and map the result
-                        // into junction entity instances so that delete callbacks work correctly.
-                        return $results->map(function ($item) use ($junctionEntity, $junctionAlias) {
-                            return new $junctionEntity(
-                                $item[$junctionAlias],
-                                ['markNew' => false, 'markClean' => true]
-                            );
-                        });
-                    });
+                // Use association to create row selection
+                // with finders & association conditions.
+                $matches = $this->_appendJunctionJoin($this->find())
+                    ->select($keys)
+                    ->where(array_combine($prefixedForeignKey, $primaryValue));
+
+                // Create a subquery join to ensure we get
+                // the correct entity passed to callbacks.
+                $existing = $junction->query()
+                    ->from([$junctionQueryAlias => $matches])
+                    ->innerJoin(
+                        [$junction->getAlias() => $junction->getTable()],
+                        $matchesConditions
+                    );
 
                 $jointEntities = $this->_collectJointEntities($sourceEntity, $targetEntities);
                 $inserts = $this->_diffLinks($existing, $jointEntities, $targetEntities, $options);

+ 7 - 0
src/Routing/Middleware/RoutingMiddleware.php

@@ -69,6 +69,13 @@ class RoutingMiddleware implements MiddlewareInterface
      */
     public function __construct(RoutingApplicationInterface $app, ?string $cacheConfig = null)
     {
+        if ($cacheConfig !== null) {
+            deprecationWarning(
+                'Use of routing cache is deprecated and will be removed in 5.0. ' .
+                'Upgrade to the new `CakeDC/CachedRouting` plugin. ' .
+                'See https://github.com/CakeDC/cakephp-cached-routing'
+            );
+        }
         $this->app = $app;
         $this->cacheConfig = $cacheConfig;
     }

+ 2 - 2
tests/TestCase/Core/BasePluginTest.php

@@ -25,7 +25,7 @@ use Cake\Http\MiddlewareQueue;
 use Cake\Routing\RouteBuilder;
 use Cake\Routing\RouteCollection;
 use Cake\TestSuite\TestCase;
-use Company\TestPluginThree\Plugin as TestPluginThree;
+use Company\TestPluginThree\TestPluginThreePlugin;
 use TestPlugin\Plugin as TestPlugin;
 
 /**
@@ -64,7 +64,7 @@ class BasePluginTest extends TestCase
         $plugin = new TestPlugin();
         $this->assertSame('TestPlugin', $plugin->getName());
 
-        $plugin = new TestPluginThree();
+        $plugin = new TestPluginThreePlugin();
         $this->assertSame('Company/TestPluginThree', $plugin->getName());
     }
 

+ 17 - 14
tests/TestCase/Core/PluginCollectionTest.php

@@ -21,9 +21,9 @@ use Cake\Core\Exception\MissingPluginException;
 use Cake\Core\PluginCollection;
 use Cake\Core\PluginInterface;
 use Cake\TestSuite\TestCase;
-use Company\TestPluginThree\Plugin as TestPluginThree;
+use Company\TestPluginThree\TestPluginThreePlugin;
 use InvalidArgumentException;
-use ParentPlugin\Plugin;
+use Named\NamedPlugin;
 use TestPlugin\Plugin as TestPlugin;
 
 /**
@@ -65,7 +65,7 @@ class PluginCollectionTest extends TestCase
     public function testAddVendoredPlugin(): void
     {
         $plugins = new PluginCollection();
-        $plugins->add(new TestPluginThree());
+        $plugins->add(new TestPluginThreePlugin());
 
         $this->assertTrue($plugins->has('Company/TestPluginThree'));
         $this->assertFalse($plugins->has('TestPluginThree'));
@@ -95,8 +95,8 @@ class PluginCollectionTest extends TestCase
     public function testGetAutoload(): void
     {
         $plugins = new PluginCollection();
-        $plugin = $plugins->get('ParentPlugin');
-        $this->assertInstanceOf(Plugin::class, $plugin);
+        $plugin = $plugins->get('Named');
+        $this->assertInstanceOf(NamedPlugin::class, $plugin);
     }
 
     public function testGetInvalid(): void
@@ -111,15 +111,18 @@ class PluginCollectionTest extends TestCase
     {
         $plugins = new PluginCollection();
 
-        $plugin = $plugins->create('ParentPlugin');
-        $this->assertInstanceOf(Plugin::class, $plugin);
+        $plugin = $plugins->create('Named');
+        $this->assertInstanceOf(NamedPlugin::class, $plugin);
 
-        $plugin = $plugins->create('ParentPlugin', ['name' => 'Granpa']);
-        $this->assertInstanceOf(Plugin::class, $plugin);
+        $plugin = $plugins->create('Named', ['name' => 'Granpa']);
+        $this->assertInstanceOf(NamedPlugin::class, $plugin);
         $this->assertSame('Granpa', $plugin->getName());
 
-        $plugin = $plugins->create(Plugin::class);
-        $this->assertInstanceOf(Plugin::class, $plugin);
+        $plugin = $plugins->create(NamedPlugin::class);
+        $this->assertInstanceOf(NamedPlugin::class, $plugin);
+
+        $plugin = $plugins->create('Company/TestPluginThree');
+        $this->assertInstanceOf(TestPluginThreePlugin::class, $plugin);
 
         $plugin = $plugins->create('TestTheme');
         $this->assertInstanceOf(BasePlugin::class, $plugin);
@@ -130,7 +133,7 @@ class PluginCollectionTest extends TestCase
     {
         $data = [
             new TestPlugin(),
-            new TestPluginThree(),
+            new TestPluginThreePlugin(),
         ];
         $plugins = new PluginCollection($data);
         $out = [];
@@ -147,7 +150,7 @@ class PluginCollectionTest extends TestCase
         $plugin = new TestPlugin();
         $plugin->disable('routes');
 
-        $pluginThree = new TestPluginThree();
+        $pluginThree = new TestPluginThreePlugin();
 
         $plugins->add($plugin);
         $plugins->add($pluginThree);
@@ -171,7 +174,7 @@ class PluginCollectionTest extends TestCase
     {
         $plugins = new PluginCollection();
         $plugin = new TestPlugin();
-        $pluginThree = new TestPluginThree();
+        $pluginThree = new TestPluginThreePlugin();
 
         $plugins->add($plugin);
         $plugins->add($pluginThree);

+ 2 - 2
tests/TestCase/Http/BaseApplicationTest.php

@@ -183,10 +183,10 @@ class BaseApplicationTest extends TestCase
             BaseApplication::class,
             [$this->path]
         );
-        $app->addPlugin('ParentPlugin');
+        $app->addPlugin('Named');
         $app->pluginBootstrap();
         $this->assertTrue(
-            Configure::check('ParentPlugin.bootstrap'),
+            Configure::check('Named.bootstrap'),
             'Plugin bootstrap should be run'
         );
         $this->assertTrue(

+ 33 - 0
tests/TestCase/Routing/Middleware/RoutingMiddlewareTest.php

@@ -461,6 +461,9 @@ class RoutingMiddlewareTest extends TestCase
      */
     public function testCacheRoutes(): void
     {
+        Configure::write('Error.ignoredDeprecationPaths', [
+            'tests/TestCase/Routing/Middleware/RoutingMiddlewareTest.php',
+        ]);
         $cacheConfigName = '_cake_router_';
         Cache::setConfig($cacheConfigName, [
             'engine' => 'File',
@@ -476,6 +479,7 @@ class RoutingMiddlewareTest extends TestCase
         $app = new Application(CONFIG);
         $middleware = new RoutingMiddleware($app, $cacheConfigName);
         $middleware->process($request, $handler);
+        Configure::delete('Error.ignoredDeprecationPaths');
     }
 
     /**
@@ -483,6 +487,9 @@ class RoutingMiddlewareTest extends TestCase
      */
     public function testCacheNotUsedIfCacheDisabled(): void
     {
+        Configure::write('Error.ignoredDeprecationPaths', [
+            'tests/TestCase/Routing/Middleware/RoutingMiddlewareTest.php',
+        ]);
         $cacheConfigName = '_cake_router_';
         Cache::drop($cacheConfigName);
         Cache::disable();
@@ -500,6 +507,7 @@ class RoutingMiddlewareTest extends TestCase
         $app = new Application(CONFIG);
         $middleware = new RoutingMiddleware($app, $cacheConfigName);
         $middleware->process($request, $handler);
+        Configure::delete('Error.ignoredDeprecationPaths');
     }
 
     /**
@@ -507,6 +515,9 @@ class RoutingMiddlewareTest extends TestCase
      */
     public function testCacheConfigNotFound(): void
     {
+        Configure::write('Error.ignoredDeprecationPaths', [
+            'tests/TestCase/Routing/Middleware/RoutingMiddlewareTest.php',
+        ]);
         $this->expectException(CacheInvalidArgumentException::class);
         $this->expectExceptionMessage('The "notfound" cache configuration does not exist.');
 
@@ -518,6 +529,7 @@ class RoutingMiddlewareTest extends TestCase
         $app = new Application(CONFIG);
         $middleware = new RoutingMiddleware($app, 'notfound');
         $middleware->process($request, new TestRequestHandler());
+        Configure::delete('Error.ignoredDeprecationPaths');
     }
 
     public function testFailedRouteCache(): void
@@ -528,6 +540,26 @@ class RoutingMiddlewareTest extends TestCase
         ]);
 
         $app = $this->createMock(Application::class);
+        $this->expectDeprecation();
+        $this->expectDeprecationMessage(
+            'Use of routing cache is deprecated and will be removed in 5.0. ' .
+            'Upgrade to the new `CakeDC/CachedRouting` plugin. ' .
+            'See https://github.com/CakeDC/cakephp-cached-routing'
+        );
+        new RoutingMiddleware($app, '_cake_router_');
+    }
+
+    public function testDeprecatedRouteCache(): void
+    {
+        Configure::write('Error.ignoredDeprecationPaths', [
+            'tests/TestCase/Routing/Middleware/RoutingMiddlewareTest.php',
+        ]);
+        Cache::setConfig('_cake_router_', [
+            'engine' => 'File',
+            'path' => CACHE,
+        ]);
+
+        $app = $this->createMock(Application::class);
         $app
             ->method('routes')
             ->will($this->returnCallback(function (RouteBuilder $routes) use ($app) {
@@ -540,6 +572,7 @@ class RoutingMiddlewareTest extends TestCase
         $this->expectException(FailedRouteCacheException::class);
         $this->expectExceptionMessage('Unable to cache route collection.');
         $middleware->process($request, new TestRequestHandler());
+        Configure::delete('Error.ignoredDeprecationPaths');
     }
 
     /**

+ 1 - 1
tests/test_app/Plugin/Company/TestPluginThree/src/Plugin.php

@@ -17,6 +17,6 @@ namespace Company\TestPluginThree;
 
 use Cake\Core\BasePlugin;
 
-class Plugin extends BasePlugin
+class TestPluginThreePlugin extends BasePlugin
 {
 }

+ 3 - 3
tests/test_app/Plugin/ParentPlugin/src/Plugin.php

@@ -1,17 +1,17 @@
 <?php
 declare(strict_types=1);
 
-namespace ParentPlugin;
+namespace Named;
 
 use Cake\Core\BasePlugin;
 use Cake\Core\Configure;
 use Cake\Core\PluginApplicationInterface;
 
-class Plugin extends BasePlugin
+class NamedPlugin extends BasePlugin
 {
     public function bootstrap(PluginApplicationInterface $app): void
     {
-        Configure::write('ParentPlugin.bootstrap', true);
+        Configure::write('Named.bootstrap', true);
 
         $app->addPlugin('TestPluginTwo', ['bootstrap' => true]);
         $app->addPlugin('TestPlugin', ['bootstrap' => true]);