-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ArgumentException should not be sent immediately #155
Comments
interesting idea. but we would need to define somehow what happens when an option is unknown and what else could someone want to do when an option is unknown except showing the help? |
What about having an Or use a callback (a Closure or whatever the user wants to use) which receives the |
Actually, there is no need to handle process errors: by default, exceptions are thrown, so that there is no BC break. If the user decides to not throw errors (I'm adding a As illustrated in the example in the documentation, there are So I'm just implementing this in a very simple way: when an error occurs (Unexpected or Missing only), send it to an I'm adding two public methods: Please, let me know if you think I'm doing the wrong way (I'll submit a PR soon, still need to write tests and adapt doc). |
so the process method catches his own exceptions and checks an option if the exception should be thrown or just rememberd? sounds good |
That's right. |
It appears that exceptions may be thrown from Another solution is to use a static property and static public methods on protected static $throwErrors = false;
protected static $processErrors = [];
public static function error(ArgumentException $exception)
{
// handle the error
} The I don't really like static properties, generally, since they are stateless. But in this case, there should not be more than one instance of I also wonder if a setter for $getOpt = new GetOpt(null, false);
// or:
$getOpt = new GetOpt();
$getOpt->throwErrors(false); |
https://github.com/Arcesilas/getopt-php/commit/ca2baca811cd43b0a95017777fe187607adedfba The comment about static methods I don't understand. There are only 3 static methods in the GetOpt class. There is no way to use the class statically - why we should remember the errors statically (global; over all GetOpt instances)? Remember: the dev could create multiple getOpt parser that handle different scenarios. We had this in another issue: #142 (comment) |
I would recommend to have two settings: The exception from setValue has to be catched the usual way: try {
$options->setValue($value);
return true;
} catch (ArgumentException $exception) {
$this->error($exception);
return false;
} The setter closures in $stopOnError = $getOpt->get(GetOpt::SETTING_STOP_ON_ERROR);
$continue = true;
while (($arg = array_shift($this->arguments)) !== null) {
if ($stopOnError && !$continue) {
return false;
}
// ...
$continue = $setOption($option);
} |
In what I had in mind, The problem I pointed out in this issue was that when an exception is thrown (which means "stop on error"), the remaining options and operands are not set. The goal of my proposition is to allow the remaining options and operands to be set, even if there are errors. Also, I'm thinking about one question: isn't it inappropriate to not throw any exception at all when there are errors? In fact, the problem is not that an exception is raised, but that it's raised to soon. Actually, I think the behaviour should be as so:
This would prevent the script from running with invalid / missing / unexpected arguments. If the user wants to do nothing in the |
Actually, we can simply use Exception chaining. |
Yes, stop on error without throwing exceptions is the same as catching a thrown exception. But some people might argue that the code looks very different: $getOpt = createGetOpt();
$getOpt->process($argv);
$options = $getOpt->hasErrors() ? $defaultOptions : $getOpt->getOptions();
// vs.
try {
$getOpt = createGetOpt();
$getOpt->process($argv);
$options = $getOpt->getOptions();
} catch (ArgumentException $exception) {
$options = $defaultOptions;
} If an exception is thrown to soon or to late depends on the length of arguments and the complexity to process them (validators that query a database for example). I wouldn't agree to a solution where it is not possible to stop processing when an error occurs. The idea of not throwing immediately but after the processing with a "aggregate" exception is a 3rd method in my opinion. Maybe an option The "aggregate" exception I would rather develop like this: class ProcessingErrors extends ArgumentException
{
/** @var ArgumentException[] */
protected $errors = [];
public function __constructor($errors) {
$this->errors = $errors;
parent::__constructor('Errors occured during processing the arguments');
}
public function getErrors() {
return $errors;
}
} |
Throwing exceptions immediately when an argument is unexpected or missing when processing arguments list prevent from using valuable options like
--help
or--verbose
, as they have not been processed and therefore are not available when handling the exception.To observe, just take the example file provided in the documentation and just make this change:
Now call the script with:
php example.php -p --help
The exception will always be thrown and the text
Help option provided
will not be displayed, despite we provided the help option.Note that this does not happen if we pass the help option first:
php example.php --help -p
To prevent this from happening, exceptions should be stored somewhere (Symfony uses an ExceptionBag if I'm correct). After processing the arguments list is done, the exceptions can then be checked and handled appropriately. This allows to use options to adjust error handling (verbose mode for example).
I may provide with some PR if you're interested, to have something concrete to discuss.
The text was updated successfully, but these errors were encountered: