Вход Регистрация
Файл: new_top/core/bootstrap.php
Строк: 360
<?php
/**
 * Bootstrap — New Top System
 */

declare(strict_types=1);
// ---- mbstring polyfill (some hostings omit the extension) ----
if (!function_exists('mb_substr')) {
    function 
mb_substr($string$start$length null$encoding null)
    {
        return 
$length === null substr((string)$string, (int)$start) : substr((string)$string, (int)$start, (int)$length);
    }
}
if (!
function_exists('mb_strlen')) {
    function 
mb_strlen($string$encoding null)
    {
        return 
strlen((string)$string);
    }
}
if (!
function_exists('mb_strtolower')) {
    function 
mb_strtolower($string$encoding null)
    {
        return 
strtolower((string)$string);
    }


if (!
function_exists('str_contains')) {
    function 
str_contains($haystack$needle)
    {
        return 
$needle === '' || strpos((string)$haystack, (string)$needle) !== false;
    }
}
if (!
function_exists('str_starts_with')) {
    function 
str_starts_with($haystack$needle)
    {
        
$needle = (string)$needle;
        return 
$needle === '' || strncmp((string)$haystack$needlestrlen($needle)) === 0;
    }
}
if (!
function_exists('str_ends_with')) {
    function 
str_ends_with($haystack$needle)
    {
        
$needle = (string)$needle;
        if (
$needle === '') return true;
        return 
substr((string)$haystack, -strlen($needle)) === $needle;
    }
}

}



define('ROOT'dirname(__DIR__));
define('CORE'ROOT '/core');
define('CONFIG_PATH'CORE '/config/database.php');
define('ASSETS'ROOT '/assets');

