PHP合并数组方法
PHP合并数组方法
引言
合并两个或多个数组是日常开发高频操作。索引数组用array_push或加号,关联数组用array_merge,+号和array_merge在键冲突时行为完全不同,搞混会踩坑。
array_merge 合并
$a = [1, 2];
$b = [3, 4];
$c = array_merge($a, $b);
// [1, 2, 3, 4]
索引数组会重排数字键,关联数组同名键后者覆盖前者。
加号 + 保留原键
$a = ['a' => 1, 'b' => 2];
$b = ['b' => 99, 'c' => 3];
$c = $a + $b;
// ['a'=>1,'b'=>2,'c'=>3] 左侧优先
+号左边数组的键优先,同键不覆盖,保留左侧值。
array_merge vs + 的区别
$a = ['a' => 1];
$b = ['a' => 99];
array_merge($a, $b); // ['a'=>99] 后者覆盖
$a + $b; // ['a'=>1] 左侧保留
关联数组合并要谁覆盖谁,选对函数很关键。
数组合并并去重
$all = array_unique(array_merge($a, $b));
合并后去重,适合标签、权限码这类集合。
追加元素到数组
$list[] = 'new';
array_push($list, 'a', 'b');
单元素直接[]追加最简洁,多元素用array_push。
注意事项
- array_merge会重置数字索引,+号保留原数字键;
- 关联数组同名键,array_merge后者覆盖,+号左侧优先;
- 合并大数组注意内存,别在循环里反复merge;
- array_merge递归合并多维要用array_replace_recursive;
- 空数组合并不会报错;
- 合并后记得检查是否都是数组类型再合并。
总结
array_merge合并数组,数字键重排、关联键后者覆盖;+号运算符左侧优先不覆盖。合并去重用array_unique包一层,追加元素直接[]最简洁。注意两者在同名键上的覆盖方向完全相反,多维递归合并用array_replace_recursive,大数组合并注意内存别在循环里反复操作。
PHP获取文件扩展名
PHP获取文件扩展名
引言
文件上传、附件处理、图片裁剪时都要先拿到后缀判断类型。别用explode切字符串硬切,pathinfo是PHP专门解析路径的函数,能干净返回扩展名、文件名、目录名各部分。
pathinfo 标准用法
$file = '/var/www/upload/photo.2024.jpg';
$info = pathinfo($file);
echo $info['extension']; // jpg
echo $info['filename']; // photo.2024
echo $info['dirname']; // /var/www/upload
一次返回路径各组成部分,最稳妥。
只取扩展名
$ext = pathinfo($file, PATHINFO_EXTENSION);
// jpg
传第二个常量直接拿扩展名,省一次数组取值。
处理无扩展名文件
$ext = pathinfo('README', PATHINFO_EXTENSION);
// 空字符串,不会报错
没扩展名时返回空串,记得做空判断。
大写转小写统一
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
用户传JPG、Jpg、jpg都归一,上传校验用统一小写比较。
不要用explode硬切
// 反面例子:photo.2024.jpg 会切错
$parts = explode('.', $file);
$ext = end($parts);
多级点号文件名会取错,pathinfo按最后一个点正确取后缀。
注意事项
- 扩展名不等于真实MIME类型,校验文件内容要用finfo;
- 扩展名可伪造,上传安全不能只信后缀;
- PATHINFO_EXTENSION对".htaccess"这种点开头文件返回空;
- 中文文件名不受影响,pathinfo按字节正确处理;
- 取到扩展名后trim并strtolower再做白名单校验;
- 别把扩展名当安全依据,配合大小限制和重命名存储。
总结
pathinfo($file, PATHINFO_EXTENSION)取扩展名最稳,filename拿文件名、dirname拿目录,无扩展名返回空串记得判断,业务里统一strtolower小写。不要用explode切多级点号文件名。扩展名只是展示用,真实类型靠finfo判断,上传安全还需白名单加文件内容校验和重命名存储。
PHP空合并运算符
PHP空合并运算符
引言
从请求数组里取值时,直接访问不存在的键会触发Notice报错。isset加三元判断写法啰嗦,PHP7引入的??空合并运算符一行搞定,且不存在键时不报Notice,是接口参数处理的高频写法。
基本用法
$page = $_GET['page'] ?? 1;
键存在且非null就取它的值,否则取默认值1,全程不报Notice。
对比老式写法
// 旧写法,啰嗦且易漏isset
$page = isset($_GET['page']) ? $_GET['page'] : 1;
// 新写法,等价且简洁
$page = $_GET['page'] ?? 1;
两段逻辑完全等价,新写法可读性明显更好。
链式兜底
$name = $input['name'] ?? $request['name'] ?? '匿名用户';
依次尝试,第一个非null的值被采用,最后一个兜底默认值。
数组嵌套取值
$city = $data['user']['city'] ?? '未知城市';
中间任何一层键缺失都安全返回默认值,不会因层级不存在报错。
??与||的区别
// ??只判断null和未定义,空字符串0都保留
$q = $_GET['q'] ?? '';
// ||会把0、空字符串当假,业务语义不同
注意0和空字符串不是null,??会原样保留。
赋值场景:合并赋值
$config['debug'] ??= false;
??=只在键不存在或为null时赋值,已存在则不动。
注意事项
- ??只在未定义或null时兜底,空串、0、false不算空;
- 数组深层取值安全,但类型仍要自行校验;
- 不要用??替代输入过滤,默认值之外仍需验证;
- PHP5不支持??,老环境需改用isset三元;
- 链式写法注意短路,前面命中即返回;
- 业务关键参数兜底后仍要做合法性范围检查。
总结
??判断键是否存在或为null,缺省返回默认值且不报Notice,替代isset三元,支持链式兜底和数组深层取值,??=实现条件赋值。区别于||,0和空字符串会被保留不会被兜底,PHP5需用旧写法,取到默认值后关键参数仍要校验合法性。
PHP去除字符串空格
PHP去除字符串空格
引言
用户输入、表单提交常带首尾空格甚至全角空格、换行,入库前不清洗会导致登录比对失败、查询匹配不上、统计失真。trim系列处理首尾,preg_replace处理中间多余空格,按需选用。
去除首尾空白:trim
$name = trim(" 张三 ");
// 结果:张三,首尾空格、制表符、换行一并去掉
trim默认去空格、tab、换行、回车、null字节等首尾空白。
只去左侧:ltrim
$path = ltrim("/var/www/", "/");
// 结果:var/www/,第二个参数指定要剥掉的字符集
只去右侧:rtrim
$file = rtrim("photo.jpg/", "/");
// 结果:photo.jpg/,只剥右边指定字符
自定义去除字符
trim($str, " \t\n,");
第二个参数列出要去除的字符,不止空格,可同时去逗号、换行。
去掉中间多余空格
$str = preg_replace('/\s+/', ' ', $str);
// 多个连续空白压成一个空格
$str = preg_replace('/\s/', '', $str);
// 彻底删除所有空格和空白
trim管首尾,中间空格必须用正则。
处理全角空格
$str = str_replace(' ', '', $str);
// 全角空格\u3000普通trim去不掉,要单独替换
数组批量清洗
$clean = array_map('trim', $_POST);
对整个表单数组逐个trim,登录注册入口常用。
注意事项
- trim只处理首尾,别指望它删中间空格;
- 全角空格普通trim无效,需str_replace单独处理;
- 密码字段不要trim,避免误改用户输入;
- 第二个参数是字符集不是整串,按字符逐个剥;
- 入库前统一清洗,查询和比对才一致;
- 中文人名中间正常空格要保留,只压连续空白。
总结
trim去首尾空白,ltrim去左,rtrim去右,第二个参数自定义字符集,中间连续空格用preg_replace压成一个或全删,全角空格单独str_replace,表单用array_map批量清洗。记住trim只管首尾,密码不清洗,全角空格要单独处理,入库前统一规范保证比对一致。
PHP字符串反转方法
PHP字符串反转方法
引言
字符串反转是面试和笔试高频小题,业务里也常用于生成验证码、翻转密文顺序、做简单加密。PHP内置strrev一行搞定,但处理中文时要注意字节和字符的区别。
英文字符反转:strrev
$str = "hello world";
echo strrev($str); // dlrow olleh
strrev按字节反转,英文和数字没有问题,性能也最高。
中文字符串反转
strrev直接转中文会乱码,因为中文UTF-8占3个字节,按字节切会把一个字拆坏。需要按字符处理:
$str = "技术分享";
$len = mb_strlen($str, 'UTF-8');
$result = '';
for ($i = $len - 1; $i >= 0; $i--) {
$result .= mb_substr($str, $i, 1, 'UTF-8');
}
echo $result; // 享分术技
用mb_strlen取字符数,mb_substr按字符回取,保证每个完整汉字不被拆开。
数组方式实现
$arr = preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY);
echo implode('', array_reverse($arr));
u修饰符按UTF-8切字符成数组,array_reverse翻转后再拼回去。
随机打乱:str_shuffle
echo str_shuffle("abc123"); // 每次结果随机
注意str_shuffle同样按字节,中文打乱不适用,验证码里建议只放英文数字。
注意事项
- strrev只适合单字节英文,中文必须走mb系列;
- 生产验证码用str_shuffle后再截取长度,别用中文;
- 反转不等于加密,明文可还原,别拿来做真正加密;
- mb扩展缺失时先检查extension_loaded('mbstring');
- 超大字符串循环回拼建议用数组收集后implode,减少字符串反复复制。
总结
英文用strrev一行反转,中文用mb_strlen加mb_substr按字符倒取,数组preg_split配合array_reverse也能实现,随机打乱用str_shuffle但仅限英文数字。记住字节反转和字符反转的区别,中文场景务必走mb扩展,大字符串用数组收集再拼接提升性能。
PHP遍历关联数组
PHP遍历关联数组
引言
关联数组用键名而非数字下标组织数据,配置项、接口返回的JSON转成数组后都是关联数组。遍历要同时拿到键和值,foreach是最常用的方式,不同场景配合array_keys、array_walk更灵活。
foreach取键和值
$user = ['name'=>'刘馨怡','age'=>26,'city'=>'连云港'];
foreach ($user as $key => $value) {
echo "$key: $value\n";
}
$key => $value同时拿到键名和值,最通用。
只取值
foreach ($user as $value) {
echo $value;
}
不需要键名时省略$key。
用键名取单个值
echo $user['name'];
// 判断键是否存在
if (isset($user['email'])) {
echo $user['email'];
}
取单值直接下标访问,先isset避免未定义键报错。
array_keys取所有键
$keys = array_keys($user);
print_r($keys); // ['name','age','city']
需要单独处理键名列表时用。
array_walk回调遍历
array_walk($user, function($value, $key) {
echo "$key => $value";
});
适合把遍历逻辑封装成回调。
二维关联数组遍历
$list = [
['id'=>1,'name'=>'A'],
['id'=>2,'name'=>'B']
];
foreach ($list as $row) {
echo $row['id'].'-'.$row['name'];
}
循环里再按键名取子元素。
注意事项
- foreach不会改变原数组指针,比each安全;
- 遍历中不要随意增删键,可能导致迭代异常;
- 关联数组键名大小写敏感;
- 值可能为null时用array_key_exists判断键是否存在,isset对null返回false;
- 大数组遍历时注意内存,避免在循环内重复计算。
总结
关联数组遍历首选foreach的$key => $value形式,单值直接下标加isset判断,取键名用array_keys,封装逻辑用array_walk,二维数组外层foreach内层按键取值。掌握这几种就能应对配置、接口数据等绝大多数场景。
PHP cURL发送POST
PHP cURL发送POST
引言
后端对接第三方接口、登录模拟、提交表单,PHP最常用cURL扩展发HTTP请求。file_get_contents只能简单GET,cURL能控制POST、请求头、Cookie、超时和错误处理,是对接API的标配。
基础POST表单
$ch = curl_init('https://api.example.com/login');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'username' => 'user01',
'password' => '123456',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 结果返回字符串而非直接输出
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$res = curl_exec($ch);
$err = curl_error($ch);
curl_close($ch);
POSTFIELDS传数组即multipart表单,接口收到的是form-data。
发送JSON
对接REST接口要手动编码并声明头:
$json = json_encode(['name' => '测试', 'age' => 18]);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $token,
]);
注意JSON方式POSTFIELDS传字符串,别传数组。
带Cookie维持登录
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookie.txt'); // 第一次保存
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookie.txt'); // 第二次读取
模拟登录再访问需鉴权页面时,用cookie文件维持会话。
错误与状态码处理
if ($err) { echo '请求失败:' . $err; }
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$data = json_decode($res, true);
别只看返回体,必须判断$code和curl_error,500/404也要处理。
注意事项
- 关SSL校验仅限本地测试,生产保留CURLOPT_SSL_VERIFYPEER;
- 超时必须设,防止接口慢拖垮进程;
- HTTPS证书异常先排查证书链,别长期关闭校验;
- 敏感token放请求头,别写进URL参数;
- 用完curl_close释放句柄。
总结
cURL POST先init,设CURLOPT_POST和POSTFIELDS,表单传数组、JSON传字符串并带头,RETURNTRANSFER取回结果,配合超时、错误判断和状态码,生产保留SSL校验。掌握这套就能稳定对接绝大多数第三方API。
PHP字符串截取方法
PHP字符串截取方法
引言
截取标题摘要、手机号中间脱敏、按字数截断列表页,是PHP开发里最高频的操作。很多人直接用substr处理中文,结果出现半个汉字的乱码,关键在于substr按字节切,中文UTF-8占3个字节。
substr按字节截取
$str = "hello世界";
echo substr($str, 0, 5); // 输出 hello,中文场景容易切坏
substr只适合纯英文ASCII,遇到中文按字节偏移,可能把一个汉字拆成两半。
mb_substr按字符截取
处理中文要用多字节扩展mbstring:
echo mb_substr("hello世界", 0, 5, 'UTF-8');
第四个参数指定编码UTF-8,按"字"而不是"字节"切,中文完整不烂。
带省略号的摘要截断
function cutText($text, $len = 20) {
if (mb_strlen($text, 'UTF-8') <= $len) return $text;
return mb_substr($text, 0, $len, 'UTF-8') . '...';
}
先mb_strlen算字符长度,超长再截并补省略号,列表页摘要常用。
手机号中间脱敏
$phone = "13812345678";
echo substr($phone, 0, 3) . '****' . substr($phone, -4);
// 138****5678
手机号纯数字,substr完全够用,用-4取后四位。
注意事项
- 中文必须mb_substr并显式传UTF-8,别依赖默认编码;
- 确认服务器装了mbstring扩展,php -m查看;
- 表情、生僻字占多个字节,更要按字符切;
- 截断前先判断长度,避免补省略号反而超长。
总结
纯英文数字用substr,含中文统一mb_substr指定UTF-8,摘要先mb_strlen判断再补省略号,手机号脱敏用substr取头尾。选对函数就不会出现半个汉字乱码。
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自定义比较。
PHP请求参数过滤
PHP请求参数过滤
引言
Web接口接收GET、POST参数,如果不做过滤校验,容易出现SQL注入、XSS、业务逻辑漏洞。PHP开发接口时,参数过滤是安全第一道防线,所有外部输入都不可信。
基础过滤思路
- 类型强制转换:数字参数直接转为int;
- 长度限制,防止传入超大字符串;
- 正则匹配手机号、邮箱等固定格式;
- 输出到页面时做html转义,防止XSS;
- 入库前转义或者使用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项目开发的基础规范。
API 设计规范:RESTful 风格的最佳实践指南
前言
一个好的 API 设计,能让前后端协作效率翻倍;一个烂的 API,能让所有人都痛苦不堪。本文总结 RESTful API 设计的最佳实践,帮你写出规范、好用的接口。
一、URL 设计
1. 用名词,不用动词
# 好的
GET /users # 获取用户列表
POST /users # 创建用户
GET /users/123 # 获取单个用户
PUT /users/123 # 更新用户
DELETE /users/123 # 删除用户
# 不好的
GET /getUsers
POST /createUser
GET /getUserById?id=123
2. 层级要清晰
# 获取某篇文章的评论
GET /users/123/articles/456/comments
3. 用复数名词
# 好的
/users
# 不好的
/user
二、HTTP 方法语义
| 方法 | 语义 | 幂等 | 安全 |
|---|---|---|---|
| GET | 获取资源 | 是 | 是 |
| POST | 创建资源 | 否 | 否 |
| PUT | 更新完整资源 | 是 | 否 |
| PATCH | 更新部分资源 | 否 | 否 |
| DELETE | 删除资源 | 是 | 否 |
幂等:同一个请求发多次,结果一样。
安全:请求不会修改服务器数据。
三、HTTP 状态码
200 OK # 成功
201 Created # 创建成功
204 No Content # 删除成功,无返回内容
400 Bad Request # 参数错误
401 Unauthorized # 未登录
403 Forbidden # 无权限
404 Not Found # 资源不存在
429 Too Many Requests # 请求太频繁
500 Internal Server Error # 服务器错误
四、统一响应格式
成功响应
{
"code": 0,
"message": "success",
"data": {
"id": 123,
"name": "张三"
}
}
列表响应
{
"code": 0,
"message": "success",
"data": {
"list": [...],
"total": 100,
"page": 1,
"page_size": 20
}
}
错误响应
{
"code": 10001,
"message": "用户名或密码错误",
"data": null
}
五、参数设计
1. 查询参数用 query string
GET /users?page=1&page_size=20&status=active
2. 创建/更新用 body
POST /users
{
"name": "张三",
"email": "zhangsan@example.com"
}
3. 路径参数用 path
GET /users/123/articles/456
六、分页、排序、过滤
# 分页
GET /users?page=1&page_size=20
# 排序
GET /users?sort=created_at&order=desc
# 过滤
GET /users?status=active&role=admin
七、版本控制
API 要加版本号,方便升级不影响老用户。
# 在 URL 里加版本
/api/v1/users
/api/v2/users
# 或者在 Header 里加版本
Accept: application/vnd.myapi.v1+json
八、认证与授权
# 请求头带 Token
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
不要把 token 放在 URL 里,会泄露。
九、接口文档
好的 API 一定要有文档:
- Swagger / OpenAPI:自动生成接口文档
- Postman:方便调试和分享
- 示例:每个接口都要有请求和响应示例
十、常见坑
- GET 请求带 body:不规范,浏览器和代理可能会忽略
- 状态码乱用:别什么错都返回 200
- 返回格式不统一:有的接口直接返回数据,有的包一层 code
- 命名不规范:一会儿驼峰,一会儿下划线
- 接口粒度太粗:一个接口干所有事,灵活性很差
总结
RESTful API 设计的核心思路:
- URL 用名词,用 HTTP 方法表达操作
- 状态码要正确,别什么都返回 200
- 响应格式统一,成功失败都一个结构
- 参数位置要合理,查询用 query,操作用 body
- 一定要有文档,不然没人会用你的接口
掌握这些,你的 API 设计水平就能超过 80% 的后端开发者了。