PHP 时间日期处理

PHP 时间日期处理

时间日期是开发常用功能,这些函数要记住。

1. 获取当前时间

// 当前时间戳
echo time();

// 当前日期时间
echo date('Y-m-d H:i:s');

// 今天日期
echo date('Y-m-d');

2. 时间戳和日期互转

时间戳转日期

$time = time();
echo date('Y-m-d H:i:s', $time);

日期转时间戳

$date = '2024-01-01 12:00:00';
$time = strtotime($date);
echo $time;

3. 常用日期格式

// 2024-01-01
echo date('Y-m-d');

// 2024-01-01 12:00:00
echo date('Y-m-d H:i:s');

// 2024年01月01日
echo date('Y年m月d日');

// 2024/01/01
echo date('Y/m/d');

// 星期几
echo date('l');  // Sunday

4. 常用格式字符

  • Y: 4位年
  • m: 2位月
  • d: 2位日
  • H: 24小时制
  • i: 分钟
  • s: 秒

5. 日期加减

// 明天
echo date('Y-m-d', strtotime('+1 day'));

// 昨天
echo date('Y-m-d', strtotime('-1 day'));

// 一周后
echo date('Y-m-d', strtotime('+1 week'));

// 一个月后
echo date('Y-m-d', strtotime('+1 month'));

// 一年后
echo date('Y-m-d', strtotime('+1 year'));

6. 计算时间差

$start = strtotime('2024-01-01');
$end = strtotime('2024-01-10');

// 相差天数
$diff = ($end - $start) / 86400;
echo $diff . '天';

7. DateTime 类(推荐)

// 创建日期对象
$date = new DateTime('2024-01-01');

// 格式化输出
echo $date->format('Y-m-d H:i:s');

// 加一天
$date->add(new DateInterval('P1D'));
echo $date->format('Y-m-d');

// 计算两个日期差
$start = new DateTime('2024-01-01');
$end = new DateTime('2024-01-10');
$diff = $start->diff($end);
echo $diff->days . '天';

8. 常用场景

1)几天前

function timeAgo($time) {
    $diff = time() - $time;
    if ($diff < 60) return '刚刚';
    if ($diff < 3600) return floor($diff/60) . '分钟前';
    if ($diff < 86400) return floor($diff/3600) . '小时前';
    return floor($diff/86400) . '天前';
}

2)生日计算

$birthday = '1990-01-01';
$age = floor((time() - strtotime($birthday)) / 31536000);
echo $age . '岁';

总结

PHP 时间日期处理要点:

  • time() 获取时间戳
  • date() 格式化日期
  • strtotime() 日期转时间戳
  • 用 +1 day 等做日期加减
  • 推荐用 DateTime 类
  • 时间差除以86400得天数
  • 注意时区设置

做开发时间日期天天用,这些函数必须熟。


emer 发布于  2026-10-9 07:23 

Linux 查看磁盘空间

Linux 查看磁盘空间

服务器磁盘满了怎么办?这些命令帮你快速排查。

1. df 查看整体磁盘

基本用法

# 查看磁盘使用情况
df

# 人性化显示(带M、G单位)
df -h

常用参数

  • -h: 人性化显示大小
  • -T: 显示文件系统类型
  • -i: 查看 inode 使用情况
# 常用组合
df -hT

输出说明:

  • Filesystem: 磁盘分区
  • Size: 总大小
  • Used: 已用
  • Avail: 可用
  • Use%: 使用率
  • Mounted on: 挂载点

2. du 查看目录大小

查看当前目录大小

# 当前目录总大小
du -sh

# 查看指定目录
du -sh /var/log

查看子目录大小

# 查看当前目录下每个子目录大小
du -sh *

# 按大小排序
du -sh * | sort -rh

常用参数

  • -s: 只显示总计
  • -h: 人性化显示
  • --max-depth=1: 只看一层
# 查看根目录下各目录大小
du -sh /* --max-depth=1

3. 查找大文件

查找大于100M的文件

find / -type f -size +100M -exec ls -lh {} \;

查找最近修改的大文件

find /var -type f -size +50M -mtime -7

4. 常见排查场景

1)磁盘满了

# 先看哪个分区满了
df -h

# 找到大目录
du -sh /* | sort -rh

2)日志占空间

# 查看日志目录大小
du -sh /var/log

# 清空大日志文件
> /var/log/large.log

3)inode满了

# 查看inode使用
df -i

5. 清理磁盘

清理日志文件

# 清空文件(不删除文件)
> /var/log/messages

# 删除旧日志
rm /var/log/*.old

清理缓存

# 清理包缓存
yum clean all

清理 Docker 没用的镜像

docker system prune -a

总结

Linux 查看磁盘空间要点:

  • df -h 查看整体磁盘使用
  • du -sh 查看目录大小
  • find 查找大文件
  • 磁盘满了先看分区,再找大目录
  • 日志文件要定期清理
  • 注意 inode 也可能满
  • 用 sort -rh 按大小排序

运维排查磁盘问题,这些命令天天用。


emer 发布于  2026-10-9 07:21 

PHP 生成随机密码

PHP 生成随机密码

注册、重置密码功能经常要生成随机密码,这几种方法要会。

1. 简单随机字符串

function generatePassword($length = 8) {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $password = '';
    for ($i = 0; $i < $length; $i++) {
        $password .= $chars[mt_rand(0, strlen($chars) - 1)];
    }
    return $password;
}

// 生成8位密码
echo generatePassword(8);

2. 带特殊字符的密码

function generateStrongPassword($length = 12) {
    $lower = 'abcdefghijklmnopqrstuvwxyz';
    $upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $numbers = '0123456789';
    $special = '!@#$%^&*()';

    $all = $lower . $upper . $numbers . $special;

    $password = '';
    // 确保至少包含每种字符
    $password .= $lower[mt_rand(0, strlen($lower) - 1)];
    $password .= $upper[mt_rand(0, strlen($upper) - 1)];
    $password .= $numbers[mt_rand(0, strlen($numbers) - 1)];
    $password .= $special[mt_rand(0, strlen($special) - 1)];

    // 剩余随机
    for ($i = 4; $i < $length; $i++) {
        $password .= $all[mt_rand(0, strlen($all) - 1)];
    }

    // 打乱顺序
    return str_shuffle($password);
}

echo generateStrongPassword(12);

3. 更安全的 random_bytes

PHP 7+ 推荐用 random_bytes,更安全:

function generateSecurePassword($length = 12) {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';
    $max = strlen($chars) - 1;
    $password = '';

    for ($i = 0; $i < $length; $i++) {
        $password .= $chars[random_int(0, $max)];
    }

    return $password;
}

echo generateSecurePassword(16);

4. 生成验证码

function generateCode($length = 6) {
    $chars = '0123456789';
    $code = '';
    for ($i = 0; $i < $length; $i++) {
        $code .= $chars[random_int(0, 9)];
    }
    return $code;
}

// 6位数字验证码
echo generateCode(6);

5. 生成随机 Token

function generateToken($length = 32) {
    return bin2hex(random_bytes($length / 2));
}

// 32位随机token
echo generateToken();

6. 安全建议

1)用 random_int 不用 mt_rand

  • random_int 是密码安全随机数
  • mt_rand 不够安全

2)密码长度

  • 普通密码:8-12位
  • 强密码:12位以上
  • Token:32位以上

3)密码强度

  • 大小写字母+数字+特殊字符
  • 避免纯数字或纯字母

4)不要自己写随机算法

  • 用 PHP 内置函数
  • 不要用时间戳做随机种子

总结

PHP 生成随机密码要点:

  • 简单密码用 mt_rand 拼接
  • 强密码包含大小写+数字+特殊字符
  • 安全密码用 random_int
  • Token 用 bin2hex(random_bytes())
  • 验证码用纯数字
  • 推荐用 PHP 7+ 的 random_int
  • 不要自己写随机算法

做用户系统,生成密码和验证码是必备功能。


emer 发布于  2026-10-9 07:20 

MySQL 常用数据类型

MySQL 常用数据类型

建表选对数据类型很重要,这些常用类型要记住。

1. 数值类型

整数类型

  • TINYINT: 1字节,范围 -128~127
  • SMALLINT: 2字节,范围 -32768~32767
  • INT: 4字节,常用,范围约 ±21亿
  • BIGINT: 8字节,大数值

小数类型

  • FLOAT: 单精度浮点数
  • DOUBLE: 双精度浮点数
  • DECIMAL: 精确小数,存金额用这个
-- 金额用 DECIMAL(总位数, 小数位数)
price DECIMAL(10,2)  -- 最多10位,2位小数

2. 字符串类型

CHAR: 定长字符串,比如 CHAR(10) 存不满补空格

  • 适合固定长度,比如手机号、性别

VARCHAR: 变长字符串,最常用

  • VARCHAR(255),存多少占多少
  • 适合变长内容,比如用户名、标题

TEXT: 长文本

  • TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT
  • 存文章内容、备注等

常用选择:

  • 短文本:VARCHAR(50)
  • 长文本:TEXT
  • 固定长度:CHAR

3. 日期时间类型

DATE: 日期,格式 YYYY-MM-DD
TIME: 时间,格式 HH:MM:SS
DATETIME: 日期+时间,范围 1000-9999年
TIMESTAMP: 时间戳,范围 1970-2038年,自动更新

create_time DATETIME DEFAULT CURRENT_TIMESTAMP
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

4. 常用字段设计示例

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL COMMENT '用户名',
    age TINYINT UNSIGNED COMMENT '年龄',
    phone CHAR(11) COMMENT '手机号',
    money DECIMAL(10,2) DEFAULT 0 COMMENT '余额',
    status TINYINT DEFAULT 1 COMMENT '状态:1正常 0禁用',
    content TEXT COMMENT '简介',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

5. 选择建议

1)主键ID

  • 用 INT 或 BIGINT
  • 不要用 UUID 做主键

2)金额

  • 用 DECIMAL,不要用 FLOAT/DOUBLE
  • 避免精度丢失

3)状态字段

  • 用 TINYINT,不要用 VARCHAR 存 "正常/禁用"

4)时间

  • 创建时间用 DATETIME
  • 更新时间用 TIMESTAMP 自动更新

5)字符串

  • 短文本用 VARCHAR
  • 长文本用 TEXT
  • 固定长度用 CHAR

6. 注意事项

  • 不要过度设计: 够用就行,不要字段太大
  • 能用数字不用字符串: 排序、计算都快
  • 避免 NULL: 尽量设默认值,NULL 不好查询
  • 加注释: 每个字段都加 COMMENT

总结

MySQL 常用数据类型要点:

  • INT/BIGINT:整数
  • DECIMAL:金额
  • VARCHAR:字符串
  • TEXT:长文本
  • DATETIME/TIMESTAMP:时间
  • 主键用 INT 自增
  • 金额用 DECIMAL
  • 状态用 TINYINT
  • 字段加注释

建表选对类型,性能和存储都好。


emer 发布于  2026-10-9 07:19 

Docker 查看容器日志

Docker 查看容器日志

容器出问题怎么排查?先看日志,这些命令要会。

1. 查看容器日志

基本命令

# 查看全部日志
docker logs 容器名

# 查看容器ID
docker logs 容器ID

2. 常用参数

看最后几行

# 最后10行
docker logs --tail 10 容器名

# 最后20行
docker logs --tail 20 容器名

实时跟踪日志(最常用)

docker logs -f 容器名

带时间戳

docker logs -t 容器名

组合使用

# 实时跟踪 + 时间戳 + 最后10行
docker logs -f -t --tail 10 容器名

3. 按时间查看

# 最近10分钟的日志
docker logs --since 10m 容器名

# 最近1小时的日志
docker logs --since 1h 容器名

# 指定时间开始
docker logs --since "2024-01-01T00:00:00" 容器名

4. 多容器日志

docker-compose 查看日志

# 查看所有服务日志
docker-compose logs

# 实时跟踪
docker-compose logs -f

# 查看指定服务
docker-compose logs nginx

# 看最后50行
docker-compose logs --tail 50 nginx

5. 日志持久化

json-file 日志驱动(默认)

  • 日志存在 /var/lib/docker/containers/
  • 单个容器日志文件

配置日志大小限制

# docker-compose.yml
services:
  app:
    image: nginx
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

6. 常见排查场景

1)容器启动失败

docker logs 容器名

看最后几行报错信息。

2)接口报错

docker logs -f --tail 100 容器名

实时看请求日志。

3)查某个时间点的错误

docker logs --since "2024-01-01T10:00:00" 容器名 | grep "error"

7. 日志清理

清理单个容器日志

# 找到日志文件
sudo find /var/lib/docker/containers/ -name "*.log"

# 清空日志
sudo sh -c "echo > /var/lib/docker/containers/容器ID/容器ID-json.log"

重启容器自动清理

docker restart 容器名

总结

Docker 查看容器日志要点:

  • docker logs 查看日志
  • -f 实时跟踪最常用
  • --tail 看最后几行
  • -t 带时间戳
  • --since 按时间查
  • docker-compose logs 多容器
  • 配置日志大小防止占满磁盘
  • 排查问题先看日志

容器运维,看日志是第一步。


emer 发布于  2026-10-9 07:18 

PHP PDO 连接数据库

PHP PDO 连接数据库

PDO 是 PHP 推荐的数据库连接方式,支持多种数据库,防注入。

1. 什么是 PDO

PDO(PHP Data Objects)是 PHP 数据库抽象层,统一接口操作多种数据库。

2. 为什么用 PDO

  • 防SQL注入: 预处理语句
  • 支持多数据库: MySQL、PostgreSQL、SQLite 等
  • 面向对象: 写法更规范
  • 错误处理: 统一的错误模式

3. 连接数据库

// 数据库配置
$host = '127.0.0.1';
$dbname = 'test';
$user = 'root';
$pass = 'root';

try {
    // 连接数据库
    $pdo = new PDO(
        "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
        $user,
        $pass,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ]
    );
    echo '连接成功';
} catch(PDOException $e) {
    die('连接失败:' . $e->getMessage());
}

4. 查询数据

查询多条

$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll();

查询单条

$stmt = $pdo->query("SELECT * FROM users WHERE id = 1");
$user = $stmt->fetch();

5. 预处理语句(防注入)

查询

// 问号占位符
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([1]);
$user = $stmt->fetch();

// 命名占位符
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute([':id' => 1]);
$user = $stmt->fetch();

插入

$stmt = $pdo->prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
$stmt->execute([
    ':name' => '张三',
    ':age' => 20
]);

更新

$stmt = $pdo->prepare("UPDATE users SET name = :name WHERE id = :id");
$stmt->execute([
    ':name' => '李四',
    ':id' => 1
]);

删除

$stmt = $pdo->prepare("DELETE FROM users WHERE id = :id");
$stmt->execute([':id' => 1]);

6. 获取自增ID

$pdo->exec("INSERT INTO users (name) VALUES ('王五')");
$id = $pdo->lastInsertId();

7. 事务处理

try {
    $pdo->beginTransaction();

    $pdo->exec("INSERT INTO orders ...");
    $pdo->exec("UPDATE stock ...");

    $pdo->commit();
    echo '事务成功';
} catch(Exception $e) {
    $pdo->rollBack();
    echo '事务失败:' . $e->getMessage();
}

8. 常用配置

// 设置返回关联数组
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC

// 错误模式
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION

// 关闭预处理模拟(真正服务端预处理)
PDO::ATTR_EMULATE_PREPARES => false

总结

PHP PDO 连接数据库要点:

  • PDO 是统一数据库接口
  • 预处理语句防 SQL 注入
  • 支持事务操作
  • 配置错误模式方便调试
  • 推荐用命名占位符
  • 生产环境关闭预处理模拟
  • 记得设置 utf8mb4 字符集

做数据库开发,PDO 比 mysqli 更好用。


emer 发布于  2026-10-9 07:17 

Linux 查看系统日志

Linux 查看系统日志

排查问题先看日志,这些命令要记住。

1. 系统日志位置

/var/log/messages    # 系统主日志
/var/log/syslog      # Debian/Ubuntu 系统日志
/var/log/boot.log    # 启动日志
/var/log/cron        # 定时任务日志
/var/log/secure      # 安全日志(登录、sudo等)
/var/log/nginx/      # Nginx 日志
/var/log/httpd/      # Apache 日志
/var/log/mysql/      # MySQL 日志

2. 基本查看命令

cat:查看全部

cat /var/log/messages

tail:看末尾几行(最常用)

# 看最后10行
tail /var/log/messages

# 看最后20行
tail -n 20 /var/log/messages

# 实时跟踪日志(最常用)
tail -f /var/log/messages

head:看开头几行

head -n 10 /var/log/messages

3. 搜索日志

grep:搜索关键词

# 搜索错误信息
grep "error" /var/log/messages

# 不区分大小写
grep -i "error" /var/log/messages

# 显示行号
grep -n "error" /var/log/messages

# 反向匹配(不包含error)
grep -v "debug" /var/log/messages

结合管道

# 实时日志里搜索错误
tail -f /var/log/messages | grep "error"

# 搜索最近的nginx错误
tail -n 1000 /var/log/nginx/error.log | grep "500"

4. journalctl 查看 systemd 日志

# 查看全部日志
journalctl

# 看最后100行
journalctl -n 100

# 实时跟踪
journalctl -f

# 按服务查看
journalctl -u nginx

# 按时间查看
journalctl --since "1 hour ago"
journalctl --since "2024-01-01" --until "2024-01-02"

5. 常用排查场景

1)服务器登录失败

grep "Failed password" /var/log/secure

2)服务起不来

systemctl status nginx
journalctl -u nginx -n 50

3)磁盘满了

df -h
du -sh /var/log/*

4)定时任务没执行

cat /var/log/cron

6. 日志清理

手动删除大日志

# 查看日志大小
du -sh /var/log/*

# 清空日志(不删除文件)
> /var/log/messages

# 删除旧日志
rm /var/log/messages.1

logrotate 自动切割

  • Linux 自带日志切割工具
  • 配置文件在 /etc/logrotate.conf
  • 一般不用管,自动轮转

总结

Linux 查看日志要点:

  • 日志都在 /var/log/ 目录
  • tail -f 实时看日志最常用
  • grep 搜索关键词
  • journalctl 查 systemd 服务日志
  • 排查问题先看错误日志
  • 日志太大要及时清理
  • 系统自动轮转不用手动管

运维排查,看日志是第一步。


emer 发布于  2026-10-9 07:16 

MySQL 视图的使用

MySQL 视图的使用

视图是虚拟表,用好了能简化查询、保护数据。

1. 什么是视图

视图(View)是基于 SQL 查询结果的虚拟表,本身不存数据,数据来自真实表。

2. 为什么用视图

1)简化查询

  • 复杂查询封装成视图
  • 直接查视图就行

2)权限控制

  • 只暴露部分字段给用户
  • 敏感字段不暴露

3)数据独立

  • 表结构变了,视图不变
  • 上层应用不用改

3. 创建视图

-- 创建视图
CREATE VIEW v_user_order AS
SELECT u.id, u.name, o.order_no, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id;

-- 使用视图
SELECT * FROM v_user_order WHERE id = 1;

4. 修改和删除视图

-- 修改视图
CREATE OR REPLACE VIEW v_user_order AS
SELECT u.id, u.name, o.order_no, o.amount, o.create_time
FROM users u
JOIN orders o ON u.id = o.user_id;

-- 删除视图
DROP VIEW IF EXISTS v_user_order;

5. 查看视图

-- 查看所有视图
SHOW FULL TABLES WHERE Table_type = 'VIEW';

-- 查看视图结构
DESC v_user_order;

-- 查看创建语句
SHOW CREATE VIEW v_user_order;

6. 视图的限制

1)不能用 ORDER BY 在视图里

  • 外层查询可以排序
  • 视图里排序意义不大

2)某些视图不能更新

  • 包含 GROUP BY、DISTINCT、JOIN 等的视图
  • 不能通过视图 INSERT/UPDATE/DELETE

3)视图不存数据

  • 每次查询都执行 SQL
  • 复杂视图查询可能慢

7. 实际使用建议

1)视图不是越多越好

  • 复杂业务逻辑用视图
  • 简单查询直接写 SQL

2)视图不要嵌套太多层

  • 视图套视图性能差
  • 最多一层就够了

3)敏感数据保护

  • 密码、手机号等敏感字段
  • 用视图隐藏或脱敏

8. 视图和表的区别

对比项 表 视图
存数据 是 否(虚拟表)
占空间 是 否
查询速度 快 取决于SQL
安全性 低 高

总结

MySQL 视图使用要点:

  • 视图是虚拟表,不存数据
  • 简化复杂查询
  • 保护敏感数据
  • 用 CREATE VIEW 创建
  • 简单视图可以更新
  • 复杂视图查询可能慢
  • 不要过度使用视图

视图是好东西,但别滥用。


emer 发布于  2026-10-9 07:15 

PHP 上传文件处理

PHP 上传文件处理

文件上传是 Web 开发常见功能,安全处理很重要。

1. HTML 表单

<form method="post" action="upload.php" enctype="multipart/form-data">
    <input type="file" name="file">
    <button type="submit">上传</button>
</form>

注意:必须加 enctype="multipart/form-data"。

2. PHP 接收文件

// $_FILES 接收上传文件
$file = $_FILES['file'];

// 文件信息
$name = $file['name'];        // 文件名
$tmp = $file['tmp_name'];     // 临时路径
$size = $file['size'];        // 文件大小
$error = $file['error'];      // 错误码
$type = $file['type'];        // MIME类型

3. 错误码说明

  • UPLOAD_ERR_OK (0): 上传成功
  • UPLOAD_ERR_INI_SIZE (1): 超过 php.ini 限制
  • UPLOAD_ERR_FORM_SIZE (2): 超过表单限制
  • UPLOAD_ERR_PARTIAL (3): 部分上传
  • UPLOAD_ERR_NO_FILE (4): 没有文件

4. 完整上传代码

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $file = $_FILES['file'];

    // 检查错误
    if ($file['error'] !== UPLOAD_ERR_OK) {
        exit('上传失败');
    }

    // 检查大小(限制2M)
    if ($file['size'] > 2 * 1024 * 1024) {
        exit('文件太大');
    }

    // 允许的扩展名
    $allowExt = ['jpg', 'png', 'gif'];
    $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
    if (!in_array($ext, $allowExt)) {
        exit('不允许的文件类型');
    }

    // 生成新文件名
    $newName = uniqid() . '.' . $ext;
    $uploadDir = 'uploads/';

    // 移动文件
    if (move_uploaded_file($file['tmp_name'], $uploadDir . $newName)) {
        echo '上传成功';
    } else {
        echo '保存失败';
    }
}

5. php.ini 配置

; 上传最大文件大小
upload_max_filesize = 2M

; 表单最大提交大小
post_max_size = 8M

; 允许上传
file_uploads = On

; 临时目录
upload_tmp_dir = /tmp

6. 安全注意事项

1)不要用原文件名

  • 防止覆盖文件和路径穿越
  • 用 uniqid 生成新文件名

2)验证文件类型

  • 不要只看扩展名
  • 用 finfo 检查 MIME 类型
$finfo = new Finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);

3)上传目录不可执行

  • 上传目录关闭 PHP 执行
  • 防止上传木马

4)重命名文件

  • 不要保留用户上传的文件名
  • 防止覆盖和目录穿越攻击

7. 常见问题

1)上传失败,$_FILES 为空

  • 检查表单是否有 enctype
  • 检查 post_max_size 够不够大

2)move_uploaded_file 失败

  • 检查目录权限
  • 检查目录是否存在

3)大文件上传超时

  • 修改 max_execution_time
  • 修改 memory_limit

总结

PHP 文件上传要点:

  • 表单必须加 enctype
  • 用 $_FILES 接收文件
  • 检查错误码和文件大小
  • 验证文件类型
  • 重命名文件防止安全问题
  • 上传目录关闭执行权限
  • 注意 php.ini 配置

做上传功能,安全永远第一位。


emer 发布于  2026-10-9 07:13 

Nginx 反向代理原理

Nginx 反向代理原理

反向代理是 Nginx 最常用的功能,搞懂原理很有必要。

1. 什么是反向代理

正向代理:代理客户端,比如翻墙工具。
反向代理:代理服务器,客户端不知道真实服务器。

用户请求 Nginx,Nginx 再转发给后端服务器,用户以为自己直接访问的就是 Nginx。

2. 为什么用反向代理

1)隐藏真实服务器

  • 后端服务器 IP 不暴露
  • 更安全

2)负载均衡

  • 多个后端服务器分摊请求
  • 提高并发能力

3)统一入口

  • 多个服务用一个域名
  • 不同路径转发到不同服务

4)SSL 卸载

  • HTTPS 证书在 Nginx 配置
  • 后端用 HTTP 就行

5)缓存静态内容

  • Nginx 直接返回静态资源
  • 减轻后端压力

3. 基本配置

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

4. 常用代理头

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

后端通过这些头获取真实客户端信息。

5. 路径转发示例

# 根路径转发到PHP服务
location / {
    proxy_pass http://127.0.0.1:8080;
}

# /api 转发到后端接口
location /api/ {
    proxy_pass http://127.0.0.1:3000/;
}

# /static 直接读本地文件
location /static/ {
    root /var/www;
}

6. 超时设置

# 连接超时
proxy_connect_timeout 60s;

# 读取超时
proxy_read_timeout 60s;

# 发送超时
proxy_send_timeout 60s;

7. 常见问题

1)后端获取不到真实IP

  • 检查 X-Real-IP 头是否设置
  • 后端代码是否读取这个头

2)WebSocket 连接失败

  • 需要添加 Upgrade 头
  • 配置 HTTP/1.1
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;

3)大文件上传失败

  • 修改 client_max_body_size
  • 增加 proxy_read_timeout

总结

Nginx 反向代理要点:

  • 代理服务器,隐藏真实后端
  • 实现负载均衡、统一入口
  • 配置 proxy_pass 转发
  • 记得设置常用代理头
  • 后端通过请求头获取真实客户端信息
  • 注意超时和上传大小限制
  • WebSocket 需要特殊配置

做 Web 开发,反向代理是必备技能。


emer 发布于  2026-10-9 07:12 

Redis 过期策略详解

Redis 过期策略详解

Redis 设置了过期时间后,怎么删除过期的 key?这就涉及过期策略。

1. 设置过期时间

# 设置过期时间(秒)
set name "张三" ex 60

# 给已有key设置过期时间
expire name 60

# 查看剩余时间
ttl name

# 移除过期时间
persist name

2. 两种过期策略

1)惰性删除

  • 等到访问这个 key 的时候才检查是否过期
  • 过期了就删除,返回 nil
  • 优点:对 CPU 友好
  • 缺点:对内存不友好,很多过期 key 没被访问就占着内存

2)定期删除

  • 每隔一段时间,随机检查一些 key
  • 发现过期的就删除
  • 优点:定期清理,控制内存
  • 缺点:随机抽查可能漏

Redis 实际是两种策略结合用。

3. 内存淘汰策略

当内存满了,写入新数据时怎么办?

# 查看当前淘汰策略
config get maxmemory-policy

常用淘汰策略:

  • noeviction: 不淘汰,写入报错(默认)
  • allkeys-lru: 所有 key 中淘汰最久没用的
  • allkeys-random: 所有 key 中随机淘汰
  • volatile-lru: 只在设置了过期时间的 key 中淘汰最久没用的
  • volatile-random: 只在设置了过期时间的 key 中随机淘汰
  • volatile-ttl: 淘汰即将过期的 key

生产推荐:allkeys-lru

4. LRU 和 LFU

  • LRU(最近最少使用): 淘汰最久没访问的
  • LFU(最不经常使用): 淘汰访问次数最少的

Redis 4.0 开始支持 LFU。

5. 实际使用建议

  1. 设置合理的 maxmemory

    • 不要让 Redis 把内存占满
    • 一般设为服务器内存的 70%
  2. 选择合适的淘汰策略

    • 纯缓存场景:allkeys-lru
    • 有持久化数据:volatile-lru
  3. key 一定要设过期时间

    • 重要数据不要设过期
    • 缓存数据都要设过期时间
  4. 监控内存使用

    • 用 info memory 查看
    • 关注 used_memory 指标

6. 查看内存信息

# 查看内存使用
info memory

# 查看key数量
dbsize

# 查看大key
--bigkeys

总结

Redis 过期策略要点:

  • 惰性删除:访问时才删
  • 定期删除:定期抽查清理
  • 两种策略结合使用
  • 内存满了要配置淘汰策略
  • 生产推荐 allkeys-lru
  • 缓存数据都要设过期时间
  • 监控内存使用情况

搞懂过期策略,Redis 用起来才不踩坑。


emer 发布于  2026-10-9 07:11 

MySQL 字符集设置

MySQL 字符集设置

中文乱码是数据库开发常见问题,字符集设置一定要搞对。

1. 常用字符集

  • utf8: 最多3字节,不支持 emoji
  • utf8mb4: 最多4字节,支持 emoji,推荐用这个
  • gbk: 中文编码,不推荐

2. 查看当前字符集

-- 查看数据库字符集
SHOW VARIABLES LIKE 'character_set%';

-- 查看排序规则
SHOW VARIABLES LIKE 'collation%';

3. 创建数据库时指定

CREATE DATABASE mydb 
DEFAULT CHARACTER SET utf8mb4 
COLLATE utf8mb4_general_ci;

4. 创建表时指定

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

5. 修改已有表字符集

-- 修改表字符集
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4;

-- 修改字段字符集
ALTER TABLE users MODIFY name VARCHAR(50) CHARACTER SET utf8mb4;

6. 连接时设置字符集

PHP 连接数据库后一定要设置:

$conn = mysqli_connect('localhost', 'user', 'pass', 'db');
mysqli_set_charset($conn, 'utf8mb4');

或者 SQL 语句:

SET NAMES utf8mb4;

7. 中文乱码排查

出现中文乱码,按顺序检查:

  1. 数据库字符集是不是 utf8mb4
  2. 表字符集是不是 utf8mb4
  3. 字段字符集是不是 utf8mb4
  4. 连接字符集是不是 utf8mb4
  5. 前端页面编码是不是 utf-8

8. 常用排序规则

  • utf8mb4_general_ci: 不区分大小写,常用
  • utf8mb4_unicode_ci: 支持 Unicode,更准确
  • utf8mb4_bin: 区分大小写,按二进制比较

总结

MySQL 字符集设置要点:

  • 统一用 utf8mb4,不要用 utf8
  • 数据库、表、字段都要设置
  • 连接后要设置字符集
  • 中文乱码逐层排查
  • 推荐 utf8mb4_general_ci 排序

字符集统一,才能避免乱码问题。


emer 发布于  2026-10-9 07:10 

PHP 超全局变量详解

PHP 超全局变量详解

PHP 超全局变量在所有作用域都能用,写接口和表单开发天天用到。

1. 什么是超全局变量

超全局变量是 PHP 预定义的数组变量,不需要 global 声明,在函数、类、文件里都能直接访问。

2. 常用超全局变量

1)$_GET
获取 URL 查询参数。

// 访问 index.php?id=1&name=张三
$id = $_GET['id'];       // 1
$name = $_GET['name'];   // 张三

2)$_POST
获取表单 POST 提交的数据。

// 表单 method="post"
$username = $_POST['username'];
$password = $_POST['password'];

3)$_REQUEST
同时获取 GET、POST、COOKIE 数据。

$id = $_REQUEST['id'];
// 不推荐,安全隐患大

4)$_SESSION
会话数据,跨页面共享。

session_start();
$_SESSION['user_id'] = 1;
$_SESSION['username'] = '张三';

5)$_COOKIE
获取客户端 Cookie。

$token = $_COOKIE['token'];

6)$_SERVER
服务器和执行环境信息。

// 获取请求方法
$method = $_SERVER['REQUEST_METHOD'];

// 获取当前URL
$uri = $_SERVER['REQUEST_URI'];

// 获取客户端IP
$ip = $_SERVER['REMOTE_ADDR'];

// 获取HTTP头
$host = $_SERVER['HTTP_HOST'];

3. 文件上传变量

$_FILES
处理文件上传。

// 表单 enctype="multipart/form-data"
$file = $_FILES['upload'];
$name = $file['name'];        // 文件名
$tmp = $file['tmp_name'];     // 临时路径
$size = $file['size'];        // 文件大小
$error = $file['error'];      // 错误码

4. 注意事项

  • 安全问题: 所有超全局变量都不可信,必须做过滤和验证
  • isset 判断: 取值前用 isset 判断是否存在,避免报错
  • 类型转换: 数字参数记得转 int
  • 不要用 $_REQUEST: 安全隐患大,明确用 GET 还是 POST

5. 安全取值示例

// 安全获取GET参数
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;

// 安全获取POST参数
$username = isset($_POST['username']) ? trim($_POST['username']) : '';

// 判断请求方法
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 处理POST请求
}

总结

PHP 超全局变量要点:

  • $_GET:获取 URL 参数
  • $_POST:获取表单数据
  • $_SESSION:会话数据
  • $_COOKIE:客户端 Cookie
  • $_SERVER:服务器信息
  • $_FILES:文件上传
  • 所有外部输入都要过滤验证,不能直接用
  • 取值前先 isset 判断

做 PHP 开发,超全局变量是基础中的基础。


emer 发布于  2026-10-9 07:09 

PHP报错Call to undefined function解决

前言

运行PHP代码,突然报错:"Call to undefined function xxx()"?这个错太常见了,一般就是扩展没装或者文件没引入。本文讲怎么排查和解决。

一、报错长啥样

Fatal error: Uncaught Error: Call to undefined function mysql_connect() in xxx.php on line 5

意思:你调用了一个不存在的函数。

二、常见原因

原因1:PHP扩展没装

比如用了 mysqli_connect(),但没装mysqli扩展。

或者用了 gd库 的函数,但没装gd。

原因2:文件没引入

你自己写的函数,忘了include/require文件。

原因3:函数名写错了

拼写错了,比如 str_lower 写成 strlower。

原因4:PHP版本不对

低版本PHP没有高版本才有的函数。

三、排查步骤

第一步:看是哪个函数报错

报错信息里会告诉你函数名:

Call to undefined function curl_init()

这里就是 curl_init() 函数没找到。

第二步:判断是内置函数还是自己写的

  • 内置函数:PHP自带的,比如 curl_init、mysqli_connect
  • 自己写的:你在别的文件里定义的

第三步:如果是内置函数,查扩展装了没

php -m | grep curl

看看curl扩展装了没。

或者写个phpinfo:

<?php
phpinfo();
?>

搜这个函数名对应的扩展,看有没有。

四、安装扩展

例子1:装curl扩展

# CentOS
yum install php-curl

# Ubuntu
apt install php-curl

例子2:装mysqli扩展

yum install php-mysqli

例子3:装gd库

yum install php-gd

装完重启

systemctl restart php-fpm

五、如果是自己写的函数

检查文件引入

<?php
// 引入函数文件
require 'functions.php';

// 调用
my_function();

检查函数名对不对

// functions.php里定义的
function my_function() {
    // ...
}

调用的时候名字要一模一样。

检查命名空间

如果用了命名空间,要注意:

<?php
namespace App;

// 调用别的命名空间的函数
\OtherNamespace\my_function();

六、常见函数和对应扩展

函数 需要的扩展
mysqli_connect mysqli
curl_init curl
imagecreate gd
mb_strlen mbstring
openssl_encrypt openssl
pdo连接 pdo_mysql
json_encode json(默认有)
file_get_contents 不需要(默认有)

七、常见坑

坑1:mysql函数被废弃了

PHP7以后,mysql_* 函数都没了,要用mysqli或者PDO。

Call to undefined function mysql_connect()

解决: 换成mysqli或者PDO。

坑2:CLI和FPM装的扩展不一样

命令行里装了,网页上没有。

原因: CLI和FPM用不同的php.ini。

解决: 看phpinfo里的配置,装到对应的PHP版本里。

坑3:装了扩展没重启

装完忘了重启php-fpm。

解决:

systemctl restart php-fpm

坑4:函数名大小写

PHP函数名不区分大小写,但自己写的类方法区分。

坑5:Composer自动加载没生效

用了Composer的库,但没 vendor/autoload.php。

require 'vendor/autoload.php';

八、快速排查

第一步:看报错的函数名

Call to undefined function xxx()

第二步:查扩展

php -m | grep 扩展名

第三步:没装就装

yum install php-扩展名

第四步:重启

systemctl restart php-fpm

九、常用扩展安装

一次性装常用扩展

yum install php-mysqli php-curl php-gd php-mbstring php-xml php-json

Ubuntu

apt install php-mysqli php-curl php-gd php-mbstring php-xml

总结

记住:

  1. 报错会告诉你是哪个函数没找到
  2. 内置函数就查扩展装了没
  3. 自己写的就查文件引入了没
  4. 装完扩展要重启php-fpm
  5. mysql_* 函数PHP7没了,换mysqli

排查流程:

看报错函数名 → 判断是内置还是自己写的 → 内置查扩展 → 自己写的查引入 → 解决

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:38 

PHP mysqli连接MySQL,完整示例代码

前言

PHP连MySQL用mysqli扩展,比mysql扩展好用,支持预处理、事务等。本文写个完整的连接和增删改查示例。

一、连接数据库

面向过程写法

<?php
$host = '127.0.0.1';
$user = 'root';
$pass = '123456';
$dbname = 'test';
$port = 3306;

// 连接
$conn = mysqli_connect($host, $user, $pass, $dbname, $port);

// 检查连接
if (!$conn) {
    die('连接失败: ' . mysqli_connect_error());
}

echo '连接成功';

面向对象写法

<?php
$conn = new mysqli('127.0.0.1', 'root', '123456', 'test', 3306);

if ($conn->connect_error) {
    die('连接失败: ' . $conn->connect_error);
}

echo '连接成功';

二、设置字符集

mysqli_set_charset($conn, 'utf8mb4');
// 或者
$conn->set_charset('utf8mb4');

注意: 一定要设utf8mb4,不然中文表情存不进去。

三、查询数据(SELECT)

面向过程

$sql = "SELECT id, name, age FROM users WHERE status = 1";
$result = mysqli_query($conn, $sql);

if (mysqli_num_rows($result) > 0) {
    while ($row = mysqli_fetch_assoc($result)) {
        echo "ID: " . $row['id'] . " 姓名: " . $row['name'] . "<br>";
    }
} else {
    echo "没有数据";
}

面向对象

$sql = "SELECT id, name, age FROM users WHERE status = 1";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        echo "ID: " . $row['id'] . " 姓名: " . $row['name'] . "<br>";
    }
}

取所有数据为数组

$list = [];
while ($row = mysqli_fetch_assoc($result)) {
    $list[] = $row;
}

print_r($list);

四、插入数据(INSERT)

直接写SQL

$name = '张三';
$age = 20;

$sql = "INSERT INTO users (name, age) VALUES ('$name', $age)";

if (mysqli_query($conn, $sql)) {
    echo "插入成功,ID: " . mysqli_insert_id($conn);
} else {
    echo "插入失败: " . mysqli_error($conn);
}

预处理(防SQL注入,推荐)

$stmt = mysqli_prepare($conn, "INSERT INTO users (name, age) VALUES (?, ?)");

mysqli_stmt_bind_param($stmt, 'si', $name, $age);
// 'si' 表示:string, int

$name = '李四';
$age = 25;

if (mysqli_stmt_execute($stmt)) {
    echo "插入成功,ID: " . mysqli_insert_id($conn);
} else {
    echo "失败: " . mysqli_error($conn);
}

五、更新数据(UPDATE)

$id = 1;
$name = '王五';

$sql = "UPDATE users SET name = '$name' WHERE id = $id";

if (mysqli_query($conn, $sql)) {
    echo "更新成功,影响行数: " . mysqli_affected_rows($conn);
}

六、删除数据(DELETE)

$id = 1;

$sql = "DELETE FROM users WHERE id = $id";

if (mysqli_query($conn, $sql)) {
    echo "删除成功";
}

七、关闭连接

mysqli_close($conn);
// 或者
$conn->close();

八、完整示例封装

<?php
class DB
{
    private $conn;

    public function __construct()
    {
        $this->conn = new mysqli('127.0.0.1', 'root', '123456', 'test', 3306);
        if ($this->conn->connect_error) {
            die('连接失败');
        }
        $this->conn->set_charset('utf8mb4');
    }

    // 查询多行
    public function queryAll($sql)
    {
        $result = $this->conn->query($sql);
        $list = [];
        while ($row = $result->fetch_assoc()) {
            $list[] = $row;
        }
        return $list;
    }

    // 查询单行
    public function queryOne($sql)
    {
        $result = $this->conn->query($sql);
        return $result->fetch_assoc();
    }

    // 关闭
    public function __destruct()
    {
        $this->conn->close();
    }
}

// 使用
$db = new DB();
$users = $db->queryAll("SELECT * FROM users");
print_r($users);

九、常见坑

坑1:SQL注入

直接拼接变量到SQL里,有注入风险。

解决: 用预处理语句(?占位符)。

坑2:中文乱码

忘了设字符集。

解决: 连接后立刻 set_charset('utf8mb4')。

坑3:只取一行

用 mysqli_fetch_assoc 循环多次才取完。

注意: 这个函数每次只取一行,要循环。

坑4:连接失败没报错

生产环境关了错误显示。

解决: 手动检查 mysqli_connect_error()。

坑5:忘了关连接

频繁连接不关,占资源。

解决: 用完就关,或者用单例。

总结

记住:

  1. mysqli_connect 连接数据库
  2. set_charset('utf8mb4') 设字符集
  3. mysqli_query 执行SQL
  4. mysqli_fetch_assoc 取数据
  5. 防注入用预处理(?占位符)
  6. 用完 mysqli_close 关连接

最常用:

$conn = mysqli_connect('127.0.0.1', 'root', '123456', 'test', 3306);
mysqli_set_charset($conn, 'utf8mb4');

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:36 

PHP上传文件大小限制,修改php.ini

前言

上传个大点的文件,提示"文件过大"或者直接报错?那是PHP上传限制太小了。本文讲怎么改php.ini调大上传限制。

一、默认限制多大

PHP默认上传限制一般是:

  • upload_max_filesize = 2M
  • post_max_size = 8M

传个10M的文件就不行了。

二、要改哪几个参数

1. upload_max_filesize

单个文件最大大小:

upload_max_filesize = 100M

2. post_max_size

整个POST请求最大大小:

post_max_size = 100M

注意: post_max_size要 >= upload_max_filesize。

3. memory_limit

脚本可用内存:

memory_limit = 128M

要比post_max_size大。

4. max_execution_time

脚本最大执行时间(秒):

max_execution_time = 300

传大文件要久一点,设长点。

5. max_input_time

接收数据最大时间(秒):

max_input_time = 300

三、找php.ini

php --ini

或者写个phpinfo:

<?php
phpinfo();
?>

看Loaded Configuration File。

四、修改php.ini

改成100M上传

upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 128M
max_execution_time = 300
max_input_time = 300

改成50M上传

upload_max_filesize = 50M
post_max_size = 50M
memory_limit = 64M

五、改完重启

PHP-FPM

systemctl restart php-fpm

Apache

systemctl restart httpd

六、验证

写个phpinfo,看这几个参数改了没:

<?php
phpinfo();
?>

搜 upload_max_filesize,看是不是改过来了。

七、Nginx也要改

光改PHP不够,Nginx也有限制。

Nginx配置

client_max_body_size 100M;

改完重载

nginx -t
nginx -s reload

八、常见坑

坑1:改了php.ini不生效

改的不是实际加载的那个。

解决: 用phpinfo看实际加载的是哪个。

坑2:只改了upload_max_filesize

post_max_size没改,还是不行。

注意: 两个都要改,post_max_size >= upload_max_filesize。

坑3:Nginx挡了

PHP改了,Nginx的client_max_body_size没改,还是413错误。

解决: Nginx也改。

坑4:内存不够

Allowed memory size of ... bytes exhausted

解决: 加大memory_limit。

坑5:上传超时

大文件传一半就断了。

解决: 加大max_execution_time和max_input_time。

坑6:.htaccess里覆盖了

有些主机在.htaccess里设了php_value。

检查: 看看.htaccess里有没有。

九、快速排查

上传报错

  1. 先看PHP的upload_max_filesize够不够
  2. 再看post_max_size够不够
  3. 再看Nginx的client_max_body_size够不够
  4. 再看memory_limit够不够

十、推荐配置

一般网站(20M上传)

upload_max_filesize = 20M
post_max_size = 20M
memory_limit = 64M

文件站(100M上传)

upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 128M
max_execution_time = 300
max_input_time = 300

Nginx对应

client_max_body_size 100M;

总结

记住:

  1. 改三个参数:upload_max_filesize、post_max_size、memory_limit
  2. post_max_size >= upload_max_filesize
  3. Nginx也要改client_max_body_size
  4. 改完重启php-fpm和reload nginx
  5. 大文件要加大超时时间

最常用:

upload_max_filesize = 100M
post_max_size = 100M
memory_limit = 128M

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:35 

PHP开启错误显示,php.ini配置修改

前言

PHP报错了,页面白屏或者500,啥也不显示,不知道哪错了?开了错误显示就能看到具体错误了。本文讲怎么改php.ini开启错误显示。

一、为什么不显示错误

生产环境为了安全,默认不显示错误,防止暴露敏感信息(路径、数据库密码等)。

但开发的时候就不方便了,啥也看不到。

二、找到php.ini

找php.ini位置

php --ini

输出:

Loaded Configuration File: /etc/php.ini

或者:

/etc/php/7.4/cli/php.ini

注意:CLI和FPM的php.ini可能不一样。

看当前加载的是哪个

写个phpinfo:

<?php
phpinfo();
?>

浏览器访问,看Loaded Configuration File。

三、修改php.ini

1. 显示错误

display_errors = On

2. 报错级别

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT

或者全开:

error_reporting = E_ALL

3. 错误日志

log_errors = On
error_log = /var/log/php_errors.log

4. 显示启动错误

display_startup_errors = On

四、改完重启

PHP-FPM

systemctl restart php-fpm

或者:

systemctl reload php-fpm

Apache

systemctl restart httpd

五、代码里临时开启

不想改php.ini,在代码开头加:

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

或者:

ini_set('error_reporting', E_ALL);
ini_set('display_errors', 1);

六、常见错误类型

E_ALL

所有错误和警告。

E_ERROR

致命运行错误,脚本停止。

E_WARNING

运行时警告,脚本继续。

E_NOTICE

运行时注意,比如未定义变量。

七、常见坑

坑1:改了php.ini不生效

改的不是实际加载的那个php.ini。

解决: 用 php --ini 看实际加载的是哪个。

坑2:FPM和CLI的php.ini不一样

命令行改了,网页上不生效。

原因: CLI和FPM用不同的php.ini。

解决: 改FPM的那个,比如 /etc/php/7.4/fpm/php.ini。

坑3:页面白屏

可能是语法错误,php.ini都没加载起来。

检查: 看PHP错误日志。

坑4:开了错误显示有安全风险

生产环境不能开!

注意: 排查完一定要改回去。

坑5:.htaccess里覆盖了

有些主机在.htaccess里设了php_flag。

检查: 看看.htaccess里有没有。

八、生产环境配置

生产环境不要开display_errors,但要开日志:

display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED

九、查看错误日志

PHP错误日志

tail -f /var/log/php_errors.log

Nginx错误日志

tail -f /var/log/nginx/error.log

Apache错误日志

tail -f /var/log/httpd/error_log

十、快速排查

第一步:代码里临时开

ini_set('display_errors', 1);
error_reporting(E_ALL);

第二步:看具体报错

刷新页面,看报什么错。

第三步:解决完删掉

改完了把代码里的ini_set删掉,或者改回生产配置。

总结

记住:

  1. display_errors = On 显示错误
  2. error_reporting = E_ALL 显示所有错误
  3. 改完重启php-fpm
  4. 生产环境关掉display_errors,开log_errors
  5. 代码里可以用ini_set临时开

开发环境推荐:

display_errors = On
error_reporting = E_ALL
log_errors = On

生产环境推荐:

display_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_NOTICE

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:34 

Composer安装依赖超时,国内镜像源配置

前言

composer install 装依赖,半天没反应,最后超时失败?那是因为默认的源在国外,国内访问慢。换个国内镜像就快了。

一、为什么会超时

Composer默认从 packagist.org 下载包,服务器在国外,国内访问慢,经常超时。

换个国内镜像源,速度飞快。

二、常用国内镜像

1. 阿里云镜像

https://mirrors.aliyun.com/composer/

2. 腾讯云镜像

https://mirrors.tencent.com/composer/

3. 华为云镜像

https://mirrors.huaweicloud.com/repository/php/

4. 豆瓣镜像(老了,不推荐)

https://packagist.phpcomposer.com

三、全局配置镜像

方法1:命令行配置

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

方法2:改config文件

编辑 ~/.composer/config.json:

{
    "repositories": {
        "packagist": {
            "type": "composer",
            "url": "https://mirrors.aliyun.com/composer/"
        }
    }
}

查看当前镜像

composer config -g repo.packagist

四、项目单独配置

在项目根目录的 composer.json 里加:

{
    "repositories": {
        "packagist": {
            "type": "composer",
            "url": "https://mirrors.aliyun.com/composer/"
        }
    }
}

或者用命令:

composer config repo.packagist composer https://mirrors.aliyun.com/composer/

五、临时使用镜像

不想改全局配置,临时用一次:

composer install --prefer-dist -vvv --repository-url=https://mirrors.aliyun.com/composer/

六、其他常用配置

1. 禁用github接口调用

有时候github的API调用会卡住,禁用掉:

composer config -g github-protocols https

2. 国内composer下载地址

安装composer的时候也慢,用国内下载:

php -r "copy('https://mirrors.aliyun.com/composer/composer.phar', 'composer.phar');"

3. 查看配置

composer config -g --list

七、安装依赖

安装

composer install

更新

composer update

加-v看详细过程

composer install -vvv

八、常见坑

坑1:装了还是慢

缓存没清。

解决:

composer clear-cache

坑2:报SSL错误

SSL certificate problem

解决: 关掉SSL验证(不推荐临时用):

composer config -g secure-http false

坑3:提示文件权限不够

Failed to download ...

解决: 目录权限不对。

chmod -R 777 vendor
chmod -R 777 composer.json

坑4:内存不够

Allowed memory size of ... bytes exhausted

解决: 加大内存:

php -d memory_limit=-1 /usr/local/bin/composer install

坑5:镜像也挂了

阿里云镜像有时候也抽风。

解决: 换一个镜像,比如腾讯云的。

九、验证镜像生效

查看当前镜像源

composer config -g repo.packagist

输出应该是你设的阿里云地址。

装个小包试试速度

composer require monolog/monolog

看下载速度快不快。

十、推荐配置

一键配置阿里云镜像

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

清缓存

composer clear-cache

安装

composer install

总结

记住:

  1. 慢就换国内镜像,阿里云的最稳
  2. 全局配置:composer config -g repo.packagist composer 地址
  3. 不行就清缓存:composer clear-cache
  4. 换镜像:阿里云、腾讯云、华为云
  5. 临时用:加 --repository-url=地址

最常用:

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:32 

ThinkPHP6 500报错,开启错误日志查看原因

前言

ThinkPHP6网站突然500报错,页面啥也不显示,不知道哪错了?本文讲怎么开启错误显示和日志,快速定位问题。

一、为什么500不显示错误

生产环境为了安全,关闭了错误显示,直接返回500,不显示具体错误信息。

这是对的,不然用户能看到你的代码路径、数据库密码等敏感信息。

但排查问题就不方便了。

二、临时开启错误显示

修改 .env 文件

项目根目录下有个 .env 文件:

APP_DEBUG = true

把 APP_DEBUG 改成 true。

刷新页面,就能看到具体错误了。

注意

排查完记得改回 false! 生产环境不能开debug。

三、开启日志记录

就算不开debug,错误也会记录到日志文件里。

日志配置

config/log.php 文件:

return [
    // 默认日志通道
    'default' => 'file',

    // 日志通道列表
    'channels' => [
        'file' => [
            // 日志记录方式
            'type' => 'File',
            // 日志保存目录
            'path' => '',
            // 单文件日志写入
            'single' => false,
            // 独立日志级别
            'apart_level' => [],
            // 最大日志文件数量
            'max_files' => 30,
        ],
    ],
];

日志位置

默认在 runtime/log/ 目录下。

按日期分目录:

runtime/log/
├── 2026-10/
│   ├── 08.log
│   ├── 07.log
│   └── 06.log
└── 2026-09/
    └── ...

查看今天的错误

tail -f runtime/log/2026-10/08.log

四、常见500错误排查

1. 目录权限问题

runtime目录没权限写:

Permission denied

解决:

chmod -R 755 runtime
chown -R www:www runtime

2. 缺少扩展

Call to undefined function xxx()

解决: 装对应的PHP扩展。

3. 数据库连接失败

SQLSTATE[HY000] [1045] Access denied

检查: 数据库配置对不对。

.env 或 config/database.php:

'hostname' => '127.0.0.1',
'database' => '数据库名',
'username' => '用户名',
'password' => '密码',

4. 路由错误

Route not found

检查: 路由配置对不对。

5. 模板找不到

template not exists

检查: 模板文件路径对不对。

五、手动写日志

代码里可以手动写日志:

// 记录错误
Trace::error('这是一个错误信息');

// 记录警告
Trace::warning('这是一个警告');

// 记录信息
Trace::info('这是一个信息');

或者用助手函数:

trace('这是调试信息', 'debug');

六、查看PHP错误日志

除了ThinkPHP的日志,PHP本身也有错误日志。

查看PHP错误日志位置

php --ini | grep error_log

Nginx错误日志

tail -f /var/log/nginx/error.log

七、常见排查步骤

第一步:开debug看具体错误

改 .env:

APP_DEBUG = true

刷新页面,看具体报什么错。

第二步:看ThinkPHP日志

tail -f runtime/log/2026-10/08.log

第三步:看Nginx错误日志

tail -f /var/log/nginx/error.log

第四步:看PHP错误日志

tail -f /var/log/php-fpm/error.log

第五步:解决完改回debug

改完 .env:

APP_DEBUG = false

八、常见坑

坑1:开了debug还是不显示

缓存没清。

解决: 删除runtime目录下的缓存:

rm -rf runtime/cache/*

坑2:日志目录没权限

stream_socket_client(): unable to connect

runtime目录没写权限。

解决:

chmod -R 755 runtime

坑3:生产环境开着debug

危险!用户能看到你的敏感信息。

解决: 排查完立刻改回 APP_DEBUG = false。

坑4:日志太大占磁盘

日志越积越多,磁盘满了。

解决: 设 max_files,自动删除旧日志:

'max_files' => 30,

坑5:502和500搞混

  • 500:PHP代码错误
  • 502:PHP-FPM挂了

区分: 看Nginx错误日志。

总结

记住:

  1. 改 .env 的 APP_DEBUG = true 临时看错误
  2. 生产环境必须改回 false
  3. 日志在 runtime/log/ 目录
  4. 排查完记得清缓存
  5. 500是PHP代码问题,502是FPM问题

排查流程:

开debug → 看具体报错 → 看ThinkPHP日志 → 看Nginx日志 → 解决问题 → 关debug

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:31 

PHP FPM进程过多导致服务器卡顿优化

前言

PHP FPM进程太多,把服务器内存吃满,网站卡顿甚至502?本文讲怎么优化PHP FPM进程数。

一、为什么FPM进程多会卡

PHP FPM每处理一个请求,就开一个进程。

进程开多了:

  • 内存占满
  • CPU切换不过来
  • 服务器卡顿
  • 甚至OOM被杀

二、查看当前进程数

查看PHP FPM进程

ps aux | grep php-fpm

数一下有多少个进程。

看每个进程占多少内存

ps aux | grep php-fpm | awk '{print $6/1024 "MB"}'

算一下总内存

ps aux | grep php-fpm | awk '{sum+=$6} END {print sum/1024 "MB"}'

三、FPM配置文件在哪

CentOS

/etc/php-fpm.d/www.conf

或者:

/etc/php/7.4/fpm/pool.d/www.conf

Ubuntu

/etc/php/7.4/fpm/pool.d/www.conf

四、进程管理模式

PHP FPM有三种进程管理模式:

1. static(静态)

固定进程数,启动就开这么多。

pm = static
pm.max_children = 50

2. dynamic(动态)

默认模式,按需开进程。

pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 35

3. ondemand

没人访问的时候不开进程,来了请求才开。

pm = ondemand
pm.max_children = 50
pm.process_idle_timeout = 10s

五、怎么算合适的进程数

公式

pm.max_children = 可用内存 / 每个进程平均内存

例子

  • 服务器2G内存
  • 系统和其他服务占1G
  • 剩下1G给PHP
  • 每个PHP进程平均占40M
pm.max_children = 1024 / 40 ≈ 25

所以设25个进程差不多。

别设太大

设太大了,内存不够用,就会OOM。

六、推荐配置

小服务器(1G内存)

pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5

中服务器(2-4G内存)

pm = dynamic
pm.max_children = 30
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20

大服务器(8G以上)

pm = dynamic
pm.max_children = 80
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 40

七、其他优化参数

request_terminate_timeout

单个请求最长执行时间,超时就杀掉。

request_terminate_timeout = 30s

slow log

慢日志,看看哪个请求慢。

slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 10s

php_admin_value[memory_limit]

每个PHP进程的内存限制。

php_admin_value[memory_limit] = 128M

八、改完配置重载

systemctl reload php-fpm

或者:

systemctl restart php-fpm

九、验证效果

看进程数

ps aux | grep php-fpm | wc -l

看内存

free -h

看负载

top

十、常见坑

坑1:max_children设太大

比如2G内存设了100个进程,内存直接爆。

解决: 按上面的公式算,别拍脑袋。

坑2:改完配置不生效

忘了重载。

解决:

systemctl reload php-fpm

坑3:进程数一直涨

有慢请求,占着进程不放。

检查:

request_terminate_timeout = 30s

设个超时,超时就杀。

坑4:502 Bad Gateway

进程都忙不过来,Nginx连不上FPM。

解决: 加max_children,或者优化慢请求。

坑5:用了static模式

static固定进程数,不用就浪费内存。

推荐: 用dynamic或ondemand。

十一、排查步骤

第一步:看是不是FPM进程多

ps aux | grep php-fpm | wc -l

第二步:看内存够不够

free -h

第三步:看有没有慢请求

tail -f /var/log/php-fpm/slow.log

第四步:调整配置

改www.conf里的pm.max_children等参数。

第五步:重载验证

systemctl reload php-fpm

总结

记住:

  1. 进程数别设太大,按内存算
  2. 公式:可用内存 / 每个进程平均内存
  3. 推荐用dynamic模式
  4. 设个request_terminate_timeout,防慢请求占着进程
  5. 改完记得reload

推荐配置:

pm = dynamic
pm.max_children = 30
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
request_terminate_timeout = 30s

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-8 08:29 
'); })();