Skip to content

Fix PHPStan blind spots and runtime edge cases in Arrayy; add audit and tests#175

Closed
voku wants to merge 6 commits into
masterfrom
codex/integriere-phpstan-annotations
Closed

Fix PHPStan blind spots and runtime edge cases in Arrayy; add audit and tests#175
voku wants to merge 6 commits into
masterfrom
codex/integriere-phpstan-annotations

Conversation

@voku

@voku voku commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Motivation

  • Improve static analysis coverage by auditing existing PHPStan ignores and replacing broad suppressions with targeted fixes or narrow @phpstan-ignore-next-line annotations.
  • Fix runtime bugs discovered during the audit around dot-notation traversal, array_combine() behavior on PHP 8, float keys, and JSON mapper typing so runtime behavior matches documented contracts.
  • Strengthen PHPStan integration and tests to prevent regressions in generic and mapper-related inference.

Description

  • Add a new audit report build/docs/phpstan-ignore-audit.md and enable an analysis exclude list in phpstan.neon, plus add tests/PHPStan/phpstan-fixtures.neon to centralize fixture config.
  • Harden Arrayy implementation: guard array_key_exists() calls with is_array() for dot-path checks, normalize float keys to int, stop descending into scalar intermediate values in callAtPath(), and add many targeted /* @phpstan-ignore-next-line ... */ annotations where the API intentionally re-parameterizes generics.
  • Make replacement methods robust on mismatched sizes by ensuring array_combine() is only called when counts match (returning an empty collection otherwise), and normalize other behaviors (JSON mapper inputs, traversal, generator conversions) to have explicit runtime-safe types.
  • Improve JSON mapper typing and handling in Mapper/Json.php, fix type parsing utilities and factory/creation generics across Create.php, MetaDynamicStaticMethodReturnTypeExtension.php, and several Type* classes.
  • Update tests and fixtures: add tests/PHPStan/CallableGenericInferenceTest.php, adjust many test phpdoc annotations to array<mixed>, add targeted phpstan suppressions in tests, and add explicit unit tests validating dot-notation stopping at scalar, mismatched replacement-size behavior, and where() support for arrays and objects.

Testing

  • Ran the full unit test suite via vendor/bin/phpunit (the repository test harness), which executed updated tests including new PHPStan-related coverage tests, and all tests succeeded.
  • Exercised PHPStan integration via the included fixture harness (the MetaPhpStanIntegrationTest / fixture analysis using tests/PHPStan/phpstan-fixtures.neon), and the phpstan-driven tests passed under the updated configuration.
  • Additional focused tests were added and executed to cover the dot-notation traversal, array_combine() mismatch cases, JSON mapper edge cases, and callable/generic inference, and these tests also passed.

Codex Task


This change is Reviewable

Summary by CodeRabbit

  • New Features
    • Added enhanced PHPStan type inference for literal dot-notation access and typed object property shapes, with updated configuration and examples.
  • Bug Fixes
    • Improved dot-notation traversal to stop at scalar intermediates and validate missing/mismatched path segments sooner.
    • Replacement methods now avoid array-combine failures on size mismatches.
    • Value extraction and nested removal now handle arrays/objects more consistently (including float key normalization).
  • Documentation
    • Added setup guidance and an audit report for PHPStan ignore coverage.
  • Quality Improvements
    • Expanded type-checking tests for generics, mixed inputs, and access behaviors.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b89a794-5734-4b5d-9128-9b5c317c1f5f

📥 Commits

Reviewing files that changed from the base of the PR and between c98cec7 and e991bc1.

📒 Files selected for processing (2)
  • src/Type/DetectFirstValueTypeCollection.php
  • tests/ArrayyTest.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/ArrayyTest.php

Walkthrough

This change audits PHPStan ignores, adds dynamic return-type configuration and fixtures, tightens annotations and generic typing, adjusts JSON mapping and Arrayy traversal behavior, and expands tests for scalar paths, replacements, mixed data, access shapes, and callable inference.

Changes

PHPStan audit and runtime corrections

