PHP 生成静态页面快速上手指南(完整版)

PHP的生态非常丰富,但生成静态页面依然是最常用的功能点之一。本文整理了最实用的几种实现方式。


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

PHP 实现简单缓存机制避坑指南(完整版)

如果你正在学习PHP,那么实现简单缓存机制几乎是绕不开的一个环节。本文从原理到实践,带你快速上手。


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

PHP 内容关键词高亮案例讲解(完整版)

做PHP开发这几年,内容关键词高亮是被身边同事问得最多的问题之一。今天把答案整理出来,供大家参考。


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

PHP Cookie读写操作实战详解(2026最新整理)

最近在项目里又遇到了Cookie读写操作的典型问题,索性把解决方法整理成这篇文章,希望能帮到遇到同样问题的同学。


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

PHP 数组转树形结构快速上手指南(建议收藏)

数组转树形结构是PHP开发者必须掌握的基础技能之一。掌握它不仅能提升开发效率,还能避免很多隐蔽的 Bug。


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

PHP 判断请求是否来自手机避坑指南

很多初学者在学习PHP时,都会遇到判断请求是否来自手机相关的问题。网上的资料零散且质量参差不齐,本文尝试用一篇文章把判断请求是否来自手机讲清楚。


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

PHP 生成随机字符串常见问题汇总(新手必看)

本文面向PHP初学者,用通俗的语言讲解生成随机字符串,读完即可动手实践。


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

PHP 判断请求是否来自手机入门教程

判断请求是否来自手机是PHP开发者必须掌握的基础技能之一。掌握它不仅能提升开发效率,还能避免很多隐蔽的 Bug。


emer 发布于  2026-10-10 08:23 

PHP 批量导入CSV数据案例讲解(2026最新整理)

最近在项目里又遇到了批量导入CSV数据的典型问题,索性把解决方法整理成这篇文章,希望能帮到遇到同样问题的同学。


emer 发布于  2026-10-10 08:21 

PHP 身份证号码校验进阶技巧分享(附代码示例)

PHP的生态非常丰富,但身份证号码校验依然是最常用的功能点之一。本文整理了最实用的几种实现方式。


emer 发布于  2026-10-10 08:20 

PHP 字符串反转快速上手指南(建议收藏)

很多线上 Bug 的根源,都在于对字符串反转的理解不够深入。本文从常见错误出发,反推正确的使用姿势。


emer 发布于  2026-10-10 08:16 

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个必须掌握的数组函数:

  1. array_column — 取某一列
  2. array_map — 遍历处理
  3. array_filter — 过滤
  4. array_merge — 合并
  5. in_array — 判断存在
  6. array_keys — 取键
  7. array_values — 取值
  8. array_sum — 求和
  9. count — 统计长度
  10. explode/implode — 字符串数组互转

记住:

  • 天天用的就是这10个
  • 写代码前先想想有没有现成函数
  • 别自己写循环,用内置函数更快

遇到问题加QQ23979811 协助处理


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

PHP实现简单分页,SQL limit用法

前言

做网站列表页,数据太多一页显示不完,需要分页。PHP+MySQL做分页很简单,就是用SQL的limit。本文写个完整的分页示例,直接拿去用。

一、分页原理

分页就两件事:

  1. 查总条数:看看一共有多少条数据
  2. 查当前页数据:用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";

总结

记住:

  1. 分页就是 LIMIT 偏移量, 每页条数
  2. 偏移量 = (当前页 - 1) × 每页条数
  3. 先查总条数,再算总页数
  4. 页码要做限制,不能超出范围
  5. 带条件查询时,总条数也要带条件

核心公式:

$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$pageSize = 10;
$offset = ($page - 1) * $pageSize;

$sql = "SELECT * FROM users LIMIT $offset, $pageSize";

遇到问题加QQ23979811 协助处理


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

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/ 没权限,还是进不去。

解决: 从根开始一层一层检查。

总结

记住:

  1. 报错先看是哪个文件/目录
  2. 看文件所有者是不是PHP的用户(一般是www)
  3. 不是就 chown -R www:www 目录
  4. 还不行就检查SELinux
  5. 尽量改所有者,不要随便777

最常用:

chown -R www:www /你的目录/

遇到问题加QQ23979811 协助处理


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

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'];
}

总结

记住:

  1. file_get_contents不支持https,是openssl扩展没装
  2. 装openssl扩展,重启php-fpm
  3. 或者关SSL验证临时解决
  4. 最推荐:改用curl,比file_get_contents好用多了

推荐方案:

直接用curl,别用file_get_contents搞https。

遇到问题加QQ23979811 协助处理


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

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);
// &lt;script&gt;alert(1)&lt;/script&gt;

十、常见坑

坑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个:

  1. strlen() 字符串长度(字节)
  2. mb_strlen() 字符长度(中文友好)
  3. substr() 截取字符串
  4. strpos() 查找位置
  5. str_replace() 替换
  6. explode() 字符串转数组
  7. implode() 数组转字符串
  8. trim() 去空格
  9. strtoupper() / strtolower() 大小写
  10. str_pad() 填充

记住:

  • 中文用mb_*系列函数
  • 查找用=== false判断
  • 数组字符串互转用explode/implode

遇到问题加QQ23979811 协助处理


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

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个:

  1. count() 数组长度
  2. in_array() 值是否在数组里
  3. array_key_exists() key是否存在
  4. array_unique() 去重
  5. array_merge() 合并
  6. array_slice() 取一段
  7. array_sum() 求和
  8. explode() 字符串转数组
  9. implode() 数组转字符串
  10. array_column() 取二维数组某一列

遇到问题加QQ23979811 协助处理


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

PHP cURL请求超时,超时时间设置

前言

PHP用cURL请求接口,结果一直卡住或者超时?cURL默认没有超时时间,对方服务器慢的话能卡你几分钟。必须设超时,不然整个脚本都堵死了。本文讲怎么设。

一、为什么要设超时

不设超时的后果:

  1. 对方服务器挂了,你的脚本一直等着
  2. 对方服务器慢,你的脚本卡很久
  3. 高并发下,进程都堵在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分钟总超时

总结

记住:

  1. cURL一定要设超时,不然卡死
  2. CURLOPT_CONNECTTIMEOUT 连接超时(秒)
  3. CURLOPT_TIMEOUT 总超时(秒)
  4. 单位是秒,不是毫秒
  5. 超时了用 curl_errno 判断是不是28

最常用:

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);

遇到问题加QQ23979811 协助处理


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

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',

总结

记住:

  1. PHP默认UTC时区,差8小时
  2. 改php.ini里的 date.timezone = Asia/Shanghai
  3. 改完重启php-fpm
  4. 或者代码里 date_default_timezone_set('Asia/Shanghai')
  5. CLI和FPM可能用不同的php.ini

最常用:

date_default_timezone_set('Asia/Shanghai');
echo date('Y-m-d H:i:s');

遇到问题加QQ23979811 协助处理


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

PHP session不生效,cookie配置检查

前言

PHP用session存用户登录状态,结果刷新页面就掉了?session不生效太常见了,一般就是cookie配置不对、路径不对、或者目录没权限。本文讲怎么排查和解决。

一、session工作原理

简单说:

  1. 浏览器访问网站
  2. PHP创建session,生成session_id
  3. session_id通过cookie发给浏览器
  4. 浏览器每次请求带上这个cookie
  5. 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

九、快速排查清单

  1. F12看cookie里有没有PHPSESSID
  2. 没有 → 查cookie_path、cookie_domain、cookie_secure
  3. 有 → 查session目录权限
  4. 都没问题 → 看session.auto_start有没有冲突
  5. 跨域问题 → cookie_domain设成 .主域名

总结

记住:

  1. session靠cookie传session_id
  2. 先看cookie里有没有PHPSESSID
  3. 没有就查cookie配置
  4. 有就查session目录权限
  5. 跨域名记得设cookie_domain=.主域名

最常用:

ini_set('session.cookie_path', '/');
session_start();

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:27 
'); })();