文件还在测试中

This commit is contained in:
2026-08-08 15:53:53 +08:00
parent 5f1e7adb64
commit 8101177cb5
241 changed files with 18074 additions and 22 deletions
+162
View File
@@ -0,0 +1,162 @@
<?php
namespace Core;
/**
* 模型基类:同时支持 MySQL 与 文件(JSON) 两种存储
* 子类设置 $table 与 $orderBy 即可。
*/
class Model
{
protected $table;
protected $pk = 'id';
protected $orderBy = 'id';
/* ---------- 文件模式 ---------- */
private function file(): string
{
return Db::fileDir() . '/' . $this->table . '.json';
}
private function read(): array
{
$f = $this->file();
if (!is_file($f)) return [];
$d = json_decode(file_get_contents($f), true);
return is_array($d) ? $d : [];
}
private function write(array $rows): void
{
$rows = $this->sanitizeUtf8($rows);
// JSON_INVALID_UTF8_SUBSTITUTE (PHP 7.2+) 保证即使存在非法 UTF-8 也不会让 json_encode 返回 false
// 避免 file_put_contents(false) 把整个数据文件清空为 0 字节(灾难性数据丢失)。
$json = json_encode($rows, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_INVALID_UTF8_SUBSTITUTE);
if ($json === false || $json === '') {
error_log('[Model::write] json_encode failed for ' . $this->table . '; aborting write to avoid data loss');
return;
}
$f = $this->file();
$dir = dirname($f);
if (!is_dir($dir)) {
@mkdir($dir, 0755, true);
}
// 写入失败(最常见:服务器目录/文件权限不足,PHP 进程无写权限)必须记录日志,
// 否则会“假成功”——页面跳转回去、用户以为保存了,数据却没变。
$bytes = @file_put_contents($f, $json);
if ($bytes === false) {
$err = error_get_last();
error_log('[Model::write] FAILED to write ' . $f . ' — ' . ($err['message'] ?? 'unknown error') .
' | 请检查目录/文件所有者是否为 PHP 运行用户(宝塔通常是 www)并赋予写权限');
}
}
/** 递归将数组中的字符串修复为合法 UTF-8,剔除非法字节序列 */
private function sanitizeUtf8($v)
{
if (is_array($v)) {
return array_map([$this, 'sanitizeUtf8'], $v);
}
if (is_string($v) && !mb_check_encoding($v, 'UTF-8')) {
return mb_convert_encoding($v, 'UTF-8', 'UTF-8');
}
return $v;
}
private function sort(array &$rows): void
{
$col = $this->orderBy;
usort($rows, function ($a, $b) use ($col) {
$va = $a[$col] ?? 0; $vb = $b[$col] ?? 0;
return $va <=> $vb;
});
}
/* ---------- 通用 CRUD ---------- */
public function all(): array
{
if (Db::driver() === 'mysql') {
return Db::query("SELECT * FROM `{$this->table}` ORDER BY `{$this->orderBy}` ASC")->fetchAll();
}
$rows = $this->read(); $this->sort($rows); return $rows;
}
public function find($id)
{
if (Db::driver() === 'mysql') {
return Db::query("SELECT * FROM `{$this->table}` WHERE `{$this->pk}`=?", [$id])->fetch() ?: null;
}
foreach ($this->read() as $r) if (($r[$this->pk] ?? null) == $id) return $r;
return null;
}
public function where(string $col, $val)
{
if (Db::driver() === 'mysql') {
return Db::query("SELECT * FROM `{$this->table}` WHERE `{$col}`=?", [$val])->fetch() ?: null;
}
foreach ($this->read() as $r) if (($r[$col] ?? null) == $val) return $r;
return null;
}
public function whereAll(string $col, $val): array
{
if (Db::driver() === 'mysql') {
return Db::query("SELECT * FROM `{$this->table}` WHERE `{$col}`=? ORDER BY `{$this->orderBy}` ASC", [$val])->fetchAll();
}
$out = []; foreach ($this->read() as $r) if (($r[$col] ?? null) == $val) $out[] = $r;
$this->sort($out); return $out;
}
public function insert(array $data)
{
if (Db::driver() === 'mysql') {
$cols = array_keys($data);
$sql = "INSERT INTO `{$this->table}` (`" . implode('`,`', $cols) . "`) VALUES (" . implode(',', array_fill(0, count($cols), '?')) . ")";
Db::query($sql, array_values($data));
return Db::pdo()->lastInsertId();
}
$rows = $this->read();
$id = $rows ? (max(array_column($rows, $this->pk)) + 1) : 1;
$data[$this->pk] = $id;
$rows[] = $data; $this->write($rows);
return $id;
}
public function update($id, array $data): void
{
if (Db::driver() === 'mysql') {
$cols = array_keys($data);
$sql = "UPDATE `{$this->table}` SET `" . implode('`=?,`', $cols) . "`=? WHERE `{$this->pk}`=?";
Db::query($sql, array_merge(array_values($data), [$id]));
return;
}
$rows = $this->read();
foreach ($rows as &$r) {
if (($r[$this->pk] ?? null) == $id) { $r = array_merge($r, $data); break; }
}
$this->write($rows);
}
public function delete($id): void
{
if (Db::driver() === 'mysql') {
Db::query("DELETE FROM `{$this->table}` WHERE `{$this->pk}`=?", [$id]);
return;
}
$rows = array_filter($this->read(), fn($r) => ($r[$this->pk] ?? null) != $id);
$this->write(array_values($rows));
}
/** 按任意列批量删除(用于主从表级联删除从表) */
public function deleteRaw(string $col, $val): void
{
if (Db::driver() === 'mysql') {
Db::query("DELETE FROM `{$this->table}` WHERE `{$col}`=?", [$val]);
return;
}
$rows = array_filter($this->read(), fn($r) => ($r[$col] ?? null) != $val);
$this->write(array_values($rows));
}
public function count(): int
{
return count($this->all());
}
}