Files
laoyang 3a3c5890cc 安装向导 + PHP 8.5 兼容修复
新增 install.php:全新部署时自动接管,用户只需上传文件、打开站点即进入
安装向导。安装页自己做三件事:
  - 建 db/ 与 uploads/{posters,tickets,photos,avatars}/,权限调到 775
  - 逐目录真写临时文件实测可写,不靠 is_writable 猜
  - 建库、建管理员、写 db/.installed 锁、自删本文件

配套改动:
  - config.php 把安装检查前置于建库之前,未安装一律 302 到 install.php。
    原实现无条件调用 initDB(),导致「打开首页」就等于建库并种下 admin/123456。
  - lib/db.php 的 initDB(\$seedDefaultAdmin = true);安装向导传 false,
    管理员由用户自己设,绝不种弱口令。新增 isInstalled()。
  - login.php / nav.php 接上 siteName(),站点名称不再只存不用。
  - lib/settings.php 新增 siteName(),缺省回退「电影日志」。

PHP 8.5 兼容:
  - 删掉 fetch_movie.php 与 lib/storage/http.php 的 curl_close()。
    8.5 起该函数 deprecated,调用会往输出插一行警告,把 JSON 响应污染成
    「警告 + JSON」,前端 JSON.parse 失败 → 电影名联想功能完全失灵。
  - config.php 的 display_errors 由 1 改为 0(生产环境不应向浏览器吐错误),
    错误仍写日志;需要调试时设 MOVIELOG_DEBUG=1 打开。

线上验证(movie.yangyq.net,PHP 8.5.12):
  未安装时 / 、/login.php 均 302 → install.php;
  安装页 200、体检 10/10 通过、自动建好 5 个目录;
  安装后 install.php 404、管理员登录成功、首页 200 无报错;
  联想接口带会话请求「流浪地球/星际穿越/霸王别姬」均返回纯 JSON(首字符 {)。
2026-10-31 21:35:41 +08:00

94 lines
4.4 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// ============================================
// 文件: config.php
// 说明: 应用引导入口。负责错误报告、会话、路径常量、上传目录,
// 并加载 lib/ 下的各功能模块,最后初始化数据库。
// 所有页面统一通过 require_once 'config.php' 引入本文件。
// ============================================
// 错误报告策略。
//
// 关键:PHP 的警告/弃用提示是**直接写进响应体**的。对返回 JSON 的接口
// (fetch_movie.php、storage_api.php 等)来说,一条 Warning 插在 JSON 前面
// 就会让前端 JSON.parse 失败,表现为「功能静默失灵」——联想下拉空着、
// 存储测试报连接失败,都可能是这个原因,而接口本身其实是好的。
//
// 本项目踩过:PHP 8.5 把 curl_close() 标为 deprecated,一行提示就把
// 「电影名联想」整个搞挂了(接口返回的 JSON 完全正确,只是前面多了警告)。
//
// 因此:
// - 永远不把错误显示给浏览器(display_errors=0);
// - 仍然记录到日志(log_errors=1),排查时看 PHP 错误日志;
// - 需要临时调试时,设环境变量 MOVIELOG_DEBUG=1 即可把错误显示打开。
$__movielog_debug = getenv('MOVIELOG_DEBUG') === '1';
error_reporting(E_ALL);
ini_set('display_errors', $__movielog_debug ? '1' : '0');
ini_set('log_errors', '1');
unset($__movielog_debug);
session_start();
// 跨域资源引用策略:本站页面加载对象存储(OSS / COS / 七牛)等第三方域名的图片时,
// 浏览器不发送 Referer,避免被云厂商的防盗链(bucket referer policy)拦成 403。
// 同源请求(本站自身的页面与接口)仍照常携带 Referer,不影响任何安全检查。
if (!headers_sent()) {
header('Referrer-Policy: same-origin');
}
// 项目根目录(lib/ 下的模块统一用它拼接绝对路径)
define('ROOT_DIR', __DIR__);
// 数据库路径
define('DB_PATH', ROOT_DIR . '/db/movies.db');
// 上传目录
define('UPLOAD_DIR', ROOT_DIR . '/uploads/');
define('POSTER_DIR', UPLOAD_DIR . 'posters/');
define('TICKET_DIR', UPLOAD_DIR . 'tickets/');
define('PHOTO_DIR', UPLOAD_DIR . 'photos/');
define('AVATAR_DIR', UPLOAD_DIR . 'avatars/');
// 创建上传目录
foreach ([POSTER_DIR, TICKET_DIR, PHOTO_DIR, AVATAR_DIR] as $dir) {
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
}
// ===== 安装检查(必须在加载任何业务模块、建库之前)=====
//
// 未安装时:把请求引到 install.php,并立刻退出 —— 绝不能继续往下走到 initDB()。
// 判据只看锁文件 db/.installed,由 install.php 装完时写入。
//
// 为什么不能「先 initDB() 再判断」:
// initDB() 会建库建表,并在 admins 为空时插入 admin/123456。
// 那样「打开任意页面」这个动作就等于完成了一半安装,还会把弱口令暴露出去
// (FeedLite 踩过同一个坑:访问动作本身建库 → 「未安装」永远判不出来)。
//
// 例外:install.php 自身、以及 css/js 等静态资源不受此检查约束
// (install.php 不加载本文件;静态资源由 Web 服务器直接返回,不经过 PHP)。
if (!is_file(ROOT_DIR . '/db/.installed')) {
// 当前脚本名(兼容子目录部署)
$self = basename($_SERVER['SCRIPT_NAME'] ?? '');
if ($self !== 'install.php' && PHP_SAPI !== 'cli') {
header('Location: install.php');
exit;
}
}
// ===== 功能模块 =====
require_once ROOT_DIR . '/lib/db.php'; // 数据库连接与初始化
require_once ROOT_DIR . '/lib/settings.php'; // 系统设置读写
require_once ROOT_DIR . '/lib/storage.php'; // 对象存储门面(含各云厂商驱动)
require_once ROOT_DIR . '/lib/helpers.php'; // 通用工具函数
require_once ROOT_DIR . '/lib/theme.php'; // 界面配色主题(注册表 / 当前主题)
require_once ROOT_DIR . '/lib/auth.php'; // 登录校验与账号管理
require_once ROOT_DIR . '/lib/movies.php'; // 电影 / 影院 / 搜索
require_once ROOT_DIR . '/lib/reviews.php'; // 评价与评价下的讨论
require_once ROOT_DIR . '/lib/stats.php'; // 统计与年度报告
// 初始化数据库
// 走到这里说明已装过(上面的检查已拦截未安装的情况),
// 因此 seedDefaultAdmin 传 false:老库本来就有人,空库也不该被塞一个 admin/123456。
initDB(false);