Browse Source

Make Server/CommandRunner proxy event managers.

Instead of copying state around, we can use the existing interfaces to
create proxies for evented applications. This lets us make evented and
non-evented applications work the same as far as the
Server/CommandRunner are concerned.
Mark Story 8 years ago
parent
commit
e4cf40d097

+ 65 - 22
src/Console/CommandRunner.php

@@ -23,10 +23,11 @@ use Cake\Console\Exception\StopException;
 use Cake\Console\Shell;
 use Cake\Core\ConsoleApplicationInterface;
 use Cake\Core\PluginApplicationInterface;
-use Cake\Event\EventApplicationInterface;
 use Cake\Event\EventDispatcherInterface;
 use Cake\Event\EventDispatcherTrait;
+use Cake\Event\EventManager;
 use Cake\Utility\Inflector;
+use InvalidArgumentException;
 use RuntimeException;
 
 /**
@@ -34,7 +35,14 @@ use RuntimeException;
  */
 class CommandRunner implements EventDispatcherInterface
 {
-    use EventDispatcherTrait;
+    /**
+     * Alias methods away so we can implement proxying methods.
+     */
+    use EventDispatcherTrait {
+        eventManager as private _eventManager;
+        getEventManager as private _getEventManager;
+        setEventManager as private _setEventManager;
+    }
 
     /**
      * The application console commands are being run for.
@@ -65,7 +73,7 @@ class CommandRunner implements EventDispatcherInterface
      */
     public function __construct(ConsoleApplicationInterface $app, $root = 'cake')
     {
-        $this->setApp($app);
+        $this->app = $app;
         $this->root = $root;
         $this->aliases = [
             '--version' => 'version',
@@ -98,23 +106,6 @@ class CommandRunner implements EventDispatcherInterface
     }
 
     /**
-     * Set the application.
-     *
-     * @param \Cake\Core\ConsoleApplicationInterface $app The application to run CLI commands for.
-     * @return $this
-     */
-    public function setApp(ConsoleApplicationInterface $app)
-    {
-        $this->app = $app;
-
-        if ($app instanceof EventDispatcherInterface) {
-            $this->setEventManager($app->getEventManager());
-        }
-
-        return $this;
-    }
-
-    /**
      * Run the command contained in $argv.
      *
      * Use the application to do the following:
@@ -194,10 +185,62 @@ class CommandRunner implements EventDispatcherInterface
         if ($this->app instanceof PluginApplicationInterface) {
             $this->app->pluginBootstrap();
 
-            $events = $this->app->events($this->getEventManager());
+            $events = $this->app->getEventManager();
+            $events = $this->app->events($events);
             $events = $this->app->pluginEvents($events);
-            $this->setEventManager($events);
+            $this->app->setEventManager($events);
+        }
+    }
+
+    /**
+     * Get the application's event manager or the global one.
+     *
+     * @return \Cake\Event\EventManagerInterface
+     */
+    public function getEventManager()
+    {
+        if ($this->app instanceof PluginApplicationInterface) {
+            return $this->app->getEventManager();
+        }
+
+        return EventManager::instance();
+    }
+
+    /**
+     * Get/set the application's event manager.
+     *
+     * If the application does not support events and this method is used as
+     * a setter, an exception will be raised.
+     *
+     * @param \Cake\Event\EventManagerInterface $events The event manager to set.
+     * @return \Cake\Event\EventManagerInterface|$this
+     * @deprecated Will be removed in 4.0
+     */
+    public function eventManager(EventManager $events = null)
+    {
+        if ($eventManager === null) {
+            return $this->getEventManager();
         }
+
+        return $this->setEventManager($events);
+    }
+
+    /**
+     * Get/set the application's event manager.
+     *
+     * If the application does not support events and this method is used as
+     * a setter, an exception will be raised.
+     *
+     * @param \Cake\Event\EventManagerInterface $events The event manager to set.
+     * @return $this
+     */
+    public function setEventManager(EventManager $events)
+    {
+        if ($this->app instanceof PluginApplicationInterface) {
+            return $this->app->setEventManager($events);
+        }
+
+        throw new InvalidArgumentException('Cannot set the event manager, the application does not support events.');
     }
 
     /**

+ 64 - 21
src/Http/Server.php

@@ -16,9 +16,10 @@ namespace Cake\Http;
 
 use Cake\Core\HttpApplicationInterface;
 use Cake\Core\PluginApplicationInterface;
-use Cake\Event\EventApplicationInterface;
 use Cake\Event\EventDispatcherInterface;
 use Cake\Event\EventDispatcherTrait;
+use Cake\Event\EventManager;
+use InvalidArgumentException;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use RuntimeException;
@@ -30,7 +31,14 @@ use Zend\Diactoros\Response\EmitterInterface;
 class Server implements EventDispatcherInterface
 {
 
-    use EventDispatcherTrait;
+    /**
+     * Alias methods away so we can implement proxying methods.
+     */
+    use EventDispatcherTrait {
+        eventManager as private _eventManager;
+        getEventManager as private _getEventManager;
+        setEventManager as private _setEventManager;
+    }
 
     /**
      * @var \Cake\Core\HttpApplicationInterface
@@ -49,7 +57,7 @@ class Server implements EventDispatcherInterface
      */
     public function __construct(HttpApplicationInterface $app)
     {
-        $this->setApp($app);
+        $this->app = $app;
         $this->setRunner(new Runner());
     }
 
@@ -115,9 +123,10 @@ class Server implements EventDispatcherInterface
         if ($this->app instanceof PluginApplicationInterface) {
             $this->app->pluginBootstrap();
 
-            $events = $this->app->events($this->getEventManager());
+            $events = $this->app->getEventManager();
+            $events = $this->app->events($events);
             $events = $this->app->pluginEvents($events);
-            $this->setEventManager($events);
+            $this->app->setEventManager($events);
         }
     }
 
@@ -138,42 +147,76 @@ class Server implements EventDispatcherInterface
     }
 
     /**
-     * Set the application.
+     * Get the current application.
+     *
+     * @return \Cake\Core\HttpApplicationInterface The application that will be run.
+     */
+    public function getApp()
+    {
+        return $this->app;
+    }
+
+    /**
+     * Set the runner
      *
-     * @param \Cake\Core\HttpApplicationInterface $app The application to set.
+     * @param \Cake\Http\Runner $runner The runner to use.
      * @return $this
      */
