Repository navigation
Description
Not sure if I'm "doing it wrong", but I am having the same issue as reported in other languages, where Docopt does not treat quote delimited strings as one parameter.
As demonstrated here
Also reported in:
docopt/docopt#207
docopt/try.docopt.org#3
docopt/docopt.R#4
Until this is fixed I suggest putting a warning or removing examples like git commit where it's typical for a command like git commit -m "To have multiple words inside a quote".
In the mean time I have created a workaround that isn't perfect but has so far worked okay for me. I pre-process and post-process the input/output.
The pre-process function replaces any quoted strings with a variable so that git commit -m "To have multiple words inside a quote" becomes git commit -m $_v12345abcdef.
function pre_processor(string $string) {
$variables = [];
$new_string = preg_replace_callback('/["\'](.*?(?<!\\\\))["\']/m', function($match) use (&$variables) {
// Results in a var name like 'v12345abcdef'
$varName = 'v' . hash('fnv164', $match[0]);
$variables[$varName] = $match[1];
// Results in a placeholder like $_v12345abcdef
return '$_' . $varName;
},$string);
return ['string' => $new_string, 'variables' => $variables];
}The $new_string is the passed to Docopt handler in place of the normal CLI input args. The result of which is then sent to a post processing function to fill the variables back in
function post_processor(array $args, array $variables) {
foreach ($args as &$arg){
// find any thing that matches like $_v12345abcdef and replace it with the actual value
$arg = preg_replace_callback('/\$_(v[a-f0-9]{16})\b/', function($match) use ($variables) {
return str_replace($match[0], isset($variables[$match[1]]) ? $variables[$match[1]] : $match[0], $match[0]);
}, $arg);
}
return $args;
}For example, you might have something that looks like this:
function handle($string) {
list('string' => $processed_string, 'variables' => $variables) = pre_processor($string);
$result = $DOCOPT->handle($doc, $processed_string);
$real_args = post_processor($result->args, $variables);
return $real_args;
}I haven't looked through the Docopt.php code, but this might be something that could be done as part of the existing handle() function.
Activity
shabbyrobe commentedon Sep 8, 2019
Hi tuaris, thanks for reporting.
The PHP port of docopt is a pretty straight transliteration of the Python code. It was done this way to make integrating upstream patches easier and to ensure consistency between the implementations.
Unfortunately, that means that until the issue is fixed in the Python version, the PHP version will be stuck with this behaviour. I think it makes sense to update the README here to indicate this is a known issue though. I'll keep this issue open so that it's known that this is a problem, but I'll label it as "NeedsUpstreamFix" or somesuch.
JKingweb commentedon Dec 26, 2023
This report is a little misleading. Docopt will only act incorrectly if the input command is provided as a string (docopt then does naïve splitting). If the input is an alread-split array, the bug does not manifest.
If you're just using
$argvanyway, providing that array is the best option. If your input is a string for whatever reason, you can use a dedicated argument tokenizer to produce an argument array for docopt to use.