CommandTask.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 2.5.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Console\Command\Task;
  16. use Cake\Console\ConsoleOptionParser;
  17. use Cake\Console\Shell;
  18. use Cake\Core\App;
  19. use Cake\Core\Plugin;
  20. use Cake\Utility\Inflector;
  21. use Cake\Utility\Folder;
  22. use \ReflectionClass;
  23. use \ReflectionMethod;
  24. /**
  25. * Base class for Shell Command reflection.
  26. *
  27. */
  28. class CommandTask extends Shell {
  29. /**
  30. * Gets the shell command listing.
  31. *
  32. * @return array
  33. */
  34. public function getShellList() {
  35. $skipFiles = ['AppShell'];
  36. $hiddenCommands = ['CommandListShell', 'CompletionShell'];
  37. $plugins = Plugin::loaded();
  38. $shellList = array_fill_keys($plugins, null) + ['CORE' => null, 'app' => null];
  39. $corePath = App::core('Console/Command');
  40. $shells = $this->_scanDir($corePath[0]);
  41. $shells = array_diff($shells, $skipFiles, $hiddenCommands);
  42. $shellList = $this->_appendShells('CORE', $shells, $shellList);
  43. $appPath = App::path('Console/Command');
  44. $appShells = $this->_scanDir($appPath[0]);
  45. $appShells = array_diff($appShells, $shells, $skipFiles);
  46. $shellList = $this->_appendShells('app', $appShells, $shellList);
  47. foreach ($plugins as $plugin) {
  48. $pluginPath = Plugin::classPath($plugin) . 'Console' . DS . 'Command';
  49. $pluginShells = $this->_scanDir($pluginPath);
  50. $shellList = $this->_appendShells($plugin, $pluginShells, $shellList);
  51. }
  52. return array_filter($shellList);
  53. }
  54. /**
  55. * Scan the provided paths for shells, and append them into $shellList
  56. *
  57. * @param string $type The type of object.
  58. * @param array $shells The shell name.
  59. * @param array $shellList List of shells.
  60. * @return array The updated $shellList
  61. */
  62. protected function _appendShells($type, $shells, $shellList) {
  63. foreach ($shells as $shell) {
  64. $shellList[$type][] = Inflector::underscore(str_replace('Shell', '', $shell));
  65. }
  66. return $shellList;
  67. }
  68. /**
  69. * Scan a directory for .php files and return the class names that
  70. * should be within them.
  71. *
  72. * @param string $dir The directory to read.
  73. * @return array The list of shell classnames based on conventions.
  74. */
  75. protected function _scanDir($dir) {
  76. $dir = new Folder($dir);
  77. $contents = $dir->read(true, true);
  78. if (empty($contents[1])) {
  79. return [];
  80. }
  81. $shells = [];
  82. foreach ($contents[1] as $file) {
  83. if (substr($file, -4) !== '.php') {
  84. continue;
  85. }
  86. $shells[] = substr($file, 0, -4);
  87. }
  88. return $shells;
  89. }
  90. /**
  91. * Return a list of all commands
  92. *
  93. * @return array
  94. */
  95. public function commands() {
  96. $shellList = $this->getShellList();
  97. $options = [];
  98. foreach ($shellList as $type => $commands) {
  99. $prefix = '';
  100. if (!in_array(strtolower($type), ['app', 'core'])) {
  101. $prefix = $type . '.';
  102. }
  103. foreach ($commands as $shell) {
  104. $options[] = $prefix . $shell;
  105. }
  106. }
  107. return $options;
  108. }
  109. /**
  110. * Return a list of subcommands for a given command
  111. *
  112. * @param string $commandName The command you want subcommands from.
  113. * @return array
  114. */
  115. public function subCommands($commandName) {
  116. $Shell = $this->getShell($commandName);
  117. if (!$Shell) {
  118. return [];
  119. }
  120. $taskMap = $this->Tasks->normalizeArray((array)$Shell->tasks);
  121. $return = array_keys($taskMap);
  122. $return = array_map('Cake\Utility\Inflector::underscore', $return);
  123. $shellMethodNames = ['main', 'help'];
  124. $baseClasses = ['Object', 'Shell', 'AppShell'];
  125. $Reflection = new ReflectionClass($Shell);
  126. $methods = $Reflection->getMethods(ReflectionMethod::IS_PUBLIC);
  127. $methodNames = [];
  128. foreach ($methods as $method) {
  129. $declaringClass = $method->getDeclaringClass()->getShortName();
  130. if (!in_array($declaringClass, $baseClasses)) {
  131. $methodNames[] = $method->getName();
  132. }
  133. }
  134. $return += array_diff($methodNames, $shellMethodNames);
  135. sort($return);
  136. return $return;
  137. }
  138. /**
  139. * Get Shell instance for the given command
  140. *
  141. * @param mixed $commandName The command you want.
  142. * @return mixed
  143. */
  144. public function getShell($commandName) {
  145. list($pluginDot, $name) = pluginSplit($commandName, true);
  146. if (in_array(strtolower($pluginDot), ['app.', 'core.'])) {
  147. $commandName = $name;
  148. $pluginDot = '';
  149. }
  150. if (!in_array($commandName, $this->commands())) {
  151. return false;
  152. }
  153. $name = Inflector::camelize($name);
  154. $pluginDot = Inflector::camelize($pluginDot);
  155. $class = App::className($pluginDot . $name, 'Console/Command', 'Shell');
  156. if (!$class) {
  157. return false;
  158. }
  159. $Shell = new $class();
  160. $Shell->plugin = trim($pluginDot, '.');
  161. $Shell->initialize();
  162. return $Shell;
  163. }
  164. /**
  165. * Get Shell instance for the given command
  166. *
  167. * @param mixed $commandName The command to get options for.
  168. * @return array
  169. */
  170. public function options($commandName) {
  171. $Shell = $this->getShell($commandName);
  172. if (!$Shell) {
  173. $parser = new ConsoleOptionParser();
  174. } else {
  175. $parser = $Shell->getOptionParser();
  176. }
  177. $options = [];
  178. $array = $parser->options();
  179. foreach ($array as $name => $obj) {
  180. $options[] = "--$name";
  181. $short = $obj->short();
  182. if ($short) {
  183. $options[] = "-$short";
  184. }
  185. }
  186. return $options;
  187. }
  188. }