Skip to content

update tests, and classes according to the interfaces of aura/filter-interface#80

Merged
harikt merged 3 commits into
auraphp:6.xfrom
harikt:stage-2
May 28, 2026
Merged

update tests, and classes according to the interfaces of aura/filter-interface#80
harikt merged 3 commits into
auraphp:6.xfrom
harikt:stage-2

Conversation

@harikt

@harikt harikt commented May 22, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Chores

    • Bumped minimum PHP to 8.4 and updated PHPUnit to ^11.0
    • CI matrix updated to newer PHP versions; added phpunit cache to ignore list
  • New Features

    • Filter now returns a result object; failures support dot-notation keys and nested reconstruction
    • New Failure and FailureCollection helpers for richer failure access
  • Bug Fixes

    • More reliable validation/filter result handling
  • Refactor

    • Widespread typed signatures and strict typing across public APIs
  • Documentation

    • Added Getting Started and Upgrade guide for 1.x → 6.0.0

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0fcdf726-4e04-4c5a-a3c1-7d172a580b90

📥 Commits

Reviewing files that changed from the base of the PR and between 422c753 and ca60b70.

📒 Files selected for processing (12)
  • .github/workflows/continuous-integration.yml
  • .gitignore
  • CHANGELOG.md
  • composer.json
  • docs/_bookdown.json
  • docs/getting-started.md
  • docs/index.md
  • docs/upgrade.md
  • phpunit.xml.dist
  • tests/CollectionTest.php
  • tests/FieldsetTest.php
  • tests/FilterTest.php

Walkthrough

Modernize codebase to PHP 8.4 strict typing, replace Yoast PHPUnit polyfills with PHPUnit 11, change Filter::apply() to return a FilterResult, introduce immutable Filter\Failure and richer FailureCollection with dot-notation keys, and update Collection/Fieldset and tests to use FailuresInterface and typed signatures.

Changes

Strict typing and failure interface refactoring

