small/forms

Provide PHP class to validate api data.

Maintainers

Package info

git.small-project.dev/lib/small-forms

pkg:composer/small/forms

Transparency log

Statistics

Installs: 205

Dependents: 2

Suggesters: 0

2.2.0 2026-08-12 20:18 UTC

README

            

About

Small Forms is a framework-agnostic PHP library for building form-like input schemas, validating API or application input, normalizing values with modifiers, and hydrating arrays or objects.

It supports:

  • inline forms built at runtime;
  • reusable form classes;
  • forms generated from PHP objects and attributes;
  • input from arrays, JSON, URL-encoded strings, and objects;
  • nested objects and arrays of nested objects;
  • validation rules and value modifiers;
  • hydration back into DTOs or other objects.

Requirements

  • PHP 8.3 or newer;
  • small/collection 3.x.

Install the library with Composer:

composer require small/forms

Attribute forms and Symfony Validator

FormBuilder::createFromAttributes() uses AnnotationAdapter. The current adapter checks for Symfony Validator at runtime, so applications using attribute-driven forms must also install:

composer require symfony/validator

AnnotationAdapter reads Small Forms attributes that implement ValidationRuleInterface or ModifierInterface. Symfony Assert\* constraints are not converted into Small Forms validation rules by the current adapter.

Quick start: inline form

<?php

use Small\Collection\Collection\StringCollection;
use Small\Forms\Form\Field\Type\IntType;
use Small\Forms\Form\Field\Type\StringType;
use Small\Forms\Form\FormBuilder;
use Small\Forms\ValidationRule\ValidateGreaterOrEqual;
use Small\Forms\ValidationRule\ValidateNotEmpty;
use Small\Forms\ValidationRule\ValidateNumberCharsLessThan;

$form = FormBuilder::createInlineForm()
    ->addField(
        'name',
        new StringType(),
        [
            new ValidateNotEmpty(),
            new ValidateNumberCharsLessThan(256),
        ],
    )
    ->addField(
        'age',
        new IntType(),
        [new ValidateGreaterOrEqual(18)],
    );

$form->fillFromArray([
    'name' => 'Ada',
    'age' => 37,
]);

$messages = new StringCollection();
$form->validate($messages);

if ($messages->count() > 0) {
    // Handle validation messages.
}

$data = $form->toArray();

Unknown keys passed to fillFromArray() are ignored.

Validation and error handling

AbstractForm::validate() collects field errors into a StringCollection.

use Small\Collection\Collection\StringCollection;

$messages = new StringCollection();
$form->validate($messages);

if ($messages->count() > 0) {
    // Invalid input.
}

By default, validation does not throw after collecting field messages. Pass true as the second argument when exception-based flow is preferred:

use Small\Collection\Collection\StringCollection;
use Small\Forms\ValidationRule\Exception\ValidationFailException;

$messages = new StringCollection();

try {
    $form->validate($messages, true);
} catch (ValidationFailException $exception) {
    // $messages contains the individual field messages.
}

Reusable form classes

Extend AbstractForm and define fields in build():

<?php

namespace App\Form;

use Small\Forms\Form\AbstractForm;
use Small\Forms\Form\Field\Type\IntType;
use Small\Forms\Form\Field\Type\StringType;
use Small\Forms\ValidationRule\ValidateGreaterOrEqual;
use Small\Forms\ValidationRule\ValidateNotEmpty;

final class PersonForm extends AbstractForm
{
    protected function build(): void
    {
        $this->addField('name', new StringType(), [new ValidateNotEmpty()]);
        $this->addField('age', new IntType(), [new ValidateGreaterOrEqual(18)]);
    }
}

Instantiate the form directly:

$form = (new \App\Form\PersonForm())
    ->fillFromArray($payload);

Input sources

Forms can be populated from several input representations:

$form->fillFromArray($payload);
$form->fillFromJson($json);
$form->fillFromUrlEncodedString($body);
$form->fillFromObject($object);

fillFromJson() throws InvalidInputDataException when the input is not valid JSON.

Field values can also be managed individually:

$form->setFieldValue('name', 'Ada');
$name = $form->getFieldValue('name');

Output and hydration

Convert the form to an array:

$data = $form->toArray();

Or hydrate an existing object whose properties match the form field names:

$form->hydrate($dto);

Nested SubFormType values and arrays of subforms are hydrated recursively.

Modifiers are applied when values are read through getFieldValue() and during hydration. toArray() serializes the form's stored values and formats DateTimeType / DateTimeImmutableType values using the type format.

Object and attribute forms

A form can be generated from an object with FormBuilder::createFromAttributes():

<?php

use Small\Forms\Form\Field\Type\IntType;
use Small\Forms\Form\Field\Type\StringType;
use Small\Forms\Modifier\TrimModifier;
use Small\Forms\ValidationRule\ValidateGreaterOrEqual;
use Small\Forms\ValidationRule\ValidateNotEmpty;

final class PersonInput
{
    #[StringType]
    #[TrimModifier]
    #[ValidateNotEmpty]
    public string $name;

