本文目录导读:

<?php
/**
* 中国大陆身份证号码信息提取类
* 支持18位和15位身份证号码
*/
class IdCardInfo {
// 身份证号码
private $idCard;
// 是否有效
private $isValid = false;
// 解析后的信息
private $info = [];
// 省市县代码对照表(简化版,实际开发中可扩展)
private $areaCode = [
'110000' => '北京市', '120000' => '天津市', '130000' => '河北省',
'140000' => '山西省', '150000' => '内蒙古自治区', '210000' => '辽宁省',
'220000' => '吉林省', '230000' => '黑龙江省', '310000' => '上海市',
'320000' => '江苏省', '330000' => '浙江省', '340000' => '安徽省',
'350000' => '福建省', '360000' => '江西省', '370000' => '山东省',
'410000' => '河南省', '420000' => '湖北省', '430000' => '湖南省',
'440000' => '广东省', '450000' => '广西壮族自治区', '460000' => '海南省',
'500000' => '重庆市', '510000' => '四川省', '520000' => '贵州省',
'530000' => '云南省', '540000' => '西藏自治区', '610000' => '陕西省',
'620000' => '甘肃省', '630000' => '青海省', '640000' => '宁夏回族自治区',
'650000' => '新疆维吾尔自治区', '710000' => '台湾省', '810000' => '香港特别行政区',
'820000' => '澳门特别行政区'
];
/**
* 构造函数
* @param string $idCard 身份证号码
*/
public function __construct($idCard) {
$this->idCard = trim($idCard);
$this->parse();
}
/**
* 解析身份证信息
*/
private function parse() {
// 去除空格并转大写
$this->idCard = strtoupper($this->idCard);
// 验证身份证格式
if (!$this->validateFormat()) {
$this->isValid = false;
$this->info = ['error' => '身份证号码格式不正确'];
return;
}
// 处理15位身份证(转换为18位分析)
$idCard = $this->idCard;
if (strlen($idCard) == 15) {
$idCard = $this->convert15To18($idCard);
if ($idCard === false) {
$this->isValid = false;
$this->info = ['error' => '身份证号码无效'];
return;
}
}
// 验证校验位
if (!$this->validateChecksum($idCard)) {
$this->isValid = false;
$this->info = ['error' => '身份证号码校验失败'];
return;
}
// 提取基本信息
$this->isValid = true;
$this->info = [
'id_card' => $idCard, // 18位身份证号
'original' => $this->idCard, // 原始输入
'area_code' => substr($idCard, 0, 6), // 地区代码
'birth_date' => $this->formatBirthDate(substr($idCard, 6, 8)), // 出生日期
'gender' => $this->getGender($idCard), // 性别
'age' => $this->calculateAge(substr($idCard, 6, 8)), // 年龄
'zodiac' => $this->getZodiac(substr($idCard, 6, 8)), // 属相
'constellation' => $this->getConstellation(substr($idCard, 6, 8)), // 星座
'region' => $this->getRegion(substr($idCard, 0, 6)), // 地区名称
'checksum' => substr($idCard, 17, 1), // 校验位
];
}
/**
* 验证身份证格式
* @return bool
*/
private function validateFormat() {
// 18位:6位地区 + 8位生日 + 3位顺序码 + 1位校验码
if (preg_match('/^\d{17}[\dX]$/', $this->idCard)) {
return $this->validateBirthDate(substr($this->idCard, 6, 8));
}
// 15位:6位地区 + 6位生日 + 3位顺序码
if (preg_match('/^\d{15}$/', $this->idCard)) {
return $this->validateBirthDate('19' . substr($this->idCard, 6, 6));
}
return false;
}
/**
* 验证出生日期
* @param string $date 日期字符串 YYYYMMDD
* @return bool
*/
private function validateBirthDate($date) {
if (strlen($date) != 8) return false;
$year = (int)substr($date, 0, 4);
$month = (int)substr($date, 4, 2);
$day = (int)substr($date, 6, 2);
if ($year < 1900 || $year > date('Y')) return false;
if ($month < 1 || $month > 12) return false;
if ($day < 1 || $day > 31) return false;
// 检查特定月份天数
$daysInMonth = [31, ($year % 4 == 0 && $year % 100 != 0) || $year % 400 == 0 ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
return $day <= $daysInMonth[$month - 1];
}
/**
* 将15位身份证转换为18位
* @param string $idCard15 15位身份证
* @return string|false
*/
private function convert15To18($idCard15) {
// 15位身份证出生日期加19前缀
$idCard17 = substr($idCard15, 0, 6) . '19' . substr($idCard15, 6);
// 计算校验码
$checksum = $this->calculateChecksum($idCard17);
if ($checksum === false) return false;
return $idCard17 . $checksum;
}
/**
* 计算校验码
* @param string $idCard17 前17位
* @return string|false
*/
private function calculateChecksum($idCard17) {
if (strlen($idCard17) != 17) return false;
// 加权因子
$factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
// 校验码对应值
$checksumMap = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
$sum = 0;
for ($i = 0; $i < 17; $i++) {
$sum += (int)$idCard17[$i] * $factors[$i];
}
return $checksumMap[$sum % 11];
}
/**
* 验证校验码
* @param string $idCard 18位身份证
* @return bool
*/
private function validateChecksum($idCard) {
if (strlen($idCard) != 18) return false;
$calculate = $this->calculateChecksum(substr($idCard, 0, 17));
return $calculate !== false && $calculate === $idCard[17];
}
/**
* 格式化出生日期
* @param string $date YYYYMMDD
* @return string
*/
private function formatBirthDate($date) {
return substr($date, 0, 4) . '-' . substr($date, 4, 2) . '-' . substr($date, 6, 2);
}
/**
* 获取性别
* @param string $idCard 身份证号
* @return string
*/
private function getGender($idCard) {
// 第17位(倒数第二位)奇数为男,偶数为女
$genderDigit = (int)$idCard[16];
return ($genderDigit % 2 == 1) ? '男' : '女';
}
/**
* 计算年龄
* @param string $birthDate 出生日期 YYYYMMDD
* @return int
*/
private function calculateAge($birthDate) {
$birthYear = (int)substr($birthDate, 0, 4);
$birthMonth = (int)substr($birthDate, 4, 2);
$birthDay = (int)substr($birthDate, 6, 2);
$now = new DateTime();
$currentYear = (int)$now->format('Y');
$currentMonth = (int)$now->format('m');
$currentDay = (int)$now->format('d');
$age = $currentYear - $birthYear;
// 如果还没到生日,年龄减一
if ($currentMonth < $birthMonth || ($currentMonth == $birthMonth && $currentDay < $birthDay)) {
$age--;
}
return max($age, 0);
}
/**
* 获取属相
* @param string $birthDate 出生日期 YYYYMMDD
* @return string
*/
private function getZodiac($birthDate) {
$year = (int)substr($birthDate, 0, 4);
$zodiacs = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪'];
return $zodiacs[($year - 4) % 12];
}
/**
* 获取星座
* @param string $birthDate 出生日期 YYYYMMDD
* @return string
*/
private function getConstellation($birthDate) {
$month = (int)substr($birthDate, 4, 2);
$day = (int)substr($birthDate, 6, 2);
$constellations = [
'摩羯座' => [1, 19], '水瓶座' => [2, 18], '双鱼座' => [3, 20],
'白羊座' => [4, 19], '金牛座' => [5, 20], '双子座' => [6, 21],
'巨蟹座' => [7, 22], '狮子座' => [8, 22], '处女座' => [9, 22],
'天秤座' => [10, 23], '天蝎座' => [11, 23], '射手座' => [12, 21],
'摩羯座' => [12, 22]
];
foreach ($constellations as $name => $date) {
if ($month == $date[0] && $day >= $date[1]) {
return $name;
}
}
// 处理特殊日期
if ($month == 1 && $day <= 19) return '摩羯座';
if ($month == 2 && $day <= 18) return '水瓶座';
if ($month == 3 && $day <= 20) return '双鱼座';
if ($month == 4 && $day <= 19) return '白羊座';
if ($month == 5 && $day <= 20) return '金牛座';
if ($month == 6 && $day <= 21) return '双子座';
if ($month == 7 && $day <= 22) return '巨蟹座';
if ($month == 8 && $day <= 22) return '狮子座';
if ($month == 9 && $day <= 22) return '处女座';
if ($month == 10 && $day <= 23) return '天秤座';
if ($month == 11 && $day <= 23) return '天蝎座';
if ($month == 12 && $day <= 21) return '射手座';
return '未知';
}
/**
* 获取地区名称
* @param string $areaCode 6位地区代码
* @return string
*/
private function getRegion($areaCode) {
// 省级代码(前2位 + 4个0)
$provinceCode = substr($areaCode, 0, 2) . '0000';
$province = isset($this->areaCode[$provinceCode]) ? $this->areaCode[$provinceCode] : '未知地区';
// 城市级(前4位 + 2个0)- 简化处理
$cityCode = substr($areaCode, 0, 4) . '00';
$city = isset($this->areaCode[$cityCode]) ? $this->areaCode[$cityCode] : '';
$cityAreaCodes = [
'1101' => '市辖区', '1102' => '县', '1201' => '市辖区', '1202' => '县',
'1301' => '石家庄市', '1302' => '唐山市', '1303' => '秦皇岛市',
'1304' => '邯郸市', '1305' => '邢台市', '1306' => '保定市',
'1307' => '张家口市', '1308' => '承德市', '1309' => '沧州市',
'1310' => '廊坊市', '1311' => '衡水市', '1401' => '太原市',
'2101' => '沈阳市', '2102' => '大连市', '2103' => '鞍山市',
'3101' => '市辖区', '3201' => '南京市', '3202' => '无锡市',
'3301' => '杭州市', '3302' => '宁波市', '3501' => '福州市',
'3502' => '厦门市', '3701' => '济南市', '3702' => '青岛市',
'4101' => '郑州市', '4201' => '武汉市', '4301' => '长沙市',
'4401' => '广州市', '4403' => '深圳市', '5101' => '成都市',
'6101' => '西安市', '6201' => '兰州市', '6501' => '乌鲁木齐市'
];
$cityKey = substr($areaCode, 0, 4);
if (isset($cityAreaCodes[$cityKey])) {
$city = $cityAreaCodes[$cityKey];
} elseif ($province != '未知地区') {
$city = '';
}
return $province . ($city ? ' ' . $city : '');
}
/**
* 获取解析结果
* @return array|false 成功返回信息数组,失败返回false
*/
public function getInfo() {
return $this->isValid ? $this->info : false;
}
/**
* 获取具体字段
* @param string $key 字段名
* @return mixed|null
*/
public function get($key) {
return $this->isValid && isset($this->info[$key]) ? $this->info[$key] : null;
}
/**
* 检查是否有效
* @return bool
*/
public function isValid() {
return $this->isValid;
}
}
/**
* 使用示例
*/
// 测试18位身份证
echo "=== 测试18位身份证 ===\n";
$idCard18 = new IdCardInfo('11010519491231002X');
if ($idCard18->isValid()) {
$info = $idCard18->getInfo();
echo "身份证号:{$info['id_card']}\n";
echo "出生日期:{$info['birth_date']}\n";
echo "性别:{$info['gender']}\n";
echo "年龄:{$info['age']}\n";
echo "属相:{$info['zodiac']}\n";
echo "星座:{$info['constellation']}\n";
echo "地区:{$info['region']}\n";
echo "校验位:{$info['checksum']}\n";
} else {
echo "无效的身份证号\n";
}
echo "\n=== 测试15位身份证 ===\n";
$idCard15 = new IdCardInfo('110105491231002');
if ($idCard15->isValid()) {
$info = $idCard15->getInfo();
echo "原始号码:{$info['original']}\n";
echo "转换后:{$info['id_card']}\n";
echo "出生日期:{$info['birth_date']}\n";
echo "性别:{$info['gender']}\n";
} else {
echo "无效的身份证号\n";
}
echo "\n=== 测试无效身份证 ===\n";
$invalidIdCard = new IdCardInfo('123456789012345678');
if (!$invalidIdCard->isValid()) {
echo "身份证号无效:" . $invalidIdCard->get('error') . "\n";
}
echo "\n=== 演示常用方法 ===\n";
$demo = new IdCardInfo('440305199003078819');
echo "性别:{$demo->get('gender')}\n";
echo "年龄:{$demo->get('age')}\n";
echo "出生日期:{$demo->get('birth_date')}\n";
echo "属相:{$demo->get('zodiac')}\n";
echo "星座:{$demo->get('constellation')}\n";
echo "地区:{$demo->get('region')}\n";
功能说明
该工具类可以从身份证号中提取以下信息:
- 基础信息:出生日期、性别、年龄
- 文化信息:属相(生肖)、星座
- 地理信息:省份、城市(简版地区对照表)
- 验证功能:自动验证格式、校验位
- 兼容性:支持18位和15位身份证号
主要方法
| 方法 | 说明 |
|---|---|
isValid() |
检查身份证是否有效 |
getInfo() |
获取完整信息数组 |
get($key) |
获取指定字段信息 |
注意事项
- 地区代码表为简化版,生产环境建议使用完整数据库
- 15位身份证会自动转换为18位进行分析
- 年龄基于当前日期实时计算
- 属相和星座计算基于公历生日
您可以直接运行此代码查看效果,也可以根据需求扩展地区数据库或添加其他功能。