Layer / File(s) Summary
Project dependencies and PHP version
composer.json, .github/workflows/continuous-integration.yml, phpunit.xml.dist
Bump PHP requirement to ^8.4; switch aura/filter-interface to 6.x-dev; replace yoast/phpunit-polyfills with phpunit/phpunit:^11.0; update CI PHP matrix and phpunit schema.
declare(strict_types) additions
src/* files
Add declare(strict_types=1); to core source files updated in this PR.
Immutable Failure value object
src/Filter/Failure.php
Convert Failure to constructor-promoted readonly properties; preserve getters and jsonSerialize.
FailureCollection expanded interface
src/Filter/FailureCollection.php
Privatize messages storage; add forField()/forPath() returning Failure objects; add getNestedMessages() to rebuild nested arrays; refine add/set/addMessagesForField/getMessagesForField behavior.
Filter refactoring with result object
src/Filter.php
Type Filter internals; change `apply(array
CollectionIterator typed properties and methods
src/CollectionIterator.php
Add typed Collection property, typed keys array and bool valid flag; add explicit return types for iterator methods.
Collection typed signatures and failure aggregation
src/Collection.php
Type fieldsets; add fill(): void, filter(): bool; implement getFailures(): FailuresInterface that collects child failures using dot-notation keys; add native return types for public APIs.
Fieldset failure aggregation with dot-notation keys
src/Fieldset.php
Type failures as FailuresInterface, add init(): void, refactor filter() to aggregate top-level and nested failures into a FailureCollection using dot-notation keys, and expose getFailures(): FailuresInterface.
PHPUnit migration and test updates
tests/*, tests/Example/*
Switch tests to PHPUnit\Framework\TestCase, rename set_up() to setUp(): void, add : void return types to init/test methods, and update tests to assert FilterResult::isSuccess() and failures via getMessages()/getNestedMessages() dot-notation keys.
Docs and changelog
docs/*, CHANGELOG.md
Add Getting Started and Upgrade Guide, update book metadata, document breaking changes in CHANGELOG.md including API and dependency updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Poem

A rabbit hops through modern PHP,
Strict types snug beneath my fur, you see;
Failures turned readonly, neat and small,
Dot-dotted keys to map them all,
Tests now native — hop, hooray! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the changeset: updating tests and classes to align with aura/filter-interface interfaces.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Filter.php (1)

101-110: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix Filter::apply() to handle array subjects without object property access

apply(array|object $values) calls $closure($values->$field, $values) on line 107; if $values is an array this will throw at runtime. Use array indexing for array inputs instead of ->$field—and preserve reference behavior for sanitize closures that take function (&$value) so mutations apply to the returned getValues() result.

🤖 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/Filter.php` around lines 101 - 110, Filter::apply currently uses object
property access ($values->$field) which breaks when $values is an array and also
prevents sanitize closures declared as function (&$value) from mutating the
stored value; update Filter::apply to detect whether $values is an array or
object, obtain the target field value by reference (e.g., $fieldValue =&
$values[$field] for arrays or $fieldValue =& $values->$field for objects) before
invoking the rule closure so $closure($fieldValue, $values) works for both types
and preserves in-place mutations that will be visible to getValues(); keep
existing failure handling via $this->failures and $this->proto_failures and only
change the value retrieval/invocation logic around iterating $this->rules and
calling each $closure.
🤖 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 `@composer.json`:
- Around line 19-22: composer.json declares "php": "^8.4" but the CI workflow
(continuous-integration.yml) still tests PHP 7.2–8.3 causing install failures;
update the GitHub Actions PHP matrix to only include PHP versions matching
composer.json (e.g., 8.4 and 8.5), removing any entries below 8.4, and ensure
any matrix variable name like matrix.php or the job step that runs composer
install reflects the new set so CI runs use versions compatible with the "php":
"^8.4" requirement.
- Line 21: The composer.json currently pins the dependency
"aura/filter-interface" to the unstable branch "dev-sub-filter"; update that
requirement to a stable tagged constraint such as "^3.0" or "3.0.0" in the
composer.json dependency list (replace "aura/filter-interface": "dev-sub-filter"
with the chosen stable constraint), then run composer update to lock the stable
version and run tests to verify compatibility with any code using
Aura\Filter_Interface.

In `@src/Fieldset.php`:
- Around line 75-77: The protected typed property $failures on class Fieldset is
uninitialized and causes a runtime error if getFailures() is called before
filter(); initialize it in the constructor to an empty FailureCollection (or
appropriate implementation of FailuresInterface) so $failures is always valid;
update the Fieldset::__construct to set $this->failures = new
FailureCollection() (or factory) and ensure any references in getFailures() and
filter() use that property safely.

In `@src/Filter.php`:
- Around line 56-60: The constructor currently accepts
?FailureCollectionInterface but silently discards any implementation that is not
FailureCollection; change Filter::__construct to either type-hint
?FailureCollection (narrow the parameter) or add a guard that throws a
TypeError/InvalidArgumentException if $failures is provided and not an instance
of FailureCollection to preserve caller state/behavior (reference
FailureCollectionInterface, FailureCollection, and Filter::__construct). For
apply, fix the mismatched array|object handling in Filter::apply by either
restricting the parameter to object only or implementing proper array access:
detect is_array($values) and use array_key_exists($field, $values) and
$values[$field] when appropriate (reference Filter::apply, the $values->$field
usage and $field) and update the signature/docblock accordingly so array access
no longer breaks.

---

Outside diff comments:
In `@src/Filter.php`:
- Around line 101-110: Filter::apply currently uses object property access
($values->$field) which breaks when $values is an array and also prevents
sanitize closures declared as function (&$value) from mutating the stored value;
update Filter::apply to detect whether $values is an array or object, obtain the
target field value by reference (e.g., $fieldValue =& $values[$field] for arrays
or $fieldValue =& $values->$field for objects) before invoking the rule closure
so $closure($fieldValue, $values) works for both types and preserves in-place
mutations that will be visible to getValues(); keep existing failure handling
via $this->failures and $this->proto_failures and only change the value
retrieval/invocation logic around iterating $this->rules and calling each
$closure.
🪄 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

Run ID: 427c84a1-bd2e-45bc-bf96-13c00063aa8e

📥 Commits

Reviewing files that changed from the base of the PR and between 5241230 and 422c753.

📒 Files selected for processing (22)
  • composer.json
  • src/Collection.php
  • src/CollectionIterator.php
  • src/Fieldset.php
  • src/Filter.php
  • src/Filter/Failure.php
  • src/Filter/FailureCollection.php
  • tests/CollectionTest.php
  • tests/ConfigurableFieldset.php
  • tests/ConfigurableForm.php
  • tests/Example/AddressFieldset.php
  • tests/Example/ContactFilter.php
  • tests/Example/ContactForm.php
  • tests/Example/ExampleTest.php
  • tests/Example/PhoneFieldset.php
  • tests/Example/PhoneFilter.php
  • tests/FieldTest.php
  • tests/FieldsetTest.php
  • tests/FilterTest.php
  • tests/FormFactoryTest.php
  • tests/FormTest.php
  • tests/MockFieldset.php

Comment thread composer.json
Comment thread composer.json Outdated
"php": "^7.2 || ^8.0",
"aura/filter-interface":"4.x-dev"
"php": "^8.4",
"aura/filter-interface": "dev-sub-filter"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check available versions/tags of aura/filter-interface

# Using GitHub API to check repository tags
gh api repos/auraphp/Aura.Filter_Interface/tags --jq '.[].name' | head -20

Repository: auraphp/Aura.Input

Length of output: 81


Avoid the dev-sub-filter branch for aura/filter-interface
composer.json pins "aura/filter-interface": "dev-sub-filter", but auraphp/Aura.Filter_Interface has a stable tagged release (3.0.0). Switch to a stable constraint (e.g., ^3.0 / 3.0.0) unless there’s a documented reason the dev branch is required.

🤖 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 `@composer.json` at line 21, The composer.json currently pins the dependency
"aura/filter-interface" to the unstable branch "dev-sub-filter"; update that
requirement to a stable tagged constraint such as "^3.0" or "3.0.0" in the
composer.json dependency list (replace "aura/filter-interface": "dev-sub-filter"
with the chosen stable constraint), then run composer update to lock the stable
version and run tests to verify compatibility with any code using
Aura\Filter_Interface.

Comment thread src/Fieldset.php
Comment on lines +75 to +77
* Failures aggregated after the last filter() call.
*/
protected $failures;
protected FailuresInterface $failures;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Uninitialized typed property will cause error if getFailures() is called before filter().

