PSR suggestion: Exception standardisation

270 views
Skip to first unread message

Mathieu Dumoulin

unread,
Jul 2, 2015, 7:15:46 AM7/2/15
to php...@googlegroups.com
A good while ago, i started working on an exception library to standardize exception availability. It hasn't been muct popular because first of all, i'm a nobody :) and second because i didn't really make much effort getting it known.

I joined this group to propose an exception standardisation PSR and i'd like to work on this part with whoever is interrested to work with me.

You can find my project at:


Tell me what you think... It's an old version 1, there are a lot of things to change for version 2, but hell, while at it, why not get community in and propose a PSR about it...

Thanks

Tobion

unread,
Jul 2, 2015, 10:57:51 AM7/2/15
to php...@googlegroups.com
Exceptions are part of the domain logic. I don't think it makes sense to standardize that as you won't be able to cover everything anyway.
And what would the use-case be? The only point would be to allow catching the same type of error across all libraries. That sounds like a non-existing edge case that 
is not reliable anyway.

Mathieu Dumoulin

unread,
Jul 2, 2015, 11:13:20 AM7/2/15
to php...@googlegroups.com
I don't agree with you... Just try to throw a EntityNotFound exception in a relatively complex projects with tons of dependencies, on my PHPStorm, EntityNotFound exception pops in about 15 different libraries including Doctrine. I don't find that a edge case...

Look at the current PHP exception library, limited to the core, thus, people need to reimplement their own exceptions that mean the same thing here and there.

Did you actually read the project's Readme before stating that comment, cause it doesn't really look like it to me...

Woody Gilk

unread,
Jul 2, 2015, 11:29:56 AM7/2/15
to php...@googlegroups.com
Personally, I like your package and think it is quite valuable. At the
same time, I am not totally convinced that more (user-defined)
standard exceptions are useful. The reason being that no one catches
InvalidArgumentException as a generalization, nor would it be useful
to catch DirectoryNotFoundException as a generalization, because the
_context_ is what makes exceptions useful. When we catch
vendor-specific exceptions, we (should) know exactly why said
exception was thrown and can generate a proper domain response. If we
start catching generic exceptions, the context becomes unclear and the
domain responses become more generic, which makes debugging harder.

So while I see very strong value to this package as a _project_
dependency, I see real danger in it as a standard.
--
Woody Gilk
http://about.me/shadowhand
> --
> You received this message because you are subscribed to the Google Groups
> "PHP Framework Interoperability Group" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to php-fig+u...@googlegroups.com.
> To post to this group, send email to php...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/php-fig/7f99aee5-46cc-4c5b-923d-e5a16b8bf20b%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.

Mathieu Dumoulin

unread,
Jul 2, 2015, 11:38:53 AM7/2/15
to php...@googlegroups.com

Agreed on the domain specific aspect, if i create a library and something very specific to my library happens, you shouldn't generalize. But the whole concept of the library is to provide a solid base to build domain exceptions and help people reuse common ones to upgrade interop between projects

For example, lets look at the EntityNotFound exception i was talking about. If you have a project that catches EntityNotFoundException for Doctrine but also are using 2-3 other service providers that throw EntityNotFoundException but their own, imagine the digging you need to do to create an exception handler that covers all these in an API. So you will have to register that you may have 3-4 different entity not found exceptions being thrown while they are technically all the same.

In the event everyone aggrees to use the same base, you can have:

