身份证校验需分三步:格式初筛、地址码查最新国标库、时间与加权校验码精验;末位校验码须用固定权重数组计算,地址码须动态获取gb/t 2260最新数据,生日解析须用datetime::createfromformat严格验证。

PHP 里验证身份证号码是否合法、提取出生日期和籍贯代码,不能只靠正则——18位末尾校验码有严格加权算法,地址码要查国标数据库,直接硬编码会漏掉新设行政区。
身份证18位校验码必须用 calcIdCardChecksum() 算,不能用正则或长度判断
很多人用 strlen($id) === 18 或 /^\d{17}[\dxX]$/ 判断,这只能拦住明显格式错误,但完全无法识别“110101199003072915”这种地址码非法、日期不存在、校验码错位的假号。
- 校验码是前17位按固定权重(
[7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2])加权求和后对11取模,再映射到"10X98765432" - 必须逐位转整型,
"X"只允许出现在最后一位且大小写不敏感,中间出现就直接失败 - 年份必须是1900–2099之间,月份为01–12,日期需符合该年月实际天数(注意闰年)
地址码解析依赖最新 GB/T 2260 行政区划代码,不能硬写数组
用静态数组匹配前两位或前四位地址码,大概率在2023年后失效——比如北京朝阳区2021年拆出“北京自贸区朝阳片区”,河北雄安新区2023年升格为地级行政区,代码已变。
- 建议从民政部官网或国家统计局接口获取最新
area_codes.json,结构如{"110101": "北京市东城区"} - 解析时优先匹配6位完整码;若无,则退化到4位(地级市),再无则退到2位(省级),避免直接报错
- 注意:港澳台地区代码(如810000、820000、710000)不在大陆
GB/T 2260范围内,需单独维护
DateTime::createFromFormat() 解析生日字段必须指定严格格式并检查返回值
用 substr($id, 6, 8) 拿到8位数字后直接 new DateTime($yymmdd) 很危险——它会静默转换无效日期(如“19900230”变成1990-03-02),导致校验失真。
- 必须用
DateTime::createFromFormat('Ymd', $dateStr)并检查返回值是否为false - 再调用
$dt->format('Ymd') === $dateStr确保无自动修正(例如防止“19900230”被接受) - 15位老身份证需补“19”前缀,但要注意:1999年前发的15位证可能用“19”或“20”,需结合年份范围交叉判断
完整校验函数应分三步:格式初筛 → 地址码查表 → 时间+校验码精验
把所有逻辑塞进一个函数看似简洁,但调试困难、复用性差。更稳妥的做法是分层:
-
isValidIdCardFormat($id):只做长度、字符、末位X大小写、年份范围等轻量检查 -
getAreaFromCode($code):传入6位码,查本地缓存或远程API,返回行政区名称或null -
verifyIdCardChecksum($id):专注加权计算与模运算,返回bool,不处理业务逻辑
真实系统中,地址码查询最好加 Redis 缓存(key为6位码),避免每次HTTP请求拖慢响应;校验码计算虽快,但高频调用仍建议 opcode 缓存开启。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











