3v4l.org

run code in 300+ PHP versions simultaneously
<?php /** * @package at.exceptable * @author Adrian <adrian@enspi.red> * @copyright 2014 - 2016 * @license GPL-3.0 (only) * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License, version 3. * The right to apply the terms of later versions of the GPL is RESERVED. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program. * If not, see <http://www.gnu.org/licenses/gpl-3.0.txt>. */ declare(strict_types = 1); namespace at\exceptable; use ErrorException, Throwable; class Handler { /** @type _Handler[] list of registered error handlers. */ private $_errorHandlers = []; /** @type _Handler[] list of registered exception handlers. */ private $_exceptionHandlers = []; /** @type _Handler[] list of registered shutdown handlers. */ private $_shutdownHandlers = []; /** @type bool is this Handler currently registered (active)? */ private $_registered = false; /** @type int error types which should be thrown as ErrorExceptions. */ private $_throw = 0; /** * registers this handler to invoke a callback, and then restores the previous handler(s). * * @param callable $callback the callback to execute * @param mixed …$arguments arguments to pass to the callback * @return mixed the value returned from the callback */ public function during(callable $callback, ...$arguments) { $this->register(); $value = $callback(...$arguments); $this->unregister(); return $value; } /** * adds an error handler. * @see <http://php.net/set_error_handler> $error_handler * * @param callable $handler the error handler to add * @param int $severity the error severity to trigger this handler * (one of the E_* constants, or omit for "any severity") * @return Handler $this */ public function onError(callable $handler, int $severity=null) : Handler { $this->_errorHandlers[] = new _Handler($handler, _Handler::TYPE_ERROR, $severity); return $this; } /** * adds an exception handler. * @see <http://php.net/set_exception_handler> $exception_handler * * @param callable $handler the exception handler to add * @param int $severity the exception severity to trigger this handler * (one of Exceptable::ERROR|WARNING|NOTICE, or omit for "any severity") * @return Handler $this */ public function onException(callable $handler, int $severity=null) : Handler { $this->_exceptionHandlers[] = new _Handler($handler, _Handler::TYPE_EXCEPTION, $severity); return $this; } /** * adds a shutdown handler. * @see <http:/php.net/register_shutdown_handler> $callback * * @param callable $handler the shutdown handler to add * @param mixed $arguments optional agrs to pass to shutdown handler when invoked * @return Handler $this */ public function onShutdown(callable $handler, ...$arguments) : Handler { $this->_shutdownHandlers[] = (new _Handler($handler, _Handler::TYPE_SHUTDOWN)) ->defaultArguments($arguments); return $this; } /** * registers this Handler's error, exception, and shutdown handlers. * * @return Handler $this */ public function register() : Handler { set_error_handler(function(...$args) { return $this->_error(...$args); }); set_exception_handler(function(...$args) { return $this->_exception(...$args); }); register_shutdown_function(function(...$args) { return $this->_shutdown(...$args); }); $this->_registered = true; return $this; } /** * specifies the ErrorException class for this handler to use. * @todo keep this? * * @param string $fqcn fully qualified ErrorException classname * @return Handler $this */ public function setErrorExceptionClass(string $fqcn) : Handler { throw new ExceptableException('not yet implemented'); } /** * sets error types which should be thrown as ErrorExceptions. * * @param int $severity the error types to be thrown * (defaults to Errors and Warnings; use 0 to stop throwing) * @return Handler $this */ public function throw(int $severity=E_ERROR|E_WARNING) : Handler { $this->_throw = $severity; return $this; } /** * un-registers this Handler. * * @return Handler $this */ public function unregister() : Handler { restore_error_handler(); restore_exception_handler(); // shutdown functions can't be unregistered; just have to flag them so they're non-ops :( $this->_registered = false; return $this; } /** * handles php errors. * * @param int $s error severity * @param string $m error message * @param string $f error file * @param int $l error line * @param array $c error context * @throws ErrorException if error severity matches $_throw setting * @return bool true if error handled; false if php's error handler should continue */ protected function _error(int $s, string $m, string $f, int $l, array $c) : bool { if (! $this->_registered) { return false; } if (($s & $this->_throw) === $s) { throw new ErrorException($m, 0, $s, $f, $l); } foreach ($this->_errorHandlers as $handler) { if ($handler->handles(_Handler::TYPE_ERROR, $s) && $handler->handle($s, $m, $f, $l, $c)) { return true; } } return false; } /** * handles uncaught exceptions. * * @param Throwable $e the exception * @throws ExceptableException if no registered handler handles the exception */ protected function _exception(Throwable $e) { if (! $this->_registered) { return; } $severity = method_exists($e, 'getSeverity') ? $e->getSeverity() : Exceptable::ERROR; foreach ($this->_exceptionHandlers as $handler) { if ($handler->handles(_Handler::TYPE_EXCEPTION, $severity) && $handler->handle($e)) { return; } } throw new ExceptableException(ExceptableException::UNCAUGHT_EXCEPTION, $e); } /** * handles shutdown sequence. * * @throws ErrorException if shutdown is due to a fatal error */ protected function _shutdown() { if (! $this->_registered) { return; } $e = error_get_last(); if ($e && $e['type'] === E_ERROR) { throw new ErrorException($e['message'], 0, $e['type'], $e['file'], $e['line']); } foreach ($this->_shutdownHandlers as $handler) { $handler(); } } } /** @internal utility class for wrapping callables as error/exception/shutdown handlers. */ class _Handler { const TYPE_ERROR = 1; const TYPE_EXCEPTION = 2; const TYPE_SHUTDOWN = 3; const ANY_SEVERITY = -1; protected $_arguments = []; protected $_handler; protected $_type; protected $_severity; public function __construct(callable $handler, int $type, int $severity=null) { $this->_handler = $handler; $this->_type = $type; $this->_severity = $severity ?? self::ANY_SEVERITY; } /** @internal invokes the callback with given arguments. */ public function handle(...$arguments) : bool { try { return ($this->_handler)(...($arguments + $this->_arguments)); } catch (Throwable $e) { throw new ExceptableException( ExceptableException::INVALID_HANDLER, ['type' => $this->_type()], $e ); } } /** @internal checks whether this handler is registered for the given severity. */ public function handles(int $type, int $severity) : bool { return $type === $this->_type && ($this->_severity === self::ANY_SEVERITY || ($severity && $this->_severity) === $severity); } /** @internal specifies default arguments to pass to handler. */ public function defaultArguments(array $arguments) { $this->_arguments = $arguments; return $this; } /** @internal gets a string representation of the handler type. */ protected function _type() : string { switch ($this->_type) { case self::TYPE_ERROR : return 'error'; case self::TYPE_EXCEPTION : return 'exception'; case self::TYPE_SHUTDOWN : return 'shutdown'; default : return (string) $this->_type; } } } interface Exceptable extends \Throwable { /** * exception severity levels. * * @type int ERROR error * @type int WARNING warning * @type int NOTICE notice */ const ERROR = E_ERROR; const WARNING = E_WARNING; const NOTICE = E_NOTICE; /** * default info for unknown/generic exception cases. * * @type int DEFAULT_CODE * @type int DEFAULT_MESSAGE * @type int DEFAULT_SEVERITY */ const DEFAULT_CODE = 0; const DEFAULT_MESSAGE = 'unknown error.'; const DEFAULT_SEVERITY = self::ERROR; /** * gets information about a code known to the implementing class. * * @param int $code the exception code to look up * @throws UnderflowException if the code is not known to the implementation * @return array a map of info about the code, * including (at a minimum) its "code", "severity", and "message". */ public static function get_info(int $code) : array; /** * checks whether the implementation has info about the given code. * * @param int $code the code to check * @return bool true if the code is known; false otherwise */ public static function has_info(int $code) : bool; /** * @param string $0 exception message * if omitted, a message must be set based on the exception code * @param int $1 exception code * if omitted, a default code must be set * @param Throwable $2 previous exception * @param array $3 additional exception context * @throws ExceptableException if argument(s) are invalid */ public function __construct(...$args); /** * adds contextual info to this exception. * * @param array $context map of info to add * @return $this */ public function addContext(array $context) : Exceptable; /** * gets contextual info about this exception. * * @return array map of contextual info about this exception */ public function getContext() : array; /** * traverses the chain of previous exception(s) and gets the root exception. * * @return Throwable the root exception */ public function getRoot() : \Throwable; /** * gets exception severity. * * @return int the exception severity */ public function getSeverity() : int; /** * checks the exception severity. * * @return bool true if exception severity is "Error"; false otherwise */ public function isError() : bool; /** * checks the exception severity. * * @return bool true if exception severity is "Warning"; false otherwise */ public function isWarning() : bool; /** * checks the exception severity. * * @return bool true if exception severity is "Notice"; false otherwise */ public function isNotice() : bool; /** * adds contextual info to this exception. * * @param int $severity one of Exceptable::ERROR|WARNING|NOTICE * @throws ExceptableException if severity is invalid * @return $this */ public function setSeverity(int $severity) : Exceptable; } class ExceptableException extends Exception { /** * @type int NO_SUCH_CODE invalid exception code. * @type int INVALID_CONSTRUCT_ARGS invalid/out-of-order constructor arguments. * @type int INVALID_SEVERITY invalid severity level. * @type int UNCAUGHT_EXCEPTION uncaught/unhandled exception during runtime. * @type int INVALID_HANDLER invalid handler (e.g., wrong signature, or throws). */ const NO_SUCH_CODE = 1; const INVALID_CONSTRUCT_ARGS = (1<<1); const INVALID_SEVERITY = (1<<2); const UNCAUGHT_EXCEPTION = (1<<3); const INVALID_HANDLER = (1<<4); /** @see Exceptable::INFO */ const INFO = [ self::NO_SUCH_CODE => [ 'message' => 'no such code', 'severity' => Exceptable::WARNING, 'tr_message' => "no exception code '{code}' is known" ], self::INVALID_CONSTRUCT_ARGS => [ 'message' => 'constructor arguments are invalid and/or out of order', 'severity' => Exceptable::ERROR, 'tr_message' => "constructor arguments are invalid and/or out of order: {args}" ], self::INVALID_SEVERITY => [ 'message' => 'invalid severity', 'severity' => Exceptable::WARNING, 'tr_message' => 'severity must be one of Exceptable::ERROR|WARNING|NOTICE; {severity} provided' ], self::UNCAUGHT_EXCEPTION => [ 'message' => 'uncaught exception', 'severity' => Exceptable::ERROR, 'tr_message' => 'no registered handler caught exception: {__rootMessage__}' ], self::INVALID_HANDLER => [ 'message' => 'invalid handler', 'severity' => Exceptable::ERROR, 'tr_message' => 'invalid handler [{type}]: {__rootMessage__}' ] ]; } $handler = new Handler(); $handler->onError(function($s, $m, $f, $l, $c) { error_log($m); }) ->register(); trigger_error('foo!', E_USER_NOTICE);
Output for 8.0.0 - 8.0.30, 8.1.0 - 8.1.27, 8.2.0 - 8.2.18, 8.3.0 - 8.3.4, 8.3.6
Fatal error: Uncaught Error: Class "at\exceptable\Exception" not found in /in/u8c1o:392 Stack trace: #0 {main} thrown in /in/u8c1o on line 392
Process exited with code 255.
Output for 8.3.5
Warning: PHP Startup: Unable to load dynamic library 'sodium.so' (tried: /usr/lib/php/8.3.5/modules/sodium.so (libsodium.so.23: cannot open shared object file: No such file or directory), /usr/lib/php/8.3.5/modules/sodium.so.so (/usr/lib/php/8.3.5/modules/sodium.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0 Fatal error: Uncaught Error: Class "at\exceptable\Exception" not found in /in/u8c1o:392 Stack trace: #0 {main} thrown in /in/u8c1o on line 392
Process exited with code 255.
Output for 7.3.0 - 7.3.33, 7.4.0 - 7.4.33
Fatal error: Uncaught Error: Class 'at\exceptable\Exception' not found in /in/u8c1o:392 Stack trace: #0 {main} thrown in /in/u8c1o on line 392
Process exited with code 255.
Output for 7.0.0 - 7.0.20, 7.1.0 - 7.1.33, 7.2.6 - 7.2.33
Fatal error: Class 'at\exceptable\Exception' not found in /in/u8c1o on line 392
Process exited with code 255.

preferences:
162.86 ms | 402 KiB | 194 Q