PageRenderTime 35ms CodeModel.GetById 5ms RepoModel.GetById 1ms app.codeStats 0ms

/cake/console/libs/shell.php

https://github.com/msadouni/cakephp2x
PHP | 635 lines | 266 code | 64 blank | 305 comment | 60 complexity | d54c55fd77d77ddf1a7f4b7e2d1e1238 MD5 | raw file
  1. <?php
  2. /**
  3. * Base class for Shells
  4. *
  5. * Long description for file
  6. *
  7. * PHP Version 5.x
  8. *
  9. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  10. * Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. *
  12. * Licensed under The MIT License
  13. * Redistributions of files must retain the above copyright notice.
  14. *
  15. * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  16. * @link http://cakephp.org CakePHP(tm) Project
  17. * @package cake
  18. * @subpackage cake.cake.console.libs
  19. * @since CakePHP(tm) v 1.2.0.5012
  20. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  21. */
  22. /**
  23. * Base class for command-line utilities for automating programmer chores.
  24. *
  25. * @package cake
  26. * @subpackage cake.cake.console.libs
  27. */
  28. class Shell extends Object {
  29. /**
  30. * An instance of the ShellDispatcher object that loaded this script
  31. *
  32. * @var ShellDispatcher
  33. * @access public
  34. */
  35. private $Dispatch = null;
  36. /**
  37. * If true, the script will ask for permission to perform actions.
  38. *
  39. * @var boolean
  40. * @access public
  41. */
  42. public $interactive = true;
  43. /**
  44. * Holds the DATABASE_CONFIG object for the app. Null if database.php could not be found,
  45. * or the app does not exist.
  46. *
  47. * @var DATABASE_CONFIG
  48. * @access public
  49. */
  50. private $DbConfig = null;
  51. /**
  52. * Contains command switches parsed from the command line.
  53. *
  54. * @var array
  55. * @access public
  56. */
  57. private $params = array();
  58. /**
  59. * Contains arguments parsed from the command line.
  60. *
  61. * @var array
  62. * @access public
  63. */
  64. private $args = array();
  65. /**
  66. * The file name of the shell that was invoked.
  67. *
  68. * @var string
  69. * @access public
  70. */
  71. private $shell = null;
  72. /**
  73. * The class name of the shell that was invoked.
  74. *
  75. * @var string
  76. * @access public
  77. */
  78. private $className = null;
  79. /**
  80. * The command called if public methods are available.
  81. *
  82. * @var string
  83. * @access public
  84. */
  85. private $command = null;
  86. /**
  87. * The name of the shell in camelized.
  88. *
  89. * @var string
  90. * @access public
  91. */
  92. public $name = null;
  93. /**
  94. * An alias for the shell
  95. *
  96. * @var string
  97. * @access public
  98. */
  99. private $alias = null;
  100. /**
  101. * Contains tasks to load and instantiate
  102. *
  103. * @var array
  104. * @access public
  105. */
  106. private $tasks = array();
  107. /**
  108. * Contains the loaded tasks
  109. *
  110. * @var array
  111. * @access protected
  112. */
  113. protected $_taskNames = array();
  114. /**
  115. * Contains models to load and instantiate
  116. *
  117. * @var array
  118. * @access public
  119. */
  120. private $uses = array();
  121. /**
  122. * Constructs this Shell instance.
  123. *
  124. */
  125. public function __construct(&$dispatch) {
  126. $vars = array('params', 'args', 'shell', 'shellCommand' => 'command');
  127. foreach ($vars as $key => $var) {
  128. if (is_string($key)) {
  129. $this->{$var} = $dispatch->{$key};
  130. } else {
  131. $this->{$var} = $dispatch->{$var};
  132. }
  133. }
  134. if ($this->name == null) {
  135. $this->name = get_class($this);
  136. }
  137. if ($this->alias == null) {
  138. $this->alias = $this->name;
  139. }
  140. ClassRegistry::addObject($this->name, $this);
  141. ClassRegistry::map($this->name, $this->alias);
  142. $this->Dispatch = $dispatch;
  143. }
  144. /**
  145. * Initializes the Shell
  146. * acts as constructor for subclasses
  147. * allows configuration of tasks prior to shell execution
  148. *
  149. * @access public
  150. */
  151. public function initialize() {
  152. $this->_loadModels();
  153. }
  154. /**
  155. * Starts up the the Shell
  156. * allows for checking and configuring prior to command or main execution
  157. * can be overriden in subclasses
  158. *
  159. * @access public
  160. */
  161. public function startup() {
  162. $this->_welcome();
  163. }
  164. /**
  165. * Displays a header for the shell
  166. *
  167. * @access protected
  168. */
  169. protected function _welcome() {
  170. $this->Dispatch->clear();
  171. $this->out();
  172. $this->out('Welcome to CakePHP v' . Configure::version() . ' Console');
  173. $this->hr();
  174. $this->out('App : '. $this->params['app']);
  175. $this->out('Path: '. $this->params['working']);
  176. $this->hr();
  177. }
  178. /**
  179. * Loads database file and constructs DATABASE_CONFIG class
  180. * makes $this->DbConfig available to subclasses
  181. *
  182. * @return bool
  183. * @access protected
  184. */
  185. protected function _loadDbConfig() {
  186. if (config('database') && class_exists('DATABASE_CONFIG')) {
  187. $this->DbConfig = new DATABASE_CONFIG();
  188. return true;
  189. }
  190. $this->err('Database config could not be loaded.');
  191. $this->out('Run `bake` to create the database configuration.');
  192. return false;
  193. }
  194. /**
  195. * if var $uses = true
  196. * Loads AppModel file and constructs AppModel class
  197. * makes $this->AppModel available to subclasses
  198. * if var $uses is an array of models will load those models
  199. *
  200. * @return bool
  201. * @access protected
  202. */
  203. protected function _loadModels() {
  204. if ($this->uses === null || $this->uses === false) {
  205. return;
  206. }
  207. if ($this->uses === true && App::import('Model', 'AppModel')) {
  208. $this->AppModel = new AppModel(false, false, false);
  209. return true;
  210. }
  211. if ($this->uses !== true && !empty($this->uses)) {
  212. $uses = is_array($this->uses) ? $this->uses : array($this->uses);
  213. $modelClassName = $uses[0];
  214. if (strpos($uses[0], '.') !== false) {
  215. list($plugin, $modelClassName) = explode('.', $uses[0]);
  216. }
  217. $this->modelClass = $modelClassName;
  218. foreach ($uses as $modelClass) {
  219. list($plugin, $modelClass) = pluginSplit($modelClass, true);
  220. $this->{$modelClass} = ClassRegistry::init($plugin . $modelClass);
  221. }
  222. return true;
  223. }
  224. return false;
  225. }
  226. /**
  227. * Loads tasks defined in var $tasks
  228. *
  229. * @return bool
  230. * @access public
  231. */
  232. public function loadTasks() {
  233. if ($this->tasks === null || $this->tasks === false || $this->tasks === true || empty($this->tasks)) {
  234. return true;
  235. }
  236. $tasks = $this->tasks;
  237. if (!is_array($tasks)) {
  238. $tasks = array($tasks);
  239. }
  240. foreach ($tasks as $taskName) {
  241. $task = Inflector::underscore($taskName);
  242. $taskClass = Inflector::camelize($taskName . 'Task');
  243. if (!class_exists($taskClass)) {
  244. foreach ($this->Dispatch->shellPaths as $path) {
  245. $taskPath = $path . 'tasks' . DS . $task . '.php';
  246. if (file_exists($taskPath)) {
  247. require_once $taskPath;
  248. break;
  249. }
  250. }
  251. }
  252. if (ClassRegistry::isKeySet($taskClass)) {
  253. $this->_taskNames[] = $taskName;
  254. $this->{$taskName} = ClassRegistry::getObject($taskClass);
  255. } else {
  256. $this->_taskNames[] = $taskName;
  257. $this->{$taskName} = new $taskClass($this->Dispatch);
  258. }
  259. if (!isset($this->{$taskName})) {
  260. $this->err("Task `{$taskName}` could not be loaded");
  261. $this->_stop();
  262. }
  263. }
  264. return true;
  265. }
  266. /**
  267. * Return loaded task names
  268. *
  269. * @return array
  270. * @access public
  271. */
  272. public function tasks() {
  273. return $this->_taskNames;
  274. }
  275. /**
  276. * Prompts the user for input, and returns it.
  277. *
  278. * @param string $prompt Prompt text.
  279. * @param mixed $options Array or string of options.
  280. * @param string $default Default input value.
  281. * @return Either the default value, or the user-provided input.
  282. * @access public
  283. */
  284. public function in($prompt, $options = null, $default = null) {
  285. if (!$this->interactive) {
  286. return $default;
  287. }
  288. $in = $this->Dispatch->getInput($prompt, $options, $default);
  289. if ($options && is_string($options)) {
  290. if (strpos($options, ',')) {
  291. $options = explode(',', $options);
  292. } elseif (strpos($options, '/')) {
  293. $options = explode('/', $options);
  294. } else {
  295. $options = array($options);
  296. }
  297. }
  298. if (is_array($options)) {
  299. while ($in == '' || ($in && (!in_array(strtolower($in), $options) && !in_array(strtoupper($in), $options)) && !in_array($in, $options))) {
  300. $in = $this->Dispatch->getInput($prompt, $options, $default);
  301. }
  302. }
  303. if ($in) {
  304. return $in;
  305. }
  306. }
  307. /**
  308. * Outputs a single or multiple messages to stdout. If no parameters
  309. * are passed outputs just a newline.
  310. *
  311. * @param mixed $message A string or a an array of strings to output
  312. * @param integer $newlines Number of newlines to append
  313. * @return integer Returns the number of bytes returned from writing to stdout.
  314. * @access public
  315. */
  316. public function out($message = null, $newlines = 1) {
  317. if (is_array($message)) {
  318. $message = implode($this->nl(), $message);
  319. }
  320. return $this->Dispatch->stdout($message . $this->nl($newlines), false);
  321. }
  322. /**
  323. * Outputs a single or multiple error messages to stderr. If no parameters
  324. * are passed outputs just a newline.
  325. *
  326. * @param mixed $message A string or a an array of strings to output
  327. * @param integer $newlines Number of newlines to append
  328. * @access public
  329. */
  330. public function err($message = null, $newlines = 1) {
  331. if (is_array($message)) {
  332. $message = implode($this->nl(), $message);
  333. }
  334. $this->Dispatch->stderr($message . $this->nl($newlines));
  335. }
  336. /**
  337. * Returns a single or multiple linefeeds sequences.
  338. *
  339. * @param integer $multiplier Number of times the linefeed sequence should be repeated
  340. * @access public
  341. * @return string
  342. */
  343. public function nl($multiplier = 1) {
  344. return str_repeat("\n", $multiplier);
  345. }
  346. /**
  347. * Outputs a series of minus characters to the standard output, acts as a visual separator.
  348. *
  349. * @param integer $newlines Number of newlines to pre- and append
  350. * @access public
  351. */
  352. public function hr($newlines = 0) {
  353. $this->out(null, $newlines);
  354. $this->out('---------------------------------------------------------------');
  355. $this->out(null, $newlines);
  356. }
  357. /**
  358. * Displays a formatted error message
  359. * and exits the application with status code 1
  360. *
  361. * @param string $title Title of the error
  362. * @param string $message An optional error message
  363. * @access public
  364. */
  365. public function error($title, $message = null) {
  366. $this->err(sprintf(__('Error: %s', true), $title));
  367. if (!empty($message)) {
  368. $this->err($message);
  369. }
  370. $this->_stop(1);
  371. }
  372. /**
  373. * Will check the number args matches otherwise throw an error
  374. *
  375. * @param integer $expectedNum Expected number of paramters
  376. * @param string $command Command
  377. * @access protected
  378. */
  379. protected function _checkArgs($expectedNum, $command = null) {
  380. if (!$command) {
  381. $command = $this->command;
  382. }
  383. if (count($this->args) < $expectedNum) {
  384. $message[] = "Got: " . count($this->args);
  385. $message[] = "Expected: {$expectedNum}";
  386. $message[] = "Please type `cake {$this->shell} help` for help";
  387. $message[] = "on usage of the {$this->name} {$command}.";
  388. $this->error('Wrong number of parameters', $message);
  389. }
  390. }
  391. /**
  392. * Creates a file at given path
  393. *
  394. * @param string $path Where to put the file.
  395. * @param string $contents Content to put in the file.
  396. * @return boolean Success
  397. * @access public
  398. */
  399. public function createFile($path, $contents) {
  400. $path = str_replace(DS . DS, DS, $path);
  401. $this->out();
  402. $this->out(sprintf(__("Creating file %s", true), $path));
  403. if (is_file($path) && $this->interactive === true) {
  404. $prompt = sprintf(__('File `%s` exists, overwrite?', true), $path);
  405. $key = $this->in($prompt, array('y', 'n', 'q'), 'n');
  406. if (strtolower($key) == 'q') {
  407. $this->out(__('Quitting.', true), 2);
  408. $this->_stop();
  409. } elseif (strtolower($key) != 'y') {
  410. $this->out(sprintf(__('Skip `%s`', true), $path), 2);
  411. return false;
  412. }
  413. }
  414. if (!class_exists('File')) {
  415. require LIBS . 'file.php';
  416. }
  417. if ($File = new File($path, true)) {
  418. $data = $File->prepare($contents);
  419. $File->write($data);
  420. $this->out(sprintf(__('Wrote `%s`', true), $path));
  421. return true;
  422. } else {
  423. $this->err(sprintf(__('Could not write to `%s`.', true), $path), 2);
  424. return false;
  425. }
  426. }
  427. /**
  428. * Outputs usage text on the standard output. Implement it in subclasses.
  429. *
  430. * @access public
  431. */
  432. public function help() {
  433. if ($this->command != null) {
  434. $this->err("Unknown {$this->name} command `{$this->command}`.");
  435. $this->err("For usage, try `cake {$this->shell} help`.", 2);
  436. } else {
  437. $this->Dispatch->help();
  438. }
  439. }
  440. /**
  441. * Action to create a Unit Test
  442. *
  443. * @return boolean Success
  444. * @access protected
  445. */
  446. public function _checkUnitTest() {
  447. if (App::import('vendor', 'simpletest' . DS . 'simpletest')) {
  448. return true;
  449. }
  450. $prompt = 'SimpleTest is not installed. Do you want to bake unit test files anyway?';
  451. $unitTest = $this->in($prompt, array('y','n'), 'y');
  452. $result = strtolower($unitTest) == 'y' || strtolower($unitTest) == 'yes';
  453. if ($result) {
  454. $this->out();
  455. $this->out('You can download SimpleTest from http://simpletest.org');
  456. }
  457. return $result;
  458. }
  459. /**
  460. * Makes absolute file path easier to read
  461. *
  462. * @param string $file Absolute file path
  463. * @return sting short path
  464. * @access public
  465. */
  466. public function shortPath($file) {
  467. $shortPath = str_replace(ROOT, null, $file);
  468. $shortPath = str_replace('..' . DS, '', $shortPath);
  469. return str_replace(DS . DS, DS, $shortPath);
  470. }
  471. /**
  472. * Creates the proper controller path for the specified controller class name
  473. *
  474. * @param string $name Controller class name
  475. * @return string Path to controller
  476. * @access protected
  477. */
  478. protected function _controllerPath($name) {
  479. return strtolower(Inflector::underscore($name));
  480. }
  481. /**
  482. * Creates the proper controller plural name for the specified controller class name
  483. *
  484. * @param string $name Controller class name
  485. * @return string Controller plural name
  486. * @access protected
  487. */
  488. protected function _controllerName($name) {
  489. return Inflector::pluralize(Inflector::camelize($name));
  490. }
  491. /**
  492. * Creates the proper controller camelized name (singularized) for the specified name
  493. *
  494. * @param string $name Name
  495. * @return string Camelized and singularized controller name
  496. * @access protected
  497. */
  498. protected function _modelName($name) {
  499. return Inflector::camelize(Inflector::singularize($name));
  500. }
  501. /**
  502. * Creates the proper singular model key for associations
  503. *
  504. * @param string $name Controller class name
  505. * @return string Singular model key
  506. * @access protected
  507. */
  508. protected function _modelKey($name) {
  509. return Inflector::underscore(Inflector::singularize($name)) . '_id';
  510. }
  511. /**
  512. * Creates the proper model name from a foreign key
  513. *
  514. * @param string $key Foreign key
  515. * @return string Model name
  516. * @access protected
  517. */
  518. protected function _modelNameFromKey($key) {
  519. return Inflector::camelize(str_replace('_id', '', $key));
  520. }
  521. /**
  522. * creates the singular name for use in views.
  523. *
  524. * @param string $name
  525. * @return string $name
  526. * @access protected
  527. */
  528. protected function _singularName($name) {
  529. return Inflector::variable(Inflector::singularize($name));
  530. }
  531. /**
  532. * Creates the plural name for views
  533. *
  534. * @param string $name Name to use
  535. * @return string Plural name for views
  536. * @access protected
  537. */
  538. protected function _pluralName($name) {
  539. return Inflector::variable(Inflector::pluralize($name));
  540. }
  541. /**
  542. * Creates the singular human name used in views
  543. *
  544. * @param string $name Controller name
  545. * @return string Singular human name
  546. * @access protected
  547. */
  548. protected function _singularHumanName($name) {
  549. return Inflector::humanize(Inflector::underscore(Inflector::singularize($name)));
  550. }
  551. /**
  552. * Creates the plural human name used in views
  553. *
  554. * @param string $name Controller name
  555. * @return string Plural human name
  556. * @access protected
  557. */
  558. protected function _pluralHumanName($name) {
  559. return Inflector::humanize(Inflector::underscore(Inflector::pluralize($name)));
  560. }
  561. /**
  562. * Find the correct path for a plugin. Scans $pluginPaths for the plugin you want.
  563. *
  564. * @param string $pluginName Name of the plugin you want ie. DebugKit
  565. * @return string $path path to the correct plugin.
  566. */
  567. protected function _pluginPath($pluginName) {
  568. return App::pluginPath($pluginName);
  569. }
  570. }
  571. ?>