Class it:

/**
 * If display_errors is on or stdout is a tty, shows error in page or on stdout respectively
 * If display_errors is off, mails (with rate limiting) diagnostics to .diffnotice addresses or file owner or SERVER_ADMIN
 * @param $p either error title or assoc array of params, see below
 * @param $p['title'] error title, one line. also accepted in $p[0] (with priority). false means ignore error
 * @param $p['body'] error body: multiline string or any data type (will be dumped)
 * @param $p['to'] comma separated recipient list
 * @param $p['id'] id of error, used with timewindow, defaults to file and line of caller
 * @param $p['graceperiod'] in seconds. deprecated, see $p['timewindow']
 * @param $p['timewindow'] in secs. "5-35" means for an notice to be sent, a second error must occur 5 to 35 seconds after first err
 * @param $p['blockmailid'] block mail for $p['blockmail'] seconds with same id. Default: $p['to']
 * @param $p['blockmail'] number of seconds to block mails after having sent a mail [3600]
 * @param $p['backtraceskip'] number of stack levels to drop
 * @param $p['skipfiles'] files to skip in backtrace
 * @param $p['okstate'] give current ok label and ok state, e.g. telresult=1 for working. see failcount
 * @param $p['failcount'] give number of consecutive okstate failures needed for creating an error [2]
 * @param $p['omitdebuginfo'] value 1 omits long stack and var dumps, value 2 also minimal infos
 * @param $p['fatal'] exit after displaying error
 * @return always null (so users can return it::error() in error cases)
 *
 * TIMEWINDOW 5-35 (x = error occurs, no mail; m = error occurs, mail sent)
 *   time        ---|5 secs|30 secs      |------|5 secs|30 secs
 *                                                          |5 secs|30 secs
 *                  x  x                        x   x       m   x     m   x
 * BLOCKMAIL
 *   time        ---|blockmail|--|blockmail|---->
 *   blockmailid    m  x     x   m   x
 *
 * GRACPERIOD/TIMEWINDOW (DEPRECATED, graceperiod is first argument of 5-35, timewindow is difference between 5 and 35)
 *   time        ---|graceperiod|timewindow|
 *                                   |graceperiod|timewindow|--.
 *                                                             |graceperiod|timewindow|------>
 *   id             x  x     x       m    x   x                x     x
 */
static function error($p = array(), $extra null)
{
    
$p $origp = (array)$p;

    if (
error_reporting() == @error_reporting() || $p[0] === false || $p['title'] === false# called with @ or suppressed
        
return $p['fatal'] ? self::_exit($p) : null;

    
$p['title'] = $p[0] ?: $p['title']; # handle 'it_error' => "oops" that was cast to array on the way
    
$p['title'] = grapheme_substr($p['title'], 02000) ?: substr($p['title'], 02000);

    
$GLOBALS['ULTRAERROR'] = $p['title'];
    if (
$GLOBALS['ULTRANOERRORS'])
        return 
null;

    if (
$extra)
        
$p = ['title' => 'extraneous params passed to it::error''fatal' => $p['fatal']];

    foreach (
array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS10), 1) as $level)
        if (
$level["class"] . $level["type"] . $level["function"] == "it::error")
            return 
null# prevent recursion

    
if ($_SERVER['REMOTE_ADDR'])
        
$url "http://" it::replace([':443$' => ""], $_SERVER['HTTP_HOST']) . $_SERVER['REQUEST_URI']; # we ignore https for easier debugging
    
else
        
$url $_SERVER['SCRIPT_NAME'] . " " implode(" "array_slice($GLOBALS['argv'], 1)) . " (pwd " $_SERVER['PWD'] . ")";

    
$authors it::replace(['\*' => ""], join(", "it::grep('\*', (array)@it::cat($GLOBALS['ULTRAHOME'] . "/.diffnotice")->lines, ['invert' => true])));
    if (!
$p['to'])
        unset(
$p['to']); # allow defaults to kick in
    
$p += array(
        
'title' => "it::error",
        
'to' => $authors $authors : (get_current_user() ? get_current_user() : $_SERVER['SERVER_ADMIN']),
        
'graceperiod' => 60,
        
'timewindow' => 25*3600,
        
'backtraceskip' => 1,
        
'blockmail' => $p['okstate'] ? 60 3600,
        
'omitdebuginfo' => false,
        
'failcount' => 2,
        
'id' => $p['timewindow'] ? "it_error_id_" it_debug::backtrace(['skipfiles' => "it.class"'levels' => 1]) : 0,
    );
    if (
it::match('-'$p['timewindow']) && ($parts explode("-"$p['timewindow'])))
        list(
$p['graceperiod'], $p['timewindow']) = [$parts[0], $parts[1] - $parts[0]];
    
