namespace Ice;
/**
* Exception class.
*
* @package Ice/Exception
* @category Error
* @author Ice Team
* @copyright (c) 2014-2025 Ice Team
* @license http://iceframework.org/license
*/
class Exception extends \Exception
{
/**
* Creates a new exception.
* Translate exception's message using the [I18n] class.
*
* @param mixed message Error message
* @param mixed code The exception code
* @param Exception|Throwable previous Previous exception
*/
public function __construct(var message = "", code = 0, previous = null)
{
var di, values, str;
let di = Di::$fetch();
if typeof message == "array" {
let values = array_slice(message, 1),
str = message[0];
} else {
let values = null,
str = message;
}
// Check if translation module is available.
if di->has("i18n") {
let message = di->get("i18n")->translate(str, values);
} elseif typeof values == "array" {
// Check if values is associative or sequential
if count(array_filter(array_keys(values), "is_string")) {
let message = strtr(str, values);
} else {
let message = call_user_func_array("sprintf", message);
}
}
parent::__construct((string) message, (int) code, previous);
}
/**
* Get the full trace as string.
*
* @param Exception|Throwable $e
* @return string
*/
public function getFullTraceAsString(e)
{
var output, frame, args, arg, node;
int count = 0;
let output = "";
for frame in e->getTrace() {
let args = "";
if isset frame["args"] {
let node = [];
for arg in frame["args"] {
switch typeof arg {
case "string":
let node[] = "'" . arg . "'";
break;
case "array":
let node[] = "Array";
break;
case "NULL":
let node[] = "NULL";
break;
case "boolean":
let node[] = arg ? "true" : "false";
break;
case "object":
let node[] = "Object(" . get_class(arg) . ")";
break;
case "resource":
let node[] = arg;
break;
default:
let node[] = arg;
break;
}
}
let args = join(", ", node);
}
let output .= sprintf(
"#%s %s: %s(%s)\n",
count,
(isset frame["file"] ? frame["file"] . "(" . frame["line"] . ")" : "[internal function]"),
(isset frame["class"] ? frame["class"] . frame["type"] . frame["function"] : frame["function"]),
args
),
count++;
}
return output;
}
/**
* PHP error handler, converts all errors into ErrorExceptions. This handler respects error_reporting settings.
*
* @throws ErrorException
* @return true
*/
public static function errorHandler(int code, string message, string file = null, int line = 0, array context = [])
{
if error_reporting() & code {
// This error is not suppressed by current error reporting settings
// Convert the error into an ErrorException
throw new \ErrorException(message, code, 0, file, line);
}
// Do not execute the PHP error handler
return true;
}
/**
* Inline exception handler, displays the error message, source of the exception, and the stack trace of the error.
*
* @param Exception|Throwable $e
* @return void
*/
public static function handler(e)
{
var di, response;
let di = Di::$fetch(),
response = di->get("response");
// Clear the previous response body
response->setBody("");
di->applyHook("exception.after.uncaught", [e, di]);
if response->getBody() {
echo response->send();
} else {
throw e;
}
exit(1);
}
/**
* Catches errors that are not caught by the error handler.
* E_PARSE, E_ERROR, E_CORE_ERROR, E_USER_ERROR
*
* @return void
*/
public static function shutdownHandler()
{
var e;
let e = error_get_last();
if typeof e == "array" && in_array(e["type"], [E_PARSE, E_ERROR, E_CORE_ERROR, E_USER_ERROR]) {
// Clean the output buffer
ob_get_level();
ob_clean();
// Fake an exception for nice debugging
Exception::handler(new \ErrorException(e["message"], e["type"], 0, e["file"], e["line"]));
// Shutdown now to avoid a "death loop"
exit(1);
}
}
}