关键是在匹配键名的叶子字符串值上加密,跳过非字符串、空值、已加密项及键名和数组本身,通过引用递归修改原数组,避免array_walk_recursive。

直接用递归函数遍历并加密多层数组字段,关键不是“怎么写递归”,而是“在哪一层加密”“加密什么”“是否保留结构”。PHP7 中没有内置的“递归加密函数”,必须手动控制加密时机和范围,否则容易漏掉值、误加密键、或破坏数组结构。
加密前先判断是否为待加密的叶子值
递归过程中,只有真正需要加密的数据(比如 id_card、phone、email 这类字段的值)才调用 openssl_encrypt 或 password_hash;其余字段(如数字 ID、状态码、空字符串)应跳过。不能对整个子数组或键名做加密操作。
- 用
is_array($value)判断是否继续递归,避免对数组本身调用加密函数 - 用
is_string($value) && strlen($value) > 0排除空值和非字符串类型,防止openssl_encrypt报错 - 建议加白名单校验:只对明确匹配键名(如
$key === 'phone')或正则匹配(如preg_match('/^(id_card|mobile|email)$/', $key))的值执行加密
使用引用传参才能修改原数组值
如果目标是“原地加密并返回加密后的新数组”,必须通过引用传递参数,否则递归函数里改的只是副本。
- 函数定义要带
&$array,例如:function encryptArrayFields(&$array, $keysToEncrypt = ['phone', 'id_card']) - 在递归调用时仍需保持引用:
encryptArrayFields($value, $keysToEncrypt)—— 因为$value是$array[$key]的引用副本,只要顶层传的是引用,深层也能生效 - 不要用
return $array试图覆盖,那样会丢失引用关系;所有修改都在原变量上进行
避免重复加密和类型污染
已加密过的字段(比如值已经是 base64 编码的密文)若再次进入递归,可能被二次加密,导致解密失败。需约定加密后的值特征,提前跳过。
- 加密后统一加前缀(如
'ENC:' . base64_encode(...)),解密前检查strpos($value, 'ENC:') === 0 - 不要对
int、bool、null类型调用加密函数,openssl_encrypt只接受 string - 加密失败时(如密钥错误、IV 不合法),
openssl_encrypt返回false,需显式处理,避免把false写进数组
array_walk_recursive 不适合加密场景
array_walk_recursive 看似省事,但它无法获取当前键路径,也没法区分“这个 phone 是用户一级的,还是地址子数组里的”,更没法跳过已加密项。它只适合无上下文的批量操作(如全转小写)。
- 它不提供键的层级信息,无法做白名单键匹配(只认值,不认位置)
- 它不支持中断或条件跳过分支,一旦进入就遍历到底
- 它无法 unset 或替换键名,而加密后有时需同时清理明文字段、新增
phone_enc字段——这必须用自定义递归 + 引用控制
最易被忽略的一点:加密密钥和 IV 必须全局一致且安全存储,不能每次递归都生成新 IV;否则同字段多次加密结果不同,后续无法解密。递归只是流程控制,加解密逻辑本身必须与密码学规范对齐。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











