PHP身份证号提取信息

wen PHP项目 3

本文目录导读:

PHP身份证号提取信息

  1. 功能说明
  2. 主要方法
  3. 注意事项
<?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";

功能说明

该工具类可以从身份证号中提取以下信息:

  1. 基础信息:出生日期、性别、年龄
  2. 文化信息:属相(生肖)、星座
  3. 地理信息:省份、城市(简版地区对照表)
  4. 验证功能:自动验证格式、校验位
  5. 兼容性:支持18位和15位身份证号

主要方法

方法 说明
isValid() 检查身份证是否有效
getInfo() 获取完整信息数组
get($key) 获取指定字段信息

注意事项

  1. 地区代码表为简化版,生产环境建议使用完整数据库
  2. 15位身份证会自动转换为18位进行分析
  3. 年龄基于当前日期实时计算
  4. 属相和星座计算基于公历生日

您可以直接运行此代码查看效果,也可以根据需求扩展地区数据库或添加其他功能。

抱歉,评论功能暂时关闭!