PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/xandra.org/www/modules/minion/classes/Kohana/Minion/Task.php

https://bitbucket.org/ekkl/tanora
PHP | 364 lines | 181 code | 54 blank | 129 comment | 15 complexity | e2b283e9c3637a49c76e4079df52de0c MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Interface that all minion tasks must implement
  4. *
  5. * @package Kohana
  6. * @category Minion
  7. * @author Kohana Team
  8. * @copyright (c) 2009-2011 Kohana Team
  9. * @license http://kohanaframework.org/license
  10. */
  11. abstract class Kohana_Minion_Task {
  12. /**
  13. * The separator used to separate different levels of tasks
  14. * @var string
  15. */
  16. public static $task_separator = ':';
  17. /**
  18. * Converts a task (e.g. db:migrate to a class name)
  19. *
  20. * @param string Task name
  21. * @return string Class name
  22. */
  23. public static function convert_task_to_class_name($task)
  24. {
  25. $task = trim($task);
  26. if (empty($task))
  27. return '';
  28. return 'Task_'.implode('_', array_map('ucfirst', explode(Minion_Task::$task_separator, $task)));
  29. }
  30. /**
  31. * Gets the task name of a task class / task object
  32. *
  33. * @param string|Minion_Task The task class / object
  34. * @return string The task name
  35. */
  36. public static function convert_class_to_task($class)
  37. {
  38. if (is_object($class))
  39. {
  40. $class = get_class($class);
  41. }
  42. return strtolower(str_replace('_', Minion_Task::$task_separator, substr($class, 5)));
  43. }
  44. /**
  45. * Factory for loading minion tasks
  46. *
  47. * @param array An array of command line options. It should contain the 'task' key
  48. * @throws Minion_Exception_InvalidTask
  49. * @return Minion_Task The Minion task
  50. */
  51. public static function factory($options)
  52. {
  53. if (($task = Arr::get($options, 'task')) !== NULL)
  54. {
  55. unset($options['task']);
  56. }
  57. else if (($task = Arr::get($options, 0)) !== NULL)
  58. {
  59. // The first positional argument (aka 0) may be the task name
  60. unset($options[0]);
  61. }
  62. else
  63. {
  64. // If we didn't get a valid task, generate the help
  65. $task = 'help';
  66. }
  67. $class = Minion_Task::convert_task_to_class_name($task);
  68. if ( ! class_exists($class))
  69. {
  70. throw new Minion_Exception_InvalidTask(
  71. "Task ':task' is not a valid minion task",
  72. array(':task' => $class)
  73. );
  74. }
  75. $class = new $class;
  76. if ( ! $class instanceof Minion_Task)
  77. {
  78. throw new Minion_Exception_InvalidTask(
  79. "Task ':task' is not a valid minion task",
  80. array(':task' => $class)
  81. );
  82. }
  83. $class->set_options($options);
  84. // Show the help page for this task if requested
  85. if (array_key_exists('help', $options))
  86. {
  87. $class->_method = '_help';
  88. }
  89. return $class;
  90. }
  91. /**
  92. * The list of options this task accepts and their default values.
  93. *
  94. * protected $_options = array(
  95. * 'limit' => 4,
  96. * 'table' => NULL,
  97. * );
  98. *
  99. * @var array
  100. */
  101. protected $_options = array();
  102. /**
  103. * Populated with the accepted options for this task.
  104. * This array is automatically populated based on $_options.
  105. *
  106. * @var array
  107. */
  108. protected $_accepted_options = array();
  109. protected $_method = '_execute';
  110. protected function __construct()
  111. {
  112. // Populate $_accepted_options based on keys from $_options
  113. $this->_accepted_options = array_keys($this->_options);
  114. }
  115. /**
  116. * The file that get's passes to Validation::errors() when validation fails
  117. * @var string|NULL
  118. */
  119. protected $_errors_file = 'validation';
  120. /**
  121. * Gets the task name for the task
  122. *
  123. * @return string
  124. */
  125. public function __toString()
  126. {
  127. static $task_name = NULL;
  128. if ($task_name === NULL)
  129. {
  130. $task_name = Minion_Task::convert_class_to_task($this);
  131. }
  132. return $task_name;
  133. }
  134. /**
  135. * Sets options for this task
  136. *
  137. * $param array the array of options to set
  138. * @return this
  139. */
  140. public function set_options(array $options)
  141. {
  142. foreach ($options as $key => $value)
  143. {
  144. $this->_options[$key] = $value;
  145. }
  146. return $this;
  147. }
  148. /**
  149. * Get the options that were passed into this task with their defaults
  150. *
  151. * @return array
  152. */
  153. public function get_options()
  154. {
  155. return (array) $this->_options;
  156. }
  157. /**
  158. * Get a set of options that this task can accept
  159. *
  160. * @return array
  161. */
  162. public function get_accepted_options()
  163. {
  164. return (array) $this->_accepted_options;
  165. }
  166. /**
  167. * Adds any validation rules/labels for validating _options
  168. *
  169. * public function build_validation(Validation $validation)
  170. * {
  171. * return parent::build_validation($validation)
  172. * ->rule('paramname', 'not_empty'); // Require this param
  173. * }
  174. *
  175. * @param Validation the validation object to add rules to
  176. *
  177. * @return Validation
  178. */
  179. public function build_validation(Validation $validation)
  180. {
  181. // Add a rule to each key making sure it's in the task
  182. foreach ($validation->as_array() as $key => $value)
  183. {
  184. $validation->rule($key, array($this, 'valid_option'), array(':validation', ':field'));
  185. }
  186. return $validation;
  187. }
  188. /**
  189. * Returns $_errors_file
  190. *
  191. * @return string
  192. */
  193. public function get_errors_file()
  194. {
  195. return $this->_errors_file;
  196. }
  197. /**
  198. * Execute the task with the specified set of options
  199. *
  200. * @return null
  201. */
  202. public function execute()
  203. {
  204. $options = $this->get_options();
  205. // Validate $options
  206. $validation = Validation::factory($options);
  207. $validation = $this->build_validation($validation);
  208. if ( $this->_method != '_help' AND ! $validation->check())
  209. {
  210. echo View::factory('minion/error/validation')
  211. ->set('task', Minion_Task::convert_class_to_task($this))
  212. ->set('errors', $validation->errors($this->get_errors_file()));
  213. }
  214. else
  215. {
  216. // Finally, run the task
  217. $method = $this->_method;
  218. echo $this->{$method}($options);
  219. }
  220. }
  221. abstract protected function _execute(array $params);
  222. /**
  223. * Outputs help for this task
  224. *
  225. * @return null
  226. */
  227. protected function _help(array $params)
  228. {
  229. $tasks = $this->_compile_task_list(Kohana::list_files('classes/task'));
  230. $inspector = new ReflectionClass($this);
  231. list($description, $tags) = $this->_parse_doccomment($inspector->getDocComment());
  232. $view = View::factory('minion/help/task')
  233. ->set('description', $description)
  234. ->set('tags', (array) $tags)
  235. ->set('task', Minion_Task::convert_class_to_task($this));
  236. echo $view;
  237. }
  238. public function valid_option(Validation $validation, $option)
  239. {
  240. if ( ! in_array($option, $this->_accepted_options))
  241. {
  242. $validation->error($option, 'minion_option');
  243. }
  244. }
  245. /**
  246. * Parses a doccomment, extracting both the comment and any tags associated
  247. *
  248. * Based on the code in Kodoc::parse()
  249. *
  250. * @param string The comment to parse
  251. * @return array First element is the comment, second is an array of tags
  252. */
  253. protected function _parse_doccomment($comment)
  254. {
  255. // Normalize all new lines to \n
  256. $comment = str_replace(array("\r\n", "\n"), "\n", $comment);
  257. // Remove the phpdoc open/close tags and split
  258. $comment = array_slice(explode("\n", $comment), 1, -1);
  259. // Tag content
  260. $tags = array();
  261. foreach ($comment as $i => $line)
  262. {
  263. // Remove all leading whitespace
  264. $line = preg_replace('/^\s*\* ?/m', '', $line);
  265. // Search this line for a tag
  266. if (preg_match('/^@(\S+)(?:\s*(.+))?$/', $line, $matches))
  267. {
  268. // This is a tag line
  269. unset($comment[$i]);
  270. $name = $matches[1];
  271. $text = isset($matches[2]) ? $matches[2] : '';
  272. $tags[$name] = $text;
  273. }
  274. else
  275. {
  276. $comment[$i] = (string) $line;
  277. }
  278. }
  279. $comment = trim(implode("\n", $comment));
  280. return array($comment, $tags);
  281. }
  282. /**
  283. * Compiles a list of available tasks from a directory structure
  284. *
  285. * @param array Directory structure of tasks
  286. * @param string prefix
  287. * @return array Compiled tasks
  288. */
  289. protected function _compile_task_list(array $files, $prefix = '')
  290. {
  291. $output = array();
  292. foreach ($files as $file => $path)
  293. {
  294. $file = substr($file, strrpos($file, DIRECTORY_SEPARATOR) + 1);
  295. if (is_array($path) AND count($path))
  296. {
  297. $task = $this->_compile_task_list($path, $prefix.$file.Minion_Task::$task_separator);
  298. if ($task)
  299. {
  300. $output = array_merge($output, $task);
  301. }
  302. }
  303. else
  304. {
  305. $output[] = strtolower($prefix.substr($file, 0, -strlen(EXT)));
  306. }
  307. }
  308. return $output;
  309. }
  310. }