|
|
@ -38,32 +38,33 @@ class Docopt { |
|
|
|
* --options, <positional-argument>, commands, which could be |
|
|
|
* [optional], (required), (mutually | exclusive) or repeated... |
|
|
|
* |
|
|
|
* Example |
|
|
|
* ------- |
|
|
|
* Example: |
|
|
|
* |
|
|
|
* > use MensBeam\Docopt\Docopt; |
|
|
|
* > $doc = <<<DOCSTRING |
|
|
|
* <<< Usage: |
|
|
|
* <<< my_program tcp <host> <port> [--timeout=<seconds>] |
|
|
|
* <<< my_program serial <port> [--baud=<n>] [--timeout=<seconds>] |
|
|
|
* <<< my_program (-h | --help | --version) |
|
|
|
* <<< |
|
|
|
* <<< Options: |
|
|
|
* <<< -h, --help Show this screen and exit. |
|
|
|
* <<< --baud=<n> Baudrate [default: 9600] |
|
|
|
* <<< DOCSTRING; |
|
|
|
* > $argv = ['tcp', '127.0.0.1', '80', '--timeout', '30']; |
|
|
|
* > echo json_encode(Docopt::docopt($doc, $argv), \JSON_PRETTY_PRINT); |
|
|
|
* { |
|
|
|
* "--baud": "9600", |
|
|
|
* "--help": false, |
|
|
|
* "--timeout": "30", |
|
|
|
* "--version": false, |
|
|
|
* "<host>": "127.0.0.1", |
|
|
|
* "<port>": "80", |
|
|
|
* "serial": false, |
|
|
|
* "tcp": true |
|
|
|
* } |
|
|
|
* ``` |
|
|
|
* use MensBeam\Docopt\Docopt; |
|
|
|
* $doc = <<<DOCSTRING |
|
|
|
* Usage: |
|
|
|
* my_program tcp <host> <port> [--timeout=<seconds>] |
|
|
|
* my_program serial <port> [--baud=<n>] [--timeout=<seconds>] |
|
|
|
* my_program (-h | --help | --version) |
|
|
|
* |
|
|
|
* Options: |
|
|
|
* -h, --help Show this screen and exit. |
|
|
|
* --baud=<n> Baudrate [default: 9600] |
|
|
|
* DOCSTRING; |
|
|
|
* $argv = ['tcp', '127.0.0.1', '80', '--timeout', '30']; |
|
|
|
* echo json_encode(Docopt::docopt($doc, $argv), \JSON_PRETTY_PRINT); |
|
|
|
* //{ |
|
|
|
* // "--baud": "9600", |
|
|
|
* // "--help": false, |
|
|
|
* // "--timeout": "30", |
|
|
|
* // "--version": false, |
|
|
|
* // "<host>": "127.0.0.1", |
|
|
|
* // "<port>": "80", |
|
|
|
* // "serial": false, |
|
|
|
* // "tcp": true |
|
|
|
* //} |
|
|
|
* ``` |
|
|
|
* |
|
|
|
* @param string $docstring Description of your command-line interface |
|
|
|
* @param string[]|string|null $argv Argument list to be parsed. $_SERVER['argv'] is used if null is provided |
|
|
@ -81,6 +82,7 @@ class Docopt { |
|
|
|
bool $more_magic = false |
|
|
|
): ParsedOptions { |
|
|
|
# argv = sys.argv[1:] if argv is None else argv |
|
|
|
$argv = $argv ?? array_slice($_SERVER['argv'] ?? [], 1); |
|
|
|
# maybe_frame = inspect.currentframe() |
|
|
|
# if maybe_frame: |
|
|
|
# parent_frame = doc_parent_frame = magic_parent_frame = maybe_frame.f_back |
|
|
@ -91,6 +93,8 @@ class Docopt { |
|
|
|
# more_magic = True |
|
|
|
# else: |
|
|
|
# magic_parent_frame = magic_parent_frame.f_back |
|
|
|
// DEVIATION: All these lines are to do with automatic invocation of the "magic" variant of docopt() |
|
|
|
// We cannot do this in PHP |
|
|
|
# if not docstring: # go look for one, if none exists, raise Exception |
|
|
|
# while not docstring and doc_parent_frame: |
|
|
|
# docstring = doc_parent_frame.f_locals.get("__doc__") |
|
|
@ -98,6 +102,7 @@ class Docopt { |
|
|
|
# doc_parent_frame = doc_parent_frame.f_back |
|
|
|
# if not docstring: |
|
|
|
# raise DocoptLanguageError("Either __doc__ must be defined in the scope of a parent or passed as the first argument.") |
|
|
|
// DEVIATION: All these lines look for a docstring in the parent context. We simply require one as input |
|
|
|
# output_value_assigned = False |
|
|
|
# if more_magic and parent_frame: |
|
|
|
# import dis |
|
|
@ -110,14 +115,27 @@ class Docopt { |
|
|
|
# MAYBE_STORE = next(instrs) |
|
|
|
# if MAYBE_STORE and (MAYBE_STORE.opname.startswith("STORE") or MAYBE_STORE.opname.startswith("RETURN")): |
|
|
|
# output_value_assigned = True |
|
|
|
// DEVIATION: This is all for magic which is not possible in PHP |
|
|
|
|
|
|
|
# usage_sections = parse_section("usage:", docstring) |
|
|
|
# if len(usage_sections) == 0: |
|
|
|
# raise DocoptLanguageError('"usage:" section (case-insensitive) not found. Perhaps missing indentation?') |
|
|
|
# if len(usage_sections) > 1: |
|
|
|
# raise DocoptLanguageError('More than one "usage:" (case-insensitive).') |
|
|
|
$usage_sections = Util::parse_section("usage:", $docstring); |
|
|
|
if (sizeof($usage_sections) === 0) { |
|
|
|
throw new DocoptLanguageError('"usage:" section (case-insensitive) not found. Perhaps missing indentation?'); |
|
|
|
} |
|
|
|
if (sizeof($usage_sections) > 1) { |
|
|
|
throw new DocoptLanguageError('More than one "usage:" (case-insensitive).'); |
|
|
|
} |
|
|
|
# options_pattern = re.compile(r"\n\s*?options:", re.IGNORECASE) |
|
|
|
# if options_pattern.search(usage_sections[0]): |
|
|
|
# raise DocoptExit("Warning: options (case-insensitive) was found in usage." "Use a blank line between each section..") |
|
|
|
$options_pattern = '/\n\s*?options:/si'; |
|
|
|
if (preg_match($options_pattern, $usage_sections[0])) { |
|
|
|
throw new DocoptExit("Warning: options (case-insensitive) was found in usage."."Use a blank line between each section.."); |
|
|
|
} |
|
|
|
# DocoptExit.usage = usage_sections[0] |
|
|
|
# options = parse_defaults(docstring) |
|
|
|
# pattern = parse_pattern(formal_usage(DocoptExit.usage), options) |
|
|
|