get('ERROR.trace');
        $errorTraceArray = preg_split('/$\R?^/m', $errorTraceString);
        $maximalStringIndex = 0;
        $maximalStringLength = 0;
        $iters = count($errorTraceArray);
        for ($i = 0; $i < $iters; ++$i) {
            $currentStringLength = strlen($errorTraceArray[$i]);
            if ($maximalStringLength < $currentStringLength) {
                $maximalStringIndex = $i;
                $maximalStringLength = $currentStringLength;
            }
        }
        if ($iters > 1) {
            array_splice($errorTraceArray, $maximalStringIndex, 1);
        }
        $iters = count($errorTraceArray);
        for ($i = 0; $i < $iters; ++$i) {
            $errorTraceArray[$i] = strip_tags($errorTraceArray[$i]);
            $errorTraceArray[$i] = str_replace(['>', '<'], ['>', '<'], $errorTraceArray[$i]);
        }
        $errorCode = $f3->get('ERROR.code');
        $errorMessage = join(', ', ['ERROR_' . $errorCode, $f3->get('ERROR.text')]);
        return [
            'ip' => $f3->IP,
            'code' => $errorCode,
            'message' => $errorMessage,
            'trace' => join('
', $errorTraceArray),
            'date' => date('l jS \of F Y h:i:s A'),
            'post' => $f3->get('POST'),
            'get' => $f3->get('GET'),
        ];
    }
    public static function saveErrorInformation(\Base $f3, array $errorData): void {
        \Utils\Logger::log(null, $errorData['message']);
        $errorTraceArray = explode('
', $errorData['trace']);
        $printErrorTraceToLog = $f3->get('PRINT_ERROR_TRACE_TO_LOG');
        if ($printErrorTraceToLog) {
            $iters = count($errorTraceArray);
            for ($i = 0; $i < $iters; ++$i) {
                \Utils\Logger::log(null, $errorTraceArray[$i]);
            }
        }
        $db = $f3->get('API_DATABASE');
        if ($db) {
            $errorData['sql_log'] = $db->log();
            $logModel = new \Models\Log();
            $logModel->add($errorData);
            \Utils\Logger::log('SQL', $errorData['sql_log']);
        }
        if ($errorData['code'] === 500) {
            $toName = 'Admin';
            $toAddress = \Utils\Variables::getAdminEmail();
            if ($toAddress === null) {
                \Utils\Logger::log('Log mail error', 'ADMIN_EMAIL is not set');
                return;
            }
            $subject = $f3->get('error_email_subject');
            $subject = sprintf($subject, $errorData['code']);
            $currentTime = date('d-m-Y H:i:s');
            $errorMessage = $errorData['message'];
            $errorTrace = $errorData['trace'];
            $message = $f3->get('error_email_body_template');
            $message = sprintf($message, $currentTime, $errorMessage, $errorTrace);
            \Utils\Mailer::send($toName, $toAddress, $subject, $message);
        }
    }
    protected static function getAjaxErrorMessage(array $errorData): string|false {
        return json_encode(
            [
                'status' => false,
                'code' => $errorData['code'],
                'message' => sprintf('Request finished with code %s', $errorData['code']),
            ],
        );
    }
    public static function getOnErrorHandler(): callable {
        /**
         * Custom onError handler: http://stackoverflow.com/questions/19763414/fat-free-framework-f3-custom-404-page-and-others-errors, https://groups.google.com/forum/#!topic/f3-framework/BOIrLs5_aEA
         * We can can use $f3->get('ERROR.text'), and decide which template should be displayed.
         *
         * @param $f3
         */
        return function (\Base $f3): void {
            $hive = $f3->hive();
            $isAjax = $hive['AJAX'];
            $errorData = self::getErrorDetails($f3);
            self::saveErrorInformation($f3, $errorData);
            if ($errorData['code'] === 403 && $isAjax) {
                echo self::getAjaxErrorMessage($errorData);
                return;
            }
            if ($errorData['code'] === 403 && !$isAjax) {
                $f3->reroute('/logout');
                return;
            }
            // Add handling 404 error
            if ($errorData['code'] === 404) {
            }
            if ($isAjax) {
                echo self::getAjaxErrorMessage($errorData);
                return;
            }
            $response = new \Views\Frontend();
            $pageController = new \Controllers\Pages\Error();
            $errorData['message'] = 'ERROR_' . $errorData['code'];
            unset($errorData['trace']);
            $pageParams = $pageController->getPageParams($errorData);
            $response->data = $pageParams;
            echo $response->render();
        };
    }
    public static function getCronErrorHandler(): callable {
        return function (\Base $f3): void {
            $errorData = self::getErrorDetails($f3);
            self::saveErrorInformation($f3, $errorData);
        };
    }
    public static function exceptionErrorHandler(int $severity, string $message, string $file, int $line): bool {
        if (!(error_reporting() & $severity)) {
            return false;
        }
        throw new \ErrorException($message, 0, $severity, $file, $line);
        return true;
    }
}