PHP Composer 入门指南
PHP Composer 入门指南
Composer 是 PHP 的依赖管理工具,现在做 PHP 开发不用 Composer 几乎不可能。
1. 安装 Composer
# Linux/Mac
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
# Windows 直接下载安装包
# https://getcomposer.org/Composer-Setup.exe
验证安装:
composer -V
2. 换国内镜像(必须)
官方镜像太慢,一定要换国内镜像:
# 阿里云镜像
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
# 腾讯云镜像
composer config -g repo.packagist composer https://mirrors.tencent.com/composer/
3. 初始化项目
# 进入项目目录
cd myproject
# 初始化 composer.json
composer init
或者手动创建 composer.json:
{
"name": "my/project",
"require": {
"monolog/monolog": "^2.0"
}
}
4. 常用命令
# 安装依赖
composer install
# 更新依赖
composer update
# 安装某个包
composer require monolog/monolog
# 卸载某个包
composer remove monolog/monolog
# 自动加载
composer dump-autoload
5. 自动加载
// 引入自动加载文件
require __DIR__ . '/vendor/autoload.php';
// 然后就可以直接用第三方库了
$log = new Monolog\Logger('name');
6. composer.json 详解
{
"name": "项目名",
"description": "项目描述",
"type": "project",
"require": {
"php": ">=7.4",
"monolog/monolog": "^2.0",
"guzzlehttp/guzzle": "^7.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
7. 版本号说明
^2.0:兼容 2.0 到 3.0(不包含 3.0)~2.0:兼容 2.0 到 2.1(不包含 2.1)*:任意版本2.0.*:2.0.x 版本
总结
Composer 核心:
- 安装:一行命令
- 换镜像:必须换国内源
- 常用:install / update / require / remove
- 自动加载:require vendor/autoload.php
搞懂这些,Composer 日常开发就够用了。
PHP 字符串处理技巧
PHP 字符串处理常用技巧
字符串处理是 PHP 开发最常用的操作,掌握这些函数,写代码效率翻倍。
1. 截取字符串
// 截取前 5 个字符
substr("hello world", 0, 5); // hello
// 截取从第 6 个开始到结尾
substr("hello world", 6); // world
// 中文字符串截取(用 mb_)
mb_substr("你好世界", 0, 2, "UTF-8"); // 你好
注意:中文要用 mb_substr,不能用 substr,否则会乱码。
2. 替换字符串
// str_replace(替换所有)
str_replace("world", "PHP", "hello world"); // hello PHP
// str_ireplace(不区分大小写)
str_ireplace("WORLD", "PHP", "hello world");
// preg_replace(正则替换)
preg_replace("/\d/", "", "abc123def"); // abcdef
3. 查找字符串
// 查找位置(找不到返回 false)
strpos("hello world", "world"); // 6
// 查找最后出现位置
strrpos("hello world hello", "hello"); // 12
// 判断是否包含(PHP 8+)
str_contains("hello world", "world"); // true
// 判断开头
str_starts_with("hello world", "hello"); // true
// 判断结尾
str_ends_with("hello world", "world"); // true
4. 格式化字符串
// 去除首尾空格
trim(" hello "); // hello
// 只去左边/右边
ltrim(" hello"); // hello
rtrim("hello "); // hello
// 转小写/大写
strtolower("Hello"); // hello
strtoupper("hello"); // HELLO
// 首字母大写
ucfirst("hello"); // Hello
// 每个单词首字母大写
ucwords("hello world"); // Hello World
5. 字符串和数组转换
// 字符串转数组
explode(",", "a,b,c"); // ["a","b","c"]
// 数组转字符串
implode(",", ["a","b","c"]); // a,b,c
// 重复字符串
str_repeat("ab", 3); // ababab
// 字符串长度
strlen("hello"); // 5
mb_strlen("你好", "UTF-8"); // 2
6. 常用技巧
// 生成随机字符串
substr(md5(time()), 0, 8);
// 脱敏手机号
substr_replace("13812345678", "****", 3, 4); // 138****5678
// 截取摘要(带省略号)
mb_strlen($text) > 10 ? mb_substr($text, 0, 10) . "..." : $text;
// 清除 HTML 标签
strip_tags("<p>hello</p>"); // hello
总结
PHP 字符串处理核心函数:
- 截取:substr / mb_substr
- 替换:str_replace / preg_replace
- 查找:strpos / str_contains
- 格式化:trim / strtolower / ucwords
- 转换:explode / implode
记住这些,日常开发基本够用了。
PHP 数组操作技巧
PHP 数组操作技巧
数组是 PHP 最常用的数据结构,掌握这些技巧,写代码效率翻倍。
1. 数组合并
// 索引数组合并
$arr1 = [1, 2];
$arr2 = [3, 4];
$result = array_merge($arr1, $arr2); // [1,2,3,4]
// 关联数组合并,后面覆盖前面
$arr1 = ['a' => 1, 'b' => 2];
$arr2 = ['b' => 3, 'c' => 4];
$result = array_merge($arr1, $arr2); // ['a'=>1, 'b'=>3, 'c'=>4]
2. 数组去重
// 一维数组去重
$arr = [1, 2, 2, 3, 3, 3];
$result = array_unique($arr); // [1,2,3]
// 二维数组按字段去重
$users = [
['id' => 1, 'name' => '张三'],
['id' => 2, 'name' => '李四'],
['id' => 1, 'name' => '张三'],
];
$ids = array_column($users, 'id');
$unique = array_unique($ids);
3. 数组排序
// 升序
sort($arr);
// 降序
rsort($arr);
// 按键排序
ksort($arr);
// 按值排序,保留键
asort($arr);
4. 数组过滤
// 过滤空值
$arr = [1, 0, '', null, 'a'];
$result = array_filter($arr); // [1, 'a']
// 自定义过滤
$result = array_filter($arr, function($value) {
return $value > 2;
});
5. 数组取值
// 取某一列
$names = array_column($users, 'name');
// 取交集
$arr1 = [1, 2, 3];
$arr2 = [2, 3, 4];
$result = array_intersect($arr1, $arr2); // [2,3]
// 取差集
$result = array_diff($arr1, $arr2); // [1]
6. 常用技巧
// 判断键是否存在
isset($arr['key']);
array_key_exists('key', $arr);
// 随机取一个
$value = array_rand($arr);
// 打乱数组
shuffle($arr);
// 数组合并成字符串
$str = implode(',', $arr);
// 字符串转数组
$arr = explode(',', $str);
总结
PHP 数组函数非常丰富,常用的就这些:合并、去重、排序、过滤、取值。把这些记熟,日常开发基本够用了。
PHP 安全最佳实践
PHP 开发必须注意的安全问题
PHP 开发最容易出安全问题,几个常见坑一定要避开。
1. SQL 注入
最常见的安全漏洞。用户输入拼接到 SQL 里,被注入恶意 SQL。
解决方案:用预处理语句
// 不要这样
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
// 要这样
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
2. XSS 跨站脚本
用户输入直接输出到页面,被注入恶意 JS。
解决方案:输出时转义
echo htmlspecialchars($user_input);
3. CSRF 跨站请求伪造
别人伪造你的请求操作。
解决方案:加 Token 验证
// 表单里加 Token
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['token']; ?>">
// 后端验证
if ($_POST['csrf_token'] !== $_SESSION['token']) {
die('CSRF 攻击');
}
4. 文件上传漏洞
用户上传 PHP 文件,直接拿到 webshell。
解决方案:
- 限制文件类型
- 重命名文件
- 上传目录不执行 PHP
5. 密码明文存储
数据库被拖库,密码全泄露。
解决方案:用 password_hash
$hash = password_hash($password, PASSWORD_DEFAULT);
// 验证
password_verify($password, $hash);
6. 错误信息泄露
生产环境把报错直接展示给用户,暴露路径和代码。
解决方案:关闭 display_errors
display_errors = Off
log_errors = On
总结
PHP 安全的核心:用户输入都不可信,所有输出都要转义,数据库操作用预处理。安全开发意识比工具更重要。
PHP 8.0 新特性
PHP 8.0 有哪些重磅更新
PHP 8.0 是一个大版本更新,带来了很多革命性的特性。
1. JIT 编译
PHP 8.0 引入了 JIT(即时编译),性能大幅提升,计算密集型场景性能翻倍。
2. 命名参数
array_filter(array: $array, callback: fn($x) => $x > 0);
不用按顺序传参,写参数名就行,可读性更好。
3. 枚举 Enum
enum Status: string
{
case Pending = 'pending';
case Active = 'active';
case Rejected = 'rejected';
}
4. 构造函数属性提升
class User {
public function __construct(
public int $id,
public string $name
) {}
}
不用再写 $this->id = $id 了,构造函数直接定义属性。
5. 空安全运算符
$user?->getAddress()?->city;
不用层层判断 null,有 null 直接返回 null。
6. Match 表达式
$result = match($status) {
200 => '成功',
404 => '未找到',
default => '未知'
};
比 switch 更简洁,还能直接赋值。
7. 联合类型
function foo(int|string $input) {}
参数可以是多种类型。
总结
PHP 8.0 是现代 PHP 的起点,类型系统更完善,语法更现代。新项目强烈推荐用 PHP 8.0+。
PHP 7.4 新特性
PHP 7.4 有哪些实用新特性
PHP 7.4 是 7.x 系列的最后一个版本,带来了不少语法糖和性能优化,至今还有很多项目在用。
1. 类型属性
类的属性可以声明类型了:
class User {
public int $id;
public string $name;
public ?string $email;
}
之前属性不能写类型,只能靠 PHPDoc 注释,现在直接写在代码里,IDE 提示更准确。
2. 箭头函数
支持短闭包:
$users = array_map(fn($user) => $user['name'], $users);
比传统的 function ($user) use (...) {} 简洁多了,自动捕获外层变量。
3. 空合并赋值
$data['user'] ??= [];
相当于:
if (!isset($data['user'])) {
$data['user'] = [];
}
4. 数组展开
$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$merged = [...$arr1, ...$arr2];
5. 数字字面量分隔
$amount = 1_000_000;
$price = 9_999.99;
大数字用下划线分隔,可读性提升。
6. 性能提升
PHP 7.4 比 7.3 性能提升约 10%,内存占用也更少。
总结
PHP 7.4 虽然不是大版本,但实用特性很多,尤其是类型属性和箭头函数,写起来舒服不少。新项目建议直接上 PHP 8+,老项目升级到 7.4 也很稳妥。
PHP Session 与 Cookie:会话管理实战指南
前言
HTTP 是无状态的,你每次请求服务器都不认识你。Session 和 Cookie 就是用来解决这个问题的。本文讲清楚 PHP 中 Session 和 Cookie 的用法。
一、Cookie 是什么
Cookie 是存在浏览器里的小数据,每次请求自动带给服务器。
设置 Cookie
setcookie("username", "张三", time() + 3600, "/");
参数说明:
- 第一个参数:名称
- 第二个参数:值
- 第三个参数:过期时间(时间戳)
- 第四个参数:路径
读取 Cookie
echo $_COOKIE['username'];
删除 Cookie
setcookie("username", "", time() - 3600, "/");
把过期时间设成过去就行。
二、Session 是什么
Session 是存在服务器上的数据,通过 Session ID 识别用户。
启动 Session
session_start();
注意:必须在输出之前调用,不然会报错。
存数据
$_SESSION['user_id'] = 1;
$_SESSION['username'] = '张三';
读数据
echo $_SESSION['username'];
删除单个数据
unset($_SESSION['user_id']);
销毁 Session
session_destroy();
三、Session 和 Cookie 的区别
| 对比 | Cookie | Session |
|---|---|---|
| 存储位置 | 浏览器 | 服务器 |
| 大小限制 | 4KB | 无限制 |
| 安全性 | 低(用户能改) | 高 |
| 生命周期 | 可设置 | 默认浏览器关闭就没 |
四、登录功能示例
登录页
<?php
session_start();
if ($_POST) {
$username = $_POST['username'];
$password = $_POST['password'];
// 验证用户名密码
if ($username == 'admin' && $password == '123456') {
$_SESSION['user_id'] = 1;
$_SESSION['username'] = $username;
header('Location: dashboard.php');
exit;
} else {
echo "用户名或密码错误";
}
}
?>
判断是否登录
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
?>
退出登录
<?php
session_start();
session_destroy();
header('Location: login.php');
exit;
?>
五、Session 配置
修改 Session 有效期
ini_set('session.gc_maxlifetime', 3600); // 1小时
session_start();
修改 Session 存储路径
session_save_path('/tmp/sessions');
session_start();
用 Redis 存储 Session
生产环境推荐用 Redis 存 Session,默认文件存储性能差。
php.ini 配置:
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379"
六、安全问题
1. Session 劫持
别人拿到你的 Session ID,就能冒充你。
防护:
- 用 HTTPS
- Session ID 定期更换
- 绑定 IP(不太推荐,IP 会变)
2. Cookie 劫持
别人拿到你的 Cookie,就能冒充你。
防护:
- 设置 HttpOnly,JS 读不到
- 设置 Secure,只在 HTTPS 下传
- 设置 SameSite,防止 CSRF
setcookie("token", $token, [
'expires' => time() + 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]);
3. CSRF 攻击
别人伪造你的请求。
防护:
- 表单加 CSRF Token
- 验证 Referer
七、常见坑
- session_start() 在输出之后:报错 headers already sent
- Session 没销毁:退出登录后还能访问
- Cookie 大小限制:别存太多东西,超过 4KB 就丢
- Session 文件太多:默认文件存储,大了要清理
八、最佳实践
- 敏感数据用 Session,别存 Cookie
- Cookie 加 HttpOnly 和 Secure
- 生产环境用 Redis 存 Session
- 登录成功后重新生成 Session ID,防止固定攻击
// 登录成功后
session_regenerate_id(true);
总结
Session 和 Cookie 的核心要点:
- Cookie 存在浏览器,Session 存在服务器
- session_start() 必须在输出之前调用
- Session 更安全,Cookie 更轻量
- 生产环境用 Redis 存 Session
- Cookie 加 HttpOnly 和 Secure,防劫持
登录、权限、购物车,都离不开 Session 和 Cookie。搞懂这俩,PHP 后端才算入门。
PHP Composer 入门:依赖管理完全指南
前言
Composer 是 PHP 的依赖管理工具,就像 Node 的 npm、Python 的 pip。现在写 PHP 项目,不会 Composer 根本没法干活。本文讲清楚 Composer 的核心用法。
一、安装 Composer
Linux / Mac
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
Windows
去官网下载安装包,一路下一步就行。
验证安装
composer -V
二、初始化项目
# 在项目目录下
composer init
会问你几个问题,一路回车就行,然后会生成 composer.json 文件。
composer.json 长什么样
{
"name": "yourname/project",
"description": "项目描述",
"require": {
"monolog/monolog": "^2.0"
}
}
三、安装依赖
1. 安装所有依赖
composer install
会根据 composer.lock 文件安装指定版本的依赖。
2. 添加新依赖
composer require monolog/monolog
会自动加到 composer.json 里,然后安装。
3. 安装指定版本
composer require monolog/monolog:^2.0
版本号说明
^2.0:2.0 到 3.0 之前(不包括 3.0)~2.0:2.0 到 2.1 之前(不包括 2.1)*:任意版本2.0.*:2.0 开头的版本
四、自动加载
Composer 最爽的就是自动加载,不用自己写 require。
引入自动加载
<?php
require 'vendor/autoload.php';
// 直接用类,不用 require
$log = new Monolog\Logger('name');
PSR-4 自动加载
在 composer.json 里配置:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
然后执行:
composer dump-autoload
这样 App\User 类就会从 src/User.php 加载。
五、常用命令
| 命令 | 作用 |
|---|---|
composer install |
安装所有依赖 |
composer update |
更新所有依赖 |
composer require 包名 |
添加依赖 |
composer remove 包名 |
删除依赖 |
composer dump-autoload |
重新生成自动加载 |
composer show |
查看已安装的包 |
composer self-update |
更新 Composer 本身 |
六、开发环境依赖
有些包只在开发时用,比如测试工具:
composer require --dev phpunit/phpunit
线上部署的时候,不安装开发依赖:
composer install --no-dev
七、中国镜像
国外源太慢,换中国镜像:
# 阿里云镜像
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
# 腾讯云镜像
composer config -g repo.packagist composer https://mirrors.tencent.com/composer/
换完之后速度飞起来。
八、composer.lock 的作用
- 记录当前安装的依赖版本
- 团队成员 install 的时候,装的版本一模一样
- 一定要提交到 Git
git add composer.json composer.lock
git commit -m "update composer"
九、常见坑
- 没装 composer.lock:团队成员装的版本不一样,出 bug
- 自动加载不生效:改了 autoload 配置,忘了 dump-autoload
- 版本冲突:两个包依赖同一个库的不同版本
- 国内太慢:不换镜像,下载半天
十、最佳实践
- composer.json 和 composer.lock 都提交 Git
- 线上用 install --no-dev,不要用 update
- 用中文镜像,加速下载
- 改了自动加载配置,记得 dump-autoload
总结
Composer 的核心要点:
- require 添加依赖,install 安装依赖
- vendor/autoload.php 是自动加载入口
- composer.lock 要提交 Git,保证版本一致
- 换国内镜像,速度快很多
- 开发依赖用 --dev,线上不装
学会 Composer,PHP 开发效率提升一倍。
PHP 数组常用操作:从排序到过滤的完整指南
前言
数组是 PHP 里最常用的数据结构,没有之一。PHP 提供了 100 多个数组函数,很多人只知道其中几个。本文整理最常用的数组操作。
一、创建数组
<?php
// 索引数组
$arr1 = [1, 2, 3, 4, 5];
// 关联数组
$arr2 = ['name' => '张三', 'age' => 18];
// 空数组
$arr3 = [];
二、添加元素
<?php
$arr = [1, 2, 3];
// 末尾添加
$arr[] = 4; // [1, 2, 3, 4]
array_push($arr, 5); // [1, 2, 3, 4, 5]
// 开头添加
array_unshift($arr, 0); // [0, 1, 2, 3, 4, 5]
// 关联数组添加
$user = ['name' => '张三'];
$user['age'] = 18; // ['name' => '张三', 'age' => 18]
三、删除元素
<?php
$arr = [1, 2, 3, 4, 5];
// 删除末尾元素
array_pop($arr); // [1, 2, 3, 4]
// 删除开头元素
array_shift($arr); // [2, 3, 4]
// 删除指定元素
unset($arr[1]); // [0 => 2, 2 => 4],注意索引不连续
四、查找元素
<?php
$arr = ['a', 'b', 'c'];
// 值是否存在
in_array('b', $arr); // true
// 键是否存在
$user = ['name' => '张三', 'age' => 18];
array_key_exists('name', $user); // true
// 查找值的键
array_search('b', $arr); // 1
五、数组长度
<?php
$arr = [1, 2, 3];
count($arr); // 3
sizeof($arr); // 3(count 的别名)
六、排序
索引数组排序
<?php
$arr = [3, 1, 2, 5, 4];
sort($arr); // [1, 2, 3, 4, 5] 升序
rsort($arr); // [5, 4, 3, 2, 1] 降序
关联数组排序
<?php
$age = ['张三' => 20, '李四' => 18, '王五' => 25];
asort($age); // 按值升序,保持键名
ksort($age); // 按键升序
arsort($age); // 按值降序
krsort($age); // 按键降序
自定义排序
<?php
$users = [
['name' => '张三', 'age' => 20],
['name' => '李四', 'age' => 18],
['name' => '王五', 'age' => 25],
];
// 按 age 升序
usort($users, function ($a, $b) {
return $a['age'] <=> $b['age'];
});
七、数组过滤
array_filter()
<?php
$arr = [1, 2, 3, 4, 5, 6];
// 过滤出偶数
$even = array_filter($arr, function ($n) {
return $n % 2 == 0;
});
// [2, 4, 6]
array_map()
<?php
$arr = [1, 2, 3, 4, 5];
// 每个元素乘以 2
$doubled = array_map(function ($n) {
return $n * 2;
}, $arr);
// [2, 4, 6, 8, 10]
array_walk()
<?php
$arr = ['a' => 1, 'b' => 2];
array_walk($arr, function (&$value, $key) {
$value = $value * 10;
});
// ['a' => 10, 'b' => 20]
八、数组合并
<?php
$arr1 = [1, 2];
$arr2 = [3, 4];
// 数组合并
$merged = array_merge($arr1, $arr2); // [1, 2, 3, 4]
// + 运算符(保留第一个数组的键)
$result = $arr1 + $arr2;
九、数组切片
<?php
$arr = [1, 2, 3, 4, 5];
// 从第 2 个开始取 3 个
$slice = array_slice($arr, 1, 3); // [2, 3, 4]
// 从末尾取
$last = array_slice($arr, -2); // [4, 5]
十、键值操作
<?php
$user = ['name' => '张三', 'age' => 18];
// 所有键
array_keys($user); // ['name', 'age']
// 所有值
array_values($user); // ['张三', 18]
// 翻转键值
array_flip($user); // ['张三' => 'name', 18 => 'age']
十一、常用操作速查表
| 操作 | 函数 |
|---|---|
| 数组长度 | count() |
| 查找值 | in_array() |
| 查找键 | array_key_exists() |
| 排序 | sort() / asort() / ksort() |
| 过滤 | array_filter() |
| 映射 | array_map() |
| 合并 | array_merge() |
| 切片 | array_slice() |
十二、常见坑
- unset 后索引不连续:用 array_values() 重新索引
- array_merge 和 + 的区别:数字键 array_merge 会重新索引,+ 不会
- in_array 严格模式:加第三个参数 true 防止类型转换
- sort 会改变原数组:不是返回新数组
总结
PHP 数组操作的核心要点:
- 增删改查:array_push/pop/unshift/shift/unset
- 查找判断:in_array/array_key_exists/array_search
- 排序:sort/asort/ksort/usort
- 处理:array_filter/array_map/array_walk
- 合并切片:array_merge/array_slice
数组用好了,PHP 开发效率能提升一倍。
PHP 字符串处理:常用函数和最佳实践指南
前言
字符串处理是 PHP 开发中最常用的操作之一。很多人写了很多年代码,却还是用错字符串函数。本文整理最常用的字符串处理函数和最佳实践。
一、字符串长度
strlen()
<?php
$str = "hello";
echo strlen($str); // 5
注意:strlen 按字节算,中文会出问题。
mb_strlen()
<?php
$str = "你好";
echo mb_strlen($str, 'UTF-8'); // 2
echo strlen($str); // 6(3个字节一个汉字)
记住:处理中文一定要用 mb_ 系列函数。
二、字符串查找
strpos()
查找字符串第一次出现的位置:
<?php
$str = "hello world";
$pos = strpos($str, "world");
echo $pos; // 6
坑: 如果没找到返回 false,如果找到的位置是 0,也返回 0。
<?php
// 错误写法
if (strpos($str, "hello")) {
// 如果 hello 在第 0 位,这里不会执行
}
// 正确写法
if (strpos($str, "hello") !== false) {
// 这样才对
}
strrpos()
查找字符串最后一次出现的位置。
stripos() / strripos()
不区分大小写的查找。
三、字符串替换
str_replace()
<?php
$str = "hello world";
echo str_replace("world", "PHP", $str); // hello PHP
数组替换:
<?php
echo str_replace(
["hello", "world"],
["hi", "PHP"],
"hello world"
); // hi PHP
substr_replace()
替换字符串的一部分:
<?php
echo substr_replace("hello world", "PHP", 6); // hello PHP
preg_replace()
正则替换:
<?php
// 把多个空格变成一个
echo preg_replace('/\s+/', ' ', "hello world"); // hello world
四、字符串截取
substr()
<?php
$str = "hello world";
echo substr($str, 0, 5); // hello
echo substr($str, 6); // world
中文截取要用 mb_substr():
<?php
$str = "你好世界";
echo mb_substr($str, 0, 2, 'UTF-8'); // 你好
五、字符串分割与连接
explode()
<?php
$str = "a,b,c,d";
$arr = explode(",", $str);
print_r($arr); // ['a', 'b', 'c', 'd']
implode() / join()
<?php
$arr = ['a', 'b', 'c'];
echo implode(",", $arr); // a,b,c
六、字符串去空格
trim()
<?php
$str = " hello ";
echo trim($str); // "hello",去掉两边空格
echo ltrim($str); // "hello ",去掉左边空格
echo rtrim($str); // " hello",去掉右边空格
七、大小写转换
<?php
$str = "Hello World";
echo strtolower($str); // hello world
echo strtoupper($str); // HELLO WORLD
echo ucfirst($str); // Hello world
echo ucwords($str); // Hello World
八、HTML 转义
htmlspecialchars()
输出到 HTML 时一定要转义,防止 XSS:
<?php
$str = "<script>alert('xss')</script>";
echo htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
// 输出 <script>alert('xss')</script>
htmlentities()
更彻底的转义,把所有 HTML 都转义。
九、JSON 处理
json_encode()
<?php
$arr = ['name' => '张三', 'age' => 18];
echo json_encode($arr, JSON_UNESCAPED_UNICODE);
// {"name":"张三","age":18}
记住:加 JSON_UNESCAPED_UNICODE,不然中文会变成 \uXXXX。
json_decode()
<?php
$json = '{"name":"张三","age":18}';
$obj = json_decode($json); // 对象
$arr = json_decode($json, true); // 数组
十、常见坑
- strpos 用 == 判断:位置 0 和 false 会混淆
- 中文用 strlen/substr:按字节算,中文会乱码
- 输出 HTML 不转义:XSS 漏洞
- json_encode 不加 JSON_UNESCAPED_UNICODE:中文变 \uXXXX
- 字符串拼接用 . 不用 +:PHP 是 . 号
总结
PHP 字符串处理的核心要点:
- 中文用 mb_ 系列函数,不要用 strlen/substr
- strpos 用 !== false 判断,不要用 ==
- 输出 HTML 一定要 htmlspecialchars 转义
- json_encode 加 JSON_UNESCAPED_UNICODE
- 字符串拼接用 . 号,不要用 + 号
掌握这些,字符串处理就不会出大问题了。
PHP 错误与异常处理:从 die 到优雅的错误处理
前言
很多 PHP 新手处理错误就是 die() 或者 exit(),页面直接白屏,用户体验极差。本文讲清楚 PHP 的错误和异常处理机制,帮你写出更健壮的代码。
一、错误 vs 异常
很多人搞不清错误和异常的区别:
- 错误:PHP 本身的问题,比如语法错误、调用不存在的函数
- 异常:程序逻辑上的问题,比如数据库连不上、文件不存在
PHP 7 以后,很多错误也变成了异常,可以用 try-catch 捕获。
二、错误级别
PHP 有很多错误级别:
E_ERROR 致命错误,脚本停止运行
E_WARNING 警告,脚本继续运行
E_NOTICE 注意,一般不影响运行
E_PARSE 语法解析错误
E_DEPRECATED 过时提醒
开发环境和生产环境的配置不一样:
# 开发环境:显示所有错误
display_errors = On
error_reporting = E_ALL
# 生产环境:不显示错误,只记录日志
display_errors = Off
log_errors = On
error_reporting = E_ALL
三、异常处理基础
try-catch-finally
<?php
try {
// 可能出错的代码
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
} catch (PDOException $e) {
// 捕获异常
echo "数据库连接失败:" . $e->getMessage();
} finally {
// 不管有没有异常都会执行
echo "执行完毕";
}
抛出异常
<?php
function divide($a, $b)
{
if ($b == 0) {
throw new Exception("除数不能为 0");
}
return $a / $b;
}
try {
echo divide(10, 0);
} catch (Exception $e) {
echo "错误:" . $e->getMessage();
}
四、自定义异常类
<?php
class DbException extends Exception
{
public function errorMessage()
{
return "数据库错误:" . $this->getMessage();
}
}
class UserException extends Exception
{
public function errorMessage()
{
return "用户错误:" . $this->getMessage();
}
}
// 使用
try {
if (!$user) {
throw new UserException("用户不存在");
}
} catch (UserException $e) {
echo $e->errorMessage();
} catch (DbException $e) {
echo $e->errorMessage();
} catch (Exception $e) {
echo "其他错误:" . $e->getMessage();
}
五、错误处理器
把 PHP 错误转换成异常,统一处理:
<?php
// 自定义错误处理器
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
// 把错误转换成异常抛出
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
try {
// 这个本来是 warning,现在会被捕获
$result = 10 / 0;
} catch (ErrorException $e) {
echo "错误:" . $e->getMessage();
}
六、异常处理器
兜底处理,最后一道防线:
<?php
// 自定义异常处理器
set_exception_handler(function ($e) {
echo "未捕获的异常:" . $e->getMessage();
// 记录日志
error_log($e->getMessage());
});
// 没有 try-catch 的异常会走这里
throw new Exception("爆炸了");
七、常见错误处理方式对比
| 方式 | 问题 |
|---|---|
die() / exit() |
页面白屏,用户体验差 |
echo 错误信息 |
代码混在一起,不好维护 |
| 错误码返回 | 调用方需要判断错误码 |
| 异常处理 | 代码清晰,错误统一管理 |
八、最佳实践
- 生产环境不要显示错误:会暴露服务器信息
- 一定要记录日志:方便排查问题
- 用异常处理替代 die():代码更优雅
- 自定义异常类:区分不同类型的错误
- 兜底异常处理器:防止未捕获的异常白屏
九、日志记录
<?php
try {
// 可能出错的代码
} catch (Exception $e) {
// 记录日志
error_log(
date('Y-m-d H:i:s') . " 错误:" . $e->getMessage() . "\n",
3,
'/var/log/php/error.log'
);
// 给用户友好提示
echo "系统繁忙,请稍后再试";
}
总结
PHP 错误处理的核心思路:
- 开发环境显示错误,生产环境隐藏错误
- 用 try-catch 捕获异常,不要用 die()
- 自定义异常类,区分不同错误类型
- 设置错误处理器,把错误转成异常
- 一定要记录日志,方便排查问题
掌握这些,你的 PHP 代码会健壮很多。
PHP 设计模式:单例、工厂、观察者实战详解
前言
设计模式不是八股文,它是前人踩坑踩出来的最佳实践。PHP 开发中最常用的三个设计模式:单例、工厂、观察者。本文用最直白的方式讲清楚。
一、单例模式(Singleton)
什么时候用
全局只需要一个实例的对象,比如:
- 数据库连接
- 配置管理
- 日志对象
代码实现
<?php
class Database
{
private static $instance = null;
private $conn;
// 构造函数私有化,防止外部 new
private function __construct()
{
$this->conn = mysqli_connect('localhost', 'user', 'pass', 'db');
}
// 禁止克隆
private function __clone() {}
// 禁止反序列化
public function __wakeup()
{
throw new Exception("不能反序列化单例");
}
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection()
{
return $this->conn;
}
}
// 使用
$db = Database::getInstance();
$conn = $db->getConnection();
为什么要私有化构造函数
如果构造函数是 public 的,外面就能 new Database(),那就不是单例了。
二、工厂模式(Factory)
什么时候用
需要根据不同条件创建不同对象的时候,比如:
- 根据支付类型创建不同的支付对象(微信、支付宝、银行卡)
- 根据数据库类型创建不同的数据库对象(MySQL、Redis)
简单工厂
<?php
interface Payment
{
public function pay($amount);
}
class WechatPay implements Payment
{
public function pay($amount)
{
echo "微信支付 {$amount} 元";
}
}
class Alipay implements Payment
{
public function pay($amount)
{
echo "支付宝支付 {$amount} 元";
}
}
class PaymentFactory
{
public static function make($type)
{
switch ($type) {
case 'wechat':
return new WechatPay();
case 'alipay':
return new Alipay();
default:
throw new Exception("不支持的支付方式");
}
}
}
// 使用
$pay = PaymentFactory::make('wechat');
$pay->pay(100);
工厂模式的好处
- 解耦:客户端不需要知道具体怎么创建对象
- 易扩展:新增支付方式只需要加一个类和工厂里的一个 case
- 统一管理:所有对象的创建都在工厂里,方便维护
三、观察者模式(Observer)
什么时候用
一个对象状态改变时,需要通知多个其他对象,比如:
- 用户注册后:发邮件、发短信、送积分
- 订单状态变更后:通知仓库、通知物流、通知客服
代码实现
<?php
// 事件接口
interface Observer
{
public function update($eventData);
}
// 事件主体
class Event
{
private $observers = [];
public function attach(Observer $observer)
{
$this->observers[] = $observer;
}
public function trigger($eventData)
{
foreach ($this->observers as $observer) {
$observer->update($eventData);
}
}
}
// 具体观察者
class SendEmail implements Observer
{
public function update($eventData)
{
echo "发送邮件给 {$eventData['email']}\n";
}
}
class SendSms implements Observer
{
public function update($eventData)
{
echo "发送短信给 {$eventData['phone']}\n";
}
}
class GivePoints implements Observer
{
public function update($eventData)
{
echo "给用户送 100 积分\n";
}
}
// 使用
$event = new Event();
$event->attach(new SendEmail());
$event->attach(new SendSms());
$event->attach(new GivePoints());
// 用户注册,触发事件
$event->trigger([
'email' => 'user@example.com',
'phone' => '13800138000'
]);
观察者模式的好处
- 解耦:事件主体不需要知道谁在监听
- 易扩展:新增监听者只需要加一个类,不需要改原来的代码
- 符合开闭原则:对扩展开放,对修改关闭
四、什么时候用什么模式
| 场景 | 用什么模式 |
|---|---|
| 全局只需要一个实例 | 单例 |
| 根据条件创建不同对象 | 工厂 |
| 一个动作触发多个后续操作 | 观察者 |
五、常见坑
- 单例用太多:单例用多了会变成全局变量,代码难维护
- 工厂过度设计:简单逻辑就别硬上工厂,会增加复杂度
- 观察者滥用:所有操作都做成观察者,调试会很痛苦
总结
设计模式是工具,不是目的。
- 单例:保证全局只有一个实例
- 工厂:统一管理对象创建
- 观察者:一个事件,多个响应
用对了让代码更清晰,用错了让代码更复杂。记住:先解决问题,再考虑模式。
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 性能优化的核心思路:
- 先找出瓶颈在哪里,不要盲目优化
- 代码层面:减少数据库查询、减少内存占用
- 配置层面:OPcache 必开
- 部署层面:PHP-FPM、Nginx、数据库都要调优
- 用工具量化优化效果
性能优化是一个持续迭代的过程,不要指望一次性优化到位。每次做一点,慢慢积累。
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,既能获得这些新特性的好处,也能享受性能上的小幅提升。