<?php




/**


 * 2026企业CMS - 通用函数库


 */





// 引入配置文件


require_once __DIR__ . '/../config/database.php';


require_once __DIR__ . '/../config/config.php';





// ============================================================


// 安全函数


// ============================================================






// ============================================================
// CSRF 防护
// ============================================================

function csrf_generate() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function csrf_validate($token) {
    if (empty($_SESSION['csrf_token'])) return false;
    return hash_equals($_SESSION['csrf_token'], $token);
}

function csrf_input() {
    $token = csrf_generate();
    return '<input type="hidden" name="csrf_token" value="' . htmlspecialchars($token) . '">';
}

// XSS过滤


function xss_clean($data) {


    if (is_array($data)) {


        return array_map('xss_clean', $data);


    }


    return htmlspecialchars(trim($data), ENT_QUOTES, 'UTF-8');


}





// SQL注入过滤(使用PDO预处理，此函数仅作额外过滤)


function sql_filter($str) {


    return preg_replace('/[\'\"\\\;]/', '', $str);


}





// 生成随机字符串


function random_str($length = 32) {


    return bin2hex(random_bytes($length / 2));


}





// 生成SLUG


function generate_slug($str) {


    $str = preg_replace('/[\s]+/', '-', $str);


    $str = preg_replace('/[^\p{L}\p{N}-]/u', '', $str);


    $str = mb_strtolower($str, 'UTF-8');


    return trim($str, '-');


}





// ============================================================


// 文件上传


// ============================================================





function handle_upload($file, $type = 'image') {


    $allowed_ext = [


        'image' => ['jpg', 'jpeg', 'png', 'gif', 'webp'],


        'file'  => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'zip', 'rar']


    ];


    


    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));


    if (!in_array($ext, $allowed_ext[$type])) {


        return ['error' => '不支持的文件类型'];


    }


    


    $yearMonth = date('Ym');


    $targetDir = UPLOAD_DIR . $type . 's/' . $yearMonth . '/';


    if (!is_dir($targetDir)) {


        mkdir($targetDir, 0755, true);


    }


    


    $filename = date('YmdHis') . '_' . random_str(8) . '.' . $ext;


    $targetPath = $targetDir . $filename;


    


    if (move_uploaded_file($file['tmp_name'], $targetPath)) {


        $relativePath = '/uploads/' . $type . 's/' . $yearMonth . '/' . $filename;


        return ['success' => true, 'path' => $relativePath, 'filename' => $filename];


    }


    return ['error' => '文件上传失败'];


}





// ============================================================


// 分页函数


// ============================================================





function paginate($total, $page = 1, $pageSize = PAGE_SIZE, $urlPattern = '') {


    $totalPage = ceil($total / $pageSize);


    $page = max(1, min($page, $totalPage));


    


    $html = '<div class="pagination">';


    if ($page > 1) {


        $html .= '<a href="' . sprintf($urlPattern, $page - 1) . '" class="page-prev">上一页</a>';


    }


    


    $start = max(1, $page - 2);


    $end = min($totalPage, $page + 2);


    


    if ($start > 1) $html .= '<a href="' . sprintf($urlPattern, 1) . '">1</a>';


    if ($start > 2) $html .= '<span>...</span>';


    


    for ($i = $start; $i <= $end; $i++) {


        if ($i == $page) {


            $html .= '<span class="current">' . $i . '</span>';


        } else {


            $html .= '<a href="' . sprintf($urlPattern, $i) . '">' . $i . '</a>';


        }


    }


    


    if ($end < $totalPage - 1) $html .= '<span>...</span>';


    if ($end < $totalPage) $html .= '<a href="' . sprintf($urlPattern, $totalPage) . '">' . $totalPage . '</a>';


    


    if ($page < $totalPage) {


        $html .= '<a href="' . sprintf($urlPattern, $page + 1) . '" class="page-next">下一页</a>';


    }


    $html .= '</div>';


    


    return $html;


}





// ============================================================


// SEO函数


// ============================================================





// 生成页面标题


function seo_title($pageTitle = '') {


    $siteName = SITE_NAME;


    if ($pageTitle) {


        return $pageTitle . ' - ' . $siteName;


    }


    return get_setting('seo_title', $siteName);


}





// 生成META描述