StandardExceptions (aka PSR-###)/EntityExceptions/EntityNotFoundException
- Doctrine throws that specific exception
- Library A throws either that exception or something less generic such as CarNotFoundException which extends EntityNotFoundException
- Library B throws either that exception or something less generic such as CarNotFoundException which extends EntityNotFoundException
- Library C throws either that exception or something less generic such as CarNotFoundException which extends EntityNotFoundException

So now, all libraries actually throw an EntityNotFoundException and you and anyone else can refer to this ONE exception base to catch the errors.

As far as i know, this pattern promotes application and framework interoperability, reduces the amount of code to type in many cases, reduces the tests to write and makes our development environment cleaner because you won't have tons of repetitive exceptions.

Mathieu Dumoulin

unread,
Jul 2, 2015, 11:42:13 AM7/2/15
to php...@googlegroups.com
Sorry i copy pasted the Part about the domain specific extensions and left CarNotFoundException everywhere which is wrong, i wanted to change those names and i can'T find a "edit post"

Woody Gilk

unread,
Jul 2, 2015, 12:03:34 PM7/2/15
to php...@googlegroups.com
On Thu, Jul 2, 2015 at 10:38 AM, Mathieu Dumoulin
<craz...@crazycoders.net> wrote:
> If you have a project that catches EntityNotFoundException for Doctrine but
> also are using 2-3 other service providers that throw
> EntityNotFoundException but their own, imagine the digging you need to do to
> create an exception handler that covers all these in an API. So you will
> have to register that you may have 3-4 different entity not found exceptions
> being thrown while they are technically all the same.

This exactly illustrates the danger of having "standard" exceptions.
Those 3-4 different exceptions are _not_ technically the same, because
they are produced by different libraries and in different areas of the
code. We cannot provide a generic response to every type of exception.

> So now, all libraries actually throw an EntityNotFoundException and you and anyone else can refer to this ONE exception base to catch the errors.

Your example seems to imply that exception responses would be
generated by an exception handler, which should _never_ be the case.
If the exception handler is run it means that code somewhere is
missing a try/catch block that would provide a correct domain
response.

Mathieu Dumoulin

unread,
Jul 2, 2015, 12:15:40 PM7/2/15
to php...@googlegroups.com
Actually, the project we work with at work uses an event listener that checks for exceptions not being handled and transposes those exceptions into configured HTTP status codes. For exemple, if we throw UserNotFoundException from our UserBundle, we don't catch it in the controller and make the controller messier. Instead, we configure in the project that any UserNotFoundException not caught gets in fact changed for a 404 response using the event handler.

The same thing is done in Laravel (We use Symfony) with the Exception Handler in the App directory. So if a senior like my collegue is doing that and Taylor Otwell from Laravel proposes the same thing, i think we can safely assume it's a ok to good practice :)

If you wanted to abstract all possible entity not found exceptions and transform them to a 404 in your app or api, have a base exception would be a very good strategy.

This exactly illustrates the danger of having "standard" exceptions. 
Those 3-4 different exceptions are _not_ technically the same, because 
they are produced by different libraries and in different areas of the 
code. We cannot provide a generic response to every type of exception.

I agree that to some extent, the Liskov principle is broken here. But you have to strike the balance between usability and that principle. I agree that a UserNotFoundException is not the same as a CarNotFoundException, but they are the same at a base and represent a "SomethingNotFoundException" which all derive from Exception.

On that same note, if you want to apply the "These two exceptions don't mean the same thing", then you could apply that same train of thought to everything and say that we shouldn't extend "Exception" because then InvalidArgumentException is not the same as BadMethodCallException. Thats where usability needs to break the Liskov principle. Sometimes, to make our life better, you have to break the rules.

The right way to prevent this substitution problem in this case would be to not catch EntityNotFoundException unless you really need it... Segregate your exception cases to the most precise concept that you want to work with and it will work well. 

Evert Pot

unread,
Jul 2, 2015, 12:28:58 PM7/2/15
to php...@googlegroups.com


On 2015-07-02 12:15 PM, Mathieu Dumoulin wrote:
> Actually, the project we work with at work uses an event listener that
> checks for exceptions not being handled and transposes those exceptions
> into configured HTTP status codes. For exemple, if we throw
> UserNotFoundException from our UserBundle, we don't catch it in the
> controller and make the controller messier. Instead, we configure in the
> project that any UserNotFoundException not caught gets in fact changed
> for a 404 response using the event handler.
>
> The same thing is done in Laravel (We use Symfony) with the Exception
> Handler in the App directory. So if a senior like my collegue is doing
> that and Taylor Otwell from Laravel proposes the same thing, i think we
> can safely assume it's a ok to good practice :)
>
> If you wanted to abstract all possible entity not found exceptions and
> transform them to a 404 in your app or api, have a base exception would
> be a very good strategy.

If anything it should be a base interface, not a base exception.

I also applied a pattern similar to that, but realized afterwards that
there were some issues with this approach.

Consider this scenario:

If there's a UserModel that throws a NotFound exception, then it makes a
lot of sense for a controller to catch that and emit a 404 when
retrieving information about a user.

Lets consider there's also a BlogPost model that relies on the UserModel
when creating new posts... This could be a:

PUT /posts/new-post

If a new post is created, referencing an author that doesn't exist in
the UserModel, and this exception is not caught, it would bubble to the
controller as a 404, when it should really have been a 409 Conflict.

Likewise, if I depend on a library that retrieves information for me,
it's possible that a 404 is completely unexpected, and the only
appropriate response is actually a 500.

A better design is that *every* exception is turned into a 500 Internal
Server Error, *unless* it's caught by the top-most controller, which
understands enough of the context to turn it into a different status code.


Evert

Mathieu Dumoulin

unread,
Jul 2, 2015, 1:23:37 PM7/2/15
to php...@googlegroups.com
If anything it should be a base interface, not a base exception.

For the sake of being logic and respecting design principles to the core, yes, it should be an interface. But the problem would be that you'd have to reimplement every damn method for exceptions everytime. You could use a Trait but at this point, it defeats the purpose of the PSR and of having a library of base exceptions. Furthermore you'd have to recode the exception principle of PHP which is not possible, you'd need a major version and completely break backward compatibility...
 
I also applied a pattern similar to that, but realized afterwards that
there were some issues with this approach.

Consider this scenario:

If there's a UserModel that throws a NotFound exception, then it makes a
lot of sense for a controller to catch that and emit a 404 when
retrieving information about a user.

Lets consider there's also a BlogPost model that relies on the UserModel
when creating new posts... This could be a:

PUT /posts/new-post

Aside from the rest, why the hell are you using PUT to create an entity?

If a new post is created, referencing an author that doesn't exist in
the UserModel, and this exception is not caught, it would bubble to the
controller as a 404, when it should really have been a 409 Conflict.

I understand what you mean, but you try to present a design flaw using another design flaw... For real, if you try to load a user that is linked to an entity and it throws a 404 in this case, then the problem you have here is that, either you didn't check if there was a user attached, or you failed to setup your relationships correctly in your database. 

If you absolutely need a user to exist to be tied to a post, then you should have a DENY on your relationship to prevent the user from disapearing. 

Furthermore, if you use a service based approach to load something that depends on something else, you should catch the exceptions of your dependency. There should be no exceptions thrown from dependent libraries or dependencies from your dependency unless they are clearly announced or else it means you are defeating the encapsulation principle where you should not know the inner workings of something. If you know that a UserNotFoundException is supposed to be thrown from the CarService because it tries to fetch the user associated with a car, you should handle the UserNotFoundException in CarBundle, not in your dependant module unless you are announcing it. (Which makes no sense anyway)

Finaly, 409 should be used when you are creating a conflict between the existing entity and the new entity data you are sending, simply said, merge conflicts or optimistic locking failure. It has nothing to do with an entity not being found in the context or if an error occurs on the server.
 
Likewise, if I depend on a library that retrieves information for me,
it's possible that a 404 is completely unexpected, and the only
appropriate response is actually a 500.

A better design is that *every* exception is turned into a 500 Internal
Server Error, *unless* it's caught by the top-most controller, which
understands enough of the context to turn it into a different status code.

I'd like to see some proof of that... Technically, if you catch a UserNotFoundException into a 500, it makes it much harder to find out where the problem came from, log crunching, tracing, debugging, etc... Add the complexification of the controller to catch all potential exceptions...

Evert

Evert Pot

unread,
Jul 2, 2015, 2:11:52 PM7/2/15
to php...@googlegroups.com

>
> I also applied a pattern similar to that, but realized afterwards that
> there were some issues with this approach.
>
> Consider this scenario:
>
> If there's a UserModel that throws a NotFound exception, then it
> makes a
> lot of sense for a controller to catch that and emit a 404 when
> retrieving information about a user.
>
> Lets consider there's also a BlogPost model that relies on the
> UserModel
> when creating new posts... This could be a:
>
> PUT /posts/new-post
>
>
> Aside from the rest, why the hell are you using PUT to create an entity?

If the client can determine the uri of the new entity, it's reasonable
to use PUT instead. It has much better defined semantics than POST.

>
> If a new post is created, referencing an author that doesn't exist in
> the UserModel, and this exception is not caught, it would bubble to the
> controller as a 404, when it should really have been a 409 Conflict.
>
>
> I understand what you mean, but you try to present a design flaw using
> another design flaw... For real, if you try to load a user that is
> linked to an entity and it throws a 404 in this case, then the problem
> you have here is that, either you didn't check if there was a user
> attached, or you failed to setup your relationships correctly in your
> database.

This is true, but that's also the purpose of an exception... It's an
exceptional case that somebody failed to handle. It's reasonable to
throw a NotFound exception, but if it was not caught in this context,
presenting it as a 404 to a user is not the appropriate result.

>
> If you absolutely need a user to exist to be tied to a post, then you
> should have a DENY on your relationship to prevent the user from
> disapearing.

True. Emphasis on *should have*.


>
> Furthermore, if you use a service based approach to load something that
> depends on something else, you should catch the exceptions of your
> dependency. There should be no exceptions thrown from dependent
> libraries or dependencies from your dependency unless they are clearly
> announced or else it means you are defeating the encapsulation principle
> where you should not know the inner workings of something. If you know
> that a UserNotFoundException is supposed to be thrown from the
> CarService because it tries to fetch the user associated with a car, you
> should handle the UserNotFoundException in CarBundle, not in your
> dependant module unless you are announcing it. (Which makes no sense anyway)

I should only catch exceptions if I can reasonably recover from this
exceptional situation, and opt into doing that. If you force a user to
catch every exception, you are mis-using exceptions as a means for
flow-control.

This is a common and often discussed anti-pattern.

>
> Finaly, 409 should be used when you are creating a conflict between the
> existing entity and the new entity data you are sending, simply said,
> merge conflicts or optimistic locking failure. It has nothing to do with
> an entity not being found in the context or if an error occurs on the
> server.

There's some room for interpretation when picking appropriate HTTP
status codes, and it's not terribly interesting for this discussion. For
the sake of this discussion, you can replace it for whatever HTTP status
you think is appropriate.

Suffice it to say that I think you're incorrect ;)

>
>
> Likewise, if I depend on a library that retrieves information for me,
> it's possible that a 404 is completely unexpected, and the only
> appropriate response is actually a 500.
>
> A better design is that *every* exception is turned into a 500 Internal
> Server Error, *unless* it's caught by the top-most controller, which
> understands enough of the context to turn it into a different status
> code.
>
>
> I'd like to see some proof of that... Technically, if you catch a
> UserNotFoundException into a 500, it makes it much harder to find out
> where the problem came from, log crunching, tracing, debugging, etc...
> Add the complexification of the controller to catch all potential
> exceptions...

This depends on what you do in your catch clause. I would rethrow a
different exception appropriate for its context, and set the earlier
exception as it's previous exception. I would not suggest discarding the
exception in every case.

Evert

Mathieu Dumoulin

unread,
Jul 2, 2015, 2:24:51 PM7/2/15
to php...@googlegroups.com
Hey there, love this discussion btw! Read on!


On Thursday, 2 July 2015 14:11:52 UTC-4, Evert Pot wrote:
If the client can determine the uri of the new entity, it's reasonable
to use PUT instead. It has much better defined semantics than POST.

I'll have to reread PUT just to make sure. I was under the impression that PUT/PATCH are only for updating existing entities. 

This is true, but that's also the purpose of an exception... It's an
exceptional case that somebody failed to handle. It's reasonable to
throw a NotFound exception, but if it was not caught in this context,
presenting it as a 404 to a user is not the appropriate result.

True. Emphasis on *should have*.

I should only catch exceptions if I can reasonably recover from this
exceptional situation, and opt into doing that. If you force a user to
catch every exception, you are mis-using exceptions as a means for
flow-control.

Mis-undertanding i guess. You should NEVER catch all exceptions, only the ones you can recover from, that i agree. What i meant is that if your services advertises that you can you can expect a UserNotFoundException in a certain case because you didn't provide a valid ID for example, then you should be handling it or at least advertising from your service that this could happen. The **much** better case would be to handle it yourself because then you create a dependency outside your service provider to other service provider **you** are using.
 
This is a common and often discussed anti-pattern.

Ahem... Exception flow control has nothing to do with what we are discussing. You may want to review what Exception flow control is defined as. Void methods with exception throwing here and there to simulate GOTO statements. This has nothing to do with throwing exceptions in a correctly designed environment when executing actions resulting in errors...

If you start placing "hasXYZ" and "canDoABC" in your application everywhere and control your app with ifs, you end up with a lot of extract useless code. I guess it's a taste but it is a very valid programming practice that has nothing to do with exception control flow.

There's some room for interpretation when picking appropriate HTTP
status codes, and it's not terribly interesting for this discussion. For
the sake of this discussion, you can replace it for whatever HTTP status
you think is appropriate.

Suffice it to say that I think you're incorrect ;)

I hear ya :)

