Overview

Namespaces

  • None
  • Peg
    • Cli
      • Commands
    • Config
    • Custom
      • Command
        • Action
          • Generate
          • Parse
      • CommandLine
      • Config
      • Localization
      • Utilities
    • Lib
      • Definitions
        • Element
      • Generator
      • Lexers
      • Plugins
      • Signals
        • Data
          • Definitions
          • Lexers
        • Type
  • PHP

Classes

  • Action
  • Command
  • Error
  • Option
  • OptionType
  • Parser
  • Overview
  • Namespace
  • Class
  • Tree
  • Todo
  1: <?php
  2: /**
  3:  * @author Jefferson González
  4:  * @license MIT
  5:  * @link http://github.com/peg-org/peg-custom Source code.
  6:  */
  7: 
  8: namespace Peg\Custom\CommandLine;
  9: 
 10: /**
 11:  * Class in charge of parsing the command line options
 12:  */
 13: class Parser
 14: {
 15: 
 16:     /**
 17:      * Stores the number of arguments passed on command line.
 18:      * @var integer
 19:      */
 20:     private $argument_count;
 21: 
 22:     /**
 23:      * Stores the values passed on the command line.
 24:      * @var string[]
 25:      */
 26:     private $argument_values;
 27: 
 28:     /**
 29:      * List of command line options registered on the parser.
 30:      * @var \Peg\Custom\CommandLine\Option[]
 31:      */
 32:     private $options;
 33: 
 34:     /**
 35:      * List of sub-commands registered on the parser.
 36:      * @var \Peg\Custom\CommandLine\Command[]
 37:      */
 38:     private $commands;
 39: 
 40:     /**
 41:      * Name of the main application using the command line parser, displayed
 42:      * when printing the help message.
 43:      * @var string
 44:      */
 45:     public $application_name;
 46: 
 47:     /**
 48:      * Version number of the main application using the command line parser,
 49:      * displayed when printing the help message.
 50:      * @var string
 51:      */
 52:     public $application_version;
 53: 
 54:     /**
 55:      * Description of the main application using the command line parser,
 56:      * displayed when printing the help message.
 57:      * @var string
 58:      */
 59:     public $application_description;
 60: 
 61:     /**
 62:      * Initialize the parser.
 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:      * Get array of options.
 76:      * @return \Peg\Custom\CommandLine\Option[]
 77:      */
 78:     public function GetOptions()
 79:     {
 80:         return $this->options;
 81:     }
 82: 
 83:     /**
 84:      * Get an option by its long name.
 85:      * @param string $name
 86:      * @return \Peg\Custom\CommandLine\Option|boolean
 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:      * Get array of commands
 98:      * @return \Peg\Custom\CommandLine\Command[]
 99:      */
100:     public function GetCommands()
101:     {
102:         return $this->commands;
103:     }
104: 
105:     /**
106:      * Get a command object by its name.
107:      * @param string $name
108:      * @return \Peg\Custom\CommandLine\Command|boolean
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:      * Adds a sub command to the parser.
120:      * @param \Peg\Custom\CommandLine\Command $command
121:      * @throws \Exception
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:      * Adds an option to the parser.
133:      * @param \Peg\Custom\CommandLine\Option $option
134:      * @throws \Exception
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:      * Begins the process of reading command line options and calling command
146:      * actions as needed.
147:      * @param integer $argc
148:      * @param array $argv
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:      * Generates and prints the help based on the registered commands and options.
184:      */
185:     public function PrintHelp()
186:     {
187:         // Store the len of the longest command name
188:         $max_command_len = 0;
189: 
190:         //Store the len of longest command name
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:      * Generates and prints the help based on the registered commands and options.
287:      */
288:     public function PrintVersion()
289:     {
290:         print "v" . $this->application_version . "\n";
291: 
292:         exit(0);
293:     }
294: 
295:     /**
296:      * Checks if a given name is registered as a command.
297:      * @param string $name
298:      * @return boolean
299:      */
300:     private function IsCommand($name)
301:     {
302:         return isset($this->commands[$name]);
303:     }
304: 
305:     /**
306:      * Checks if a given option exists on a given options array
307:      * @param type $name
308:      * @param \Peg\Custom\CommandLine\Option[] $options
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:      * Parses the command line options depending on a set of given options.
323:      * The given options are updated with the values assigned on the
324:      * command line.
325:      * @param \Peg\Custom\CommandLine\Option[] $options
326:      * @param \Peg\Custom\CommandLine\Command $command
327:      */
328:     private function ParseOptions(&$options, \Peg\Custom\CommandLine\Command $command = null)
329:     {
330:         // In case command doesn't has any options just copy any values passed to it
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:         // Store values passed to the command to prevent repetition
357:         $command_values = array();
358: 
359:         //Parse every option
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:             // If command passed start parsing after it.
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++; //Forward to next argument
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: }
PEG Api API documentation generated by ApiGen 2.8.0