simulate; if (! is_string ($simulate)) throw new \Exception ("Simulate provided to getopts is not a string", 500); if ($simulate === "") $simulate = null; $this->simulate = $simulate; return $this; } /** Add a new option to check * @param string $identifier The identifier of the options * @param string $short The short options (one letter with/without * semi-colon) to check * @param array|string $long The long options without double dash, * with/without semi-colon to check. If string, use colon to separate the * options * @param string $description The description of the option * @param string $paramName The description (or name) of the parameter * @param integer $multiple The same identifier can be called multiple time. * Set $multiple to set the maximum number of values */ public function add ($identifier, $short, $long, $description, $paramName = "", $multiple = 0) { if (! is_string ($identifier)) throw new \Exception ("Identifier provided to getopts is not a string", 500); if (strlen (trim ($identifier)) === 0) throw new \Exception ("Identifier provided to getopts is too short", 500); if (! is_string ($short)) throw new \Exception ("Short option provided to getopts is not a string", 500); if (! is_array ($long) && ! is_string ($long)) throw new \Exception ( "Long option provided to getopts is not a string or an array", 500); if (! is_string ($description)) throw new \Exception ( "Description of option provided to getopts is not a string", 500); if (! is_string ($paramName)) throw new \Exception ( "paramName of option provided to getopts is not a string", 500); if (is_string ($long)) { if (trim ($long) !== "") $long = explode (",", $long); else $long = array (); } if ($short[0] === ":") throw new \Exception ("Short option can't start by semi-colon", 500); if ((strpos ($short, ":") !== false || strpos (implode ($long), ":") !== false) && trim ($paramName) === "") throw new \Exception ( "One parameter is waiting, but there is no paramName defined", 500); if (strspn ($paramName, "abcdefghijklmnopqrstuvwxyz". "ABCDEFGHIJKLMNOPQRSTUVWXYZ". "0123456789-_.") !== strlen ($paramName)) throw new \Exception ( "paramName of option provided to getopts contains invalid chars", 500); if (! is_int ($multiple) && \verify::staticIs_integer ("$multiple") === false) throw new \Exception ("Multiple option must be an integer", 500); preg_match_all ("#[\S]:?#u", $short, $matches); if (! array_key_exists (0, $matches)) throw new \Exception ("Short parameter is invalid", 500); $short = $matches[0]; foreach ($short as $key=>$s) { if (substr ($s, 1) !== false && substr ($s, 1) !== ":") throw new \Exception ( "Short parameter is invalid : only one letter allowed", 500); $short[$key] = "-".$s; } foreach ($long as $key=>$l) { $long[$key] = "--".$l; } $optShort = array (); $optLong = array (); foreach ($this->options as $opt) { // Do not allow to add if the short is already defined (with : or :: or // without any) if ($opt["identifier"] === $identifier) throw new \Exception ("The identifier is already defined", 500); // Do not allow to add if the short is already defined $optShort = array_merge ($optShort, $opt["short"]); $optLong = array_merge ($optLong, $opt["long"]); } foreach ($short as $s) { if (count (preg_grep ("#^".str_replace (":", "", str_replace ("?", "\\?", $s)).":?:?$#", $optShort))) throw new \Exception (sprintf ( "The short option '%s' is already defined in '%s'", $s, $opt["identifier"]), 500); } // Do not allow to add if the long is already defined (with : or :: or // without any) foreach ($long as $l) { if (count (preg_grep ("#^".str_replace (":", "", str_replace ("?", "\\?", $l)).":?:?$#", $optLong))) throw new \Exception (sprintf ( "The long option '%s' is already defined in '%s'", $l, $opt["identifier"]), 500); } // Do not allow a parameter to be defined multiple times in the new option foreach ($short as $s) { if (strspn (substr ($s, 1), "abcdefghijklmnopqrstuvwxyz". "ABCDEFGHIJKLMNOPQRSTUVWXYZ". "0123456789?_.:") !== strlen ($s) -1) throw new \Exception (sprintf ( "Short option '%s' to getopts contains invalid chars", $s), 500); $regex = "#^".str_replace (":", "", str_replace ("?", "\\?", $s)). ":?:?$#"; if (count (preg_grep ($regex, $short)) !== 1) throw new \Exception (sprintf ( "The short option '%s' can not be defined multiple time in '%s'", str_replace (":", "", $s), $identifier), 500); } foreach ($long as $l) { if (strspn (substr ($l, 2), "abcdefghijklmnopqrstuvwxyz". "ABCDEFGHIJKLMNOPQRSTUVWXYZ". "0123456789?_.:") !== strlen ($l) -2) throw new \Exception (sprintf ( "Long option '%s' to getopts contains invalid chars", $l), 500); $regex = "#^".str_replace (":", "", str_replace ("?", "\\?", $l)). ":?:?$#"; if (count (preg_grep ($regex, $long)) !== 1) throw new \Exception (sprintf ( "The long option '%s' can not be defined multiple time in '%s'", str_replace (":", "", $l), $identifier), 500); } $this->options[] = array ("identifier" => $identifier, "short" => $short, "long" => $long, "description" => trim ($description), "paramName" => trim ($paramName), "multiple" => $multiple); return $this; } /** Scan the command line and fill the parameters. * Use the simulate line if provided or use the $argv if not * Set the parameters property to an array */ public function scan () { global $argv; if ($this->simulate !== null) $commandLine = $this->simulate; else $commandLine = implode (" ", $argv); $debug = false; $tokens = array (); $prevToken = ""; // Look for sentences in the arguments of the command line $offset = 0; if ($debug) echo "\n012345678901234567890123456789\n$commandLine\n"; while ($offset <= mb_strlen ($commandLine)) { if ($debug) echo "OFFSET=$offset\n"; $start = strpos ($commandLine, "\"", $offset); if ($start === $offset) { // Sentence, see if there is a end $end = strpos ($commandLine, "\"", $offset + 1); if ($end !== false) { // Complete sentence (with ending double quote) $nbchars = $end - $offset - 1; if ($debug) echo "COMPLETE SENTENCE (Start ".($offset+1). " with $nbchars chars)\n"; $token = substr ($commandLine, $offset + 1, $nbchars); $tokens[] = $token; $offset = $end + 1; continue; } } // Word analysis $end = strpos ($commandLine, " ", $offset); if ($end === false) $end = strlen ($commandLine); $nbchars = $end - $offset; $token = substr ($commandLine, $offset, $nbchars); if ($debug) echo "WORD FOUND (Start $offset with $nbchars chars): $token\n"; // The '\ ' are token concatenation. Remove the \ in the parameters if (substr ($token, -1) === "\\") { $prevToken .= substr ($token, 0, -1)." "; $offset = $end + 1; continue; } if ($prevToken !== "") { $token = "$prevToken$token"; $prevToken = ""; } if ($this->restOfLine !== null) $this->restOfLine[] = $token; elseif (trim ($token) !== "") $tokens[] = $token; $offset = $end + 1; } if ($debug) print_r ($tokens); // Analyze the tokens to fill the $this->parameters if ($this->restOfLine === null) $this->restOfLine = ""; if (! array_key_exists (0, $tokens)) throw new \Exception ("Can not find the program name (\$argv[0]", 500); $this->programName = array_shift ($tokens); foreach ($this->options as $option) { foreach (array ("short", "long") as $len) { foreach ($option[$len] as $opt) { if ($option["multiple"] < 2) { if ($debug) echo "CHECK $opt UNIQUE -> "; $keys = array_keys ($tokens, str_replace (":", "", $opt)); if (count ($keys) === 0) { if ($debug) echo "Option Not found\n"; continue; } if (count ($keys) > 1) throw new \Exception (sprintf (dgettext ("domframework", "Too much identical parameters provided: %s"), $opt), 500); $pos = reset ($keys); // Look for parameter in this option. Can't start by dash if (strpos ($opt, ":") === false) { if ($debug) echo "FOUND UNIQUE without param\n"; $this->parameters[$option["identifier"]] = true; unset ($tokens[$pos]); } elseif (strpos ($opt, "::") !== false && array_key_exists ($pos+1, $tokens) && array_key_exists ($pos+2, $tokens) && $tokens[$pos+1] === "--") { if ($debug) echo "FOUND UNIQUE with optional param filled ". "and double-dash\n"; $this->parameters[$option["identifier"]] = $tokens[$pos+2]; unset ($tokens[$pos]); unset ($tokens[$pos+1]); unset ($tokens[$pos+2]); } elseif (strpos ($opt, "::") !== false && array_key_exists ($pos+1, $tokens) && substr ($tokens[$pos+1], 0, 1) !== "-") { if ($debug) echo "FOUND UNIQUE with optional param filled\n"; $this->parameters[$option["identifier"]] = $tokens[$pos+1]; unset ($tokens[$pos]); unset ($tokens[$pos+1]); } elseif (strpos ($opt, ":") !== false) { if (array_key_exists ($pos+1, $tokens) && $tokens[$pos+1] === "--" && ! array_key_exists ($pos+2, $tokens)) throw new \Exception (dgettext ("domframework", "Mandatory value for parameter '$opt' is not provided ". "after double-dash"), 500); if (! array_key_exists ($pos+1, $tokens) || substr ($tokens[$pos+1], 0, 1) === "-") throw new \Exception (dgettext ("domframework", "Mandatory value for parameter '$opt' is not provided"), 500); if ($debug) echo "FOUND UNIQUE with mandatory param filled\n"; $this->parameters[$option["identifier"]] = $tokens[$pos+1]; unset ($tokens[$pos]); unset ($tokens[$pos+1]); } else { if ($debug) echo "Not found\n"; } } else { if ($debug) echo "CHECK $opt MULTIPLE -> "; $keys = array_keys ($tokens, str_replace (":", "", $opt)); if (count ($keys) === 0) { if ($debug) echo "Option Not found\n"; continue; } if (count ($keys) > $option["multiple"]) throw new \Exception (sprintf (dgettext ("domframework", "Too much multiple parameters provided: %s"), $opt), 500); foreach ($keys as $pos) { // Look for parameter in this option. Can't start by dash if (strpos ($opt, ":") === false) { if ($debug) echo "FOUND MUTLIPLE without param\n"; $this->parameters[$option["identifier"]][] = true; unset ($tokens[$pos]); } elseif (strpos ($opt, "::") !== false && array_key_exists ($pos+1, $tokens) && substr ($tokens[$pos+1], 0, 1) !== "-") { if ($debug) echo "FOUND MUTLIPLE with optional param filled\n"; $this->parameters[$option["identifier"]][] = $tokens[$pos+1]; unset ($tokens[$pos]); unset ($tokens[$pos+1]); } elseif (strpos ($opt, ":") !== false) { if (! array_key_exists ($pos+1, $tokens) || substr ($tokens[$pos+1], 0, 1) === "-") throw new \Exception (sprintf (dgettext ("domframework", "Mandatory value for parameter '%s' is not provided"), $opt), 500); if ($debug) echo "FOUND MUTLIPLE with mandatory param filled\n"; $this->parameters[$option["identifier"]][] = $tokens[$pos+1]; unset ($tokens[$pos]); unset ($tokens[$pos+1]); } else { if ($debug) echo "Not found\n"; } } } } } } // Manage the restOfLine foreach ($tokens as $key=>$tok) { if ($tok === "--" && array_key_exists ($key+1, $tokens)) { $this->restOfLine[] = $tokens[$key+1]; unset ($tokens[$key]); unset ($tokens[$key+1]); continue; } if (substr ($tok, 0, 1) === "-") continue; $this->restOfLine[] = $tok; unset ($tokens[$key]); } if (count ($tokens)) { throw new \Exception (sprintf (dgettext ("domframework", "Provided tokens are not known: %s"), implode (",", $tokens)), 500); } if ($this->parameters === null) $this->parameters = array (); } /** Get the value of the option if set in the command line. If simulate is * defined, use it. * Return false if the option is not set * Return true if the option is set without parameter * Return the content of the parameter if the option is set and parameter is * defined * Throw an exception if the identifier is not set * @param string $identifier The identifier option to get */ public function get ($identifier) { $exists = false; foreach ($this->options as $opt) { if ($opt["identifier"] === $identifier) { $exists = true; break; } } if ($exists === false) throw new \Exception (sprintf (dgettext ("domframework", "Provided parameter is not known: %s"), $identifier), 500); if ($this->parameters === null) $this->scan (); if (array_key_exists ($identifier, $this->parameters)) return $this->parameters[$identifier]; if ($opt["multiple"] > 1) return array (); return false; } /** Get the value found in the rest of line (after the double dashes) */ public function restOfLine () { if ($this->restOfLine === null) $this->scan (); return $this->restOfLine; } /** Get the name of the program found in the command line */ public function programName () { if ($this->programName === null) $this->scan (); return $this->programName; } /** Get the Help message with all the descriptions and options */ public function help () { if (count ($this->options) === 0) return dgettext ("domframework", "No option defined")."\n"; $d = ""; foreach ($this->options as $opt) { $i = 0; foreach (array ("short", "long") as $len) { foreach ($opt[$len] as $option) { if (strpos ($option, ":") !== false) $param = true; else $param = false; if (strpos ($option, "::") !== false) $paramOptional = true; else $paramOptional = false; if ($i > 0) $d .= ", "; $d .= $option; if ($param) { $d .= " "; if ($paramOptional) $d .= "["; $d .= $opt["paramName"]; if ($paramOptional) $d .= "]"; } $i++; } } $d .= "\n\t".$opt["description"]; $d .= "\n"; } return $d; } }