Layer / File(s) Summary
Arrayy traversal and replacement behavior
src/Arrayy.php, tests/ArrayyTest.php
Dot-path traversal validates scalar intermediates, extraction supports arrays and objects, float keys are normalized, and replacement methods handle mismatched sizes without invalid array_combine() calls.
Arrayy generic annotations and construction sites
src/Arrayy.php
Identifier-scoped PHPStan suppressions and generic callback return annotations are added across Arrayy construction, transformation, filtering, and immutable return paths.
Dynamic get() return-type inference
src/PHPStan/*, tests/PHPStan/*, phpstan.neon
PHPStan resolves literal dot-path get() calls against typed Arrayy shapes, with fallback handling and fixture coverage for nested access patterns.
Mapper and type-checking contracts
src/Mapper/Json.php, src/TypeCheck/*, src/Collection/AbstractCollection.php, src/Create.php
JSON mapper types and object input handling are refined, obsolete type metadata state is removed, and PHPStan diagnostics are scoped at affected collection and factory boundaries.
PHPStan configuration, fixtures, and audit record
phpstan.neon, tests/PHPStan/phpstan-fixtures.neon, tests/MetaPhpStanIntegrationTest.php, build/docs/*, README.md
PHPStan exclusions, extension services, fixture settings, user documentation, and the ignore audit record are updated.
Static-analysis and behavior test coverage
tests/ArrayyTest.php, tests/BasicArrayTest.php, tests/PHPStan/*, tests/Collection/*, tests/TypeCheckCoreCoverageTest.php, tests/JsonMapper*.php
Test annotations and suppressions are tightened, callable generic inference and access-shape inference are covered, and behavior tests cover scalar paths, replacement mismatches, mixed arrays and objects, and updated serialization expectations.

Estimated code review effort: 4 (Complex) | ~60 minutes

Poem

A rabbit audits warnings bright,
With typed-up hops from morn till night.
Paths stop safely, keys align,
Mappers read each object fine.
Tests thump softly: “Types are right!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main themes: PHPStan fixes, runtime edge cases, and added audit/tests.
Docstring Coverage ✅ Passed Docstring coverage is 80.59% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/integriere-phpstan-annotations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mergify

mergify Bot commented Jul 26, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 92.676% (+0.3%) from 92.38% — codex/integriere-phpstan-annotations into master

3 similar comments
@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 92.676% (+0.3%) from 92.38% — codex/integriere-phpstan-annotations into master

@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 92.676% (+0.3%) from 92.38% — codex/integriere-phpstan-annotations into master

@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 92.676% (+0.3%) from 92.38% — codex/integriere-phpstan-annotations into master

@coveralls

coveralls commented Jul 26, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 91.157% (-1.2%) from 92.38% — codex/integriere-phpstan-annotations into master

@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 92.676% (+0.3%) from 92.38% — codex/integriere-phpstan-annotations into master

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Mapper/Json.php`:
- Around line 82-83: Update the normalization before the foreach in the JSON
mapping method to preserve \Traversable inputs by iterating them directly, while
continuing to use get_object_vars() for non-Traversable objects and arrays. Add
a regression test covering an iterator such as ArrayIterator or Generator and
verify its yielded entries are mapped.

In `@tests/ArrayyTest.php`:
- Around line 216-229: Update cleanProvider() to branch its expected result for
the negative-key dataset based on PHP_VERSION_ID: retain the PHP 8.3+
expectation, while PHP 8.0–8.2 must expect [-8 => -9, 0 => 1]. Keep all other
provider cases unchanged and preserve compatibility with the declared PHP >=8.0
matrix.

In `@tests/PHPStan/AnalyseTest.php`:
- Around line 34-36: The PHPStan regression test must continue verifying the
inferred generic types instead of suppressing analysis errors. Update the
assertions around the foreach using $chunk and the corresponding checks at lines
69-78 to restore equivalent assertType coverage for $chunk, int, and \stdClass,
and remove the method.nonObject suppression.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cb6009c8-a23b-4103-a88b-24c7c459968a

📥 Commits

Reviewing files that changed from the base of the PR and between 57c7fca and f74111c.

📒 Files selected for processing (26)
  • build/docs/phpstan-ignore-audit.md
  • phpstan.neon
  • src/Arrayy.php
  • src/Collection/AbstractCollection.php
  • src/Create.php
  • src/Mapper/Json.php
  • src/PHPStan/MetaDynamicStaticMethodReturnTypeExtension.php
  • src/Type/DetectFirstValueTypeCollection.php
  • src/TypeCheck/TypeCheckCallback.php
  • src/TypeCheck/TypeCheckPhpDoc.php
  • tests/Account.php
  • tests/ArrayyTest.php
  • tests/BasicArrayTest.php
  • tests/Collection/BoolTypeTest.php
  • tests/Collection/CollectionTest.php
  • tests/Collection/StringTypeTest.php
  • tests/Collection/TypeTypeTest.php
  • tests/Collection/TypesTest.php
  • tests/JsonMapperCoverageTest.php
  • tests/JsonMapperTest.php
  • tests/MetaPhpStanIntegrationTest.php
  • tests/ModelA.php
  • tests/PHPStan/AnalyseTest.php
  • tests/PHPStan/CallableGenericInferenceTest.php
  • tests/PHPStan/phpstan-fixtures.neon
  • tests/TypeCheckCoreCoverageTest.php

Comment thread src/Mapper/Json.php Outdated
Comment thread tests/ArrayyTest.php
Comment thread tests/PHPStan/AnalyseTest.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/PHPStan/GetDynamicMethodReturnTypeExtension.php`:
- Around line 46-60: Restrict nested path inference in
GetDynamicMethodReturnTypeExtension to calls using the default "." path
separator, or otherwise obtain and honor the configured separator before
splitting paths. Document this default-separator restriction in README.md lines
123-133 and build/docs/base.md lines 122-132; no other behavior should change.

In `@tests/PHPStan/AccessShapeProfile.php`:
- Around line 14-17: Update the offset parameter annotation on
AccessShapeProfile::offsetGet() from only 'name' to the full 'avatar'|'name'
union declared by the profile, while preserving its string return type and
existing missing-value exception behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c11c3951-ebec-47e1-a787-2f811a4937a9

📥 Commits

Reviewing files that changed from the base of the PR and between f74111c and fb5babe.

📒 Files selected for processing (8)
  • README.md
  • build/docs/base.md
  • phpstan.neon
  • src/PHPStan/GetDynamicMethodReturnTypeExtension.php
  • tests/PHPStan/AccessShapeProfile.php
  • tests/PHPStan/AccessShapeUser.php
  • tests/PHPStan/AccessWaysTest.php
  • tests/PHPStan/phpstan-fixtures.neon
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/PHPStan/phpstan-fixtures.neon

Comment thread src/PHPStan/GetDynamicMethodReturnTypeExtension.php
Comment thread tests/PHPStan/AccessShapeProfile.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/Mapper/Json.php (1)

82-85: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the nested ternary into an independent conditional.

Line 84 is behaviorally correct, but separating the Traversable, object, and iterable cases will improve readability and address the reported SonarCloud warning.

Proposed refactor
-        $jsonValues = $json instanceof \Traversable
-            ? $json
-            : (\is_object($json) ? \get_object_vars($json) : $json);
+        if ($json instanceof \Traversable) {
+            $jsonValues = $json;
+        } elseif (\is_object($json)) {
+            $jsonValues = \get_object_vars($json);
+        } else {
+            $jsonValues = $json;
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Mapper/Json.php` around lines 82 - 85, In the JSON value preparation
before the foreach in the mapper, replace the nested ternary with separate
conditional branches for Traversable values, objects, and other iterable inputs.
Preserve the existing behavior and resulting $jsonValues for each case while
eliminating the SonarCloud readability warning.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/Mapper/Json.php`:
- Around line 82-85: In the JSON value preparation before the foreach in the
mapper, replace the nested ternary with separate conditional branches for
Traversable values, objects, and other iterable inputs. Preserve the existing
behavior and resulting $jsonValues for each case while eliminating the
SonarCloud readability warning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d947ad8-2e48-4bb4-8bd3-28559254f7c4

📥 Commits

Reviewing files that changed from the base of the PR and between fb5babe and c98cec7.

📒 Files selected for processing (13)
  • README.md
  • build/docs/base.md
  • src/Arrayy.php
  • src/Mapper/Json.php
  • src/PHPStan/DefaultDotNotationTypeInterface.php
  • src/PHPStan/GetDynamicMethodReturnTypeExtension.php
  • tests/ArrayyTest.php
  • tests/JsonMapperCoverageTest.php
  • tests/PHPStan/AccessShapeProfile.php
  • tests/PHPStan/AccessShapeUser.php
  • tests/PHPStan/AccessWaysTest.php
  • tests/PHPStan/AnalyseTest.php
  • tests/PHPStan/CustomSeparatorAccessUser.php
🚧 Files skipped from review as they are similar to previous changes (7)
  • tests/PHPStan/AccessShapeProfile.php
  • tests/PHPStan/AccessWaysTest.php
  • tests/PHPStan/AccessShapeUser.php
  • tests/JsonMapperCoverageTest.php
  • src/PHPStan/GetDynamicMethodReturnTypeExtension.php
  • tests/ArrayyTest.php
  • src/Arrayy.php

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants