Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Zend Inputfilter Laravel Package

zendframework/zend-inputfilter

Zend InputFilter provides robust validation and filtering for input data in PHP applications. It supports reusable filter and validator chains, nested inputs, file uploads, and seamless integration with Zend Form for clean, secure request handling.

Deep Wiki
Context7

Getting Started

First, decide whether you should install it at all. zendframework/zend-inputfilter is archived (last release 2019-08-28) and every zendframework/* package is superseded by its laminas/* fork. On anything new, install laminas/laminas-inputfilter; the namespace is Laminas\InputFilter\* instead of Zend\InputFilter\*. If you are porting a legacy ZF2/ZF3 app, keep zendframework/zend-inputfilter only long enough to add laminas/laminas-zendframework-bridge, which aliases Zend\ → Laminas\ at runtime and lets you flip composer packages without a code rewrite.

composer require laminas/laminas-inputfilter

If you are in a greenfield Laravel app, stop here — Illuminate\Validation already does 95% of this. The realistic reasons to reach for this package in Laravel are: (a) a legacy Zend/Laminas codebase being strangled into Laravel, (b) shared validation specs between a Zend service and a Laravel service, or (c) you specifically need Collection/FileInput semantics and its nested message structure for per-row import validation.

Where to look first, in this order: InputFilter (the container), Input (one field: filters + validators + required/allow_empty), Collection (arrays of rows), FileInput (uploads), Factory (builds filters from array specs), and the interfaces InputFilterInterface, InputFilterAwareInterface, EmptyContextInterface.

A minimal first use case — validating a signup payload:

use Laminas\InputFilter\InputFilter;
use Laminas\Validator\EmailAddress;
use Laminas\Validator\StringLength;
use Laminas\Filter\StringTrim;
use Laminas\Filter\StripTags;

$filter = new InputFilter();
$filter->add([
    'name'       => 'email',
    'required'   => true,
    'filters'    => [['name' => StringTrim::class], ['name' => StripTags::class]],
    'validators' => [['name' => EmailAddress::class]],
]);
$filter->add([
    'name'       => 'password',
    'required'   => true,
    'allow_empty' => false,
    'validators' => [['name' => StringLength::class, 'options' => ['min' => 12]]],
]);

$filter->setData($request->all());

if (! $filter->isValid()) {
    // ['email' => ['...message...'], 'password' => [...]]
    return back()->withErrors($filter->getMessages());
}

$clean = $filter->getValues(); // filters already applied

Note the two-step contract: setData() then isValid(), then read getValues() / getMessages(). There is no Laravel-style $request->validate() sugar here.

Implementation Patterns

Spec arrays in config, built by a factory. The idiomatic Zend pattern is to keep input filter specifications as plain arrays and let a factory instantiate them. Mirror that in Laravel with a dedicated config file rather than building filters inline in controllers:

// config/input_filters.php
return [
    'signup' => [
        'email'    => ['required' => true, 'validators' => [/* ... */]],
        'password' => ['required' => true, 'allow_empty' => false, /* ... */],
    ],
];
// app/Providers/InputFilterServiceProvider.php
public function register(): void
{
    // scoped, NOT singleton — InputFilter is stateful (see Gotchas)
    $this->app->scoped(SignupInputFilter::class, fn () => (new Factory())
        ->createInputFilter(config('input_filters.signup')));
}

Prefer this over singleton(); scoped gives you a fresh instance per request (and per Octane request).

Bridge into Laravel's validation pipeline so your JSON error shape stays consistent. The cleanest seam is a FormRequest that delegates to the input filter and rethrows as Laravel's ValidationException:

class SignupRequest extends FormRequest
{
    public function __construct(private SignupInputFilter $filter) {}

    public function rules(): array
    {
        $this->filter->setData($this->all());

        if (! $this->filter->isValid()) {
            throw ValidationException::withMessages($this->filter->getMessages());
        }

        return []; // no rules; but authorize() still runs
    }

    public function validated($key = null, $default = null)
    {
        return $this->filter->getValues();
    }
}

withMessages() accepts the nested array getMessages() returns, so field-level errors map naturally. Beware: because rules() is empty, authorize() is the only remaining guard — don't accidentally bypass policy checks.

Implement InputFilterProviderInterface on DTOs/commands. The Zend convention is getInputFilterSpecification() on the object that owns the data. If you have ported models or command objects, implementing this interface (or just the method, and wiring it manually) keeps the spec next to the data shape and lets you reuse it in both the Laravel app and the legacy service.

