ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PHPStan 错误标识符 new.internalInterface 完全解读:禁止实例化 @internal 内部接口

PHPStan 错误标识符 new.internalInterface 完全解读:禁止实例化 @internal 内部接口 开发工具代码质量静态分析【免费下载链接】phpstanPHP Static Analysis Tool - discover bugs in your code without running it!项目地址https://gitcode.com/gh_mirrors/ph/phpstan点击查看免费下载本篇技术指南围绕 PHPStan 错误标识符new.internalInterface展开讲解静态分析器在实例化new一个标记为internal的内部接口场景下如何报错、为何报错以及如何修复。读完本文你将掌握 PHPStan 内部 API 约束体系的判定逻辑、new前缀标识符家族的组织方式以及在实际项目中安全处理内部接口引用的完整实战方案。一、错误标识符总览什么是new.internalInterfacenew.internalInterface是 PHPStan 在实例化上下文instantiation context中检测到内部接口引用时报告的错误标识符。其官方定义为Referencing an internal interface in an instantiation context.在实例化上下文中引用了内部接口。该标识符的元数据在 website/errors/new.internalInterface.md 的 frontmatter 中声明了三个关键属性title: new.internalInterface错误标识符全名ignorable: true该错误可通过ignoreErrors配置忽略unlikely: true该标识符被标记为不太可能单独出现——因为接口根本无法实例化此场景通常会被更基础的new.interface错误优先命中详见下文第三节。从错误标识符的命名规则看参见 CLAUDE.md 中的前缀参考表前缀new明确对应new ClassName()实例化表达式。同样的前缀还派生出new.interface、new.internalTrait、new.noConstructor、new.nonObject、new.privateConstructor、new.protectedConstructor等一组与实例化相关的标识符见 website/src/errorsIdentifiers.json。从源码结构看该标识符的来源根据错误标识符与规则类的映射表 website/src/errorsIdentifiers.jsonnew.internalInterface由规则类PHPStan\Rules\InternalTag\RestrictedInternalClassNameUsageExtension产生。该类属于 phpstan-src 仓库中src/Rules/InternalTag/目录下的内部标签限制规则族——该规则族专门负责检查internal标记类型在各种使用位置的越权访问与new.internalInterface同族的标识符还包括assert.internalInterface、instanceof.internalInterface、catch.internalInterface、property.internalInterface、staticMethod.internalInterface等二十余个这些文档均收录于 website/errors 目录。二、触发场景完整的可复现示例文档给出了最小可复现示例见 website/errors/new.internalInterface.md?php declare(strict_types 1); namespace Vendor { /** internal */ interface Handler { public function handle(): void; } } namespace App { $handler new \Vendor\Handler(); // error: Instantiation of internal interface Vendor\Handler. }该示例包含两个关键要素接口被标记为internalVendor命名空间中的Handler接口通过 PHPDoc 标签internal声明为包内部类型在外部命名空间进行实例化App命名空间中通过new \Vendor\Handler()试图实例化该接口。PHPStan 对第二个要素——new实例化表达式中的\Vendor\Handler类型引用——进行检查时发现它指向一个internal接口于是上报new.internalInterface错误提示信息为Instantiation of internal interface Vendor\Handler.。需要注意的是示例中的$handler new \Vendor\Handler();只是触发检测的最小代码形态。实际项目中internal接口的越权使用不局限于实例化凡是类型名出现在任何使用位置都可能被同族标识符命中——例如instanceof检查对应instanceof.internalInterface见 website/errors/instanceof.internalInterface.mdphpstan-assert断言对应assert.internalInterface见 website/errors/assert.internalInterface.md。理解这一族错误本质上就是理解 PHPStan 对internal契约的强制约束。三、为什么很少单独出现与new.interface的优先级关系原文档明确指出一个重要的实践经验见 website/errors/new.internalInterface.mdIn practice, this is typically reported asnew.interfacebecause interfaces cannot be instantiated at all. Thenew.internalInterfaceidentifier is reported when the internal access violation is the primary concern.翻译过来即实际场景中该错误通常表现为new.interface因为接口从根本上就无法实例化只有当内部 API 越权访问成为首要问题时PHPStan 才报告new.internalInterface。对比new.interface的文档见 website/errors/new.interface.md?php declare(strict_types 1); interface LoggerInterface { public function log(string $message): void; } $logger new LoggerInterface();new.interface描述的问题是接口不能被实例化——这是 PHP 语言层面的硬性约束接口只定义契约、不提供方法实现new一个接口名会在运行时直接触发致命错误。而new.internalInterface描述的问题是内部接口被外部越权引用——这是 API 设计层面的约束internal类型不保证向后兼容。两个标识符的关系可以归纳为维度new.interfacenew.internalInterface核心问题接口无法实例化语言语义内部类型被越权使用API 契约判定来源InstantiationRule见 errorsIdentifiers.json 中new.nonObject等同类规则RestrictedInternalClassNameUsageExtension内部标签规则族报告时机只要new目标是接口即报告仅当内部访问违规是首要关注点时报告严重程度运行期必然致命错误可能当前能运行但未来版本随时会坏因此如果你的代码同时命中两者PHPStan 通常优先报告new.interface接口实例化本身就不合法只有内部契约的破坏才是分析重点时new.internalInterface才作为独立标识符浮出水面。这也解释了 frontmatter 中unlikely: true标记的缘由。四、为什么会报告internal的 API 契约语义原文档Why is it reported?一节给出的核心解释见 website/errors/new.internalInterface.md内部接口正在被实例化且调用方位于其根命名空间之外标记为internal的接口不打算在定义它的包或命名空间之外被使用内部接口可能在未来的版本中无通知地变更或被移除。这三点构成了 PHPStan 报告此类错误的设计动机internal是 PHP 生态中广泛认可的一种实现细节标记。第三方包用它在公开 API 表面之下隐藏实现细节而外部代码一旦直接引用internal类型就形成了一种脆弱的、对实现细节的隐式依赖——当包的下一个版本重构内部结构时这类引用会静默损坏。PHPStan 通过静态分析在编译期/分析期就暴露出这种隐患而不是等到升级依赖后由运行时错误来惩罚。从同类文档的表述可以更完整地理解这套契约如 website/errors/assert.internalInterface.md 所述依赖内部类型会在你的断言、实例化、instanceof等位置形成对会无通知变更的实现细节的脆弱依赖。这本质上是把 API 边界问题前置到了静态分析阶段。五、如何修复两种标准解法原文档给出了两种修复路径见 website/errors/new.internalInterface.md。解法一改用包的公开 API不要直接new内部接口而是使用包公共 API 提供的工厂方法或具体类namespace App { - $handler new \Vendor\Handler(); $handler \Vendor\HandlerFactory::create(); }这一方案的要点是Vendor包既然把Handler标记为internal说明它期望调用方通过公开的创建入口工厂、构造器、DI 容器、服务定位器等获得实例而不是自己直接实例化。HandlerFactory::create()返回的具体类型可能是Handler接口的公开实现类也可能是满足公开契约的别的类型——总之类型引用的书写位置从越权使用内部类型变成了合法使用公开 API。解法二从源头开放内部接口如果你自己就是该内部接口的维护者可以考虑两条路将接口设为公开移除internal标记将其纳入包的正式公共 API 并承诺向后兼容提供公开替代品保留内部接口不动另设计一个公开接口或抽象类并让内部实现实现它。如果两个方向都不可行例如你只是第三方代码的使用者、无法影响上游包文档建议与包维护者沟通请求为你的用例提供公开 API。修复时的通用原则结合 CLAUDE.md 中的修复偏好顺序此类错误的修复优先级应为修复真正的 bug——使用公开类型替换内部类型引用如果问题出在类型声明上用原生 PHP 类型声明或 PHPDoc 类型param、return、var收紧类型在函数体内做类型收窄只有规则可配置时才考虑调整 PHPStan 配置。注意不要通过ignoreErrors忽略该错误来解决问题——ignorable: true只说明技术上允许忽略但忽略意味着把脆弱依赖继续留在代码里与 PHPStan 报告它的初衷背道而驰。六、进阶new.internalInterface与new.internalTrait的同族关系在 website/src/errorsIdentifiers.json 中可以看到紧邻new.internalInterface的是new.internalTrait二者映射到同一个规则类RestrictedInternalClassNameUsageExtension且源码位置完全一致src/Rules/InternalTag/RestrictedInternalClassNameUsageExtension.php#L65。这说明 PHPStan 内部标签规则族对内部类名在实例化上下文中的使用采用统一判定只要new的目标是被internal标记的类、接口或 trait就由同一规则上报new.internal*系列标识符。同理new.interface则聚焦接口不可实例化这一与internal无关的通用问题。new前缀下的标识符家族映射自 errorsIdentifiers.json还包括标识符含义new.noConstructor目标类没有构造函数对应InstantiationRulenew.nonObjectnew的目标不是可实例化对象new.privateConstructor/new.protectedConstructor构造函数可见性不允许外部实例化理解这条家族脉络有助于你在排查实例化相关报错时快速定位new.internal*看 API 契约new.interface看语言语义new.privateConstructor等看访问控制——三者是不同层面的约束修复策略也各不相同。七、实践要点总结快速识别看到错误信息Instantiation of internal interface FQCN.即对应new.internalInterface标识符的 frontmatter 文档位于 website/errors/new.internalInterface.md。判定根因检查被new的类型是否带有internal标记以及调用代码是否位于该类型定义命名空间之外。优先修复顺序换用工厂/公开实现 → 开放接口若你拥有该类型→ 与上游维护者沟通申请公开 API。识别连带风险即使代码当前能运行internal引用也是定时炸弹升级依赖时可能随时失效因此不应以忽略错误了事。举一反三同样的internal契约约束遍布instanceof、catch、phpstan-assert、属性声明、静态方法调用等所有类型使用位置分别对应instanceof.internalInterface、catch.internalInterface、assert.internalInterface、property.internalInterface、staticMethod.internalInterface等同族标识符完整列表可查阅 website/src/errorsIdentifiers.json 与 website/errors 目录下的对应文档。延伸阅读想深入了解 PHPStan 错误标识符文档体系的生成规则与命名前缀约定可阅读 website/errors/CLAUDE.md想了解同族internal约束在instanceof场景下的表现可对照 website/errors/instanceof.internalInterface.md。赞分享开发工具代码质量静态分析【免费下载链接】phpstanPHP Static Analysis Tool - discover bugs in your code without running it!项目地址https://gitcode.com/gh_mirrors/ph/phpstan点击查看免费下载相关推荐PHPStan 错误标识符 instanceof.internalInterface 详解instanceof 检查引用了 internal 内部接口PHPStan 错误标识符 instanceof.internalInterface 详解instanceof 检查引用了 internal 内部接口 本文开发工具代码质量静态分析WinUI 3 应用动效与打磨实战主题过渡、连接动画与动画纪律WinUI 3 应用动效与打磨实战主题过渡、连接动画与动画纪律 导读 本文围绕 winui app 技能库中的动效参考文档 motion animations开发工具代码质量静态分析PHPStan 错误标识符 interface.extendsInternalEnum 全解析接口继承 internal 枚举PHPStan 错误标识符 interface.extendsInternalEnum 全解析接口继承 internal 枚举 导读 interface.e开发工具代码质量静态分析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进