Skip to content
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

Enforce throwables/exceptions as rejection reasons #93

Merged
merged 12 commits into from
Apr 18, 2019

Conversation

jsor
Copy link
Member

@jsor jsor commented Mar 7, 2017

Because rejections are the promise counter parts of throwing an exception, throwables/exceptions should be enforced as rejection reasons.

Also, ReactPHP has always used exceptions as rejection reasons throughout it's components.

Closes #46

@jsor jsor added this to the v3.0 milestone Mar 7, 2017
@jsor jsor force-pushed the enforce-exception-reasons branch from 66ce56b to 411a415 Compare March 7, 2017 19:51

namespace React\Promise\Exception;

class CompositeException extends \Exception
Copy link

Choose a reason for hiding this comment

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

How about MultiReasonException?

Copy link
Member

Choose a reason for hiding this comment

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

No objections to either class name, but the use of this class appears a bit unclear. Does it make sense to add documentation for this class and/or mark this class a @internal?

Copy link
Member Author

@jsor jsor Sep 21, 2017

Choose a reason for hiding this comment

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

Added documentation in 07b7afd. I think it makes sense to keep it public API as it might be useful for custom collection functions.

@jsor jsor requested review from WyriHaximus and clue March 8, 2017 19:48
@clue
Copy link
Member

clue commented Mar 16, 2017

I like the idea of only accepting Exception here, but can you elaborate on the rationale to also support Throwable? Thanks!

@jsor
Copy link
Member Author

jsor commented Mar 16, 2017

@clue I thought about that myself, but if only Exceptions are accepted, it's not possible to forward rejections easily.

ini_set('assert.exception', 1);

$promise = new \React\Promise\Promise(function() {
    assert(1 === 0);
});

$nextPromise = $promise->otherwise(function($e) {
    handle($e);

    // $e is AssertionError and can't be passed to reject()
    return \React\Promise\reject($e);
});

A solution would be to just rethrow, but that needs to be documented (and the documentation read by the user).

$nextPromise = $promise->otherwise(function($e) {
    handle($e);

    throw $e;
});

@kelunik
Copy link

kelunik commented Mar 16, 2017

@clue Why would one want to not support Throwable?

@WyriHaximus
Copy link
Member

Explicitly not supporting Throwable's would make this PR useless in PHP 7 environments when TypeErrors bubble up, or anything else from PHP implementing Throwable, for example Error

@stefanotorresi
Copy link

isn't the point of this PR to be "not longer possible to reject a promise without a reason
or with another promise."
?
FWIW I think it makes perfectly sense to support Throwables. What's the counter argument?

@clue
Copy link
Member

clue commented Apr 5, 2017

I'm 👍 on getting this in and didn't mean to question whether supporting Throwable is useful here but rather that this is not exactly clear from a consumer perspective (checking just the README). Does it make sense to describe this feature and its implications in the README? 👍

@jsor
Copy link
Member Author

jsor commented Apr 5, 2017

@clue In fact, \Exception is only mentioned for PHP 5 support. Where do you want to see more documentation and about what exactly?

@jsor
Copy link
Member Author

jsor commented Aug 24, 2017

Ping @clue

Copy link
Member

@clue clue left a comment

Choose a reason for hiding this comment

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

Good catch! I think this feature and corresponding BC break makes perfect sense 👍

I have a couple of questions regarding the new custom exception classes, otherwise LGTM 👍

src/Exception/CompositeException.php Outdated Show resolved Hide resolved
src/Exception/InvalidArgumentException.php Outdated Show resolved Hide resolved

namespace React\Promise\Exception;

class CompositeException extends \Exception
Copy link
Member

Choose a reason for hiding this comment

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

No objections to either class name, but the use of this class appears a bit unclear. Does it make sense to add documentation for this class and/or mark this class a @internal?

src/Exception/InvalidArgumentException.php Outdated Show resolved Hide resolved
@jsor jsor requested a review from clue September 21, 2017 06:10
@kelunik
Copy link

kelunik commented Feb 21, 2018

