PHP 过滤和验证数据

PHP 过滤和验证数据

用户输入不可信,必须过滤验证,这是Web开发的基本功。

1. 为什么要过滤验证

  • 防止 SQL 注入
  • 防止 XSS 攻击
  • 保证数据格式正确
  • 防止脏数据入库

2. filter_var 函数

PHP 内置过滤器,很好用。

验证邮箱

$email = 'test@example.com';
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
    echo '邮箱格式正确';
} else {
    echo '邮箱格式错误';
}

验证数字

$age = '20abc';
if (filter_var($age, FILTER_VALIDATE_INT)) {
    echo '是整数';
}

验证URL

$url = 'https://example.com';
if (filter_var($url, FILTER_VALIDATE_URL)) {
    echo 'URL格式正确';
}

验证IP

$ip = '192.168.1.1';
if (filter_var($ip, FILTER_VALIDATE_IP)) {
    echo 'IP格式正确';
}

3. filter_input 获取并过滤

从 GET/POST 获取并过滤:

// 获取并过滤GET参数
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);

// 获取并过滤POST参数
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);

4. 过滤输入

过滤HTML标签

$str = '<script>alert(1)</script>你好';
// 去除HTML标签
echo strip_tags($str);

// HTML转义(安全输出)
echo htmlspecialchars($str);

过滤特殊字符

$str = "hello ' world";
// 转义单引号
echo addslashes($str);

5. 常用验证场景

验证手机号

function checkPhone($phone) {
    return preg_match('/^1[3-9]\d{9}$/', $phone);
}

if (checkPhone('13800138000')) {
    echo '手机号正确';
}

验证身份证

function checkIdCard($idcard) {
    return preg_match('/^\d{17}[\dXx]$/', $idcard);
}

验证用户名

// 4-16位,字母数字下划线
function checkUsername($name) {
    return preg_match('/^\w{4,16}$/', $name);
}

6. 安全建议

1)所有外部输入都要验证

  • GET、POST、COOKIE
  • 不要相信任何用户输入

2)用预处理防SQL注入

  • 不要自己拼接SQL
  • 用 PDO 预处理

3)输出时转义

  • 输出到HTML要 htmlspecialchars
  • 防止 XSS 攻击

4)白名单验证

  • 只接受允许的值
  • 不要用黑名单

7. 完整示例

// 接收用户注册信息
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$age = (int)$_POST['age'];

$errors = [];

// 验证用户名
if (!preg_match('/^\w{4,16}$/', $username)) {
    $errors[] = '用户名4-16位字母数字下划线';
}

// 验证邮箱
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errors[] = '邮箱格式错误';
}

// 验证年龄
if ($age < 18 || $age > 100) {
    $errors[] = '年龄必须18-100岁';
}

if (empty($errors)) {
    // 验证通过,入库
    echo '注册成功';
} else {
    print_r($errors);
}

总结

PHP 过滤和验证数据要点:

  • 所有用户输入都不可信
  • 用 filter_var 验证格式
  • 用 filter_input 获取并过滤
  • 输出用 htmlspecialchars 防XSS
  • 用预处理防SQL注入
  • 用正则验证手机号、用户名
  • 白名单验证比黑名单好
  • 不要自己拼接SQL字符串

做Web开发,安全永远第一位。


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

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 

Nginx反向代理配置,把请求转发到后端服务

前言

现在做后端开发,后端服务一般跑在 3000、8080 这种端口上,总不能让用户直接访问 IP:端口 吧?这时候就需要 Nginx 反向代理,把 80 端口的请求转发到后端服务。本文就讲一下 Nginx 反向代理的配置方法。

什么是反向代理

简单说,反向代理就是:

  • 用户访问 Nginx(80 端口)
  • Nginx 把请求转发给后端服务(比如 3000 端口)
  • 后端服务返回结果给 Nginx
  • Nginx 再返回给用户

用户不知道后端服务的存在,只跟 Nginx 打交道。

基本配置

最简单的反向代理

server {
    listen 80;
    server_name www.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

就这么简单,所有访问 www.example.com 的请求都会被转发到 http://127.0.0.1:3000。

检查并重载

nginx -t
systemctl reload nginx

常用配置项

proxy_pass 后端地址

proxy_pass http://127.0.0.1:3000;

后端服务的地址,可以是 IP 加端口,也可以是域名。

传递真实客户端 IP

默认情况下,后端服务看到的是 Nginx 的 IP,不是真实用户的 IP。需要加上几个请求头:

location / {
    proxy_pass http://127.0.0.1:3000;
    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;
}

这样后端服务就能拿到真实用户的 IP 了。

代理 WebSocket

如果后端服务用了 WebSocket,需要额外配置:

location /ws {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

超时设置

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_connect_timeout 60s;
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;
}

如果后端服务响应慢,可以把超时时间调大一点。

按路径分流

不同路径转发到不同后端服务:

server {
    listen 80;
    server_name www.example.com;

    location /api/ {
        proxy_pass http://127.0.0.1:3000;
    }

    location /admin/ {
        proxy_pass http://127.0.0.1:8080;
    }

    location / {
        root /var/www/html;
        index index.html;
    }
}

这样:

  • /api/ 开头的请求 → 3000 端口
  • /admin/ 开头的请求 → 8080 端口
  • 其他请求 → 静态文件

常见坑

坑1:502 Bad Gateway

访问报 502,说明 Nginx 连不上后端服务。

排查:

  1. 看后端服务有没有启动:curl http://127.0.0.1:3000
  2. 看后端服务监听的地址对不对,是不是只监听了 127.0.0.1
  3. 看防火墙有没有挡住

坑2:后端拿不到真实 IP

日志里全是 Nginx 的 IP,不是用户真实 IP。

解决: 加上那几个请求头:

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

坑3:静态资源 404

前端是 Vue/React 这种单页应用,刷新页面就 404。

原因: Nginx 找不到对应的静态文件。

解决: 加个 try_files:

location / {
    root /var/www/html;
    index index.html;
    try_files $uri $uri/ /index.html;
}

所有找不到的路径都返回 index.html,交给前端路由处理。

坑4:HTTPS 代理后端 HTTP

前端是 HTTPS,后端是 HTTP,浏览器报混合内容错误。

解决: 确保 Nginx 和后端之间是 HTTP 没问题,但要让后端知道原始请求是 HTTPS:

proxy_set_header X-Forwarded-Proto $scheme;

总结

Nginx 反向代理的核心就是 proxy_pass,把请求转发到后端服务。常用配置:

配置项 作用
proxy_pass 后端服务地址
proxy_set_header Host 传递域名
proxy_set_header X-Real-IP 传递真实 IP
proxy_set_header X-Forwarded-For 传递完整 IP 链
proxy_read_timeout 读取超时时间

反向代理是 Nginx 最常用的功能之一,做 Web 开发必备。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 07:55 

PHP 性能优化:从代码到部署的完整指南

前言

PHP 性能优化是每个 PHP 开发者都绕不开的话题。很多人觉得 PHP 性能差,但实际上通过合理的优化,PHP 应用完全可以达到很高的性能水平。本文从代码层面到部署层面,带你完整梳理 PHP 性能优化的思路。

一、代码层面优化

1. 避免在循环中做数据库查询

// 慢:循环中查询数据库
foreach ($users as $user) {
    $order = $db->query("SELECT * FROM orders WHERE user_id = {$user['id']}");
}

// 快:一次性查询,然后用数组映射
$userIds = array_column($users, 'id');
$orders = $db->query("SELECT * FROM orders WHERE user_id IN (" . implode(',', $userIds) . ")")->fetchAll();
$orderMap = array_column($orders, null, 'user_id');

2. 用生成器处理大数据

// 慢:一次性加载所有数据到内存
$users = $db->query("SELECT * FROM big_table")->fetchAll();

// 快:用生成器逐行处理
function getUsers() {
    $stmt = $pdo->query("SELECT * FROM big_table");
    while ($row = $stmt->fetch()) {
        yield $row;
    }
}

3. 字符串拼接 vs implode

// 慢:频繁拼接字符串
$str = '';
foreach ($items as $item) {
    $str .= $item . ',';
}

// 快:用数组收集最后拼接
$parts = [];
foreach ($items as $item) {
    $parts[] = $item;
}
$str = implode(',', $parts);

二、PHP 配置优化

1. OPcache 必开

OPcache 是 PHP 性能提升最大的一个扩展,没有之一。

; php.ini
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1

开启 OPcache 后,PHP 脚本会被编译成字节码缓存起来,避免每次请求都重新编译。性能提升通常在 2-5 倍。

2. 合理设置内存限制

memory_limit = 256M

不要设置太大,也不要太小。根据你的应用实际情况调整。

三、数据库优化

1. 开启慢查询日志

slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1

找出慢查询,然后针对性优化。

2. 使用持久化连接要谨慎

// 不一定更快,可能反而更慢
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass', [
    PDO::ATTR_PERSISTENT => true
]);

持久化连接在高并发场景下可能导致连接耗尽,要根据实际情况测试。