-    public function setApp(HttpApplicationInterface $app)
+    public function setRunner(Runner $runner)
     {
-        $this->app = $app;
+        $this->runner = $runner;
+
+        return $this;
+    }
 
-        if ($app instanceof EventDispatcherInterface) {
-            $this->setEventManager($app->getEventManager());
+    /**
+     * Get the application's event manager or the global one.
+     *
+     * @return \Cake\Event\EventManagerInterface
+     */
+    public function getEventManager()
+    {
+        if ($this->app instanceof PluginApplicationInterface) {
+            return $this->app->getEventManager();
         }
 
-        return $this;
+        return EventManager::instance();
     }
 
     /**
-     * Get the current application.
+     * Get/set the application's event manager.
      *
-     * @return \Cake\Core\HttpApplicationInterface The application that will be run.
+     * If the application does not support events and this method is used as
+     * a setter, an exception will be raised.
+     *
+     * @param \Cake\Event\EventManagerInterface $events The event manager to set.
+     * @return \Cake\Event\EventManagerInterface|$this
+     * @deprecated Will be removed in 4.0
      */
-    public function getApp()
+    public function eventManager(EventManager $events = null)
     {
-        return $this->app;
+        if ($eventManager === null) {
+            return $this->getEventManager();
+        }
+
+        return $this->setEventManager($events);
     }
 
     /**
-     * Set the runner
+     * Get/set the application's event manager.
      *
-     * @param \Cake\Http\Runner $runner The runner to use.
+     * If the application does not support events and this method is used as
+     * a setter, an exception will be raised.
+     *
+     * @param \Cake\Event\EventManagerInterface $events The event manager to set.
      * @return $this
      */
-    public function setRunner(Runner $runner)
+    public function setEventManager(EventManager $events)
     {
-        $this->runner = $runner;
+        if ($this->app instanceof PluginApplicationInterface) {
+            return $this->app->setEventManager($events);
+        }
 
-        return $this;
+        throw new InvalidArgumentException('Cannot set the event manager, the application does not support events.');
     }
 }

+ 36 - 9
tests/TestCase/Console/CommandRunnerTest.php

@@ -19,11 +19,13 @@ use Cake\Console\CommandRunner;
 use Cake\Console\ConsoleIo;
 use Cake\Console\Shell;
 use Cake\Core\Configure;
+use Cake\Core\ConsoleApplicationInterface;
 use Cake\Event\EventList;
 use Cake\Event\EventManager;
 use Cake\Http\BaseApplication;
 use Cake\TestSuite\Stub\ConsoleOutput;
 use Cake\TestSuite\TestCase;
+use InvalidArgumentException;
 use TestApp\Command\DemoCommand;
 use TestApp\Http\EventApplication;
 use TestApp\Shell\SampleShell;
@@ -53,25 +55,50 @@ class CommandRunnerTest extends TestCase
     }
 
     /**
-     * test set on the app
+     * test event manager proxies to the application.
      *
      * @return void
      */
-    public function testSetApp()
+    public function testEventManagerProxies()
     {
-        $app = $this->getMockBuilder(BaseApplication::class)
-            ->setConstructorArgs([$this->config])
-            ->getMock();
-
-        $manager = new EventManager();
-        $app->method('getEventManager')
-            ->willReturn($manager);
+        $app = $this->getMockForAbstractClass(
+            BaseApplication::class,
+            [$this->config]
+        );
 
         $runner = new CommandRunner($app);
         $this->assertSame($app->getEventManager(), $runner->getEventManager());
     }
 
     /**
+     * test event manager cannot be set on applications without events.
+     *
+     * @return void
+     */
+    public function testGetEventManagerNonEventedApplication()
+    {
+        $app = $this->createMock(ConsoleApplicationInterface::class);
+
+        $runner = new CommandRunner($app);
+        $this->assertSame(EventManager::instance(), $runner->getEventManager());
+    }
+
+    /**
+     * test event manager cannot be set on applications without events.
+     *
+     * @return void
+     */
+    public function testSetEventManagerNonEventedApplication()
+    {
+        $this->expectException(InvalidArgumentException::class);
+        $app = $this->createMock(ConsoleApplicationInterface::class);
+
+        $events = new EventManager();
+        $runner = new CommandRunner($app);
+        $runner->setEventManager($events);
+    }
+
+    /**
      * Test that the console hook not returning a command collection
      * raises an error.
      *

+ 47 - 0
tests/TestCase/Http/ServerTest.php

@@ -14,13 +14,16 @@
  */
 namespace Cake\Test\TestCase;
 
+use Cake\Core\HttpApplicationInterface;
 use Cake\Event\Event;
 use Cake\Event\EventList;
 use Cake\Event\EventManager;
+use Cake\Http\BaseApplication;
 use Cake\Http\CallbackStream;
 use Cake\Http\MiddlewareQueue;
 use Cake\Http\Server;
 use Cake\TestSuite\TestCase;
+use InvalidArgumentException;
 use Psr\Http\Message\ResponseInterface;
 use RuntimeException;
 use TestApp\Http\BadResponseApplication;
@@ -301,4 +304,48 @@ class ServerTest extends TestCase
         $this->assertInstanceOf('Closure', $this->middleware->get(3), '2nd last middleware is a closure');
         $this->assertSame($app, $this->middleware->get(4), 'Last middleware is an app instance');
     }
+
+    /**
+     * test event manager proxies to the application.
+     *
+     * @return void
+     */
+    public function testEventManagerProxies()
+    {
+        $app = $this->getMockForAbstractClass(
+            BaseApplication::class,
+            [$this->config]
+        );
+
+        $server = new Server($app);
+        $this->assertSame($app->getEventManager(), $server->getEventManager());
+    }
+
+    /**
+     * test event manager cannot be set on applications without events.
+     *
+     * @return void
+     */
+    public function testGetEventManagerNonEventedApplication()
+    {
+        $app = $this->createMock(HttpApplicationInterface::class);
+
+        $server = new Server($app);
+        $this->assertSame(EventManager::instance(), $server->getEventManager());
+    }
+
+    /**
+     * test event manager cannot be set on applications without events.
+     *
+     * @return void
+     */
+    public function testSetEventManagerNonEventedApplication()
+    {
+        $this->expectException(InvalidArgumentException::class);
+        $app = $this->createMock(HttpApplicationInterface::class);
+
+        $events = new EventManager();
+        $server = new Server($app);
+        $server->setEventManager($events);
+    }
 }