Skip to content

Instantly share code, notes, and snippets.

@Zeronights
Last active December 17, 2015 00:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zeronights/5518445 to your computer and use it in GitHub Desktop.
Save Zeronights/5518445 to your computer and use it in GitHub Desktop.
PHP Try Catch Finally emulator.
<?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