PHPackages                             qubus/form - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. qubus/form

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

qubus/form
==========

A PHP form builder for HTML 5 and Bootstrap forms.

v3.0.0(2w ago)0661MITPHPPHP &gt;=8.4

Since Nov 6Pushed 2w agoCompare

[ Source](https://github.com/QubusPHP/form)[ Packagist](https://packagist.org/packages/qubus/form)[ RSS](/packages/qubus-form/feed)WikiDiscussions 3.x Synced 1w ago

READMEChangelogDependencies (8)Versions (12)Used By (1)

Qubus Form
==========

[](#qubus-form)

A fluent HTML5 form builder with Bootstrap 4 and Bootstrap 5 rendering.

Requirements
------------

[](#requirements)

- PHP 8.4+

Installation
------------

[](#installation)

```
composer require qubus/form
```

Fluent API
----------

[](#fluent-api)

```
use Qubus\Form\FormBuilder;
use Qubus\Form\FormBuilder\Decorator\Bootstrap;

$form = FormBuilder::create(
    options: ['id' => 'account'],
    attributes: ['action' => '/accounts']
)->addDecorator(new Bootstrap(Bootstrap::VERSION_5));

$form
    ->field('email', 'user[email]', ['required' => true])
        ->attributes(['autocomplete' => 'email'])
        ->classes('email-field')
    ->end()
    ->field('password', 'user[password]', ['required' => true, 'minlength' => 12])
    ->end()
    ->field('select', 'user[role]', [
        'items' => ['member' => 'Member', 'admin' => 'Administrator'],
    ]);

echo $form;
```

`field()` returns the new element. Call `end()` to return to its parent. Existing methods such as `setOption()` and `setAttr()` remain available, while `option()`, `options()`, `attributes()`, `classes()`, and `value()` provide the concise fluent surface.

Field examples and rendered HTML
--------------------------------

[](#field-examples-and-rendered-html)

The following examples show the builder calls and their rendered output. Generated IDs combine the form ID and field name, including nested field names.

### Email field

[](#email-field)

```
$form = FormBuilder::create(['id' => 'account']);
$form->field('email', 'user[email]', [
    'description' => 'Email address',
    'required' => true,
])
    ->attributes([
        'autocomplete' => 'email',
        'placeholder' => 'name@example.com',
    ])
    ->value('person@example.com');
```

```

        Email address *

```

### Textarea

[](#textarea)

Textarea values are placed between the tags and escaped; they are never rendered as a `value`attribute.

```
$form = FormBuilder::create(['id' => 'profile']);
$form->field('textarea', 'bio', [
    'description' => 'Biography',
    'maxlength' => 160,
])->value('Developer & writer');
```

```

        Biography
        Developer &amp; writer

```

### Select field

[](#select-field)

The configured value selects the matching item. Submitted values not present in `items` fail server-side validation.

```
$form = FormBuilder::create(['id' => 'settings']);
$form->field('select', 'timezone', [
    'description' => 'Time zone',
    'placeholder' => 'Choose a time zone',
    'items' => [
        'America/Los_Angeles' => 'Pacific Time',
        'America/New_York' => 'Eastern Time',
    ],
])->value('America/Los_Angeles');
```

```

        Time zone

            Choose a time zone
            Pacific Time
            Eastern Time

```

### Checkbox

[](#checkbox)

The `boolean` type creates a checkbox. Its hidden input ensures an unchecked checkbox still submits an empty value.

```
$form = FormBuilder::create(['id' => 'terms']);
$form->field('boolean', 'terms', [
    'description' => 'I accept the terms',
    'required' => true,
])->value(true);
```

```

         I accept the terms *

```

### Bootstrap 5 field with help text

[](#bootstrap-5-field-with-help-text)

```
$form = FormBuilder::create(['id' => 'contact'])
    ->addDecorator(new Bootstrap(Bootstrap::VERSION_5));

$form->field('email', 'email', [
    'description' => 'Email',
    'help' => 'We will never share your email.',
])->attributes(['placeholder' => 'name@example.com']);
```

```

        Email

            We will never share your email.

```

Use `Bootstrap::VERSION_4` for Bootstrap 4. The same field uses `form-group`, `form-control`, and Bootstrap 4 help-text conventions.

### Required indicators

[](#required-indicators)

Setting `required` adds both the native HTML attribute and a visible suffix to the label. The default suffix is ` *`:

```
$form->field('text', 'name', ['required' => true]);
// Name *
```

Customize or disable the marker per field:

```
$form->field('text', 'name', [
    'required' => true,
    'required-suffix' => ' (required)',
]);

$form->field('text', 'reference', [
    'required' => true,
    'required-suffix' => '',
]);
```

You can also change the default for all fields through `FormBuilder::$options['required-suffix']`. The suffix is escaped as label text and is not added to placeholders.

Bootstrap 4 and 5
-----------------

[](#bootstrap-4-and-5)

Pass the target major version explicitly:

```
$form->addDecorator(new Bootstrap(Bootstrap::VERSION_4));
$form->addDecorator(new Bootstrap(Bootstrap::VERSION_5));

// The registered fluent equivalent:
$form->addDecorator('bootstrap', 5);
```

Bootstrap 4 emits `form-group`, `form-control`, and `text-muted` conventions. Bootstrap 5 emits `mb-3`, `form-select`, and current input-group markup without the removed prepend and append wrapper elements.

Submission and CSRF
-------------------

[](#submission-and-csrf)

```
$form->csrf($_SESSION['csrf_token']);

if ($form->isSubmitted() && $form->isValid()) {
    $values = $form->getValues();
}
```

CSRF protection is opt-in because token storage belongs to the host application. When enabled, a hidden token field is rendered and `isSubmitted()` rejects mismatches using a timing-safe comparison. Request arrays can also be injected into `isSubmitted()` for tests. Nested names such as `user[address][city]` are hydrated from normal PHP request arrays.

Rendered attribute values, labels, errors, textarea values, choices, and button text are escaped. Passing literal strings to `Group::add()` is the explicit raw-HTML escape hatch; never pass untrusted input to it.

Password and file values are not repopulated. Select and choice controls reject submitted values that are absent from their configured item list.

Uploads
-------

[](#uploads)

`file` uses `FileInput`; `image` uses `ImageInput` and additionally verifies that PHP recognizes the temporary upload as an image. Upload fields support these optional validation rules:

- `max-size`: maximum size in bytes, measured from the temporary file rather than client data.
- `mime-types`: one MIME type or an array of allowed types, detected from file contents with PHP's Fileinfo extension. Wildcards such as `image/*` are supported.
- `extensions`: one filename extension or an array of allowed extensions. Matching is case-insensitive and leading dots are optional.

When `accept` is not set explicitly, it is generated from `mime-types`, or from `extensions`when no MIME types are configured. The browser attribute is only a file-picker hint; the configured options are also enforced by server-side validation.

`FileInput::moveUploadedFile()` accepts an explicit path or directory, sanitizes client filenames used with directories, refuses overwrites by default, and does not delete wildcard matches. Applications should still choose a safe storage location and generated destination name, and apply any domain-specific content scanning needed before serving uploaded files.

### Upload form example

[](#upload-form-example)

Upload forms must use `multipart/form-data`. This Bootstrap 5 example includes a required PDF, an optional image, help text, and a submit button:

```
use Qubus\Form\FormBuilder;
use Qubus\Form\FormBuilder\Decorator\Bootstrap;

$form = FormBuilder::create(
    options: ['id' => 'documents'],
    attributes: [
        'action' => '/documents/upload',
        'enctype' => 'multipart/form-data',
    ],
)->addDecorator(new Bootstrap(Bootstrap::VERSION_5));

$form
    ->field('file', 'document', [
        'description' => 'Document',
        'required' => true,
        'help' => 'PDF files up to 10 MB.',
        'max-size' => 10 * 1024 * 1024,
        'mime-types' => ['application/pdf'],
        'extensions' => ['pdf'],
    ])
    ->end()
    ->field('image', 'cover', [
        'description' => 'Cover image',
        'help' => 'JPEG, PNG, or WebP.',
        'max-size' => 5 * 1024 * 1024,
        'mime-types' => ['image/jpeg', 'image/png', 'image/webp'],
        'extensions' => ['jpg', 'jpeg', 'png', 'webp'],
    ])
    ->end()
    ->field('submit', 'upload', [
        'description' => 'Upload files',
    ]);

echo $form;
```

Rendered HTML:

```

        Document *

            PDF files up to 10 MB.

        Cover image

            JPEG, PNG, or WebP.

        Upload files

```

Validate before moving either upload. `moveUploadedFile()` only accepts a genuine file uploaded through PHP and refuses to replace an existing destination unless its second argument is `true`:

```
use Qubus\Form\FormBuilder\FileInput;

if ($form->isSubmitted() && $form->isValid()) {
    $document = $form->get('document');

    if ($document instanceof FileInput && $document->isUploaded()) {
        $document->moveUploadedFile('/srv/private-uploads/generated-document.pdf');
    }
}
```

###  Health Score

49

—

FairBetter than 94% of packages

Maintenance97

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~57 days

Recently: every ~84 days

Total

12

Last Release

16d ago

Major Versions

v1.0.5 → v2.0.02025-08-31

v2.0.1 → v3.0.02026-08-02

PHP version history (2 changes)v1.0.0PHP &gt;=8.2

v2.0.0PHP &gt;=8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/86d25d805eb5b710fab925600008b72f9f5f11a5206154fcd1ded01794a4d1b8?d=identicon)[nomadicjosh](/maintainers/nomadicjosh)

---

Top Contributors

[![nomadicjosh](https://avatars.githubusercontent.com/u/2042176?v=4)](https://github.com/nomadicjosh "nomadicjosh (12 commits)")

---

Tags

htmlHTML5bootstrapFormsformbuilder

### Embed Badge

![Health badge](/badges/qubus-form/health.svg)

```
[![Health](https://phpackages.com/badges/qubus-form/health.svg)](https://phpackages.com/packages/qubus-form)
```

###  Alternatives

[barryvdh/laravel-form-bridge

This packages integrates Symfony Form Component in Laravel.

163377.4k4](/packages/barryvdh-laravel-form-bridge)[netojose/laravel-bootstrap-4-forms

Bootstrap 4 form builder for Laravel 5

179123.5k](/packages/netojose-laravel-bootstrap-4-forms)[gregwar/formidable

Formidable, the pragmatic forms library

11965.1k1](/packages/gregwar-formidable)[phpstrap/phpstrap

Bootstrap layout generator

1215.3k](/packages/phpstrap-phpstrap)[czubehead/bootstrap-4-forms

Nette extension for Bootstrap 4 forms

1019.5k1](/packages/czubehead-bootstrap-4-forms)[bostondv/bootstrap-ninja-forms

Adds Bootstrap classes to Ninja Forms

222.2k](/packages/bostondv-bootstrap-ninja-forms)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