四、部署层面优化

1. 使用 PHP-FPM 优化

; php-fpm/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30

根据服务器内存调整 max_children,每个 PHP-FPM 进程大约占用 20-40MB 内存。

2. Nginx 开启 gzip

gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;

3. 静态文件 CDN 加速

把图片、CSS、JS 等静态资源放到 CDN 上,减轻服务器压力。

五、性能监控工具

  • Xdebug:代码性能分析,找出瓶颈函数
  • Blackfire:更专业的性能分析工具
  • New Relic:APM 应用性能监控
  • ab / wrk:压力测试工具

总结

PHP 性能优化的核心思路:

  1. 先找出瓶颈在哪里,不要盲目优化
  2. 代码层面:减少数据库查询、减少内存占用
  3. 配置层面:OPcache 必开
  4. 部署层面:PHP-FPM、Nginx、数据库都要调优
  5. 用工具量化优化效果

性能优化是一个持续迭代的过程,不要指望一次性优化到位。每次做一点,慢慢积累。


emer 发布于  2026-10-4 09:55 

PHP 8.3 新特性详解:类型类获取、动态常量获取和更多实用功能

前言

PHP 8.3 于 2023 年 11 月正式发布,作为 PHP 8.x 系列的第四个版本,它带来了一系列实用的语法增强和性能优化。本文将深入介绍 PHP 8.3 中最值得关注的几个新特性。

1. 类型类获取(Typed class constants)

在 PHP 8.3 之前,类常量无法声明类型。从 PHP 8.3 开始,类常量可以指定类型,这有助于提高代码的类型安全性。

class Foo
{
    const string BAR = 'bar';
    const int MAX_COUNT = 100;
}

声明了类型的常量必须与声明的类型匹配,否则会抛出 TypeError。支持的类型包括:标量类型、array、object、static、self、parent、类名/接口名以及 iterable。

2. 动态类常量获取

PHP 8.3 新增了 constant() 函数的增强,允许通过表达式动态获取类常量:

class Status
{
    const DRAFT = 'draft';
    const PUBLISHED = 'published';
    const ARCHIVED = 'archived';
}

$status = 'PUBLISHED';
echo constant(Status::class . "::$status"); // 输出: published

这在需要根据变量动态选择常量时非常有用,避免了大量的 switch 或 match 分支。

3. #[\Override] 注解

PHP 8.3 引入了 #[\Override] 注解,用于显式标记一个方法是重写父类的方法:

abstract class Controller
{
    abstract public function handle(): void;
}

class UserController extends Controller
{
    #[\Override]
    public function handle(): void
    {
        echo "Handling user request...";
    }
}

如果使用了 #[\Override] 注解,但方法实际上并没有重写任何父类方法(比如方法名拼错了),PHP 会在编译时报错。这在大型项目中非常有价值,可以防止因为方法名不一致导致的隐蔽 bug。

4. Readonly 深拷贝

PHP 8.3 改进了 readonly 属性的处理。在 PHP 8.2 中,readonly 属性不能通过克隆来修改。PHP 8.3 允许在克隆时修改 readonly 属性:

class Point
{
    public function __construct(
        public readonly int $x,
        public readonly int $y
    ) {}
}

$p1 = new Point(10, 20);
$p2 = clone $p1;
$p2->x = 30; // PHP 8.3 中允许,PHP 8.2 会报错

这个特性在值对象模式中特别有用,可以创建修改后的副本而不影响原始对象。

5. 其他实用改进

json_validate() 函数

PHP 8.3 新增了 json_validate() 函数,用于验证 JSON 字符串是否有效,而不需要解码:

$json = '{"name": "Alice", "age": 30}';
if (json_validate($json)) {
    echo "Valid JSON";
}

这比 json_decode() 更高效,因为它不需要分配内存来存储解码后的结果。

随机浮点数

PHP 8.3 的 Random\Randomizer 类新增了 getFloat() 方法:

$r = new Random\Randomizer();
$float = $r->getFloat(0, 10, \Random\IntervalBoundary::ClosedOpen);

总结

PHP 8.3 虽然不是一个大版本更新,但它带来的每一个新特性都直击开发者的日常痛点。类型类常量和 #[\Override] 注解增强了代码的可维护性,动态常量获取简化了代码逻辑,readonly 深拷贝则让值对象模式更加实用。

建议正在使用 PHP 8.x 的项目尽快升级到 8.3,既能获得这些新特性的好处,也能享受性能上的小幅提升。


emer 发布于  2026-10-4 09:48 
'); })();