-
-
Save Zeronights/5518445 to your computer and use it in GitHub Desktop.
PHP Try Catch Finally emulator.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
The Block class uses Reflection and instantiated class member access to | |
cleanly emulate the missing Finally construct. There are some drawbacks | |
in that you cannot access out of scope variables because each block is | |
a Closure so you either have to use it within a class with class | |
properties ($this) or use the global keyword. You cannot return from | |
the catch blocks either. While this may not suite everyone, I posted it | |
up for those who could use it. | |
If an exception is thrown in a catch, it is rethrown after the finally | |
closure is run. You can chain catch statements with different Exception | |
types and the class will figure out which catch block to use for the | |
exception. If no exception is caught, it is thrown again after the | |
finally closure is run. | |
Example usage after the class. | |
Ozzy - zeronights.com | |
*/ | |
class Block { | |
public function __call($method, $params) { | |
switch ($method) { | |
case 'try': | |
return call_user_func_array([$this, '__try'], $params); | |
case 'catch': | |
return call_user_func_array([$this, '__catch'], $params); | |
case 'finally': | |
return call_user_func_array([$this, '__finally'], $params); | |
default: | |
throw new \BadMethodCallException( | |
sprintf('%s::%s method does not exist.', get_class($this), $method) | |
); | |
} | |
} | |
protected $caught_exception = null; | |
protected $uncaught_exception = null; | |
protected $caught = false; | |
protected function __try(\Closure $func) { | |
try { | |
$func(); | |
} catch (Exception $e) { | |
$this->caught_exception = $e; | |
} | |
return $this; | |
} | |
protected function __catch(\Closure $func) { | |
$catch = new \ReflectionFunction($func); | |
if ($catch->getNumberOfRequiredParameters() !== 1) { | |
throw new \InvalidArgumentException( | |
sprintf('Unable to catch exception, only one parameter must be specified') | |
); | |
} | |
$reflection = $catch->getParameters(); | |
$type = $reflection[0]->getClass()->name; | |
if (!$this->caught && !$this->uncaught_exception && $this->caught_exception instanceof $type) { | |
$this->caught = true; | |
try { | |
$func($this->caught_exception); | |
} catch (\Exception $e) { | |
$caught = true; | |
$this->uncaught_exception = $e; | |
} | |
$this->caught_exception = null; | |
} | |
return $this; | |
} | |
protected function __finally(\Closure $func) { | |
$func(); | |
if ($this->caught_exception instanceof \Exception) { | |
throw $this->caught_exception; | |
} | |
if ($this->uncaught_exception instanceof \Exception) { | |
throw $this->uncaught_exception; | |
} | |
} | |
} | |
(new Block)->try(function() { | |
throw new Exception('test'); | |
})->catch(function(Exception $e) { | |
echo $e->getMessage(); | |
})->finally(function() { | |
echo 'cleanup'; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment