<?php
/**
 * 入口文件（所有请求经此转发）
 */
define('BASE_PATH', dirname(__DIR__));
require BASE_PATH . '/app/Core/App.php';
\Core\App::init();          // 加载 Helper（含全局辅助函数）
apply_security_headers();  // 全局函数（与视图/控制器中的 e()/site_url() 等同源）

/**
 * 输出缓冲：自动为内联 <script>/<style> 注入本次请求的 CSP nonce，
 * 从而可移除 script-src 的 'unsafe-inline'（严格 CSP 的关键前提）。
 */
ob_start(function ($html) {
    $nonce = function_exists('csp_nonce') ? csp_nonce() : '';
    if ($nonce === '') return $html;
    $html = preg_replace_callback('#<script(?![^>]*\bsrc=)(?![^>]*\bnonce=)([^>]*)>#i', function ($m) use ($nonce) {
        return '<script nonce="' . $nonce . '"' . $m[1] . '>';
    }, $html);
    $html = preg_replace_callback('#<style(?![^>]*\bnonce=)([^>]*)>#i', function ($m) use ($nonce) {
        return '<style nonce="' . $nonce . '"' . $m[1] . '>';
    }, $html);
    return $html;
});

/**
 * 一键部署自愈：上传覆盖后无需手动点「数据升级」。
 * 仅 MySQL 模式生效；若新模块表（crm_contacts）缺失，自动补齐表/列并刷新种子，幂等且保留已有数据。
 * 数据库暂不可用时静默跳过，下次请求再尝试，绝不阻塞站点。
 */
if (\Core\Db::driver() === 'mysql') {
    try {
        $pdo = \Core\Db::pdo();
        $has = $pdo->query("SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='crm_contacts' LIMIT 1")->fetchColumn();
        if (!$has) {
            \Core\Installer::upgrade();
            // 升级可能变更文件结构，顺带清一次 OPcache 以防 FPM 跑旧字节码
            if (function_exists('opcache_reset')) @opcache_reset();
        }
    } catch (\Throwable $e) {
        // 自愈失败不影响正常访问，留待管理员在 /admin/upgrade 手动处理
    }
}

/**
 * 自动清 OPcache：覆盖部署后若发现磁盘文件比 OPcache 缓存的编译时间更新，
 * 自动重置字节码缓存，免去手动重启 PHP-FPM。（仅当 OPcache 扩展可用时生效）
 * 平时文件未变动时不触发，无性能开销。
 */
if (function_exists('opcache_get_status') && function_exists('opcache_reset')) {
    $st = @opcache_get_status(false);
    if (!empty($st['scripts'])) {
        foreach ($st['scripts'] as $path => $info) {
            if (isset($info['timestamp']) && is_file($path) && filemtime($path) > $info['timestamp']) {
                @opcache_reset();
                break;
            }
        }
    }
}

\Core\App::run();

// 冲刷输出缓冲（执行 nonce 注入回调后输出）
if (ob_get_level() > 0) ob_end_flush();
