PHP数组去重排序

PHP数组去重排序

引言

数组是PHP最常用的数据结构,去重和排序是开发中反复出现的基础需求。掌握内置函数的正确用法,比自己写循环更高效,也更不容易出错。

一维数组去重

$arr = [3, 1, 2, 3, 1, 5];
$unique = array_unique($arr);
//结果保留首次出现的值,键名不变

注意array_unique默认按字符串比较,去重后键名可能不连续,需要重新索引:

$unique = array_values(array_unique($arr));

关联数组按字段去重

$users = [
    ['id'=>1,'name'=>'张三'],
    ['id'=>2,'name'=>'李四'],
    ['id'=>1,'name'=>'张三副本'],
];
$seen = [];
$unique = array_filter($users, function($item) use (&$seen) {
    if (in_array($item['id'], $seen)) return false;
    $seen[] = $item['id'];
    return true;
});

排序函数区分

  • sort():值升序,重置键名;
  • rsort():值降序;
  • asort():值升序,保留键名;
  • ksort():按键名升序;
  • krsort():按键名降序。
$score = ['张三'=>88,'王五'=>95,'李四'=>76];
arsort($score); //按分数降序,保留姓名键名
print_r($score);

自定义排序

$users = [['age'=>20],['age'=>35],['age'=>28]];
usort($users, function($a, $b) {
    return $a['age'] <=> $b['age']; //太空船运算符比较
});

注意事项

  • array_unique对多维数组不能直接按字段去重;
  • sort系列会改变原数组,返回bool;
  • 中文排序建议用Collator或mb方式;
  • 去重后及时array_values重置索引,避免后续遍历异常。

总结

一维去重用array_unique加array_values,多维按字段用array_filter配合标记数组。排序先想清是否保留键名,普通值排序用sort/rsort,关联数组用asort/ksort,复杂规则用usort自定义比较。


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

Go语言channel缓冲与阻塞原理详解

前言

channel 是 Go 语言 goroutine 之间通信的核心机制,很多新手写并发程序时会遇到 goroutine 阻塞、死锁、数据写不进去等问题,根源都是没搞懂无缓冲通道和有缓冲通道的阻塞规则。本篇结合代码讲清 channel 的缓冲模型与阻塞时机。

一、无缓冲通道(unbuffered channel)

ch := make(chan int)
go func() {
    ch <- 100 // 发送
}()
v := <-ch     // 接收

无缓冲通道容量为 0,发送方必须等到有接收方准备好才会完成发送,属于"同步握手"。如果只发不收,发送 goroutine 会永久阻塞。

二、有缓冲通道(buffered channel)

ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
// 再发第4个会阻塞,直到有人接收腾出位置

容量为 3 时,前 3 个元素先进缓冲区,发送方不会阻塞;缓冲区满了之后,发送方才会等待。接收方在缓冲区为空时阻塞。

三、阻塞与关闭的常见规则

  1. 向已关闭的 channel 发送数据会 panic;
  2. 从已关闭的 channel 接收会得到零值,ok 标识为 false;
  3. 关闭后再次关闭会 panic;
  4. 遍历 channel 用 range,channel 关闭后 range 自动退出。

四、常见踩坑

  • 忘记接收导致发送 goroutine 泄漏,程序卡住;
  • 缓冲容量设置过大失去解耦意义,过小又频繁阻塞;
  • 主 goroutine 提前退出,子协程数据没发完就被回收。

总结

无缓冲通道做同步通信,有缓冲通道做异步削峰。理解"缓冲区满则发送阻塞、空则接收阻塞"这两条规则,就能避开大部分 channel 死锁问题。

遇到问题加QQ23979811 协助处理


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

Git撤销提交操作

Git撤销提交操作

引言

提交错了、提交了不该上传的文件、commit message写错,都是日常开发常见情况。Git提供多种撤销方式,关键是分清:还没push的本地提交和已经推到远程的提交,处理方式完全不同。

场景一:改错还没add

工作区改动想放弃:

git checkout -- 文件名      #旧写法
git restore 文件名          #新写法

整个工作区全部还原:git restore .

场景二:add了但没commit

把文件撤出暂存区,保留改动:

git reset HEAD 文件名
git restore --staged 文件名

场景三:commit了但没push

改最后一次提交信息:

git commit --amend -m "新的提交说明"