function seo_description($content = '') {


    $default = get_setting('seo_description', '');


    if ($content) {


        $content = strip_tags($content);


        $content = mb_substr($content, 0, 200, 'UTF-8');


        return $content;


    }


    return $default;


}





// 生成META关键词


function seo_keywords($keywords = '') {


    $default = get_setting('seo_keywords', '');


    return $keywords ?: $default;


}





// 生成面包屑导航


function breadcrumb($items = []) {


    $html = '<div class="breadcrumb">';


    $html .= '<a href="/">首页</a>';


    foreach ($items as $item) {


        if (isset($item['url'])) {


            $html .= ' &gt; <a href="' . $item['url'] . '">' . $item['title'] . '</a>';


        } else {


            $html .= ' &gt; <span>' . $item['title'] . '</span>';


        }


    }


    $html .= '</div>';


    return $html;


}





// 生成Sitemap XML


function generate_sitemap() {


    $xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";


    $xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";


    


    // 首页


    $xml .= sprintf(


        '<url><loc>%s/</loc><changefreq>daily</changefreq><priority>1.0</priority></url>' . "\n",


        rtrim(SITE_URL, '/')


    );


    


    // 文章


    $articles = db_query("SELECT slug FROM articles WHERE status=1 ORDER BY id DESC");


    foreach ($articles as $a) {


        $url = rtrim(SITE_URL, '/') . '/article/' . $a['slug'] . HTML_SUFFIX;


        $xml .= sprintf('<url><loc>%s</loc><changefreq>weekly</changefreq><priority>0.8</priority></url>' . "\n", $url);


    }


    


    // 产品


    $products = db_query("SELECT slug FROM products WHERE status=1 ORDER BY id DESC");


    foreach ($products as $p) {


        $url = rtrim(SITE_URL, '/') . '/product/' . $p['slug'] . HTML_SUFFIX;


        $xml .= sprintf('<url><loc>%s</loc><changefreq>weekly</changefreq><priority>0.8</priority></url>' . "\n", $url);


    }


    


    $xml .= '</urlset>';


    


    file_put_contents(__DIR__ . '/../sitemap.xml', $xml);


    return true;


}





// ============================================================


// 模板函数


// ============================================================





// 加载模板文件


function load_template($file, $data = []) {
    // 预览模式：检查 GET 参数 preview_theme
    static $preview_theme = null;
    static $preview_checked = false;
    if (!$preview_checked) {
        if (isset($_GET['preview_theme']) && !empty($_GET['preview_theme'])) {
            $preview_slug = preg_replace('/[^a-z0-9\-_]/', '', $_GET['preview_theme']);
            if (is_dir(THEME_DIR . $preview_slug)) {
                $preview_theme = $preview_slug;
            }
        }
        $preview_checked = true;
    }
    
    // 如果预览模式，使用预览模板
    if ($preview_theme !== null) {
        $theme = $preview_theme;
        $templateFile = THEME_DIR . $theme . '/' . $file;
        if (!file_exists($templateFile)) {
            $templateFile = THEME_DIR . 'default/' . $file;
        }
        if (file_exists($templateFile)) {
            $data["theme"] = $theme;
            extract($data);
            ob_start();
            require $templateFile;
            return ob_get_clean();
        }
        return '';
    }
    
    // 正常模式：从数据库读取当前模板
    $theme_row = db_query_one("SELECT value FROM settings WHERE `key` = 'template'");
    $theme = $theme_row ? $theme_row['value'] : CURRENT_THEME;
    // DEBUG: Write to file
    $templateFile = THEME_DIR . $theme . '/' . $file;

    
    $templateFile = THEME_DIR . $theme . '/' . $file;
    if (!file_exists($templateFile)) {
        $templateFile = THEME_DIR . 'default/' . $file;
    }

    if (file_exists($templateFile)) {
        $data["theme"] = $theme;
        extract($data);
        ob_start();
        require $templateFile;
        $result = ob_get_clean();
    return $result;
    }

    return '';
}

function get_themes() {


    $themes = [];


    $dirs = glob(THEME_DIR . '*', GLOB_ONLYDIR);


    foreach ($dirs as $dir) {


        $slug = basename($dir);


        $infoFile = $dir . '/theme.json';


        if (file_exists($infoFile)) {


            $info = json_decode(file_get_contents($infoFile), true);


            $info['slug'] = $slug;


            $themes[] = $info;


        } else {


            $themes[] = [


                'slug' => $slug,


                'name' => $slug,


                'description' => '',


                'version' => '1.0'


            ];


        }


    }


    return $themes;


}





