CommandCollection.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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 3.5.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Console;
  16. use ArrayIterator;
  17. use Cake\Console\CommandScanner;
  18. use Cake\Console\Shell;
  19. use Countable;
  20. use InvalidArgumentException;
  21. use IteratorAggregate;
  22. /**
  23. * Collection for Commands.
  24. *
  25. * Used by Applications to whitelist their console commands.
  26. * CakePHP will use the mapped commands to construct and dispatch
  27. * shell commands.
  28. */
  29. class CommandCollection implements IteratorAggregate, Countable
  30. {
  31. /**
  32. * Command list
  33. *
  34. * @var array
  35. */
  36. protected $commands = [];
  37. /**
  38. * Constructor
  39. *
  40. * @param array $commands The map of commands to add to the collection.
  41. */
  42. public function __construct(array $commands = [])
  43. {
  44. foreach ($commands as $name => $command) {
  45. $this->add($name, $command);
  46. }
  47. }
  48. /**
  49. * Add a command to the collection
  50. *
  51. * @param string $name The name of the command you want to map.
  52. * @param string|\Cake\Console\Shell $command The command to map.
  53. * @return $this
  54. */
  55. public function add($name, $command)
  56. {
  57. // Once we have a new Command class this should check
  58. // against that interface.
  59. if (!is_subclass_of($command, Shell::class)) {
  60. $class = is_string($command) ? $command : get_class($command);
  61. throw new InvalidArgumentException(
  62. "Cannot use '$class' for command '$name' it is not a subclass of Cake\Console\Shell."
  63. );
  64. }
  65. $this->commands[$name] = $command;
  66. return $this;
  67. }
  68. /**
  69. * Add multiple commands at once.
  70. *
  71. * @param array $commands A map of command names => command classes/instances.
  72. * @return $this
  73. * @see \Cake\Console\CommandCollection::add()
  74. */
  75. public function addMany(array $commands)
  76. {
  77. foreach ($commands as $name => $class) {
  78. $this->add($name, $class);
  79. }
  80. return $this;
  81. }
  82. /**
  83. * Remove a command from the collection if it exists.
  84. *
  85. * @param string $name The named shell.
  86. * @return $this
  87. */
  88. public function remove($name)
  89. {
  90. unset($this->commands[$name]);
  91. return $this;
  92. }
  93. /**
  94. * Check whether the named shell exists in the collection.
  95. *
  96. * @param string $name The named shell.
  97. * @return bool
  98. */
  99. public function has($name)
  100. {
  101. return isset($this->commands[$name]);
  102. }
  103. /**
  104. * Get the target for a command.
  105. *
  106. * @param string $name The named shell.
  107. * @return string|\Cake\Console\Shell Either the shell class or an instance.
  108. * @throws \InvalidArgumentException when unknown commands are fetched.
  109. */
  110. public function get($name)
  111. {
  112. if (!$this->has($name)) {
  113. throw new InvalidArgumentException("The $name is not a known command name.");
  114. }
  115. return $this->commands[$name];
  116. }
  117. /**
  118. * Implementation of IteratorAggregate.
  119. *
  120. * @return \ArrayIterator
  121. */
  122. public function getIterator()
  123. {
  124. return new ArrayIterator($this->commands);
  125. }
  126. /**
  127. * Implementation of Countable.
  128. *
  129. * Get the number of commands in the collection.
  130. *
  131. * @return int
  132. */
  133. public function count()
  134. {
  135. return count($this->commands);
  136. }
  137. /**
  138. * Automatically discover shell commands in CakePHP, the application and all plugins.
  139. *
  140. * Commands will be located using filesystem conventions. Commands are
  141. * discovered in the following order:
  142. *
  143. * - CakePHP provided commands
  144. * - Application commands
  145. * - Plugin commands
  146. *
  147. * Commands from plugins will be added based on the order plugins are loaded.
  148. * Plugin shells will attempt to use a short name. If however, a plugin
  149. * provides a shell that conflicts with CakePHP or the application shells,
  150. * the full `plugin_name.shell` name will be used. Plugin shells are added
  151. * in the order that plugins were loaded.
  152. *
  153. * @return array An array of command names and their classes.
  154. */
  155. public function autoDiscover()
  156. {
  157. $scanner = new CommandScanner();
  158. $shells = $scanner->scanAll();
  159. $adder = function ($out, $shells, $key) {
  160. if (empty($shells[$key])) {
  161. return $out;
  162. }
  163. foreach ($shells[$key] as $info) {
  164. $name = $info['name'];
  165. $addLong = $name !== $info['fullName'];
  166. // If the short name has been used, use the full name.
  167. // This allows app shells to have name preference.
  168. // and app shells to overwrite core shells.
  169. if (isset($out[$name]) && $addLong) {
  170. $name = $info['fullName'];
  171. }
  172. $out[$name] = $info['class'];
  173. if ($addLong) {
  174. $out[$info['fullName']] = $info['class'];
  175. }
  176. }
  177. return $out;
  178. };
  179. $out = $adder([], $shells, 'CORE');
  180. $out = $adder($out, $shells, 'app');
  181. foreach (array_keys($shells['plugins']) as $key) {
  182. $out = $adder($out, $shells['plugins'], $key);
  183. }
  184. return $out;
  185. }
  186. }