应使用 mb_strlen($str, 'UTF-8') 校验 UTF-8 字符串字符数,并结合 utf8mb4 下最大字节估算(字符数×4)与字段真实字节上限双重校验,同时启用 MySQL 严格模式捕获 SQLSTATE[22001] 截断错误。

PHP 中用 mb_strlen() 而不是 strlen() 校验 UTF-8 字符串长度
MySQL 的 VARCHAR(255) 是按字节算上限的,但 PHP 默认的 strlen() 在 UTF-8 下会把一个中文算成 3 字节,而实际插入时 MySQL 可能按字符数或字节限制截断——尤其在 utf8mb4 编码下,emoji 或生僻字占 4 字节,strlen() 容易误判“没超长”,结果写入失败或被静默截断。
正确做法是统一按字节长度校验,且明确指定编码:
- 用
mb_strlen($str, 'UTF-8')获取字符数(用于对比字段定义的「字符长度」,如VARCHAR(255)表示最多 255 个字符) - 用
mb_strlen($str, 'UTF-8') * 4估算最大可能字节数(utf8mb4下单字符最多 4 字节),再和字段字节上限比对 - 更稳妥:用
mb_convert_encoding($str, 'UTF-8', 'UTF-8')先标准化编码,避免源字符串编码混乱导致长度计算偏差
查清目标字段的真实字节上限
不能只看 VARCHAR(255) 就认定能存 255 字节——MySQL 实际字节上限取决于 character_set_client、表默认字符集、字段显式字符集三者共同作用。例如:
-
CREATE TABLE t (s VARCHAR(255)) CHARSET=utf8mb4;→ 真实上限是255 × 4 = 1020字节 -
ALTER TABLE t CONVERT TO CHARSET utf8mb4;不会自动改已有字段的字符集声明,得显式MODIFY s VARCHAR(255) CHARACTER SET utf8mb4; - 用
SHOW FULL COLUMNS FROM t LIKE 's';查Collation列确认实际生效字符集
在入库前做双重校验:字符数 + 估算字节数
仅校验字符数会漏掉 4 字节字符;仅校验字节数又无法适配不同字符集策略。推荐组合判断:
立即学习“PHP免费学习笔记(深入)”;
function validateStringForColumn($str, $maxLengthChars, $charset = 'utf8mb4') {
if (!is_string($str)) return false;
$charLen = mb_strlen($str, 'UTF-8');
if ($charLen > $maxLengthChars) return false;
// utf8mb4 下最大字节占用
$maxBytes = $charset === 'utf8mb4' ? $charLen * 4 : $charLen * 3;
// 假设字段定义为 VARCHAR(255),对应最大字节为 1020(utf8mb4)
$columnMaxBytes = 1020;
return $maxBytes <= $columnMaxBytes;
}
注意:$columnMaxBytes 必须根据实际建表语句和字符集手动算出,不能硬编码通用值。
INSERT 失败时捕获 SQLSTATE[22001] 错误而非忽略
MySQL 在数据超长时默认行为是静默截断(sql_mode 不含 STRICT_TRANS_TABLES),这会让校验失效。必须确保:
- 连接时设置严格模式:
SET sql_mode = 'STRICT_TRANS_TABLES'; - 用 PDO 捕获具体异常:
if ($e->getCode() === '22001')—— 这是「data exception: string data, right truncation」的标准 SQLSTATE - 不要依赖
mysqli_affected_rows()或返回值判断是否写入成功,它不反映截断
校验逻辑再严密,如果数据库不报错,就永远不知道前端传来的字符串在存储层已被砍掉后半截。