    #[IntType]
    #[ValidateGreaterOrEqual(18)]
    public int $age;
}

$input = new PersonInput();

$form = \Small\Forms\Form\FormBuilder::createFromAttributes($input)
    ->fillFromArray([
        'name' => ' Ada ',
        'age' => 37,
    ]);

$messages = new \Small\Collection\Collection\StringCollection();
$form->validate($messages);
$form->hydrate($input);

The equivalent explicit adapter form is:

$form = \Small\Forms\Form\FormBuilder::createFromAdapter(
    new \Small\Forms\Adapter\AnnotationAdapter($input),
);

Use explicit type attributes for arrays, nested objects, custom date formats, or whenever inference would be ambiguous.

Field types

The built-in field types are:

  • StringType - string validation;
  • IntType - integer validation;
  • FloatType - float validation;
  • BooleanType($falseValue = false, $trueValue = true) - boolean validation plus form-value normalization;
  • DateTimeType($format = 'Y-m-d H:i:s') - mutable DateTime values;
  • DateTimeImmutableType($format = 'Y-m-d H:i:s') - immutable date-time values;
  • ArrayType(AbstractType $type) - arrays whose items use another Small Forms type;
  • SubFormType(string $fromClass) - nested object forms.

Types may be used directly when adding fields or as PHP attributes on object properties.

Arrays

Specify the item type explicitly:

use Small\Forms\Form\Field\Type\ArrayType;
use Small\Forms\Form\Field\Type\IntType;

$form->addField('ids', new ArrayType(new IntType()));

Nested objects

use Small\Forms\Form\Field\Type\SubFormType;

$form->addField('address', new SubFormType(AddressInput::class));

Arrays of nested objects can be expressed by combining both types:

use Small\Forms\Form\Field\Type\ArrayType;
use Small\Forms\Form\Field\Type\SubFormType;

$form->addField(
    'items',
    new ArrayType(new SubFormType(ItemInput::class)),
);

Adding rules and modifiers

Rules and modifiers can be supplied when adding a field:

$form->addField(
    'email',
    new \Small\Forms\Form\Field\Type\StringType(),
    [
        new \Small\Forms\ValidationRule\ValidateNotEmpty(),
        new \Small\Forms\ValidationRule\ValidateEmail(),
    ],
    [
        new \Small\Forms\Modifier\TrimModifier(),
        new \Small\Forms\Modifier\ToLowerModifier(),
    ],
);

They can also be added later:

$form->getField('email')
    ->addRule(new \Small\Forms\ValidationRule\ValidateEmail())
    ->addModifier(new \Small\Forms\Modifier\TrimModifier());

Custom rules implement Small\Forms\Contract\ValidationRuleInterface. Custom modifiers implement Small\Forms\Contract\ModifierInterface.

Validation rules

The current built-in validation-rule classes are:

  • ValidateAtLeastOneOf
  • ValidateBeginWith
  • ValidateBoolean
  • ValidateCallback
  • ValidateChoice
  • ValidateCountGreaterOrEqualThan
  • ValidateCountGreaterThan
  • ValidateCountLessOrEqualThan
  • ValidateCountLessThan
  • ValidateDateTime
  • ValidateDecimal
  • ValidateDivisibleBy
  • ValidateEmail
  • ValidateEmpty
  • ValidateEqual
  • ValidateFloat
  • ValidateFloatArray
  • ValidateGreater
  • ValidateGreaterOrEqual
  • ValidateInt
  • ValidateIntArray
  • ValidateIsFalse
  • ValidateIsNull
  • ValidateIsTrue
  • ValidateJson
  • ValidateLess
  • ValidateLessOrEqual
  • ValidateMatchRegex
  • ValidateMixedArray
  • ValidateNegativeNumber
  • ValidateNotEmpty
  • ValidateNotEqual
  • ValidateNotMatchRegex
  • ValidateNotNull
  • ValidateNumberCharsAtLeast
  • ValidateNumberCharsBetween
  • ValidateNumberCharsLessThan
  • ValidatePathIsDirectory
  • ValidatePosifiveNumber
  • ValidateRange
  • ValidateRequired
  • ValidateSequancialy
  • ValidateString
  • ValidateStringArray
  • ValidateUnique

ValidatePosifiveNumber and ValidateSequancialy are the current class names, including their legacy spelling.

Modifiers

The current built-in modifiers are:

  • ArrayToCollectionModifier
  • ExplodeModifier
  • FalseIfEmptyModifier
  • FormBooleanToPhpModifier
  • ImplodeModifier
  • LTrimModifier
  • NullIfEmptyModifier
  • RoundModifier
  • RTrimModifier
  • StringToDateTimeImmutableModifier
  • StringToDateTimeModifier
  • SubStrModifier
  • ToLowerModifier
  • ToUpperModifier
  • TrimModifier
  • UcFirstModifier
  • UcWordsModifier

Development

Install development dependencies and run the test suite with:

composer install
composer unit-tests

Run Rector with:

composer rector

License

Small Forms is released under the MIT License. See LICENSE.