本文目录导读:

我来帮你详细解释PHP中的数据屏蔽(Data Masking)的多种实现方法。
什么是数据屏蔽
数据屏蔽是对敏感信息进行部分隐藏的处理方式,常见于:
- 手机号:138****1234
- 身份证:110*1234
- 银行卡号:62221234
基本实现方法
1 简单字符串替换
<?php
// 手机号屏蔽
function maskPhone($phone) {
return substr($phone, 0, 3) . '****' . substr($phone, -4);
}
// 邮箱屏蔽
function maskEmail($email) {
$parts = explode('@', $email);
$name = substr($parts[0], 0, 2) . '***';
return $name . '@' . $parts[1];
}
// 身份证屏蔽
function maskIdCard($id) {
return substr($id, 0, 6) . '********' . substr($id, -4);
}
// 测试
echo maskPhone('13812345678'); // 138****5678
echo maskEmail('example@test.com'); // ex***@test.com
echo maskIdCard('110101199001011234'); // 110101****1234
?>
2 使用正则表达式
<?php
// 银行卡号屏蔽
function maskBankCard($card) {
return preg_replace('/(\d{4})\d{8}(\d{4})/', '$1********$2', $card);
}
// 姓名屏蔽(保留姓氏)
function maskName($name) {
return mb_substr($name, 0, 1) . str_repeat('*', mb_strlen($name) - 1);
}
// 测试
echo maskBankCard('6222021234567890'); // 6222********7890
echo maskName('张三'); // 张*
echo maskName('李小明'); // 李**
?>
灵活可配置的屏蔽函数
<?php
class DataMasker {
/**
* 通用数据屏蔽函数
* @param string $data 原始数据
* @param int $start 保留开头的字符数
* @param int $end 保留结尾的字符数
* @param string $mask 屏蔽字符
* @return string
*/
public static function mask($data, $start = 0, $end = 0, $mask = '*') {
$length = strlen($data);
if ($start + $end >= $length) {
return str_repeat($mask, $length);
}
$masked = substr($data, 0, $start);
$masked .= str_repeat($mask, $length - $start - $end);
$masked .= substr($data, -$end);
return $masked;
}
// 预定义的屏蔽规则
public static function phone($phone) {
return self::mask($phone, 3, 4);
}
public static function email($email) {
$parts = explode('@', $email);
$name = self::mask($parts[0], 1, 0);
return $name . '@' . $parts[1];
}
public static function idCard($id) {
return self::mask($id, 6, 4);
}
public static function bankCard($card) {
return self::mask($card, 4, 4);
}
}
?>
批量数据屏蔽
<?php
// 多维数组数据屏蔽
function maskArrayData($data) {
$rules = [
'phone' => 'maskPhone',
'email' => 'maskEmail',
'id_card' => 'maskIdCard',
'name' => 'maskName',
'bank_card' => 'maskBankCard'
];
foreach ($data as $key => &$value) {
if (is_array($value)) {
$value = maskArrayData($value);
} elseif (isset($rules[$key])) {
$value = $rules[$key]($value);
}
}
return $data;
}
// 使用示例
$userData = [
'name' => '张三',
'phone' => '13812345678',
'email' => 'zhangsan@test.com',
'id_card' => '110101199001011234',
'bank_card' => '6222021234567890'
];
$maskedData = maskArrayData($userData);
print_r($maskedData);
?>
数据库查询时屏蔽
<?php
// MySQL查询时屏蔽手机号
$sql = "SELECT
CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) as masked_phone,
CONCAT(LEFT(email, 2), '***', '@', SUBSTRING_INDEX(email, '@', -1)) as masked_email
FROM users";
// 或者使用自定义函数
$sql = "SELECT
mask_phone(phone) as masked_phone,
mask_email(email) as masked_email
FROM users";
?>
JSON输出时自动屏蔽
<?php
class MaskedJson {
private $data;
private $maskRules;
public function __construct($data) {
$this->data = $data;
$this->maskRules = [
'phone' => [$this, 'maskPhone'],
'email' => [$this, 'maskEmail'],
// 添加更多规则
];
}
public function toJson() {
$this->applyMasking($this->data);
return json_encode($this->data, JSON_UNESCAPED_UNICODE);
}
private function applyMasking(&$data) {
foreach ($data as $key => &$value) {
if (is_array($value)) {
$this->applyMasking($value);
} elseif (isset($this->maskRules[$key])) {
$value = call_user_func($this->maskRules[$key], $value);
}
}
}
private function maskPhone($phone) {
return substr($phone, 0, 3) . '****' . substr($phone, -4);
}
private function maskEmail($email) {
$parts = explode('@', $email);
return substr($parts[0], 0, 2) . '***@' . $parts[1];
}
}
// 使用
$data = ['name' => '张三', 'phone' => '13812345678'];
$masked = new MaskedJson($data);
echo $masked->toJson(); // {"name":"张三","phone":"138****5678"}
?>
注意事项
- 性能考虑:大量数据处理时使用字符串函数比正则表达式快
- 编码问题:中文等Unicode字符使用
mb_*函数 - 安全性:确保屏蔽后的数据不能还原
- 一致性:在不同接口中保持相同的屏蔽规则
最佳实践建议
<?php
// 定义统一的屏蔽服务
class MaskService {
public static function mask($type, $data) {
switch ($type) {
case 'phone':
return preg_replace('/(\d{3})\d{4}(\d{4})/', '$1****$2', $data);
case 'email':
return preg_replace('/(.{2}).*(@.*)/', '$1***$2', $data);
case 'name':
return mb_substr($data, 0, 1) . str_repeat('*', mb_strlen($data) - 1);
default:
return $data;
}
}
}
// 使用
echo MaskService::mask('phone', '13812345678'); // 138****5678
?>
这样你就可以根据实际需求选择合适的屏蔽方式了。