Skip to content

Commit

Permalink
Make Server/CommandRunner proxy event managers.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
markstory committed Feb 23, 2018
1 parent 7f56814 commit e4cf40d
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 52 deletions.
87 changes: 65 additions & 22 deletions src/Console/CommandRunner.php
Expand Up @@ -23,18 +23,26 @@
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;

/**
* Run CLI commands for the provided application.
*/
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.
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -97,23 +105,6 @@ public function setAliases(array $aliases)
return $this;
}

/**
* 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.
*
Expand Down Expand Up @@ -194,10 +185,62 @@ protected function bootstrap()
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.');
}

/**
Expand Down
85 changes: 64 additions & 21 deletions src/Http/Server.php
Expand Up @@ -16,9 +16,10 @@

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;
Expand All @@ -30,7 +31,14 @@
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
Expand All @@ -49,7 +57,7 @@ class Server implements EventDispatcherInterface
*/
public function __construct(HttpApplicationInterface $app)
{
$this->setApp($app);
$this->app = $app;
$this->setRunner(new Runner());
}

Expand Down Expand Up @@ -115,9 +123,10 @@ protected function bootstrap()
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);
}
}

Expand All @@ -138,42 +147,76 @@ public function emit(ResponseInterface $response, EmitterInterface $emitter = nu
}

/**
* 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.');
}
}
45 changes: 36 additions & 9 deletions tests/TestCase/Console/CommandRunnerTest.php
Expand Up @@ -19,11 +19,13 @@
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;
Expand Down Expand Up @@ -53,24 +55,49 @@ public function setUp()
}

/**
* 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.
Expand Down

0 comments on commit e4cf40d

Please sign in to comment.