Fix all PHPStan errors and empty the baseline#126
Conversation
Resolve every PHPStan (level max) error across the Platform, Saas, Ui and Rector tooling so phpstan-baseline.neon no longer ignores anything. Fixes address root causes only - no @PHPStan-Ignore, no baseline entries, no assert()/inline @var overrides, no silencing casts, and no type widening: - validate external LemonSqueezy payloads via runtime-checked accessors - give Symfony config arrays precise array-shape PHPDoc - add generics to Doctrine repositories and narrow query results - correct return types and remove dead instanceof/match branches - narrow dynamic test data with real PHPUnit assertions
Coverage Report for CI Build 27784202711Warning No base build found for commit Coverage: 19.432%Details
Uncovered Changes
Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
There was a problem hiding this comment.
Pull request overview
This PR eliminates all PHPStan (level max) findings across the Platform, Saas, Ui bundles and Rector tooling, resulting in an empty phpstan-baseline.neon (ignoreErrors: []). The changes primarily tighten runtime validation for dynamic arrays/payloads and improve type precision in repositories, config builders, and tests.
Changes:
- Add runtime-validated accessors / array-navigation helpers to safely handle
mixedpayload/config shapes (notably LemonSqueezy + config builder tests). - Tighten types across repositories, bundles, kernel/config processing, and cache usage to satisfy PHPStan max without baselines/ignores.
- Update Rector rules to use reflection guards and correct return/regex handling, plus remove the baseline content.
Reviewed changes
Copilot reviewed 47 out of 47 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/Bundle/Ui/Config/Builder/UiConfigBuilderTest.php | Adds section() helper to safely assert/navigate nested config arrays. |
| tests/Bundle/Saas/RemoteEvent/LemonSqueezyWebhookConsumerTest.php | Updates data provider param PHPDoc to match new event base types. |
| tests/Bundle/Saas/Feature/PlanFeatureManagerTest.php | Adjusts repository stub return shape + adds no-op save/remove for interface parity. |
| tests/Bundle/Saas/Config/Builder/SaasConfigBuilderTest.php | Adds arrayAt() helper and extra runtime assertions when navigating built config. |
| tests/Bundle/PlatformBundle/Response/RedirectResponseTest.php | Switches to runtime instance check instead of is_a() class-string assertion. |
| tests/Bundle/PlatformBundle/Config/SchemaGeneratorTest.php | Adds section() helper to safely assert/navigate generated schema arrays. |
| tests/Bundle/PlatformBundle/Config/Builder/PlatformConfigBuilderTest.php | Adds section() helper to safely assert/navigate built config arrays. |
| src/Tools/Rector/Rules/EnforcePlatformEntityRepositoryRector.php | Adds class_exists/interface_exists guard; updates docblock manipulation logic (contains issues noted in review). |
| src/Tools/Rector/Rules/AddGenericTemplateExtendsRector.php | Adds reflection guards, handles preg_match_all() false, and tightens arg handling. |
| src/Test/Traits/UsesFixturesTrait.php | Ensures decoded fixture JSON is an array and normalizes keys to strings. |
| src/Bundle/Ui/SolidWorxPlatformUiBundle.php | Makes createContainerExtension() non-nullable. |
| src/Bundle/Saas/Webhook/Converter/LemonSqueezyPayloadConverter.php | Validates payload shape via arrayValue/stringValue before converting to RemoteEvent types. |
| src/Bundle/Saas/Subscription/SubscriptionManager.php | Simplifies plan id string extraction via match (true) with proper narrowing. |
| src/Bundle/Saas/SolidWorxPlatformSaasBundle.php | Makes createContainerExtension() non-nullable. |
| src/Bundle/Saas/Repository/PlanRepository.php | Tightens find() signature and normalizes query results to Plan lists. |
| src/Bundle/Saas/Repository/PlanFeatureRepositoryInterface.php | Adds save/remove operations to align with actual usage. |
| src/Bundle/Saas/RemoteEvent/SubscriptionRemoteEvent.php | Adds payload array PHPDoc for constructor. |
| src/Bundle/Saas/RemoteEvent/SubscriptionPaymentRemoteEvent.php | Adds payload array PHPDoc for constructor. |
| src/Bundle/Saas/RemoteEvent/LemonSqueezyWebhookConsumer.php | Splits subscription vs payment event creation and validates gateway event id extraction. |
| src/Bundle/Saas/Integration/Options.php | Adds typed options array PHPDoc and toArray() return type detail. |
| src/Bundle/Saas/Integration/LemonSqueezy.php | Adds runtime-validated accessors for LemonSqueezy response shapes. |
| src/Bundle/Saas/Feature/PlanFeatureManager.php | Requires cache to be both Contracts cache + Adapter (so clear() is available); updates return doc to list<Plan>. |
| src/Bundle/Saas/EventSubscriber/SubscriptionEventSubscriber.php | Improves invalid-status exception message context. |
| src/Bundle/Saas/Event/TrialStartedEvent.php | Uses nullsafe access for nullable subscription DTO. |
| src/Bundle/Saas/Entity/Plan.php | Adds generic type PHPDoc for subscriptions collection. |
| src/Bundle/Saas/Dto/LemonSqueezy/Meta.php | Adds typed PHPDoc for customData. |
| src/Bundle/Saas/Doctrine/EventSubscriber/PlanIdSubscriber.php | Uses slug()->lower()->toString() instead of strtolower() on unicode string. |
| src/Bundle/Saas/Console/Command/SubscriptionListCommand.php | Hardens CLI option parsing and subscriber id formatting (proxy-safe class resolution). |
| src/Bundle/Saas/Config/SaasConfiguration.php | Adds is_string guards in config validation closures. |
| src/Bundle/Saas/Config/Builder/SaasConfigBuilder.php | Builds doctrine section via local accumulator to avoid mixed offset access. |
| src/Bundle/Platform/Twig/Components/Security/TwoFactor.php | Validates form data shape before reading secret. |
| src/Bundle/Platform/SolidWorxPlatformBundle.php | Makes createContainerExtension() non-nullable and asserts it implements ExtensionInterface. |
| src/Bundle/Platform/Security/TwoFactor/Traits/UserTwoFactor.php | Adds list type for backup codes, null-guards for email-based identifiers, and stable reindexing. |
| src/Bundle/Platform/Routing/LoginPageRouteLoader.php | Adds precise iterable array-shape PHPDoc and removes unnecessary string casts. |
| src/Bundle/Platform/Repository/UserRepository.php | Fixes generics doc, removes optional class override, and hardens refresh/load behaviors. |
| src/Bundle/Platform/Model/User.php | Throws if email is unset when resolving user identifier. |
| src/Bundle/Platform/Menu/Provider.php | Updates SplPriorityQueue generic priority type annotation to match stable ordering priority arrays. |
| src/Bundle/Platform/Menu/Options.php | Expands options array-shape to include extras and fixes malformed docblock opener. |
| src/Bundle/Platform/Kernel.php | Normalizes raw config to string-keyed arrays and hardens bundle registration + 2FA enablement detection. |
| src/Bundle/Platform/Form/Type/Security/TwoFactorVerifyType.php | Adds options array-shape PHPDoc and removes unsafe cast for secret. |
| src/Bundle/Platform/Form/Type/Security/LoginType.php | Adds options array-shape PHPDoc for configurable field names. |
| src/Bundle/Platform/Feature/FeatureValue.php | Safely stringifies array values by filtering to scalar/Stringable elements. |
| src/Bundle/Platform/DependencyInjection/SolidWorxPlatformExtension.php | Updates autoconfiguration callback signature to accept Reflector and guards method-only behavior. |
| src/Bundle/Platform/DependencyInjection/Extension/TwoFactorExtension.php | Removes incompatible default config value from method signature. |
| src/Bundle/Platform/DependencyInjection/CompilerPass/MenuCompilerPass.php | Adds runtime guard ensuring tag attributes are arrays before offset access. |
| src/Bundle/Platform/DependencyInjection/CompilerPass/AuthenticationCompilerPass.php | Adds runtime guards for firewalls/options argument shapes before array ops. |
| phpstan-baseline.neon | Clears baseline to ignoreErrors: []. |
Comments suppressed due to low confidence (1)
src/Tools/Rector/Rules/EnforcePlatformEntityRepositoryRector.php:192
while ($reflection = $reflection->getParentClass())uses a non-boolean condition (ReflectionClass|false), which is exactly what PHPStan flags under strict rules (while.condNotBoolean). Since this PR aims to keep the baseline empty, rewrite the loop to use an explicit boolean condition and advance the parent reflection inside the loop.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| continue; | ||
| } | ||
|
|
||
| $typeName = $value->type->type->name; |
Resolve the strict-rules, deprecation, symfony and phpunit extension errors that surface in CI (and were previously hidden by a stale local result cache), so the empty baseline holds under the full ruleset: - keep override params contravariant (PlanRepository::find, LemonSqueezyPayloadConverter::convert, Rector refactor() rules, LoginType/TwoFactorVerifyType::buildForm) and narrow via guards - add generics to form types (@extends AbstractType<mixed>) and the 2FA component (FormInterface<mixed>) - type Kernel::registerBundles via the non-deprecated Bundle class - replace short ternary, mixed/int|false booleans and a useless cast - rename getBackUpCodes() to match the interface; use getFile() - replace always-true assertInstanceOf checks with real assertions
Summary
Resolves every PHPStan (level max) error across the Platform, Saas, Ui and Rector tooling so that
phpstan-baseline.neonno longer ignores anything — the baseline is now empty (ignoreErrors: []).Previously the baseline suppressed 244 entries (406 underlying errors across 46 files). All of them are now fixed at the source.
Approach
Fixes address root causes only — no
@phpstan-ignorecomments, no baseline entries, noassert()/inline@varoverrides, no silencing casts, and no type widening:is_string/is_arrayguards that throw on shape mismatch) and split union-typed handling into per-type methods, instead of accessing offsets onmixed.array{...}-shape PHPDoc reflecting the Configuration tree, with genuine guards where values are dynamic.@extends ServiceEntityRepository<Entity>generics and narrowing ofmixedquery results to typed entities/lists; extendedPlanFeatureRepositoryInterfacewhere a missing method was the real cause.string|null→ guaranteed-stringvia guards, removed never-null/unused union members, and eliminated always-true/falseinstanceof/matchbranches.class_exists/interface_existsguards to narrowstring→class-stringbeforenew ReflectionClass(...), plus generics andint|falsehandling.SchemaGeneratorTest): added smallsection()/arrayAt()helpers that navigate dynamic nested arrays using real PHPUnitassertArrayHasKey/assertIsArray(genuine runtime validation, not type suppression).Verification
vendor/bin/phpstan→ [OK] No errors (--generate-baselineconfirms nothing left to baseline)vendor/bin/ecs check --fix→ cleanvendor/bin/phpunit→ 280 tests, 938 assertions, all passing