Files
FamilyBook/app/bootstrap.php
T

136 lines
5.7 KiB
PHP
Raw 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
/**
* 应用引导:配置加载、会话、自动加载、全局辅助函数。
* 每个页面/入口只需 require 这一个文件。
*/
declare(strict_types=1);
// ---------------- 自动加载(避免「新增类忘了 require」这类事故)----------------
spl_autoload_register(function ($class) {
if (strpos($class, 'App\\') !== 0) {
return;
}
// App\Storage\OssDriver → app/Storage/OssDriver.php(子命名空间靠这段映射)
$rel = str_replace('\\', '/', substr($class, 4));
$file = __DIR__ . '/' . $rel . '.php';
if (is_file($file)) {
require $file;
}
});
define('FB_ROOT', dirname(__DIR__));
define('FB_APP', __DIR__);
define('FB_UPLOADS', FB_ROOT . '/uploads');
/**
* 导航图标统一落盘的边长(正方形,只缩不放、透明留白居中)。
*
* 🔴 工单 #16:这个常量从 SimpleNav 搬迁时**漏搬了定义**。
* `NavFetch::grab_icon()` 与 `NavFetch::save_custom_icon()` 两处都在用它
* (`imagecreatetruecolor($store, $store)`),但全仓没有 `define`。
* PHP 8 下访问未定义常量抛 `Error: Undefined constant "ICON_STORE"`
* ⇒ 走到 GD 位图分支就 500。
*
* 为什么之前一直没暴露:`saveUploaded()` 是**死代码**(只有定义、没有调用点,
* 因为表单里没有上传入口 = 工单 #16 本身);而抓取路径里 SVG 分支在前、
* 很多站点 favicon 恰好是 SVG,绕过了 GD。只要有位图 favicon 就会崩。
*
* 取值沿用 SimpleNav 原文:128 —— 高清屏下按 45~64px 显示仍然锐利,
* 同时 128×128 的 PNG 只有几 KB,20 个格子也不心疼。
*/
if (!defined('ICON_STORE')) {
define('ICON_STORE', 128);
}
// ---------------- 配置 ----------------
$configFile = FB_ROOT . '/config.php';
if (!is_file($configFile)) {
http_response_code(500);
echo '<!doctype html><meta charset="utf-8"><title>岁月家 · 未配置</title>'
. '<div style="max-width:520px;margin:12vh auto;padding:28px 32px;'
. 'background:#fff;border:1px solid rgba(47,36,30,.1);border-radius:16px;'
. 'font:15px/1.8 -apple-system,\'PingFang SC\',\'Microsoft YaHei\',sans-serif;color:#2F241E">'
. '<h1 style="font-size:18px;margin:0 0 12px">还差一步:没有找到 config.php</h1>'
. '<p style="color:#7C6A5B;margin:0 0 16px">'
. '请复制同目录下的 <code>config.example.php</code> 为 <code>config.php</code>,'
. '填好数据库连接与 app_key 后再访问。</p>'
. '</div>';
exit;
}
$GLOBALS['config'] = require $configFile;
// ---------------- 错误报告(线上不显示错误,避免泄露路径与库信息)----------------
$debug = !empty($GLOBALS['config']['debug']);
error_reporting(E_ALL);
ini_set('display_errors', $debug ? '1' : '0');
ini_set('log_errors', '1');
/**
* 把错误日志指到**我们够得着的地方**(v24)。
*
* 为什么需要:线上出 500 时最该看的就是错误日志,而宝塔的 `/www/wwwlogs`
* 既不在部署账号的读权限内,也不在 `open_basedir` 允许的路径里
* (本站是 `/www/wwwroot/…:/tmp/`)。这一轮真遇到了:用户报 500,
* 能查的全查了(逐文件哈希、两账号 32 次真实渲染、边界输入、DNS/证书/缓存)
* 全绿,唯独拿不到"当时哪一行炸了"。
*
* 做法:**只需指定 `error_log`** —— PHP 本来就会把 Fatal error(含文件:行号与
* 调用栈)写进去,不需要自己注册 shutdown 钩子。
* (实测:先前那版加了 `register_shutdown_function` 反而多余,
* 钩子自身一旦出错还会**再抛一个 Fatal**,把真正的原因埋掉。)
*
* 落点:优先站点根的 `db/`(敏感配置所在地,不入 Web 根、不进仓库),
* 没有就回落 `sys_get_temp_dir()`(线上 open_basedir 含 `/tmp/`,一定可写)。
* 两处都不可写就保持原样(记日志不该把站点拖垮)。
*
* ⚠️ 只记 PHP 自己的错误文本,**不记**请求体/变量值(可能含口令)。
*/
{
$fbLogFile = '';
foreach ([__DIR__ . '/../db', sys_get_temp_dir()] as $fbDir) {
if (is_dir($fbDir) && is_writable($fbDir)) {
$fbLogFile = rtrim($fbDir, '/\\') . '/familybook-error.log';
break;
}
}
if ($fbLogFile !== '') {
ini_set('log_errors', '1');
ini_set('error_log', $fbLogFile);
}
}
// ---------------- 时区:日期是家书的核心数据,一开始就必须明确 ----------------
date_default_timezone_set($GLOBALS['config']['timezone'] ?? 'Asia/Shanghai');
// ---------------- 安全响应头 ----------------
// Referrer-Policy: 跨域取图不带 Referer —— 这一条能兜住云存储防盗链
// 导致的「图片不显示、但地址栏直接打开没问题」那类怪异现象。
if (!headers_sent()) {
header('Referrer-Policy: same-origin');
header('X-Content-Type-Options: nosniff');
}
// ---------------- 会话 ----------------
$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
session_name('fbsid');
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'secure' => $isHttps,
'httponly' => true,
'samesite' => 'Lax',
]);
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
require FB_APP . '/helpers.php';
// 全局函数库(不是类,不吃自动加载):折线图等纯渲染工具。
// ⚠️ 与 helpers.php 一样在**每个请求**都加载,所以里面只能放函数定义,
// 不要放查询/副作用(那会让每个请求都多跑一次)。
require FB_APP . '/chart.php';