yf_core_errors class

Custom error handler.

Fileplugins/sys/classes/yf_core_errors.class.php
Use_class('core_errors')

Public properties

A site sets them in its config with $PROJECT_CONF['core_errors']['NAME'] = ...;

PropertyDefaultDescription
$CLEAN_OUTPUT_BUFFERS
bool
true Drop partial output before the 500 page while headers are not sent; tests capture output with their own buffer and turn it off
$ENV_ARRAYS
string
'GPF' Could be any sequence from GPFCS
$ERROR_LEVEL
int
0 Error reporting level
$ERROR_LEVEL_DEBUG
int
30711 Error reporting level for DEBUG_MODE enabled
$ERROR_LEVEL_PROD
int
0 Error reporting level for production/non-debug mode (int from built-in constants)
$ERROR_LOG_PATH
string
'{LOGS_PATH}yf_core_errors.log' Log errors switcher, keep empty to disable logging
$ERROR_MODE false
$ERROR_PAGES
bool
false Off by default until 4.0: 500 page for uncaught exceptions and fatals
$HTML_ERRORS true
$LOG_CONTEXT_FIELDS
array
['get', 'post', 'files', 'cookie', 'session', 'qs', ... (7 items)]
array (
  0 => 'get',
  1 => 'post',
  2 => 'files',
  3 => 'cookie',
  4 => 'session',
  5 => 'qs',
  6 => 'server',
)
Request sections an error log row keeps: get, post, files, cookie, session, qs (qs, url) and server (ip, ref, us); the fields of a section left out are written empty. Error log rows only, not the logger() db handler columns
$LOG_DEPRECATED_TO_FILE
bool
true Log deprecated to the error file?
$LOG_ERRORS_TO_FILE
bool
true Log errors to the error file?
$LOG_NOTICES_TO_FILE
bool
false Log notices to the error file?
$LOG_REDACT
bool
false SECURITY: mask the values of LOG_REDACT_KEYS in the request data of error log rows (get, post, files, cookie, session; query string pairs and user:pass@ of qs, url, ref) and drop call arguments from the trace (src); also the request columns and exception traces written by the logger() db / file handlers. Default false keeps the rows as before; 4.0 flips. The debug console (DEBUG_MODE) shows the request unmasked, as before: it is the developer's own request.
$LOG_REDACT_KEYS
array
['password', 'pass', 'pswd', 'passwd', 'pwd', 'secret', ... (20 items)]
array (
  0 => 'password',
  1 => 'pass',
  2 => 'pswd',
  3 => 'passwd',
  4 => 'pwd',
  5 => 'secret',
  6 => 'token',
  7 => 'csrf',
  8 => 'api_key',
  9 => 'apikey',
  10 => 'auth',
  11 => 'authorization',
  12 => 'card',
  13 => 'cc_',
  14 => 'cvv',
  15 => 'cvc',
  16 => 'iban',
  17 => 'phpsessid',
  18 => 'sid_',
  19 => 'remember',
)
Case-insensitive parts of a key whose value LOG_REDACT masks, at any depth; the current session_name() is added
$LOG_WARNINGS_TO_FILE
bool
true Log warnings to the error file?
$NO_NOTICES
bool
true Quickly turn off notices
$SHOW_DETAILS
null|bool
null Show class, message, file and trace on the 500 page; null follows DEBUG_MODE
$USE_COMPACT_FORMAT
Use
true compact format
$USE_LOGGER
bool
false Also hand every handled error and exception to logger() (off by default until 4.0: the default error_log handler would add a line per warning to the PHP error log of every site)
$_LOG_STARTED
bool
false @conf_skip Started log output or not
$_SHOW_BORDERS
bool
false Show start and end log headers or not
$_in_logger
bool
false @conf_skip Re-entrancy guard of the logger calls
$error_log_filename
string
'yf_core_errors{suffix}.log' The filename of the log file. NOTE: $error_log_filename will only be used if you have log_errors Off and ;error_log filename in php.ini if log_errors is On, and error_log is set, the filename in error_log will be used.
$error_types
array
[1 => 'E_ERROR', 2 => 'E_WARNING', 4 => 'E_PARSE', 8 => 'E_NOTICE', 16 => 'E_CORE_ERROR', 32 => 'E_CORE_WARNING', ... (15 items)]
array (
  1 => 'E_ERROR',
  2 => 'E_WARNING',
  4 => 'E_PARSE',
  8 => 'E_NOTICE',
  16 => 'E_CORE_ERROR',
  32 => 'E_CORE_WARNING',
  64 => 'E_COMPILE_ERROR',
  128 => 'E_COMPILE_WARNING',
  256 => 'E_USER_ERROR',
  512 => 'E_USER_WARNING',
  1024 => 'E_USER_NOTICE',
  30719 => 'E_ALL',
  4096 => 'E_RECOVERABLE_ERROR',
  8192 => 'E_DEPRECATED',
  16384 => 'E_USER_DEPRECATED',
)
@conf_skip Standard error types
$logger_levels
array
[1 => 'error', 256 => 'error', 4096 => 'error', 2 => 'warning', 512 => 'warning', 8 => 'notice', ... (9 items)]
array (
  1 => 'error',
  256 => 'error',
  4096 => 'error',
  2 => 'warning',
  512 => 'warning',
  8 => 'notice',
  1024 => 'notice',
  8192 => 'debug',
  16384 => 'debug',
)
@conf_skip PHP error type => PSR-3 level for logger()
$pattern_ignore '~^(Undefined array key|Undefined variable)~'