@jsor Would you be up for standardizing the CompositeException with us @ FIG? We have a MultiReasonException that basically does the same. Non-async libraries might also have a use if they invoke several callbacks and only throw an exception for all failed ones afterwards. Standardization could help with building support for this into exception reporting libraries, e.g. symfony/symfony#25883.

@jsor jsor changed the title Enforce throwables/exceptions as rejection reasons [WIP] Enforce throwables/exceptions as rejection reasons Jun 12, 2018
@jsor
Copy link
Member Author

jsor commented Jun 12, 2018

I've added [WIP] to the PR title. We still need to figure out how we can keep interoperabilty with other promise implementations which allow rejecting with non-exception reasons.

new Promise(function($resolve, $reject) use ($foreignPromise) {
    $foreignPromise->then($resolve, $reject);
});

This would now reject Promise with a \InvalidArgumentException in case $foreignPromise is rejected with a non-exception.

One solution would be to wrap the reason in a ReasonException like i did here:

https://github.com/jsor-labs/pact/blob/8ec4ea971c9cc08676081d0b5998def26dc4a165/src/Promise.php#L629-L637

@clue
Copy link
Member

clue commented Jun 12, 2018

We still need to figure out how we can keep interoperabilty with other promise implementations which allow rejecting with non-exception reasons.

Do we have a relevant use case where this happens?

@jsor
Copy link
Member Author

jsor commented Jun 12, 2018

@clue Promise\resolve() for example

if (method_exists($promiseOrValue, 'then')) {
$canceller = null;
if (method_exists($promiseOrValue, 'cancel')) {
$canceller = [$promiseOrValue, 'cancel'];
}
return new Promise(function ($resolve, $reject) use ($promiseOrValue) {
$promiseOrValue->then($resolve, $reject);
}, $canceller);
}

@clue
Copy link
Member

clue commented Jun 20, 2018

@jsor It's my understanding that if a foreign promise rejects with something that is not an Exception/Throwable, the reject() method will automatically turn this into an InvalidArgumentException with an appropriate message, no? This sounds like a good approach to me!

As far as I can tell, the ecosystem of foreign promises also seem to rely on Exceptions for the most part. Consumers that actually need foreign promises with custom rejection reasons can still use an explicit foreign rejection handler and turn this into an appropriate Exception type themselves.

Are you seeing anything that is missing here and/or is commonly used in other projects?

@jsor jsor changed the title [WIP] Enforce throwables/exceptions as rejection reasons Enforce throwables/exceptions as rejection reasons Apr 13, 2019
@WyriHaximus WyriHaximus self-requested a review April 13, 2019 14:04
Copy link
Member

@WyriHaximus WyriHaximus left a comment

Choose a reason for hiding this comment

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

LGTM :shipit: !

@jsor
Copy link
Member Author

jsor commented Apr 18, 2019

ping @clue

Copy link
Member

@clue clue left a comment

Choose a reason for hiding this comment

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

Changes LGTM! :shipit: 🎉

@clue
Copy link
Member

clue commented Apr 18, 2019

One thing: Once this is in, I would like to look into enforcing a Throwable type hint here instead of manually throwing an InvalidArgumentException. This will likely affect PHP version compatibility, so I think it's best to discuss this in a follow-up ticket 👍

@WyriHaximus WyriHaximus merged commit 04e5cfc into master Apr 18, 2019
@WyriHaximus
Copy link
Member

One thing: Once this is in, I would like to look into enforcing a Throwable type hint here instead of manually throwing an InvalidArgumentException. This will likely affect PHP version compatibility, so I think it's best to discuss this in a follow-up ticket

Well either of us can do that, but I'd like to get an PR out A.S.A.P. adding exactly that and raising minimum PHP version to 7.0

@clue clue deleted the enforce-exception-reasons branch April 19, 2019 08:28
@clue
Copy link
Member

clue commented Apr 19, 2019

@WyriHaximus Go for it! :shipit:

@WyriHaximus
Copy link
Member

@clue 🎉 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enforce \Exception instances as rejection values
5 participants