if (!
file_exists(CONFIG_PATH)) {
    if (
is_dir(ROOT '/install') && strpos($_SERVER['REQUEST_URI'] ?? '''/install') === false) {
        
header('Location: /install/');
        exit;
    }
}

$config file_exists(CONFIG_PATH) ? require CONFIG_PATH null;

spl_autoload_register(function (string $class): void {
    if (!
preg_match('/^[A-Za-z][A-Za-z0-9_]*$/'$class)) {
        return;
    }
    
$file CORE '/classes/' $class '.php';
    if (
is_file($file)) {
        require_once 
$file;
    }
});

$pdo null;
if (
$config) {
    try {
        
$dsn sprintf(
            
'mysql:host=%s;dbname=%s;charset=%s',
            
$config['db']['host'],
            
$config['db']['dbname'],
            
$config['db']['charset']
        );
        
$pdo = new PDO($dsn$config['db']['username'], $config['db']['password'], $config['db']['options']);
        
Database::init($pdo);
    } catch (
PDOException $e) {
        
http_response_code(500);
        exit(
'Ошибка подключения к базе данных.');
    }
}

if (
session_status() === PHP_SESSION_NONE) {
    
$sessName $config['security']['session_name'] ?? 'TOPSESSID';
    
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
        || (isset(
$_SERVER['SERVER_PORT']) && (int)$_SERVER['SERVER_PORT'] === 443);
    
session_name($sessName);
    
session_set_cookie_params([
        
'lifetime' => 0,
        
'path'     => '/',
        
'secure'   => $secure,
        
'httponly' => true,
        
'samesite' => 'Lax',
    ]);
    
session_start();
}

// Security headers (HTML responses; image endpoints may override Content-Type)
if (!headers_sent()) {
    
header('X-Frame-Options: SAMEORIGIN');
    
header('X-Content-Type-Options: nosniff');
    
header('Referrer-Policy: strict-origin-when-cross-origin');
    
header('Permissions-Policy: geolocation=(), microphone=(), camera=()');
}

function 
e(?string $str): string
{
    return 
htmlspecialchars((string)$strENT_QUOTES ENT_SUBSTITUTE'UTF-8');
}

function 
redirect(string $url): void
{
    
// Only relative same-site paths — prevent open redirect
    
if ($url === '' || preg_match('#^(https?:)?//#i'$url) || strpos($url"n") !== false || strpos($url"r") !== false) {
        
$url '/';
    }
    if (isset(
$url[0]) && $url[0] !== '/') {
        
$url '/';
    }
    
header('Location: ' $url);
    exit;
}

/**
 * Client IP. X-Forwarded-For is used only when security.trust_proxy = true in config.
 */
function client_ip(): string
{
    global 
$config;
    
$remote $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    
$trustProxy = !empty($config['security']['trust_proxy']);

    if (
$trustProxy && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        
$ip trim(explode(','$_SERVER['HTTP_X_FORWARDED_FOR'])[0]);
    } else {
        
$ip $remote;
    }

    
$ip preg_replace('/[^0-9a-fA-F:.]/'''$ip) ?: '0.0.0.0';
    if (
filter_var($ipFILTER_VALIDATE_IP) === false) {
        return 
'0.0.0.0';
    }
    return 
$ip;
}

function 
client_ua(): string
{
    return 
mb_substr($_SERVER['HTTP_USER_AGENT'] ?? ''0250'UTF-8');
}

function 
site_url(): string
{
    
$https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
        || (isset(
$_SERVER['SERVER_PORT']) && (int)$_SERVER['SERVER_PORT'] === 443);
    
$scheme $https 'https' 'http';
    
$host $_SERVER['HTTP_HOST'] ?? 'localhost';
    
// Host header hardening
    
$host preg_replace('/[^a-zA-Z0-9.-:]/'''$host) ?: 'localhost';
    return 
$scheme '://' $host;
}

function 
csrf_token(): string
{
    if (empty(
$_SESSION['_csrf']) || !is_string($_SESSION['_csrf'])) {
        
$_SESSION['_csrf'] = bin2hex(random_bytes(32));
    }
    return 
$_SESSION['_csrf'];
}

function 
csrf_field(): string
{
    return 
'<input type="hidden" name="_csrf" value="' e(csrf_token()) . '">';
}

function 
csrf_verify(): void
{
    
$sent $_POST['_csrf'] ?? '';
    
$ok is_string($sent) && $sent !== '' && hash_equals(csrf_token(), $sent);
    if (!
$ok) {
        
http_response_code(403);
        exit(
'Ошибка CSRF. Обновите страницу и повторите.');
    }
}

/** Normalize and validate platform hostname/path for storage (no scheme) */
function sanitize_platform_url(string $raw): ?string
{
    
$url trim($raw);
    
$url preg_replace('#^https?://#i'''$url) ?? '';
    
$url rtrim($url'/');
    
$url mb_strtolower($url);
    if (
$url === '' || mb_strlen($url) > 60) {
        return 
null;
    }
    
// Reject credentials, schemes, spaces
    
if (preg_match('#^(javascript|data|vbscript):#i'$url)) {
        return 
null;
    }
    if (
strpos($url' ') !== false || strpos($url'@') !== false || strpos($url'\') !== false) {
        return null;
    }
    // Must look like domain.tld or domain.tld/path
    if (!preg_match('
#^[a-z0-9]([a-z0-9-]*[a-z0-9])?(.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+(/[w-./]*)?$#i', $url)) {
        
return null;
    }
    return 
$url;
}

/** Safe external redirect target from stored platform url */
function external_redirect_url(string $stored): ?string
{
    
$stored trim($stored);
    if (
$stored === '' || preg_match('#^(javascript|data|vbscript):#i'$stored)) {
        return 
null;
    }
    if (
preg_match('#^https?://#i'$stored)) {
        
$parts parse_url($stored);
        if (!
$parts || empty($parts['host'])) {
            return 
null;
        }
        if (!
preg_match('#^[a-z0-9.-]+$#i'$parts['host'])) {
            return 
null;
        }
        
$scheme strtolower($parts['scheme'] ?? 'http');
        if (
$scheme !== 'http' && $scheme !== 'https') {
            return 
null;
        }
        return 
$scheme '://' $parts['host']
            . (isset(
$parts['port']) ? ':' $parts['port'] : '')
            . (
$parts['path'] ?? '')
            . (isset(
$parts['query']) ? '?' $parts['query'] : '');
    }
    
$clean sanitize_platform_url($stored);
    if (
$clean === null) {
        return 
null;
    }
    return 
'http://' $clean;
}

/**
 * Simple login throttle: max attempts per IP in a window (session-based).
 */
function login_throttle_check(string $bucket 'login'): bool
{
    
$key '_throttle_' $bucket;
    
$now time();
    if (empty(
$_SESSION[$key]) || !is_array($_SESSION[$key])) {
        
$_SESSION[$key] = ['n' => 0'start' => $now];
    }
    
$t = &$_SESSION[$key];
    if (
$now - (int)$t['start'] > 900) {
        
$t = ['n' => 0'start' => $now];
    }
    return (int)
$t['n'] < 12;
}

function 
login_throttle_hit(string $bucket 'login'): void
{
    
$key '_throttle_' $bucket;
    if (empty(
$_SESSION[$key]) || !is_array($_SESSION[$key])) {
        
$_SESSION[$key] = ['n' => 0'start' => time()];
    }
    
$_SESSION[$key]['n'] = (int)$_SESSION[$key]['n'] + 1;
}

function 
login_throttle_reset(string $bucket 'login'): void
{
    unset(
$_SESSION['_throttle_' $bucket]);
}


/** PWA meta tags for <head> */
function pwa_meta(string $appTitle 'Top Rating System'): string
{
    
$t htmlspecialchars($appTitleENT_QUOTES ENT_SUBSTITUTE'UTF-8');
    return <<<HTML
<meta name="theme-color" content="#070b14">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="
{$t}">
<meta name="application-name" content="
{$t}">
<link rel="manifest" href="/manifest.webmanifest">
<link rel="apple-touch-icon" href="/assets/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/icons/icon-192.png">
HTML;
}

/** Register service worker before </body> */
function pwa_register(): string
{
    return <<<'HTML'
<script>
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function () {
    navigator.serviceWorker.register('/sw.js', { scope: '/' }).catch(function () {});
  });
}
</script>
HTML;
}
Онлайн: 2
Реклама