// 切换模板


function switch_theme($slug) {


    $themeDir = THEME_DIR . $slug;


    if (is_dir($themeDir)) {


        return update_setting('template', $slug);


    }


    return false;


}





// ============================================================


// 缓存函数


// ============================================================





function cache_get($key) {


    if (!get_setting('cache_enabled', 0)) return false;


    $file = CACHE_DIR . md5($key) . '.cache';


    if (file_exists($file) && time() - filemtime($file) < 300) { // 5分钟缓存


        return unserialize(file_get_contents($file));


    }


    return false;


}





function cache_set($key, $data) {


    if (!get_setting('cache_enabled', 0)) return;


    $file = CACHE_DIR . md5($key) . '.cache';


    file_put_contents($file, serialize($data));


}





function cache_clear() {


    $files = glob(CACHE_DIR . '*.cache');


    foreach ($files as $file) {


        unlink($file);


    }


    return count($files);


}





// ============================================================


// 分类函数（无限极分类）


// ============================================================





// 获取分类树形结构


function get_category_tree($parent_id = 0, $level = 0) {


    $sql = "SELECT * FROM categories WHERE parent_id = ? AND status = 1 ORDER BY sort_order ASC, id ASC";


    $stmt = $GLOBALS['pdo']->prepare($sql);


    $stmt->execute([$parent_id]);


    $categories = $stmt->fetchAll(PDO::FETCH_ASSOC);


    


    $result = [];


    foreach ($categories as $category) {


        $category['level'] = $level;


        $result[] = $category;


        


        // 递归获取子分类


        $children = get_category_tree($category['id'], $level + 1);


        $result = array_merge($result, $children);


    }


    


    return $result;


}





// 获取某个分类的所有子分类ID（包括自己）


function get_category_children($category_id) {


    $children = [$category_id];


    $sql = "SELECT id FROM categories WHERE parent_id = ? AND status = 1";


    $stmt = $GLOBALS['pdo']->prepare($sql);


    $stmt->execute([$category_id]);


    


    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {


        $children = array_merge($children, get_category_children($row['id']));


    }


    


    return $children;


}





// 获取某个分类的所有父分类（用于面包屑导航）


function get_category_parents($category_id) {


    $parents = [];


    


    while ($category_id > 0) {


        $sql = "SELECT * FROM categories WHERE id = ? AND status = 1";


        $stmt = $GLOBALS['pdo']->prepare($sql);


        $stmt->execute([$category_id]);


        $category = $stmt->fetch(PDO::FETCH_ASSOC);


        


        if ($category) {


            $parents[] = $category;


            $category_id = $category['parent_id'];


        } else {


            break;


        }


    }


    


    return array_reverse($parents);


}





// 显示多级分类菜单（前台）


function display_category_menu($parent_id = 0, $current_slug = '') {


    $sql = "SELECT * FROM categories WHERE parent_id = ? AND status = 1 ORDER BY sort_order ASC, id ASC";


    $stmt = $GLOBALS['pdo']->prepare($sql);


    $stmt->execute([$parent_id]);


    $categories = $stmt->fetchAll(PDO::FETCH_ASSOC);


    


    if (empty($categories)) {


        return '';


    }


    


    $html = '<ul class="category-menu sub-menu">';


    foreach ($categories as $category) {


        $url = '/category/' . $category['slug'] . HTML_SUFFIX;


        $active = ($current_slug == $category['slug']) ? ' class="active"' : '';


        


        // 检查是否有子分类


        $check_sql = "SELECT COUNT(*) FROM categories WHERE parent_id = ? AND status = 1";


        $check_stmt = $GLOBALS['pdo']->prepare($check_sql);


        $check_stmt->execute([$category['id']]);


        $has_children = ($check_stmt->fetchColumn() > 0);


        


        $html .= '<li' . $active . '>';


        $html .= '<a href="' . $url . '">' . htmlspecialchars($category['name']) . '</a>';


        


        // 递归显示子分类


        if ($has_children) {


            $html .= display_category_menu($category['id'], $current_slug);


        }


        


        $html .= '</li>';


    }


    $html .= '</ul>';


    


    return $html;


}





