diff --git a/src/Console/ShellDispatcher.php b/src/Console/ShellDispatcher.php index 420e97d426d..59e6ecabd14 100644 --- a/src/Console/ShellDispatcher.php +++ b/src/Console/ShellDispatcher.php @@ -18,6 +18,7 @@ use Cake\Core\App; use Cake\Core\Configure; use Cake\Core\Exception\Exception; +use Cake\Core\Plugin; use Cake\Utility\Inflector; /** @@ -68,12 +69,27 @@ public function __construct($args = [], $bootstrap = true) { * * If you re-use an alias the last alias set will be the one available. * + * ### Usage + * + * Aliasing a shell named ClassName: + * + * `$this->alias('alias', 'ClassName');` + * + * Getting the original name for a given alias: + * + * `$this->alias('alias');` + * * @param string $short The new short name for the shell. - * @param string $original The original full name for the shell. - * @return void + * @param string|null $original The original full name for the shell. + * @return string|false The aliased class name, or false if the alias does not exist */ - public static function alias($short, $original) { - static::$_aliases[$short] = $original; + public static function alias($short, $original = null) { + $short = Inflector::camelize($short); + if ($original) { + static::$_aliases[$short] = $original; + } + + return isset(static::$_aliases[$short]) ? static::$_aliases[$short] : false; } /** @@ -157,16 +173,37 @@ protected function _dispatch() { return true; } + if (!self::$_aliases) { + $this->addShortPluginAliases(); + } + $Shell = $this->findShell($shell); $Shell->initialize(); return $Shell->runCommand($this->args, true); } +/** + * For all loaded plugins, add a short alias + * + * This permits a plugin which implements a shell of the same name to be accessed + * Using the plugin name alone + * + * @return void + */ + public function addShortPluginAliases() { + $plugins = Plugin::loaded(); + + foreach ($plugins as $plugin) { + self::alias($plugin, "$plugin.$plugin"); + } + } + /** * Get shell to use, either plugin shell or application shell * - * All paths in the loaded shell paths are searched. + * All paths in the loaded shell paths are searched, handles alias + * dereferencing * * @param string $shell Optionally the name of a plugin * @return \Cake\Console\Shell A shell instance. @@ -174,19 +211,34 @@ protected function _dispatch() { */ public function findShell($shell) { $className = $this->_shellExists($shell); - if (!$className && isset(static::$_aliases[$shell])) { - $shell = static::$_aliases[$shell]; + if (!$className) { + $shell = $this->_handleAlias($shell); $className = $this->_shellExists($shell); } - if ($className) { - list($plugin) = pluginSplit($shell); - $instance = new $className(); - $instance->plugin = Inflector::camelize(trim($plugin, '.')); - return $instance; + + if (!$className) { + throw new MissingShellException([ + 'class' => $shell, + ]); + } + + return $this->_createShell($className, $shell); + } + +/** + * If the input matches an alias, return the aliased shell name + * + * @param string $shell Optionally the name of a plugin or alias + * @return string Shell name with plugin prefix + */ + protected function _handleAlias($shell) { + $aliased = static::alias($shell); + if ($aliased) { + $shell = $aliased; } - throw new MissingShellException([ - 'class' => $shell, - ]); + + $class = array_map('Cake\Utility\Inflector::camelize', explode('.', $shell)); + return implode('.', $class); } /** @@ -196,15 +248,27 @@ public function findShell($shell) { * @return string|bool Either the classname or false. */ protected function _shellExists($shell) { - $class = array_map('Cake\Utility\Inflector::camelize', explode('.', $shell)); - $class = implode('.', $class); - $class = App::className($class, 'Shell', 'Shell'); + $class = App::className($shell, 'Shell', 'Shell'); if (class_exists($class)) { return $class; } return false; } +/** + * Create the given shell name, and set the plugin property + * + * @param string $className The class name to instanciate + * @param string $shortName The plugin-prefixed shell name + * @return \Cake\Console\Shell A shell instance. + */ + protected function _createShell($className, $shortName) { + list($plugin) = pluginSplit($shortName); + $instance = new $className(); + $instance->plugin = trim($plugin, '.'); + return $instance; + } + /** * Removes first argument and shifts other arguments up * diff --git a/tests/TestCase/Console/ShellDispatcherTest.php b/tests/TestCase/Console/ShellDispatcherTest.php index aafaad199be..2c2250cdf5c 100644 --- a/tests/TestCase/Console/ShellDispatcherTest.php +++ b/tests/TestCase/Console/ShellDispatcherTest.php @@ -168,6 +168,60 @@ public function testDispatchShellWithoutMain() { $this->assertEquals(0, $result); } +/** + * Verify you can dispatch a plugin's main shell with the plugin name alone + * + * @return void + */ + public function testDispatchShortPluginAlias() { + $dispatcher = $this->getMock( + 'Cake\Console\ShellDispatcher', + ['_shellExists', '_createShell'] + ); + $Shell = $this->getMock('Cake\Console\Shell'); + + $dispatcher->expects($this->at(1)) + ->method('_shellExists') + ->with('TestPlugin.TestPlugin') + ->will($this->returnValue('TestPlugin\Console\Command\TestPluginShell')); + + $dispatcher->expects($this->at(2)) + ->method('_createShell') + ->with('TestPlugin\Console\Command\TestPluginShell', 'TestPlugin.TestPlugin') + ->will($this->returnValue($Shell)); + + $dispatcher->args = array('test_plugin'); + $result = $dispatcher->dispatch(); + $this->assertEquals(1, $result); + } + +/** + * Ensure short plugin shell usage is case/camelized insensitive + * + * @return void + */ + public function testDispatchShortPluginAliasCamelized() { + $dispatcher = $this->getMock( + 'Cake\Console\ShellDispatcher', + ['_shellExists', '_createShell'] + ); + $Shell = $this->getMock('Cake\Console\Shell'); + + $dispatcher->expects($this->at(1)) + ->method('_shellExists') + ->with('TestPlugin.TestPlugin') + ->will($this->returnValue('TestPlugin\Console\Command\TestPluginShell')); + + $dispatcher->expects($this->at(2)) + ->method('_createShell') + ->with('TestPlugin\Console\Command\TestPluginShell', 'TestPlugin.TestPlugin') + ->will($this->returnValue($Shell)); + + $dispatcher->args = ['TestPlugin']; + $result = $dispatcher->dispatch(); + $this->assertEquals(1, $result); + } + /** * Verify shifting of arguments *