New rule ideas inspired by symfony/language-tools diagnostics

未关闭
#509 2 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
5/5
预计耗时
一周以上
新手友好度
35/100
Issue 类型
功能
描述清晰度
需要澄清
活跃度
活跃
技术栈
php, symfony
领域
devtools

调研方向

Start with src/Check/DiagnosticCodeRegistry.php and the diagnostics described in docs/features/*.rst, then compare the candidates with existing rules such as ContainerInterfaceUnknownServiceRule and the relevant extensions and maps named in the issue. A completed contribution would need a selected diagnostic, an agreed scope, and corresponding rule coverage rather than implementing all listed ideas.

由索引模型根据 Issue 内容生成。

描述

The new official symfony/language-tools (symfony-lsp) ships a set of 28 diagnostics, enumerated in src/Check/DiagnosticCodeRegistry.php and described in docs/features/*.rst. Several of them target PHP code and would make good phpstan-symfony rules. This issue maps their diagnostics against what we already have and ranks the candidates.

Already covered

  • service.not_found — covered by ContainerInterfaceUnknownServiceRule for $container->get() / controller get(). Language-tools additionally checks #[Autowire(service: '...')] attributes, which we don't touch at all (see candidate 1 below). They have no counterpart to our ContainerInterfacePrivateServiceRule or the four console rules.

A few more are covered indirectly by the type system rather than by a rule: the messenger handler/message pairing feeds MessengerHandleTraitReturnTypeExtension via MessageMap, and hasParameter() narrowing (with constantHassers) catches some parameter mistakes — but we never report an unknown parameter or a bad handler signature.

Candidates for new rules (ranked by effort-to-value)

  1. parameter.not_found — the most obvious gap. ParameterMap already exists; a rule reporting getParameter('unknown') would be exactly symmetric to ContainerInterfaceUnknownServiceRule. Today ParameterDynamicReturnTypeExtension silently falls back to the general union type when the parameter isn't in the map. Natural follow-up: cover #[Autowire(param: ...)] and #[Autowire('%foo%')] (we currently have zero Autowire attribute support).

  2. event.invalid_listener_method — class-level #[AsEventListener(method: 'onFoo')] where the method doesn't exist on the class. Pure reflection, no container XML needed, zero-config for users.

  3. route.not_found + route.missing_parameters — generateUrl() / redirectToRoute() / UrlGeneratorInterface::generate() with an unknown route name, or a literal parameter array missing required path/host placeholders (parameters with route defaults are optional; dynamic arrays are skipped). Routes aren't in the container XML, so this needs a new data source — a routerLoader config option following the consoleApplicationLoader pattern (load the Kernel/Router, read the RouteCollection). Probably the highest user value on this list.

  4. messenger.invalid_handler_signature — a handler whose first parameter type is incompatible with the message class Messenger routes to it (their version flags scalar params receiving object messages). MessageMapFactory already parses the messenger.message_handler tags, so a rule comparing the handler's parameter type against the handled message class is mostly plumbing we have.

  5. template.not_found — literal template names in render() / renderView() / #[Template] checked against Twig filesystem loader paths. Needs a new config surface (Twig paths or a Twig environment loader). Medium effort, high value.

  6. translation.placeholders (and optionally translation.not_found / domain_not_found) — for a known message, report %placeholder%s the message expects that a supplied literal parameter map doesn't provide (extra params allowed, dynamic arrays skipped). Needs loading the translation catalogue. Note language-tools ships missing-key diagnostics off by default because external translation providers can make the runtime catalogue incomplete — worth mirroring that caution.

  7. form.unknown_option — unknown literal options for a statically-known form type in createForm() / createNamed() / $builder->add(). Highest value of the "hard" ones, but it requires resolving the full OptionsResolver chain (language-tools does it via runtime indexing). A formFactoryLoader-style runtime approach à la ConsoleApplicationResolver is realistic; static resolution of configureOptions() is not.

Out of scope for PHPStan

The remaining diagnostics target non-PHP files: the five config.* codes (YAML config vs. bundle Configuration trees), env.* (mostly %env(...)% in YAML — though #[Autowire(env: 'json:FOO')] could give an unknown-processor rule a small PHP surface), security.unknown_firewall / security.unknown_provider (security.yaml), messenger.unknown_bus / messenger.unknown_transport (messenger.yaml), stimulus.unknown_controller, twig_callable.unknown_argument, twig_component.not_found, and importmap.unknown_entrypoint (Twig/HTML).

validation.unknown_constraint_option is largely moot in PHP: with attribute constraints PHPStan core already validates named constructor arguments, and the legacy options-array style is deprecated in Symfony 7.

主要语言
PHP
星标
806
派生
105
平均合并
1 小时 55 分钟
30 天内合并 PR
6

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

phpstan/phpstan-symfony 的其他 Issue

查看 phpstan/phpstan-symfony 的全部 Issue

相似的 Issue

更多 PHP Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。