/cake/console/cake.php

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