// 生成分类面包屑导航


function category_breadcrumb($category_id) {


    $parents = get_category_parents($category_id);


    


    $items = [];


    foreach ($parents as $category) {


        $items[] = [


            'title' => $category['name'],


            'url' => '/category/' . $category['slug'] . HTML_SUFFIX


        ];


    }


    


    return breadcrumb($items);


}





// ============================================================


// 表单函数（内置表单组件）


// ============================================================





// 显示表单


function display_form($form_id_or_slug) {


    // 获取表单


    if (is_numeric($form_id_or_slug)) {


        $form = db_query_one("SELECT * FROM `forms` WHERE `id`=? AND `status`=1", [$form_id_or_slug]);


    } else {


        $form = db_query_one("SELECT * FROM `forms` WHERE `slug`=? AND `status`=1", [$form_id_or_slug]);


    }


    


    if (!$form) {


        return '<p style="color:red;">表单不存在或已禁用</p>';


    }


    


    $fields = json_decode($form['fields'], true);


    $settings = json_decode($form['settings'], true);


    


    $html = '<div class="form-wrapper" id="form-' . $form['id'] . '">';


    


    // 表单标题和描述


    if (isset($settings['show_title']) && $settings['show_title']) {


        $html .= '<h3 class="form-title">' . htmlspecialchars($form['name']) . '</h3>';


    }


    


    if ($form['description'] && isset($settings['show_description']) && $settings['show_description']) {


        $html .= '<p class="form-description">' . htmlspecialchars($form['description']) . '</p>';


    }


    


    // 表单开始


    $html .= '<form method="post" action="/form-submit.php" class="cms-form" id="form-' . $form['id'] . '-form" enctype="multipart/form-data">';


    $html .= '<input type="hidden" name="form_id" value="' . $form['id'] . '">';


    


    // 生成CSRF令牌


    if (!isset($_SESSION['csrf_token'])) {


        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));


    }


    $html .= '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';


    


    // 表单字段


    foreach ($fields as $field) {


        $html .= render_form_field($field);


    }


    


    // 提交按钮


    $submit_text = isset($settings['submit_text']) ? $settings['submit_text'] : '提交';


    $html .= '<div class="form-group">';


    $html .= '<button type="submit" class="btn btn-primary">' . htmlspecialchars($submit_text) . '</button>';


    $html .= '</div>';


    


    $html .= '</form>';


    $html .= '</div>';


    


    // 添加CSS


    $html .= '


    <style>


        .form-wrapper {


            max-width: 600px;


            margin: 20px auto;


            padding: 30px;


            background: white;


            border-radius: 8px;


            box-shadow: 0 2px 10px rgba(0,0,0,0.1);


        }


        


        .form-title {


            font-size: 24px;


            margin-bottom: 10px;


            text-align: center;


        }


        


        .form-description {


            text-align: center;


            color: #666;


            margin-bottom: 30px;


        }


        


        .cms-form .form-group {


            margin-bottom: 20px;


        }


        


        .cms-form label {


            display: block;


            margin-bottom: 5px;


            font-weight: 500;


        }


        


        .cms-form input[type="text"],


        .cms-form input[type="email"],


        .cms-form input[type="tel"],


        .cms-form input[type="number"],


        .cms-form input[type="date"],


        .cms-form input[type="time"],


        .cms-form select,


        .cms-form textarea {


            width: 100%;


            padding: 10px 12px;


            border: 1px solid #ddd;


            border-radius: 4px;


            font-size: 14px;


        }


        


        .cms-form textarea {


            min-height: 100px;


            resize: vertical;


        }


        


        .cms-form .required {


            color: red;


        }


        


        .cms-form .form-error {


            color: red;


            font-size: 12px;


            margin-top: 5px;


        }


        


        .form-success {


            padding: 20px;


            background: #d4edda;


            color: #155724;


            border-radius: 4px;


            text-align: center;


        }


    </style>


    ';


    


    return $html;


}





// 渲染表单字段