$p += array('blockmailid' => $GLOBALS['ULTRASITE'] . "." md5($p['to']));
    
$trace it_debug::backtrace(array('skiplevels' => $p['backtraceskip'], 'skipfiles' => $p['skipfiles'], 'trace' => $p['trace'])); # moved in here for performance in mass error case

    
@mkdir("/tmp/alertdata");
    @
chmod("/tmp/alertdata"0777);

    if (
$p['okstate'])
    {
        list(
$okfn$okstatus) = explode("=""/tmp/alertdata/okstate_" $p['okstate']);
        
$failcount $okstatus : (int)@file_get_contents($okfn) + 1# NOPHPLINT
        
file_put_contents($okfn"$failcount\n"); # NOPHPLINT
        
if ($failcount != $p['failcount'] && $failcount != $p['failcount'] * 30)
            return 
$p['fatal'] && !$okstatus self::_exit($p) : null# nothing to report
    
}

    
$context is_callable(self::$error_context) ? (self::$error_context)() : [];

    
$toscreen getenv('NOERRORMAILS') || ini_get('display_errors') || (defined("STDOUT") && posix_isatty(STDERR)) || EDC('astwin') || EDC('asdevel') || $context['toscreen'];

    if (
$toscreen && !it::is_live())
        
$GLOBALS['debug_noredir'] = 1;

    if (!
$toscreen# this error can only be sent by mail: find out if we want to suppress it
    
{
        if (!
$p['id'])
            
$sendmail true;
        else
        {
            
$errstampfn "/tmp/alertdata/errstamp_" urlencode($p['id']);
            
$errstamp = @filemtime($errstampfn);
            
$errstampage time() - $errstamp;
            
$sendmail $errstampage >= $p['graceperiod'] && $errstampage <= $p['graceperiod'] + $p['timewindow'];
            if (
$errstampage $p['graceperiod'])
            {
                @
unlink($errstampfn);
                @
touch($errstampfn);
            }
        }

        if (
$sendmail)
        {
            
$lastsentfn "/tmp/alertdata/it_error_mailsent_" urlencode($p['blockmailid']);
            
clearstatcache();
            
$sendmail time() - max(@filemtime($lastsentfn), it_cache::get($lastsentfn, ['distributed' => true])) > $p['blockmail'] && !it_cache::get('it_error_mute_' $GLOBALS['ULTRASITE'], ['distributed' => true]);
            if (
$sendmail)
            {
                @
unlink($lastsentfn);
                @
touch($lastsentfn);
                if (
it::is_live())
                    @
it_cache::put($lastsentfntime(), ['distributed' => true'ttl' => 7*86400'safety' => 0]);
            }
        }
    }

    if (
$p['fatal'] && $_SERVER['REMOTE_ADDR'])
    {
        
http_response_code(500);
        echo 
"<!DOCTYPE html><html><head><title></title></head><body>Internal Server Error</body></html>\n";
    }

    if (
$toscreen || $sendmail)
    {
        
$p['body'] = is_string($p['body']) || !array_key_exists('body'$p) ? $p['body'] : it_debug::dump([$p['body']], ['html' => false'short' => true'color' => false]);
        if (
strlen($p['body']) > 500000 || it::match('[\x00-\x08\x0E-\x1F]'$p['body'], ['utf8' => false]) !== null)
        {
            
file_put_contents($datafn "/tmp/alertdata/error-" substr(md5($p['body']), 02), $p['body']); # NOPHPLINT
            
$p['body'] = "Body: " getenv('HOSTNAME') . ":$datafn";
        }

        
$body =
            (
$p['omitdebuginfo'] >= "" : ($url && !$toscreen"Title:  {$p['title']}\nUrl:    $url\n" "") .
            (
$trace ? ($sendmail "" "  ") . "Trace:  $trace\n" "")) .
            (!
$sendmail || $p['omitdebuginfo'] >= "" "Host:   " getenv('HOSTNAME') . " at " date("Y-m-d H:i:s") . (($t time() - $_SERVER['REQUEST_TIME']) ? " (invoked {$t}s before)" "") . "\n") . # no it::date() due to time- debug param
            
($sendmail $context['head'] : "") .
            (
$p['id'] && !$toscreen "Filter: timewindow=" $p['graceperiod'] . "-" . ($p['graceperiod'] + $p['timewindow']) . "  (previous err: " it::date(''$errstamp) . ")\n" "") .
            (!
$origp['blockmail'] || $p['omitdebuginfo'] || $toscreen "" "Block-resend: " $origp['blockmail'] . " seconds\n") .
            (
$p['body'] ? ($p['omitdebuginfo'] ? "" "Body:\n") . trim($p['body'])."\n\n" "");

        if (
$sendmail# we're mailing: send maximum info
        
{
            
$p['title'] = it::replace(['alert:|server:|^: "' => ""'(pw|passw|password\d*|secret)(=)[^&\s]*' => '$1$2*****'], it::match('[^.]*'$GLOBALS['ULTRASITE']) . ": " $p['title']) . " (via " getenv('HOSTNAME') . ")";

            if (!
$p['omitdebuginfo'])
            {
                foreach ((
$trace ? ($p['trace'] ?: debug_backtrace()) : []) as $level => $stackframe)
                    if (
$level >= $p['backtraceskip'] && $tracesize 100000 && ($tracesize += strlen(print_r($stackframetrue))) < 100000# save mem
                        
$stackframes[] = $stackframe;

                
$reqbody it::file_get_contents("php://input");

                
$body .= ($trace and $t it::exec('grep -h {0} `ls /tmp/alertdata/alertlog-*|tail -3` /dev/null 2>/dev/null | grep ^2 | cut -d : -f 1-2 | sort | uniq -c | tail -10'$trace)) ? "Histogram: (last 10 affected minutes in 3 days)\n$t"";
                
$body .= "\n";
                
$body .= $_GET        "\$_GET: "    var_export($_GETtrue) . "\n\n" "";
                
$body .= $_POST       "\$_POST: "   var_export($_POSTtrue) . "\n\n" "";
                
$body .= $reqbody     "\$reqbody: " var_export($reqbodytrue) . "\n\n" "";
                
$body .= $_COOKIE     "\$_COOKIE: " var_export($_COOKIEtrue) . "\n\n" "";
                
$body .= $_SERVER['REMOTE_ADDR'] ? "" "Pstree:\n" it::exec("pstree -als " getmypid() . " | head -n -3") . "\n";
                
$body .= $_SERVER     "\$_SERVER: " var_export($_SERVERtrue) . "\n\n" "";
                
$body .= $_FILES      "\$_FILES:  " var_export($_FILEStrue) . "\n\n" "";
                
$body .= "Processes:\n" it::exec('ps auxf | egrep -v "rotatelogs|getbanner|logaction|httpd|systemd|sd-pam"|egrep "^www|^cron"') . "\n";
                
$body .= $stackframes "Stack: "     print_r($stackframestrue) . "\n\n" "";
                
$body  it::replace(array('(pw|passw|password\d*|secret)(\' => |\] => |=)[^&\s]*' => '$1$2********'), $body, array('utf8' => false));
            }

            
$type = ($p['fatal'] ? (it::is_live() ? "FATAL: " "Fatal: ") : "Error: ");
            
$debugparams it::match('\.([^-.]+)'$_SERVER['HTTP_HOST'], ['all' => true]);
            if (!
it::is_live() || array_diff($debugparamsit::match('[-\w]+'$GLOBALS['ULTRAHOME'], ['all' => true]), ["devel""twin"gethostname()]))
                
$type mb_strtolower($type);

            
$user posix_getpwuid(posix_geteuid())['name'];

            
it::mail([
                
'From' => "\"$user@" gethostname() . "\" <$user>",
                
'To' => $p['to'],
                
'Reply-To' => $p['to'],
                
'Cc' => $GLOBALS['it_defaultconfig']['error_cc'],
                
'Subject' => $type substr($p['title'], 0160),
                
'Body' => it::replace(['\x00' => "[NULLBYTE]"], $body, ['utf8' => false]),
                
'forcemail' => !it::is_devel(),
            ]);
            
$p['title'] = "Mail: " $p['title'];
        }
        else if (
$_SERVER['REMOTE_ADDR'] && EDC('edplain')) # toscreen mode: web
            
echo $p['title'] . "\n";
        else if (
$_SERVER['REMOTE_ADDR']) # toscreen mode: web
            
echo "<pre style='z-index:10000; position:relative; background:white'>" htmlspecialchars($p['title'] . "\n" rtrim($body), ENT_COMPAT"iso-8859-1") . "</pre>"# works with iso-8859-1 or utf-8, UTF8SAFE
        
else  # toscreen mode: shell (outputs to stderr)
            
error_log(substr($p['title'], 0100000) . ($p['omitdebuginfo'] >= "" " in " . ($trace $trace {$p['file']}:{$p['line']}") . " Url: $url " . (EDC('verboseerrors') ? D($p['body']) : "")));
    }

    if (
$_SERVER['REMOTE_ADDR']) # additional entry in log/error_log
        
error_log("it::error: " $p['title'] . " Url: $url");

    
it::log("/tmp/alertdata/alertlog"$p['title'] . " in " . ($trace $trace "{$p['file']}:{$p['line']}") . " Url: $url. ($p['body'] ? "\n" substr(D($p['body']), 05000) . "\n" ""));

    return 
$p['fatal'] ? self::_exit($p) : null;
}