Evert Pot

unread,
Jul 2, 2015, 3:41:22 PM7/2/15
to php...@googlegroups.com

>
> This is true, but that's also the purpose of an exception... It's an
> exceptional case that somebody failed to handle. It's reasonable to
> throw a NotFound exception, but if it was not caught in this context,
> presenting it as a 404 to a user is not the appropriate result.
>
> True. Emphasis on *should have*.
>
> I should only catch exceptions if I can reasonably recover from this
> exceptional situation, and opt into doing that. If you force a user to
> catch every exception, you are mis-using exceptions as a means for
> flow-control.
>
>
> Mis-undertanding i guess. You should NEVER catch all exceptions, only
> the ones you can recover from, that i agree. What i meant is that if
> your services advertises that you can you can expect a
> UserNotFoundException in a certain case because you didn't provide a
> valid ID for example, then you should be handling it or at least
> advertising from your service that this could happen. The **much**
> better case would be to handle it yourself because then you create a
> dependency outside your service provider to other service provider
> **you** are using.

The problem with this suggestion is that you now require everyone to
catch any potential NotFound exceptions, thrown now or in the future
(when a dependency updates).

If anyone forgets to handle the NotFound case at any place in the stack,
the exception will leak to the user as a 404, which is clearly
inappropriate, because it *should* have been caught, and the fact that
it hasn't been caught means that an implementer messed up, which should
result in a normal '500' error.