function render_form_field($field) {


    $name = $field['name'] ?? '';


    $label = $field['label'] ?? '';


    $type = $field['type'] ?? 'text';


    $required = $field['required'] ?? false;


    $placeholder = $field['placeholder'] ?? '';


    $options = $field['options'] ?? [];


    $accept = $field['accept'] ?? '';


    


    $html = '<div class="form-group">';


    $html .= '<label for="field-' . $name . '">';


    $html .= htmlspecialchars($label);


    if ($required) {


        $html .= ' <span class="required">*</span>';


    }


    $html .= '</label>';


    


    switch ($type) {


        case 'text':


        case 'email':


        case 'tel':


        case 'number':


        case 'date':


        case 'time':


            $html .= '<input type="' . $type . '" id="field-' . $name . '" name="fields[' . $name . ']" placeholder="' . htmlspecialchars($placeholder) . '" ' . ($required ? 'required' : '') . '>';


            break;


            


        case 'textarea':


            $html .= '<textarea id="field-' . $name . '" name="fields[' . $name . ']" placeholder="' . htmlspecialchars($placeholder) . '" ' . ($required ? 'required' : '') . '></textarea>';


            break;


            


        case 'select':


            $html .= '<select id="field-' . $name . '" name="fields[' . $name . ']" ' . ($required ? 'required' : '') . '>';


            $html .= '<option value="">请选择</option>';


            foreach ($options as $opt) {


                $html .= '<option value="' . htmlspecialchars($opt) . '">' . htmlspecialchars($opt) . '</option>';


            }


            $html .= '</select>';


            break;


            


        case 'radio':


            foreach ($options as $opt) {


                $html .= '<label class="radio-label">';


                $html .= '<input type="radio" name="fields[' . $name . ']" value="' . htmlspecialchars($opt) . '" ' . ($required ? 'required' : '') . '>';


                $html .= htmlspecialchars($opt);


                $html .= '</label><br>';


            }


            break;


            


        case 'checkbox':


            foreach ($options as $opt) {


                $html .= '<label class="checkbox-label">';


                $html .= '<input type="checkbox" name="fields[' . $name . '][]" value="' . htmlspecialchars($opt) . '">';


                $html .= htmlspecialchars($opt);


                $html .= '</label><br>';


            }


            break;


            


        case 'file':


            $html .= '<input type="file" id="field-' . $name . '" name="fields[' . $name . ']" accept="' . htmlspecialchars($accept) . '" ' . ($required ? 'required' : '') . '>';


            break;


    }


    


    $html .= '</div>';


    


    return $html;


}





// 解析短代码


function parse_shortcodes($content) {


    // 匹配 [form id=1] 或 [form slug=contact]


    $pattern = '/\[form\s+(id|slug)=([^\]]+)\]/';


    


    $content = preg_replace_callback($pattern, function($matches) {


        $type = $matches[1];


        $value = trim($matches[2], '="\'');


        


        if ($type == 'id') {


            return display_form(intval($value));


        } else {


            return display_form($value);


        }


    }, $content);


    


    return $content;


}





// ============================================================


// 其他工具函数


// ============================================================








// 获取系统设置（带缓存）


$_settings_cache = null;


function get_setting($key, $default = '') {


    global $pdo, $_settings_cache;


    


    // 初始化缓存


    if ($_settings_cache === null) {


        $_settings_cache = [];


        try {


            $rows = db_query("SELECT `key`, `value` FROM `settings`");


            foreach ($rows as $row) {


                $_settings_cache[$row['key']] = $row['value'];


            }


        } catch (Exception $e) {


            // 表可能不存在，返回默认值


            return $default;


        }


    }


    


    return $_settings_cache[$key] ?? $default;


}





// 时间友好显示


function time_ago($datetime) {


    $time = is_numeric($datetime) ? $datetime : strtotime($datetime);


    $diff = time() - $time;


    


    if ($diff < 60) return '刚刚';


    if ($diff < 3600) return floor($diff / 60) . '分钟前';


    if ($diff < 86400) return floor($diff / 3600) . '小时前';


    if ($diff < 2592000) return floor($diff / 86400) . '天前';


    return date('Y-m-d', $time);


}





// 截取字符串


function str_cut($str, $length = 100, $suffix = '...') {


    if (mb_strlen($str, 'UTF-8') <= $length) return $str;


    return mb_substr($str, 0, $length, 'UTF-8') . $suffix;


}





// 记录日志


