Upgrade Guide: 2.x → 6.0.0

May 28, 2026 · View on GitHub

This guide covers the breaking changes introduced in 6.0.0 and how to update your code accordingly.


PHP Version Requirement

2.x required PHP >= 5.4. 6.0.0 requires PHP >= 8.4.

Ensure your runtime and CI pipeline are running PHP 8.4 or later before upgrading.


License Change

The license has changed from BSD to MIT. Update any attribution or license references in your project accordingly.


Removed: Aura.Di Integration

The config/Common.php container configuration file and the legacy autoload.php bootstrap file have been removed.

Before (2.x):

// Using Aura.Di with config/Common.php
$di->params['Aura\Filter\FilterFactory'] = [
    'validate_factories'  => [],
    'sanitize_factories'  => [],
];

After (6.0.0):

Instantiate FilterFactory directly. No container config is provided by the package; wire it yourself with whichever DI container you use:

use Aura\Filter\FilterFactory;

$factory = new FilterFactory(
    [], // validate factories
    []  // sanitize factories
);
$filter = $factory->newSubjectFilter();

SubjectFilter: apply() Now Returns a FilterResultInterface

In 2.x, apply() mutated the subject in place and returned a bool. In 6.0.0, apply() is immutable — it never mutates your original subject. Instead, it returns a FilterResultInterface containing the sanitized copy and any failures.

Before (2.x):

if ($filter->apply($subject)) {
    // $subject has been sanitized in place
} else {
    $failures = $filter->getFailures();
}

After (6.0.0):

$result = $filter->apply($subject);

if ($result->isSuccess()) {
    $sanitized = $result->getValues(); // sanitized copy — original $subject unchanged
} else {
    $failures = $result->getFailures(); // FailureCollection
}

getFailures() no longer exists as a standalone method on SubjectFilter. Failures are accessed via the FilterResultInterface returned by apply().


SubjectFilter::__invoke() Behaviour Change

In 2.x, __invoke() was equivalent to assert() — it mutated the subject and threw FilterFailed on failure (returning null on success).

In 6.0.0, __invoke() still mutates the subject (writing sanitized values back) and throws FilterFailed on failure, but internally it now uses apply(). The visible behaviour when catching FilterFailed remains the same.

If you were calling $filter($subject) and then $filter->getFailures() after catching the exception, use $e->getFailures() instead:

try {
    $filter($subject);
} catch (\Aura\Filter\Exception\FilterFailed $e) {
    $failures = $e->getFailures(); // same as before
}

SubjectFilter::getFailures() Removed

The getFailures() method on SubjectFilter has been removed. Failures are now returned via FilterResultInterface::getFailures().

Before (2.x):

if (! $filter->apply($subject)) {
    $failures = $filter->getFailures();
    foreach ($failures->getMessages() as $field => $messages) {
        // ...
    }
}

After (6.0.0):

$result = $filter->apply($subject);
if (! $result->isSuccess()) {
    $failures = $result->getFailures();
    foreach ($failures->getMessages() as $field => $messages) {
        // ...
    }
}

SubjectFilter Implements SubjectFilterInterface

SubjectFilter now declares implements Aura\Filter_Interface\SubjectFilterInterface. Type-hint against the interface when injecting filters:

use Aura\Filter_Interface\SubjectFilterInterface;

class MyService
{
    public function __construct(private SubjectFilterInterface $filter) {}
}

New Dependency: aura/filter-interface

The package now requires aura/filter-interface (currently at 6.x). This introduces:

Interface / ClassDescription
SubjectFilterInterfaceImplemented by SubjectFilter
FilterResultInterfaceReturned by apply()
FilterResultConcrete implementation of FilterResultInterface
FailureInterfaceImplemented by Failure

Failure Now Implements FailureInterface

Failure previously implemented JsonSerializable directly. It now implements Aura\Filter_Interface\FailureInterface, which itself extends JsonSerializable. Existing jsonSerialize() calls continue to work without changes.


Strict Types Throughout

All classes now declare strict_types=1 and use typed property declarations and typed method signatures. If you extended any Aura.Filter class and overrode methods without matching type signatures, you will receive a fatal error.

Example — update your overrides:

// Before (2.x override)
public function getField()
{
    return $this->field;
}

// After (6.0.0 compatible override)
public function getField(): string
{
    return $this->field;
}

New Feature: Sub-Filters (Nested / Multidimensional Filtering)

6.0.0 introduces first-class support for filtering nested objects and arrays via SubjectFilter::subfilter().

$filter = $factory->newSubjectFilter();

// The 'address' field is itself filtered by an AddressFilter
$addressFilter = $filter->subfilter('address', AddressFilter::class);
$addressFilter->validate('city')->is('string');
$addressFilter->validate('zip')->is('strlen', 5, 5);

$result = $filter->apply($subject);

Sub-filter failures are reported using dotted field paths (e.g. address.city).


New Feature: PSR-7 Uploaded File Rules

Two new rules support psr/http-message uploaded files:

RuleTypeDescription
Rule\Validate\UploadedFileValidateValidates a UploadedFileInterface; supports required, fileExtension, fileMedia, sizeMax, sizeMin checks.
Rule\Sanitize\UploadedFileOrNullSanitizeSanitizes to null if value is not a valid uploaded file.
$filter->validate('avatar')->is('uploadedFile')->required()->fileExtension(['jpg', 'png']);
$filter->sanitize('avatar')->to('uploadedFileOrNull');

New Feature: SanitizeSpec::useBlankField()

When a field is blank, copy the value from another field instead of using a hard-coded blank value:

// If 'display_name' is blank, use the value from 'username'
$filter->sanitize('display_name')->toBlankOr('trim')->useBlankField('username');

New Feature: SubjectFilter::useFieldMessage()

Override all per-rule failure messages for a field with a single custom message:

$filter->validate('email')->is('email');
$filter->useFieldMessage('email', 'Please enter a valid email address.');

Removed: autoload.php

The package no longer ships a stand-alone autoload.php. Use Composer's autoloader exclusively:

require __DIR__ . '/vendor/autoload.php';