0

0

PHP 中实现类型安全的泛型容器:DRY 原则与静态类型模拟指南

聖光之護

聖光之護

发布时间:2026-01-26 19:51:01

|

568人浏览过

|

来源于php中文网

原创

PHP 中实现类型安全的泛型容器:DRY 原则与静态类型模拟指南

本文讲解如何在 php(无原生泛型支持)中通过 psalm 模板注解(`@template`)模拟泛型容器,兼顾 dry 原则与类型专用性,避免继承导致的 lsp 违反,并提升 ide 提示与静态分析准确性。

在 PHP 开发中,我们常需构建多种语义明确的“专用容器”——如 CookieBag、CandyBag 或 ConfigBag——它们共享相同的基础操作(增、删、查、过滤),但又要求对存储值进行严格的类型约束。若采用传统继承方式(如让 CookieBag extends AbstractBag 并重写 get()/set() 的参数与返回类型),虽看似直观,实则违反里氏替换原则(LSP):父类 AbstractBag 声明接受任意 mixed 类型,子类却强制限定为 Cookie,导致依赖 BagInterface 的通用代码(如 GrandMa::giveCookie())在传入非 CookieBag 实例时发生运行时错误,且无法被类型系统提前捕获。

根本出路:放弃“继承特化”,转向“模板化泛化”
PHP 虽不支持原生泛型(如 GenericBag<Cookie>),但可通过行业标准的 PHPDoc 模板注解(@template)配合静态分析工具(如 Psalm、PHPStan)实现等效效果。这种方式不改变运行时行为,却能为开发者提供强类型提示、方法签名一致性保障和编译期错误预警。

以下是一个生产就绪的实现范例:

<?php

/**
 * @template T
 */
class GenericBag
{
    /** @var array<string, T> */
    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<string, T>
     */
    public function all(): array
    {
        return $this->bag;
    }

    /**
     * @param callable(mixed, string): bool $callback
     * @return array<string, T>
     */
    public function filter(callable $callback): array
    {
        return array_filter($this->bag, $callback, ARRAY_FILTER_USE_BOTH);
    }
}

使用时,无需创建子类,而是直接实例化并用 PHPDoc 明确类型参数:

<?php

// 定义 CookieBag —— 仅是带类型标注的 GenericBag 实例
/** @var GenericBag<Cookie> $cookieBag */
$cookieBag = new GenericBag();

// 此处 IDE 和 Psalm 将校验:只能存 Cookie 对象,返回值为 ?Cookie
$cookieBag->set('session', new Cookie('PHPSESSID', 'abc123'));
$cookie = $cookieBag->get('session'); // $cookie: ?Cookie

// 同理,CandyBag 复用同一类,零重复逻辑
/** @var GenericBag<Candy> $candyBag */
$candyBag = new GenericBag();
$candyBag->set('choco', new Candy('Dark Chocolate'));

// 在依赖注入场景中精准声明类型
class GrandMa
{
    /**
     * @param GenericBag<Cookie> $bag
     * @return void
     */
    public function giveCookie(GenericBag $bag): void
    {
        $bag->set('gift', new Cookie('grandma-cookie', 'yum')); // ✅ 类型安全
        // $bag->set('oops', new DateTime()); // ❌ Psalm 报错:Expected Cookie, got DateTime
    }
}

优势总结

零沫AI工具导航
零沫AI工具导航

零沫AI工具导航-AI导航新标杆,探索全球实用AI工具

下载

立即学习PHP免费学习笔记(深入)”;

  • 真正 DRY:所有逻辑集中于 GenericBag,无重复方法体;
  • 类型安全:借助 @template + @var / @param 注解,实现接近泛型的开发体验;
  • LSP 兼容:GenericBag<T> 是独立类型,不破坏接口契约;
  • 工具链友好:Psalm、PHPStan、IntelliJ/PhpStorm 均可识别并提供补全、跳转与错误检查;
  • 零运行时开销:注解仅用于静态分析,不参与执行。

⚠️ 注意事项

  • 确保项目已配置 Psalm(推荐 psalm.xml)或 PHPStan,并启用模板支持;
  • @template 必须声明在类级别,方法中通过 @param T / @return T 引用;
  • 数组键类型默认为 string,若需支持整数键,可扩展为 @template K of string|int;
  • 避免在 @var 注解中省略泛型参数(如 @var GenericBag $bag),否则将丢失类型特化能力。

通过这种模式,你既拥抱了 PHP 的动态本质,又借力现代工具链获得了强类型语言的严谨性与可维护性——这才是 PHP 生态中泛型思维的务实落地之道。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
phpstorm怎么导出项目
phpstorm怎么导出项目

phpstorm提供导出项目功能,步骤如下:打开phpstorm项目转到“项目”菜单选择“导出项目”选择导出格式指定导出位置选择导出范围勾选“包括依赖项”框(可选)单击“导出”完成导出。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

388

2024.04.08

phpStorm怎么运行
phpStorm怎么运行

本专题整合了phpstorm运行教程,阅读专题下面的文章了解更多相关内容。

89

2025.09.18

phpstorm开发环境搭建教程
phpstorm开发环境搭建教程

本专题整合了phpstorm开发环境搭建和运行项目教程,阅读专题下面的文章了解更多详细教程。

78

2025.09.18

phpstorm怎样运行php
phpstorm怎样运行php

本专题整合了phpstorm运行php相关教程,阅读专题下面的文章了解更多详细内容。

62

2025.09.18

phpstorm相关教程大全
phpstorm相关教程大全

本专题整合了phpstorm相关教程汇总,阅读专题下面的文章了解更多详细内容。

23

2026.01.15

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1031

2023.08.02

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1031

2023.08.02

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1031

2023.08.02

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

3

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 13.4万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号