So just to conclude... I don't necessarily want to say that there's
absolutely 0 use for this type of exception, but the presented use-case
(a generic way for a front-controller to handle the 'resource not found'
case) is flawed.

> Ahem... Exception flow control has nothing to do with what we are discussing. You may want to review what Exception flow control is defined as. Void methods with exception throwing here and there to simulate GOTO statements. This has nothing to do with throwing exceptions in a correctly designed environment when executing actions resulting in errors...

Fair enough.

Evert

Mathieu Dumoulin

unread,
Jul 2, 2015, 4:12:07 PM7/2/15
to php...@googlegroups.com
Whhhaaaa???

Forget to handle?

My PSR is about creating a common library of exceptions to limit the repetition of existing exceptions across projects and thus enhance the interroperability by using a common set...

Mathieu Dumoulin

unread,
Jul 2, 2015, 7:44:01 PM7/2/15
to php...@googlegroups.com
Ok so i had time to fork and create the draft of the PSR, if you are interrested, go ahead, fork and PR or open discussions in the Issues tab.

Odalrick

unread,
Jul 3, 2015, 5:09:24 AM7/3/15
to Mathieu Dumoulin, php...@googlegroups.com
On Thu, 2 Jul 2015 10:23:37 -0700 (PDT)
Mathieu Dumoulin <craz...@crazycoders.net> wrote:
> >
> > If anything it should be a base interface, not a base exception.
> >
>
> For the sake of being logic and respecting design principles to the
> core, yes, it should be an interface. But the problem would be that
> you'd have to reimplement every damn method for exceptions everytime.

Derailing slightly; why would that be?

Some weird cornercase where php cant catch exceptions by interface?


Because it seems to me that this would solve at least 70% of _my_
objections to it. The rest of my objections are that your proposed
exceptions are too specific.

Create a generic hierarchy of interfaces for exceptions, completely
separate (but informed by) existing exceptions.


An example based on _some_ of your exeptions.

StandardException:
- IOException:
- FileSystemExeption:
- DirectoryException:
* DirectoryNotFoundException
* DirectoryNotReadableException
* DirectoryNotWritableException
* NotADirectoryException
- FileException:
* FileNotFoundException
* FileNotReadableException
* FileNotWritableException
* NotAFileException
- NetException:
* ConnectionLostException
* ConnectionRefusedException
* UnknownHostException
- DataException:
- ParsingException:
* InvalidJSONException
* InvalidXMLException
- DataTypeException:
* NotAnEmailException
* InvalidEmailFormatException
* InvalidIPAddressFormatException
* InvalidLengthException
* InvalidNumberException
- DataCollectionException:
- KeyExeption:
* IndexNotFoundException
* InvalidKeyException
* KeyAlreadyExistsException
* KeyNotFoundException
- UseExeption:
* ReadOnlyArrayException
* ReadOnlyArrayItemException
- LogicException:
* BadMethodCallException
* BadFunctionCallException
- RunTimeException:
* BadMethodCallException
* BadFunctionCallException

This is an inheritance structure; not a namespace one. Any leaves,
the ones marked with *, are examples of exceptions that could
implement the interface and would not be part of the spec.

A specific exceptions could implement more than one of these
interfaces; for instance a DirectoryNotReadableException might be both
a RunTimeException and a DirectoryException; or a
BadMethodCallException is a LogicException normally but a
RunTimeException when the method name is generated dynamically.

Converting a library would be as easy as going through all library
exceptions and slapping the appropriate interface on them.


/Odalrick

Mathieu Dumoulin

unread,
Jul 3, 2015, 6:29:41 AM7/3/15
to php...@googlegroups.com, craz...@crazycoders.net
On Friday, 3 July 2015 05:09:24 UTC-4, Nostger Grrrssddc wrote:
On Thu, 2 Jul 2015 10:23:37 -0700 (PDT)
Mathieu Dumoulin <craz...@crazycoders.net> wrote:
> >
> > If anything it should be a base interface, not a base exception.
> >
>
> For the sake of being logic and respecting design principles to the
> core, yes, it should be an interface. But the problem would be that
> you'd have to reimplement every damn method for exceptions everytime.

Derailing slightly; why would that be?

1) You can only throw Exception objects in PHP. To be able to throw an interface would require internal changes which is out of scope of PSRs. 
2) Interfaces don't implement anything, they declare stuff, so yeah, you would have to implement the getters/setters for each existing method: line, file, inner exception, messages, etc
3) You'd completely break backward compability unless you leave the Exception object there available, which is counter intuitive and instead of promoting good code, would promote confusion and more work, not the point of the PSRs at all
 