function add_log($action, $module = '', $description = '') {


    $adminId = $_SESSION['admin_id'] ?? 0;


    $ip = $_SERVER['REMOTE_ADDR'] ?? '';


    $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';


    


    db_exec(


        "INSERT INTO `logs` (`admin_id`, `action`, `module`, `description`, `ip`, `user_agent`) VALUES (?, ?, ?, ?, ?, ?)",


        [$adminId, $action, $module, $description, $ip, $ua]


    );


}





// 发送邮件(简单版本)


function send_mail($to, $subject, $body) {


    $headers = "MIME-Version: 1.0\r\n";


    $headers .= "Content-type: text/html; charset=utf-8\r\n";


    $headers .= "From: " . SITE_NAME . " <noreply@" . parse_url(SITE_URL, PHP_URL_HOST) . ">\r\n";


    return mail($to, $subject, $body, $headers);


}








// ============================================================


// 数据库查询辅助函数


// ============================================================





if (!defined('PAGE_SIZE')) {


    define('PAGE_SIZE', 10);


}





function db_query($sql, $params = []) {


    global $pdo;


    $stmt = $pdo->prepare($sql);


    foreach ($params as $i => $param) {


        $stmt->bindValue($i + 1, $param);


    }


    try {
    $stmt->execute();
    } catch (Exception $e) {
        error_log("db_query error: " . $e->getMessage());
        return [];
    }


    return $stmt->fetchAll(PDO::FETCH_ASSOC);


}





function db_query_one($sql, $params = []) {


    global $pdo;


    $stmt = $pdo->prepare($sql);


    foreach ($params as $i => $param) {


        $stmt->bindValue($i + 1, $param);


    }


    try {
    $stmt->execute();
    } catch (Exception $e) {
        error_log("db_query_one error: " . $e->getMessage());
        return null;
    }


    return $stmt->fetch(PDO::FETCH_ASSOC);


}





function db_exec($sql, $params = []) {


    global $pdo;


    $stmt = $pdo->prepare($sql);


    foreach ($params as $i => $param) {


        $stmt->bindValue($i + 1, $param);


    }


    return $stmt->execute();


}









function db_last_insert_id() {
    global $pdo;
    return intval($pdo->lastInsertId());
}

function db_count($table, $where = '1=1', $params = []) {
    global $pdo;
    // Handle table alias (e.g., "logs l", "articles a")
    if (strpos($table, ' ') !== false) {
        $sql = "SELECT COUNT(*) as cnt FROM {$table} WHERE {$where}";
    } else {
        $sql = "SELECT COUNT(*) as cnt FROM `{$table}` WHERE {$where}";
    }
    $stmt = $pdo->prepare($sql);
    if (!empty($params)) {
        foreach ($params as $i => $param) {
            $stmt->bindValue($i + 1, $param);
        }
    }
    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    return intval($row['cnt']);
}





// ============================================================


// 伪静态 rewrite 规则生成


// ============================================================





/**


 * 生成伪静态 rewrite 规则并写入对应文件


 * @param int $url_mode 1=普通 2=伪静态 3=纯静态


 * @return array [success, message, rules, server]


 */


