1: <?php namespace Laravel\CLI;
2:
3: use Laravel\IoC;
4: use Laravel\Str;
5: use Laravel\Bundle;
6:
7: class Command {
8:
9: /**
10: * Run a CLI task with the given arguments.
11: *
12: * <code>
13: * // Call the migrate artisan task
14: * Command::run(array('migrate'));
15: *
16: * // Call the migrate task with some arguments
17: * Command::run(array('migrate:rollback', 'bundle-name'))
18: * </code>
19: *
20: * @param array $arguments
21: * @return void
22: */
23: public static function run($arguments = array())
24: {
25: static::validate($arguments);
26:
27: list($bundle, $task, $method) = static::parse($arguments[0]);
28:
29: // If the task exists within a bundle, we will start the bundle so that any
30: // dependencies can be registered in the application IoC container. If the
31: // task is registered in the container, we'll resolve it.
32: if (Bundle::exists($bundle))
33: {
34: Bundle::start($bundle);
35: }
36:
37: $task = static::resolve($bundle, $task);
38:
39: // Once the bundle has been resolved, we'll make sure we could actually
40: // find that task, and then verify that the method exists on the task
41: // so we can successfully call it without a problem.
42: if (is_null($task))
43: {
44: throw new \Exception("Sorry, I can't find that task.");
45: }
46:
47: if (is_callable(array($task, $method)))
48: {
49: $task->$method(array_slice($arguments, 1));
50: }
51: else
52: {
53: throw new \Exception("Sorry, I can't find that method!");
54: }
55: }
56:
57: /**
58: * Determine if the given command arguments are valid.
59: *
60: * @param array $arguments
61: * @return void
62: */
63: protected static function validate($arguments)
64: {
65: if ( ! isset($arguments[0]))
66: {
67: throw new \Exception("You forgot to provide the task name.");
68: }
69: }
70:
71: /**
72: * Parse the task name to extract the bundle, task, and method.
73: *
74: * @param string $task
75: * @return array
76: */
77: protected static function parse($task)
78: {
79: list($bundle, $task) = Bundle::parse($task);
80:
81: // Extract the task method from the task string. Methods are called
82: // on tasks by separating the task and method with a single colon.
83: // If no task is specified, "run" is used as the default.
84: if (str_contains($task, ':'))
85: {
86: list($task, $method) = explode(':', $task);
87: }
88: else
89: {
90: $method = 'run';
91: }
92:
93: return array($bundle, $task, $method);
94: }
95:
96: /**
97: * Resolve an instance of the given task name.
98: *
99: * <code>
100: * // Resolve an instance of a task
101: * $task = Command::resolve('application', 'migrate');
102: *
103: * // Resolve an instance of a task within a bundle
104: * $task = Command::resolve('bundle', 'foo');
105: * </code>
106: *
107: * @param string $bundle
108: * @param string $task
109: * @return object
110: */
111: public static function resolve($bundle, $task)
112: {
113: $identifier = Bundle::identifier($bundle, $task);
114:
115: // First we'll check to see if the task has been registered in the
116: // application IoC container. This allows all dependencies to be
117: // injected into tasks for more flexible testability.
118: if (IoC::registered("task: {$identifier}"))
119: {
120: return IoC::resolve("task: {$identifier}");
121: }
122:
123: // If the task file exists, we'll format the bundle and task name
124: // into a task class name and resolve an instance of the class so that
125: // the requested method may be executed.
126: if (file_exists($path = Bundle::path($bundle).'tasks/'.$task.EXT))
127: {
128: require_once $path;
129:
130: $task = static::format($bundle, $task);
131:
132: return new $task;
133: }
134: }
135:
136: /**
137: * Parse the command line arguments and return the results.
138: *
139: * @param array $argv
140: * @return array
141: */
142: public static function options($argv)
143: {
144: $options = array();
145:
146: $arguments = array();
147:
148: for ($i = 0, $count = count($argv); $i < $count; $i++)
149: {
150: $argument = $argv[$i];
151:
152: // If the CLI argument starts with a double hyphen, it is an option,
153: // so we will extract the value and add it to the array of options
154: // to be returned by the method.
155: if (starts_with($argument, '--'))
156: {
157: // By default, we will assume the value of the options is true,
158: // but if the option contains an equals sign, we will take the
159: // value to the right of the equals sign as the value and
160: // remove the value from the option key.
161: list($key, $value) = array(substr($argument, 2), true);
162:
163: if (($equals = strpos($argument, '=')) !== false)
164: {
165: $key = substr($argument, 2, $equals - 2);
166:
167: $value = substr($argument, $equals + 1);
168: }
169:
170: $options[$key] = $value;
171: }
172: // If the CLI argument does not start with a double hyphen it's
173: // simply an argument to be passed to the console task so we'll
174: // add it to the array of "regular" arguments.
175: else
176: {
177: $arguments[] = $argument;
178: }
179: }
180:
181: return array($arguments, $options);
182: }
183:
184: /**
185: * Format a bundle and task into a task class name.
186: *
187: * @param string $bundle
188: * @param string $task
189: * @return string
190: */
191: protected static function format($bundle, $task)
192: {
193: $prefix = Bundle::class_prefix($bundle);
194:
195: return '\\'.$prefix.Str::classify($task).'_Task';
196: }
197:
198: }
199: