1. 原理与编码行为
1.1 公共属性的编码规则
在 PHP 的 json_encode 编码对象时,默认只编码对象的公共属性。这意味着私有(private)和受保护(protected)属性不会直接出现在 JSON 输出中,除非通过特定方式暴露或自定义序列化逻辑来输出它们。
如果对象中存在公共属性,json_encode 会将它们作为 JSON 对象的键值对输出,键名对应属性名,键值对应该属性的值。对于包含其他对象或数组的属性,编码会进行递归处理,遵循相同的规则。
class User {public $name;private $password;protected $role;public function __construct($name, $password, $role) {$this->name = $name;$this->password = $password;$this->role = $role;}
}$user = new User('张三', 'secret', 'admin');
echo json_encode($user);
// 输出通常仅包含 { "name": "张三" },私有与受保护属性不会出现在结果中
1.2 JsonSerializable 与 jsonSerialize
JsonSerializable 接口提供了自定义编码的入口点,实现 jsonSerialize() 方法后,json_encode 会调用该方法返回的数据作为输出对象。这让你能够明确控制哪些字段被编码,以及如何编码。
如果对象实现了 JsonSerializable,那么即使对象内部有私有或受保护属性,也能通过 jsonSerialize() 输出自定义结构,从而实现更精细的权限控制和数据格式化。
class User implements JsonSerializable {public $name;private $password;protected $role;public function __construct($name, $password, $role) {$this->name = $name;$this->password = $password;$this->role = $role;}public function jsonSerialize() {// 显式输出想要暴露的字段return ['name' => $this->name,'role' => $this->role];}
}
$user = new User('张三', 'secret', 'admin');
echo json_encode($user); // {"name":"张三","role":"admin"}
2. 哪些属性会被编码与常见坑点
2.1 公共属性的可编码性与 get_object_vars
json_encode 的编码对象行为建立在对可访问属性的遍历之上,get_object_vars($obj) 只返回对象的公共属性,这也是 json_encode 在底层进行编码时的核心依据。
若想在编码前查看当前对象有哪些可编码的公开字段,可以使用 var_dump 或 get_object_vars 来查看。
class Sample {public $a = 1;private $b = 2;
}
$x = new Sample();
echo json_encode($x); // 仅输出 {"a":1}
2.2 私有与保护属性如何编码
除非通过实现 JsonSerializable 或改为公开属性,否则私有/受保护属性不会出现在 JSON 中。这也是一个常见坑点:以为所有成员都会被编码,结果缺少字段。
class Demo {private $secret = 'top';protected $token = 'abc';
}
$d = new Demo();
echo json_encode($d); // 输出:{}
要包含私有/受保护属性,可以选择以下路径:
- 实现 JsonSerializable,在 jsonSerialize() 中输出需要的字段
- 将需要暴露的字段改为公开属性(注意避免暴露敏感信息)
2.3 循环引用与编码错误的处理
对象之间存在循环引用时,直接 json_encode 可能导致错误或崩溃。为避免递归导致的问题,建议避免直接的循环引用,或通过 JsonSerializable 返回扁平化的结构。
class Node {public $value;public $next;
}
$a = new Node(); $b = new Node(); $a->next = $b; $b->next = $a;
echo json_encode($a); // 可能触发递归问题
2.4 Unicode 与编码选项
json_encode 的默认行为可能会对 Unicode 字符进行转义,使用 JSON_UNESCAPED_UNICODE 可以避免转义,从而提高可读性,尤其在国际化场景下对 SEO 友好性有帮助。
echo json_encode($user, JSON_UNESCAPED_UNICODE); // 直接输出中文
3. 实战技巧:结合 JsonSerializable 的最佳实践
3.1 自定义编码输出的清晰结构
在真实项目中,推荐使用 JsonSerializable 接口来控制输出结构,避免暴露敏感字段,同时提升前端对数据结构的稳定性。
class Product implements JsonSerializable {public $id;public $name;private $price;protected $stock;public function __construct($id, $name, $price, $stock) {$this->id = $id;$this->name = $name;$this->price = $price;$this->stock = $stock;}public function jsonSerialize() {return ['id' => $this->id,'name' => $this->name,'price' => $this->price// stock 不输出];}
}
$p = new Product(101, '摄像头', 299.99, 20);
echo json_encode($p, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
3.2 与数据库对象的整合注意点
从数据库层取出的数据通常映射为 DTO 或视图模型,再通过 JsonSerializable 将其转换为 JSON,以降低耦合并提升安全性。
// 假设从数据库取出一个行数据映射到对象
class UserDTO implements JsonSerializable {public $id;public $name;private $passwordHash;public function __construct($id, $name, $passwordHash) {$this->id = $id;$this->name = $name;$this->passwordHash = $passwordHash;}public function jsonSerialize() {return ['id' => $this->id,'name' => $this->name];}
}
重点提醒:不要无条件暴露数据库字段,保护敏感信息是后端的基本职责。
3.3 实战要点与应用场景
在实际项目中,按照需求选择输出字段、控制敏感信息暴露、再结合编码选项(如 JSON_UNESCAPED_UNICODE、JSON_PRETTY_PRINT),能够形成稳定的前后端数据接口,并便于后续维护。
核心要点:通过实现 JsonSerializable 或公开字段,搭配显式输出结构以及适当的编码选项,确保 Object 属性在 JSON 中的可控性与安全性。



