Overview

Namespaces

  • PHP
  • PhpOptions
    • Types

Classes

  • Arguments
  • Option
  • Options

Exceptions

  • InvalidArgumentException
  • LogicException
  • UserBadCallException
  • Overview
  • Namespace
  • Class
  • Tree
  • Todo
  1: <?php
  2: 
  3: /**
  4:  * PhpOptions
  5:  * @link https://github.com/masicek/PhpOptions
  6:  * @author Viktor Mašíček <viktor@masicek.net>
  7:  * @license "New" BSD License
  8:  */
  9: 
 10: namespace PhpOptions;
 11: 
 12: require_once __DIR__ . '/Exceptions.php';
 13: require_once __DIR__ . '/Arguments.php';
 14: require_once __DIR__ . '/Types/Types.php';
 15: 
 16: /**
 17:  * Class for bettwer work with one PHP comman-line option
 18:  *
 19:  * @author Viktor Mašíček <viktor@masicek.net>
 20:  */
 21: class Option
 22: {
 23: 
 24:     /**
 25:      * Type of value requirement - no value
 26:      */
 27:     const VALUE_NO = 'value_no';
 28: 
 29:     /**
 30:      * Type of value requirement - require value
 31:      */
 32:     const VALUE_REQUIRE = 'value_require';
 33: 
 34:     /**
 35:      * Type of value requirement - optional value
 36:      */
 37:     const VALUE_OPTIONAL = 'value_optional';
 38: 
 39:     /**
 40:      * Length of indent in printed help
 41:      */
 42:     const HELP_INDENT_LENGTH = 4;
 43: 
 44: 
 45:     /**
 46:      * Object for making types objects
 47:      *
 48:      * @var Types
 49:      */
 50:     private static $types = NULL;
 51: 
 52:     /**
 53:      * Name of option
 54:      *
 55:      * @var string
 56:      */
 57:     private $name;
 58: 
 59:     /**
 60:      * Type of expected value
 61:      *
 62:      * @var AType
 63:      */
 64:     private $type = NULL;
 65: 
 66:     /**
 67:      * Value of options set in command-line
 68:      *
 69:      * @var mixed
 70:      */
 71:     private $value = NULL;
 72: 
 73:     /**
 74:      * Short variant of option in command-line
 75:      *
 76:      * @var string
 77:      */
 78:     private $short;
 79: 
 80:     /**
 81:      * Long variant of option in command-line
 82:      *
 83:      * @var string
 84:      */
 85:     private $long;
 86: 
 87:     /**
 88:      * Defualt value of option
 89:      *
 90:      * @var string
 91:      */
 92:     private $defaults = NULL;
 93: 
 94:     /**
 95:      * Flag of requirements of option
 96:      *
 97:      * @var bool
 98:      */
 99:     private $required = FALSE;
100: 
101:     /**
102:      * Flag of requirement of option value
103:      *
104:      * @var string
105:      */
106:     private $valueRequired = self::VALUE_NO;
107: 
108:     /**
109:      * Desription used in help generated for option
110:      *
111:      * @var string
112:      */
113:     private $description = '';
114: 
115:     /**
116:      * Array of needed options by this option
117:      *
118:      * @var array of Option
119:      */
120:     private $needed = array();
121: 
122: 
123:     // ---- MAKE NEW OPTION ----
124: 
125: 
126:     /**
127:      * Set name of option and default short and long variant in command-line
128:      * @todo accept array as second parameere for setting all object
129:      *
130:      * @param string $name Name of option
131:      *
132:      * @throws InvalidArgumentException Name of option cannot be empty.
133:      */
134:     public function __construct($name)
135:     {
136:         if (!$name)
137:         {
138:             throw new InvalidArgumentException('Name of option cannot be empty.');
139:         }
140: 
141:         $this->name = $name;
142: 
143:         $long = strtolower($name);
144:         $long = preg_replace('/[^a-z0-9]+/', '-', $long);
145:         $long = preg_replace('/-+/', '-', $long);
146:         $this->long($long);
147: 
148:         $this->short(substr($long, 0, 1));
149:     }
150: 
151: 
152:     /**
153:      * Create new option
154:      * @see Option::__construct
155:      *
156:      * @param string $name Name of option
157:      *
158:      * @return Option
159:      */
160:     public static function make($name)
161:     {
162:         return new Option($name);
163:     }
164: 
165: 
166:     /**
167:      * Create new option of specific type.
168:      * The typed option have required value as default.
169:      * @see Option::make
170:      *
171:      * @param string $name Name of option
172:      * @param array $settings Specific settings for selected type
173:      *
174:      * @return Option
175:      */
176:     public static function __callStatic($type, $settings)
177:     {
178:         $name = (isset($settings[0])) ? array_shift($settings) : '';
179:         $option = self::make($name);
180:         $option->type = self::getTypes()->getType($type, $settings);
181:         $option->value();
182:         return $option;
183:     }
184: 
185: 
186:     /**
187:      * Register new type. It has to by child of abstract class AType.
188:      * It is possible rewrite already registered types (so defaults too).
189:      *
190:      * @param string $name Name of type
191:      * @param string $className Name of class of type with namespace
192:      * @param string $classPath Full path of file contains class of type
193:      *
194:      * @return void
195:      */
196:     public static function registerType($name, $className, $classPath)
197:     {
198:         self::getTypes()->register($name, $className, $classPath);
199:     }
200: 
201: 
202:     // ---- SET OPTION ----
203: 
204: 
205:     /**
206:      * Set short variant of option in command-line
207:      *
208:      * @param string $short
209:      *
210:      * @throws LogicException Short and long varint cannot be undefined together.
211:      * @throws InvalidArgumentException Short variant have to be only one character.
212:      * @return Option
213:      */
214:     public function short($short = NULL)
215:     {
216:         if (is_null($short) && is_null($this->long))
217:         {
218:             throw new LogicException($this->name . ': Short and long varint cannot be undefined together.');
219:         }
220: 
221:         if (!is_null($short) && strlen($short) != 1)
222:         {
223:             throw new InvalidArgumentException($this->name . ': Short variant has to be only one character.');
224:         }
225: 
226:         $this->short = $short;
227:         return $this;
228:     }
229: 
230: 
231:     /**
232:      * Set long variant of option in command-line
233:      *
234:      * @param string $long
235:      *
236:      * @throws LogicException Short and long varint cannot be undefined together.
237:      * @return Option
238:      */
239:     public function long($long = NULL)
240:     {
241:         if (is_null($long) && is_null($this->short))
242:         {
243:             throw new LogicException($this->name . ': Short and long varint cannot be undefined together.');
244:         }
245: 
246:         $this->long = $long;
247:         return $this;
248:     }
249: 
250: 
251:     /**
252:      * Set default value of option
253:      *
254:      * @param mixed $default
255:      *
256:      * @throws LogicException The default value makes sense only for options with optional value or optional option with optional or required value.
257:      * @return Option
258:      */
259:     public function defaults($defaults = NULL)
260:     {
261:         if (!is_null($defaults) && !(
262:             ($this->valueRequired == self::VALUE_OPTIONAL) ||
263:             ($this->valueRequired == self::VALUE_REQUIRE && $this->required == FALSE)
264:         ))
265:         {
266:             throw new LogicException($this->name . ': The default value makes sense only for options with optional value or optional option with optional or required value.');
267:         }
268: 
269:         $this->defaults = $defaults;
270:         return $this;
271:     }
272: 
273: 
274:     /**
275:      * Set flag of requirement of option
276:      *
277:      * @param bool $required
278:      *
279:      * @throws LogicException The required option does not make sense for option with default value and required value
280:      * @return Option
281:      */
282:     public function required($required = TRUE)
283:     {
284:         if ($this->valueRequired == self::VALUE_REQUIRE && !is_null($this->defaults))
285:         {
286:             throw new LogicException($this->name . ': The required option does not make sense for option with default value and required value.');
287:         }
288: 
289:         $this->required = (bool)$required;
290:         return $this;
291:     }
292: 
293: 
294:     /**
295:      * Set type of requirement of value of option
296:      * TRUE = require
297:      * FALSE = optional
298:      *
299:      * @param bool $value
300:      *
301:      * @throws LogicException The require/non value make sense only for option without default value.
302:      * @return Option
303:      */
304:     public function value($value = TRUE)
305:     {
306:         $value = ((bool)$value) ? self::VALUE_REQUIRE : self::VALUE_OPTIONAL;
307: 
308:         if (!is_null($this->defaults) && $value != self::VALUE_OPTIONAL)
309:         {
310:             throw new LogicException($this->name . ': The require/non value makes sense only for option without default value.');
311:         }
312: 
313:         $this->valueRequired = $value;
314:         return $this;
315:     }
316: 
317: 
318:     /**
319:      * Set description of option used in help
320:      *
321:      * @param string $description
322:      *
323:      * @return Option
324:      */
325:     public function description($description = '')
326:     {
327:         $this->description = $description;
328:         return $this;
329:     }
330: 
331: 
332:     /**
333:      * Set needed options by this option
334:      *
335:      * @param array $needed Needed options
336:      *
337:      * @return void
338:      */
339:     public function dependences($needed)
340:     {
341:         $this->needed = $needed;
342:         return $this;
343:     }
344: 
345: 
346:     // ---- GET OPTION SETTINGS ----
347: 
348: 
349:     /**
350:      * Return name of option
351:      *
352:      * @return string
353:      */
354:     public function getName()
355:     {
356:         return $this->name;
357:     }
358: 
359: 
360:     /**
361:      * Return short varian of option in command-line
362:      *
363:      * @return string
364:      */
365:     public function getShort()
366:     {
367:         return $this->short;
368:     }
369: 
370: 
371:     /**
372:      * Return long varian of option in command-line
373:      *
374:      * @return string
375:      */
376:     public function getLong()
377:     {
378:         return $this->long;
379:     }
380: 
381: 
382:     /**
383:      * Return default value of option in command-line
384:      *
385:      * @return mixed
386:      */
387:     public function getDefaults()
388:     {
389:         return $this->defaults;
390:     }
391: 
392: 
393:     /**
394:      * Return value of option
395:      * FALSE = not set
396:      * TRUE = set without value
397:      * other = value form command-line of default value
398:      *
399:      * @param bool $isDefaiultSet Flag if default option is set
400:      *
401:      * @return mixed
402:      */
403:     public function getValue($isDefaultSet = FALSE)
404:     {
405:         if (is_null($this->value))
406:         {
407:             $this->value = $this->readValue($isDefaultSet);
408:         }
409: 
410:         return $this->value;
411:     }
412: 
413: 
414:     // ---- HELP ----
415: 
416: 
417:     /**
418:      * Return short and long option in one string
419:      *
420:      * @param string $value Value of options (typically type of value)
421:      *
422:      * @return string
423:      */
424:     public function getOptions($value = '')
425:     {
426:         $options = '';
427:         $options .= ($this->short ? '-' . $this->short . $value : '');
428:         $options .= ($this->short && $this->long ? ', ' : '');
429:         $options .= ($this->long ? '--' . $this->long . $value : '');
430:         return $options;
431:     }
432: 
433: 
434:     /**
435:      * Return help for option
436:      *
437:      * @param integer $indent Level of indent of text
438:      *
439:      * @return string
440:      */
441:     public function getHelp($maxLength, $indent = 0)
442:     {
443:         $indentString = str_repeat(' ', self::HELP_INDENT_LENGTH);
444: 
445:         // indent
446:         if ($indent)
447:         {
448:             $indent = implode('', array_fill(0, $indent, $indentString));
449:         }
450:         else
451:         {
452:             $indent = '';
453:         }
454: 
455:         // option value
456:         $valueType = 'VALUE';
457:         if ($this->type)
458:         {
459:             $valueType = $this->type->getName();
460:         }
461:         switch ($this->valueRequired)
462:         {
463:             case self::VALUE_NO:
464:                 $value = '';
465:                 break;
466:             case self::VALUE_OPTIONAL:
467:                 $value = '[="' . $valueType . '"]';
468:                 break;
469:             case self::VALUE_REQUIRE:
470:                 $value = '="' . $valueType . '"';
471:                 break;
472:         }
473: 
474:         // options with value
475:         $help = $this->getOptions($value);
476:         if (!$this->required)
477:         {
478:             $help = '[' . $help . ']';
479:         }
480:         $help = $this->wordwrap($help, $maxLength, $indent);
481: 
482: 
483:         // default value
484:         if (!is_null($this->defaults))
485:         {
486:             switch (gettype($this->defaults))
487:             {
488:                 case 'boolean':
489:                     $defaults = $this->defaults ? 'TRUE' : 'FALSE';
490:                     break;
491:                 case 'array':
492:                     $defaults = 'array(' . implode(',', $this->defaults) . ')';
493:                     break;
494:                 default:
495:                     $defaults = $this->defaults;
496:                     break;
497:             }
498:             $help .= PHP_EOL . $this->wordwrap('DEFAULT="' . $defaults . '"', $maxLength, $indentString . $indent);
499:         }
500: 
501:         // needed options
502:         if (count($this->needed) > 0)
503:         {
504:             $helpNeeded = array();
505:             foreach ($this->needed as $neededOption)
506:             {
507:                 $helpNeeded[] = $neededOption->getOptions();
508:             }
509:             $help .= PHP_EOL . $this->wordwrap('NEEDED: ' . implode('; ', $helpNeeded), $maxLength, $indentString . $indent);
510:         }
511: 
512:         // descriptions
513:         if ($this->description)
514:         {
515:             $help .= PHP_EOL . $this->wordwrap(str_replace(PHP_EOL, PHP_EOL . $indentString . $indent, $this->description), $maxLength, $indentString . $indent);
516:         }
517: 
518:         return $help;
519:     }
520: 
521: 
522:     // ---- PRIVATE ----
523: 
524: 
525:     /**
526:      * Return object for work with types.
527:      *
528:      * @return Types\Types
529:      */
530:     private static function getTypes()
531:     {
532:         if (!self::$types)
533:         {
534:             self::$types = new Types\Types();
535:         }
536: 
537:         return self::$types;
538:     }
539: 
540: 
541:     /**
542:      * Read value of option
543:      * FALSE = not set
544:      * TRUE = set without value
545:      * other = value form command-line of default value
546:      *
547:      * @param bool $isDefaiultSet Flag if default option is set
548:      *
549:      * @throws UserBadCallException Option has value set by short and long variant.
550:      * @throws UserBadCallException Option has bad format
551:      * @throws UserBadCallException Option is required.
552:      * @throws UserBadCallException Option have value, but any is exected.
553:      * @throws UserBadCallException Option has require value, but no set.
554:      * @return mixed
555:      */
556:     private function readValue($isDefaultSet)
557:     {
558:         $options = Arguments::options();
559: 
560:         $short = isset($options[$this->short]) ? $options[$this->short] : FALSE;
561:         $long = isset($options[$this->long]) ? $options[$this->long] : FALSE;
562: 
563:         // get value
564:         if ($short !== FALSE && $long !== FALSE)
565:         {
566:             throw new UserBadCallException($this->getOptions() . ': Option has value set by short and long variant.');
567:         }
568:         elseif ($short === FALSE && $long === FALSE)
569:         {
570:             $value = FALSE;
571:         }
572:         elseif ($short !== FALSE)
573:         {
574:             $value = $short;
575:         }
576:         else
577:         {
578:             $value = $long;
579:         }
580: 
581:         // required
582:         if (!$isDefaultSet && $this->required && $value === FALSE)
583:         {
584:             throw new UserBadCallException($this->getOptions() . ': Option is required.');
585:         }
586: 
587:         // value required
588:         switch ($this->valueRequired)
589:         {
590:             case self::VALUE_NO:
591:                 // set argument as common argument
592:                 if (!is_bool($value))
593:                 {
594:                     throw new UserBadCallException($this->getOptions() . ': Option has value, but any is expected.');
595:                 }
596:                 break;
597: 
598:             case self::VALUE_OPTIONAL:
599:                 // use default value for not set option or empty set option
600:                 if (is_bool($value) && !is_null($this->defaults))
601:                 {
602:                     $value = $this->defaults;
603:                 }
604:                 break;
605: 
606:             case self::VALUE_REQUIRE:
607:                 // option has not value
608:                 if ($value === TRUE)
609:                 {
610:                     throw new UserBadCallException($this->getOptions() . ': Option has require value, but no set.');
611:                 }
612:                 else if ($value === FALSE && !is_null($this->defaults))
613:                 {
614:                     $value = $this->defaults;
615:                 }
616: 
617:                 break;
618:         }
619: 
620:         // check type of value + filter value
621:         if (($value !== FALSE) && !is_null($this->type))
622:         {
623:             if (!$this->type->check($value))
624:             {
625:                 throw new UserBadCallException($this->getOptions() . ': Option has bad format.');
626:             }
627: 
628:             // do not filter optional value without set value
629:             if (($this->valueRequired != self::VALUE_OPTIONAL) || !is_bool($value))
630:             {
631:                 $value = $this->type->filter($value);
632:             }
633:         }
634: 
635:         return $value;
636:     }
637: 
638: 
639:     /**
640:      * Wrapping input sring on maximal length and prepand prefix on each line
641:      *
642:      * @param string $string Wrapped string
643:      * @param int $maxLength Maximla length of one line
644:      * @param string $prefix Prepanded prefix
645:      *
646:      * @return string
647:      */
648:     private function wordwrap($string, $maxLength, $prefix)
649:     {
650:         $string = wordwrap($string, $maxLength - strlen($prefix), PHP_EOL, FALSE);
651:         $strings = explode(PHP_EOL, $string);
652:         array_walk($strings,
653:             function (&$item, $key, $prefix) {
654:                 $item =  $prefix . trim($item);
655:             },
656:             $prefix
657:         );
658:         $string = implode(PHP_EOL, $strings);
659:         return $string;
660:     }
661: 
662: 
663: 
664: }
665: 
PhpOptions API documentation generated by ApiGen.
Generated using the TokenReflection library.