function generate_rewrite_rules($url_mode) {


    $site_root = dirname(__DIR__); // 网站根目录


    $is_nginx = false;


    $is_apache = false;





    // 检测服务器环境：优先用 $_SERVER，失败则用文件特征检测


    $srv = strtolower($_SERVER['SERVER_SOFTWARE'] ?? '');


    if (strpos($srv, 'nginx') !== false) {


        $is_nginx = true;


    } elseif (strpos($srv, 'apache') !== false) {


        $is_apache = true;


    } else {


        // 备用检测：检查典型文件路径


        if (file_exists('/etc/nginx') || file_exists('/www/server/nginx')) {


            $is_nginx = true;


        }


        if (function_exists('apache_get_version') || file_exists($site_root . '/.htaccess.bak')) {


            $is_apache = true;


        }


    }





    // 非伪静态模式：清理规则文件


    if ($url_mode != 2) {


        // Apache：恢复备份或清空 .htaccess（只删 CMS 规则）


        $htaccess = $site_root . '/.htaccess';


        if (file_exists($htaccess . '.bak')) {


            @rename($htaccess . '.bak', $htaccess);


        } elseif (file_exists($htaccess)) {


            // 只删除 BarjuCMS 规则块，保留其他内容


            $content = file_get_contents($htaccess);


            $content = preg_replace('/# === BarjuCMS 伪静态规则开始 ===.*?# === BarjuCMS 伪静态规则结束 ===\n?/s', '', $content);


            file_put_contents($htaccess, $content);


        }


        // Nginx：删除规则文件


        $nginx_file = $site_root . '/nginx_rewrite_rules.conf';


        if (file_exists($nginx_file)) {


            @unlink($nginx_file);


        }


        return ['success' => true, 'message' => '伪静态已关闭，规则已清理', 'server' => $is_nginx ? 'nginx' : ($is_apache ? 'apache' : 'unknown')];


    }





    // ===== 伪静态模式：生成规则 =====





    // 通用 rewrite 规则（匹配所有一级 .html 路由）


    // 具体路由由 index.php 中的 switch 处理


    $apache_rules  = "# === BarjuCMS 伪静态规则开始 ===\n";


    $apache_rules .= "<IfModule mod_rewrite.c>\n";


    $apache_rules .= "    RewriteEngine On\n";


    $apache_rules .= "\n";


    $apache_rules .= "    # 排除真实存在的文件和目录\n";


    $apache_rules .= "    RewriteCond %{REQUEST_FILENAME} !-f\n";


    $apache_rules .= "    RewriteCond %{REQUEST_FILENAME} !-d\n";


    $apache_rules .= "\n";


    $apache_rules .= "    # 主路由：/xxx.html → /index.php?p=xxx\n";


    $apache_rules .= "    RewriteRule ^([a-zA-Z0-9_-]+)\\.html$ /index.php?p=$1 [L]\n";


    $apache_rules .= "\n";


    $apache_rules .= "    # 详情页：/xxx/123.html → /index.php?p=xxx&id=123\n";


    $apache_rules .= "    RewriteRule ^([a-zA-Z0-9_-]+)/([0-9]+)\\.html$ /index.php?p=$1&id=$2 [L]\n";


    $apache_rules .= "</IfModule>\n";


    $apache_rules .= "# === BarjuCMS 伪静态规则结束 ===\n";





    $nginx_rules  = "# === BarjuCMS 伪静态规则开始 ===\n";


    $nginx_rules .= "    # 主路由：/xxx.html → /index.php?p=xxx\n";


    $nginx_rules .= "    rewrite ^/([a-zA-Z0-9_-]+)\\.html$ /index.php?p=$1 last;\n";


    $nginx_rules .= "\n";


    $nginx_rules .= "    # 详情页：/xxx/123.html → /index.php?p=xxx&id=123\n";


    $nginx_rules .= "    rewrite ^/([a-zA-Z0-9_-]+)/([0-9]+)\\.html$ /index.php?p=$1&id=$2 last;\n";


    $nginx_rules .= "# === BarjuCMS 伪静态规则结束 ===\n";





    if ($is_apache) {


        // Apache：写入 .htaccess（备份已有内容）


        $htaccess = $site_root . '/.htaccess';


        $existing = '';


        if (file_exists($htaccess)) {


            @rename($htaccess, $htaccess . '.bak');


            $existing = file_get_contents($htaccess . '.bak');


            // 删除旧的 CMS 规则块


            $existing = preg_replace('/# === BarjuCMS 伪静态规则开始 ===.*?# === BarjuCMS 伪静态规则结束 ===\n?/s', '', $existing);


        }


        $content = $existing . "\n" . $apache_rules;


        if (@file_put_contents($htaccess, $content) !== false) {


            return ['success' => true, 'message' => '.htaccess 已自动生成并生效', 'rules' => $apache_rules, 'server' => 'apache'];


        }


        return ['success' => false, 'message' => '写入 .htaccess 失败，请检查目录权限（需要可写）', 'server' => 'apache'];


    }





    // Nginx 或其他：生成规则文件，提示用户手动配置


    $nginx_file = $site_root . '/nginx_rewrite_rules.conf';


    if (@file_put_contents($nginx_file, $nginx_rules) !== false) {


        return ['success' => true, 'message' => '规则文件已生成，请手动添加到 Nginx 配置后执行 nginx -s reload', 'rules' => $nginx_rules, 'server' => 'nginx'];


    }


    return ['success' => false, 'message' => '写入规则文件失败，请检查目录权限', 'server' => 'nginx'];


}





