grifart / not-serializable
明确指出该类不适用于序列化。
Requires
- php: >=7.4
Requires (Dev)
- nette/tester: ^2.4
- phpstan/phpstan: ^1.4
This package is auto-updated.
Last update: 2024-08-29 05:37:38 UTC
README
...这个包使您更容易实现。
一个故事
2022年3月发生了这件事。
class UserId { private int $id; public function __construct(int $id) {$this->id = $id} } // later in an app $userId = new UserId(42); // ... $_SESSION['user_id'] = $userId;
这将起作用。没有警告,没有错误,没有问题。现在...
...
一年后,您决定对UserId
对象进行内部更改——将私有属性$id
重命名为$identifier
。
class UserId { - private int $id; + private int $identifier; public function __construct(int $id) { - $this->id = $id + $this->identifier = $id } }
没有行为变化。外部观察者不应该能够找到对象行为的任何变化。静态分析通过,测试通过。一切顺利!您部署了您的应用程序。
砰!🔥 每个已经登录的用户在会话反序列化时都会导致应用程序硬崩溃。这是因为隐式序列化使私有属性公开了公共接口。
摘要
隐式序列化支持是一个爆炸性陷阱 💥,您无法从表面上或甚至在代码审查中轻松地发现。您必须检查整个对象生命周期,包括代码历史。几乎不可能发现。
这与禁用未明确设计为扩展的对象的继承支持非常相似——默认为final
。您可以在1、2中找到很好的理由。
TL;DR: 您可以获得更好的对象封装,您确切地知道您的对象是如何工作的。
解决方案
👉 用显式序列化替换隐式序列化。
1. 禁用隐式序列化
在PHP中,每个对象默认都是可序列化的,直到您明确表示否则。所以,让我们明确表示。
这对于值对象及其所有衍生品(如实体)来说最有意义。但是,禁用所有类的序列化支持并没有坏处,因为您永远不希望序列化一个服务。
打开您的PhpStorm设置,然后转到Editor > File and Code Templates > Files > PHP Class
并更新模板。
final class ${NAME} { use \Grifart\NotSerializable\NoSerialization; }
使用Composer在项目中使用此包
composer require grifart/not-serializable
它包含一个简单的特质,该特质禁用了PHP >7.4的序列化支持。这是因为序列化API已经发生了变化php-watch。
2. 显式实现序列化
当您需要序列化支持时,明确实现序列化API。
好事是,当默认情况下无法序列化时,有人必须做出决定,即我们需要它可序列化。
这还允许我们选择正确的序列化类型
a) 短期 & 简单
PHP序列化API:允许您快速开始。当您开始移动您的类时(严重依赖于我们的类的FQNs)会出错。
您可以通过以下方式走这条路:
- 移除特质
- 实现
__serialize()
和__unserialize()
方法。
b) 长期序列化
grifart/stateful:提供严格的版本控制、字段检查和FQN路由。设计用于
- 序列化可以在多年后反序列化的情况(例如不可变事件流)
- 当代码库发生重大变化时
- 为了最大限度的严格性,因此它会在您的开发机器上失败,而不是在生产环境中。
您可以通过以下方式走这条路:
- 移除特质
- 实现
Stateful
接口并使用composer require
grifart/stateful
进一步开发
- PhpStan / Rector 规则,用于查找/修复没有启用序列化且未显式实现序列化的类(如何仅应用于值对象及其派生类?)