PHPackages                             imperazim/libform - 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. imperazim/libform

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

imperazim/libform
=================

Form library for PocketMine plugins

v2.0.0(2w ago)11[1 PRs](https://github.com/ImperaZim/LibForm/pulls)MITPHPPHP ^8.2CI passing

Since Jun 3Pushed 1w agoCompare

[ Source](https://github.com/ImperaZim/LibForm)[ Packagist](https://packagist.org/packages/imperazim/libform)[ RSS](/packages/imperazim-libform/feed)WikiDiscussions stable Synced today

READMEChangelog (3)Dependencies (3)Versions (5)Used By (0)

 [![LibForm](icon.png)](icon.png)

 [![Version 2.0.0](https://camo.githubusercontent.com/d30d7eb420ef541fcc2347e387e18f34920e4a5e087534e4e17364470dc6c21f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d322e302e302d626c75653f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/d30d7eb420ef541fcc2347e387e18f34920e4a5e087534e4e17364470dc6c21f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d322e302e302d626c75653f7374796c653d666c61742d737175617265) [![PocketMine-MP 5.0.0+](https://camo.githubusercontent.com/a4a20a2df750a58a0e380a1a0b8b3e5570e598d96cf2e2ada90136aa68cec479/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f506f636b65744d696e652d2d4d502d352e302e302b2d626c75653f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/a4a20a2df750a58a0e380a1a0b8b3e5570e598d96cf2e2ada90136aa68cec479/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f506f636b65744d696e652d2d4d502d352e302e302b2d626c75653f7374796c653d666c61742d737175617265) [![PHP 8.2+](https://camo.githubusercontent.com/0a0268c7a5d91dead79d37bc60ba6bcf5ace0dcb07573d6393fcb97953c625c2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322b2d3737376262343f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/0a0268c7a5d91dead79d37bc60ba6bcf5ace0dcb07573d6393fcb97953c625c2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322b2d3737376262343f7374796c653d666c61742d737175617265) [![License](https://camo.githubusercontent.com/194e8e68ee1db8ebccc3831e1e873350a1f9cf9fc7dbd5147b65caadd00799eb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f496d706572615a696d2f4c6962466f726d3f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/194e8e68ee1db8ebccc3831e1e873350a1f9cf9fc7dbd5147b65caadd00799eb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f496d706572615a696d2f4c6962466f726d3f7374796c653d666c61742d737175617265)

LibForm
=======

[](#libform)

Description
-----------

[](#description)

*A comprehensive form library for PocketMine-MP that simplifies the creation and management of Minecraft Bedrock Edition forms, supporting class-based definitions, fluent dynamic builders, pagination, form chaining, conditional elements, async data loading, timeouts, and ready-made presets.*

Features
--------

[](#features)

- **Three form types:** Long forms (button lists), modal dialogs (yes/no), and custom forms (inputs, toggles, sliders, dropdowns).
- **Class-based and dynamic approaches:** Extend abstract classes for reusable form definitions, or use fluent method chaining for quick one-off forms.
- **FormBuilder shorthand:** Ultra-concise form creation through `FormBuilder::long()`, `FormBuilder::modal()`, and `FormBuilder::custom()`.
- **Pagination:** Automatically paginate large button lists with `PaginatedLongForm`.
- **Dynamic buttons:** Generate buttons at runtime from data sources with `DynamicButtonForm`.
- **Form chaining:** Build multi-step wizards with `FormChain`, supporting conditional step skipping.
- **Conditional elements:** Show or hide form elements based on trigger values with `ConditionalForm`.
- **Async data loading:** Load form content asynchronously with `AsyncFormData`, displaying a loading screen while data is fetched.
- **Form history:** Stack-based back-button navigation with `FormHistory`.
- **Form timeouts:** Automatically close forms after a set duration with `FormTimeout`.
- **Presets:** Common form patterns (confirm dialog, player selector, color picker, number input, text input) via `FormPresets`.
- **Server-side validation:** Validate text inputs with regex, length constraints, or custom closures using `ValidatedInput`.
- **Searchable dropdowns:** Filter dropdown options at runtime with `SearchableDropdown`.
- **State management:** Share data between forms using the array-accessible `FormData` container.
- **Flow control:** Return `FormResult::CLOSE` or `FormResult::KEEP` to control whether a form closes or re-opens after interaction.

How to Use
----------

[](#how-to-use)

Add LibForm to your project with Composer:

```
composer require imperazim/libform
```

Then import classes under the `imperazim\form` namespace. All form types require PHP 8.2+ and PocketMine-MP API 5.0.0.

---

Components
----------

[](#components)

### `imperazim\form`

[](#imperazimform)

#### Highlights

[](#highlights)

- `Form` -- abstract base class implementing `pocketmine\form\Form`. Provides `sendTo()`, `sendWithHistory()`, and JSON serialization.
- `FormResult` -- enum with `CLOSE` and `KEEP` cases to control post-interaction behavior.
- `FormData` -- array-accessible data container (`ArrayAccess`, `IteratorAggregate`, `Countable`) for sharing state between forms.
- `LibForm` -- plugin entry point for standalone usage.

#### Usage

[](#usage)

```
use imperazim\form\FormData;
use imperazim\form\FormResult;

// Create a shared data container
$formData = new FormData([
    'playerName' => $player->getName(),
    'score'      => 1000,
]);

// Access values
$name  = $formData->get('playerName');
$score = $formData['score'];

// Check existence
$formData->has('playerName'); // true

// Flow control in form callbacks
return FormResult::CLOSE; // close the form
return FormResult::KEEP;  // re-send the form
```

---

### `imperazim\form\base\elements`

[](#imperazimformbaseelements)

#### Highlights

[](#highlights-1)

- `TextElement` -- abstract base for text-based elements, implements `JsonSerializable`.
- `Title` -- represents a form title. Extends `TextElement`.
- `Content` -- represents form body text. Extends `TextElement`.

#### Usage

[](#usage-1)

```
use imperazim\form\base\elements\Title;
use imperazim\form\base\elements\Content;

$title   = new Title('My Form');
$content = new Content('Select an option below:');

$title->getText();   // "My Form"
$content->getText(); // "Select an option below:"
```

---

### `imperazim\form\long`

[](#imperazimformlong)

#### Highlights

[](#highlights-2)

- `LongForm` -- abstract class for scrollable button-list forms. Extend and override `title()`, `content()`, and `buttons()`. Pass `true` as the third constructor argument to auto-send.
- `DynamicLongForm` -- fluent builder for long forms. Create with `DynamicLongForm::create()` or `DynamicLongForm::build()`, chain methods, then call `sendTo()`.
- `DynamicButtonForm` -- long form whose buttons are generated from a factory closure at send time. Supports optional caching and cache invalidation.
- `PaginatedLongForm` -- automatically splits a large `ButtonCollection` into pages with "Previous" / "Next" navigation buttons.

#### Usage

[](#usage-2)

**Class-based LongForm:**

```
use pocketmine\player\Player;
use imperazim\form\FormData;
use imperazim\form\FormResult;
use imperazim\form\long\LongForm;
use imperazim\form\base\elements\Title;
use imperazim\form\base\elements\Content;
use imperazim\form\long\elements\Button;
use imperazim\form\long\elements\ButtonTexture;
use imperazim\form\long\elements\ButtonCollection;
use imperazim\form\long\response\ButtonResponse;

class MyListForm extends LongForm {
    protected function title(Player $player, FormData $data): Title {
        return new Title('Main Menu');
    }

    protected function content(Player $player, FormData $data): Content {
        return new Content('Select an option:');
    }

    protected function buttons(Player $player, FormData $data): ButtonCollection {
        return ButtonCollection::fromArray([
            new Button(
                'Play',
                new ButtonTexture('textures/ui/play', ButtonTexture::PATH),
                new ButtonResponse(function(Player $player): FormResult {
                    $player->sendMessage("Let's go!");
                    return FormResult::CLOSE;
                })
            ),
            new Button(
                'Settings',
                new ButtonTexture('', ButtonTexture::PATH),
                new ButtonResponse(fn(Player $p) => FormResult::CLOSE)
            ),
        ]);
    }
}

// Auto-send on construction:
new MyListForm($player, new FormData(), true);
```

**DynamicLongForm (fluent):**

```
use imperazim\form\FormResult;
use imperazim\form\long\DynamicLongForm;

DynamicLongForm::create("Quick Menu")
    ->setContent("What do you want to do?")
    ->addButton("Status", 'textures/blocks/diamond_block', function(Player $p): FormResult {
        $p->sendMessage("Status chosen!");
        return FormResult::CLOSE;
    })
    ->addButton("Settings", null, fn(Player $p) => FormResult::CLOSE)
    ->setOnClose(fn(Player $p) => FormResult::CLOSE)
    ->sendTo($player);
```

**DynamicButtonForm (runtime-generated buttons):**

```
use pocketmine\Server;
use imperazim\form\FormResult;
use imperazim\form\long\DynamicButtonForm;
use imperazim\form\long\elements\Button;
use imperazim\form\long\elements\ButtonTexture;
use imperazim\form\long\elements\ButtonCollection;
use imperazim\form\long\response\ButtonResponse;

DynamicButtonForm::send(
    $player,
    "Online Players",
    "Select a player:",
    function(): ButtonCollection {
        $buttons = new ButtonCollection();
        foreach (Server::getInstance()->getOnlinePlayers() as $p) {
            $buttons->add(new Button(
                $p->getName(),
                new ButtonTexture('', ButtonTexture::PATH),
                new ButtonResponse(fn(Player $pl) => FormResult::CLOSE)
            ));
        }
        return $buttons;
    },
    fn(Player $p, Button $btn, int $idx) => FormResult::CLOSE
);
```

**PaginatedLongForm:**

```
use imperazim\form\FormResult;
use imperazim\form\long\PaginatedLongForm;
use imperazim\form\long\elements\Button;
use imperazim\form\long\elements\ButtonCollection;

$allButtons = new ButtonCollection();
for ($i = 1; $i add(new Button(
        "Item #$i",
        new ButtonTexture('', ButtonTexture::PATH),
        new ButtonResponse(fn(Player $p) => FormResult::CLOSE)
    ));
}

$form = new PaginatedLongForm(
    player: $player,
    title: "Shop Items",
    content: "Select an item to buy:",
    buttons: $allButtons,
    onSelect: fn(Player $p, Button $btn, int $globalIndex) => FormResult::CLOSE,
    perPage: 8
);
$form->sendTo($player);
```

---

### `imperazim\form\long\elements`

[](#imperazimformlongelements)

#### Highlights

[](#highlights-3)

- `Button` -- represents a form button with text, a `ButtonTexture`, and a `ButtonResponse` callback. Implements `JsonSerializable`.
- `ButtonTexture` -- defines a button icon source. Use `ButtonTexture::PATH` for resource pack paths or `ButtonTexture::URL` for remote URLs.
- `ButtonCollection` -- immutable, countable, iterable collection of `Button` objects. Supports `ArrayAccess`, `fromArray()`, and `getByIndex()`.

#### Usage

[](#usage-3)

```
use imperazim\form\long\elements\Button;
use imperazim\form\long\elements\ButtonTexture;
use imperazim\form\long\elements\ButtonCollection;
use imperazim\form\long\response\ButtonResponse;
use imperazim\form\FormResult;

// Create a button with a path-based texture
$button = new Button(
    'Diamond Sword',
    new ButtonTexture('textures/items/diamond_sword', ButtonTexture::PATH),
    new ButtonResponse(fn(Player $p) => FormResult::CLOSE)
);

$button->getText();      // "Diamond Sword"
$button->getTexture();   // ButtonTexture instance
$button->getResponse();  // ButtonResponse instance

// Create a button with a URL-based texture
$urlButton = new Button(
    'Website',
    new ButtonTexture('https://example.com/icon.png', ButtonTexture::URL),
    new ButtonResponse(fn(Player $p) => FormResult::CLOSE)
);

// Build a collection
$collection = ButtonCollection::fromArray([$button, $urlButton]);
$collection->count();        // 2
$collection->getByIndex(0);  // first Button
```

---

### `imperazim\form\long\response`

[](#imperazimformlongresponse)

#### Highlights

[](#highlights-4)

- `ButtonResponse` -- wraps a `Closure` that handles button clicks. The closure receives a `Player` and must return a `FormResult`.

#### Usage

[](#usage-4)

```
use pocketmine\player\Player;
use imperazim\form\FormResult;
use imperazim\form\long\response\ButtonResponse;

$response = new ButtonResponse(function(Player $player): FormResult {
    $player->sendMessage("Button clicked!");
    return FormResult::CLOSE;
});

$result = $response->handle($player); // FormResult::CLOSE
```

---

### `imperazim\form\modal`

[](#imperazimformmodal)

#### Highlights

[](#highlights-5)

- `ModalForm` -- abstract class for two-button confirmation dialogs. Extend and override `title()`, `content()`, `button1()`, `button2()`, `onConfirm()`, and `onCancel()`.
- `DynamicModalForm` -- fluent builder for modal forms. Supports `setContent()`, `setButton1()`, `setButton2()`, `setOnClose()`, and the static `build()` configurator pattern.

#### Usage

[](#usage-5)

**Class-based ModalForm:**

```
use pocketmine\player\Player;
use imperazim\form\FormData;
use imperazim\form\FormResult;
use imperazim\form\modal\ModalForm;
use imperazim\form\modal\elements\ModalButton;
use imperazim\form\base\elements\Title;
use imperazim\form\base\elements\Content;

class DeleteConfirmForm extends ModalForm {
    protected function title(Player $player, FormData $data): Title {
        return new Title('Delete Item');
    }

    protected function content(Player $player, FormData $data): Content {
        return new Content('Are you sure you want to delete this item?');
    }

    protected function button1(Player $player, FormData $data): ModalButton {
        return ModalButton::confirm('Yes, delete');
    }

    protected function button2(Player $player, FormData $data): ModalButton {
        return ModalButton::cancel('Cancel');
    }

    protected function onConfirm(Player $player, FormData $data): FormResult {
        $player->sendMessage('Item deleted!');
        return FormResult::CLOSE;
    }

    protected function onCancel(Player $player, FormData $data): FormResult {
        return FormResult::CLOSE;
    }
}

new DeleteConfirmForm($player, new FormData(), true);
```

**DynamicModalForm (fluent):**

```
use pocketmine\player\Player;
use imperazim\form\FormResult;
use imperazim\form\modal\DynamicModalForm;
use imperazim\form\modal\response\ModalResponse;

DynamicModalForm::create("Save Changes")
    ->setContent("Do you want to save your changes?")
    ->setButton1("Save", function(Player $p, ModalResponse $r): FormResult {
        $p->sendMessage('Changes saved!');
        return FormResult::CLOSE;
    })
    ->setButton2("Discard", function(Player $p, ModalResponse $r): FormResult {
        $p->sendMessage('Changes discarded.');
        return FormResult::CLOSE;
    })
    ->sendTo($player);
```

---

### `imperazim\form\modal\elements`

[](#imperazimformmodalelements)

#### Highlights

[](#highlights-6)

- `ModalButton` -- represents a modal dialog button. Created via named constructors `ModalButton::confirm($label)` and `ModalButton::cancel($label)`. Associates with a `ModalResult` type.

#### Usage

[](#usage-6)

```
use imperazim\form\modal\elements\ModalButton;

$yes = ModalButton::confirm('Yes');
$no  = ModalButton::cancel('No');

$yes->getText(); // "Yes"
$yes->getType(); // ModalResult::CONFIRM
```

---

### `imperazim\form\modal\response`

[](#imperazimformmodalresponse)

#### Highlights

[](#highlights-7)

- `ModalResponse` -- value object wrapping a `ModalResult`. Provides `isConfirmed()`, `isCanceled()`, and `getResult()`.
- `ModalResult` -- enum with `CONFIRM` and `CANCEL` cases.

#### Usage

[](#usage-7)

```
use imperazim\form\modal\response\ModalResponse;
use imperazim\form\modal\result\ModalResult;

$response = ModalResponse::from(true);
$response->isConfirmed(); // true
$response->isCanceled();  // false
$response->getResult();   // ModalResult::CONFIRM
```

---

### `imperazim\form\custom`

[](#imperazimformcustom)

#### Highlights

[](#highlights-8)

- `CustomForm` -- abstract class for forms with multiple input elements. Extend and override `title()`, `elements()`, `onSubmit()`, and `onClose()`. Supports `ValidatedInput` server-side validation.
- `DynamicCustomForm` -- fluent builder for custom forms. Use `addElement()`, `setOnSubmit()`, `setOnClose()`, and the static `build()` configurator pattern.

#### Usage

[](#usage-8)

**Class-based CustomForm:**

```
use pocketmine\player\Player;
use imperazim\form\FormData;
use imperazim\form\FormResult;
use imperazim\form\custom\CustomForm;
use imperazim\form\custom\elements\ElementCollection;
use imperazim\form\custom\elements\Input;
use imperazim\form\custom\elements\Toggle;
use imperazim\form\custom\elements\Slider;
use imperazim\form\custom\elements\Dropdown;
use imperazim\form\custom\elements\Option;
use imperazim\form\custom\response\CustomResponse;
use imperazim\form\base\elements\Title;

class SettingsForm extends CustomForm {
    protected function title(Player $player, FormData $data): Title {
        return new Title('Settings');
    }

    protected function elements(Player $player, FormData $data): ElementCollection {
        return ElementCollection::fromArray([
            new Input('username', 'Username:', 'Type your name', $player->getName()),
            new Toggle('notifications', 'Enable notifications?', true),
            new Slider('volume', 'Volume', 0, 100, 1, 50),
            new Dropdown('language', 'Language', [
                new Option('en', 'English'),
                new Option('es', 'Spanish'),
            ], 'en'),
        ]);
    }

    protected function onSubmit(Player $player, CustomResponse $response): FormResult {
        $name   = $response->getInput('username');
        $volume = $response->getSlider('volume');
        $lang   = $response->getDropdown('language');
        $player->sendMessage("Saved: $name, vol=$volume, lang={$lang->getId()}");
        return FormResult::CLOSE;
    }

    protected function onClose(Player $player, FormData $data): FormResult {
        return FormResult::CLOSE;
    }
}

new SettingsForm($player, new FormData(), true);
```

**DynamicCustomForm (fluent):**

```
use pocketmine\player\Player;
use imperazim\form\FormResult;
use imperazim\form\custom\DynamicCustomForm;
use imperazim\form\custom\elements\Input;
use imperazim\form\custom\elements\Toggle;
use imperazim\form\custom\response\CustomResponse;

DynamicCustomForm::create("Player Profile")
    ->addElement(new Input('nickname', 'Nickname:', 'Enter nickname...', $player->getName()))
    ->addElement(new Toggle('pvp', 'Enable PvP?', false))
    ->setOnSubmit(function(Player $p, CustomResponse $response): FormResult {
        $nick = $response->getInput('nickname');
        $pvp  = $response->getToggle('pvp') ? 'enabled' : 'disabled';
        $p->sendMessage("Nickname: $nick | PvP: $pvp");
        return FormResult::CLOSE;
    })
    ->sendTo($player);
```

---

### `imperazim\form\custom\elements`

[](#imperazimformcustomelements)

#### Highlights

[](#highlights-9)

- `Element` -- abstract base class for all custom form elements. Provides `getId()`, `hasId()`, and `hasValue()`.
- `ElementCollection` -- immutable, countable, iterable collection of `Element` objects. Supports `ArrayAccess`, `fromArray()`, and `getByIndex()`.
- `Input` -- text input field with ID, label, placeholder, and default value.
- `ValidatedInput` -- extends `Element` with server-side validation: regex pattern, min/max length, custom validator closure, and configurable error message.
- `Toggle` -- on/off boolean switch.
- `Slider` -- numeric range selector with min, max, step, and default. Validates that min &lt; max and step &gt; 0.
- `Dropdown` -- single-select option list. Uses `OptionNormalizerTrait` for flexible option input formats.
- `StepSlider` -- visual step selector, similar to `Dropdown`. Provides `getStepByIndex()` and `getSelectedStep()` aliases.
- `SearchableDropdown` -- dropdown with runtime filtering via `search()` and `setOptions()`. Supports a placeholder text.
- `Label` -- non-interactive text element. Returns `false` from `hasValue()`.
- `Option` -- value object representing a selectable option with an `id` and display `text`.
- `OptionNormalizerTrait` -- shared trait used by `Dropdown`, `StepSlider`, and `SearchableDropdown`. Normalizes raw arrays into `Option[]` and resolves default indices.

#### Usage

[](#usage-9)

```
use imperazim\form\custom\elements\Input;
use imperazim\form\custom\elements\ValidatedInput;
use imperazim\form\custom\elements\Toggle;
use imperazim\form\custom\elements\Slider;
use imperazim\form\custom\elements\Dropdown;
use imperazim\form\custom\elements\StepSlider;
use imperazim\form\custom\elements\SearchableDropdown;
use imperazim\form\custom\elements\Label;
use imperazim\form\custom\elements\Option;
use imperazim\form\custom\elements\ElementCollection;

// Input
$input = new Input('name', 'Your Name:', 'Enter name...', 'Steve');

// ValidatedInput with regex and length constraints
$email = new ValidatedInput(
    'email', 'Email:', 'user@example.com', '',
    pattern: '/^[^@]+@[^@]+\.[a-z]+$/i',
    minLength: 5,
    maxLength: 64,
    errorMessage: 'Invalid email format'
);

// ValidatedInput with custom closure
$age = new ValidatedInput(
    'age', 'Age:', '', '',
    validator: fn(string $v) => is_numeric($v) && (int)$v >= 13,
    errorMessage: 'You must be at least 13'
);

// Toggle
$toggle = new Toggle('pvp', 'Enable PvP?', false);

// Slider
$slider = new Slider('volume', 'Volume', 0, 100, 5, 50);

// Dropdown with Option objects
$dropdown = new Dropdown('color', 'Favorite Color', [
    new Option('red', 'Red'),
    new Option('blue', 'Blue'),
    new Option('green', 'Green'),
], 'red');

// Dropdown with simple string array
$dropdown2 = new Dropdown('mode', 'Game Mode', ['Survival', 'Creative', 'Adventure']);

// StepSlider
$stepper = new StepSlider('difficulty', 'Difficulty', [
    new Option('easy', 'Easy'),
    new Option('normal', 'Normal'),
    new Option('hard', 'Hard'),
], 'normal');

// SearchableDropdown
$searchable = new SearchableDropdown('kit', 'Select Kit:', [
    new Option('warrior', 'Warrior'),
    new Option('archer', 'Archer'),
    new Option('mage', 'Mage'),
], placeholder: 'Type to search...');
$results = $searchable->search('war'); // filters matching options

// Label (non-interactive)
$label = new Label('Welcome to the settings menu!');

// Build a collection
$collection = ElementCollection::fromArray([
    $label, $input, $toggle, $slider, $dropdown,
]);
```

---

### `imperazim\form\custom\response`

[](#imperazimformcustomresponse)

#### Highlights

[](#highlights-10)

- `CustomResponse` -- encapsulates submitted form data. Provides typed getters: `getInput()`, `getToggle()`, `getSlider()`, `getDropdown()`, `getStepSlider()`, as well as generic `getString()`, `getInt()`, `getBool()`, `getFloat()`, `getArray()`. Also exposes `getFormData()`, `getElement()`, `getElementsRaw()`, and `has()`.
- `SelectedOption` -- value object returned by dropdown/step slider selections. Provides `getIndex()`, `getId()`, and `getText()`.

#### Usage

[](#usage-10)

```
use imperazim\form\custom\response\CustomResponse;
use imperazim\form\custom\response\SelectedOption;

// Inside an onSubmit callback:
$name     = $response->getInput('username');    // string
$enabled  = $response->getToggle('notifications'); // bool
$volume   = $response->getSlider('volume');     // float
$language = $response->getDropdown('language'); // SelectedOption

$language->getId();    // "en"
$language->getText();  // "English"
$language->getIndex(); // 0

// Generic access
$response->getString('username');
$response->getInt('score', 0);
$response->getBool('pvp', false);
$response->has('username'); // true
```

---

### `imperazim\form\confirm`

[](#imperazimformconfirm)

#### Highlights

[](#highlights-11)

- `ConfirmForm` -- a ready-made `ModalForm` subclass for quick yes/no confirmation dialogs. Accepts title, content, confirm/cancel callbacks, and custom button labels. Use `ConfirmForm::send()` for a one-liner.

#### Usage

[](#usage-11)

```
use pocketmine\player\Player;
use imperazim\form\FormResult;
use imperazim\form\confirm\ConfirmForm;

// One-liner static send
ConfirmForm::send(
    $player,
    "Delete Warp",
    "Are you sure you want to delete this warp?",
    function(Player $p): FormResult {
        $p->sendMessage("Warp deleted!");
        return FormResult::CLOSE;
    },
    function(Player $p): FormResult {
        $p->sendMessage("Cancelled.");
        return FormResult::CLOSE;
    },
    confirmLabel: 'Yes, delete',
    cancelLabel: 'Keep it'
);
```

---

### `imperazim\form\history`

[](#imperazimformhistory)

#### Highlights

[](#highlights-12)

- `FormHistory` -- static stack-based form history for back-button navigation. Stores forms per player using `SplStack`. Provides `push()`, `back()`, `hasHistory()`, `clear()`, and `cleanup()`.

#### Usage

[](#usage-12)

```
use imperazim\form\history\FormHistory;

// Push current form before navigating to a new one
FormHistory::push($player, $currentForm);
$nextForm->sendTo($player);

// Go back to the previous form
$previousForm = FormHistory::back($player); // re-sends automatically

// Check if back navigation is available
if (FormHistory::hasHistory($player)) {
    // Show a "Back" button
}

// Clean up on player quit
FormHistory::cleanup($player);
```

---

### `imperazim\form\builder`

[](#imperazimformbuilder)

#### Highlights

[](#highlights-13)

- `FormBuilder` -- static entry point with `custom()`, `long()`, and `modal()` methods returning specialized builders.
- `LongFormBuilder` -- fluent builder wrapping `DynamicLongForm`. Methods: `content()`, `button()`, `onClose()`, `build()`, `send()`.
- `ModalFormBuilder` -- fluent builder wrapping `DynamicModalForm`. Methods: `content()`, `accept()`, `deny()`, `onClose()`, `build()`, `send()`.
- `CustomFormBuilder` -- fluent builder wrapping `DynamicCustomForm`. Methods: `input()`, `toggle()`, `slider()`, `dropdown()`, `stepSlider()`, `label()`, `onSubmit()`, `onClose()`, `build()`, `send()`.

#### Usage

[](#usage-13)

```
use pocketmine\player\Player;
use imperazim\form\FormResult;
use imperazim\form\builder\FormBuilder;

// Long form
FormBuilder::long("Menu")
    ->content("Choose an option:")
    ->button("Play", fn(Player $p) => FormResult::CLOSE)
    ->button("Shop", fn(Player $p) => FormResult::CLOSE, "textures/items/diamond")
    ->send($player);

// Modal form
FormBuilder::modal("Confirm")
    ->content("Are you sure?")
    ->accept("Yes", fn(Player $p, $r) => FormResult::CLOSE)
    ->deny("No", fn(Player $p, $r) => FormResult::CLOSE)
    ->send($player);

// Custom form
FormBuilder::custom("Settings")
    ->label("Configure your preferences:")
    ->input("name", "Player Name", placeholder: "Enter name")
    ->toggle("pvp", "Enable PvP", default: true)
    ->slider("volume", "Volume", 0, 100, 1, 50)
    ->dropdown("mode", "Game Mode", ["Survival", "Creative"])
    ->stepSlider("difficulty", "Difficulty", ["Easy", "Normal", "Hard"])
    ->onSubmit(fn(Player $p, $r) => FormResult::CLOSE)
    ->send($player);
```

---

### `imperazim\form\preset`

[](#imperazimformpreset)

#### Highlights

[](#highlights-14)

- `FormPresets` -- static utility class providing ready-made form templates:
    - `confirm()` -- yes/no confirmation dialog.
    - `numberInput()` -- slider-based numeric input with validation.
    - `playerSelector()` -- button list of all online players (optionally excluding the sender).
    - `colorPicker()` -- Minecraft color code selector.
    - `textInput()` -- single text input form.

#### Usage

[](#usage-14)

```
use pocketmine\player\Player;
use imperazim\form\preset\FormPresets;

// Confirm dialog
FormPresets::confirm($player, "Delete", "Are you sure?", function(Player $p) {
    $p->sendMessage("Deleted!");
});

// Number input
FormPresets::numberInput($player, "Set Level", "Choose level:", function(Player $p, $value) {
    $p->sendMessage("Level set to $value");
}, min: 1, max: 50, step: 1, default: 10);

// Player selector
FormPresets::playerSelector($player, "Choose Player", function(Player $sender, Player $target) {
    $sender->sendMessage("Selected: " . $target->getName());
});

// Color picker
FormPresets::colorPicker($player, function(Player $p, string $colorCode) {
    $p->sendMessage($colorCode . "You picked this color!");
});

// Text input
FormPresets::textInput($player, "Rename", "New name:", function(Player $p, string $text) {
    $p->sendMessage("Renamed to: $text");
}, placeholder: "Enter new name...");
```

---

### `imperazim\form\async`

[](#imperazimformasync)

#### Highlights

[](#highlights-15)

- `AsyncFormData` -- populates form elements via async callbacks. Shows a "Loading..." form while data is being fetched, then re-sends with the loaded content on the next tick. Provides:
    - `customForm()` -- async custom form with dynamically loaded elements.
    - `longForm()` -- async long form with dynamically loaded buttons.

#### Usage

[](#usage-15)

```
use Closure;
use pocketmine\player\Player;
use imperazim\form\FormResult;
use imperazim\form\async\AsyncFormData;
use imperazim\form\custom\elements\Dropdown;
use imperazim\form\custom\elements\Option;

// Async custom form
AsyncFormData::customForm(
    $plugin,
    $player,
    "Shop",
    function(Closure $resolve) {
        // Simulate async operation (DB query, HTTP request, etc.)
        $resolve([
            new Dropdown("item", "Choose item", [
                new Option("sword", "Sword"),
                new Option("shield", "Shield"),
            ]),
        ]);
    },
    function(Player $p, $response): FormResult {
        $item = $response->getDropdown('item');
        $p->sendMessage("You chose: " . $item->getText());
        return FormResult::CLOSE;
    }
);

// Async long form
AsyncFormData::longForm(
    $plugin,
    $player,
    "Warps",
    "Select a warp:",
    function(Closure $resolve) {
        $resolve(["Spawn" => null, "PvP" => null, "Shop" => null]);
    },
    function(Player $p, string $buttonText): FormResult {
        $p->sendMessage("Teleporting to $buttonText...");
        return FormResult::CLOSE;
    }
);
```

---

### `imperazim\form\conditional`

[](#imperazimformconditional)

#### Highlights

[](#highlights-16)

- `ConditionalForm` -- form with elements that appear or disappear based on trigger element values. When a trigger changes, the form is automatically rebuilt and re-sent with the updated element set.

#### Usage

[](#usage-16)

```
use pocketmine\player\Player;
use imperazim\form\FormResult;
use imperazim\form\conditional\ConditionalForm;
use imperazim\form\custom\elements\Toggle;
use imperazim\form\custom\elements\Input;
use imperazim\form\custom\elements\Slider;

$form = new ConditionalForm("Settings");

// Base elements (always shown)
$form->addElement(new Toggle("advanced", "Advanced Mode"));
$form->addElement(new Input("name", "Display Name:", "Enter name..."));

// Conditional elements (shown only when "advanced" is true)
$form->when("advanced", true, function() {
    return [
        new Input("customPrefix", "Custom Prefix:", "[Prefix]"),
        new Slider("particleCount", "Particle Count", 0, 100, 1, 10),
    ];
});

$form->onSubmit(function(Player $p, $response): FormResult {
    $p->sendMessage("Settings saved!");
    return FormResult::CLOSE;
});

$form->send($player);
```

---

### `imperazim\form\chain`

[](#imperazimformchain)

#### Highlights

[](#highlights-17)

- `FormChain` -- orchestrates a sequence of forms shown one after another (wizard/stepper pattern). Each step receives accumulated data from previous steps. Supports conditional step skipping, completion callbacks, and cancellation handling.

#### Usage

[](#usage-17)

```
use pocketmine\player\Player;
use imperazim\form\FormData;
use imperazim\form\chain\FormChain;

FormChain::create($player)
    ->step(fn(Player $p, FormData $data) => new NameForm($p, $data))
    ->step(fn(Player $p, FormData $data) => new AgeForm($p, $data))
    ->step(
        fn(Player $p, FormData $data) => new ConfirmForm($p, $data),
        skip: fn(FormData $d) => $d['skipConfirm'] ?? false
    )
    ->onComplete(function(Player $p, FormData $data) {
        $p->sendMessage("Registration complete! Name: " . $data['name']);
    })
    ->onCancel(function(Player $p, FormData $data, int $step) {
        $p->sendMessage("Cancelled at step $step.");
    })
    ->start();

// Inside each step form, call $chain->advance(['key' => 'value']) to proceed
// or $chain->cancel() to abort the chain.
```

---

### `imperazim\form\timeout`

[](#imperazimformtimeout)

#### Highlights

[](#highlights-18)

- `FormTimeout` -- wraps any form with an automatic timeout that closes it after a specified number of seconds. Manages active timeout tasks per player, automatically cancelling previous timeouts. Provides `send()`, `cancel()`, and `cancelAll()`.

#### Usage

[](#usage-18)

```
use pocketmine\player\Player;
use imperazim\form\timeout\FormTimeout;

// Send a form that auto-closes after 30 seconds
$form = new MyLongForm($player, $data);

FormTimeout::send($plugin, $player, $form, seconds: 30, onTimeout: function(Player $p) {
    $p->sendMessage("Form timed out! Please try again.");
});

// Manually cancel a timeout
FormTimeout::cancel($player->getId());

// Cancel all active timeouts (e.g. on plugin disable)
FormTimeout::cancelAll();
```

---

Licensing information
---------------------

[](#licensing-information)

This project is licensed under MIT. Please see the [LICENSE](/LICENSE) file for details.

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance76

Regular maintenance activity

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 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 ~377 days

Total

2

Last Release

17d ago

Major Versions

v1.0.0 → v2.0.02026-06-16

### Community

Maintainers

![](https://www.gravatar.com/avatar/0b9fc308a46bb613e71af2a06deea6798019ee6cdff0c77a20ba399d904310bc?d=identicon)[ImperaZim](/maintainers/ImperaZim)

---

Top Contributors

[![ImperaZim](https://avatars.githubusercontent.com/u/92953608?v=4)](https://github.com/ImperaZim "ImperaZim (21 commits)")

###  Code Quality

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/imperazim-libform/health.svg)

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

PHPackages © 2026

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