1: <?php
2: 3: 4: 5: 6:
7:
8: namespace Peg\Custom\CommandLine;
9:
10: 11: 12:
13: class Parser
14: {
15:
16: 17: 18: 19:
20: private $argument_count;
21:
22: 23: 24: 25:
26: private $argument_values;
27:
28: 29: 30: 31:
32: private $options;
33:
34: 35: 36: 37:
38: private $commands;
39:
40: 41: 42: 43: 44:
45: public $application_name;
46:
47: 48: 49: 50: 51:
52: public $application_version;
53:
54: 55: 56: 57: 58:
59: public $application_description;
60:
61: 62: 63:
64: public function __construct()
65: {
66: $this->options = array();
67: $this->commands = array();
68:
69: $this->application_name = t("Untitled");
70: $this->application_version = "0.1";
71: $this->application_description = t("Untitled application description.");
72: }
73:
74: 75: 76: 77:
78: public function GetOptions()
79: {
80: return $this->options;
81: }
82:
83: 84: 85: 86: 87:
88: public function GetOption($name)
89: {
90: if(isset($this->options[$name]))
91: return $this->options[$name];
92:
93: return false;
94: }
95:
96: 97: 98: 99:
100: public function GetCommands()
101: {
102: return $this->commands;
103: }
104:
105: 106: 107: 108: 109:
110: public function GetCommand($name)
111: {
112: if(isset($this->commands[$name]))
113: return $this->commands[$name];
114:
115: return false;
116: }
117:
118: 119: 120: 121: 122:
123: public function RegisterCommand(Command $command)
124: {
125: if(!isset($this->commands[$command->name]))
126: $this->commands[$command->name] = $command;
127: else
128: throw new \Exception(t("Command") . " '{$command->name}' " . t("is already registered."));
129: }
130:
131: 132: 133: 134: 135:
136: public function RegisterOption(Option $option)
137: {
138: if(!isset($this->options[$option->long_name]))
139: $this->options[$option->long_name] = $option;
140: else
141: throw new \Exception(t("Option") . " '{$option->long_name}' " . t("is already registered."));
142: }
143:
144: 145: 146: 147: 148: 149:
150: public function Start($argc, $argv)
151: {
152: $this->argument_count = $argc;
153: $this->argument_values = $argv;
154:
155: if(
156: $this->argument_count <= 1 ||
157: in_array("--help", $this->argument_values)
158: )
159: {
160: $this->PrintHelp();
161: }
162:
163: if(in_array("--version", $this->argument_values))
164: {
165: $this->PrintVersion();
166: }
167:
168: if($this->IsCommand($this->argument_values[1]))
169: {
170: $command = $this->commands[$this->argument_values[1]];
171: $this->ParseOptions($command->options, $command);
172: $command->Execute();
173:
174: return;
175: }
176: else
177: {
178: $this->ParseOptions($this->options);
179: }
180: }
181:
182: 183: 184:
185: public function PrintHelp()
186: {
187:
188: $max_command_len = 0;
189:
190:
191: $max_option_len = 0;
192:
193: print $this->application_name . " v" . $this->application_version . "\n";
194: print t($this->application_description) . "\n\n";
195:
196: print t("Usage:") . "\n";
197: print " " . $this->application_name . " " . t("[options]") . "\n";
198:
199: if(count($this->commands) > 0)
200: {
201: foreach($this->commands as $command)
202: {
203: if(strlen($command->name) > $max_command_len)
204: $max_command_len = strlen($command->name);
205:
206: if(count($command->options) > 0)
207: {
208: foreach($command->options as $option)
209: {
210: if(strlen($option->long_name) > $max_option_len)
211: $max_option_len = strlen($option->long_name);
212: }
213: }
214: }
215:
216: print " " . $this->application_name . " <command> " . t("[options]") . "\n";
217: }
218:
219: if(count($this->commands) > 0)
220: {
221: print "\n";
222: print t("Commands:") . "\n";
223:
224: print str_pad(" ", 80, "-", STR_PAD_RIGHT);
225: print "\n\n";
226:
227: foreach($this->commands as $command)
228: {
229: $line = " " . str_pad($command->name, $max_command_len + 2) . t($command->description);
230: $line = wordwrap($line, 80);
231: $line_array = explode("\n", $line);
232:
233: print $line_array[0] . "\n";
234: unset($line_array[0]);
235:
236: if(count($line_array) > 0)
237: {
238: foreach($line_array as $line)
239: {
240: print str_pad($line, strlen($line) + ($max_command_len + 4), " ", STR_PAD_LEFT) . "\n";
241: }
242: }
243:
244: if(count($command->options) > 0)
245: {
246: print "\n";
247: print " " . t("Options:") . "\n";
248: foreach($command->options as $option)
249: {
250: $line = " " .
251: str_pad(
252: "-" . $option->short_name . " --" . $option->long_name, $max_option_len + 8
253: ) .
254: t($option->description)
255: ;
256:
257: $line = wordwrap($line, 80);
258: $line_array = explode("\n", $line);
259:
260: print $line_array[0] . "\n";
261: unset($line_array[0]);
262:
263: if(count($line_array) > 0)
264: {
265: foreach($line_array as $line)
266: {
267: print str_pad($line, strlen($line) + ($max_option_len + 14), " ", STR_PAD_LEFT) . "\n";
268: }
269: }
270: }
271: }
272:
273: print "\n";
274:
275: print str_pad(" ", 80, "-", STR_PAD_RIGHT);
276:
277: print "\n\n";
278: }
279: }
280:
281:
282: exit(0);
283: }
284:
285: 286: 287:
288: public function PrintVersion()
289: {
290: print "v" . $this->application_version . "\n";
291:
292: exit(0);
293: }
294:
295: 296: 297: 298: 299:
300: private function IsCommand($name)
301: {
302: return isset($this->commands[$name]);
303: }
304:
305: 306: 307: 308: 309:
310: private function OptionExists($name, $options)
311: {
312: foreach($options as $option)
313: {
314: if($option->long_name == $name || $option->short_name == $name)
315: return true;
316: }
317:
318: return false;
319: }
320:
321: 322: 323: 324: 325: 326: 327:
328: private function ParseOptions(&$options, \Peg\Custom\CommandLine\Command $command = null)
329: {
330:
331: if($command)
332: {
333: if(count($command->options) <= 0)
334: {
335: $argi = 2;
336:
337: for($argi; $argi < $this->argument_count; $argi++)
338: {
339: $argument = $this->argument_values[$argi];
340:
341: if(ltrim($argument, "-") == $argument)
342: {
343: $command->value = trim($command->value . " " . $argument);
344: continue;
345: }
346: else
347: {
348: Error::Show(t("Invalid parameter") . " '$argument'");
349: }
350: }
351:
352: return;
353: }
354: }
355:
356:
357: $command_values = array();
358:
359:
360: foreach($options as $index => $option)
361: {
362: if($option->required)
363: {
364: if(
365: !in_array("--" . $option->long_name, $this->argument_values) &&
366: !in_array("-" . $option->short_name, $this->argument_values)
367: )
368: Error::Show(t("Missing required option") . " '--{$option->long_name}'");
369: }
370:
371: $argi = 1;
372:
373:
374: if($command)
375: $argi = 2;
376:
377: for($argi; $argi < $this->argument_count; $argi++)
378: {
379: $argument_original = $this->argument_values[$argi];
380: $argument = $argument_original;
381: $argument_next = "";
382:
383: if($argi + 1 < $this->argument_count)
384: {
385: $argument_next = $this->argument_values[$argi + 1];
386: }
387:
388: if(ltrim($argument, "-") != $argument)
389: {
390: $argument = ltrim($argument, "-");
391:
392: if($this->OptionExists($argument, $options))
393: {
394: if(
395: $argument == $option->long_name ||
396: $argument == $option->short_name
397: )
398: {
399: switch($option->type)
400: {
401: case OptionType::FLAG:
402: $option->active = true;
403: break;
404:
405: default:
406: if($option->SetValue($argument_next))
407: $argi++;
408: }
409:
410: if($option->IsValid())
411: {
412: $options[$index] = $option;
413: }
414: else
415: {
416: Error::Show(t("Invalid value supplied for") . " '$argument_original'");
417: }
418: }
419: }
420: elseif(!$this->IsCommand($argument))
421: {
422: Error::Show(t("Invalid parameter") . " '$argument_original'");
423: }
424: }
425: else
426: {
427: if($command)
428: {
429: $previous_argument = "";
430:
431: if(isset($this->argument_values[$argi - 1]))
432: $previous_argument = $this->argument_values[$argi - 1];
433:
434: $previous_command_is_flag = false;
435:
436: if(ltrim($previous_argument, "-") != $previous_argument)
437: {
438: if($previous_command = $command->GetOption(ltrim($previous_argument, "-")))
439: {
440: if($previous_command->type == OptionType::FLAG)
441: {
442: $previous_command_is_flag = true;
443: }
444: }
445: }
446:
447: if(
448: (
449: ltrim($argument, "-") == $argument &&
450: ltrim($previous_argument, "-") == $previous_argument
451: ) ||
452: $previous_command_is_flag
453: )
454: {
455: if(!in_array($argument, $command_values))
456: {
457: $command_values[] = $argument;
458: $command->value = trim($command->value . " " . $argument);
459: continue;
460: }
461: }
462: }
463: }
464: }
465: }
466: }
467:
468: }