撤销最近一次提交,改动留在工作区:

git reset --soft HEAD^    #撤销提交,代码保留在暂存区
git reset --mixed HEAD^   #默认,撤销提交,代码退回工作区
git reset --hard HEAD^    #撤销提交并丢弃改动,慎用

场景四:已经push到远程

本地reset后强推会改写历史,团队共用分支风险大,推荐用反向提交:

git revert 提交ID

revert会生成一个新提交,把那次改动反向抵消,不改写历史,安全协作。

注意事项

  • --hard会丢代码,操作前先备份或开分支;
  • 已push到共享分支不要随意reset强推;
  • amend只用于未推送的本地提交;
  • 查看历史用git log --oneline定位提交ID。

总结

未add用restore,add未commit用reset HEAD撤出,本地commit用reset(soft/mixed保留代码,hard丢弃),已push用revert新增反向提交。记住本地和远程的区别,避免改写团队历史。


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

MySQL事务ACID原理

MySQL事务ACID原理

引言

转账、下单、扣库存这类多步操作必须保证要么全成功要么全回滚,靠的就是数据库事务。ACID是事务四个特性的缩写,理解它才能用对事务、排查并发问题。

四个特性

A 原子性 Atomicity
事务内操作不可分割,全部成功提交,失败则整体回滚到事务前状态。InnoDB靠undo log回滚日志实现,记录修改前旧值,出错反向撤销。

C 一致性 Consistency
事务前后数据库从一个合法状态转到另一个合法状态,约束(主键、外键、唯一、余额非负)始终不被破坏。一致性是最终目标,靠ABD共同保障。

I 隔离性 Isolation
并发事务之间互不干扰,隔离级别从低到高:读未提交、读提交、可重复读(InnoDB默认)、串行化,用锁和MVCC实现。

D 持久性 Durability
事务一旦提交,修改永久落盘,宕机也不丢。靠redo log重做日志,先写日志再刷盘,崩溃后重放恢复。

事务用法示例

START TRANSACTION;
UPDATE account SET money = money - 100 WHERE id = 1;
UPDATE account SET money = money + 100 WHERE id = 2;
COMMIT;
-- 出错则 ROLLBACK;

PHP中配合PDO:

$pdo->beginTransaction();
try {
    $pdo->exec("UPDATE account SET money=money-100 WHERE id=1");
    $pdo->exec("UPDATE account SET money=money+100 WHERE id=2");
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
}

注意事项

  • 事务不要包太长,避免长时间持锁;
  • 只对必须一致的操作开事务;
  • 一致性靠应用校验和数据库约束共同保证;
  • 隔离级别越高并发越低,按业务权衡。

总结

ACID:原子性靠undo log、持久性靠redo log、隔离性靠锁与MVCC、一致性是目标。转账扣库存等关键链路用事务包裹,注意控制事务长度和隔离级别。


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

Nginx开启Gzip压缩

Nginx开启Gzip压缩

引言

HTML、CSS、JS、JSON这类文本资源直接传输体积大,首屏慢。Nginx开启Gzip后,服务端先压缩再发送,浏览器解压,通常文本体积能减少60%以上,是零成本提升访问速度的手段。

配置示例

在http块或server块加入:

gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";

参数说明

  • gzip on:开关;
  • gzip_min_length:小于1k不压缩,避免小文件压缩得不偿失;
  • gzip_comp_level:压缩等级1-9,6比较均衡,太高CPU占用大;
  • gzip_types:指定压缩的MIME类型,text/html默认自带;
  • gzip_vary on:响应加Vary:Accept-Encoding,避免缓存问题;
  • gzip_disable:兼容老旧IE浏览器。

生效验证

改完执行nginx -t检查语法,再reload。浏览器F12网络面板看响应头Content-Encoding:gzip即生效。

注意事项

  • 图片、视频、PDF本身已压缩,不要再gzip,浪费CPU;
  • 压缩等级不要盲目拉满,大文件压缩吃CPU;
  • 静态资源可在CDN层提前压缩;
  • 配合Brotli现代压缩效果更好,需Nginx额外模块支持。

总结

Gzip是文本类资源提速标配,开启后配置类型、大小阈值、合理压缩等级,reload生效,用响应头验证。不压缩图片视频,平衡传输体积与服务器CPU。


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

Docker容器日志查看