Some weird cornercase where php cant catch exceptions by interface?

Because it seems to me that this would solve at least 70% of _my_
objections to it. The rest of my objections are that your proposed
exceptions are too specific.

The goal of the PSR is to propose a set of specific base exceptions so that people stop defining useless custom ones that don't share meaning with similar others.

On the long run, it helps interrop because we can now associate meanings around different exceptions. Thats the basis of interroperability increasing the basis shared by all to increase their similar features and assure cohesion and communication!
I don't mind reviewing the structure, i'm not an almighty god of programming, in fact, i know many programmers that are much better than me, and i accept it very well. 

I chose a namespace structure because it makes it easier to navigate the exception base. In CrazyCodr\Standard-Exceptions, there are many more than the proposed PSR, and thus, it creates the need for a namespace structure to clearly split the items and know where you are going through the 50 or so exceptions. If you join me on the PSR, we'll discuss organization of the exceptions for sure!
 
A specific exceptions could implement more than one of these
interfaces; for instance a DirectoryNotReadableException might be both
a RunTimeException and a DirectoryException; or a
BadMethodCallException is a LogicException normally but a
RunTimeException when the method name is generated dynamically.

Almost all my exceptions in the current package (CrazyCodr\Standard-Exceptions) are extending \RuntimeException. Some of them don't directly extend \RuntimeException but sub classes of it.

In any case, we already know that interfaces is not the way to go because it wouldn't be workable. But, creating a correct inheritance tree is fine with me, we just have to strike a balance between the use cases and the easy of use.
 
Converting a library would be as easy as going through all library
exceptions and slapping the appropriate interface on them.

Sure it's easy to convert something, but the synchronization of all changes at once is pratically impossible. You are talking about a major backward incompatibility which is not in the spirit of PHP at all. May i remind you that some PHP 3 and 4 code can still run out there with PHP 5.6 or even PHP 7?

Odalrick

unread,
Jul 3, 2015, 7:59:03 AM7/3/15
to Mathieu Dumoulin, php...@googlegroups.com
On Fri, 3 Jul 2015 03:29:41 -0700 (PDT)
Mathieu Dumoulin <craz...@crazycoders.net> wrote:

> On Friday, 3 July 2015 05:09:24 UTC-4, Nostger Grrrssddc wrote:
> 1) You can only throw Exception objects in PHP. To be able to throw
> an interface would require internal changes which is out of scope of
> PSRs.

You'd still throw an Exception ; the question is "Can you catch
interfaces?" I'll try it myself...

https://gist.github.com/Odalrick/9e6f0f4c41adfc673b89

No problem what so ever.


> 2) Interfaces don't implement anything, they declare stuff, so
> yeah, you would have to implement the getters/setters for each
> existing method: line, file, inner exception, messages, etc
> 3) You'd completely break backward compability unless you leave the
> Exception object there available, which is counter intuitive and
> instead of promoting good code, would promote confusion and more
> work, not the point of the PSRs at all

Obviously you keep the existing infrastucture. Everything still extends
the base Exception class. It's just another interface.

I dont see any possible source of confusion. Old code will continue
working without changes; new code will benefit from a more
comprehensive taxonomy of exceptions. And converting a codebase to
throw "new style" exceptions takes an afternoon at most.

After all the _thrown_ exceptions implement the correct interfaces; you
can convert the _catch_ part at your leisure. It is only the thrown
exceptions that are part of the library's api.

> > Because it seems to me that this would solve at least 70% of _my_
> > objections to it. The rest of my objections are that your proposed
> > exceptions are too specific.
>
> The goal of the PSR is to propose a set of specific base exceptions
> so that people stop defining useless custom ones that don't share
> meaning with similar others.

Thats probably where we disagree. Even if there is a perfect match for
an execption in a future PSR; I'd still consider it best
practice to subclass that exception to make a specific one.

Having interfaces for the hierarchy seems perfect to me because it
disrupts nothing.

Also, I can combine meanings. An exception can be several different
interfaces. I'm not sure that is a good idea; I haven't had time to
think it through.

> On the long run, it helps interrop because we can now associate
> meanings around different exceptions.

Exactly, exceptions are messages/meanings. Our discussion is about
what the words and sentences should be.

My suggestion is that words should be interfaces, and you construct
senteses; the complete meaning; by implementing that interface in a
concrete class, that ultimately inherits from Exception.

You suggest that the PSR should provide complete sentenses and whenever
you throw an exception you select one of those prebuilt sentences and
go. Possibly you could modify the message by subclassing, but your goal
is to provide enough exceptions that this will never be necessary?

> I chose a namespace structure because it makes it easier to navigate
> the exception base.

Obviously we'd need both; they serve different purposes.

> Almost all my exceptions in the current package
> (CrazyCodr\Standard-Exceptions) are extending \RuntimeException. Some
> of them don't directly extend \RuntimeException but sub classes of it.

Roughly I think of RuntimeException as "data error" and LogicException
as "code error". It stands to reason that most exceptions are
"runtime"; LogicException means "fix the bug in your code".

> In any case, we already know that interfaces is not the way to go
> because it wouldn't be workable.

_I_ don't know that. I cant think of any reason why it wouldn't.

> You are talking about a
> major backward incompatibility which is not in the spirit of PHP at
> all. May i remind you that some PHP 3 and 4 code can still run out
> there with PHP 5.6 or even PHP 7?

Luckily it is very much in the spirit of php-fig; iirc PSR-0 is
incompatible with PSR-4. And of course namespaces.

I've dragged a codebase from php 5.2 to 5.4; and we still maintain the
5.2 branch. Let me tell you, maintaining the 5.2 branch is horrific.

