PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/cake/console/libs/shell.php

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