3v4l.org

run code in 200+ php & hhvm versions
Bugs & Features
<?php class ErrorHandler { /** * Error number constant used when an error is silenced by use of the error * control operator '@'. * * @var int */ const E_SILENCED_ERROR = 0; /** * Chain before * @var string */ const CHAIN_BEFORE = 'before'; /** * Chain after * @var string */ const CHAIN_AFTER = 'after'; /** * Don't chain * @var bool */ const CHAIN_NONE = false; /** * @var string|bool */ private $_chainPreviousHandler = self::CHAIN_AFTER; /** * Gets if the previous handler will be called. Values are: before, after, false * @return string|bool */ public function getChainPreviousHandler() { return $this->_chainPreviousHandler; } /** * Sets if the previous handler will be called. Values are: before, after, false * @param string|bool $chainPreviousHandler */ public function setChainPreviousHandler($chainPreviousHandler) { $this->_chainPreviousHandler = $chainPreviousHandler; } /** * @var bool */ private $_usePhpDefaultBehaviour = true; /** * Gets if the default PHP behaviour for errors will be allowed at the end * of the error handling. * @return bool */ public function isUsePhpDefaultBehaviour() { return $this->_usePhpDefaultBehaviour; } /** * Sets if the default PHP behaviour for errors will be allowed at the end * of the error handling. * @param bool $usePhpDefaultBehaviour */ public function setUsePhpDefaultBehaviour($usePhpDefaultBehaviour) { $this->_usePhpDefaultBehaviour = $usePhpDefaultBehaviour; } /** * @var bool */ private $_convertErrorsToExceptions = true; /** * Gets if this handler will convert recoverable errors to exceptions * @return bool */ public function isConvertErrorsToExceptions() { return $this->_convertErrorsToExceptions; } /** * Sets if this handler will convert recoverable errors to exceptions * @param bool $convertErrorsToExceptions */ protected function setConvertErrorsToExceptions($convertErrorsToExceptions) { $this->_convertErrorsToExceptions = $convertErrorsToExceptions; } /** * Array of default errors converted to exceptions. * @var array */ private $_errorsToExceptions = array( self::E_SILENCED_ERROR, // Silenced errors E_USER_ERROR, E_RECOVERABLE_ERROR); /** * Gets an array with all the error numbers to be converted to exceptions * @return array */ public function getErrorsToExceptions() { return $this->_errorsToExceptions; } /** * Sets an array with all the error numbers to be converted to exceptions * @param array $errorsToExceptions */ public function setErrorsToExceptions(array $errorsToExceptions) { $this->_errorsToExceptions = $errorsToExceptions; } /** * Adds errors to the array of errors that will throw an exception * @param array $errorsToExceptions */ public function addErrorsToExceptions(array $errorsToExceptions) { foreach($errorsToExceptions as $pos => $errorNumber) { if (!in_array($errorNumber, $this->_errorsToExceptions)) { $this->_errorsToExceptions[] = $errorNumber; } } } /** * @var bool */ private $_registered = false; /** * Gets if this class was registered to be the default error handler for * php errors. * @return bool */ public function isRegistered() { return $this->_registered; } /** * Sets if this class was registered to be the default error handler for * php errors. * @param bool $registered */ protected function setRegistered($registered) { $this->_registered = $registered; } /** * @var mixed */ private $_previousHandler = null; /** * Gets the previously set global error handler * @return mixed */ public function getPreviousErrorHandler() { return $this->_previousHandler; } /** * Sets the previously set global error handler * @param mixed $previousHandler */ protected function setPreviousErrorHandler($previousHandler) { $this->_previousHandler = $previousHandler; } /** * @var array */ private $_errorHandlers = array(); /** * Gets stack of error handlers * @return array */ public function getErrorHandlers() { return $this->_errorHandlers; } /** * Adds a handler to the stack of error handlers * @param callable $errorHandler */ public function addErrorHandler(callable $errorHandler) { // Avoid duplicated error handlers $this->removeErrorHandler($errorHandler); $this->_errorHandlers[]= $errorHandler; } /** * Remove the error handler comparing the one provided with the ones stored. * It does not affect the previous error handler. * @param callable $errorHandler * @return boolean True if an error handler was found and removed or false * if not. */ public function removeErrorHandler($errorHandler) { $type = gettype($errorHandler); $found = false; foreach($this->_errorHandlers as $pos => $existingErrorHandler) { if ($type != gettype($existingErrorHandler)) { continue; } $remove = false; if ($type == 'string' && $errorHandler == $existingErrorHandler) { $found = true; } else if ($type == 'array' && $errorHandler[0] == $existingErrorHandler[0] && $errorHandler[1] == $existingErrorHandler[1]) { $found = true; } else if (is_object($errorHandler) && $errorHandler == $existingErrorHandler) { $found = true; } if ($remove) { unset($this->_errorHandlers[$pos]); $found = true; break; } } return $found; } /** * Finds the name of an error constant. * * @param int $error * @return string | false */ public function getErrorName($error) { $constants = get_defined_constants(); $name = array_search($error, $constants, true); return $name; } /** * Calls every error handler passing in the arguments. * * If an error handler throws an exception the rest of error handlers will * not be called. * * If this class is set as the default error handler the previous handler * will be called after all handlers have been called. * * Error handlers must conform to standard error handler signature for * set_error_handler function but with an additional optional parameter * whose value will be this same instance. * * This method follows the following order: * 1- Execute previous error handler (only uf previous error handler is * chained before) * 2- Execute error handlers * 3- Execute previous error handler (only uf previous error handler is * chained after) * 4- Throw exceptions for errors (optional) * 5- Call PHP's default error handler if configured to do so. * * Error handlers should not raise more errors but they can throw exceptions * as they will not be handled in this method so they can be handled * upstream. * * @param int $errorNumber * @param string $errorMessage * @param string $file * @param int $line * @param array $context * @throws \ErrorException When error to exception conversion is enabled * @throws \Exception When any error handler throws it * @return bool */ public function handleError($errorNumber, $errorMessage, $file, $line, $context) { $errorParams = array( $errorNumber, $errorMessage, $file, $line, $context, $this ); if ($this->_previousHandler && $this->_chainPreviousHandler == self::CHAIN_BEFORE) { call_user_func_array($this->_previousHandler, $errorParams); } foreach($this->_errorHandlers as $errorHandler) { call_user_func_array($errorHandler, $errorParams); } if ($this->_previousHandler && $this->_chainPreviousHandler == self::CHAIN_AFTER) { call_user_func_array($this->_previousHandler, $errorParams); } // The last thing we do is throwing ourselves an exception in the // error handler as this will halt execution and we need to call // all error handlers first. if ($this->_convertErrorsToExceptions) { $this->throwErrorException($errorNumber, $errorMessage, $file, $line, $context); } if ($this->_usePhpDefaultBehaviour) { return false; } else { return true; } } /** * Throws an ErrorException with the given information. * * This method does not honors error_reporting setting and will throw an * exception for every configured error number as it makes no sense to * convert a certain error to an exception and then allow it to be * "silenced". * * @param int $errorNumber * @param string $errorMessage * @param string $file * @param int $line * @param array $context * @throws \ErrorException Always */ protected function throwErrorException($errorNumber, $errorMessage, $file, $line, array $context) { if (in_array($errorNumber, $this->_errorsToExceptions)) { throw new \ErrorException( $errorMessage, $errorNumber, 0, $file, $line); } } /** * Registers this instance to handle PHP's errors (recoverable ones only) * @throws \Exception If the error handler is registered twice for some * reason. * @return boolean True if this handler was not registered, false if it * was. */ public function registerErrorHandler() { if ($this->_registered) { return false; } $previousHandler = set_error_handler(array($this, 'handleError')); if (is_array($previousHandler) && !empty($previousHandler) && $previousHandler[0] === $this) { throw new \Exception("Error handler registered twice!"); } $this->_previousHandler = $previousHandler; $this->_registered = true; return true; } /** * Unregisters this instance if it was set to handle PHP's errors * @return boolean True if this handler was registered, false if it doesn't. */ public function unregisterErrorHandler() { if (!$this->_registered) { return false; } // If one or more error handlers were set after we registered our own // this will pop the last one set and leave our own one in the stack // :( restore_error_handler(); $this->_registered = false; $this->_previousHandler = null; return true; } }
based on A0kHi
Output for 5.4.0 - 7.2.0