/**


 * 获取当前伪静态规则状态（用于后台显示）


 */


function get_rewrite_status() {


    $site_root = dirname(__DIR__);


    $url_mode = get_setting('url_mode', 1);


    $result = [


        'url_mode'   => $url_mode,


        'server'      => 'unknown',


        'htaccess_ok' => false,


        'nginx_rules' => false,


        'rules'       => '',


    ];





    $srv = strtolower($_SERVER['SERVER_SOFTWARE'] ?? '');


    if (strpos($srv, 'nginx') !== false) {


        $result['server'] = 'nginx';


    } elseif (strpos($srv, 'apache') !== false) {


        $result['server'] = 'apache';


    }





    if ($result['server'] === 'apache') {


        $ht = $site_root . '/.htaccess';


        if (file_exists($ht) && strpos(file_get_contents($ht), 'BarjuCMS') !== false) {


            $result['htaccess_ok'] = true;


            $result['rules'] = file_get_contents($ht);


        }


    }





    $nf = $site_root . '/nginx_rewrite_rules.conf';


    if (file_exists($nf)) {


        $result['nginx_rules'] = true;


        $result['rules'] = file_get_contents($nf);


    }





    return $result;


}




/**

 * 更新设置

 */

function update_setting($key, $value) {

    global $pdo;

    $sql = "UPDATE settings SET value = ? WHERE `key` = ?";

    $stmt = $pdo->prepare($sql);

    return $stmt->execute([$value, $key]);

}





/**

 * 获取所有分类（扁平列表）

 */


/**
 * 获取所有分类（扁平列表）
 */

/**
 * 根据URL模式设置生成正确的链接
 */
function cms_url($page, $params = []) {
    static $mode = null;
    if ($mode === null) {
        $mode = intval(get_setting('url_mode', 1));
    }
    $slug = $params['slug'] ?? '';
    $pageNum = isset($params['page']) ? intval($params['page']) : 0;
    $category = $params['category'] ?? '';
    $query = $params['q'] ?? '';
    
    if ($mode === 1) {
        $url = '?p=' . $page;
        if ($slug)     $url .= '&slug=' . urlencode($slug);
        if ($pageNum > 1) $url .= '&page=' . $pageNum;
        if ($category)  $url .= '&category=' . urlencode($category);
        if ($query)     $url .= '&q=' . urlencode($query);
        return $url;
    }
    
    $map = [
        'home'=>'/', 'about'=>'/about.html', 'contact'=>'/contact.html',
        'services'=>'/services.html', 'news'=>'/news.html',
        'products'=>'/products.html', 'cases'=>'/cases.html',
        'team'=>'/team.html', 'search'=>'/search.html',
    ];
    
    if (isset($map[$page])) {
        $url = $map[$page];
    } elseif ($page === 'article' && $slug) {
        $url = '/article/' . urlencode($slug) . '.html';
    } elseif ($page === 'product' && $slug) {
        $url = '/product/' . urlencode($slug) . '.html';
    } elseif ($page === 'case' && $slug) {
        $url = '/case/' . urlencode($slug) . '.html';
    } elseif ($page === 'category' && $slug) {
        $url = '/category/' . urlencode($slug) . '.html';
    } else {
        $url = '?p=' . $page; if ($slug) $url .= '&slug=' . urlencode($slug); return $url;
    }
    
    if ($pageNum > 1) $url = str_replace('.html', '/' . $pageNum . '.html', $url);
    if ($category && in_array($page, ['products'])) {
        $url = '/product/category/' . urlencode($category) . '.html';
        if ($pageNum > 1) $url = str_replace('.html', '/' . $pageNum . '.html', $url);
    }
    if ($query) $url .= '?q=' . urlencode($query);
    return $url;
}


function getSetting($key, $default = null) {
    global $pdo;
    $stmt = $pdo->prepare("SELECT value FROM settings WHERE `key` = ? LIMIT 1");
    $stmt->execute([$key]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    return $row ? $row['value'] : $default;
}

// 获取最后插入的ID
function db_last_id() {
    global $pdo;
    return intval($pdo->lastInsertId());
}
