Clear phpstan-baseline.neon by fixing the underlying test issues#41
Merged
Conversation
`$this->provider` is initialized in `setUp()` and never assigned null, so the nullable type was misleading. Dropping the `?` removes 25 of the 46 grandfathered errors in `phpstan-baseline.neon` — every `Cannot call method X() on …|null` was a downstream consequence of that one type. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`shouldReceive(...)->andReturn(...)` is the Mockery fluent API. The return type of `shouldReceive()` is declared as `Mockery\ExpectationInterface|Mockery\HigherOrderMessage`, and the chained methods only exist on `ExpectationInterface` — PHPStan can't tell the call sites are safe without type stubs. `phpstan/phpstan-mockery` is the official PHPStan extension shipping those stubs. Adding it removes 8 entries from `phpstan-baseline.neon`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`validateIdToken()` is declared `: object` in the library — PHPStan
can't see the dynamic `nonce` / `aud` properties of the decoded JWT
payload. Annotate each call site's local `$claims` with an
`object{nonce, aud}` shape so the subsequent property reads type-
check. Removes 5 entries from `phpstan-baseline.neon`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`(string) file_get_contents(...)` silently coerces the `false` returned when a fixture is missing into an empty string, which then flows into `json_decode` and produces `null`. Tests downstream of that report confusing assertion failures instead of "the fixture isn't there". Similarly, `(string) parse_url(...)` masks malformed-URL failures. Replaces the three duplicate fixture loads with a single `loadMockFixture()` helper that uses `assertNotFalse` and `assertIsArray` to fail the test at the actual error boundary. The `parse_url` call adds an `assertIsString` guard for the same reason. Removes 4 entries from `phpstan-baseline.neon`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small cleanups, each removing one entry from the baseline: - `testCheckResponseSuccess` swaps `assertTrue(true)` (which PHPStan correctly flags as a tautology) for `expectNotToPerformAssertions()`, the PHPUnit idiom for "this test verifies the call doesn't throw". - `testAbstractBaseImplementsMarker` was using a constant-folded `is_subclass_of(...)` call wrapped in `assertTrue`. Replaced with a runtime `ReflectionClass::getInterfaceNames()` check + `assertContains` — PHPStan can't fold the reflection result, and the assertion stays semantically meaningful (catches a future regression that removes the marker from the deprecated abstract). - `MockJWT::$leeway` declares a type (`?int`) so the property satisfies PHPStan's missingType.property rule. Baseline now empty — see follow-up commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The four predecessor commits cleared the 46 grandfathered errors that `phpstan-baseline.neon` was absorbing. With the baseline at zero entries the file serves no purpose, so drop it (and its include line from `phpstan.neon`) rather than keep an empty alias around. PHPStan still runs at level 8 across `src` and `tests` and remains clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #41 +/- ##
===========================================
Coverage 100.00% 100.00%
Complexity 62 62
===========================================
Files 1 1
Lines 172 172
===========================================
Hits 172 172
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
5 tasks
- `php-cs-fixer` (@symfony ruleset) flagged a redundant `@param` PHPDoc on a typed parameter in `getMockHttpSuccessResponse`. Auto-fixed. - `composer normalize` reorders `phpstan-mockery` alphabetically within `require-dev` (it landed at the bottom after the manual `composer require`, between `phpunit/phpunit` and nothing). - The `changelog` CI step requires every PR to touch `CHANGELOG.md`. Added a "Tooling" subsection to `[Unreleased]` summarising what this PR's six commits did (PHPStan scope extension, mockery extension install, baseline cleanup). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jekuaitk
approved these changes
May 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR #40 added
tests/to PHPStan'spathsand grandfathered 46 pre-existing level-8 issues intophpstan-baseline.neonwith a note that the baseline should shrink over time. This PR pays that debt down — to zero — and removes the baseline file.PHPStan still runs at level 8 across
srcandtestsand remains clean.How the 46 errors were cleared
Six small commits, each isolated and easy to review:
$providertest property —$this->provideris set insetUp()and never assignednull; the?OpenIdConfigurationProviderdeclaration was the root cause of 25Cannot call method X() on …|nullerrors. Single-character fix, 25 baseline entries removed.phpstan/phpstan-mockeryfor Mockery type stubs — the official extension shipping stubs for theshouldReceive(...)->andReturn(...)fluent API. 8 entries removed.validateIdTokenclaim accesses via@var— annotate each call site's local$claimswith anobject{nonce, aud}shape so the subsequent property reads type-check. 5 entries removed.(string)coercion offile_get_contents/parse_url(which masksfalsereturns as empty strings) with aloadMockFixture()helper usingassertNotFalse/assertIsArray, plus anassertIsStringguard onparse_url. 4 entries removed.expectNotToPerformAssertions()replacingassertTrue(true)tautology; reflection-based marker-implementation check replacing constant-foldedis_subclass_oftautology;?inttype onMockJWT::\$leeway. 4 entries removed.phpstan-baseline.neon— and drop its include line fromphpstan.neon.Why no
(string)casts forstring|falsereturnsA silent
(string)cast offalsecoerces to the empty string, which then flows into JSON / URL parsing and produces confusing downstream assertion failures rather than the actual diagnostic ("file not readable", "URL has no query"). Commit 4 handles this at the actual error boundary with PHPUnit assertions, so a missing fixture or malformed URL fails the test with a precise message.Test plan
task test:coverage— 87 tests, 135 assertions; 100% coverage onOpenIdConfigurationProvider(24/24 methods, 148/148 lines) preserved.task analyze:php— PHPStan max level 8, no errors, no baseline.task lint:php— clean.Follow-up still owed
Two pre-existing
(string)casts insrc/Security/OpenIdConfigurationProvider.php(lines 373 and 472) coerce mixed JSON-decoded values to string. They're not thestring|falseantipattern — the source is JWKS / OIDC discovery payload whose schema specifies strings — but replacing them with explicitis_stringguards would catch any future malformed-payload case at the actual boundary. Will follow up separately so this PR stays scoped to the test/baseline cleanup.🤖 Generated with Claude Code