Public methods 31

error_handler

public function error_handler($error_type, $error_msg, $error_file, $error_line)

The error handling routine set by set_error_handler(). return true = Don't execute PHP internal error handler.

    public function error_handler($error_type, $error_msg, $error_file, $error_line)
    {
        if ($this->pattern_ignore && preg_match($this->pattern_ignore, $error_msg)) {
            return true;
        }

        // quickly turn off notices logging
        if ($this->NO_NOTICES && ($error_type == E_NOTICE || $error_type == E_USER_NOTICE)) {
            return true;
        }
        // PHP 8 still calls the handler for @-silenced errors; those stay out of the log like they stay off the screen
        defined('DEBUG_MODE') && DEBUG_MODE && (error_reporting() & $error_type) && $this->_debug_record($error_type, $error_msg, $error_file, $error_line);
        (error_reporting() & $error_type) && $this->_to_logger($this->logger_levels[$error_type] ?? 'warning', $error_msg, [
            'file' => $error_file,
            'line' => $error_line,
            'type' => $error_type,
        ]);
        $msg = '';
        $save_log = false;
        // Process critical errors
        if ($error_type == E_ERROR || $error_type == E_USER_ERROR) {
            if ($this->LOG_ERRORS_TO_FILE) {
                $save_log = true;
            }
            // Process warnings errors
        } elseif ($error_type == E_WARNING || $error_type == E_USER_WARNING) {
            if ($this->LOG_WARNINGS_TO_FILE) {
                $save_log = true;
            }
            // Process notices
        } elseif ($error_type == E_NOTICE || $error_type == E_USER_NOTICE) {
            if ($this->LOG_NOTICES_TO_FILE) {
                $save_log = true;
            }
        } elseif ($error_type == E_DEPRECATED) {
            if ($this->LOG_DEPRECATED_TO_FILE) {
                $save_log = true;
            }
        }
        $IP = is_object(common()) ? common()->get_ip() : false;
        if ( ! $IP) {
            $IP = ($_SERVER['REMOTE_ADDR'] ?? null);
        }
        $trace = array_slice($this->LOG_REDACT ? $this->_log_trace_lines() : explode(PHP_EOL, main()->trace_string()), 0, 5);
        if ($save_log) {
            $msg = json_encode($this->_log_row_filter([
                'time' => date('Y-m-d H:i:s'),
                'type' => $this->error_types[$error_type],
                'msg' => str_replace(["\r", PHP_EOL], '', $error_msg) . ';',
                'src' => implode(';', $trace),
                'site' => conf('SITE_ID'),
                'ip' => $IP,
                'qs' => WEB_PATH . (strlen($_SERVER['QUERY_STRING'] ?? '') ? '?' . ($_SERVER['QUERY_STRING'] ?? null) : ''),
                'url' => 'http://' . ($_SERVER['HTTP_HOST'] ?? '') . ($_SERVER['REQUEST_URI'] ?? ''),
                'ref' => ($_SERVER['HTTP_REFERER'] ?? null),
                'get' => $this->_log_display_array('GET'),
                'post' => $this->_log_display_array('POST'),
                'files' => $this->_log_display_array('FILES'),
                'cookie' => $this->_log_display_array('COOKIE'),
                'session' => $this->_log_display_array('SESSION'),
                'us' => ($_SERVER['HTTP_USER_AGENT'] ?? ''),
            ])) . PHP_EOL;

            if ( ! $this->_LOG_STARTED) {
                if ($this->_SHOW_BORDERS) {
                    $this->_do_save_log_info('START EXECUTION' . PHP_EOL, 1);
                }
                $this->_LOG_STARTED = true;
            }
            $this->_do_save_log_info($msg);
        }
        if (($this->ERROR_LEVEL & $error_type) && strlen($msg)) {
            if ($this->HTML_ERRORS) {
                $tpl   = '<b>%s</b>: <pre>%s</pre> (<i>%s on line %d</i>)<pre>%s</pre><br>' . PHP_EOL;
                $msg   = _prepare_html($error_msg);
                $trace = _prepare_html(main()->trace_string());
            } else {
                $tpl = '%s: %s (%s on line %d)' . PHP_EOL . '%s' . PHP_EOL;
                $msg   = $error_msg;
                $trace = main()->trace_string();
            }
            printf(
                $tpl,
                $this->error_types[$error_type],
                $msg,
                $error_file,
                $error_line,
                $trace
            );
        }
        return true;
    }
public function error_handler_destructor()

Destructor.

    public function error_handler_destructor()
    {
        // Restore startup working directory
        chdir(main()->_CWD);
        // Send the endian log text if errors exists
        if ($this->_LOG_STARTED && $this->_SHOW_BORDERS) {
            $this->_do_save_log_info('END EXECUTION' . PHP_EOL, 1);
        }
    }
public function exception_handler($exception)
    public function exception_handler($exception)
    {
        $traceline = '#%s %s(%s): %s(%s)';
        $msg = "YF core errors: Uncaught exception '%s' with message '%s' in %s:%s\nStack trace:\n%s\n  thrown in %s on line %s";

        $trace = $exception->getTrace();
        foreach ($trace as $key => $stackPoint) {
            $trace[$key]['args'] = array_map('gettype', $trace[$key]['args'] ?? []);
        }

        $result = [];
        $key = null;
        foreach ($trace as $key => $stackPoint) {
            $result[] = sprintf(
                $traceline,
                $key,
                isset($stackPoint['file']) ? $stackPoint['file'] : '',
                isset($stackPoint['line']) ? $stackPoint['line'] : '',
                isset($stackPoint['function']) ? $stackPoint['function'] : '',
                implode(', ', isset($stackPoint['args']) ? $stackPoint['args'] : [])
            );
        }
        $result[] = '#' . ++$key . ' {main}';

        $msg = sprintf(
            $msg,
            get_class($exception),
            $exception->getMessage(),
            $exception->getFile(),
            $exception->getLine(),
            implode(PHP_EOL, $result),
            $exception->getFile(),
            $exception->getLine()
        );

        $error_id = $this->ERROR_PAGES ? $this->_error_id() : '';
        error_log($error_id ? $error_id . ' ' . $msg : $msg);
        if ($error_id) {
            $this->_to_logger('critical', 'Uncaught exception', ['exception' => $exception, 'error_id' => $error_id]);
        } else {
            $this->_to_logger('error', 'Uncaught exception', ['exception' => $exception]);
        }

        if ($error_id && ! $this->_is_cli()) {
            $this->_clean_output_buffers();
            echo $this->_error_500_page($error_id, $this->_show_details() ? $this->_exception_details($exception) : '');
        } elseif ($this->ERROR_MODE) {
            if ($this->HTML_ERRORS) {
                echo '<pre>' . _prepare_html($msg) . '</pre>';
            } else {
                echo $msg;
            }
        }
    }
public function init_reporting($force_error_mode = null)

Method that changes the error reporting level.

    public function init_reporting($force_error_mode = null)
    {
        // New mode designed to show errors without enabling DEBUG_MODE
        if (defined('ERROR_MODE')) {
            $this->ERROR_MODE = constant('ERROR_MODE');
        }
        $conf_error_mode = conf('ERROR_MODE');
        if ($conf_error_mode) {
            $this->ERROR_MODE = $conf_error_mode;
        }
        if (DEBUG_MODE) {
            $this->ERROR_MODE = true;
        }
        if (isset($force_error_mode)) {
            $this->ERROR_MODE = $force_error_mode;
        }

        $this->ERROR_LEVEL = $this->ERROR_LEVEL_PROD;
        if ($this->ERROR_MODE) {
            $this->ERROR_LEVEL = $this->ERROR_LEVEL_DEBUG;
        }
        if (defined('ERROR_LEVEL')) {
            $this->ERROR_LEVEL = constant('ERROR_LEVEL');
        }
        // Set value to E_ERROR is you want to turn off error level to minimum
        $conf_error_level = conf('ERROR_LEVEL');
        if ($conf_error_level) {
            $this->ERROR_LEVEL = $conf_error_level;
        }

        error_reporting($this->ERROR_LEVEL);
        ini_set('ignore_repeated_errors', 1);
        ini_set('ignore_repeated_source', 1);

        if (php_sapi_name() == 'cli') {
            $this->HTML_ERRORS = false;
            ini_set('html_errors', false);
        }

        set_error_handler([$this, 'error_handler'], E_ALL);
        set_exception_handler([$this,  'exception_handler']);

        register_shutdown_function([$this, 'error_handler_destructor']);
    }
public function restore_handler()

Method that restores the error handler to the default error handler.

    public function restore_handler()
    {
        restore_error_handler();
    }
public function return_handler()

Method that returns the error handler to error_handler().

    public function return_handler()
    {
        set_error_handler([$this, 'error_handler']);
    }
public function set_log_file_name($filename)

Method that changes the filename of the generated log file.

    public function set_log_file_name($filename)
    {
        $dir = dirname($filename);
        if ( ! file_exists($dir)) {
            mkdir($dir, 0755, true);
        }
        $this->error_log_filename = $filename;
    }

Methods starting with _ hooks and helpers; not routable as page actions

__call

public function __call($name, $args)

Catch missing method call.

    public function __call($name, $args)
    {
        return main()->extend_call($this, $name, $args);
    }

__construct

public function __construct()

Constructor.

    public function __construct()
    {
        if ($this->ERROR_LOG_PATH) {
            ini_set('error_log', main()->_replace_core_paths($this->ERROR_LOG_PATH));
        }
        $this->set_log_file_name($this->_get_file_path());

        $this->init_reporting();
    }
public function _clean_output_buffers()

Partial page output must not precede the 500 page; after headers are sent the page is appended.

    public function _clean_output_buffers()
    {
        if ( ! $this->CLEAN_OUTPUT_BUFFERS || headers_sent()) {
            return false;
        }
        while (ob_get_level() > 0) {
            ob_end_clean();
        }
        return true;
    }

_debug_record

public function _debug_record($error_type, $error_msg, $error_file, $error_line)

The message for the debug console's errors tab (DEBUG_MODE only; debug() keeps at most 500).

    public function _debug_record($error_type, $error_msg, $error_file, $error_line)
    {
        // full: no trace to build for a record debug() would drop
        if ( ! function_exists('debug') || count((array) debug('php_errors')) >= 500) {
            return false;
        }
        return debug('php_errors[]', [
            'type' => $this->error_types[$error_type] ?? (string) $error_type,
            'message' => (string) $error_msg,
            'file' => (string) $error_file,
            'line' => (int) $error_line,
            'time' => microtime(true),
            'trace' => function_exists('main') && is_object(main()) ? main()->trace_string() : '',
        ]);
    }
public function _do_save_log_info($msg, $add_time = false)

Save log info to file or stdout.

    public function _do_save_log_info($msg, $add_time = false)
    {
        if ($add_time) {
            $msg = date('Y-m-d H:i:s') . ' - ' . $msg;
        }
        // Save log to file
        if ($this->error_log_filename == '') {
            error_log($msg, 0);
        } else {
            $log_dir = dirname($this->error_log_filename);
            if ( ! file_exists($log_dir)) {
                mkdir($log_dir, 0755, true);
            }
            error_log($msg, 3, $this->error_log_filename);
        }
    }
public function _error_500_page($error_id, $details = '')

500 page; falls back to inline HTML when the template engine is not available (bootstrap errors).

  • $details mixed Raw text, shown only when not empty
    public function _error_500_page($error_id, $details = '')
    {
        $this->_send_status(500);
        try {
            $vars = [
                'error_id' => _prepare_html($error_id),
                'details' => $details === '' ? '' : _prepare_html($details),
            ];
            $html = $this->_render_500_template($vars);
            if (trim($html) !== '') {
                return $html;
            }
        } catch (Throwable $e) {
        }
        return '<h1>Internal Server Error</h1><p>Error id: ' . htmlspecialchars((string) $error_id, ENT_QUOTES) . '</p>';
    }

_error_id

public function _error_id()
    public function _error_id()
    {
        return uniqid('err_');
    }
public function _exception_details($exception)

Text for the developer part of the 500 page, not escaped.

    public function _exception_details($exception)
    {
        return get_class($exception) . ': ' . $exception->getMessage() . PHP_EOL
            . $exception->getFile() . ':' . $exception->getLine() . PHP_EOL
            . $exception->getTraceAsString();
    }
public function _get_file_path()
    public function _get_file_path()
    {
        $main = main();
        $is = [
            'unit' => $main->is_unit_test(),
            'admin' => MAIN_TYPE == 'admin',
            'user' => MAIN_TYPE == 'user',
            'console' => $main->is_console(),
            'ajax' => $main->is_ajax(),
            'debug' => $main->is_debug(),
        ];
        $suffix = '';
        foreach ($is as $name => $enabled) {
            if ($enabled) {
                $suffix .= '_' . $name;
            }
        }
        $file_path = defined('ERROR_LOGS_FILE') ? constant('ERROR_LOGS_FILE') : APP_PATH . 'logs/' . $this->error_log_filename;
        $file_path = str_replace('{suffix}', $suffix, $file_path);
        $this->error_log_filename = $file_path;
        return $this->error_log_filename;
    }

_is_cli

public function _is_cli()
    public function _is_cli()
    {
        return PHP_SAPI === 'cli';
    }
public function _log_display_array($array_name = '')

Display array.

    public function _log_display_array($array_name = '')
    {
        if (empty($array_name)) {
            return '';
        }
        $A = match ($array_name) {
            'GET' => $_GET,
            'POST' => $_POST,
            'FILES' => $_FILES,
            'COOKIE' => $_COOKIE,
            'SESSION' => $_SESSION ?? null,
            'SERVER' => $_SERVER,
            'REQUEST' => $_REQUEST,
            'ENV' => $_ENV,
            default => null,
        };
        if (empty($A)) {
            return '';
        }
        if ($this->LOG_REDACT && is_array($A)) {
            $A = $this->_log_redact_array($A, $this->_log_redact_pattern());
        }
        $output = str_replace(["\r", PHP_EOL], '', var_export($A, 1));
        $output = preg_replace('/^array \((.*?)[\,]{0,1}\)$/i', '$1', $output);
        return '_' . $array_name . '=' . $output;
    }
public function _log_exception_trace(Throwable $e)

getTraceAsString() of a throwable without its call arguments.

  • $e Throwable
    public function _log_exception_trace(Throwable $e)
    {
        $lines = $this->_log_format_frames($e->getTrace());
        $lines[] = '#' . count($lines) . ' {main}';
        return implode("\n", $lines);
    }
public function _log_format_frames(array $frames)

Lines in the getTraceAsString() format, "***" in place of the arguments of a call that had any.

  • $frames array
    public function _log_format_frames(array $frames)
    {
        $lines = [];
        foreach (array_values($frames) as $i => $frame) {
            $where = isset($frame['file']) ? $frame['file'] . '(' . ($frame['line'] ?? 0) . ')' : '[internal function]';
            $call = ($frame['class'] ?? '') . ($frame['type'] ?? '') . ($frame['function'] ?? '');
            $lines[] = '#' . $i . ' ' . str_replace(["\r", "\n"], ' ', $where) . ': ' . $call . '(' . (empty($frame['args']) ? '' : '***') . ')';
        }
        return $lines;
    }
public function _log_redact_array(array $data, $pattern, $depth = 0)

Values under a matching key are masked whole, arrays included.

  • $data array
    public function _log_redact_array(array $data, $pattern, $depth = 0)
    {
        if ($pattern === '') {
            return $data;
        }
        foreach ($data as $key => $value) {
            if (preg_match($pattern, (string) $key)) {
                $data[$key] = '***';
            } elseif (is_array($value)) {
                $data[$key] = $depth < 20 ? $this->_log_redact_array($value, $pattern, $depth + 1) : '***';
            }
        }
        return $data;
    }
public function _log_redact_pattern()

Regex matching any of LOG_REDACT_KEYS or the session name, '' when there is nothing to match.

    public function _log_redact_pattern()
    {
        $keys = (array) $this->LOG_REDACT_KEYS;
        $keys[] = session_name();
        $quoted = [];
        foreach ($keys as $key) {
            if (is_scalar($key) && (string) $key !== '') {
                $quoted[] = preg_quote((string) $key, '~');
            }
        }
        return $quoted ? '~' . implode('|', array_unique($quoted)) . '~i' : '';
    }
public function _log_redact_query($query, $pattern)

Masks the value of each name=value pair (separated by & or ;) whose decoded name matches, in a raw query string.

    public function _log_redact_query($query, $pattern)
    {
        $query = (string) $query;
        if ($pattern === '' || strpos($query, '=') === false) {
            return $query;
        }
        return preg_replace_callback('~(^|[&;])([^=&;]+)=([^&;]*)~', function ($m) use ($pattern) {
            return preg_match($pattern, urldecode($m[2])) ? $m[1] . $m[2] . '=***' : $m[0];
        }, $query);
    }
public function _log_redact_url($url, $pattern)

URL with its userinfo (user:pass@) masked and the pairs of its query part (after ?, before #) redacted; the path is kept.

    public function _log_redact_url($url, $pattern)
    {
        $url = (string) preg_replace('~^((?:[a-z][a-z0-9+.\-]*:)?//)[^/?#]*@~i', '$1***@', (string) $url);
        $fragment = '';
        if (($pos = strpos($url, '#')) !== false) {
            $fragment = substr($url, $pos);
            $url = substr($url, 0, $pos);
        }
        if (($pos = strpos($url, '?')) !== false) {
            $url = substr($url, 0, $pos + 1) . $this->_log_redact_query(substr($url, $pos + 1), $pattern);
        }
        return $url . $fragment;
    }
public function _log_row_filter(array $row)

Section allow-list and redaction of the query strings and the trace of an error log row; the identity with the defaults.

  • $row array
    public function _log_row_filter(array $row)
    {
        $sections = [
            'get' => ['get'],
            'post' => ['post'],
            'files' => ['files'],
            'cookie' => ['cookie'],
            'session' => ['session'],
            'qs' => ['qs', 'url'],
            'server' => ['ip', 'ref', 'us'],
        ];
        foreach (array_diff(array_keys($sections), (array) $this->LOG_CONTEXT_FIELDS) as $section) {
            foreach ($sections[$section] as $field) {
                array_key_exists($field, $row) && $row[$field] = '';
            }
        }
        if ( ! $this->LOG_REDACT) {
            return $row;
        }
        $pattern = $this->_log_redact_pattern();
        foreach (['qs', 'url', 'ref'] as $field) {
            is_string($row[$field] ?? null) && $row[$field] = $this->_log_redact_url($row[$field], $pattern);
        }
        return $row;
    }
public function _log_trace_lines()

The caller's trace as main()->trace_string() lists it (from #1, without {main}), built from the frames so that no call argument reaches the log.

    public function _log_trace_lines()
    {
        $lines = $this->_log_format_frames((new Exception())->getTrace());
        array_shift($lines);
        return $lines;
    }
public function _render_500_template($vars)

Seam for the template engine, returns '' when it is not available (bootstrap errors).

    public function _render_500_template($vars)
    {
        if ( ! function_exists('tpl') || ! function_exists('main') || ! is_object(main())) {
            return '';
        }
        return (string) tpl()->parse('system/error_500', $vars);
    }
public function _render_error_page($code, $tpl_name, array $vars = [])

Status header and the template of an error page; $vars must be escaped by the caller.

  • $vars array
    public function _render_error_page($code, $tpl_name, array $vars = [])
    {
        $this->_send_status($code);
        if ($code == 404) {
            main()->IS_404 = true;
        } elseif ($code == 403) {
            main()->IS_403 = true;
        }
        return tpl()->parse($tpl_name, $vars);
    }

_send_status

public function _send_status($code)

Send a status line, unless the response has already started.

    public function _send_status($code)
    {
        $texts = [403 => 'Forbidden', 404 => 'Not Found', 500 => 'Internal Server Error'];
        if ($this->_is_cli() || headers_sent()) {
            return false;
        }
        header((($_SERVER['SERVER_PROTOCOL'] ?? '') ?: 'HTTP/1.1') . ' ' . $code . ' ' . ($texts[$code] ?? ''));
        return true;
    }

_show_details

public function _show_details()
    public function _show_details()
    {
        return (bool) ($this->SHOW_DETAILS ?? DEBUG_MODE);
    }

_to_logger

public function _to_logger($level, $message, array $context = [])

Pass a record to logger(); it can run during bootstrap and must never raise an error of its own.

  • $context array
    public function _to_logger($level, $message, array $context = [])
    {
        if ( ! $this->USE_LOGGER || $this->_in_logger || ! function_exists('logger') || ! function_exists('main') || ! is_object(main())) {
            return false;
        }
        $this->_in_logger = true;
        try {
            return (bool) logger()->log($level, $message, $context);
        } catch (Throwable $e) {
            return false;
        } finally {
            $this->_in_logger = false;
        }
    }