Skip to content

Instantly share code, notes, and snippets.

@sshymko
Created February 15, 2020 06:10
Show Gist options
  • Save sshymko/0bc2ea1a2f3b23c6c5f7952492a51acd to your computer and use it in GitHub Desktop.
Save sshymko/0bc2ea1a2f3b23c6c5f7952492a51acd to your computer and use it in GitHub Desktop.
Chaining of immutable throwable exceptions/errors (PHP8 proposal)
<?php
interface Throwable
{
/**
* Return immutable copy with causal chain extended by given root cause
*
* @return Throwable
*/
public function chain(Throwable $cause = null): Throwable;
}
class Exception implements Throwable
{
public function chain(Throwable $cause = null): Throwable
{
// Return immutable self with already matching causal chain
if (!$cause) {
return $this;
}
// Create immutable copy of causal chain extended by given root cause
$prev = $this->getPrevious()
? $this->getPrevious()->chain($cause)
: $cause;
// Create immutable copy of self with the extended causal chain
$self = clone $this;
$self->previous = $prev;
return $self;
}
}
@sshymko
Copy link
Author

sshymko commented Feb 15, 2020

Use Case

Re-throwing multiple exceptions aggregated during graceful fallback that eventually fails.

// Independent sub-systems unaware of each other (potentially 3rd party code)
$callbacks = [
    function () {
        throw new Exception('Fault 1');
    },
    function () {
        throw new Exception('Fault 2');
    },
    function () {
        throw new Exception('Fault 3');
    },
];

// Client code consuming the sub-systems in a fallback manner:
try {
    $error = null;
    foreach ($callbacks as $callback) {
        try {
            $result = $callback();
            // Handle success...
        } catch (Throwable $e) {
            $error = $e->chain($error);
        }
    }
    if ($error) {
        throw $error;
    }
} catch (Throwable $e) {
    // Handle failure...
    echo $e;
}

Exceptions are chained in order of occurrence carrying informative history of execution:

Exception: Fault 1 in exception_chaining.php:43
Stack trace:
#0 exception_chaining.php(57): {closure}()
#1 {main}

Next Exception: Fault 2 in exception_chaining.php:46
Stack trace:
#0 exception_chaining.php(57): {closure}()
#1 {main}

Next Exception: Fault 3 in exception_chaining.php:49
Stack trace:
#0 exception_chaining.php(57): {closure}()
#1 {main}

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