ZF-11133: Zend_Console_Getopt: Missing required parameter consumes next option as its parameter value

Description

Given a rules declaration like:

    $rules = array('user|u=s' => 'Username (required)',
                   'case|c=i' => 'Case number (required)');
    $options = new Zend_Console_Getopt($rules);

calling this with the first option missing its parameter, e.g.:

    php test.php -u -c 12345

will result in the user option being assigned the value -c and the case option not being set at all. The same would not occur if the parameter were optional.

The problem arises in the _parseSingleOption function of Zend/Console/Getopt.php (line numbers refer to the version included in release 1.11.3). For options with required parameters the next argument (if one exists) is always processed as the parameter (line 791/792),

            case 'required':
                if (count($argv) > 0) {

whereas for optional parameters, the next argument is checked for a leading '-' (line 802/803):

            case 'optional':
                if (count($argv) > 0 && substr($argv[0], 0, 1) != '-') {

The simple solution is to add the check for the leading '-' to the required parameter processing too (make line 792 identical to line 803). The downside is that required parameters with a genuine leading '-' would no longer be possible, but since optional parameters already cannot have it, and allowing it is a potential source of confusion, this should not be an issue.

If it is desired to allow parameter values with leading '-', it should be possible to do something either by checking whether the next argument matches a valid option, or by insisting on using the --user="-Fred" syntax with an extra processing to indicate that the next argument (created from the string after the =) is definitely a parameter.

Comments

Yes this is confusing the docs still mention you can make it required!

Where's the error: docs or implementation? Would a patch be useful?

Probably only implementation, depending on the decision as to required behaviour.

According to the documentation at present, in the example I gave above an exception should be thrown for the missing parameter to the username option, but in fact it consumes the -c as the parameter value, and the casenumber option is not set. This results in incorrect errors - the calling script thinks that the casenumber option has not been set (or passed at all), whereas in fact it is the username that is missing.

As I said, the simple solution would be to check the next argument for a leading '-' in the required parameter case as well, just as the optional parameter case already does. This would enable the code to function as documented. (It is possible, though I would hope unlikely, that this would break some existing code relying on the undocumented behaviour.)