Docker容器日志查看

引言

容器启动报错、服务运行异常时,不能进容器盲目找文件,第一时间用docker logs查看标准输出日志。应用把日志打到stdout/stderr,Docker统一收集,是容器排错最常用入口。

基础查看命令

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

#实时跟踪最新日志,类似tail -f
docker logs -f 容器名

#只看最近100行
docker logs --tail 100 容器名

#查看某时间之后日志
docker logs --since 30m 容器名
docker logs --since 2026-10-18T07:00:00 容器名

#带时间戳
docker logs -t 容器名

常用组合

#实时跟踪并显示时间戳、最近50行
docker logs -f -t --tail 50 myapp

排查重启循环、接口报错时,先tail看最后报错,再结合时间过滤。

日志原理

Docker默认把容器标准输出保存到本地json-file驱动,存在宿主机/var/lib/docker/containers目录。应用要把日志输出到控制台,而不是只写容器内文件,才能被docker logs抓到。

日志注意事项

  • 默认json-file不限制大小,长时间运行会占满磁盘,生产配置日志轮转:
    {"log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"3"}}
  • 需要长期分析的日志,接入ELK等集中日志系统;
  • 容器删除后默认日志随之丢失;
  • 应用内部业务日志建议挂载到宿主机目录持久化。

总结

docker logs是容器排错第一命令,-f跟踪、--tail取尾部、--since按时间过滤。生产配置大小轮转避免磁盘写满,业务重要日志持久化或集中采集。


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

JS跨域CORS方案

JS跨域CORS方案

引言

浏览器同源策略要求协议、域名、端口完全一致,前端页面访问另一个域名的接口就会被拦截报跨域错误。CORS是目前最标准、主流的跨域解决方式,由后端配合响应头放行。

什么是同源策略

三个任意一项不同都算跨域:

  • 协议不同(http/https);
  • 域名不同;
  • 端口不同。

CORS后端响应头(PHP)

<?php
header('Access-Control-Allow-Origin: https://www.example.com');
header('Access-Control-Allow-Methods: GET,POST,OPTIONS');
header('Access-Control-Allow-Headers: Content-Type,Authorization');
header('Access-Control-Allow-Credentials: true');

携带cookie时Allow-Origin不能写*,必须指定具体域名,且前端请求withCredentials为true。

预检请求OPTIONS

POST JSON等复杂请求,浏览器会先发OPTIONS预检,后端要直接放行OPTIONS,返回204,否则正式请求发不出去。

其他常见跨域方式对比

  • JSONP:只支持GET,老旧方案;
  • Nginx反向代理:前端请求同源nginx,由nginx转发接口,无浏览器跨域;
  • 开发环境代理:Vite、Webpack devServer proxy临时解决。

注意事项

  • 生产不要用*放开所有来源;
  • 区分简单请求和预检请求;
  • 配合cookie要带Credentials并指定来源;
  • 接口服务端做好来源校验。

总结

CORS是前后端分离首选方案,核心是后端设置允许来源、方法、响应头。复杂请求先处理OPTIONS预检,带cookie时指定具体域名,安全可控,比JSONP和代理更规范。


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

PHP错误日志排查

PHP错误日志排查

引言

线上页面白屏、500错误,前台看不到具体原因,靠猜很难定位。正确做法是开启并查看PHP错误日志,从日志里拿到文件、行号、错误类型,再针对性修复。

先确认日志位置

在phpinfo页面搜索error_log,或查看php.ini配置:

#查看php.ini错误日志配置
php -i | grep error_log
#常见位置
tail -f /var/log/php-fpm/www-error.log
tail -f /var/log/httpd/php_errors.log

宝塔、面板环境一般在日志菜单里可直接打开PHP错误日志。

开发环境临时显示错误

<?php
ini_set('display_errors',1);
ini_set('error_reporting',E_ALL);
error_reporting(E_ALL);

生产环境不要直接display_errors到页面,统一写日志。

日志格式解读

一条错误日志通常包含:错误级别、报错文件、行号、错误信息。

[12-Oct-2026 07:00:00] PHP Fatal error:
Call to undefined function xxx() in /www/app/index.php on line 18

含义:第18行调用了不存在的函数,检查函数是否引入、命名是否拼错。

常见错误类型

  • Fatal error:致命错误,程序中断,如调用不存在的类、函数;
  • Warning:警告不中断,如参数类型不对;
  • Notice:提示,未定义变量;
  • Deprecated:废弃语法,未来版本不兼容。

排查流程

  1. 复现问题,记录访问时间;
  2. 按时间过滤日志,定位对应报错;
  3. 打开报错文件对应行号;
  4. 修复后清空日志再验证。

总结

PHP排错第一步永远是看错误日志,而不是反复刷新页面。开发环境显示错误,生产环境写日志,按时间和行号快速定位,养成记录日志的习惯。


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

Linux防火墙配置

Linux防火墙配置

引言

云服务器安全不能只靠系统防火墙,开通端口是上线必经步骤。CentOS 7以上默认使用firewalld,很多新手忘了放行端口,导致网站、SSH、数据库从外部连不上。

firewalld常用命令

#查看防火墙状态
firewall-cmd --state

#查看已放行端口
firewall-cmd --list-ports

#永久放行80、443端口
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp

#重新加载生效
firewall-cmd --reload

#移除某个端口
firewall-cmd --permanent --remove-port=3306/tcp

区域概念

firewalld分public、internal等zone,默认public。--permanent写入永久规则,不加只对当前会话生效,重启失效。改完必须reload。

常用放行场景

  • Web服务:80、443;
  • SSH远程:22(建议改非默认端口再放行);
  • 不要对公网开放3306、6379、27017等数据库端口;
  • 只允许指定IP访问管理端口更安全:
    firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=1.2.3.4 port port=22 protocol=tcp accept'

注意事项

  • 云服务器除了系统防火墙,还要在云厂商控制台安全组同步放行;
  • 改SSH端口前先确认新端口放行,避免把自己挡在门外;
  • 防火墙规则最小化,只开放必要端口。

总结

firewalld核心:查看列表、永久加端口、reload生效。记住系统防火墙和云安全组两层都要放行,数据库端口绝不暴露公网,管理端口限制来源IP。


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

Composer国内镜像配置

Composer国内镜像配置

引言

PHP项目用Composer安装依赖时,默认从packagist.org国外源拉取,国内网络经常超时、下载极慢。配置国内镜像源后,安装速度能提升数倍,是PHP开发环境必备操作。

全局配置方法

终端执行下面命令,把全局镜像换成国内源:

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

执行后写入用户全局配置,所有项目生效。

单个项目配置

只给当前项目换源,在项目目录执行:

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

常见国内镜像

验证与切换

#查看当前源
composer config -g repos

#回到官方源
composer config -g --unset repos.packagist

注意事项

  • 镜像只加速包下载,不影响代码本身;
  • 生产环境尽量锁定composer.lock版本;
  • 个别新包国内镜像同步延迟,临时可切官方源;
  • 配置后先清缓存再安装:composer clearcache。

总结

Composer换源是国内PHP开发第一步,全局配置阿里云镜像即可解决大部分超时问题。安装依赖前先清缓存,项目锁定lock保证团队版本一致。


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

MySQL分页查询优化

MySQL分页查询优化

引言

后台列表、文章分页是最常见的SQL场景。数据量小的时候LIMIT偏移量查询很快,数据到几十万、上百万行,深分页会明显变慢,甚至拖垮数据库。

为什么深分页慢

LIMIT 100000,20 会先扫描前100000行再丢弃,只返回20行,扫描行数越多IO开销越大。

优化方案一:子查询定位主键

先查出起始主键,再用主键关联取数据,避免回表扫描:

SELECT a.* FROM article a
INNER JOIN (
  SELECT id FROM article ORDER BY id LIMIT 100000,20
) b ON a.id=b.id;

子查询走覆盖索引,速度明显提升。

优化方案二:游标/记住上一页最大ID

SELECT * FROM article
WHERE id > 100000
ORDER BY id LIMIT 20;

每次只查比上一页最后一条ID大的记录,不走大偏移,适合无限滚动。

其他优化点

  • 分页排序字段必须建索引,避免filesort;
  • 只查询需要的字段,不用SELECT *;
  • 禁止前端跳转到任意深页,后台限制最大页码;
  • 超大表考虑分表、搜索引擎承接列表。

总结

深分页慢的根源是大偏移扫描。优先用主键游标翻页,配合覆盖索引;传统offset分页用子查询优化。列表数据量极大时,交给ES等搜索方案更合理。


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

PHP文件上传校验

PHP文件上传校验

引言

文件上传是网站高频功能,也是最容易出安全问题的地方。只判断后缀名,黑客可以把木马改名为图片绕过,上传到服务器执行。上传必须做多层校验。

必须校验的内容

  1. 检查上传错误码,确认文件确实上传成功;
  2. 限制文件大小,防止超大文件占满磁盘;
  3. 白名单校验扩展名,只允许jpg/png等指定类型;
  4. 读取文件真实MIME类型,不能只信前端传的type;
  5. 重新生成随机文件名,避免原文件名覆盖或路径穿越;
  6. 上传目录禁止执行PHP脚本。

基础校验示例

<?php
$allow = ['jpg','png','gif'];
$ext = strtolower(pathinfo($_FILES['file']['name'],PATHINFO_EXTENSION));
if(!in_array($ext,$allow)){
    exit('类型不允许');
}
if($_FILES['file']['size'] > 2*1024*1024){
    exit('文件过大');
}
//获取真实类型
$info = getimagesize($_FILES['file']['tmp_name']);
if(!$info) exit('不是合法图片');
//随机重命名
$newName = uniqid().'.'.$ext;
move_uploaded_file($_FILES['file']['tmp_name'],'./upload/'.$newName);

服务端加固

  • Nginx/Apache配置上传目录禁止解析php;
  • 图片上传后重新压缩生成,破坏木马脚本;
  • 关闭上传目录的执行权限;
  • 定期扫描上传目录文件。

总结

文件上传安全核心:不信任前端、白名单、校验真实类型、随机改名、目录隔离。多层校验才能防止webshell上传,保护服务器安全。


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

Git分支管理实践

Git分支管理实践

引言

多人协作开发如果所有人直接往master主分支提交,代码很容易混乱、出现问题无法回滚。一套清晰的分支命名和合并规则,是团队开发协作的基础。

常见分支模型

  • master/main:稳定生产分支,只接受合并,不直接开发;
  • develop:日常集成开发分支;
  • feature/xxx:单个功能开发分支,从develop拉出;
  • hotfix/xxx:线上紧急bug修复分支,从master拉出;
  • release/x.x:版本发布前测试分支。

常用命令

# 新建并切换功能分支
git checkout -b feature/login

# 提交代码
git add .
git commit -m "新增登录接口"

# 推送到远程
git push origin feature/login

开发流程

  1. 从develop拉取新功能分支;
  2. 本地开发提交,定期同步develop最新代码;
  3. 测试通过后发起合并请求(MR/PR);
  4. 代码评审通过合并到develop;
  5. 上线前拉出release分支测试,最终合并master打tag。

注意事项

  • 功能分支命名要语义化,带上开发人或需求编号;
  • 每次提交信息写清楚改了什么;
  • 不要在长期分支堆积大量提交,频繁同步主干避免冲突;
  • hotfix修复完成后要同步回develop,防止下次版本再次出现bug。

总结

Git分支模型不追求复杂,适合团队规模即可。核心原则:主干保持稳定,功能独立分支,小步提交,评审合并,线上问题走hotfix单独修复。


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

Redis哈希结构场景

Redis哈希结构场景

引言

Redis Hash(哈希)适合存储对象类数据,类似数据库单行记录。相比把对象序列化为JSON字符串存储,Hash可以单独读写某个字段,不用整体读取修改,适合用户资料、商品基础信息这类场景。

Hash基础命令

# 设置单个字段
HSET user:100 name "test" age 20

# 获取单个字段
HGET user:100 name

# 获取全部字段
HGETALL user:100

# 删除字段
HDEL user:100 age

PHP操作示例

<?php
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$key = 'user:100';
$redis->hMSet($key,[
    'name'=>'admin',
    'phone'=>'13800138000'
]);
$info = $redis->hGetAll($key);
print_r($info);

适用与不适用场景

✅ 适合:用户资料、商品基础属性,需要局部更新字段

❌ 不适合:字段数量超大、海量对象;大量独立统计计数,推荐单独key

注意事项

Redis Hash有底层ziplist编码,字段少、值小的时候内存占用极低。当字段数量或者value超过阈值,会转为hash表,内存占用上升。不要在单个hash里面存放成千上万的字段。

总结

Hash是Redis高频数据结构,适合存储结构化小对象,可以局部读写字段,避免JSON字符串每次全量读写。根据业务字段数量合理选择,不要滥用。


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

Docker多阶段镜像构建

Docker多阶段镜像构建

引言

直接打包项目代码、编译工具到镜像,镜像体积巨大,部署传输慢。Docker多阶段构建可以在第一阶段编译代码,第二阶段只复制编译后的成品,剔除编译器、源码,大幅度减小镜像大小。

原理

一个Dockerfile可以写多个FROM,每一个FROM代表一个阶段。阶段之间可以复制文件,最终镜像只保留最后一个阶段内容,前面阶段的编译环境不会打包进最终镜像。

示例Dockerfile(PHP项目)

#阶段1:构建
FROM php:8.2-cli AS builder
WORKDIR /app
COPY . .
RUN composer install --no-dev

#阶段2:运行环境
FROM php:8.2-cli-alpine
WORKDIR /app
COPY --from=builder /app .
CMD ["php","index.php"]

最终镜像只有php运行环境加业务代码,没有composer、源码缓存文件。

优势

  • 镜像体积大幅缩小,传输更快;
  • 减少攻击面,镜像内没有编译工具,安全性更高;
  • 只维护单个Dockerfile,构建流程简单。

注意事项

  • 多阶段命名便于阅读;
  • 不要复制不需要的文件,.dockerignore忽略日志、缓存;
  • 区分开发镜像与生产镜像,生产镜像精简。

总结

多阶段构建是容器化部署最佳实践,适合PHP、Go、前端打包等场景,生产环境镜像尽量使用多阶段构建,减小体积提升部署效率。


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

Web XSS攻击分类

Web XSS攻击分类

引言

XSS跨站脚本是最常见的Web漏洞,黑客注入JS代码,在用户浏览器执行,盗取cookie、劫持页面。建站开发必须了解XSS三种类型,针对性做好防护。

XSS三大类型

  1. 存储型XSS:代码存入数据库,所有访问页面的用户都会触发,危害最大。例如评论区没有过滤,提交恶意js存入数据库。
  2. 反射型XSS:恶意代码放在URL参数,用户点击链接,页面直接输出参数触发,不会存入数据库。
  3. DOM型XSS:前端JS直接读取URL参数修改DOM,不经过后端服务,后端日志看不到攻击载荷。

基础防护方案

  • 后端输出页面使用htmlspecialchars转义;
  • 前端渲染数据不要直接innerHtml,优先textContent;
  • 设置Cookie HttpOnly,防止JS读取cookie;
  • 配置CSP内容安全策略。

简单示例漏洞代码

<?php
//危险代码,直接输出参数,存在反射XSS
echo $_GET['name'];
//安全写法
echo htmlspecialchars($_GET['name'],ENT_QUOTES);
?>

总结

XSS核心成因:把外部可控数据当成页面代码执行。存储型风险最高,DOM型容易被忽略。前后端配合做输出转义、CSP、HttpOnly Cookie,多层防护抵御XSS。


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

MySQL慢查询定位

MySQL慢查询定位

引言

网站访问缓慢很多根源是执行时间过长的SQL。开启MySQL慢查询日志,可以捕获执行超时SQL,找到需要优化的语句,是数据库性能排查的核心手段。

慢查询基础配置

慢查询日志会记录执行时长超过指定阈值的SQL语句。

-- 查看慢查询是否开启
SHOW VARIABLES LIKE 'slow_query_log';

-- 设置阈值,单位秒,超过1秒就记录
SET GLOBAL long_query_time =1;

常用分析工具

  1. mysqldumpslow:mysql自带工具,统计慢日志
    mysqldumpslow -s t /var/lib/mysql/slow.log
  2. pt-query-digest:percona工具,分析效果更强,适合大量日志。

优化排查流程

  1. 开启慢查询,收集慢SQL;
  2. 使用explain查看执行计划,判断是否缺少索引;
  3. 优化where条件,添加合适索引,改写SQL逻辑;
  4. 优化完成后,持续观察慢日志确认效果。

示例

EXPLAIN SELECT * FROM article WHERE title like '%PHP%';

看type字段,ALL代表全表扫描,是需要优化的信号。

总结

慢查询日志用来抓取低效SQL,但是开启会少量增加服务器IO压力,生产环境可以按需开启。定位到慢SQL后优先检查索引,再调整SQL写法,解决数据库卡顿问题。


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

PHP请求参数过滤

PHP请求参数过滤

引言

Web接口接收GET、POST参数,如果不做过滤校验,容易出现SQL注入、XSS、业务逻辑漏洞。PHP开发接口时,参数过滤是安全第一道防线,所有外部输入都不可信。

基础过滤思路

  1. 类型强制转换:数字参数直接转为int;
  2. 长度限制,防止传入超大字符串;
  3. 正则匹配手机号、邮箱等固定格式;
  4. 输出到页面时做html转义,防止XSS;
  5. 入库前转义或者使用PDO预编译防止注入。

代码示例

<?php
// 获取GET参数
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$name = trim($_GET['name'] ?? '');

// 长度校验
if(mb_strlen($name)>50){
    exit("参数过长");
}

// 输出到页面,防XSS
echo htmlspecialchars($name,ENT_QUOTES);

框架层面最佳实践

TP6、Laravel等框架自带验证器,推荐优先使用框架验证,不用手写大量if判断。

// ThinkPHP6验证示例
$validate = new \think\Validate([
    'id'=>'require|integer',
    'name'=>'require|max:50'
]);

避坑点

不要简单依靠addslashes防御注入,推荐PDO预处理语句;参数过滤不要只在前端校验,后端必须二次校验,前端校验可被绕过。

总结

任何外部传入的数据都要校验,区分类型校验、长度校验、格式校验、输出转义四层防护,降低Web安全漏洞风险,是PHP项目开发的基础规范。


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

Linux查看系统负载

Linux查看系统负载

引言

服务器CPU、内存压力过大,会造成网站接口响应缓慢、502报错。运维排查服务器性能问题第一步就是查看系统负载,了解服务器当前资源压力,及时扩容或者优化程序。

负载概念

uptime输出的三个数字:1分钟、5分钟、15分钟平均负载。代表等待CPU处理的任务数量。

多核CPU参考标准:4核服务器,负载接近4说明CPU打满;超过代表任务排队,系统过载。

常用命令

# 快速查看负载
uptime

# 实时监控
top

# 更轻量化监控
htop

# 查看CPU核心数量
nproc

top命令内可以按1查看每个核心负载;P按CPU使用率排序,快速找到耗资源进程。

排查思路

  1. 负载高,CPU使用率高:程序代码循环、大量计算,定位进程优化代码;
  2. 负载高,CPU使用率低:大量IO等待(磁盘读写慢、数据库阻塞),查看磁盘io iostat。

实操示例

# 安装iostat
yum install -y sysstat

# 持续查看io状态
iostat -x 1

总结

平均负载不只代表CPU,还包含IO等待。看到高负载不能直接判定CPU瓶颈,结合top、iostat综合定位,区分是计算密集还是IO密集导致服务器卡顿。


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

Nginx防盗链原理

Nginx防盗链原理

引言

网站图片、视频、静态资源经常被其他站点直接引用,消耗服务器流量与带宽。Nginx防盗链通过校验请求来源Referer,阻止第三方网站直接嵌入你的资源链接,降低服务器流量损耗。

原理说明

浏览器访问页面加载图片时,请求头会携带Referer,代表来源页面地址。Nginx读取该请求头,判断域名是否属于本站,非信任域名直接返回403拒绝访问。

局限:Referer可被客户端伪造,防盗链属于基础防护,不是100%安全,重要资源建议搭配token鉴权。

Nginx配置示例

location ~* \.(jpg|png|gif|mp4)$ {
    valid_referers none blocked *.0454.wang;
    if ($invalid_referer) {
        return 403;
    }
}

配置解释:

  • valid_referers配置允许的来源;
  • none允许直接浏览器地址栏访问资源;
  • 匹配不到白名单,$invalid_referer生效,返回403。

进阶优化

  • 重要资源可使用url签名,生成带时效token的资源地址;
  • CDN环境,需要同步在CDN配置防盗链;
  • 部分爬虫、APP请求不带Referer,按需调整规则。

总结

Nginx Referer防盗链是低成本的静态资源保护手段,适合图片博客、素材站点。适合作为第一层防护,高安全场景需要结合URL签名二次校验。


emer 发布于  2026-10-18 07:12 
'); })();