(Granted, mostly because the codebase is horrific, but also because
namespaces. And anonymous functions. And everything else.)

/Odalrick

Mathieu Dumoulin

unread,
Jul 3, 2015, 8:14:23 AM7/3/15
to php...@googlegroups.com, craz...@crazycoders.net
I was under the impression you wanted to recode the exceptions as interfaces. My bad i guess, i don't want to reread the whole conversation anyway.

I think we both understand and agree on the same thing after all, just not the same format, me base exceptions, you interfaces.

I like your idea of using interfaces to "tag" meaning to exceptions and one of the paragraph of the PSR (at the bottom) actually discusses that.
What i'm against though is your part about having two meanings to an exception, this is bad design... Single responsibility please :)
Obviously, if we go down the route of interfaces it will be possible but you can't stop people from badly coding, you can only teach them not to.
So for the sake of keeping the PSR as user friendly as possible, well organized, useful, i'm ready to explore that route!

Like i said, i'll be more than happy to work something out with whoever wants to pitch in, just follow the link to the PSR and let's work on it.


I recommend we stop arguing about the structure of the project here and more in the repo's issues.

Let's keep this post here to succinct interest!

Chuck Burgess

unread,
Jul 3, 2015, 11:32:07 AM7/3/15
to php...@googlegroups.com
As as aside, we put together the "base exception interface" layout back in '09 when writing up new PEAR standards and policies for PEAR2 -- https://wiki.php.net/pear/rfc/pear2_exception_policy.  As best I can tell, that's the first instance of the idea, so that RFC was written to be a convincing argument of the layout.  It's not too long a read, methinks, so it could add some context to the discussion.

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.

Larry Garfield

unread,
Jul 3, 2015, 1:39:58 PM7/3/15
to php...@googlegroups.com
Let's keep posts here to avoid forking the conversation, because
GitHub's notification mechanism sucks donkey balls. :-)

FWIW, I completely agree with Odalrick. A deep hierarchy of Exception
classes is very brittle and gives me no flexibility to opt in and out of
things as I need. A set of defined Exception interfaces I can implement
on my to-be-thrown exception classes makes a lot of sense, as I can opt
into any number of categories of exception.

I recall once upon a time there was discussion about expecting every
project to have a project-specific exception interface that one could
catch, which did nothing but denote the package. Eg, I could
catch(DoctrineException $e) to catch any and all Doctrine-originating
exceptions, by interface. (And of course could still catch more
specific Doctrine extensions if I had special handling for those.) That
would make it easier to fully encapsulate some dependency and not let
knowledge of "Doctrine" leak out to other parts of the system. I would
be open to that as well.

class WidgetNotFormedCorrectlyException extends Exception implements
NotFoundException, WidgetException, InvalidArgumentException,
CrellsWidgetLibraryException {}

Now I can catch that exception by anything I want depending on my needs
and what error handling makes sense for my application.

(NotFound because no valid widget was located, Widget because it relates
to Widgets, InvalidArgument because I was passed an invalid widget, and
CrellsWidgetLibrary because then you can fully hide my library's errors
from something further up the stack than what's using it directly.
Obviously the exact list there is subject to debate, this is just an
example.)

--Larry Garfield

Tim Otten

unread,
Jul 4, 2015, 12:23:01 AM7/4/15
to php...@googlegroups.com
On Friday, July 3, 2015 at 10:39:58 AM UTC-7, Larry Garfield wrote:

class WidgetNotFormedCorrectlyException extends Exception implements
NotFoundException, WidgetException, InvalidArgumentException,
CrellsWidgetLibraryException {}

I like how this model allows one to mix different tags into an exception.

But... at the risk of fanciful...

When I first read the proposal for a standard exception library... it resonated a bit emotionally because exceptions aren't fun to declare. To achieve fairly thorough expressiveness, one needs to create a large number of almost single-use exception classes. ("Single use" in that there's realistically only one piece of code which would ever throw that exception.) When writing code to check for various error conditions (WidgetValidator), you might write 5 conditionals... and 5 throws... and 5 classes. In reality, those 5 conditions are often permutations of other concepts (e.g. "InvalidArgument" vs "MissingArgument" vs "Unauthorized"; "UserRecord" vs "OrganizationRecord" vs "BlogRecord"; "CrellPackage" vs "SymfonyPackage")

It would be nice if there were some way to skip the administrative drudgery of declaring new single-use exception classes. For example, perhaps an error could be practically defined as a combination of tags. However, I struggle to find a way to do that without changing PHP syntax.

```
// Allow exceptions using parametric types
throw new NotFoundExcpetion<Widget,CrellLibrary>();

// Allow exceptions as arrays or bags.
throw array(
  new NotFoundException("Widget type is unrecognized"),
  new WidgetException($widget),
  new InvalidArgumentException(),
  new CrellsWidgetLibraryException()
)
```

Mathieu Dumoulin

unread,
Jul 4, 2015, 7:12:13 AM7/4/15
to php...@googlegroups.com
The problem with this idea Tim is that although it matches more my original idea, it creates a gap between having a base of exceptions (or one/several basic generic enabled exceptions) and having a contract list.

I would love to see generics implemented into PHP. Back when i was doing a lot of .NET a few years ago, i loved the generic concept, it opens up lots of cool features about type flexibility and type hinting security. Considering how fast type based changes flow into PHP, i don't expect this to come to PHP before i die :P

Back to the problem at hand, maybe the PSR could suggest a link to a symfony command that generates exceptions for us based on the interface list we provide it. It would give us flexiblity to update our existing exceptions, do interfaced based catching, and create new exceptions with their tests (Maybe?) directly from the command line which would aleviate a lot of issues. Since exceptions usually follow mostly all the same pattern, the PSR could also define the best way to setup exceptions and generate the boilerplate for them?

Tim Otten

