PHP 判断请求是否来自手机避坑指南
很多初学者在学习PHP时,都会遇到判断请求是否来自手机相关的问题。网上的资料零散且质量参差不齐,本文尝试用一篇文章把判断请求是否来自手机讲清楚。
PHP常用数组函数,10个必须掌握
前言
PHP数组函数太多了,记不住?本文整理了10个最常用的数组函数,写代码天天用,必须掌握。
一、array_column — 取数组某一列
用法
$users = [
['id' => 1, 'name' => '张三'],
['id' => 2, 'name' => '李四'],
['id' => 3, 'name' => '王五']
];
$names = array_column($users, 'name');
// 结果:['张三', '李四', '王五']
取列并指定键
$userMap = array_column($users, 'name', 'id');
// 结果:[1 => '张三', 2 => '李四', 3 => '王五']
二、array_map — 遍历处理数组
用法
$numbers = [1, 2, 3, 4, 5];
$squares = array_map(function($n) {
return $n * $n;
}, $numbers);
// 结果:[1, 4, 9, 16, 25]
实际应用
// 把所有名字转成大写
$names = ['zhang', 'li', 'wang'];
$upperNames = array_map('strtoupper', $names);
// 结果:['ZHANG', 'LI', 'WANG']
三、array_filter — 过滤数组
用法
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$even = array_filter($numbers, function($n) {
return $n % 2 == 0;
});
// 结果:[2, 4, 6, 8, 10]
过滤空值
$arr = ['a', '', 0, null, 'b', '0'];
$result = array_filter($arr);
// 结果:['a', 'b']
四、array_merge — 数组合并
用法
$arr1 = ['a', 'b'];
$arr2 = ['c', 'd'];
$merged = array_merge($arr1, $arr2);
// 结果:['a', 'b', 'c', 'd']
关联数组合并
$arr1 = ['name' => '张三', 'age' => 20];
$arr2 = ['age' => 25, 'city' => '北京'];
$merged = array_merge($arr1, $arr2);
// 结果:['name' => '张三', 'age' => 25, 'city' => '北京']
// 注意:age被后面的覆盖了
五、in_array — 判断值是否在数组里
用法
$fruits = ['apple', 'banana', 'orange'];
if (in_array('apple', $fruits)) {
echo '有苹果';
}
注意严格模式
$arr = [1, '2', '3'];
in_array(2, $arr); // true(因为类型转换了)
in_array(2, $arr, true); // false(严格模式)
六、array_keys — 取数组的键
用法
$user = ['name' => '张三', 'age' => 20, 'city' => '北京'];
$keys = array_keys($user);
// 结果:['name', 'age', 'city']
找某个值的键
$ages = [
'张三' => 20,
'李四' => 25,
'王五' => 30
];
$keys = array_keys($ages, 25);
// 结果:['李四']
七、array_values — 取数组的值
用法
$user = ['name' => '张三', 'age' => 20, 'city' => '北京'];
$values = array_values($user);
// 结果:['张三', 20, '北京']
八、array_sum — 数组求和
用法
$numbers = [1, 2, 3, 4, 5];
$sum = array_sum($numbers);
// 结果:15
九、count — 统计数组长度
用法
$fruits = ['apple', 'banana', 'orange'];
$count = count($fruits);
// 结果:3
多维数组
$users = [
['name' => '张三'],
['name' => '李四']
];
count($users); // 2
count($users, COUNT_RECURSIVE); // 4(递归统计)
十、explode / implode — 字符串和数组互转
explode — 字符串转数组
$str = 'apple,banana,orange';
$fruits = explode(',', $str);
// 结果:['apple', 'banana', 'orange']
implode — 数组转字符串
$fruits = ['apple', 'banana', 'orange'];
$str = implode(',', $fruits);
// 结果:'apple,banana,orange'
十一、常用场景
场景1:从数据库取数据,提取某列
$users = Db::name('user')->select();
$names = array_column($users, 'name');
场景2:过滤空值
$list = array_filter($list);
场景3:判断是否存在
if (in_array($userId, $adminIds)) {
// 是管理员
}
场景4:数组去重后转字符串
$tags = array_unique($tags);
$tagStr = implode(',', $tags);
十二、常见坑
坑1:array_merge关联数组键名覆盖
问题: 相同键名会被覆盖。
解决: 用+号或array_replace。
坑2:in_array类型不严格
问题: in_array(0, ['a', 'b'])返回true。
解决: 加第三个参数true,严格模式。
坑3:count返回0
问题: 传了null进去。
解决: 先判断是不是数组。
总结
10个必须掌握的数组函数:
array_column— 取某一列array_map— 遍历处理array_filter— 过滤array_merge— 合并in_array— 判断存在array_keys— 取键array_values— 取值array_sum— 求和count— 统计长度explode/implode— 字符串数组互转
记住:
- 天天用的就是这10个
- 写代码前先想想有没有现成函数
- 别自己写循环,用内置函数更快
遇到问题加QQ23979811 协助处理
PHP实现简单分页,SQL limit用法
前言
做网站列表页,数据太多一页显示不完,需要分页。PHP+MySQL做分页很简单,就是用SQL的limit。本文写个完整的分页示例,直接拿去用。
一、分页原理
分页就两件事:
- 查总条数:看看一共有多少条数据
- 查当前页数据:用limit取当前页要显示的那几条
公式:
偏移量 = (当前页 - 1) × 每页条数
比如每页10条,第3页:
偏移量 = (3 - 1) × 10 = 20
SQL: SELECT * FROM users LIMIT 20, 10
二、SQL limit用法
语法
SELECT * FROM 表名 LIMIT 偏移量, 每页条数
例子
-- 取前10条
SELECT * FROM users LIMIT 0, 10;
-- 取第11-20条
SELECT * FROM users LIMIT 10, 10;
-- 取第21-30条
SELECT * FROM users LIMIT 20, 10;
三、完整分页代码
第一步:接收当前页码
<?php
// 当前页,默认第1页
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// 每页显示多少条
$pageSize = 10;
// 计算偏移量
$offset = ($page - 1) * $pageSize;
第二步:查总条数
// 连接数据库
$conn = mysqli_connect('127.0.0.1', 'root', '123456', 'test', 3306);
mysqli_set_charset($conn, 'utf8mb4');
// 查总条数
$sql = "SELECT COUNT(*) as total FROM users";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_assoc($result);
$total = $row['total'];
// 算总页数
$totalPage = ceil($total / $pageSize);
第三步:查当前页数据
// 查当前页的数据
$sql = "SELECT id, name, age FROM users ORDER BY id DESC LIMIT $offset, $pageSize";
$result = mysqli_query($conn, $sql);
// 取数据
$list = [];
while ($row = mysqli_fetch_assoc($result)) {
$list[] = $row;
}
第四步:显示数据和分页导航
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>用户列表</title>
</head>
<body>
<h1>用户列表</h1>
<table border="1">
<tr>
<th>ID</th>
<th>姓名</th>
<th>年龄</th>
</tr>
<?php foreach ($list as $item): ?>
<tr>
<td><?php echo $item['id']; ?></td>
<td><?php echo $item['name']; ?></td>
<td><?php echo $item['age']; ?></td>
</tr>
<?php endforeach; ?>
</table>
<p>共 <?php echo $total; ?> 条,第 <?php echo $page; ?> / <?php echo $totalPage; ?> 页</p>
<p>
<?php if ($page > 1): ?>
<a href="?page=<?php echo $page - 1; ?>">上一页</a>
<?php endif; ?>
<?php for ($i = 1; $i <= $totalPage; $i++): ?>
<?php if ($i == $page): ?>
<strong><?php echo $i; ?></strong>
<?php else: ?>
<a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a>
<?php endif; ?>
<?php endfor; ?>
<?php if ($page < $totalPage): ?>
<a href="?page=<?php echo $page + 1; ?>">下一页</a>
<?php endif; ?>
</p>
</body>
</html>
四、封装成分页函数
function pagination($page, $totalPage, $url = '')
{
$html = '';
if ($page > 1) {
$html .= '<a href="' . $url . '?page=' . ($page - 1) . '">上一页</a> ';
}
for ($i = 1; $i <= $totalPage; $i++) {
if ($i == $page) {
$html .= '<strong style="margin:0 5px;">' . $i . '</strong> ';
} else {
$html .= '<a href="' . $url . '?page=' . $i . '" style="margin:0 5px;">' . $i . '</a> ';
}
}
if ($page < $totalPage) {
$html .= '<a href="' . $url . '?page=' . ($page + 1) . '">下一页</a>';
}
return $html;
}
// 使用
echo pagination($page, $totalPage);
五、常见坑
坑1:页码没做限制
用户输入 ?page=999,结果查出来是空的。
解决: 限制页码范围:
$page = max(1, min($page, $totalPage));
坑2:offset算错了
$offset = $page * $pageSize; // 错!
正确:
$offset = ($page - 1) * $pageSize; // 对!
坑3:总条数查错了
带条件查询的时候,总条数也要带条件。
比如:
// 带条件查数据
$sql = "SELECT * FROM users WHERE status = 1 LIMIT $offset, $pageSize";
// 总条数也要带条件
$sql = "SELECT COUNT(*) FROM users WHERE status = 1";
坑4:大表分页越查越慢
数据多了以后,LIMIT 100000, 10 会很慢。
原因: MySQL要先扫描前面10万条再跳过。
优化:
-- 先查id,再关联
SELECT u.* FROM users u
INNER JOIN (SELECT id FROM users ORDER BY id LIMIT 100000, 10) t
ON u.id = t.id;
六、带搜索条件的分页
// 搜索关键词
$keyword = isset($_GET['keyword']) ? trim($_GET['keyword']) : '';
// 拼WHERE
$where = "WHERE 1=1";
if ($keyword) {
$where .= " AND name LIKE '%$keyword%'";
}
// 总条数
$sql = "SELECT COUNT(*) as total FROM users $where";
// 当前页数据
$sql = "SELECT * FROM users $where ORDER BY id DESC LIMIT $offset, $pageSize";
总结
记住:
- 分页就是
LIMIT 偏移量, 每页条数 - 偏移量 = (当前页 - 1) × 每页条数
- 先查总条数,再算总页数
- 页码要做限制,不能超出范围
- 带条件查询时,总条数也要带条件
核心公式:
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$pageSize = 10;
$offset = ($page - 1) * $pageSize;
$sql = "SELECT * FROM users LIMIT $offset, $pageSize";
遇到问题加QQ23979811 协助处理
PHP报错Permission denied权限问题解决
前言
PHP写文件、读文件、上传文件,突然报"Permission denied"?这是Linux文件权限问题,最常见就是文件或目录的所有者不对、权限不够。本文讲怎么排查和解决。
一、报错长啥样
Warning: fopen(/data/test.txt): failed to open stream: Permission denied in /www/test.php on line 5
意思:PHP没权限操作这个文件/目录。
二、常见原因
原因1:文件/目录权限不够
PHP没有读或写的权限。
原因2:文件/目录所有者不对
PHP用www用户跑,但文件是root创建的,www没权限。
原因3:SELinux开着
CentOS默认开SELinux,即使权限对了也不让访问。
原因4:目录权限不够
要写文件的话,目录本身也要有写权限。
三、排查步骤
第一步:看报错的文件路径
报错信息里会告诉你是哪个文件:
Permission denied: /data/upload/test.jpg
就是 /data/upload/ 这个目录的问题。
第二步:看文件权限
ls -l /data/upload/
输出类似:
-rw-r--r-- 1 root root 1024 Oct 9 10:00 test.txt
看第一个字段:
-rw-r--r--表示所有者能读写,其他人只能读
看所有者:
root root表示所有者和组都是root
第三步:看PHP用什么用户跑
ps aux | grep php-fpm
输出里会看到:
www 1234 0.0 0.5 123456 7890 ? S 10:00 0:00 php-fpm: pool www
PHP是用 www 用户跑的。
如果文件所有者是root,那www用户就没权限写。
四、解决方法
方法1:改文件所有者
把文件/目录的所有者改成PHP的用户:
# 改单个文件
chown www:www /data/upload/test.txt
# 改整个目录(包括里面的文件)
chown -R www:www /data/upload/
推荐这个方法,最安全。
方法2:改权限
给所有人加权限:
# 文件加写权限
chmod 666 /data/upload/test.txt
# 目录加写权限
chmod 777 /data/upload/
注意: 777太不安全了,尽量用方法1改所有者。
方法3:看SELinux
CentOS开了SELinux的话,即使权限对了也不行。
检查SELinux:
getenforce
输出 Enforcing 就是开着。
临时关掉:
setenforce 0
永久关掉:
vi /etc/selinux/config
把:
SELINUX=enforcing
改成:
SELINUX=disabled
重启服务器生效。
或者只给目录加SELinux权限:
chcon -R -t httpd_sys_rw_content_t /data/upload/
五、常见场景
场景1:上传图片报错
用户上传图片,报Permission denied。
原因: uploads目录所有者不对。
解决:
chown -R www:www /www/wwwroot/你的站/uploads/
场景2:写日志报错
PHP写日志文件,报Permission denied。
原因: 日志目录没权限。
解决:
chown -R www:www /var/log/你的应用/
场景3:session不存
PHP session存不进去。
原因: session目录没权限。
解决:
chown -R www:www /var/lib/php/session/
场景4:缓存目录没权限
ThinkPHP/Laravel缓存写不进去。
原因: runtime目录没权限。
解决:
chown -R www:www runtime/
六、常用命令
看文件权限
ls -l 文件名
看目录权限
ls -ld 目录名
改所有者
chown 用户:组 文件
chown -R 用户:组 目录
改权限
chmod 755 目录
chmod 644 文件
七、权限数字含义
| 数字 | 含义 |
|---|---|
| 7 | 读+写+执行 (4+2+1) |
| 6 | 读+写 (4+2) |
| 5 | 读+执行 (4+1) |
| 4 | 只读 |
常用:
- 目录:755
- 文件:644
- 上传目录:775(或改所有者)
八、常见坑
坑1:只改了文件,没改目录
要写文件的话,目录本身也要有写权限。
解决: 目录也要改所有者。
坑2:递归改权限
chmod -R 777 目录
太危险了,所有人都能写。
解决: 优先改所有者,不要随便777。
坑3:SELinux偷偷开着
CentOS7以后默认开SELinux,权限对了也不行。
检查:
getenforce
坑4:父目录没权限
比如 /data/upload/ 有权限了,但 /data/ 没权限,还是进不去。
解决: 从根开始一层一层检查。
总结
记住:
- 报错先看是哪个文件/目录
- 看文件所有者是不是PHP的用户(一般是www)
- 不是就
chown -R www:www 目录 - 还不行就检查SELinux
- 尽量改所有者,不要随便777
最常用:
chown -R www:www /你的目录/
遇到问题加QQ23979811 协助处理
PHP file_get_contents获取https内容失败
前言
PHP用file_get_contents获取http内容没问题,换成https就报错了?这是最常见的问题,一般就是openssl扩展没开,或者SSL证书验证没过。本文讲怎么解决。
一、问题现象
<?php
$url = 'https://api.example.com/data';
$result = file_get_contents($url);
echo $result;
结果报错:
Warning: file_get_contents(): failed to open stream: Unable to find the wrapper "https"
或者:
Warning: file_get_contents(): SSL operation failed with code 1
二、常见原因
原因1:openssl扩展没装
PHP默认不开openssl扩展,file_get_contents不支持https。
原因2:allow_url_open没开
php.ini里禁了远程打开。
原因3:SSL证书验证失败
对方网站证书有问题,PHP验证不通过。
原因4:网络问题
服务器出不去,或者DNS解析不了。
三、排查步骤
第一步:看openssl扩展装了没
php -m | grep openssl
有输出就是装了,没输出就是没装。
或者写个phpinfo:
<?php
phpinfo();
搜"openssl",看有没有。
第二步:看allow_url_open开了没
<?php
echo ini_get('allow_url_fopen');
输出1就是开了,空就是没开。
第三步:用curl测试
curl https://api.example.com/data
curl能通就是PHP的问题,curl也不通就是网络问题。
四、解决方法
方法1:装openssl扩展(推荐)
CentOS
yum install php-openssl
或者:
yum install php7x-openssl
(x是你的PHP版本)
Ubuntu
apt install php-openssl
装完重启
systemctl restart php-fpm
方法2:改php.ini配置
打开php.ini,找到:
;extension=openssl
去掉前面的分号:
extension=openssl
然后重启php-fpm。
方法3:关掉SSL验证(临时)
不想改配置,可以在代码里关验证:
<?php
$context = stream_context_create([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
]);
$result = file_get_contents('https://api.example.com/data', false, $context);
echo $result;
注意: 这是临时方案,生产环境建议把证书配好。
方法4:改用curl(推荐)
file_get_contents不好用,直接用curl:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/data');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo '错误: ' . curl_error($ch);
}
curl_close($ch);
echo $result;
curl比file_get_contents好用多了,推荐用curl。
五、常见坑
坑1:CLI和FPM配置不一样
命令行里openssl装了,网页上还是不行。
原因: CLI和FPM用不同的php.ini。
解决: 看phpinfo里的配置路径,改对应的那个。
坑2:装了扩展没重启
装完忘了重启php-fpm。
解决:
systemctl restart php-fpm
坑3:对方网站证书过期
对方网站SSL证书过期了,PHP验证不通过。
解决: 关验证,或者让对方更新证书。
坑4:allow_url_fopen没开
php.ini里禁了远程打开。
解决:
allow_url_fopen = On
坑5:防火墙挡了出站
服务器防火墙挡了443端口出站。
解决:
# 看能不能出去
curl -I https://www.baidu.com
# 不行的话开防火墙
firewall-cmd --add-port=443/tcp --permanent
firewall-cmd --reload
六、POST请求示例
file_get_contents发POST:
<?php
$url = 'https://api.example.com/submit';
$data = http_build_query([
'name' => '张三',
'age' => 20
]);
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'content' => $data,
'timeout' => 30
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
]);
$result = file_get_contents($url, false, $context);
echo $result;
还是建议用curl,更灵活。
七、封装函数
function http_get($url, $timeout = 30)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
$result = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
return ['code' => -1, 'msg' => $error];
}
curl_close($ch);
return ['code' => 0, 'data' => $result];
}
// 使用
$res = http_get('https://api.example.com/data');
if ($res['code'] == 0) {
echo $res['data'];
} else {
echo '请求失败: ' . $res['msg'];
}
总结
记住:
- file_get_contents不支持https,是openssl扩展没装
- 装openssl扩展,重启php-fpm
- 或者关SSL验证临时解决
- 最推荐:改用curl,比file_get_contents好用多了
推荐方案:
直接用curl,别用file_get_contents搞https。
遇到问题加QQ23979811 协助处理
PHP字符串处理常用函数,substr、explode
前言
PHP字符串处理天天用,截取、拼接、查找、替换,每个项目都离不开。本文整理最常用的字符串函数,收藏随用随查。
一、字符串长度
strlen()
字节长度(中文算3个字节):
echo strlen('hello'); // 5
echo strlen('你好'); // 6(UTF-8每个中文3字节)
mb_strlen()
字符长度(中文算1个字符):
echo mb_strlen('你好', 'utf-8'); // 2
区别: strlen看字节,mb_strlen看字符。
二、截取字符串
substr()
$str = 'hello world';
// 从第0位取5个
echo substr($str, 0, 5); // hello
// 从第6位取到最后
echo substr($str, 6); // world
// 从倒数第5位取
echo substr($str, -5); // world
mb_substr()(中文截取)
$str = '我是中国人';
// 从第0个字符取2个
echo mb_substr($str, 0, 2, 'utf-8'); // 我是
注意: 中文一定要用mb_substr,不然会乱码。
三、查找字符串
strpos()
找位置(从左找,找到返回位置,没找到返回false):
$str = 'hello world';
echo strpos($str, 'world'); // 6
// 没找到
var_dump(strpos($str, 'abc')); // bool(false)
strrpos()
从右找:
echo strrpos('a/b/c', '/'); // 3
strstr()
找并截取:
$str = 'user@example.com';
echo strstr($str, '@'); // @example.com
str_contains()(PHP8)
var_dump(str_contains('hello world', 'world')); // bool(true)
注意: PHP8以上才有。
四、替换字符串
str_replace()
$str = 'hello world';
echo str_replace('world', 'php', $str); // hello php
str_ireplace()
不区分大小写:
echo str_ireplace('WORLD', 'php', $str); // hello php
preg_replace()
正则替换:
$str = '2026-10-09';
// 把横线换成点
echo preg_replace('/-/g', '.', $str); // 2026.10.09
五、拼接和拆分
implode()(数组转字符串)
$arr = ['a', 'b', 'c'];
echo implode(',', $arr); // a,b,c
echo implode('-', $arr); // a-b-c
explode()(字符串转数组)
$str = 'a,b,c';
$arr = explode(',', $str);
// ['a', 'b', 'c']
限制拆分次数
$str = 'a,b,c,d';
// 只拆成2段
$arr = explode(',', $str, 2);
// ['a', 'b,c,d']
六、去除空白
trim()
去首尾空格:
$str = ' hello ';
echo trim($str); // hello
ltrim() / rtrim()
ltrim($str); // 去左边
rtrim($str); // 去右边
七、大小写转换
strtoupper('hello'); // HELLO
strtolower('HELLO'); // hello
ucwords('hello world'); // Hello World
ucfirst('hello'); // Hello
八、重复和填充
str_repeat()
echo str_repeat('ab', 3); // ababab
str_pad()
// 补到10位,右边补*
echo str_pad('123', 10, '*', STR_PAD_RIGHT); // 123*******
// 左边补
echo str_pad('123', 10, '0', STR_PAD_LEFT); // 0000000123
九、常见场景
场景1:手机号中间打星号
$phone = '13812345678';
echo substr($phone, 0, 3) . '****' . substr($phone, -4);
// 138****5678
场景2:截取文件名后缀
$file = 'photo.jpg';
echo strrchr($file, '.'); // .jpg
场景3:邮箱拆分
$email = 'user@example.com';
$name = strstr($email, '@', true); // user
$domain = strstr($email, '@'); // @example.com
场景4:URL参数解析
$url = 'https://example.com/index.php?id=1&name=zhangsan';
// 拿问号后面的
$query = parse_url($url, PHP_URL_QUERY);
// id=1&name=zhangsan
parse_str($query, $params);
// ['id' => '1', 'name' => 'zhangsan']
场景5:HTML标签转义
$str = '<script>alert(1)</script>';
echo htmlspecialchars($str);
// <script>alert(1)</script>
十、常见坑
坑1:中文截取乱码
用substr截中文,结果乱码了。
原因: substr按字节截,中文一个字3个字节,容易截到半个字。
解决: 用mb_substr:
mb_substr($str, 0, 10, 'utf-8');
坑2:strpos===0的问题
$str = 'abc';
if (strpos($str, 'a') == false) {
echo '没找到';
}
问题: strpos找到a在位置0,0==false是true。
解决:
if (strpos($str, 'a') === false) {
echo '没找到';
}
注意: 一定要用===,不是==。
坑3:trim只去空格
trim默认只去空格和换行。
要去指定字符:
trim($str, "/"); // 去首尾的/
坑4:explode空字符串
$str = '';
$arr = explode(',', $str);
// [''] 不是空数组!
解决:
if ($str === '') {
$arr = [];
} else {
$arr = explode(',', $str);
}
总结
最常用的10个:
strlen()字符串长度(字节)mb_strlen()字符长度(中文友好)substr()截取字符串strpos()查找位置str_replace()替换explode()字符串转数组implode()数组转字符串trim()去空格strtoupper()/strtolower()大小写str_pad()填充
记住:
- 中文用mb_*系列函数
- 查找用=== false判断
- 数组字符串互转用explode/implode
遇到问题加QQ23979811 协助处理
PHP数组常用函数大全,array操作
前言
PHP数组用得最多,几乎每个脚本都要操作数组。本文整理最常用的数组函数,收藏起来随用随查。
一、创建和遍历
数组定义
<?php
// 索引数组
$arr = [1, 2, 3, 4, 5];
// 关联数组
$user = [
'name' => '张三',
'age' => 20,
'email' => 'zhangsan@qq.com'
];
遍历
// foreach遍历
foreach ($arr as $value) {
echo $value;
}
// 带key
foreach ($user as $key => $value) {
echo "$key: $value";
}
二、增删改查
添加元素
// 末尾加
$arr[] = 6;
array_push($arr, 7, 8);
// 开头加
array_unshift($arr, 0);
// 关联数组加
$user['phone'] = '13800138000';
删除元素
// 删最后一个
array_pop($arr);
// 删第一个
array_shift($arr);
// 删指定key
unset($user['age']);
// 删多个
unset($arr[0], $arr[1]);
修改元素
$arr[0] = 100;
$user['name'] = '李四';
查找元素
// 值是否在数组里
in_array(3, $arr); // true
// 找key
array_key_exists('name', $user); // true
// 找值的key
array_search(2, $arr); // 1
三、数组统计
长度
count($arr);
sizeof($arr); // 和count一样
求和、最大、最小
array_sum($arr); // 求和
max($arr); // 最大
min($arr); // 最小
去重
$unique = array_unique($arr);
打乱
shuffle($arr);
四、排序
索引数组排序
sort($arr); // 升序
rsort($arr); // 降序
关联数组排序
asort($user); // 按值升序
arsort($user); // 按值降序
ksort($user); // 按key升序
krsort($user); // 按key降序
五、拆分和合并
取一段
// 从第2个开始取3个
$slice = array_slice($arr, 1, 3);
合并数组
$arr1 = [1, 2];
$arr2 = [3, 4];
$merge = array_merge($arr1, $arr2); // [1,2,3,4]
交差
$arr1 = [1, 2, 3];
$arr2 = [2, 3, 4];
array_intersect($arr1, $arr2); // [2,3] 交集
array_diff($arr1, $arr2); // [1] 差集
六、key和value操作
取所有key
$keys = array_keys($user);
// ['name', 'age', 'email']
取所有value
$values = array_values($user);
// ['张三', 20, 'zhangsan@qq.com']
翻转key和value
$flipped = array_flip($user);
填充数组
// 5个元素,值都是0
$arr = array_fill(0, 5, 0);
七、数组和字符串互转
数组转字符串
$arr = ['a', 'b', 'c'];
$str = implode(',', $arr); // 'a,b,c'
字符串转数组
$str = 'a,b,c';
$arr = explode(',', $str); // ['a', 'b', 'c']
八、常用场景
场景1:数组去重
$arr = [1, 2, 2, 3, 3, 3];
$unique = array_unique($arr);
// [1, 2, 3]
场景2:查值在不在
$fruits = ['apple', 'banana', 'orange'];
if (in_array('apple', $fruits)) {
echo '有苹果';
}
场景3:二维数组按key排序
$users = [
['name' => '张三', 'age' => 20],
['name' => '李四', 'age' => 25],
['name' => '王五', 'age' => 18]
];
// 按age排序
array_multisort(array_column($users, 'age'), SORT_ASC, $users);
场景4:数组分页
$page = 1;
$pageSize = 10;
$offset = ($page - 1) * $pageSize;
$pageData = array_slice($allData, $offset, $pageSize);
场景5:过滤空值
$arr = ['a', '', null, 'b', 0, 'c'];
$filtered = array_filter($arr);
// ['a', 'b', 'c'] 空字符串、null、0都被过滤了
九、常见坑
坑1:count(null)报错
PHP7.2以后,count(null)会警告。
解决:
$count = is_array($arr) ? count($arr) : 0;
坑2:==和===的区别
in_array('123', [123]); // true(==)
in_array('123', [123], true); // false(===)
注意: 第三个参数true才是严格比较。
坑3:array_merge和+的区别
$arr1 = ['a' => 1, 'b' => 2];
$arr2 = ['b' => 3, 'c' => 4];
array_merge($arr1, $arr2); // ['a'=>1, 'b'=>3, 'c'=>4]
$arr1 + $arr2; // ['a'=>1, 'b'=>2, 'c'=>4]
区别: array_merge后者覆盖前者,+前者保留。
坑4:引用传递
foreach ($arr as &$value) {
$value = $value * 2;
}
unset($value); // 一定要unset,不然后面会出问题
总结
最常用的10个:
count()数组长度in_array()值是否在数组里array_key_exists()key是否存在array_unique()去重array_merge()合并array_slice()取一段array_sum()求和explode()字符串转数组implode()数组转字符串array_column()取二维数组某一列
遇到问题加QQ23979811 协助处理
PHP cURL请求超时,超时时间设置
前言
PHP用cURL请求接口,结果一直卡住或者超时?cURL默认没有超时时间,对方服务器慢的话能卡你几分钟。必须设超时,不然整个脚本都堵死了。本文讲怎么设。
一、为什么要设超时
不设超时的后果:
- 对方服务器挂了,你的脚本一直等着
- 对方服务器慢,你的脚本卡很久
- 高并发下,进程都堵在cURL上,服务器卡死
一定要设!
二、两个超时参数
1. CURLOPT_CONNECTTIMEOUT
连接超时,单位秒。
就是TCP连接花多久。
推荐: 5-10秒
2. CURLOPT_TIMEOUT
总超时,单位秒。
整个请求(连接+发送+接收)花多久。
推荐: 30-60秒
区别
| 参数 | 含义 | 推荐值 |
|---|---|---|
| CURLOPT_CONNECTTIMEOUT | 连接超时 | 5秒 |
| CURLOPT_TIMEOUT | 总超时 | 30秒 |
三、完整示例
<?php
$url = 'https://api.example.com/data';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 连接超时(秒)
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
// 总超时(秒)
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
// 毫秒级超时(可选)
// curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 5000);
// curl_setopt($ch, CURLOPT_TIMEOUT_MS, 30000);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'cURL错误: ' . curl_error($ch);
}
curl_close($ch);
echo $result;
四、超时错误码
常见错误码
| 错误码 | 说明 |
|---|---|
| 28 | 超时(CURLE_OPERATION_TIMEDOUT) |
| 7 | 连接失败(CURLE_COULDNT_CONNECT) |
| 6 | 域名解析失败(CURLE_COULDNT_RESOLVE_HOST) |
检查错误码
if (curl_errno($ch) == 28) {
echo "请求超时了";
}
五、常见坑
坑1:timeout设成了毫秒
CURLOPT_TIMEOUT的单位是秒,不是毫秒!
错误:
curl_setopt($ch, CURLOPT_TIMEOUT, 5000); // 5000秒,不是5秒!
正确:
curl_setopt($ch, CURLOPT_TIMEOUT, 5); // 5秒
或者用毫秒版:
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000); // 5000毫秒 = 5秒
坑2:set_time_limit不够
PHP脚本本身有最大执行时间。
如果cURL超时30秒,但PHP脚本只允许10秒,PHP先超时了。
解决:
set_time_limit(0); // 不限制
// 或者
set_time_limit(60); // 60秒
坑3:DNS解析慢
域名解析也要时间,算在连接超时里。
解决: 连接超时设长一点,比如10秒。
坑4:https证书问题
https请求如果证书有问题,也会卡住或报错。
临时关闭验证:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
注意: 生产环境不建议关,最好把证书配好。
坑5:重定向太多
对方返回301/302重定向,cURL默认不自动跳。
开启自动跳转:
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, 3); // 最多跳3次
六、POST请求示例
<?php
$url = 'https://api.example.com/submit';
$data = [
'name' => '张三',
'age' => 20
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo '错误: ' . curl_error($ch);
}
curl_close($ch);
echo $result;
七、封装成函数
function http_curl($url, $post = false, $data = [], $timeout = 30)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
if ($post) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? http_build_query($data) : $data);
}
$result = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
return ['code' => -1, 'msg' => $error];
}
curl_close($ch);
return ['code' => 0, 'data' => $result];
}
// 使用
$res = http_curl('https://api.example.com/data');
if ($res['code'] == 0) {
echo $res['data'];
} else {
echo '请求失败: ' . $res['msg'];
}
八、推荐配置
普通接口请求
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); // 5秒连接
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 30秒总超时
快速接口(查个数据)
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); // 3秒连接
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 10秒总超时
慢接口(上传大文件)
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 10秒连接
curl_setopt($ch, CURLOPT_TIMEOUT, 120); // 2分钟总超时
总结
记住:
- cURL一定要设超时,不然卡死
CURLOPT_CONNECTTIMEOUT连接超时(秒)CURLOPT_TIMEOUT总超时(秒)- 单位是秒,不是毫秒
- 超时了用
curl_errno判断是不是28
最常用:
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
遇到问题加QQ23979811 协助处理
PHP获取当前时间不对,时区设置date.timezone
前言
PHP用date()获取当前时间,结果比实际时间差了8个小时?这是最常见的时区问题。PHP默认用UTC时区,北京时间是东八区,所以差了8小时。本文讲怎么改。
一、问题现象
<?php
echo date('Y-m-d H:i:s');
输出:
2026-10-09 00:30:00
但实际北京时间是:
2026-10-09 08:30:00
差了8个小时。
二、原因
PHP默认时区是UTC(世界标准时间),不是北京时间。
北京时间是 UTC+8,所以差了8小时。
三、解决方法
方法1:改php.ini(推荐)
第一步:找到php.ini
php --ini
找到Loaded Configuration File那一行:
Loaded Configuration File: /etc/php.ini
第二步:修改date.timezone
打开php.ini,找到这行:
;date.timezone =
改成:
date.timezone = Asia/Shanghai
第三步:重启php-fpm
systemctl restart php-fpm
方法2:代码里设置(临时)
不想改php.ini,可以在代码里设:
<?php
date_default_timezone_set('Asia/Shanghai');
echo date('Y-m-d H:i:s');
注意: 要在所有日期函数之前设。
方法3:.htaccess里设
Apache的话,可以在.htaccess里设:
php_value date.timezone Asia/Shanghai
四、常用时区对照
| 时区 | 说明 |
|---|---|
| Asia/Shanghai | 北京时间(东八区) |
| Asia/Chongqing | 重庆时间 |
| Asia/Urumqi | 乌鲁木齐时间 |
| Asia/Hong_Kong | 香港时间 |
| Asia/Taipei | 台北时间 |
| Asia/Tokyo | 东京时间(东九区) |
| America/New_York | 纽约时间 |
| Europe/London | 伦敦时间 |
| UTC | 世界标准时间 |
国内用: Asia/Shanghai
五、验证时区
看当前时区
<?php
echo date_default_timezone_get();
// 输出:Asia/Shanghai
看服务器时间
date
看PHP时间
<?php
echo date('Y-m-d H:i:s');
对比一下,应该一样了。
六、常见坑
坑1:CLI和FPM时区不一样
命令行里改了php.ini,但网页上还是不对。
原因: CLI和FPM用不同的php.ini。
解决: 看phpinfo()里的配置路径,改对应的那个。
<?php
phpinfo();
搜"Loaded Configuration File",看用的哪个文件。
坑2:改了没重启
改了php.ini忘了重启php-fpm。
解决:
systemctl restart php-fpm
坑3:代码里又改回去了
某个框架或库里又调了 date_default_timezone_set('UTC')。
解决: 全局搜一下,看哪里改的。
坑4:用了gmdate()
gmdate()总是用UTC时区,不管你设什么。
比如:
echo gmdate('Y-m-d H:i:s');
这个输出的是UTC时间,不是北京时间。
解决: 用date()不要用gmdate(),或者自己加8小时。
坑5:数据库时间不对
PHP时区对了,但MySQL里的时间不对。
原因: MySQL自己也有时区设置。
解决: MySQL连接后设时区:
SET time_zone = '+8:00';
或者改my.cnf:
default-time-zone = '+8:00'
七、常用日期函数
获取当前时间
date_default_timezone_set('Asia/Shanghai');
// 当前日期时间
echo date('Y-m-d H:i:s');
// 今天
echo date('Y-m-d');
// 现在几点
echo date('H:i:s');
// 时间戳
echo time();
时间戳转日期
$time = time();
echo date('Y-m-d H:i:s', $time);
日期转时间戳
$str = '2026-10-09 08:30:00';
echo strtotime($str);
几天以后
// 3天后
echo date('Y-m-d', strtotime('+3 days'));
// 下周
echo date('Y-m-d', strtotime('next week'));
八、框架里的设置
ThinkPHP
在config/app.php里:
'default_timezone' => 'Asia/Shanghai',
Laravel
在config/app.php里:
'timezone' => 'Asia/Shanghai',
总结
记住:
- PHP默认UTC时区,差8小时
- 改php.ini里的
date.timezone = Asia/Shanghai - 改完重启php-fpm
- 或者代码里
date_default_timezone_set('Asia/Shanghai') - CLI和FPM可能用不同的php.ini
最常用:
date_default_timezone_set('Asia/Shanghai');
echo date('Y-m-d H:i:s');
遇到问题加QQ23979811 协助处理
PHP session不生效,cookie配置检查
前言
PHP用session存用户登录状态,结果刷新页面就掉了?session不生效太常见了,一般就是cookie配置不对、路径不对、或者目录没权限。本文讲怎么排查和解决。
一、session工作原理
简单说:
- 浏览器访问网站
- PHP创建session,生成session_id
- session_id通过cookie发给浏览器
- 浏览器每次请求带上这个cookie
- PHP根据cookie里的session_id找到对应数据
核心: session靠cookie传session_id,cookie不行session就没了。
二、常见原因
原因1:cookie没设置对
session.cookie_path、session.cookie_domain配置不对。
原因2:session保存目录没权限
session文件存不进去。
原因3:session.auto_start没开
或者开了但和代码冲突。
原因4:跨域问题
主域和子域之间cookie不共享。
原因5:HTTPS下cookie的secure问题
cookie设了secure,但用http访问。
三、排查步骤
第一步:检查session有没有启动
<?php
session_start();
// 存个值
$_SESSION['test'] = 'hello';
// 看有没有
echo '<pre>';
print_r($_SESSION);
echo '</pre>';
刷新页面,看test还在不在。
第二步:看cookie里有没有PHPSESSID
按F12打开浏览器开发者工具 → Application → Cookies。
看有没有 PHPSESSID 这个cookie。
没有的话: 就是cookie没设对。
有的话: 那就是session保存目录的问题。
四、php.ini配置检查
找到php.ini位置
php --ini
关键配置
;session保存目录
session.save_path = "/var/lib/php/session"
;是否自动启动
session.auto_start = 0
;cookie路径
session.cookie_path = /
;cookie域名
session.cookie_domain =
;cookie有效期(秒)
session.cookie_lifetime = 0
;是否仅https
session.cookie_secure = 0
;是否仅http(防XSS)
session.cookie_httponly = 1
五、常见坑和解决
坑1:session目录没权限
session文件写不进去。
检查:
ls -ld /var/lib/php/session
解决:
chmod 777 /var/lib/php/session
chown apache:apache /var/lib/php/session
或者看php-fpm用的哪个用户。
坑2:cookie_path不对
比如网站装在子目录,但cookie_path设成了别的。
解决:
ini_set('session.cookie_path', '/');
session_start();
坑3:cookie_domain不对
多域名访问时,cookie只在一个域名下有效。
解决:
ini_set('session.cookie_domain', '.example.com');
注意前面有个点,表示所有子域名都生效。
坑4:HTTPS下设了secure
cookie_secure=1表示只在https下传cookie,用http访问就没了。
解决:
session.cookie_secure = 0
或者全站用https。
坑5:session.auto_start冲突
代码里已经session_start()了,但php.ini里也开了auto_start,冲突。
解决:
session.auto_start = 0
六、代码里动态设置
不想改php.ini,可以在代码里设:
<?php
// 必须在session_start()之前
ini_set('session.cookie_path', '/');
ini_set('session.cookie_domain', '.example.com');
ini_set('session.cookie_lifetime', 0);
session_start();
$_SESSION['user_id'] = 1;
七、检查session是否生效
写个测试页:
<?php
session_start();
// 第一次访问,设置
if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 1;
} else {
$_SESSION['count']++;
}
echo "访问次数: " . $_SESSION['count'];
echo "<br>";
echo "Session ID: " . session_id();
echo "<br>";
echo "Session 文件: " . session_save_path() . "/sess_" . session_id();
刷新页面,数字应该一直涨。
如果不涨: 就是session没存住。
八、常见场景
场景1:登录后刷新就掉
原因: cookie_path或domain不对。
解决: 检查cookie配置。
场景2:换了个子域名就掉
比如 www.example.com 登录了,跳到 bbs.example.com 就没登录。
原因: cookie_domain没设成 .example.com。
解决:
ini_set('session.cookie_domain', '.example.com');
场景3:从http跳到https就掉
原因: cookie_secure=1,但http不带cookie。
解决: 全站用https,或者关掉secure。
场景4:session目录满了
原因: session文件太多。
解决: 清理旧session,或者设gc_maxlifetime。
session.gc_maxlifetime = 1440
九、快速排查清单
- F12看cookie里有没有PHPSESSID
- 没有 → 查cookie_path、cookie_domain、cookie_secure
- 有 → 查session目录权限
- 都没问题 → 看session.auto_start有没有冲突
- 跨域问题 → cookie_domain设成 .主域名
总结
记住:
- session靠cookie传session_id
- 先看cookie里有没有PHPSESSID
- 没有就查cookie配置
- 有就查session目录权限
- 跨域名记得设cookie_domain=.主域名
最常用:
ini_set('session.cookie_path', '/');
session_start();
遇到问题加QQ23979811 协助处理