Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Optionally handle subclasses of exceptions in custom error handler #2862

Merged
merged 2 commits into from
Dec 10, 2019
Merged

Optionally handle subclasses of exceptions in custom error handler #2862

merged 2 commits into from
Dec 10, 2019

Conversation

nickdnk
Copy link
Contributor

@nickdnk nickdnk commented Oct 8, 2019

Hello guys

I thought this could be a nice addition, so a particular app can decide to handle all subclasses of an exception with one handler. This is nice way to keep things separated.

I added this as an option that defaults to false so this should not be a breaking change, but I'm not 100% sure since $handlers is protected and not private.

What do you think?

@coveralls
Copy link

coveralls commented Oct 8, 2019

Coverage Status

Coverage remained the same at 100.0% when pulling 7ea0a3d on nickdnk:4.x into ea75cf4 on slimphp:4.x.

@nickdnk
Copy link
Contributor Author

nickdnk commented Oct 8, 2019

Force pushed for typo.

@nickdnk
Copy link
Contributor Author

nickdnk commented Oct 8, 2019

We could mitigate the BC by doing this instead:

if (isset($this->handlers[$type])) {
    return $this->callableResolver->resolve(
        is_array($this->handlers[$type]) ? $this->handlers[$type]['handler'] : $this->handlers[$type]
    );
} else {
    foreach ($this->handlers as $class => $handler) {
        if (is_array($handler) && $handler['handle_subclass'] === true) {
            if (is_subclass_of($type, $class)) {
                return $this->callableResolver->resolve($handler['handler']);
            }
        }
    }
}

Then even if a someone has subclassed ErrorMiddleware and overriden $handlers to contain callables while not overriding the setErrorHandler method, it still wouldn't break. Why someone would do that I don't know though.

@l0gicgate
Copy link
Member

@adriansuter I think this would be a good addition and there's no BC breaks. What do you think about it?

@nickdnk
Copy link
Contributor Author

nickdnk commented Oct 12, 2019

Is there a reason that $handlers is protected and not private ? Given how close this variable is tied to the setErrorHandler method, should it be visible to a subclass?

@l0gicgate
Copy link
Member

Is there a reason that $handlers is protected and not private ? Given how close this variable is tied to the setErrorHandler method, should it be visible to a subclass?

In case one wants to extend ErrorMiddleware we left it as protected.

@l0gicgate l0gicgate added this to the 4.4.0 milestone Nov 8, 2019
@l0gicgate l0gicgate requested a review from adriansuter November 8, 2019 17:35
Copy link
Contributor

@adriansuter adriansuter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good idea.

Another solution would be to add a new protected property subClassHandlers and then just write

    public function setErrorHandler(string $type, $handler, bool $handleSubclasses = false): self
    {
        if ($handleSubclasses) {
            $this->subClassHandlers[$type] = $handler;
        } else {
            $this->handlers[$type] = $handler;
        }

        return $this;
    }

That way, we do not change the data logic of the handlers property which is expected to be a plain array of handlers.

Of course we would have to change getErrorHandler() too.

    public function getErrorHandler(string $type)
    {
        if (isset($this->handlers[$type])) {
            return $this->callableResolver->resolve($this->handlers[$type]);
        } elseif (isset($this->subClassHandlers[$type])) {
            return $this->callableResolver->resolve($this->subClassHandlers[$type]);
        } else {
            foreach ($this->subClassHandlers as $class => $handler) {
                if (is_subclass_of($type, $class)) {
                    return $this->callableResolver->resolve($handler);
                }
            }
        }

        return $this->getDefaultErrorHandler();
    }

That way, we would also get rid of the double if-statements in the foreach loop.

What do you mean, @l0gicgate and @nickdnk ? If you want to leave it as requested in this PR, then I am fine too.

@nickdnk
Copy link
Contributor Author

nickdnk commented Nov 8, 2019

I think yours is a good workaround, @adriansuter.

@l0gicgate
Copy link
Member

@adriansuter I like your solution. We should proceed with that.

@nickdnk
Copy link
Contributor Author

nickdnk commented Nov 19, 2019

Updated the PR.

Also, I added phpdocs for the handler arrays based on the value of $defaultErrorHandler - but I'm not entirely sure that you can annotate them as ErrorHandlerInterface since they're functions? Edit: Aha, so if you pass that interface in then it will behave as a function when __invoke is called. Got it.

@l0gicgate
Copy link
Member

Need to write tests as well for the subclass case. Are you able to do that?

@nickdnk
Copy link
Contributor Author

nickdnk commented Nov 20, 2019

I did. I don't know why it claims it wasn't covered. There is a test for both with and without subclass use.

@adriansuter
Copy link
Contributor

Did you add a test case for the case you set an error handler as subclass handler, but the exception thrown is not a subclass but exactly that class?

} elseif (isset($this->subClassHandlers[$type])) {

@nickdnk
Copy link
Contributor Author

nickdnk commented Nov 20, 2019

I think that did it.

…ndlers

Implemented subclass handler array to avoid breaking changes
Cleaned up test syntax a bit
@nickdnk nickdnk requested a review from l0gicgate December 1, 2019 22:38
@l0gicgate l0gicgate merged commit 941828c into slimphp:4.x Dec 10, 2019
@l0gicgate l0gicgate mentioned this pull request Jan 5, 2020
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants