cake.php

Go to the documentation of this file.
00001 #!/usr/bin/php -q
00002 <?php
00003 /* SVN FILE: $Id: cake_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00004 /**
00005  * Command-line code generation utility to automate programmer chores.
00006  *
00007  * Shell dispatcher class
00008  *
00009  * PHP versions 4 and 5
00010  *
00011  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00012  * Copyright 2005-2008, Cake Software Foundation, Inc.
00013  *                              1785 E. Sahara Avenue, Suite 490-204
00014  *                              Las Vegas, Nevada 89104
00015  *
00016  * Licensed under The MIT License
00017  * Redistributions of files must retain the above copyright notice.
00018  *
00019  * @filesource
00020  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00021  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00022  * @package         cake
00023  * @subpackage      cake.cake.console
00024  * @since           CakePHP(tm) v 1.2.0.5012
00025  * @version         $Revision: 580 $
00026  * @modifiedby      $LastChangedBy: gwoo $
00027  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00028  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00029  */
00030 /**
00031  * Shell dispatcher
00032  *
00033  * @package     cake
00034  * @subpackage  cake.cake.console
00035  */
00036 class ShellDispatcher {
00037 /**
00038  * Standard input stream.
00039  *
00040  * @var filehandle
00041  * @access public
00042  */
00043     var $stdin;
00044 /**
00045  * Standard output stream.
00046  *
00047  * @var filehandle
00048  * @access public
00049  */
00050     var $stdout;
00051 /**
00052  * Standard error stream.
00053  *
00054  * @var filehandle
00055  * @access public
00056  */
00057     var $stderr;
00058 /**
00059  * Contains command switches parsed from the command line.
00060  *
00061  * @var array
00062  * @access public
00063  */
00064     var $params = array();
00065 /**
00066  * Contains arguments parsed from the command line.
00067  *
00068  * @var array
00069  * @access public
00070  */
00071     var $args = array();
00072 /**
00073  * The file name of the shell that was invoked.
00074  *
00075  * @var string
00076  * @access public
00077  */
00078     var $shell = null;
00079 /**
00080  * The class name of the shell that was invoked.
00081  *
00082  * @var string
00083  * @access public
00084  */
00085     var $shellClass = null;
00086 /**
00087  * The command called if public methods are available.
00088  *
00089  * @var string
00090  * @access public
00091  */
00092     var $shellCommand = null;
00093 /**
00094  * The path locations of shells.
00095  *
00096  * @var array
00097  * @access public
00098  */
00099     var $shellPaths = array();
00100 /**
00101  * The path to the current shell location.
00102  *
00103  * @var string
00104  * @access public
00105  */
00106     var $shellPath = null;
00107 /**
00108  * The name of the shell in camelized.
00109  *
00110  * @var string
00111  * @access public
00112  */
00113     var $shellName = null;
00114 /**
00115  * Constructs this ShellDispatcher instance.
00116  *
00117  * @param array $args the argv.
00118  */
00119     function ShellDispatcher($args = array()) {
00120         $this->__construct($args);
00121     }
00122 /**
00123  * Constructor
00124  *
00125  * @param array $args the argv.
00126  */
00127     function __construct($args = array()) {
00128         set_time_limit(0);
00129         $this->__initConstants();
00130         $this->parseParams($args);
00131         $this->__initEnvironment();
00132         $this->dispatch();
00133         die("\n");
00134     }
00135 /**
00136  * Defines core configuration.
00137  *
00138  * @access private
00139  */
00140     function __initConstants() {
00141         if (function_exists('ini_set')) {
00142             ini_set('display_errors', '1');
00143             ini_set('error_reporting', E_ALL);
00144             ini_set('html_errors', false);
00145             ini_set('implicit_flush', true);
00146             ini_set('max_execution_time', 0);
00147         }
00148 
00149         if (!defined('CAKE_CORE_INCLUDE_PATH')) {
00150             define('PHP5', (phpversion() >= 5));
00151             define('DS', DIRECTORY_SEPARATOR);
00152             define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__))));
00153             define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
00154             define('DISABLE_DEFAULT_ERROR_HANDLING', false);
00155             define('CAKEPHP_SHELL', true);
00156         }
00157         require_once(CORE_PATH . 'cake' . DS . 'basics.php');
00158     }
00159 /**
00160  * Defines current working environment.
00161  *
00162  * @access private
00163  */
00164     function __initEnvironment() {
00165         $this->stdin = fopen('php://stdin', 'r');
00166         $this->stdout = fopen('php://stdout', 'w');
00167         $this->stderr = fopen('php://stderr', 'w');
00168 
00169         if (!$this->__bootstrap()) {
00170             $this->stderr("\nCakePHP Console: ");
00171             $this->stderr("\nUnable to load Cake core:");
00172             $this->stderr("\tMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH);
00173             exit();
00174         }
00175 
00176         if (!isset($this->args[0]) || !isset($this->params['working'])) {
00177             $this->stderr("\nCakePHP Console: ");
00178             $this->stderr('This file has been loaded incorrectly and cannot continue.');
00179             $this->stderr('Please make sure that ' . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console is in your system path,');
00180             $this->stderr('and check the manual for the correct usage of this command.');
00181             $this->stderr('(http://manual.cakephp.org/)');
00182             exit();
00183         }
00184 
00185         if (basename(__FILE__) !=  basename($this->args[0])) {
00186             $this->stderr("\nCakePHP Console: ");
00187             $this->stderr('Warning: the dispatcher may have been loaded incorrectly, which could lead to unexpected results...');
00188             if ($this->getInput('Continue anyway?', array('y', 'n'), 'y') == 'n') {
00189                 exit();
00190             }
00191         }
00192 
00193         $this->shiftArgs();
00194 
00195         $this->shellPaths = array(
00196             APP . 'vendors' . DS . 'shells' . DS,
00197             VENDORS . 'shells' . DS,
00198             CONSOLE_LIBS
00199         );
00200     }
00201 /**
00202  * Initializes the environment and loads the Cake core.
00203  *
00204  * @return boolean Success.
00205  * @access private
00206  */
00207     function __bootstrap() {
00208 
00209         define('ROOT', $this->params['root']);
00210         define('APP_DIR', $this->params['app']);
00211         define('APP_PATH', $this->params['working'] . DS);
00212         define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS);
00213 
00214         $includes = array(
00215             CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php',
00216             CORE_PATH . 'cake' . DS . 'libs' . DS . 'object.php',
00217             CORE_PATH . 'cake' . DS . 'libs' . DS . 'inflector.php',
00218             CORE_PATH . 'cake' . DS . 'libs' . DS . 'configure.php',
00219             CORE_PATH . 'cake' . DS . 'libs' . DS . 'file.php',
00220             CORE_PATH . 'cake' . DS . 'libs' . DS . 'cache.php',
00221             CORE_PATH . 'cake' . DS . 'libs' . DS . 'string.php',
00222             CORE_PATH . 'cake' . DS . 'libs' . DS . 'class_registry.php',
00223             CORE_PATH . 'cake' . DS . 'console' . DS . 'error.php'
00224         );
00225 
00226         foreach ($includes as $inc) {
00227             if (!require($inc)) {
00228                 $this->stderr("Failed to load Cake core file {$inc}");
00229                 return false;
00230             }
00231         }
00232 
00233         Configure::getInstance(file_exists(CONFIGS . 'bootstrap.php'));
00234 
00235         if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) {
00236             include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'libs' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php';
00237             Configure::buildPaths(array());
00238         }
00239 
00240         Configure::write('debug', 1);
00241         return true;
00242     }
00243 
00244 /**
00245  * Dispatches a CLI request
00246  *
00247  * @access public
00248  */
00249     function dispatch() {
00250         if (isset($this->args[0])) {
00251             $plugin = null;
00252             $shell = $this->args[0];
00253             if (strpos($shell, '.') !== false)  {
00254                 list($plugin, $shell) = explode('.', $this->args[0]);
00255             }
00256 
00257             $this->shell = $shell;
00258             $this->shiftArgs();
00259             $this->shellName = Inflector::camelize($this->shell);
00260             $this->shellClass = $this->shellName . 'Shell';
00261 
00262             if ($this->shell == 'help') {
00263                 $this->help();
00264             } else {
00265                 $loaded = false;
00266                 $paths = array();
00267 
00268                 if ($plugin !== null) {
00269                     $pluginPaths = Configure::read('pluginPaths');
00270                     $count = count($pluginPaths);
00271                     for ($i = 0; $i < $count; $i++) {
00272                         $paths[] = $pluginPaths[$i] . $plugin . DS . 'vendors' . DS . 'shells' . DS;
00273                     }
00274                 }
00275 
00276                 $vendorPaths = array_values(Configure::read('vendorPaths'));
00277                 $count = count($vendorPaths);
00278 
00279                 for ($i = 0; $i < $count; $i++) {
00280                     $paths[] = rtrim($vendorPaths[$i], DS) . DS . 'shells' . DS;
00281                 }
00282 
00283                 $this->shellPaths = array_merge($paths, array(CONSOLE_LIBS));
00284                 foreach ($this->shellPaths as $path) {
00285                     $this->shellPath = $path . $this->shell . ".php";
00286                     if (file_exists($this->shellPath)) {
00287                         $loaded = true;
00288                         break;
00289                     }
00290                 }
00291 
00292                 if ($loaded) {
00293                     if (!class_exists('Shell')) {
00294                         require CONSOLE_LIBS . 'shell.php';
00295                     }
00296                     require $this->shellPath;
00297                     if (class_exists($this->shellClass)) {
00298                         $command = null;
00299                         if (isset($this->args[0])) {
00300                             $command = $this->args[0];
00301                         }
00302                         $this->shellCommand = Inflector::variable($command);
00303                         $shell = new $this->shellClass($this);
00304 
00305                         if (strtolower(get_parent_class($shell)) == 'shell') {
00306                             $shell->initialize();
00307                             $shell->loadTasks();
00308 
00309                             foreach ($shell->taskNames as $task) {
00310                                 if (strtolower(get_parent_class($shell)) == 'shell') {
00311                                     $shell->{$task}->initialize();
00312                                     $shell->{$task}->loadTasks();
00313                                 }
00314                             }
00315 
00316                             $task = Inflector::camelize($command);
00317                             if (in_array($task, $shell->taskNames)) {
00318                                 $this->shiftArgs();
00319                                 $shell->{$task}->startup();
00320                                 if (isset($this->args[0]) && $this->args[0] == 'help') {
00321                                     if (method_exists($shell->{$task}, 'help')) {
00322                                         $shell->{$task}->help();
00323                                         exit();
00324                                     } else {
00325                                         $this->help();
00326                                     }
00327                                 }
00328                                 $shell->{$task}->execute();
00329                                 return;
00330                             }
00331                         }
00332 
00333                         $classMethods = get_class_methods($shell);
00334 
00335                         $privateMethod = $missingCommand = false;
00336                         if ((in_array($command, $classMethods) || in_array(strtolower($command), $classMethods)) && strpos($command, '_', 0) === 0) {
00337                             $privateMethod = true;
00338                         }
00339 
00340                         if (!in_array($command, $classMethods) && !in_array(strtolower($command), $classMethods)) {
00341                             $missingCommand = true;
00342                         }
00343 
00344                         $protectedCommands = array(
00345                             'initialize','in','out','err','hr',
00346                             'createfile', 'isdir','copydir','object','tostring',
00347                             'requestaction','log','cakeerror', 'shelldispatcher',
00348                             '__initconstants','__initenvironment','__construct',
00349                             'dispatch','__bootstrap','getinput','stdout','stderr','parseparams','shiftargs'
00350                         );
00351 
00352                         if (in_array(strtolower($command), $protectedCommands)) {
00353                             $missingCommand = true;
00354                         }
00355 
00356                         if ($missingCommand && method_exists($shell, 'main')) {
00357                             $shell->startup();
00358                             $shell->main();
00359                         } elseif (!$privateMethod && method_exists($shell, $command)) {
00360                             $this->shiftArgs();
00361                             $shell->startup();
00362                             $shell->{$command}();
00363                         } else {
00364                             $this->stderr("Unknown {$this->shellName} command '$command'.\nFor usage, try 'cake {$this->shell} help'.\n\n");
00365                         }
00366                     } else {
00367                         $this->stderr('Class '.$this->shellClass.' could not be loaded');
00368                     }
00369                 } else {
00370                     $this->help();
00371                 }
00372             }
00373         } else {
00374             $this->help();
00375         }
00376     }
00377 /**
00378  * Prompts the user for input, and returns it.
00379  *
00380  * @param string $prompt Prompt text.
00381  * @param mixed $options Array or string of options.
00382  * @param string $default Default input value.
00383  * @return Either the default value, or the user-provided input.
00384  * @access public
00385  */
00386     function getInput($prompt, $options = null, $default = null) {
00387         if (!is_array($options)) {
00388             $print_options = '';
00389         } else {
00390             $print_options = '(' . implode('/', $options) . ')';
00391         }
00392 
00393         if ($default == null) {
00394             $this->stdout($prompt . " $print_options \n" . '> ', false);
00395         } else {
00396             $this->stdout($prompt . " $print_options \n" . "[$default] > ", false);
00397         }
00398         $result = fgets($this->stdin);
00399 
00400         if ($result === false){
00401             exit;
00402         }
00403         $result = trim($result);
00404 
00405         if ($default != null && empty($result)) {
00406             return $default;
00407         } else {
00408             return $result;
00409         }
00410     }
00411 /**
00412  * Outputs to the stdout filehandle.
00413  *
00414  * @param string $string String to output.
00415  * @param boolean $newline If true, the outputs gets an added newline.
00416  * @access public
00417  */
00418     function stdout($string, $newline = true) {
00419         if ($newline) {
00420             fwrite($this->stdout, $string . "\n");
00421         } else {
00422             fwrite($this->stdout, $string);
00423         }
00424     }
00425 /**
00426  * Outputs to the stderr filehandle.
00427  *
00428  * @param string $string Error text to output.
00429  * @access public
00430  */
00431     function stderr($string) {
00432         fwrite($this->stderr, 'Error: '. $string);
00433     }
00434 /**
00435  * Parses command line options
00436  *
00437  * @param array $params Parameters to parse
00438  * @access public
00439  */
00440     function parseParams($params) {
00441         $this->__parseParams($params);
00442 
00443         $defaults = array('app' => 'app', 'root' => dirname(dirname(dirname(__FILE__))), 'working' => null, 'webroot' => 'webroot');
00444 
00445         $params = array_merge($defaults, array_intersect_key($this->params, $defaults));
00446 
00447         $isWin = array_filter(array_map('strpos', $params, array('\\')));
00448 
00449         $params = str_replace('\\', '/', $params);
00450 
00451         if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) {
00452             if (empty($this->params['app']) && $params['working'] != $params['root']) {
00453                 $params['root'] = dirname($params['working']);
00454                 $params['app'] = basename($params['working']);
00455             } else {
00456                 $params['root'] = $params['working'];
00457             }
00458         }
00459 
00460         if($params['app'][0] == '/' || preg_match('/([a-z])(:)/i', $params['app'], $matches)) {
00461             $params['root'] = dirname($params['app']);
00462         } elseif (strpos($params['app'], '/')) {
00463             $params['root'] .= '/' . dirname($params['app']);
00464         }
00465 
00466         $params['app'] = basename($params['app']);
00467         $params['working'] = $params['root'] . '/' . $params['app'];
00468 
00469         if (!empty($matches[0]) || !empty($isWin)) {
00470             $params = str_replace('/', '\\', $params);
00471         }
00472 
00473         $this->params = array_merge($this->params, $params);
00474     }
00475 /**
00476  * Helper for recursively paraing params
00477  *
00478  * @return array params
00479  * @access private
00480  */
00481     function __parseParams($params) {
00482         $count = count($params);
00483         for ($i = 0; $i < $count; $i++) {
00484             if (isset($params[$i])) {
00485                 if ($params[$i]{0} === '-') {
00486                     $key = substr($params[$i], 1);
00487                     $this->params[$key] = true;
00488                     unset($params[$i]);
00489                     if (isset($params[++$i])) {
00490                         if ($params[$i]{0} !== '-') {
00491                             $this->params[$key] = str_replace('"', '', $params[$i]);
00492                             unset($params[$i]);
00493                         } else {
00494                             $i--;
00495                             $this->__parseParams($params);
00496                         }
00497                     }
00498                 } else {
00499                     $this->args[] = $params[$i];
00500                     unset($params[$i]);
00501                 }
00502 
00503             }
00504         }
00505     }
00506 /**
00507  * Removes first argument and shifts other arguments up
00508  *
00509  * @return boolean False if there are no arguments
00510  * @access public
00511  */
00512     function shiftArgs() {
00513         if (empty($this->args)) {
00514             return false;
00515         }
00516         unset($this->args[0]);
00517         $this->args = array_values($this->args);
00518         return true;
00519     }
00520 /**
00521  * Shows console help
00522  *
00523  * @access public
00524  */
00525     function help() {
00526         $this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console");
00527         $this->stdout("---------------------------------------------------------------");
00528         $this->stdout("Current Paths:");
00529         $this->stdout(" -app: ". $this->params['app']);
00530         $this->stdout(" -working: " . $this->params['working']);
00531         $this->stdout(" -root: " . $this->params['root']);
00532         $this->stdout(" -core: " . CORE_PATH);
00533         $this->stdout("");
00534         $this->stdout("Changing Paths:");
00535         $this->stdout("your working path should be the same as your application path");
00536         $this->stdout("to change your path use the '-app' param.");
00537         $this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp");
00538 
00539         $this->stdout("\nAvailable Shells:");
00540         foreach ($this->shellPaths as $path) {
00541             if (is_dir($path)) {
00542                 $shells = Configure::listObjects('file', $path);
00543                 $path = r(CORE_PATH, '', $path);
00544                 $this->stdout("\n " . $path . ":");
00545                 if (empty($shells)) {
00546                     $this->stdout("\t - none");
00547                 } else {
00548                     foreach ($shells as $shell) {
00549                         if ($shell != 'shell.php') {
00550                             $this->stdout("\t " . r('.php', '', $shell));
00551                         }
00552                     }
00553                 }
00554             }
00555         }
00556         $this->stdout("\nTo run a command, type 'cake shell_name [args]'");
00557         $this->stdout("To get help on a specific command, type 'cake shell_name help'");
00558         exit();
00559     }
00560 }
00561 if (!defined('DISABLE_AUTO_DISPATCH')) {
00562     $dispatcher = new ShellDispatcher($argv);
00563 }
00564 ?>