sfTask.class.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * Abstract class for all tasks.
  11. *
  12. * @package symfony
  13. * @subpackage task
  14. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  15. * @version SVN: $Id: sfTask.class.php 12533 2008-11-01 13:33:00Z Kris.Wallsmith $
  16. */
  17. abstract class sfTask
  18. {
  19. protected
  20. $namespace = '',
  21. $name = null,
  22. $aliases = array(),
  23. $briefDescription = '',
  24. $detailedDescription = '',
  25. $arguments = array(),
  26. $options = array(),
  27. $dispatcher = null,
  28. $formatter = null;
  29. /**
  30. * Constructor.
  31. *
  32. * @param sfEventDispatcher $dispatcher An sfEventDispatcher instance
  33. * @param sfFormatter $formatter An sfFormatter instance
  34. */
  35. public function __construct(sfEventDispatcher $dispatcher, sfFormatter $formatter)
  36. {
  37. $this->initialize($dispatcher, $formatter);
  38. $this->configure();
  39. }
  40. /**
  41. * Initializes the sfTask instance.
  42. *
  43. * @param sfEventDispatcher $dispatcher A sfEventDispatcher instance
  44. * @param sfFormatter $formatter A sfFormatter instance
  45. */
  46. public function initialize(sfEventDispatcher $dispatcher, sfFormatter $formatter)
  47. {
  48. $this->dispatcher = $dispatcher;
  49. $this->formatter = $formatter;
  50. }
  51. /**
  52. * Configures the current task.
  53. */
  54. protected function configure()
  55. {
  56. }
  57. /**
  58. * Runs the task from the CLI.
  59. *
  60. * @param sfCommandManager $commandManager An sfCommandManager instance
  61. * @param mixed $options The command line options
  62. */
  63. public function runFromCLI(sfCommandManager $commandManager, $options = null)
  64. {
  65. $commandManager->getArgumentSet()->addArguments($this->getArguments());
  66. $commandManager->getOptionSet()->addOptions($this->getOptions());
  67. return $this->doRun($commandManager, $options);
  68. }
  69. /**
  70. * Runs the task.
  71. *
  72. * @param array $arguments An array of arguments
  73. * @param array $options An array of options
  74. */
  75. public function run($arguments = array(), $options = array())
  76. {
  77. $commandManager = new sfCommandManager(new sfCommandArgumentSet($this->getArguments()), new sfCommandOptionSet($this->getOptions()));
  78. // add -- before each option if needed
  79. foreach ($options as &$option)
  80. {
  81. if (0 !== strpos($option, '--'))
  82. {
  83. $option = '--'.$option;
  84. }
  85. }
  86. return $this->doRun($commandManager, implode(' ', array_merge($arguments, $options)));
  87. }
  88. /**
  89. * Returns the argument objects.
  90. *
  91. * @return sfCommandArgument An array of sfCommandArgument objects.
  92. */
  93. public function getArguments()
  94. {
  95. return $this->arguments;
  96. }
  97. /**
  98. * Adds an array of argument objects.
  99. *
  100. * @param array $arguments An array of arguments
  101. */
  102. public function addArguments($arguments)
  103. {
  104. $this->arguments = array_merge($this->arguments, $arguments);
  105. }
  106. /**
  107. * Add an argument.
  108. *
  109. * This method always use the sfCommandArgument class to create an option.
  110. *
  111. * @see sfCommandArgument::__construct()
  112. */
  113. public function addArgument($name, $mode = null, $help = '', $default = null)
  114. {
  115. $this->arguments[] = new sfCommandArgument($name, $mode, $help, $default);
  116. }
  117. /**
  118. * Returns the options objects.
  119. *
  120. * @return sfCommandOption An array of sfCommandOption objects.
  121. */
  122. public function getOptions()
  123. {
  124. return $this->options;
  125. }
  126. /**
  127. * Adds an array of option objects.
  128. *
  129. * @param array $options An array of options
  130. */
  131. public function addOptions($options)
  132. {
  133. $this->options = array_merge($this->options, $options);
  134. }
  135. /**
  136. * Add an option.
  137. *
  138. * This method always use the sfCommandOption class to create an option.
  139. *
  140. * @see sfCommandOption::__construct()
  141. */
  142. public function addOption($name, $shortcut = null, $mode = null, $help = '', $default = null)
  143. {
  144. $this->options[] = new sfCommandOption($name, $shortcut, $mode, $help, $default);
  145. }
  146. /**
  147. * Returns the task namespace.
  148. *
  149. * @param string The task namespace
  150. */
  151. public function getNamespace()
  152. {
  153. return $this->namespace;
  154. }
  155. /**
  156. * Returns the task name
  157. *
  158. * @return string The task name
  159. */
  160. public function getName()
  161. {
  162. if ($this->name)
  163. {
  164. return $this->name;
  165. }
  166. $name = get_class($this);
  167. if ('sf' == substr($name, 0, 2))
  168. {
  169. $name = substr($name, 2);
  170. }
  171. if ('Task' == substr($name, -4))
  172. {
  173. $name = substr($name, 0, -4);
  174. }
  175. return str_replace('_', '-', sfInflector::underscore($name));
  176. }
  177. /**
  178. * Returns the fully qualified task name.
  179. *
  180. * @return string The fully qualified task name
  181. */
  182. final function getFullName()
  183. {
  184. return $this->getNamespace() ? $this->getNamespace().':'.$this->getName() : $this->getName();
  185. }
  186. /**
  187. * Returns the brief description for the task.
  188. *
  189. * @return string The brief description for the task
  190. */
  191. public function getBriefDescription()
  192. {
  193. return $this->briefDescription;
  194. }
  195. /**
  196. * Returns the detailed description for the task.
  197. *
  198. * It also formats special string like [...|COMMENT]
  199. * depending on the current formatter.
  200. *
  201. * @return string The detailed description for the task
  202. */
  203. public function getDetailedDescription()
  204. {
  205. return preg_replace('/\[(.+?)\|(\w+)\]/se', '$this->formatter->format("$1", "$2")', $this->detailedDescription);
  206. }
  207. /**
  208. * Returns the aliases for the task.
  209. *
  210. * @return array An array of aliases for the task
  211. */
  212. public function getAliases()
  213. {
  214. return $this->aliases;
  215. }
  216. /**
  217. * Returns the synopsis for the task.
  218. *
  219. * @param string The synopsis
  220. */
  221. public function getSynopsis()
  222. {
  223. $options = array();
  224. foreach ($this->getOptions() as $option)
  225. {
  226. $shortcut = $option->getShortcut() ? sprintf('|-%s', $option->getShortcut()) : '';
  227. $options[] = sprintf('['.($option->isParameterRequired() ? '--%s%s="..."' : ($option->isParameterOptional() ? '--%s%s[="..."]' : '--%s%s')).']', $option->getName(), $shortcut);
  228. }
  229. $arguments = array();
  230. foreach ($this->getArguments() as $argument)
  231. {
  232. $arguments[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : ''));
  233. if ($argument->isArray())
  234. {
  235. $arguments[] = '... [nameN]';
  236. }
  237. }
  238. return sprintf('%%s %s %s %s', $this->getFullName(), implode(' ', $options), implode(' ', $arguments));
  239. }
  240. protected function process(sfCommandManager $commandManager, $options)
  241. {
  242. $commandManager->process($options);
  243. if (!$commandManager->isValid())
  244. {
  245. throw new sfCommandArgumentsException(sprintf("The execution of task \"%s\" failed.\n- %s", $this->getFullName(), implode("\n- ", $commandManager->getErrors())));
  246. }
  247. }
  248. protected function doRun(sfCommandManager $commandManager, $options)
  249. {
  250. $this->dispatcher->filter(new sfEvent($this, 'command.filter_options', array('command_manager' => $commandManager)), $options);
  251. $this->process($commandManager, $options);
  252. $event = new sfEvent($this, 'command.pre_command', array('arguments' => $commandManager->getArgumentValues(), 'options' => $commandManager->getOptionValues()));
  253. $this->dispatcher->notifyUntil($event);
  254. if ($event->isProcessed())
  255. {
  256. return $event->getReturnValue();
  257. }
  258. $ret = $this->execute($commandManager->getArgumentValues(), $commandManager->getOptionValues());
  259. $this->dispatcher->notify(new sfEvent($this, 'command.post_command'));
  260. return $ret;
  261. }
  262. /**
  263. * Logs a message.
  264. *
  265. * @param mixed $messages The message as an array of lines of a single string
  266. */
  267. public function log($messages)
  268. {
  269. if (!is_array($messages))
  270. {
  271. $messages = array($messages);
  272. }
  273. $this->dispatcher->notify(new sfEvent($this, 'command.log', $messages));
  274. }
  275. /**
  276. * Logs a message in a section.
  277. *
  278. * @param string $section The section name
  279. * @param string $message The message
  280. * @param int $size The maximum size of a line
  281. * @param string $style The color scheme to apply to the section string (INFO, ERROR, or COMMAND)
  282. */
  283. public function logSection($section, $message, $size = null, $style = 'INFO')
  284. {
  285. $this->dispatcher->notify(new sfEvent($this, 'command.log', array($this->formatter->formatSection($section, $message, $size, $style))));
  286. }
  287. /**
  288. * Logs a message as a block of text.
  289. *
  290. * @param string|array $message The message to display in the block
  291. * @param string $style The style to use
  292. */
  293. public function logBlock($messages, $style)
  294. {
  295. if (!is_array($messages))
  296. {
  297. $messages = array($messages);
  298. }
  299. $len = 0;
  300. $lines = array();
  301. foreach ($messages as $message)
  302. {
  303. $lines[] = sprintf(' %s ', $message);
  304. $len = max($this->strlen($message) + 4, $len);
  305. }
  306. $messages = array(str_repeat(' ', $len));
  307. foreach ($lines as $line)
  308. {
  309. $messages[] = $line.str_repeat(' ', $len - $this->strlen($line));
  310. }
  311. $messages[] = str_repeat(' ', $len);
  312. foreach ($messages as $message)
  313. {
  314. $this->log($this->formatter->format($message, $style));
  315. }
  316. }
  317. /**
  318. * Asks a question to the user.
  319. *
  320. * @param string|array $question The question to ask
  321. * @param string $style The style to use (QUESTION by default)
  322. *
  323. * @param string The user answer
  324. */
  325. public function ask($question, $style = 'QUESTION')
  326. {
  327. if (false === $style)
  328. {
  329. $this->log($question);
  330. }
  331. else
  332. {
  333. $this->logBlock($question, is_null($style) ? 'QUESTION' : $style);
  334. }
  335. return trim(fgets(STDIN));
  336. }
  337. /**
  338. * Asks a confirmation to the user.
  339. *
  340. * The question will be asked until the user answer by nothing, yes, or no.
  341. *
  342. * @param string|array $question The question to ask
  343. * @param string $style The style to use (QUESTION by default)
  344. * @param Boolean $default The default answer if the user enters nothing
  345. *
  346. * @param Boolean true if the user has confirmed, false otherwise
  347. */
  348. public function askConfirmation($question, $style = 'QUESTION', $default = true)
  349. {
  350. $answer = 'z';
  351. while ($answer && !in_array(strtolower($answer[0]), array('y', 'n')))
  352. {
  353. $answer = $this->ask($question, $style);
  354. }
  355. if (false === $default)
  356. {
  357. return $answer && 'y' == strtolower($answer[0]);
  358. }
  359. else
  360. {
  361. return !$answer || 'y' == strtolower($answer[0]);
  362. }
  363. }
  364. /**
  365. * Executes the current task.
  366. *
  367. * @param array $arguments An array of arguments
  368. * @param array $options An array of options
  369. */
  370. abstract protected function execute($arguments = array(), $options = array());
  371. protected function strlen($string)
  372. {
  373. return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string);
  374. }
  375. }