Create.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2012, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\console\command;
  9. use lithium\util\String;
  10. use lithium\core\Libraries;
  11. use lithium\util\Inflector;
  12. use lithium\core\ClassNotFoundException;
  13. /**
  14. * The `create` command allows you to rapidly develop your models, views, controllers, and tests
  15. * by generating the minimum code necessary to test and run your application.
  16. *
  17. * `li3 create --template=controller Posts`
  18. * `li3 create --template=model Posts`
  19. *
  20. */
  21. class Create extends \lithium\console\Command {
  22. /**
  23. * Name of library to use
  24. *
  25. * @var string
  26. */
  27. public $library = null;
  28. /**
  29. * The name of the template to use to generate the file. This allows you to add a custom
  30. * template to be used in place of the core template for each command. Place templates in
  31. * `<library>\extensions\command\create\template`.
  32. *
  33. * @var string
  34. */
  35. public $template = null;
  36. /**
  37. * Holds library data from `lithium\core\Libraries::get()`.
  38. *
  39. * @var array
  40. */
  41. protected $_library = array();
  42. /**
  43. * Class initializer. Parses template and sets up params that need to be filled.
  44. *
  45. * @return void
  46. */
  47. protected function _init() {
  48. parent::_init();
  49. $this->library = $this->library ?: true;
  50. $defaults = array('prefix' => null, 'path' => null);
  51. $this->_library = (array) Libraries::get($this->library) + $defaults;
  52. }
  53. /**
  54. * Run the create command. Takes `$command` and delegates to `$command::$method`
  55. *
  56. * @param string $command
  57. * @return boolean
  58. */
  59. public function run($command = null) {
  60. if ($command && !$this->request->args()) {
  61. return $this->_default($command);
  62. }
  63. $this->request->shift();
  64. $this->template = $this->template ?: $command;
  65. if (!$command) {
  66. return false;
  67. }
  68. if ($this->_execute($command)) {
  69. return true;
  70. }
  71. $this->error("{$command} could not be created.");
  72. return false;
  73. }
  74. /**
  75. * Execute the given sub-command for the current request.
  76. *
  77. * @param string $command The sub-command name. example: Model, Controller, Test
  78. * @return boolean
  79. */
  80. protected function _execute($command) {
  81. try {
  82. if (!$class = $this->_instance($command)) {
  83. return false;
  84. }
  85. } catch (ClassNotFoundException $e) {
  86. return false;
  87. }
  88. $data = array();
  89. $params = $class->invokeMethod('_params');
  90. foreach ($params as $i => $param) {
  91. $data[$param] = $class->invokeMethod("_{$param}", array($this->request));
  92. }
  93. if ($message = $class->invokeMethod('_save', array($data))) {
  94. $this->out($message);
  95. return true;
  96. }
  97. return false;
  98. }
  99. /**
  100. * Run through the default set. model, controller, test model, test controller
  101. *
  102. * @param string $name class name to create
  103. * @return boolean
  104. */
  105. protected function _default($name) {
  106. $commands = array(
  107. array('model', Inflector::pluralize($name)),
  108. array('controller', Inflector::pluralize($name)),
  109. array('test', 'model', Inflector::pluralize($name)),
  110. array('test', 'controller', Inflector::pluralize($name))
  111. );
  112. foreach ($commands as $args) {
  113. $command = $this->template = $this->request->params['command'] = array_shift($args);
  114. $this->request->params['action'] = array_shift($args);
  115. $this->request->params['args'] = $args;
  116. if (!$this->_execute($command)) {
  117. return false;
  118. }
  119. }
  120. return true;
  121. }
  122. /**
  123. * Get the namespace.
  124. *
  125. * @param string $request
  126. * @param array $options
  127. * @return string
  128. */
  129. protected function _namespace($request, $options = array()) {
  130. $name = $request->command;
  131. $defaults = array(
  132. 'prefix' => $this->_library['prefix'],
  133. 'prepend' => null,
  134. 'spaces' => array(
  135. 'model' => 'models', 'view' => 'views', 'controller' => 'controllers',
  136. 'command' => 'extensions.command', 'adapter' => 'extensions.adapter',
  137. 'helper' => 'extensions.helper'
  138. )
  139. );
  140. $options += $defaults;
  141. if (isset($options['spaces'][$name])) {
  142. $name = $options['spaces'][$name];
  143. }
  144. return str_replace('.', '\\', $options['prefix'] . $options['prepend'] . $name);
  145. }
  146. /**
  147. * Parse a template to find available variables specified in `{:name}` format. Each variable
  148. * corresponds to a method in the sub command. For example, a `{:namespace}` variable will
  149. * call the namespace method in the model command when `li3 create model Post` is called.
  150. *
  151. * @return array
  152. */
  153. protected function _params() {
  154. $contents = $this->_template();
  155. if (empty($contents)) {
  156. return array();
  157. }
  158. preg_match_all('/(?:\{:(?P<params>[^}]+)\})/', $contents, $keys);
  159. if (!empty($keys['params'])) {
  160. return array_values(array_unique($keys['params']));
  161. }
  162. return array();
  163. }
  164. /**
  165. * Returns the contents of the template.
  166. *
  167. * @return string
  168. */
  169. protected function _template() {
  170. $file = Libraries::locate('command.create.template', $this->template, array(
  171. 'filter' => false, 'type' => 'file', 'suffix' => '.txt.php'
  172. ));
  173. if (!$file || is_array($file)) {
  174. return false;
  175. }
  176. return file_get_contents($file);
  177. }
  178. /**
  179. * Get an instance of a sub-command
  180. *
  181. * @param string $name the name of the sub-command to instantiate
  182. * @param array $config
  183. * @return object;
  184. */
  185. protected function _instance($name, array $config = array()) {
  186. if ($class = Libraries::locate('command.create', Inflector::camelize($name))) {
  187. $this->request->params['template'] = $this->template;
  188. return new $class(array(
  189. 'request' => $this->request,
  190. 'classes' => $this->_classes
  191. ));
  192. }
  193. return parent::_instance($name, $config);
  194. }
  195. /**
  196. * Save a template with the current params. Writes file to `Create::$path`.
  197. *
  198. * @param array $params
  199. * @return string A result string on success of writing the file. If any errors occur along
  200. * the way such as missing information boolean false is returned.
  201. */
  202. protected function _save(array $params = array()) {
  203. $defaults = array('namespace' => null, 'class' => null);
  204. $params += $defaults;
  205. if (empty($params['class']) || empty($this->_library['path'])) {
  206. return false;
  207. }
  208. $contents = $this->_template();
  209. $result = String::insert($contents, $params);
  210. $namespace = str_replace($this->_library['prefix'], '\\', $params['namespace']);
  211. $path = str_replace('\\', '/', "{$namespace}\\{$params['class']}");
  212. $path = $this->_library['path'] . stristr($path, '/');
  213. $file = str_replace('//', '/', "{$path}.php");
  214. $directory = dirname($file);
  215. $relative = str_replace($this->_library['path'] . '/', "", $file);
  216. if ((!is_dir($directory)) && !mkdir($directory, 0755, true)) {
  217. return false;
  218. }
  219. if (file_exists($file)) {
  220. $prompt = "{$relative} already exists. Overwrite?";
  221. $choices = array('y', 'n');
  222. if ($this->in($prompt, compact('choices')) !== 'y') {
  223. return "{$params['class']} skipped.";
  224. }
  225. }
  226. if (file_put_contents($file, "<?php\n\n{$result}\n\n?>")) {
  227. return "{$params['class']} created in {$relative}.";
  228. }
  229. return false;
  230. }
  231. }
  232. ?>