<?php /** * Filters an array and extracts and validates command line arguments * * @author Patrick Forget <patforg at webtrendi.com> */ namespace Clapp; /** * Filters an array and extracts and validates command line arguments * * @author Patrick Forget <patforg at webtrendi.com> */ class CommandArgumentFilter { /** * Command line arguments * @var array */ private $arguments = array(); /** * Definition of allowed parameters * @var \Clapp\CommandLineArgumentDefinition */ private $definitions = null; /** * Flag if arguments have been parsed in to params * @var boolean */ private $parsed = false; /** * Parsed params * @var array */ private $params = array(); /** * Trailing values * @var string */ private $trailingValues = ""; /** * program name * @var string */ private $programName = ""; /** * class constructor * * @author Patrick Forget <patforg at webtrendi.com> * * @param \Clapp\CommandLineDefinition $definitions contains list of allowed parameters * @param array $args list of arguments to filter. */ public function __construct(\Clapp\CommandLineArgumentDefinition $definitions, $args) { if (is_array($args)) { $this->arguments = $args; } //if $this->definitions = $definitions; } // __construct() /** * returns parameter matching provided name * * @author Patrick Forget <patforg at webtrendi.com> * * @param string name of the paramter to retreive * * @return mixed if param the param appears only once the method will * return 1 if the parameter doesn't take a value. The specified value * for that param will returned if it does take value. * * If many occurence of the param appear the number of occurences will * be returned for params that do not take values. An array of values * will be returned for the parameters that do take values. * * If the parameter is not present null if it takes a value and false if * it's not present and doesn't allow values */ public function getParam($name) { if (!$this->parsed) { $this->parseParams(); } //if $longName = strlen($name) === 1 ? $this->definitions->getLongName($name) : $name; if (isset($this->params[$longName])) { return $this->params[$longName]; } else { if ($this->definitions->allowsValue($longName)) { return null; } else { return false; } //if } //if } // getParam() /** * retreive the program name * * @author Patrick Forget <patforg at webtrendi.com> */ public function getProgramName() { if (!$this->parsed) { $this->parseParams(); } //if return $this->programName; } // getProgramName() /** * retreive the trailing values * * @author Patrick Forget <patforg at webtrendi.com> */ public function getTrailingValues() { if (!$this->parsed) { $this->parseParams(); } //if return $this->trailingValues; } // getTrailingValues() /** * extracts params from arguments * * @author Patrick Forget <patforg at webtrendi.com> */ protected function parseParams() { $argumentStack = $this->arguments; $expectingValue = false; $currentLongName = null; $currentValue = null; $trailingValues = ""; $endOfDashedArguments = false; $addParam = false; $argumentsLeft = sizeof($argumentStack); $multiShortParams = array(); $this->programName = array_shift($argumentStack); // remove first argument which is the program name while ($currentArgument = array_shift($argumentStack)) { $argumentsLeft--; $currentArgumentLength = strlen($currentArgument); // arguments that don't start with a dash if (substr($currentArgument, 0, 1) !== '-') { if ($expectingValue) { $currentValue = $currentArgument; $addParam = true; } else { $trailingValues .= " ". $currentArgument; $endOfDashedArguments = true; } //if // double dash detected } elseif (substr($currentArgument, 1, 1) === '-') { if ($expectingValue) { throw new \UnexpectedValueException("Parameter {$currentLongName} expects a values"); } //if /* stop parsing arguments if double dash only param is encountered */ if ($currentArgumentLength == 2) { if ($trailingValues !== "") { throw new \UnexpectedValueException("Trailing values must appear after double dash"); } //if $trailingValues = " ". implode(" ", $argumentStack); $argumentStack = array(); $endOfDashedArguments = true; break; } //if $longNameParts = explode("=", substr($currentArgument, 2), 2); $currentLongName = $longNameParts[0]; if (sizeof($longNameParts) > 1) { $currentValue = $longNameParts[1]; $addParam = true; } elseif ($this->definitions->allowsValue($currentLongName)) { $expectingValue = true; } else { $addParam = true; } //if // single dash } else { if ($expectingValue) { throw new \UnexpectedValueException("Parameter {$currentLongName} expects a values"); } //if $shortNameParts = explode("=", substr($currentArgument, 1), 2); $shortName = $shortNameParts[0]; if (strlen($shortName) <= 1) { $currentLongName = $this->definitions->getLongName($shortName); if ($currentLongName === null) { throw new \InvalidArgumentException("Unable to find name with ". "provided parameter ({$shortName})"); } //if if (sizeof($shortNameParts) > 1) { $currentValue = $shortNameParts[1]; $addParam = true; } elseif ($this->definitions->allowsValue($currentLongName)) { $expectingValue = true; } else { $addParam = true; } //if } else { $multiShortParams = str_split($shortName); /* process the last one (which is the only one that can have a value) */ $lastParam = array_pop($multiShortParams); $currentLongName = $this->definitions->getLongName($lastParam); if (sizeof($shortNameParts) > 1) { $currentValue = $shortNameParts[1]; $addParam = true; } elseif ($this->definitions->allowsValue($lastParam)) { $expectingValue = true; } else { $addParam = true; } //if } //if } //if if ($addParam) { if ($endOfDashedArguments) { throw new \UnexpectedValueException("Unexpected argument after undashed values"); } //if /* Not sure how this could happen */ // @codeCoverageIgnoreStart if ($currentLongName === false || $currentLongName === null) { throw new \UnexpectedValueException("Missing argument name"); } //if // @codeCoverageIgnoreEnd if (!$this->definitions->paramExists($currentLongName)) { throw new \InvalidArgumentException("Invalid argument name"); } //if $allowsMultiple = $this->definitions->allowsMultiple($currentLongName); $allowsValue = $this->definitions->allowsValue($currentLongName); if (isset($this->params[$currentLongName]) && !$allowsMultiple) { throw new \UnexpectedValueException("Multiple instace of parameter {$currentLongName} not allowed"); } //if if ($allowsValue) { /* Missing values should always be detected before addParam is true */ // @codeCoverageIgnoreStart if ($currentValue === null) { throw new \UnexpectedValueException("Parameter {$currentLongName} expects a values"); } //if // @codeCoverageIgnoreEnd } elseif ($currentValue !== null) { throw new \UnexpectedValueException("Parameter {$currentLongName} does not accept values"); } else { $currentValue = true; } //if if ($allowsMultiple) { if ($allowsValue) { if (!isset($this->params[$currentLongName])) { $this->params[$currentLongName] = array(); } //if $this->params[$currentLongName][] = $currentValue; } else { if (!isset($this->params[$currentLongName])) { $this->params[$currentLongName] = 0; } //if $this->params[$currentLongName]++; } //if } else { $this->params[$currentLongName] = $currentValue; } //if foreach ($multiShortParams as $shortName) { $argumentStack[] = "-{$shortName}"; $argumentsLeft++; } //foreach /* reset stuff for next param */ $expectingValue = false; $currentLongName = null; $currentValue = null; $addParam = false; $multiShortParams = array(); } //if } //while if ($expectingValue !== false) { throw new \UnexpectedValueException("Parameter {$currentLongName} expects a values"); } //if /* Not sure how this could happen */ // @codeCoverageIgnoreStart if ($currentLongName !== null || $addParam !== false || $currentValue !== null || sizeof($multiShortParams) !== 0) { throw new \UnexpectedValueException("Unable to process some parameters"); } //if // @codeCoverageIgnoreEnd if ($trailingValues !== "") { $this->trailingValues = substr($trailingValues, 1); // remove extra space at the begging } //if $this->parsed = true; } // parseParams() }