unread,
Jul 6, 2015, 7:43:03 PM7/6/15
to php...@googlegroups.com
On Jul 4, 2015, at 4:12 AM, Mathieu Dumoulin <craz...@crazycoders.net> wrote:

> The problem with this idea Tim is that although it matches more my original idea, it creates a gap between having a base of exceptions (or one/several basic generic enabled exceptions) and having a contract list.

I’m trying to understand the meaning of “a contract list”. It could be:

+ When writing a “throw” expression, one wants to browse the standardized list and pick a relevant class/interface (if there is a relevant one). This seems equally doable whether the standardized list takes the form of classes or interfaces.

+ When consuming a single function, one wants a good summary of its contract (e.g. “@throws” documentation which lists out interfaces that downstream is likely to consume). If the mental-model is focused on tags/interfaces rather than concrete implementations, then the current “@throws” idiom doesn't provide a proper listing of contract details. This should be more like:

- Current: @throws MyNodeAccessDeniedException
- Better: @throws MeInterface | AccessDeniedInterface

> I would love to see generics implemented into PHP. Back when i was doing a lot of .NET a few years ago, i loved the generic concept, it opens up lots of cool features about type flexibility and type hinting security. Considering how fast type based changes flow into PHP, i don't expect this to come to PHP before i die :P

Agree - PHP core isn’t going to change any time soon.

> Back to the problem at hand, maybe the PSR could suggest a link to a symfony command that generates exceptions for us based on the interface list we provide it. It would give us flexiblity to update our existing exceptions, do interfaced based catching, and create new exceptions with their tests (Maybe?) directly from the command line which would aleviate a lot of issues. Since exceptions usually follow mostly all the same pattern, the PSR could also define the best way to setup exceptions and generate the boilerplate for them?

FWIW, on second viewing, one can do an in-situ declaration with arbitrary combinations of tags without changing the core language — eg quick proof-of-concept:

https://gist.github.com/totten/65f254e4b81cc02a24d8#file-dyn-tag-exc-php-L40

Ultimately, I suppose these are two orthogonal problems with orthogonal solutions:

A. Problem: The taxonomy for exceptions/interfaces/traits/tags.
- Example Solutions: SPL Exceptions and your draft PSR.
- Why Standardize: Reduce learning costs for developers who work with multiple frameworks. Allow cross-cutting “catch” statements (where “try/catch” uses a common interface supported by multiple libraries).

B. Problem: The technique for implementing one-off concrete classes based on the various interfaces/traits.
- Example Solutions: An eval() wrapper or code-scanner (like in my POC). A CLI code-generator (eg `app/console generate:exception FooBundle AccessDeniedException`).
- Why Standardize: Reduce learning costs for developers who work with multiple frameworks. Avoid duplicate code-scanners in build process (ie one scanner can serve all packages/libraries/bundles).

The two problems could coupled or decoupled.

Mathieu Dumoulin

unread,
Jul 7, 2015, 8:00:08 AM7/7/15
to php...@googlegroups.com


On Monday, 6 July 2015 19:43:03 UTC-4, Tim Otten wrote:
On Jul 4, 2015, at 4:12 AM, Mathieu Dumoulin <craz...@crazycoders.net> wrote:

> The problem with this idea Tim is that although it matches more my original idea, it creates a gap between having a base of exceptions (or one/several basic generic enabled exceptions) and having a contract list.

I’m trying to understand the meaning of “a contract list”. It could be:

A contract list is a list of interface in my jargon, sorry :)
 
 + When writing a “throw” expression, one wants to browse the standardized list and pick a relevant class/interface (if there is a relevant one). This seems equally doable whether the standardized list takes the form of classes or interfaces.

 + When consuming a single function, one wants a good summary of its contract (e.g. “@throws” documentation which lists out interfaces that downstream is likely to consume). If the mental-model is focused on tags/interfaces rather than concrete implementations, then the current “@throws” idiom doesn't provide a proper listing of contract details. This should be more like:

  - Current: @throws MyNodeAccessDeniedException
  - Better: @throws MeInterface | AccessDeniedInterface

I wouldn't change the signature of the throws annotation, just add many annotations, and only the ones that fit the context...

For the exception base/contract list, i'm afraid that if we provide only one of the two, the acceptance rate will be limited. But if we provide both, then the @throws everywhere are going to be mixed up and a lot of young programmers are going to state they are throwing concrete exception classes instead of interfaces, while others, some of the too serious guys considering every possible edge case, will take the interfaces too seriously and probably not use the base exceptions when they truly fit and then disregard the exception base...

Unless we can state in the PSR something really clear and simple on how to use the exception library such as : 

 - Use base exceptions when applicable to refrain from creating useless copies of existing exceptions, these base exceptions implement the different similar interfaces.
 - Extend base exceptions when needed to create a more domain specific context
 - Use interfaces directly inside of your existing exceptions that you don't want to replace with base exceptions because it would break you backward compatibility
 - Always catch exception interfaces unless you need to catch some very specific concrete domain exception not present in the generic interfaces
 
> I would love to see generics implemented into PHP. Back when i was doing a lot of .NET a few years ago, i loved the generic concept, it opens up lots of cool features about type flexibility and type hinting security. Considering how fast type based changes flow into PHP, i don't expect this to come to PHP before i die :P

Agree - PHP core isn’t going to change any time soon.

Well PHP7 will have scalar types it seems and return types (object for sure and i think scalar return types too)...
 
> Back to the problem at hand, maybe the PSR could suggest a link to a symfony command that generates exceptions for us based on the interface list we provide it. It would give us flexiblity to update our existing exceptions, do interfaced based catching, and create new exceptions with their tests (Maybe?) directly from the command line which would aleviate a lot of issues. Since exceptions usually follow mostly all the same pattern, the PSR could also define the best way to setup exceptions and generate the boilerplate for them?

FWIW, on second viewing, one can do an in-situ declaration with arbitrary combinations of tags without changing the core language — eg quick proof-of-concept:

