BakeShell.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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 1.2.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Shell;
  16. use Cake\Cache\Cache;
  17. use Cake\Console\Shell;
  18. use Cake\Core\Configure;
  19. use Cake\Core\ConventionsTrait;
  20. use Cake\Core\Plugin;
  21. use Cake\Datasource\ConnectionManager;
  22. use Cake\Model\Model;
  23. use Cake\Utility\Inflector;
  24. /**
  25. * Command-line code generation utility to automate programmer chores.
  26. *
  27. * Bake is CakePHP's code generation script, which can help you kickstart
  28. * application development by writing fully functional skeleton controllers,
  29. * models, and views. Going further, Bake can also write Unit Tests for you.
  30. *
  31. * @link http://book.cakephp.org/3.0/en/console-and-shells/code-generation-with-bake.html
  32. */
  33. class BakeShell extends Shell {
  34. use ConventionsTrait;
  35. /**
  36. * The connection being used.
  37. *
  38. * @var string
  39. */
  40. public $connection = 'default';
  41. /**
  42. * Assign $this->connection to the active task if a connection param is set.
  43. *
  44. * @return void
  45. */
  46. public function startup() {
  47. parent::startup();
  48. Configure::write('debug', true);
  49. Cache::disable();
  50. $task = $this->_camelize($this->command);
  51. if (isset($this->{$task}) && !in_array($task, ['Project'])) {
  52. if (isset($this->params['connection'])) {
  53. $this->{$task}->connection = $this->params['connection'];
  54. }
  55. }
  56. if (isset($this->params['connection'])) {
  57. $this->connection = $this->params['connection'];
  58. }
  59. }
  60. /**
  61. * Override main() to handle action
  62. *
  63. * @return mixed
  64. */
  65. public function main() {
  66. $connections = ConnectionManager::configured();
  67. if (empty($connections)) {
  68. $this->out('Your database configuration was not found.');
  69. $this->out('Add your database connection information to config/app.php.');
  70. return false;
  71. }
  72. $this->out('The following commands can be used to generate skeleton code for your application.', 2);
  73. $this->out('<info>Available bake commands:</info>', 2);
  74. $this->out('- all');
  75. foreach ($this->tasks as $task) {
  76. list($p, $name) = pluginSplit($task);
  77. $this->out('- ' . Inflector::underscore($name));
  78. }
  79. $this->out('');
  80. $this->out('By using <info>`cake bake [name]`</info> you can invoke a specific bake task.');
  81. return false;
  82. }
  83. /**
  84. * Locate the tasks bake will use.
  85. *
  86. * Scans the following paths for tasks that are subclasses of
  87. * Cake\Shell\Task\BakeTask:
  88. *
  89. * - Cake/Shell/Task/
  90. * - App/Shell/Task/
  91. * - Shell/Task for each loaded plugin
  92. *
  93. * @return void
  94. */
  95. public function loadTasks() {
  96. $tasks = [];
  97. $tasks = $this->_findTasks($tasks, CAKE, 'Cake');
  98. $tasks = $this->_findTasks($tasks, APP, Configure::read('App.namespace'));
  99. foreach (Plugin::loaded() as $plugin) {
  100. $tasks = $this->_findTasks(
  101. $tasks,
  102. Plugin::classPath($plugin),
  103. $plugin,
  104. $plugin
  105. );
  106. }
  107. $this->tasks = array_values($tasks);
  108. parent::loadTasks();
  109. }
  110. /**
  111. * Append matching tasks in $path to the $tasks array.
  112. *
  113. * @param array $tasks The task list to modify and return.
  114. * @param string $path The base path to look in.
  115. * @param string $namespace The base namespace.
  116. * @param string $prefix The prefix to append.
  117. * @return array Updated tasks.
  118. */
  119. protected function _findTasks($tasks, $path, $namespace, $prefix = false) {
  120. $path .= 'Shell/Task';
  121. if (!is_dir($path)) {
  122. return $tasks;
  123. }
  124. $candidates = $this->_findClassFiles($path, $namespace);
  125. $classes = $this->_findTaskClasses($candidates);
  126. foreach ($classes as $class) {
  127. list($ns, $name) = namespaceSplit($class);
  128. $name = substr($name, 0, -4);
  129. $fullName = ($prefix ? $prefix . '.' : '') . $name;
  130. $tasks[$name] = $fullName;
  131. }
  132. return $tasks;
  133. }
  134. /**
  135. * Find task classes in a given path.
  136. *
  137. * @param string $path The path to scan.
  138. * @param string $namespace Namespace.
  139. * @return array An array of files that may contain bake tasks.
  140. */
  141. protected function _findClassFiles($path, $namespace) {
  142. $iterator = new \DirectoryIterator($path);
  143. $candidates = [];
  144. foreach ($iterator as $item) {
  145. if ($item->isDot() || $item->isDir()) {
  146. continue;
  147. }
  148. $name = $item->getBasename('.php');
  149. $candidates[] = $namespace . '\Shell\Task\\' . $name;
  150. }
  151. return $candidates;
  152. }
  153. /**
  154. * Find bake tasks in a given set of files.
  155. *
  156. * @param array $files The array of files.
  157. * @return array An array of matching classes.
  158. */
  159. protected function _findTaskClasses($files) {
  160. $classes = [];
  161. foreach ($files as $className) {
  162. if (!class_exists($className)) {
  163. continue;
  164. }
  165. $reflect = new \ReflectionClass($className);
  166. if (!$reflect->isInstantiable()) {
  167. continue;
  168. }
  169. if (!$reflect->isSubclassOf('Cake\Shell\Task\BakeTask')) {
  170. continue;
  171. }
  172. $classes[] = $className;
  173. }
  174. return $classes;
  175. }
  176. /**
  177. * Quickly bake the MVC
  178. *
  179. * @param string $name Name.
  180. * @return void
  181. */
  182. public function all($name = null) {
  183. $this->out('Bake All');
  184. $this->hr();
  185. if (!empty($this->params['connection'])) {
  186. $this->connection = $this->params['connection'];
  187. }
  188. if (empty($name)) {
  189. $this->Model->connection = $this->connection;
  190. $this->out('Possible model names based on your database:');
  191. foreach ($this->Model->listAll() as $table) {
  192. $this->out('- ' . $table);
  193. }
  194. $this->out('Run <info>`cake bake all [name]`</info> to generate skeleton files.');
  195. return false;
  196. }
  197. foreach (['Model', 'Controller', 'View'] as $task) {
  198. $this->{$task}->connection = $this->connection;
  199. }
  200. $name = $this->_camelize($name);
  201. $this->Model->bake($name);
  202. $this->Controller->bake($name);
  203. $this->View->main($name);
  204. $this->out('<success>Bake All complete.</success>', 1, Shell::QUIET);
  205. return true;
  206. }
  207. /**
  208. * Gets the option parser instance and configures it.
  209. *
  210. * @return \Cake\Console\ConsoleOptionParser
  211. */
  212. public function getOptionParser() {
  213. $parser = parent::getOptionParser();
  214. $parser->description(
  215. 'The Bake script generates controllers, views and models for your application.' .
  216. ' If run with no command line arguments, Bake guides the user through the class creation process.' .
  217. ' You can customize the generation process by telling Bake where different parts of your application are using command line arguments.'
  218. )->addSubcommand('all', [
  219. 'help' => 'Bake a complete MVC skeleton.',
  220. ])->addOption('connection', [
  221. 'help' => 'Database connection to use in conjunction with `bake all`.',
  222. 'short' => 'c',
  223. 'default' => 'default'
  224. ])->addOption('template', [
  225. 'short' => 't',
  226. 'help' => 'Theme to use when baking code.'
  227. ]);
  228. foreach ($this->_taskMap as $task => $config) {
  229. $taskParser = $this->{$task}->getOptionParser();
  230. $parser->addSubcommand(Inflector::underscore($task), [
  231. 'help' => $taskParser->description(),
  232. 'parser' => $taskParser
  233. ]);
  234. }
  235. return $parser;
  236. }
  237. }