Collections for row-oriented imports. For CSV/JSON batch imports, Collection with a target_element spec gives you per-row, per-field messages keyed by index — something Laravel's array.* rules do but with flatter messages:

$filter->add([
    'name' => 'rows',
    'type' => Collection::class,
    'options' => [
        'count' => 100,
        'target_element' => [
            'type' => InputFilter::class,
            'input_filter' => [
                'sku'  => ['required' => true],
                'qty'  => ['required' => true, 'validators' => [
                    ['name' => GreaterThan::class, 'options' => ['min' => 0]],
                ]],
            ],
        ],
    ],
]);

count enforces cardinality; allow_add / allow_remove are really form-layer rendering hints from zend-form, not security controls — don't rely on them to reject unexpected keys.

Nested filters work the same way: add an entry whose type is InputFilter::class (or 'InputFilter' via the plugin manager) with an input_filter spec. The child filter receives the value at that key.

Wrap Laravel rules as Zend validators if you want one source of truth during a migration. A tiny adapter implementing Laminas\Validator\ValidatorInterface that calls Validator::make(...)->passes() lets existing Laravel rules participate in a Zend chain, and lets you delete the legacy spec incrementally.

File uploads: FileInput expects the $_FILES-shaped array (name, type, tmp_name, error, size), not Laravel's UploadedFile. Convert explicitly: $inputFilter->setData(['avatar' => ['name' => $file->getClientOriginalName(), 'tmp_name' => $file->getRealPath(), ...]]), or skip FileInput and validate the UploadedFile in Laravel.

Gotchas and Tips

  • required does not mean "not blank." It means "the key must be present." An empty string satisfies required. To reject blanks you need allow_empty => false or a NotEmpty validator. This is the single most common source of silent validation holes when porting.
  • allow_empty defaults to true and short-circuits the validator chain. If a value is empty and allow_empty is true, Input::isValid() returns early and none of your validators run. People add StringLength/Regex validators and are baffled when "" passes.
  • continue_if_empty (added late in the 2.x line) is the opposite knob: when true, an empty value still flows through filters and validators. If you need "empty is allowed but if present must be valid," set allow_empty => false and handle the optionality with required => false — do not stack allow_empty => true with NotEmpty.
  • NotEmpty belongs last in the validator chain. The validator chain stops evaluating once NotEmpty sees an empty value, so anything after it is dead code.
  • Filters run before validators, in array order. StripTags before StringTrim is usually wrong (tags become text), and trimming after a length check is pointless. Order filters deliberately.
  • getValues() can contain null keys for absent optional inputs. Never pass it straight to Model::create() or update() — intersect it with $model->getFillable() or a only([...]) list, or you will null out columns.
  • It is stateful — never register it as a singleton. setData() mutates the instance and isValid() accumulates error state. Under Laravel Octane a singleton will leak one user's payload into another request. Use scoped()/bind(), or construct per use.
  • Config caching kills closures. Any spec containing a closure (e.g. a callback filter or an inline factory) will blow up php artisan config:cache. Keep specs to class-name strings and scalars; resolve anything dynamic inside the factory.
  • setData() requires an array and throws on anything else. Entities/objects don't fit; hydrate to arrays first (or use InputFilterAwareInterface on the object and pull values manually).
  • Debugging: getMessages() is only populated after a failed isValid(); use getInvalidInput() to get the actual Input objects and inspect getRawValues() vs getValue($name) to see whether your filter mangled the value. getRawValues() is the unfiltered view — the fastest way to tell "filter ate it" from "validator rejected it."
  • Messages are not translated by Laravel. Zend messages come from zend-i18n/laminas-i18n (AbstractValidator::setDefaultTranslator()), so lang/ resources won't apply. Either wire a Laminas translator into the same locale or map messages to your own translation keys at the bridge layer.
  • Empty file inputs are the classic trap: a missing upload arrives as UPLOAD_ERR_NO_FILE, and FileInput can fail on NotEmpty before you ever get to "optional file" handling. Combine required => false with an explicit error === UPLOAD_ERR_NO_FILE bypass.
  • Extension points worth knowing: subclass Input and register it in InputFilterPluginManager (that's what the type key resolves against), and register named input filters there too so specs can reference them by string. Note that Laminas\InputFilter\Factory collides with several Laravel Factory classes — alias your imports (use Laminas\InputFilter\Factory as InputFilterFactory).
  • Version skew: zendframework/zend-inputfilter 2.7/2.8 vs current Laminas differ in InputFilterPluginManager wiring, type resolution, and continue_if_empty. Pin the version in composer.json and check composer show before trusting a spec snippet from a 2015 blog post.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager