PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/yii/framework/console/CConsoleCommand.php

https://bitbucket.org/sanbrar/zurmo-invoice-example
PHP | 448 lines | 285 code | 24 blank | 139 comment | 42 complexity | 6082c183c6ae954653aa9b566730493e MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, GPL-3.0, BSD-3-Clause
  1. <?php
  2. /**
  3. * CConsoleCommand class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2011 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CConsoleCommand represents an executable console command.
  12. *
  13. * It works like {@link CController} by parsing command line options and dispatching
  14. * the request to a specific action with appropriate option values.
  15. *
  16. * Users call a console command via the following command format:
  17. * <pre>
  18. * yiic CommandName ActionName --Option1=Value1 --Option2=Value2 ...
  19. * </pre>
  20. *
  21. * Child classes mainly needs to implement various action methods whose name must be
  22. * prefixed with "action". The parameters to an action method are considered as options
  23. * for that specific action. The action specified as {@link defaultAction} will be invoked
  24. * when a user does not specify the action name in his command.
  25. *
  26. * Options are bound to action parameters via parameter names. For example, the following
  27. * action method will allow us to run a command with <code>yiic sitemap --type=News</code>:
  28. * <pre>
  29. * class SitemapCommand {
  30. * public function actionIndex($type) {
  31. * ....
  32. * }
  33. * }
  34. * </pre>
  35. *
  36. * @author Qiang Xue <qiang.xue@gmail.com>
  37. * @version $Id: CConsoleCommand.php 3276 2011-06-15 14:21:12Z alexander.makarow $
  38. * @package system.console
  39. * @since 1.0
  40. */
  41. abstract class CConsoleCommand extends CComponent
  42. {
  43. /**
  44. * @var string the name of the default action. Defaults to 'index'.
  45. * @since 1.1.5
  46. */
  47. public $defaultAction='index';
  48. private $_name;
  49. private $_runner;
  50. /**
  51. * Constructor.
  52. * @param string $name name of the command
  53. * @param CConsoleCommandRunner $runner the command runner
  54. */
  55. public function __construct($name,$runner)
  56. {
  57. $this->_name=$name;
  58. $this->_runner=$runner;
  59. }
  60. /**
  61. * Initializes the command object.
  62. * This method is invoked after a command object is created and initialized with configurations.
  63. * You may override this method to further customize the command before it executes.
  64. * @since 1.1.6
  65. */
  66. public function init()
  67. {
  68. }
  69. /**
  70. * Executes the command.
  71. * The default implementation will parse the input parameters and
  72. * dispatch the command request to an appropriate action with the corresponding
  73. * option values
  74. * @param array $args command line parameters for this command.
  75. */
  76. public function run($args)
  77. {
  78. list($action, $options, $args)=$this->resolveRequest($args);
  79. $methodName='action'.$action;
  80. if(!preg_match('/^\w+$/',$action) || !method_exists($this,$methodName))
  81. $this->usageError("Unknown action: ".$action);
  82. $method=new ReflectionMethod($this,$methodName);
  83. $params=array();
  84. // named and unnamed options
  85. foreach($method->getParameters() as $i=>$param)
  86. {
  87. $name=$param->getName();
  88. if(isset($options[$name]))
  89. {
  90. if($param->isArray())
  91. $params[]=is_array($options[$name]) ? $options[$name] : array($options[$name]);
  92. else if(!is_array($options[$name]))
  93. $params[]=$options[$name];
  94. else
  95. $this->usageError("Option --$name requires a scalar. Array is given.");
  96. }
  97. else if($name==='args')
  98. $params[]=$args;
  99. else if($param->isDefaultValueAvailable())
  100. $params[]=$param->getDefaultValue();
  101. else
  102. $this->usageError("Missing required option --$name.");
  103. unset($options[$name]);
  104. }
  105. // try global options
  106. if(!empty($options))
  107. {
  108. $class=new ReflectionClass(get_class($this));
  109. foreach($options as $name=>$value)
  110. {
  111. if($class->hasProperty($name))
  112. {
  113. $property=$class->getProperty($name);
  114. if($property->isPublic() && !$property->isStatic())
  115. {
  116. $this->$name=$value;
  117. unset($options[$name]);
  118. }
  119. }
  120. }
  121. }
  122. if(!empty($options))
  123. $this->usageError("Unknown options: ".implode(', ',array_keys($options)));
  124. if($this->beforeAction($action,$params))
  125. {
  126. $method->invokeArgs($this,$params);
  127. $this->afterAction($action,$params);
  128. }
  129. }
  130. /**
  131. * This method is invoked right before an action is to be executed.
  132. * You may override this method to do last-minute preparation for the action.
  133. * @param string $action the action name
  134. * @param array $params the parameters to be passed to the action method.
  135. * @return boolean whether the action should be executed.
  136. */
  137. protected function beforeAction($action,$params)
  138. {
  139. return true;
  140. }
  141. /**
  142. * This method is invoked right after an action finishes execution.
  143. * You may override this method to do some postprocessing for the action.
  144. * @param string $action the action name
  145. * @param array $params the parameters to be passed to the action method.
  146. */
  147. protected function afterAction($action,$params)
  148. {
  149. }
  150. /**
  151. * Parses the command line arguments and determines which action to perform.
  152. * @param array $args command line arguments
  153. * @return array the action name, named options (name=>value), and unnamed options
  154. * @since 1.1.5
  155. */
  156. protected function resolveRequest($args)
  157. {
  158. $options=array(); // named parameters
  159. $params=array(); // unnamed parameters
  160. foreach($args as $arg)
  161. {
  162. if(preg_match('/^--(\w+)(=(.*))?$/',$arg,$matches)) // an option
  163. {
  164. $name=$matches[1];
  165. $value=isset($matches[3]) ? $matches[3] : true;
  166. if(isset($options[$name]))
  167. {
  168. if(!is_array($options[$name]))
  169. $options[$name]=array($options[$name]);
  170. $options[$name][]=$value;
  171. }
  172. else
  173. $options[$name]=$value;
  174. }
  175. else if(isset($action))
  176. $params[]=$arg;
  177. else
  178. $action=$arg;
  179. }
  180. if(!isset($action))
  181. $action=$this->defaultAction;
  182. return array($action,$options,$params);
  183. }
  184. /**
  185. * @return string the command name.
  186. */
  187. public function getName()
  188. {
  189. return $this->_name;
  190. }
  191. /**
  192. * @return CConsoleCommandRunner the command runner instance
  193. */
  194. public function getCommandRunner()
  195. {
  196. return $this->_runner;
  197. }
  198. /**
  199. * Provides the command description.
  200. * This method may be overridden to return the actual command description.
  201. * @return string the command description. Defaults to 'Usage: php entry-script.php command-name'.
  202. */
  203. public function getHelp()
  204. {
  205. $help='Usage: '.$this->getCommandRunner()->getScriptName().' '.$this->getName();
  206. $options=$this->getOptionHelp();
  207. if(empty($options))
  208. return $help;
  209. if(count($options)===1)
  210. return $help.' '.$options[0];
  211. $help.=" <action>\nActions:\n";
  212. foreach($options as $option)
  213. $help.=' '.$option."\n";
  214. return $help;
  215. }
  216. /**
  217. * Provides the command option help information.
  218. * The default implementation will return all available actions together with their
  219. * corresponding option information.
  220. * @return array the command option help information. Each array element describes
  221. * the help information for a single action.
  222. * @since 1.1.5
  223. */
  224. public function getOptionHelp()
  225. {
  226. $options=array();
  227. $class=new ReflectionClass(get_class($this));
  228. foreach($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method)
  229. {
  230. $name=$method->getName();
  231. if(!strncasecmp($name,'action',6) && strlen($name)>6)
  232. {
  233. $name=substr($name,6);
  234. $name[0]=strtolower($name[0]);
  235. $help=$name;
  236. foreach($method->getParameters() as $param)
  237. {
  238. $optional=$param->isDefaultValueAvailable();
  239. $defaultValue=$optional ? $param->getDefaultValue() : null;
  240. $name=$param->getName();
  241. if($optional)
  242. $help.=" [--$name=$defaultValue]";
  243. else
  244. $help.=" --$name=value";
  245. }
  246. $options[]=$help;
  247. }
  248. }
  249. return $options;
  250. }
  251. /**
  252. * Displays a usage error.
  253. * This method will then terminate the execution of the current application.
  254. * @param string $message the error message
  255. */
  256. public function usageError($message)
  257. {
  258. echo "Error: $message\n\n".$this->getHelp()."\n";
  259. exit(1);
  260. }
  261. /**
  262. * Copies a list of files from one place to another.
  263. * @param array $fileList the list of files to be copied (name=>spec).
  264. * The array keys are names displayed during the copy process, and array values are specifications
  265. * for files to be copied. Each array value must be an array of the following structure:
  266. * <ul>
  267. * <li>source: required, the full path of the file/directory to be copied from</li>
  268. * <li>target: required, the full path of the file/directory to be copied to</li>
  269. * <li>callback: optional, the callback to be invoked when copying a file. The callback function
  270. * should be declared as follows:
  271. * <pre>
  272. * function foo($source,$params)
  273. * </pre>
  274. * where $source parameter is the source file path, and the content returned
  275. * by the function will be saved into the target file.</li>
  276. * <li>params: optional, the parameters to be passed to the callback</li>
  277. * </ul>
  278. * @see buildFileList
  279. */
  280. public function copyFiles($fileList)
  281. {
  282. $overwriteAll=false;
  283. foreach($fileList as $name=>$file)
  284. {
  285. $source=strtr($file['source'],'/\\',DIRECTORY_SEPARATOR);
  286. $target=strtr($file['target'],'/\\',DIRECTORY_SEPARATOR);
  287. $callback=isset($file['callback']) ? $file['callback'] : null;
  288. $params=isset($file['params']) ? $file['params'] : null;
  289. if(is_dir($source))
  290. {
  291. $this->ensureDirectory($target);
  292. continue;
  293. }
  294. if($callback!==null)
  295. $content=call_user_func($callback,$source,$params);
  296. else
  297. $content=file_get_contents($source);
  298. if(is_file($target))
  299. {
  300. if($content===file_get_contents($target))
  301. {
  302. echo " unchanged $name\n";
  303. continue;
  304. }
  305. if($overwriteAll)
  306. echo " overwrite $name\n";
  307. else
  308. {
  309. echo " exist $name\n";
  310. echo " ...overwrite? [Yes|No|All|Quit] ";
  311. $answer=trim(fgets(STDIN));
  312. if(!strncasecmp($answer,'q',1))
  313. return;
  314. else if(!strncasecmp($answer,'y',1))
  315. echo " overwrite $name\n";
  316. else if(!strncasecmp($answer,'a',1))
  317. {
  318. echo " overwrite $name\n";
  319. $overwriteAll=true;
  320. }
  321. else
  322. {
  323. echo " skip $name\n";
  324. continue;
  325. }
  326. }
  327. }
  328. else
  329. {
  330. $this->ensureDirectory(dirname($target));
  331. echo " generate $name\n";
  332. }
  333. file_put_contents($target,$content);
  334. }
  335. }
  336. /**
  337. * Builds the file list of a directory.
  338. * This method traverses through the specified directory and builds
  339. * a list of files and subdirectories that the directory contains.
  340. * The result of this function can be passed to {@link copyFiles}.
  341. * @param string $sourceDir the source directory
  342. * @param string $targetDir the target directory
  343. * @param string $baseDir base directory
  344. * @return array the file list (see {@link copyFiles})
  345. */
  346. public function buildFileList($sourceDir, $targetDir, $baseDir='')
  347. {
  348. $list=array();
  349. $handle=opendir($sourceDir);
  350. while(($file=readdir($handle))!==false)
  351. {
  352. if($file==='.' || $file==='..' || $file==='.svn' ||$file==='.yii')
  353. continue;
  354. $sourcePath=$sourceDir.DIRECTORY_SEPARATOR.$file;
  355. $targetPath=$targetDir.DIRECTORY_SEPARATOR.$file;
  356. $name=$baseDir===''?$file : $baseDir.'/'.$file;
  357. $list[$name]=array('source'=>$sourcePath, 'target'=>$targetPath);
  358. if(is_dir($sourcePath))
  359. $list=array_merge($list,$this->buildFileList($sourcePath,$targetPath,$name));
  360. }
  361. closedir($handle);
  362. return $list;
  363. }
  364. /**
  365. * Creates all parent directories if they do not exist.
  366. * @param string $directory the directory to be checked
  367. */
  368. public function ensureDirectory($directory)
  369. {
  370. if(!is_dir($directory))
  371. {
  372. $this->ensureDirectory(dirname($directory));
  373. echo " mkdir ".strtr($directory,'\\','/')."\n";
  374. mkdir($directory);
  375. }
  376. }
  377. /**
  378. * Renders a view file.
  379. * @param string $_viewFile_ view file path
  380. * @param array $_data_ optional data to be extracted as local view variables
  381. * @param boolean $_return_ whether to return the rendering result instead of displaying it
  382. * @return mixed the rendering result if required. Null otherwise.
  383. */
  384. public function renderFile($_viewFile_,$_data_=null,$_return_=false)
  385. {
  386. if(is_array($_data_))
  387. extract($_data_,EXTR_PREFIX_SAME,'data');
  388. else
  389. $data=$_data_;
  390. if($_return_)
  391. {
  392. ob_start();
  393. ob_implicit_flush(false);
  394. require($_viewFile_);
  395. return ob_get_clean();
  396. }
  397. else
  398. require($_viewFile_);
  399. }
  400. /**
  401. * Converts a word to its plural form.
  402. * @param string $name the word to be pluralized
  403. * @return string the pluralized word
  404. */
  405. public function pluralize($name)
  406. {
  407. $rules=array(
  408. '/(x|ch|ss|sh|us|as|is|os)$/i' => '\1es',
  409. '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
  410. '/(m)an$/i' => '\1en',
  411. '/(child)$/i' => '\1ren',
  412. '/(r)y$/i' => '\1ies',
  413. '/s$/' => 's',
  414. );
  415. foreach($rules as $rule=>$replacement)
  416. {
  417. if(preg_match($rule,$name))
  418. return preg_replace($rule,$replacement,$name);
  419. }
  420. return $name.'s';
  421. }
  422. }