标记接口是无方法的类型标签,用于运行时识别并触发自定义序列化或克隆策略;它语义明确、零开销,优于注解和trait,但需配合手动类型检查使用。

标记接口(Marker Interface)本身不定义任何方法,仅作为类型标签存在。在 PHP 中,它常用于运行时类型识别——比如告诉序列化器或克隆逻辑:“这个对象需要特殊处理”。但它不能自动触发行为,必须配合手动检查才能起作用。
序列化中用 MarkerInterface 区分可序列化策略
PHP 默认会序列化所有 public 属性,但有时你希望某些类跳过默认序列化,改用自定义逻辑(如只保留 ID、忽略敏感字段或转为 DTO)。这时可定义一个标记接口,再在 serialize() 方法中做类型判断:
- 定义接口:
interface SerializableAsIdOnly {} - 让目标类实现它:
class User implements SerializableAsIdOnly { ... } - 在统一序列化工具中检查:
if ($obj instanceof SerializableAsIdOnly) { return ['id' => $obj->id]; }
注意:不要覆盖 __serialize() 或 serialize() 后完全忽略标记接口——那会失去灵活性。更推荐在封装层(如 API 响应构造器)里统一判断,保持原始对象不变。
克隆时用 MarkerInterface 控制深浅行为
PHP 的 clone 默认是浅拷贝。若某类包含资源、代理对象或需隔离状态(如数据库连接、缓存句柄),你可能希望强制深克隆或跳过克隆。标记接口可作为决策依据:
- 定义:
interface DeepCloneable {} - 在
__clone()中不直接写逻辑,而由外部克隆工具检查:if ($obj instanceof DeepCloneable) { return deep_clone($obj); } - 避免在每个类里重复写
__clone(),把克隆策略集中到一个Cloner类中,提升可维护性
这样既解耦了业务类和克隆细节,又保留了按需定制的能力。
为什么不直接用 trait 或属性注解?
标记接口的优势在于语义明确、类型安全、运行时零开销。相比注解(需反射解析、影响性能)或 trait(会引入方法/属性,违背“纯标记”意图),接口最轻量。而且 IDE 和静态分析工具(如 PHPStan)能识别 instanceof 检查,提供类型提示和错误预警。
不过要留意:PHP 8.2+ 支持只读类和只读属性,某些场景下用只读性约束比标记接口更直接;但如果需跨多个不相关类统一识别行为,标记接口仍是简洁可靠的选择。










