1: <?php
2: /**
3: * CConsoleApplication class file.
4: *
5: * @author Qiang Xue <qiang.xue@gmail.com>
6: * @link http://www.yiiframework.com/
7: * @copyright 2008-2013 Yii Software LLC
8: * @license http://www.yiiframework.com/license/
9: */
10:
11: /**
12: * CConsoleApplication represents a console application.
13: *
14: * CConsoleApplication extends {@link CApplication} by providing functionalities
15: * specific to console requests. In particular, it deals with console requests
16: * through a command-based approach:
17: * <ul>
18: * <li>A console application consists of one or several possible user commands;</li>
19: * <li>Each user command is implemented as a class extending {@link CConsoleCommand};</li>
20: * <li>User specifies which command to run on the command line;</li>
21: * <li>The command processes the user request with the specified parameters.</li>
22: * </ul>
23: *
24: * The command classes reside in the directory {@link getCommandPath commandPath}.
25: * The name of the class follows the pattern: <command-name>Command, and its
26: * file name is the same as the class name. For example, the 'ShellCommand' class defines
27: * a 'shell' command and the class file name is 'ShellCommand.php'.
28: *
29: * To run the console application, enter the following on the command line:
30: * <pre>
31: * php path/to/entry_script.php <command name> [param 1] [param 2] ...
32: * </pre>
33: *
34: * You may use the following to see help instructions about a command:
35: * <pre>
36: * php path/to/entry_script.php help <command name>
37: * </pre>
38: *
39: * @property string $commandPath The directory that contains the command classes. Defaults to 'protected/commands'.
40: * @property CConsoleCommandRunner $commandRunner The command runner.
41: * @property CConsoleCommand $command The currently active command.
42: *
43: * @author Qiang Xue <qiang.xue@gmail.com>
44: * @package system.console
45: * @since 1.0
46: */
47: class CConsoleApplication extends CApplication
48: {
49: /**
50: * @var array mapping from command name to command configurations.
51: * Each command configuration can be either a string or an array.
52: * If the former, the string should be the file path of the command class.
53: * If the latter, the array must contain a 'class' element which specifies
54: * the command's class name or {@link YiiBase::getPathOfAlias class path alias}.
55: * The rest name-value pairs in the array are used to initialize
56: * the corresponding command properties. For example,
57: * <pre>
58: * array(
59: * 'email'=>array(
60: * 'class'=>'path.to.Mailer',
61: * 'interval'=>3600,
62: * ),
63: * 'log'=>'path/to/LoggerCommand.php',
64: * )
65: * </pre>
66: */
67: public $commandMap=array();
68:
69: private $_commandPath;
70: private $_runner;
71:
72: /**
73: * Initializes the application by creating the command runner.
74: */
75: protected function init()
76: {
77: parent::init();
78: if(empty($_SERVER['argv']))
79: die('This script must be run from the command line.');
80: $this->_runner=$this->createCommandRunner();
81: $this->_runner->commands=$this->commandMap;
82: $this->_runner->addCommands($this->getCommandPath());
83: }
84:
85: /**
86: * Processes the user request.
87: * This method uses a console command runner to handle the particular user command.
88: * Since version 1.1.11 this method will exit application with an exit code if one is returned by the user command.
89: */
90: public function processRequest()
91: {
92: $exitCode=$this->_runner->run($_SERVER['argv']);
93: if(is_int($exitCode))
94: $this->end($exitCode);
95: }
96:
97: /**
98: * Creates the command runner instance.
99: * @return CConsoleCommandRunner the command runner
100: */
101: protected function createCommandRunner()
102: {
103: return new CConsoleCommandRunner;
104: }
105:
106: /**
107: * Displays the captured PHP error.
108: * This method displays the error in console mode when there is
109: * no active error handler.
110: * @param integer $code error code
111: * @param string $message error message
112: * @param string $file error file
113: * @param string $line error line
114: */
115: public function displayError($code,$message,$file,$line)
116: {
117: echo "PHP Error[$code]: $message\n";
118: echo " in file $file at line $line\n";
119: $trace=debug_backtrace();
120: // skip the first 4 stacks as they do not tell the error position
121: if(count($trace)>4)
122: $trace=array_slice($trace,4);
123: foreach($trace as $i=>$t)
124: {
125: if(!isset($t['file']))
126: $t['file']='unknown';
127: if(!isset($t['line']))
128: $t['line']=0;
129: if(!isset($t['function']))
130: $t['function']='unknown';
131: echo "#$i {$t['file']}({$t['line']}): ";
132: if(isset($t['object']) && is_object($t['object']))
133: echo get_class($t['object']).'->';
134: echo "{$t['function']}()\n";
135: }
136: }
137:
138: /**
139: * Displays the uncaught PHP exception.
140: * This method displays the exception in console mode when there is
141: * no active error handler.
142: * @param Exception $exception the uncaught exception
143: */
144: public function displayException($exception)
145: {
146: echo $exception;
147: }
148:
149: /**
150: * @return string the directory that contains the command classes. Defaults to 'protected/commands'.
151: */
152: public function getCommandPath()
153: {
154: $applicationCommandPath = $this->getBasePath().DIRECTORY_SEPARATOR.'commands';
155: if($this->_commandPath===null && file_exists($applicationCommandPath))
156: $this->setCommandPath($applicationCommandPath);
157: return $this->_commandPath;
158: }
159:
160: /**
161: * @param string $value the directory that contains the command classes.
162: * @throws CException if the directory is invalid
163: */
164: public function setCommandPath($value)
165: {
166: if(($this->_commandPath=realpath($value))===false || !is_dir($this->_commandPath))
167: throw new CException(Yii::t('yii','The command path "{path}" is not a valid directory.',
168: array('{path}'=>$value)));
169: }
170:
171: /**
172: * Returns the command runner.
173: * @return CConsoleCommandRunner the command runner.
174: */
175: public function getCommandRunner()
176: {
177: return $this->_runner;
178: }
179:
180: /**
181: * Returns the currently running command.
182: * This is shortcut method for {@link CConsoleCommandRunner::getCommand()}.
183: * @return CConsoleCommand|null the currently active command.
184: * @since 1.1.14
185: */
186: public function getCommand()
187: {
188: return $this->getCommandRunner()->getCommand();
189: }
190:
191: /**
192: * This is shortcut method for {@link CConsoleCommandRunner::setCommand()}.
193: * @param CConsoleCommand $value the currently active command.
194: * @since 1.1.14
195: */
196: public function setCommand($value)
197: {
198: $this->getCommandRunner()->setCommand($value);
199: }
200: }
201: