为Symfony 7.1项目创建可复用SDK,必须从零构建PSR-4自动加载的Composer库,声明"type": "library",仅依赖symfony/-contract和psr/契约包,通过构造函数注入HttpClientInterface等接口,强制类型声明、final类、readonly数据对象,并确保测试在无框架环境下运行。

要为 Symfony 7.1 项目创建可复用的 SDK(即封装特定领域能力、对外提供 API 的独立 PHP 组件),不能直接用 symfony new 或 composer create-project 生成应用级骨架——必须从零构建一个符合 PSR-4 自动加载、含明确命名空间、带类型声明与测试入口的 Composer 包,且需规避框架绑定、禁止依赖 symfony/framework-bundle 等运行时组件。
初始化 SDK 包结构
在空目录中执行:composer init → 按提示输入包名(如 acme/payment-sdk)、描述、作者、license(建议 MIT)、最低稳定版本(^7.1)→ 选择「否」跳过自动创建 autoload → 手动编辑 composer.json,确保包含:
【"type": "library"】 字段必须显式声明,否则 Packagist 会拒绝收录;autoload 部分写成:"autoload": {"psr-4": {"Acme\PaymentSdk\": "src/"}},这决定了类文件必须放在 src/ 下且以 AcmePaymentSdk 开头。
创建 src/ 目录并放入首个类:src/PaymentClient.php,顶部必须声明 declare(strict_types=1);,类使用 final 修饰(避免被继承破坏契约),方法参数与返回值全部标注类型。
注入 Symfony 核心契约,不引入框架
SDK 要调用 HTTP、序列化、缓存等能力,但绝不能 require symfony/framework-bundle —— 否则使用者会被强制拖入完整框架。正确做法是只依赖 Symfony 官方发布的「契约(Contracts)」包:
运行 composer require symfony/http-client-contract symfony/serializer-contract symfony/cache-contract。
这些包体积极小(无实现,只有接口),允许使用者自由选择具体实现:比如用 symfony/http-client、php-http/guzzle6-adapter 或自定义客户端,SDK 只接收 HttpClientInterface 实例,完全解耦。
若 SDK 内部需日志记录,也只 require psr/log,而非 symfony/logger。
编写可测试的对外接口
第一步:在 src/ 下新建 Exception/ 子目录,定义 PaymentException.php 和 InvalidAmountException.php,全部继承 RuntimeException,不引入任何 Symfony 异常基类。
第二步:创建 src/ClientBuilder.php,提供静态工厂方法:public static function create(string $apiKey, HttpClientInterface $client): self —— 强制调用方传入已配置好的 HTTP 客户端,不隐藏内部依赖。
第三步:在 src/ 根下建 Payload/ 目录,放 ChargeRequest.php,用 readonly 属性 + 构造函数参数验证金额是否 >0,拒绝无效数据进入核心逻辑。
第四步:实现 PaymentClient::charge(ChargeRequest $request): ChargeResponse,内部只做三件事:序列化请求、发送 HTTP POST、反序列化响应。所有第三方交互点都通过构造函数注入,不 new 任何具体类。
配置 PHP-CS-Fixer 与 PHPUnit
方法一:本地开发用
执行 composer require --dev phpunit/phpunit:^10.5 php-cs-fixer/php-cs-fixer:^3.26 → 创建 .php-cs-fixer.php,内容为:return (new PhpCsFixerConfig())->setRules(['@PSR12' => true, 'array_syntax' => ['syntax' => 'short']])->setFinder(PhpCsFixerFinder::create()->in('src')->in('tests')); → 运行 vendor/bin/php-cs-fixer fix 自动格式化。
方法二:CI 流水线用
在 phpunit.xml.dist 中指定 bootstrap="vendor/autoload.php",测试用例必须覆盖异常路径(如空 API key)、边界值(金额为 0.01)、HTTP 错误码(401/422)。
【测试必须在无 Symfony 运行时环境下执行】,即 phpunit 命令不能触发 bin/console 或加载 config/bundles.php,否则说明 SDK 仍隐式依赖框架。
发布前的最后验证
① 运行 composer validate,确认无 schema 错误且 license 字段合法;
② 执行 composer install --no-dev --dry-run,检查生产环境安装时是否只拉取 runtime 依赖(不含 phpunit);
③ 手动创建测试项目:mkdir test-consumer && cd test-consumer && composer init → 添加 "repositories": [{"type": "path", "url": "../your-sdk"}] → composer require acme/payment-sdk:dev-main → 编写最简调用代码,确认能成功实例化 ClientBuilder::create() 并调用 charge();
④ 删除 vendor/ 后运行 composer show acme/payment-sdk,输出应显示 type : library 且 requires 列表里只有 php、symfony/*-contract 和 psr/*。


