The $failures property is declared with type FailuresInterface but has no default value. It is only initialized in filter(). Calling getFailures() before filter() throws:

Typed property Aura\Input\Fieldset::$failures must not be accessed before initialization

Initialize the property to an empty FailureCollection to prevent this runtime error.

Proposed fix in constructor
     public function __construct(
         BuilderInterface $builder,
         FilterInterface  $filter,
         $options = null
     ) {
         $this->builder  = $builder;
         $this->filter   = $filter;
         $this->options  = $options;
+        $this->failures = new FailureCollection();
         $this->init();
     }

Also applies to: 392-395

🤖 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/Fieldset.php` around lines 75 - 77, The protected typed property
$failures on class Fieldset is uninitialized and causes a runtime error if
getFailures() is called before filter(); initialize it in the constructor to an
empty FailureCollection (or appropriate implementation of FailuresInterface) so
$failures is always valid; update the Fieldset::__construct to set
$this->failures = new FailureCollection() (or factory) and ensure any references
in getFailures() and filter() use that property safely.

Comment thread src/Filter.php
Comment on lines 56 to +60
public function __construct(?FailureCollectionInterface $failures = null)
{
if ($failures === null) {
$failures = new FailureCollection();
}
$this->proto_failures = $failures;
$proto = ($failures instanceof FailureCollection)
? $failures
: new FailureCollection();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and show the relevant constructor/apply code in src/Filter.php
if [ -f src/Filter.php ]; then
  echo "=== src/Filter.php (around lines 40-120) ==="
  nl -ba src/Filter.php | sed -n '40,130p'
else
  echo "src/Filter.php not found"
  exit 1
fi

# Find the FailureCollectionInterface definition
echo "=== Finding FailureCollectionInterface ==="
rg -n "interface FailureCollectionInterface" -S . || true
rg -n "FailureCollectionInterface" -S src tests . || true

# Find all concrete FailureCollection classes implementing/used
echo "=== Finding class FailureCollection ==="
rg -n "class FailureCollection" -S . || true

# Enumerate any other implementations of FailureCollectionInterface
echo "=== Searching for 'implements FailureCollectionInterface' ==="
rg -n "implements\s+FailureCollectionInterface" -S . || true

Repository: auraphp/Aura.Input

Length of output: 168


🏁 Script executed:

#!/bin/bash
set -euo pipefail

if [ -f src/Filter.php ]; then
  echo "=== src/Filter.php (around lines 40-130) ==="
  # Print with line numbers using awk (nl not available)
  awk 'NR>=40 && NR<=130 {printf "%5d:%s\n", NR, $0}' src/Filter.php
else
  echo "src/Filter.php not found"
  exit 1
fi

echo "=== Finding FailureCollectionInterface ==="
rg -n "interface FailureCollectionInterface" -S src tests . || true
rg -n "FailureCollectionInterface" -S src tests . || true

echo "=== Finding class FailureCollection ==="
rg -n "class FailureCollection\b" -S src tests . || true

echo "=== Searching for implementations ==="
rg -n "implements\s+FailureCollectionInterface" -S src tests . || true

Repository: auraphp/Aura.Input

Length of output: 6723


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check where FailureCollectionInterface is type-hinted/constructed
echo "=== Searching constructors/usages of FailureCollection/FailureCollectionInterface ==="
rg -n "new\s+FailureCollection\s*\(" -S src tests . || true
rg -n "\bFailureCollection\s*\(" -S src tests . || true
rg -n ":\s*\?FailureCollectionInterface\b|\b\?FailureCollectionInterface\b" -S src tests . || true

Repository: auraphp/Aura.Input

Length of output: 1102


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check apply signature and array/object handling in Filter
echo "=== Searching Filter::apply implementation details ==="
rg -n "function\s+apply\s*\(" -S src/Filter.php src tests . || true
rg -n "\$values->" -S src/Filter.php || true
rg -n "\$values\[" -S src/Filter.php || true

# Also scan for any mention of $values being array vs object handling elsewhere
rg -n "is_array\(\$values\)|is_object\(\$values\)" -S src tests . || true

Repository: auraphp/Aura.Input

Length of output: 389


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Searching for calls to apply() ==="
rg -n "->apply\s*\(" -S src tests . || true

echo "=== Searching for Filter::apply usage by name ==="
rg -n "Filter::apply\s*\(" -S src tests . || true

echo "=== Searching for 'apply(' in context of Filter objects ==="
rg -n "function\s+filter|filter\(" -S src tests . || true

Repository: auraphp/Aura.Input

Length of output: 5016


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Searching for Filter::apply() call sites ==="
rg -n '\->apply\s*\(' -S src tests . || true

echo "=== Searching for ->apply( without requiring '->apply' tokenization issues ==="
rg -n 'apply\s*\(' -S src tests . || true

echo "=== Locate where Fieldset/Collection call Filter::apply ==="
rg -n 'getFilter\(\)|->getFilter\(\)|filter\(\)|Filter::apply|->apply' -S src/Fieldset.php src/Collection.php src/Form* src . || true

Repository: auraphp/Aura.Input

Length of output: 7121


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== tests/FilterTest.php around apply($values) usages ==="
awk 'NR>=1 && NR<=240 {printf "%5d:%s\n", NR, $0}' tests/FilterTest.php | sed -n '40,220p'

echo "=== src/Fieldset.php around where apply($this) is called ==="
awk 'NR>=330 && NR<=420 {printf "%5d:%s\n", NR, $0}' src/Fieldset.php

echo "=== src/Filter.php around apply() implementation details ==="
awk 'NR>=80 && NR<=150 {printf "%5d:%s\n", NR, $0}' src/Filter.php

Repository: auraphp/Aura.Input

Length of output: 106


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== tests/FilterTest.php excerpt (1-220) ==="
awk 'NR>=1 && NR<=220 {print NR ":" $0}' tests/FilterTest.php

echo "=== src/Fieldset.php excerpt (330-410) ==="
awk 'NR>=330 && NR<=410 {print NR ":" $0}' src/Fieldset.php

echo "=== src/Filter.php excerpt (45-130) ==="
awk 'NR>=45 && NR<=130 {print NR ":" $0}' src/Filter.php

Repository: auraphp/Aura.Input

Length of output: 11622


🌐 Web query:

Aura\Filter_Interface\FailureCollectionInterface constructor expectation / intended usage in aura/filter-interface

💡 Result:

No constructor expectations apply because FailureCollectionInterface is an interface; it should not declare (and callers should not expect) any construct signature. In Aura’s design, the “failure collection” is produced via an implementation that implements FailureCollectionInterface, and user code should only rely on the interface’s methods. In PHP, interfaces describe behavior/contracts; they cannot be instantiated and therefore can’t express a constructor contract. As a general language/design rule, this is why you can’t specify a constructor on an interface and consumers should not assume anything about how an implementing class is constructed [1]. Within the Aura.Filter ecosystem, FailureCollectionInterface lives in the separate interface package (aura/filter-interface), which is meant to provide contracts for filter/failure collection behavior, not concrete instantiation details [2]. Practical intended usage: 1) Type-hint against Aura\Filter_Interface\FailureCollectionInterface (behavior contract). 2) Let Aura.Filter (or your DI container/factory wiring) instantiate the concrete implementation that implements that interface. 3) Do not call new FailureCollectionInterface(...) or rely on any specific constructor parameters; those belong to the concrete implementation class, not the interface [1].

Citations:


Don’t ignore non-FailureCollection implementations and don’t advertise array support in Filter::apply.

  • src/Filter.php (lines 56-64): __construct(?FailureCollectionInterface $failures) silently replaces any non-Aura\Input\Filter\FailureCollection implementation with new FailureCollection(), discarding caller-provided behavior/state. Narrow the type to ?FailureCollection or fail fast when an unsupported implementation is passed.
  • src/Filter.php (lines 101-112): apply(array|object $values) uses $values->$field (object property access), which will break for plain arrays despite the array|object signature. Restrict the parameter type or implement correct array handling.
🤖 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/Filter.php` around lines 56 - 60, The constructor currently accepts
?FailureCollectionInterface but silently discards any implementation that is not
FailureCollection; change Filter::__construct to either type-hint
?FailureCollection (narrow the parameter) or add a guard that throws a
TypeError/InvalidArgumentException if $failures is provided and not an instance
of FailureCollection to preserve caller state/behavior (reference
FailureCollectionInterface, FailureCollection, and Filter::__construct). For
apply, fix the mismatched array|object handling in Filter::apply by either
restricting the parameter to object only or implementing proper array access:
detect is_array($values) and use array_key_exists($field, $values) and
$values[$field] when appropriate (reference Filter::apply, the $values->$field
usage and $field) and update the signature/docblock accordingly so array access
no longer breaks.

@harikt
harikt merged commit d336c11 into auraphp:6.x May 28, 2026
2 of 3 checks passed
@harikt
harikt deleted the stage-2 branch May 28, 2026 08:27
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.

1 participant