PHP 中实现类型安全的泛型容器:DRY 原则与静态类型提示实践指南
技术百科
花韻仙語
发布时间:2026-01-26
浏览: 次 本文介绍如何在 php 缺乏原生泛型支持的前提下,通过 psalm 等静态分析工具的模板注解(`@template`)模拟类型专用容器,兼顾代码复用性(dry)与方法签名的类型准确性,避免 lsp 违反风险。
在 PHP 开发中,我们常需构建多种语义明确、类型受限的“容器”类(如 CookieBag、CandyBag),以提升领域建模清晰度与 IDE 支持体验。但若采用继承方式强行覆盖 get()/set() 的参数与返回类型(如将 mixed 替换为 ?Cookie),虽看似直观,实则违反里氏替换原则(LSP):子类无法完全替代父类使用场景,导致依赖 BagInterface 的通用逻辑(如 GrandMa::giveCookie())在传入 CookieBag 时因类型契约不兼容而失效。
PHP 目前不支持原生泛型(如 GenericBag
以下是一个可落地的实践方案:
*/
private array $bag = [];
public function has(string $key): bool
{
return array_key_exists($key, $this->bag);
}
/**
* @param string $key
* @param T|null $fallback
* @return T
*/
public function get(string $key, $fallback = null)
{
return $this->has($key) ? $this->bag[$key] : $fallback;
}
/**
* @param string $key
* @param T $value
* @return static
*/
public function set(string $key, $value): self
{
$this->bag[$key] = $value;
return $this;
}
/**
* @param string $key
* @return void
*/
public function del(string $key): void
{
unset($this->bag[$key]);
}
/**
* @return array
*/
public function all(): array
{
return $this->bag;
}
/**
* @param callable(mixed, string): bool $callback
* @return array
*/
public function filter(callable $callback): array
{
return array_filter($this->bag, $callback, ARRAY_FILTER_USE_BOTH);
}
} ✅ 关键要点说明:
- @template T 声明该类支持一个泛型类型参数;
- @var array
明确内部存储结构的键值类型关系; - 所有涉及值操作的方法均通过 @param T / @return T 绑定
类型,确保 get() 返回值与 set() 输入值类型一致;
- 使用 static 作为返回类型(而非 self)更准确表达“返回当前特化实例”,利于链式调用推导;
- 注解语法兼容 Psalm、PHPStan 及主流 IDE(如 PHPStorm),可在编码阶段获得类型检查与自动补全。
使用时,无需创建子类,直接实例化并借助类型注解声明语义:
*/ $cookieBag = new GenericBag(); /** @var GenericBag*/ $candyBag = new GenericBag(); // ✅ 正确:类型匹配 $cookieBag->set('session', new Cookie()); $cookie = $cookieBag->get('session'); // ❌ Psalm/PHPStan 将报错:Expected Cookie, got Candy // $cookieBag->set('bad', new Candy()); class GrandMa { /** * @param GenericBag $bag */ public function giveCookie(GenericBag $bag): void { $bag->set('gift', new Cookie()); // ✅ 类型安全调用 } }
⚠️ 注意事项:
- 此方案不提供运行时类型强制,所有检查依赖静态分析工具。务必集成 Psalm 或 PHPStan 到 CI 流程中;
- 避免在 GenericBag 内部对 T 做运行时类型判断(如 is_a($value, T::class)),因 T 仅为注解,PHP 解析器不可见;
- 若需运行时类型保障(如防止非法写入),可结合构造器参数或工厂方法注入类型约束逻辑,但会牺牲部分简洁性;
- 不推荐为每种类型创建继承子类(如 CookieBag extends GenericBag),这既破坏 DRY,又重蹈 LSP 覆辙。
总结而言,在 PHP 当前生态下,@template + 工具链支持是最务实的泛型模拟方案:它让抽象容器真正“一次编写、多处强类型复用”,在不增加运行时开销的前提下,显著提升大型项目的可维护性与协作效率。
# 工具
# go
# c++
# String
# 编码
# 子类
# Static
# lsp
# session
# php
# cookie
# 父类
# Array
# phpstorm
# typescript
# 代码复用
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- win11 OneDrive怎么彻底关闭 Win1
- Win11怎样激活系统密钥_Win11系统密钥激活
- Win11怎么关闭自动修复_跳过Win11开机自动
- Python性能剖析高级教程_cProfileLi
- 如何使用 Python 合并文件夹内多个 Exce
- 如何使用Golang操作指针变量_Golang解引
- Win10电脑C盘红了怎么清理_Windows10
- How to Properly Use NumPy
- Windows10系统怎么查看硬盘健康_Win10
- c# 如何用c#实现一个支持优先级的任务队列
- Mac如何开启夜览模式_Mac护眼模式设置与定时
- Go 语言标准库为何不提供泛型 Contains
- Windows 10怎么把任务栏放在屏幕上方_Wi
- 短链接怎么自定义还原php_修改解码规则适配需求【
- Win11怎么设置虚拟内存最佳大小_Windows
- Python字符串操作教程_切片拼接与格式化详解
- 当网站SEO排名下降时,如何应对?
- php命令行怎么运行_通过CLI模式执行PHP脚本
- mac怎么看硬盘大小_MAC查看磁盘存储空间与文件
- Windows10如何更改桌面背景_Win10个性
- Windows10系统怎么查看运行时间_Win10
- c++怎么使用std::unique实现去重_c+
- Win11截图快捷键是什么_Win11自带截图工具
- Windows资源管理器总是卡顿或重启怎么办?(修
- Win11玩游戏全屏闪退怎么办_Win11全屏优化
- 如何在 ACF 中正确更新嵌套多层的 Group
- Python对象比较排序规则_集合使用说明【指导】
- Win10怎样设置闹钟贪睡时间 Win10闹钟贪睡
- 如何处理“XML格式不正确”错误 常见XML we
- Win11怎么设置鼠标宏_Win11鼠标按键自定义
- Win11怎么更改账户头像_Windows 11自
- 微信企业付款回调PHP怎么接收_处理企业付款异步通
- Windows如何设置登录时的欢迎屏幕背景?(锁屏
- php下载安装包怎么选_threadsafe与nt
- Win11怎么设置触控板手势_Windows11三
- c++ try_emplace用法_c++ map
- 如何使用Golang写入二进制文件_Golang
- c++的STL算法库find怎么用 在容器中查找指
- C++ static_cast和dynamic_c
- php内存溢出怎么排查_php内存限制调试与优化方
- Linux如何安装Golang环境_Linux下G
- 如何使用Golang实现跨域请求支持_Golang
- 如何优化Golang内存分配与GC调度_Golan
- Python文件操作优化_大文件与流处理解析【教程
- Windows10系统怎么查看防火墙状态_Win1
- Win11如何卸载OneDrive_Win11卸载
- mac怎么右键_MAC鼠标右键设置与触控板手势技巧
- Win11相机打不开提示错误怎么修_相机权限开启与
- Win11怎么关闭任务栏小组件_Windows11
- Python正则表达式实战_模式匹配说明【教程】


QQ客服