/cake/console/cake.php

https://github.com/hardsshah/bookmarks · PHP · 587 lines · 358 code · 42 blank · 187 comment · 87 complexity · 120f98cd5c66e762cd1d9fed5e0e99c0 MD5 · raw file

  1. #!/usr/bin/php -q
  2. <?php
  3. /* SVN FILE: $Id$ */
  4. /**
  5. * Command-line code generation utility to automate programmer chores.
  6. *
  7. * Shell dispatcher class
  8. *
  9. * PHP versions 4 and 5
  10. *
  11. * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
  12. * Copyright 2005-2008, Cake Software Foundation, Inc.
  13. *
  14. * Licensed under The MIT License
  15. * Redistributions of files must retain the above copyright notice.
  16. *
  17. * @filesource
  18. * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
  19. * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
  20. * @package cake
  21. * @subpackage cake.cake.console
  22. * @since CakePHP(tm) v 1.2.0.5012
  23. * @version $Revision$
  24. * @modifiedby $LastChangedBy$
  25. * @lastmodified $Date$
  26. * @license http://www.opensource.org/licenses/mit-license.php The MIT License
  27. */
  28. /**
  29. * Shell dispatcher
  30. *
  31. * @package cake
  32. * @subpackage cake.cake.console
  33. */
  34. class ShellDispatcher {
  35. /**
  36. * Standard input stream.
  37. *
  38. * @var filehandle
  39. * @access public
  40. */
  41. var $stdin;
  42. /**
  43. * Standard output stream.
  44. *
  45. * @var filehandle
  46. * @access public
  47. */
  48. var $stdout;
  49. /**
  50. * Standard error stream.
  51. *
  52. * @var filehandle
  53. * @access public
  54. */
  55. var $stderr;
  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 $shellClass = null;
  84. /**
  85. * The command called if public methods are available.
  86. *
  87. * @var string
  88. * @access public
  89. */
  90. var $shellCommand = null;
  91. /**
  92. * The path locations of shells.
  93. *
  94. * @var array
  95. * @access public
  96. */
  97. var $shellPaths = array();
  98. /**
  99. * The path to the current shell location.
  100. *
  101. * @var string
  102. * @access public
  103. */
  104. var $shellPath = null;
  105. /**
  106. * The name of the shell in camelized.
  107. *
  108. * @var string
  109. * @access public
  110. */
  111. var $shellName = null;
  112. /**
  113. * Constructs this ShellDispatcher instance.
  114. *
  115. * @param array $args the argv.
  116. */
  117. function ShellDispatcher($args = array()) {
  118. $this->__construct($args);
  119. }
  120. /**
  121. * Constructor
  122. *
  123. * @param array $args the argv.
  124. */
  125. function __construct($args = array()) {
  126. set_time_limit(0);
  127. $this->__initConstants();
  128. $this->parseParams($args);
  129. $this->_initEnvironment();
  130. $this->__buildPaths();
  131. $this->_stop($this->dispatch());
  132. }
  133. /**
  134. * Defines core configuration.
  135. *
  136. * @access private
  137. */
  138. function __initConstants() {
  139. if (function_exists('ini_set')) {
  140. ini_set('display_errors', '1');
  141. ini_set('error_reporting', E_ALL);
  142. ini_set('html_errors', false);
  143. ini_set('implicit_flush', true);
  144. ini_set('max_execution_time', 0);
  145. }
  146. if (!defined('CAKE_CORE_INCLUDE_PATH')) {
  147. define('PHP5', (PHP_VERSION >= 5));
  148. define('DS', DIRECTORY_SEPARATOR);
  149. define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__))));
  150. define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
  151. define('DISABLE_DEFAULT_ERROR_HANDLING', false);
  152. define('CAKEPHP_SHELL', true);
  153. }
  154. require_once(CORE_PATH . 'cake' . DS . 'basics.php');
  155. }
  156. /**
  157. * Defines current working environment.
  158. *
  159. * @access protected
  160. */
  161. function _initEnvironment() {
  162. $this->stdin = fopen('php://stdin', 'r');
  163. $this->stdout = fopen('php://stdout', 'w');
  164. $this->stderr = fopen('php://stderr', 'w');
  165. if (!$this->__bootstrap()) {
  166. $this->stderr("\nCakePHP Console: ");
  167. $this->stderr("\nUnable to load Cake core:");
  168. $this->stderr("\tMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH);
  169. $this->_stop();
  170. }
  171. if (!isset($this->args[0]) || !isset($this->params['working'])) {
  172. $this->stderr("\nCakePHP Console: ");
  173. $this->stderr('This file has been loaded incorrectly and cannot continue.');
  174. $this->stderr('Please make sure that ' . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console is in your system path,');
  175. $this->stderr('and check the manual for the correct usage of this command.');
  176. $this->stderr('(http://manual.cakephp.org/)');
  177. $this->_stop();
  178. }
  179. if (basename(__FILE__) != basename($this->args[0])) {
  180. $this->stderr("\nCakePHP Console: ");
  181. $this->stderr('Warning: the dispatcher may have been loaded incorrectly, which could lead to unexpected results...');
  182. if ($this->getInput('Continue anyway?', array('y', 'n'), 'y') == 'n') {
  183. $this->_stop();
  184. }
  185. }
  186. $this->shiftArgs();
  187. }
  188. /**
  189. * Builds the shell paths.
  190. *
  191. * @access private
  192. * @return void
  193. */
  194. function __buildPaths() {
  195. $paths = array();
  196. $pluginPaths = Configure::read('pluginPaths');
  197. if (!class_exists('Folder')) {
  198. require LIBS . 'folder.php';
  199. }
  200. foreach ($pluginPaths as $pluginPath) {
  201. $Folder =& new Folder($pluginPath);
  202. list($plugins,) = $Folder->read(false, true);
  203. foreach ((array)$plugins as $plugin) {
  204. $path = $pluginPath . Inflector::underscore($plugin) . DS . 'vendors' . DS . 'shells' . DS;
  205. if (file_exists($path)) {
  206. $paths[] = $path;
  207. }
  208. }
  209. }
  210. $vendorPaths = array_values(Configure::read('vendorPaths'));
  211. foreach ($vendorPaths as $vendorPath) {
  212. $path = rtrim($vendorPath, DS) . DS . 'shells' . DS;
  213. if (file_exists($path)) {
  214. $paths[] = $path;
  215. }
  216. }
  217. $this->shellPaths = array_values(array_unique(array_merge($paths, Configure::read('shellPaths'))));
  218. }
  219. /**
  220. * Initializes the environment and loads the Cake core.
  221. *
  222. * @return boolean Success.
  223. * @access private
  224. */
  225. function __bootstrap() {
  226. define('ROOT', $this->params['root']);
  227. define('APP_DIR', $this->params['app']);
  228. define('APP_PATH', $this->params['working'] . DS);
  229. define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS);
  230. $includes = array(
  231. CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php',
  232. CORE_PATH . 'cake' . DS . 'libs' . DS . 'object.php',
  233. CORE_PATH . 'cake' . DS . 'libs' . DS . 'inflector.php',
  234. CORE_PATH . 'cake' . DS . 'libs' . DS . 'configure.php',
  235. CORE_PATH . 'cake' . DS . 'libs' . DS . 'file.php',
  236. CORE_PATH . 'cake' . DS . 'libs' . DS . 'cache.php',
  237. CORE_PATH . 'cake' . DS . 'libs' . DS . 'string.php',
  238. CORE_PATH . 'cake' . DS . 'libs' . DS . 'class_registry.php',
  239. CORE_PATH . 'cake' . DS . 'console' . DS . 'error.php'
  240. );
  241. foreach ($includes as $inc) {
  242. if (!require($inc)) {
  243. $this->stderr("Failed to load Cake core file {$inc}");
  244. return false;
  245. }
  246. }
  247. Configure::getInstance(file_exists(CONFIGS . 'bootstrap.php'));
  248. if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) {
  249. include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'libs' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php';
  250. Configure::buildPaths(array());
  251. }
  252. Configure::write('debug', 1);
  253. return true;
  254. }
  255. /**
  256. * Dispatches a CLI request
  257. *
  258. * @access public
  259. */
  260. function dispatch() {
  261. if (isset($this->args[0])) {
  262. $plugin = null;
  263. $shell = $this->args[0];
  264. if (strpos($shell, '.') !== false) {
  265. list($plugin, $shell) = explode('.', $this->args[0]);
  266. }
  267. $this->shell = $shell;
  268. $this->shiftArgs();
  269. $this->shellName = Inflector::camelize($this->shell);
  270. $this->shellClass = $this->shellName . 'Shell';
  271. if ($this->shell === 'help') {
  272. $this->help();
  273. } else {
  274. $loaded = false;
  275. foreach ($this->shellPaths as $path) {
  276. $this->shellPath = $path . $this->shell . '.php';
  277. $isPlugin = ($plugin && strpos($path, DS . $plugin . DS . 'vendors' . DS . 'shells' . DS) !== false);
  278. if (($isPlugin && file_exists($this->shellPath)) || (!$plugin && file_exists($this->shellPath))) {
  279. $loaded = true;
  280. break;
  281. }
  282. }
  283. if ($loaded) {
  284. if (!class_exists('Shell')) {
  285. require CONSOLE_LIBS . 'shell.php';
  286. }
  287. require $this->shellPath;
  288. if (class_exists($this->shellClass)) {
  289. $command = null;
  290. if (isset($this->args[0])) {
  291. $command = $this->args[0];
  292. }
  293. $this->shellCommand = Inflector::variable($command);
  294. $shell = new $this->shellClass($this);
  295. if (strtolower(get_parent_class($shell)) == 'shell') {
  296. $shell->initialize();
  297. $shell->loadTasks();
  298. foreach ($shell->taskNames as $task) {
  299. if (strtolower(get_parent_class($shell)) == 'shell') {
  300. $shell->{$task}->initialize();
  301. $shell->{$task}->loadTasks();
  302. }
  303. }
  304. $task = Inflector::camelize($command);
  305. if (in_array($task, $shell->taskNames)) {
  306. $this->shiftArgs();
  307. $shell->{$task}->startup();
  308. if (isset($this->args[0]) && $this->args[0] == 'help') {
  309. if (method_exists($shell->{$task}, 'help')) {
  310. $shell->{$task}->help();
  311. $this->_stop();
  312. } else {
  313. $this->help();
  314. }
  315. }
  316. return $shell->{$task}->execute();
  317. }
  318. }
  319. $classMethods = get_class_methods($shell);
  320. $privateMethod = $missingCommand = false;
  321. if ((in_array($command, $classMethods) || in_array(strtolower($command), $classMethods)) && strpos($command, '_', 0) === 0) {
  322. $privateMethod = true;
  323. }
  324. if (!in_array($command, $classMethods) && !in_array(strtolower($command), $classMethods)) {
  325. $missingCommand = true;
  326. }
  327. $protectedCommands = array(
  328. 'initialize','in','out','err','hr',
  329. 'createfile', 'isdir','copydir','object','tostring',
  330. 'requestaction','log','cakeerror', 'shelldispatcher',
  331. '__initconstants','__initenvironment','__construct',
  332. 'dispatch','__bootstrap','getinput','stdout','stderr','parseparams','shiftargs'
  333. );
  334. if (in_array(strtolower($command), $protectedCommands)) {
  335. $missingCommand = true;
  336. }
  337. if ($missingCommand && method_exists($shell, 'main')) {
  338. $shell->startup();
  339. return $shell->main();
  340. } elseif (!$privateMethod && method_exists($shell, $command)) {
  341. $this->shiftArgs();
  342. $shell->startup();
  343. return $shell->{$command}();
  344. } else {
  345. $this->stderr("Unknown {$this->shellName} command '$command'.\nFor usage, try 'cake {$this->shell} help'.\n\n");
  346. }
  347. } else {
  348. $this->stderr('Class '.$this->shellClass.' could not be loaded');
  349. }
  350. } else {
  351. $this->help();
  352. }
  353. }
  354. } else {
  355. $this->help();
  356. }
  357. }
  358. /**
  359. * Prompts the user for input, and returns it.
  360. *
  361. * @param string $prompt Prompt text.
  362. * @param mixed $options Array or string of options.
  363. * @param string $default Default input value.
  364. * @return Either the default value, or the user-provided input.
  365. * @access public
  366. */
  367. function getInput($prompt, $options = null, $default = null) {
  368. if (!is_array($options)) {
  369. $printOptions = '';
  370. } else {
  371. $printOptions = '(' . implode('/', $options) . ')';
  372. }
  373. if ($default == null) {
  374. $this->stdout($prompt . " $printOptions \n" . '> ', false);
  375. } else {
  376. $this->stdout($prompt . " $printOptions \n" . "[$default] > ", false);
  377. }
  378. $result = fgets($this->stdin);
  379. if ($result === false) {
  380. exit;
  381. }
  382. $result = trim($result);
  383. if ($default != null && empty($result)) {
  384. return $default;
  385. }
  386. return $result;
  387. }
  388. /**
  389. * Outputs to the stdout filehandle.
  390. *
  391. * @param string $string String to output.
  392. * @param boolean $newline If true, the outputs gets an added newline.
  393. * @access public
  394. */
  395. function stdout($string, $newline = true) {
  396. if ($newline) {
  397. fwrite($this->stdout, $string . "\n");
  398. } else {
  399. fwrite($this->stdout, $string);
  400. }
  401. }
  402. /**
  403. * Outputs to the stderr filehandle.
  404. *
  405. * @param string $string Error text to output.
  406. * @access public
  407. */
  408. function stderr($string) {
  409. fwrite($this->stderr, 'Error: '. $string);
  410. }
  411. /**
  412. * Parses command line options
  413. *
  414. * @param array $params Parameters to parse
  415. * @access public
  416. */
  417. function parseParams($params) {
  418. $this->__parseParams($params);
  419. $defaults = array('app' => 'app', 'root' => dirname(dirname(dirname(__FILE__))), 'working' => null, 'webroot' => 'webroot');
  420. $params = array_merge($defaults, array_intersect_key($this->params, $defaults));
  421. $isWin = array_filter(array_map('strpos', $params, array('\\')));
  422. $params = str_replace('\\', '/', $params);
  423. if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) {
  424. if (empty($this->params['app']) && $params['working'] != $params['root']) {
  425. $params['root'] = dirname($params['working']);
  426. $params['app'] = basename($params['working']);
  427. } else {
  428. $params['root'] = $params['working'];
  429. }
  430. }
  431. if ($params['app'][0] == '/' || preg_match('/([a-zA-Z])(:)/i', $params['app'], $matches)) {
  432. $params['root'] = dirname($params['app']);
  433. } elseif (strpos($params['app'], '/')) {
  434. $params['root'] .= '/' . dirname($params['app']);
  435. }
  436. $params['app'] = basename($params['app']);
  437. $params['working'] = rtrim($params['root'], '/') . '/' . $params['app'];
  438. if (!empty($matches[0]) || !empty($isWin)) {
  439. $params = str_replace('/', '\\', $params);
  440. }
  441. $this->params = array_merge($this->params, $params);
  442. }
  443. /**
  444. * Helper for recursively paraing params
  445. *
  446. * @return array params
  447. * @access private
  448. */
  449. function __parseParams($params) {
  450. $count = count($params);
  451. for ($i = 0; $i < $count; $i++) {
  452. if (isset($params[$i])) {
  453. if ($params[$i]{0} === '-') {
  454. $key = substr($params[$i], 1);
  455. $this->params[$key] = true;
  456. unset($params[$i]);
  457. if (isset($params[++$i])) {
  458. if ($params[$i]{0} !== '-') {
  459. $this->params[$key] = str_replace('"', '', $params[$i]);
  460. unset($params[$i]);
  461. } else {
  462. $i--;
  463. $this->__parseParams($params);
  464. }
  465. }
  466. } else {
  467. $this->args[] = $params[$i];
  468. unset($params[$i]);
  469. }
  470. }
  471. }
  472. }
  473. /**
  474. * Removes first argument and shifts other arguments up
  475. *
  476. * @return boolean False if there are no arguments
  477. * @access public
  478. */
  479. function shiftArgs() {
  480. if (empty($this->args)) {
  481. return false;
  482. }
  483. unset($this->args[0]);
  484. $this->args = array_values($this->args);
  485. return true;
  486. }
  487. /**
  488. * Shows console help
  489. *
  490. * @access public
  491. */
  492. function help() {
  493. $this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console");
  494. $this->stdout("---------------------------------------------------------------");
  495. $this->stdout("Current Paths:");
  496. $this->stdout(" -app: ". $this->params['app']);
  497. $this->stdout(" -working: " . rtrim($this->params['working'], DS));
  498. $this->stdout(" -root: " . rtrim($this->params['root'], DS));
  499. $this->stdout(" -core: " . rtrim(CORE_PATH, DS));
  500. $this->stdout("");
  501. $this->stdout("Changing Paths:");
  502. $this->stdout("your working path should be the same as your application path");
  503. $this->stdout("to change your path use the '-app' param.");
  504. $this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp");
  505. $this->stdout("\nAvailable Shells:");
  506. $_shells = array();
  507. foreach ($this->shellPaths as $path) {
  508. if (is_dir($path)) {
  509. $shells = Configure::listObjects('file', $path);
  510. $path = str_replace(CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS, 'CORE' . DS, $path);
  511. $path = str_replace(APP, 'APP' . DS, $path);
  512. $path = str_replace(ROOT, 'ROOT', $path);
  513. $path = rtrim($path, DS);
  514. $this->stdout("\n " . $path . ":");
  515. if (empty($shells)) {
  516. $this->stdout("\t - none");
  517. } else {
  518. sort($shells);
  519. foreach ($shells as $shell) {
  520. if ($shell !== 'shell.php') {
  521. $this->stdout("\t " . str_replace('.php', '', $shell));
  522. }
  523. }
  524. }
  525. }
  526. }
  527. $this->stdout("\nTo run a command, type 'cake shell_name [args]'");
  528. $this->stdout("To get help on a specific command, type 'cake shell_name help'");
  529. $this->_stop();
  530. }
  531. /**
  532. * Stop execution of the current script
  533. *
  534. * @param $status see http://php.net/exit for values
  535. * @return void
  536. * @access protected
  537. */
  538. function _stop($status = 0) {
  539. exit($status);
  540. }
  541. }
  542. if (!defined('DISABLE_AUTO_DISPATCH')) {
  543. $dispatcher = new ShellDispatcher($argv);
  544. }
  545. ?>