https://gist.github.com/totten/65f254e4b81cc02a24d8#file-dyn-tag-exc-php-L40

Yuck... This is... functional but very wrong i think because code inception cannot find those exceptions anymore and we want to simplify the life of programmers out there, maybe at the cost of flexibility, but this is wrong for all the reasons you mention in the gist and for the fact that no one can easily incept on your code unless your IDE is smart enough to convert @throws to types.
 
Ultimately, I suppose these are two orthogonal problems with orthogonal solutions:

 A. Problem: The taxonomy for exceptions/interfaces/traits/tags.
    - Example Solutions: SPL Exceptions and your draft PSR.
    - Why Standardize: Reduce learning costs for developers who work with multiple frameworks. Allow cross-cutting “catch” statements (where “try/catch” uses a common interface supported by multiple libraries).

 B. Problem: The technique for implementing one-off concrete classes based on the various interfaces/traits.
  - Example Solutions: An eval() wrapper or code-scanner (like in my POC). A CLI code-generator (eg `app/console generate:exception FooBundle AccessDeniedException`).
  - Why Standardize: Reduce learning costs for developers who work with multiple frameworks. Avoid duplicate code-scanners in build process (ie one scanner can serve all packages/libraries/bundles).

The two problems could coupled or decoupled.

Agreed but i'd add these two:  

C. Problem: Numerous copies of existing SPL exceptions exist throughout all projects to give domain specific behavior. For example: InvalidArgumentException, FileNotFoundException, EntityNotFoundException.
  - Example Solutions: Provide a base structure to build on (either interfaces or base exceptions or both)
  - Why Standardize: Reduce learning costs for developers who work with multiple frameworks or tools. Avoid duplicate code in all projects (good) which ultimately helps in making development easier, less error prone

D. Problem: Interchanging libraries is hard because we often use domain specific exceptions instead of base exceptions. Current state forces us to adopt really low and generic level exceptions (SPL) or create lots of DomainExceptions which reduce the interop.
 - Example Solution: Provide a base structure to build on
 - Why standardize: If exceptions are caught on a common basis and libraries that are interchangable are based on a common basis too (outside the scope of this PSR) then we can easily swap libraries.

Anthony Ferrara

unread,
Jul 7, 2015, 11:53:34 AM7/7/15
to php...@googlegroups.com
Suggestion:

Don't specify standard exception types.

Instead, let's work on a set of common ones for PHP 7.1, and namespace
them inside of \php.

The majority of the proposed list can sit comfortably in core. And
defining them here gives little benefit to interoperability (except
that interfaces can define from this standard set). So if we did it at
the language level everyone can benefit from them being built-in.

Additionally, it would help to get a more generic (non-fw-centric)
view point on the set of exceptions...

Anthony

On Tue, Jul 7, 2015 at 8:00 AM, Mathieu Dumoulin
> --
> You received this message because you are subscribed to the Google Groups
> "PHP Framework Interoperability Group" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to php-fig+u...@googlegroups.com.
> To post to this group, send email to php...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/php-fig/8cb8c8bc-6481-42ab-bea2-f4ffa372342e%40googlegroups.com.

Mathieu Dumoulin

unread,
Jul 7, 2015, 12:15:07 PM7/7/15
to php...@googlegroups.com
Sorry but i'm pretty new in that field of standards and php rfcs, how would we/I go down that route Anthony?

Mathieu

Mathieu Dumoulin

unread,
Jul 8, 2015, 1:28:16 PM7/8/15
to php...@googlegroups.com
Regarding what Antony Ferrara said, i think there is still room to create a PSR along side a PHP 7.1 RFC (or whatever the process may be) because not everyone will move to PHP7 and thus, this will either force developpers to not adopt the new exception base or wait a very long time to be adopted.

I think it would be a good think to setup a PSR package that makes exceptions available under the \php namespace like Anthony proposed so that anyone not in PHP 7.1 will still benefit from these exceptions. Adding a version constraint for this package shouldn't be too hard IMO.

What do you think?

Jeremy Lindblom

unread,
Jul 8, 2015, 2:18:20 PM7/8/15
to php...@googlegroups.com
I think a better solution would be that you attempt a PHP RFC. If it passes, then someone (not FIG) could create a shim package for those that are not on PHP7.

In general though, I'm not really interested in this idea. Sorry.

--
Jeremy Lindblom (@jeremeamia)
President of the Seattle PHP User Group (@seaphp)
PHP-FIG Representative for the Guzzle project


--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.

Paul Dragoonis

unread,
Jul 9, 2015, 6:04:01 AM7/9/15
to php...@googlegroups.com
On Wed, Jul 8, 2015 at 6:39 PM, Jeremy Lindblom <jerem...@gmail.com> wrote:
I think a better solution would be that you attempt a PHP RFC. If it passes, then someone (not FIG) could create a shim package for those that are not on PHP7.

In general though, I'm not really interested in this idea. Sorry.

This matches my thoughts.
 

--
Jeremy Lindblom (@jeremeamia)
President of the Seattle PHP User Group (@seaphp)
PHP-FIG Representative for the Guzzle project


On Wed, Jul 8, 2015 at 10:28 AM, Mathieu Dumoulin <craz...@crazycoders.net> wrote:
Regarding what Antony Ferrara said, i think there is still room to create a PSR along side a PHP 7.1 RFC (or whatever the process may be) because not everyone will move to PHP7 and thus, this will either force developpers to not adopt the new exception base or wait a very long time to be adopted.

I think it would be a good think to setup a PSR package that makes exceptions available under the \php namespace like Anthony proposed so that anyone not in PHP 7.1 will still benefit from these exceptions. Adding a version constraint for this package shouldn't be too hard IMO.

What do you think?

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/618c0335-d596-4097-9a57-1e601b874636%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages