_error_handler_real($error_level, $message, $filename, $line, $context); } /** * Provides custom PHP error handling. * * @param $error_level * The level of the error raised. * @param $message * The error message. * @param $filename * The filename that the error was raised in. * @param $line * The line number the error was raised at. * @param $context * An array that points to the active symbol table at the point the error * occurred. */ function _error_handler_real($error_level, $message, $filename, $line, $context) { if ($error_level) { $types = $this->_error_levels(); list($severity_msg, $severity_level) = $types[$error_level]; $caller = $this->_get_last_caller(debug_backtrace()); // We treat recoverable errors as fatal. $this->_log_error(array( '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error', // The standard PHP error handler considers that the error messages // are HTML. We mimick this behavior here. '!message' => esc_html($message), '%function' => $caller['function'], '%file' => $caller['file'], '%line' => $caller['line'], 'severity_level' => $severity_level, ), $error_level == E_RECOVERABLE_ERROR); } } /** * Maps PHP error constants to format_err severity levels. * * The error constants are documented at * http://php.net/manual/errorfunc.constants.php * * @ingroup logging_severity_levels */ function _error_levels() { $types = array( E_ERROR => array('Error', ZEROERROR_ERROR), E_WARNING => array('Warning', ZEROERROR_WARNING), E_PARSE => array('Parse error', ZEROERROR_ERROR), E_NOTICE => array('Notice', ZEROERROR_NOTICE), E_CORE_ERROR => array('Core error', ZEROERROR_ERROR), E_CORE_WARNING => array('Core warning', ZEROERROR_WARNING), E_COMPILE_ERROR => array('Compile error', ZEROERROR_ERROR), E_COMPILE_WARNING => array('Compile warning', ZEROERROR_WARNING), E_USER_ERROR => array('User error', ZEROERROR_ERROR), E_USER_WARNING => array('User warning', ZEROERROR_WARNING), E_USER_NOTICE => array('User notice', ZEROERROR_NOTICE), E_STRICT => array('Strict warning', ZEROERROR_DEBUG), E_RECOVERABLE_ERROR => array('Recoverable fatal error', ZEROERROR_ERROR), ); // E_DEPRECATED and E_USER_DEPRECATED were added in PHP 5.3.0. if (defined('E_DEPRECATED')) { $types[E_DEPRECATED] = array('Deprecated function', ZEROERROR_DEBUG); $types[E_USER_DEPRECATED] = array('User deprecated function', ZEROERROR_DEBUG); } return $types; } /** * Gets the last caller from a backtrace. * * @param $backtrace * A standard PHP backtrace. * * @return * An associative array with keys 'file', 'line' and 'function'. */ function _get_last_caller($backtrace) { // Errors that occur inside PHP internal functions do not generate // information about file and line. Ignore black listed functions. $blacklist = array('_zerror_handler', '_ze_error_handler', '_ze_exception_handler'); while (($backtrace && !isset($backtrace[0]['line'])) || (isset($backtrace[1]['function']) && in_array($backtrace[1]['function'], $blacklist))) { array_shift($backtrace); } // The first trace is the call itself. // It gives us the line and the file of the last call. $call = $backtrace[0]; // The second call give us the function where the call originated. if (isset($backtrace[1])) { if (isset($backtrace[1]['class'])) { $call['function'] = $backtrace[1]['class'] . $backtrace[1]['type'] . $backtrace[1]['function'] . '()'; } else { $call['function'] = $backtrace[1]['function'] . '()'; } } else { $call['function'] = 'main()'; } return $call; } /** * Provides access to a single instance of a module using the singleton pattern * * @mvc Controller * * @return object */ public static function get_ajax_errs() { if (!isset(self::$ajax_errs)) { self::$ajax_errs = get_option('ze_hxr_errors', array()); } return self::$ajax_errs; } /** * Logs a PHP error or exception and displays an error page in fatal cases. * * @param $error * An array with the following keys: %type, !message, %function, %file, %line * and severity_level. All the parameters are plain-text, with the exception * of !message, which needs to be a safe HTML string. * @param $fatal * TRUE if the error is fatal. */ function _log_error($error, $fatal = FALSE) { if ($fatal) { wp_die(__('Hey, We are working on somethig but we\'ll be back soon!'), 500); } //Detects whether the current script is running in a command-line environment. if (!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0))) { if ($fatal) { // When called from CLI, simply output a plain text message. print html_entity_decode(strip_tags(format_string('%type: !message in %function (line %line of %file).', $error))) . "\n"; exit; } } if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { $errmsg = $this->format_string('%type: !message in %function (line %line of %file).', $error); if (defined('DOING_AJAX') && DOING_AJAX) { self::get_ajax_errs(); if (!in_array($errmsg, self::get_ajax_errs())) { $GLOBALS['_ze_errors'][] = $errmsg; if (count(self::$ajax_errs) == 10) { $ajax_errs = self::$ajax_errs; reset($ajax_errs); // move to the first member of the array $key = key($ajax_errs); // retrieve the key in the first array position unset($ajax_errs[$key]); self::$ajax_errs = $ajax_errs; } self::$ajax_errs[time()] = $errmsg; } } if ($fatal) { // When called from JavaScript, simply output the error message. print $errmsg; exit; } } else { $this->format_err('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']); } } /** * Logs a system message. * * @param $type * The category to which this message belongs. Can be any string, but the * @param $message * The message to store in the log. * @param $variables * Array of variables to replace in the message on display or * NULL if message is already translated or not possible to * translate. * @param $severity * The severity of the message; one of the following values as defined in * @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink * - ZEROERROR_EMERGENCY: Emergency, system is unusable. * - ZEROERROR_ALERT: Alert, action must be taken immediately. * - ZEROERROR_CRITICAL: Critical conditions. * - ZEROERROR_ERROR: Error conditions. * - ZEROERROR_WARNING: Warning conditions. * - ZEROERROR_NOTICE: (default) Normal but significant conditions. * - ZEROERROR_INFO: Informational messages. * - ZEROERROR_DEBUG: Debug-level messages. * @param $link * A link to associate with the message. * * @see ZEROERROR_severity_levels() */ function format_err($type, $message, $variables = array(), $severity = ZEROERROR_NOTICE, $link = NULL) { static $in_error_state = FALSE; // It is possible that the error handling will itself trigger an error. In that case, we could // end up in an infinite loop. To avoid that, we implement a simple static semaphore. if (!$in_error_state) { $in_error_state = TRUE; // The userid may not exist in all conditions, so 0 is may not exactly mean user was not logged in $user_uid = get_current_user_id(); // Prepare the fields to be logged $log_entry = array( 'type' => $type, 'message' => $message, 'variables' => $variables, 'severity' => $severity, 'link' => $link, 'uid' => $user_uid, 'request_uri' => $this->request_uri(), 'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', // Request time isn't accurate for long processes, use time() instead. 'timestamp' => time(), ); $message = strtr('!base_url|!timestamp|!type|!request_uri|!referer|!uid|!link|!message', array( '!base_url' => site_url(), '!timestamp' => $log_entry['timestamp'], '!type' => $log_entry['type'], '!request_uri' => $log_entry['request_uri'], '!referer' => $log_entry['referer'], '!uid' => $log_entry['uid'], '!link' => strip_tags($log_entry['link']), '!message' => strip_tags(!isset($log_entry['variables']) ? $log_entry['message'] : strtr($log_entry['message'], $log_entry['variables'])), )); syslog($log_entry['severity'], $message); $log_vars = $log_entry['variables']; $unq_index = wp_create_nonce($log_vars['%type'] . $log_vars['!message'] . $log_vars['%function'] . $log_vars['%file'] . $log_vars['%line']); $GLOBALS['_ze_errors'][$unq_index] = array('errtype' => $log_vars['%type'], 'errmsg' => $message); // It is critical that the semaphore is only cleared here, in the parent // format_err() call (not outside the loop), to prevent recursive execution. $in_error_state = FALSE; return $message; } } /** * Returns the equivalent of Apache's $_SERVER['REQUEST_URI'] variable. * * Because $_SERVER['REQUEST_URI'] is only available on Apache, we generate an * equivalent using other environment variables. */ function request_uri() { if (isset($_SERVER['REQUEST_URI'])) { $uri = $_SERVER['REQUEST_URI']; } else { if (isset($_SERVER['argv'])) { $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0]; } elseif (isset($_SERVER['QUERY_STRING'])) { $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING']; } else { $uri = $_SERVER['SCRIPT_NAME']; } } // Prevent multiple slashes to avoid cross site requests via the Form API. $uri = '/' . ltrim($uri, '/'); return $uri; } /** * Formats a string for HTML display by replacing variable placeholders. * * This function replaces variable placeholders in a string with the requested * values and escapes the values so they can be safely displayed as HTML. It * should be used on any unknown text that is intended to be printed to an HTML * page (especially text that may have come from untrusted users, since in that * case it prevents cross-site scripting and other security problems). * * * @param $string * A string containing placeholders. * @param $args * An associative array of replacements to make. Occurrences in $string of * any key in $args are replaced with the corresponding value, after optional * sanitization and formatting. The type of sanitization and formatting * depends on the first character of the key: * - @variable: Escaped to HTML using esc_html(). Use this as the default * choice for anything displayed on a page on the site. * - %variable: Escaped to HTML and formatted using placeholder(), * which makes it display as emphasized text. * - !variable: Inserted as is, with no sanitization or formatting. Only use * this for text that has already been prepared for HTML display (for * example, user-supplied text that has already been run through * esc_html() previously, or is expected to contain some limited HTML * * @ingroup sanitization */ function format_string($string, array $args = array()) { // Transform arguments before inserting them. foreach ($args as $key => $value) { switch ($key[0]) { case '@': // Escaped only. $args[$key] = esc_html($value); break; case '%': default: // Escaped and placeholder. $args[$key] = $this->placeholder($value); break; case '!': // Pass-through. } } return strtr($string, $args); } /** * Formats text for emphasized display in a placeholder inside a sentence. * * Used automatically by format_string(). * * @param $text * The text to format (plain-text). * * @return * The formatted text (html). */ function placeholder($text) { return '' . esc_html($text) . ''; } }