Skip to content

PR A: Type constructor $options / $collaborators array shapes#43

Merged
turegjorup merged 1 commit into
developfrom
feature/typed-constructor-options
May 12, 2026
Merged

PR A: Type constructor $options / $collaborators array shapes#43
turegjorup merged 1 commit into
developfrom
feature/typed-constructor-options

Conversation

@turegjorup
Copy link
Copy Markdown
Collaborator

Summary

First of three small follow-ups (A → B → C) tightening the JSON-payload boundary to make a future level: 8level: max PHPStan bump achievable.

PR A targets the constructor's untyped array $options / array $collaborators parameters. The four reads we make — cacheItemPool, openIDConnectMetadataUrl, cacheDuration, jwt — currently flow as mixed into typed setters, producing 4 argument.type errors at level: max.

What changes

Adds PHPDoc array shapes:

/**
 * @param array{
 *     cacheItemPool?: CacheItemPoolInterface,
 *     openIDConnectMetadataUrl?: string,
 *     cacheDuration?: int,
 *     leeway?: int,
 *     allowHttp?: bool,
 *     ...
 * } $options
 * @param array{
 *     jwt?: \League\OAuth2\Client\Tool\RequestFactory,
 *     httpClient?: \GuzzleHttp\ClientInterface,
 *     ...
 * } $collaborators
 */
  • Keys are optional (?:) — the runtime array_key_exists + ConfigurationException checks still gate the required ones. PHPStan narrows their type when present.
  • ... trailing entry allows league/oauth2-client's extras (clientId, clientSecret, redirectUri, …) and Guzzle's forwarded options (timeout, proxy, verify) to pass through.

Also drops a redundant is_int($options['leeway']) runtime check — once the shape declares leeway?: int, the check becomes a function.alreadyNarrowedType tautology. Under declare(strict_types=1), PHP itself enforces the type via setLeeway(int $leeway).

Behaviour change

None. Pure static-analysis tightening at the constructor boundary.

Test plan

  • task test:coverage — 100% coverage on OpenIdConfigurationProvider (24/24 methods, 148/148 lines) preserved.
  • task analyze:php at level: 8 — no errors.
  • task analyze:php at level: max — 4 constructor argument.type errors gone (16 unrelated max-level errors remain, scheduled for PR B and PR C).
  • task lint:php — clean.
  • task lint:markdown — clean.
  • CI matrix on PR.

Follow-ups

  • PR B: narrow fetchJsonResource() and json_decode return types via is_array / is_string guards (~10 max-level errors).
  • PR C: narrow validateIdToken claim accesses + the remaining test-side Mockery / fixture-helper types (~6 max-level errors).

After all three land, level: max becomes achievable in a final config bump.

🤖 Generated with Claude Code

`OpenIdConfigurationProvider::__construct(array $options, array
$collaborators)` previously took untyped arrays — every
`$options['cacheItemPool']`, `$options['cacheDuration']`,
`$collaborators['jwt']`, `$options['openIDConnectMetadataUrl']` read
flowed as `mixed` into the typed setters
(`setCacheItemPool(CacheItemPoolInterface)`,
`setCacheDuration(int)`, `setRequestFactory(RequestFactory)`,
`setOpenIDConnectMetadataUrl(string)`), so PHPStan at `level: max`
produced 4 `argument.type` errors.

Add PHPDoc array shapes that declare the keys we read and their
types. Keys are marked optional (`?:`) because the runtime
`array_key_exists` + `ConfigurationException` checks still gate them
— but their type is narrowed when present so the setter calls
type-check. The `...` trailing entry lets the league/oauth2-client
extra options (`clientId`, `clientSecret`, `redirectUri`, …) and
Guzzle's forwarded options (`timeout`, `proxy`, `verify`) pass
through without flagging.

Drops a now-redundant `is_int($options['leeway'])` runtime check
that became a `function.alreadyNarrowedType` tautology once the
shape declared `leeway?: int`. Under `declare(strict_types=1)`, PHP
itself enforces the type via `setLeeway(int $leeway)`.

Behaviour unchanged — purely a static-analysis tightening at the
constructor boundary. Removes 4 errors when PHPStan is run at
`level: max`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (1774bc0) to head (c715359).

Additional details and impacted files
@@             Coverage Diff             @@
##             develop       #43   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
+ Complexity        62        61    -1     
===========================================
  Files              1         1           
  Lines            172       172           
===========================================
  Hits             172       172           
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@turegjorup turegjorup self-assigned this May 12, 2026
@turegjorup turegjorup requested a review from jekuaitk May 12, 2026 09:52
@turegjorup turegjorup merged commit ff00903 into develop May 12, 2026
16 checks passed
@turegjorup turegjorup deleted the feature/typed-constructor-options branch May 12, 2026 10:30
@turegjorup turegjorup mentioned this pull request May 12, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants