/cake/console/cake.php

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