class.idcard.inc.php 文件如下<?php
define('IDC_INDEX_CAT', 0); // 号码类别索引名称
define('IDC_INDEX_NUM_ORIGIN', 1); // 号码原始数据索引名称
define('IDC_INDEX_NUM_18', 2); // 号码对应18位编号索引名称
define('IDC_INDEX_ADDR', 3); // 号码对应地址信息索引名称
define('IDC_INDEX_BIRTH', 4); // 号码对应生日信息索引名称
define('IDC_INDEX_SEX', 5); // 号码对应性别信息索引名称define('IDC_FLAG_INVALID', 0); // 无效号码
define('IDC_FLAG_OLD', 1); // 15位旧号码
define('IDC_FLAG_NEW', 2); // 18位新号码define('IDC_SEX_UNKNOWN', 0); // 性别未知
define('IDC_SEX_MALE', 1); // 男性
define('IDC_SEX_FEMALE', 2); // 女性class idcard{
/**
 * @var array 经过解析的信息数组:
 */
var $arr_info;

/**
 * 构造函数
 */
function idcard()
{
$this->flag_cat = 0;
$this->arr_info = array (
IDC_INDEX_CAT => IDC_FLAG_INVALID, 
IDC_INDEX_NUM_ORIGIN => '',
IDC_INDEX_NUM_18 => '',
IDC_INDEX_ADDR => array(0 => '', 1 => '', 2 => ''), // 省/市/区县
IDC_INDEX_BIRTH => array(0 => '', 1 => '', 2 => ''),// 年/月/日
IDC_INDEX_SEX => IDC_SEX_UNKNOWN,
);
}

/**
 * 解析身份证号码
 * 
 * @param string $num 传入15位或18位身份证的编码
 */
function parse_idc($num)
{
if (preg_match("/^\d{15}$|^\d{18}$|^\d{17}x$/", $num) == 0)
{
$this->arr_info[IDC_INDEX_CAT] = IDC_FLAG_INVALID; // 设置无效号码标志
return $this->arr_info;
}

$this->arr_info[IDC_INDEX_NUM_ORIGIN] = $num; //

if (strlen($num) == 15) // 旧15位编码
{
$this->arr_info[IDC_INDEX_CAT] = IDC_FLAG_OLD; //
$this->arr_info[IDC_INDEX_NUM_18] = $this->update15($num);
}

if (strlen($num) == 18) // 新18位编码
{
$this->arr_info[IDC_INDEX_CAT] = IDC_FLAG_NEW; //
$this->arr_info[IDC_INDEX_NUM_18] = $num;
}

if (!$this->verify_num($this->arr_info[IDC_INDEX_NUM_18])) // 判断编码是否有效
{
$this->arr_info[IDC_INDEX_CAT] = IDC_FLAG_INVALID; // 设置无效号码标志
return $this->arr_info;
}

$this->arr_info[IDC_INDEX_ADDR] = $this->parse_addr($this->arr_info[IDC_INDEX_NUM_18]);
$this->arr_info[IDC_INDEX_BIRTH] = $this->parse_birth($this->arr_info[IDC_INDEX_NUM_18]);
$this->arr_info[IDC_INDEX_SEX] = $this->parse_sex($this->arr_info[IDC_INDEX_NUM_18]);

// 判断生日是否合理,是否大于当前时间
if (checkdate($this->arr_info[IDC_INDEX_BIRTH][1], $this->arr_info[IDC_INDEX_BIRTH][2], $this->arr_info[IDC_INDEX_BIRTH][0]) == false)
{
$this->arr_info[IDC_INDEX_CAT] = IDC_FLAG_INVALID; // 设置无效号码标志
}
elseif( strtotime($this->arr_info[IDC_INDEX_BIRTH][0]."/".$this->arr_info[IDC_INDEX_BIRTH][1]."/".$this->arr_info[IDC_INDEX_BIRTH][2]) > time())
{
$this->arr_info[IDC_INDEX_CAT] = IDC_FLAG_INVALID; // 设置无效号码标志
}
return $this->arr_info;
}

/**
 * 15位升级到18位
 * 
 * @param string $num 传入15位旧身份证的编码
 * @return string 返回对应的18位新身份证编码
 */
function update15($num)
{
if (strlen($num) != 15)
{
return '';
}

// 如果身份证顺序码是996 997 998 999,这些是为百岁以上老人的特殊编码 
        if (array_search(substr($num, 12, 3), array( 996 , 997 , 998 , 999 )) !== false)
        {
        $num = substr($num, 0, 6) . 18 . substr($num, 6, 9);
        }
        else
        {
        $num = substr($num, 0, 6) . 19 . substr($num, 6, 9);
        }
       
        return $num.$this->cal_verify($num);
}

/**
 * 识别地址码
 * 
 * @param string $num 传入18位身份证的编码
 * @return array 地址信息,array(0 => '省', 1 => '市', 2 => '区(县)')
 */
function parse_addr($num)
{
$arr_rtn = array(0 => '', 1 => '', 2 => ''); // 省/市/区县

if (strlen($num) != 18)
{
return $arr_rtn;
}
$file_data = dirname(__FILE__)."/area_code.dat";
if (!file_exists($file_data))
{
return $arr_rtn;
}

$h = fopen($file_data, "r");

$s1 = str_pad(substr($num, 0, 2), 6, "0", STR_PAD_RIGHT);
$s2 = str_pad(substr($num, 0, 4), 6, "0", STR_PAD_RIGHT);
$s3 = str_pad(substr($num, 0, 6), 6, "0", STR_PAD_RIGHT);

while (!feof ($h))
{
$buffer = fgets($h, 4096);
$arr = explode(",", $buffer);

// 前两位
if (strcmp($arr[0], $s1) == 0)
{
$arr_rtn[0] = $arr[1];
}
// 中间两位
if (strcmp($arr[0], $s2) == 0)
{
$arr_rtn[1] = $arr[1];
}
// 末两位
if (strcmp($arr[0], $s3) == 0)
{
$arr_rtn[2] = $arr[1];
break;
}
}

fclose($h);
return $arr_rtn;
}

/**
 * 识别出生年月日
 * 
 * @param string $num 传入18位身份证的编码
 * @return array 生日信息,array(0 => '年', 1 => '月', 2 => '日') 
 */
function parse_birth($num)
{
$arr_rtn = array(0 => '', 1 => '', 2 => ''); // 年/月/日

if (strlen($num) != 18)
{
return $arr_rtn;
}

$arr_rtn[0] = substr($num, 6, 4); // 年
$arr_rtn[1] = substr($num, 10, 2); // 月
$arr_rtn[2] = substr($num, 12, 2); // 日

return $arr_rtn;
}

/**
 * 识别性别
 * 
 * @param string $num 传入18位身份证的编码
 * @return int 返回男女标志
 */
function parse_sex($num)
{
$rtn_sex = IDC_SEX_UNKNOWN;
if (strlen($num) != 18)
{
return $rtn_sex;
}

if (is_int(substr($num, 16, 1)/2))
{
$rtn_sex = IDC_SEX_FEMALE; // 女
}
else
{
$rtn_sex = IDC_SEX_MALE; // 男
}
return  $rtn_sex;
}

/**
 * 生成校验码
 * 
 * @param string $num 传入18位或者前17位身份证的编码
 * @return string 返回校验码
 */
function cal_verify($num)
{
if (strlen($num) != 17)
{
if (strlen($num) == 18)
{
$num = substr($num, 0, 17);
}
else
{
return false;
}
}

// 加权因子 
$factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);

// 校验码对应值
$verify_number_list = array( 1 , 0 , X , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 );
$checksum = 0;
for ($i = 0; $i < strlen($num); $i++)
{
$checksum += substr($num, $i, 1) * $factor[$i];
}

$mod = $checksum % 11;
return $verify_number_list[$mod];
}

/**
 * 验证18位新号码的有效性
 * 
 * @param string $num 传入18位身份证号码
 * @return bool 验证成功:true|验证失败:false
 */
function verify_num($num)
{
if (strlen($num) != 18)
{
return false;
}

if (strcasecmp(substr($num, 17, 1), $this->cal_verify($num)) == 0)
{
return true;
}
else
{
return false;
}
}
}
?>