PHPackages                             shipmonk/input-mapper - 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. shipmonk/input-mapper

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

shipmonk/input-mapper
=====================

Performant array-to-object mapper supporting generics, array shapes, optional fields and much more!

2.1.1(3w ago)3030.9k↓12.8%1[2 issues](https://github.com/shipmonk-rnd/input-mapper/issues)[3 PRs](https://github.com/shipmonk-rnd/input-mapper/pulls)MITPHPPHP ^8.1CI passing

Since May 12Pushed 2d ago3 watchersCompare

[ Source](https://github.com/shipmonk-rnd/input-mapper)[ Packagist](https://packagist.org/packages/shipmonk/input-mapper)[ RSS](/packages/shipmonk-input-mapper/feed)WikiDiscussions master Synced yesterday

READMEChangelog (10)Dependencies (44)Versions (30)Used By (0)

ShipMonk Input Mapper
=====================

[](#shipmonk-input-mapper)

Bidirectional mapper for PHP with support for generics, array shapes and nullable types. For each class, input and output mappers are generated at runtime and cached on disk. The mappers are generated only once and then reused on subsequent requests. The generated mappers are highly optimized for performance and designed to be human readable. You can see examples of generated mappers in the tests directory: [input mapper](tests/Compiler/Mapper/Object/Data/MovieMapper.php), [output mapper](tests/Compiler/Mapper/Object/Data/SimplePersonOutputMapper.php).

Installation:
-------------

[](#installation)

```
composer require shipmonk/input-mapper
```

Features
--------

[](#features)

### Built-in mappers

[](#built-in-mappers)

Input Mapper comes with built-in mappers for the following types:

- `array`, `bool`, `float`, `int`, `mixed`, `string`, `list`
- `positive-int`, `negative-int`, `int`, `non-empty-string`, `non-empty-list`
- `array`, `array`, `list`, `non-empty-list`
- `array{K1: V1, ...}`
- `?T`, `Optional`
- `DateTimeInterface`, `DateTimeImmutable`
- `BackedEnum`
- and most importantly classes with public constructor

All built-in mappers support both input (array → object) and output (object → array) directions.

You can write your own mappers or replace the default mappers with your own.

### Built-in validators

[](#built-in-validators)

Input Mapper comes with some built-in validators (input mapping only):

- int validators:
    - `AssertInt16`
    - `AssertInt32`
    - `AssertIntRange`
    - `AssertPositiveInt`
    - `AssertNegativeInt`
    - `AssertNonNegativeInt`
    - `AssertNonPositiveInt`
    - `AssertIntMultipleOf`
- float validators:
    - `AssertFloatRange`
    - `AssertPositiveFloat`
    - `AssertNegativeFloat`
    - `AssertNonNegativeFloat`
    - `AssertNonPositiveFloat`
    - `AssertFloatMultipleOf`
- string validators:
    - `AssertStringLength`
    - `AssertStringMatches`
    - `AssertStringNonEmpty`
    - `AssertUrl`
- list validators:
    - `AssertListItem`
    - `AssertListLength`
    - `AssertUniqueItems` (compares items by `===`)
- date time validators:
    - `AssertDateTimeRange`

You can write your own validators if you need more.

Usage:
------

[](#usage)

### Write Input Class

[](#write-input-class)

To use Input Mapper, write a class with a public constructor and add either native or PHPDoc types to all constructor parameters.

Optional fields can either be marked with `#[Optional]` attribute (allowing you to specify a default value), or if you need to distinguish between default and missing values, you can wrap the type with `ShipMonk\InputMapper\Runtime\Optional` class.

```
use ShipMonk\InputMapper\Compiler\Attribute\Optional;

class Person
{
    public function __construct(
        public readonly string $name,

        public readonly int $age,

        #[Optional]
        public readonly ?string $email,

        /** @var list */
        public readonly array $hobbies,

        /** @var list */
        #[Optional(default: [])]
        public readonly array $friends,
    ) {}
}
```

By default, any extra properties are not allowed. You can change that by adding `#[AllowExtraKeys]` over the class.

### Map Input

[](#map-input)

To map input data (e.g. JSON) to objects, use `MapperProvider`:

```
$tempDir = __DIR__ . '/temp/input-mapper'; // writable, project-local directory
$autoRefresh = true; // MUST be set to false in production
$mapperProvider = new ShipMonk\InputMapper\Runtime\MapperProvider($tempDir, $autoRefresh);
$mapper = $mapperProvider->getInputMapper(Person::class);

try {
    $person = $mapper->map([
        'name' => 'John',
        'age' => 30,
        'hobbies' => ['hiking', 'reading'],
        'friends' => [
            [
                'name' => 'Jane',
                'age' => 28,
                'hobbies' => ['hiking', 'reading'],
            ],
            [
                'name' => 'Jack',
                'age' => 28,
                'hobbies' => ['hiking', 'reading'],
            ],
        ],
    ]);
} catch (\ShipMonk\InputMapper\Runtime\Exception\MappingFailedException $e) {
    // $e->getMessage() // programmer readable error message in English
    // $e->getPath() // path of the problematic field for example ['friends', 0, 'name']
    // ...
}
```

Generated mappers are PHP files that get `include`d — point `tempDir` at a directory owned by your application, not a shared world-writable location such as the system temporary directory, so that no other local user can pre-create it and plant files that your application would then execute.

### Map Output

[](#map-output)

To convert objects back to plain arrays (e.g. for JSON serialization), use the same `MapperProvider`:

```
$mapper = $mapperProvider->getOutputMapper(Person::class);

$data = $mapper->map($person);
// ['name' => 'John', 'age' => 30, 'email' => null, 'hobbies' => ['hiking', 'reading'], 'friends' => [...]]
```

The output mapper converts objects to arrays, enums to their backing values, `DateTimeImmutable` to formatted strings, and `Optional` properties are omitted from the output when not defined. All types supported by input mapping are also supported by output mapping.

### Adding Validation Rules

[](#adding-validation-rules)

You can add validation rules by adding attributes to constructor parameters.

For example, to validate that `age` is between 18 and 99, you can add the `AssertIntRange` attribute to the constructor parameter:

```
use ShipMonk\InputMapper\Compiler\Validator\Int\AssertIntRange;

class Person
{
    public function __construct(
        public readonly string $name,

        #[AssertIntRange(gte: 18, lte: 99)]
        public readonly int $age,
    ) {}
}
```

### Renaming keys

[](#renaming-keys)

If the input keys do not match the property names, you can use the `#[SourceKey]` attribute to specify the key name:

```
use ShipMonk\InputMapper\Compiler\Attribute\SourceKey;

class Person
{
    public function __construct(
        #[SourceKey('full_name')]
        public readonly string $name,
    ) {}
}
```

To rename keys globally — e.g. camelCase PHP properties ↔ snake\_case wire keys — configure a `PropertyNameTransformer`on the `DefaultMapperCompilerFactoryProvider`. The transform is applied in both directions (input lookup and output emission), and `#[SourceKey]` always takes precedence over it on properties where it is set:

```
use ShipMonk\InputMapper\Compiler\MapperFactory\DefaultMapperCompilerFactoryProvider;
use ShipMonk\InputMapper\Compiler\PropertyNameTransformer\CamelToSnakeCasePropertyNameTransformer;
use ShipMonk\InputMapper\Runtime\MapperProvider;

$provider = new MapperProvider(
    tempDir: __DIR__ . '/temp',
    mapperCompilerFactoryProvider: new DefaultMapperCompilerFactoryProvider(
        new CamelToSnakeCasePropertyNameTransformer(),
    ),
);
```

The built-in `CamelToSnakeCasePropertyNameTransformer` handles common acronym boundaries (`HTTPServer` → `http_server`, `parseURL` → `parse_url`). Implement `PropertyNameTransformer` yourself for other conventions.

### Parsing polymorphic classes (subtypes with a common parent)

[](#parsing-polymorphic-classes-subtypes-with-a-common-parent)

If you need to parse a hierarchy of classes, you can use the `#[Discriminator]` attribute. (The discriminator field does not need to be mapped to a property if `#[AllowExtraKeys]` is used.)

```
use ShipMonk\InputMapper\Compiler\Attribute\Discriminator;

#[Discriminator(
    key: 'type', // key to use for mapping
    mapping: [
        'car' => Car::class,
        'truck' => Truck::class,
    ]
)]
abstract class Vehicle {
    public function __construct(
        public readonly string $type,
    ) {}
}

class Car extends Vehicle {

    public function __construct(
        string $type,
        public readonly string $color,
    ) {
        parent::__construct($type);
    }

}

class Truck extends Vehicle {

    public function __construct(
        string $type,
        public readonly string $color,
    ) {
        parent::__construct($type);
    }

}
```

or, with enum:

```
use ShipMonk\InputMapper\Compiler\Attribute\Discriminator;

enum VehicleType: string {
    case Car = 'car';
    case Truck = 'truck';
}

#[Discriminator(
    key: 'type', // key to use for mapping
    mapping: [
        VehicleType::Car->value => Car::class,
        VehicleType::Truck->value => Truck::class,
    ]
)]
abstract class Vehicle {
    public function __construct(
        VehicleType $type,
    ) {}
}

class Car extends Vehicle {

    public function __construct(
        VehicleType $type,
        public readonly string $color,
    ) {
        parent::__construct($type);
    }

}

class Truck extends Vehicle {

    public function __construct(
        VehicleType $type,
        public readonly string $color,
    ) {
        parent::__construct($type);
    }

}
```

### Using custom mappers

[](#using-custom-mappers)

To map a class with your own hand-written mapper, implement the `Mapper` interface and register a factory for the class with `MapperProvider`:

```
use ShipMonk\InputMapper\Runtime\Mapper;
use ShipMonk\InputMapper\Runtime\MapperProvider;

/**
 * @implements Mapper
 */
class MyCustomClassInputMapper implements Mapper
{
    public function map(mixed $data, array $path = []): MyCustomClass
    {
        return MyCustomClass::createFrom($data);
    }
}

$mapperProvider->registerInputFactory(MyCustomClass::class, function () {
    return new MyCustomClassInputMapper();
});
```

Use `registerOutputFactory()` the same way to customize the output direction. The factory callable receives the concrete class name, the list of generic inner mappers, and the `MapperProvider` instance, so it can delegate to other mappers if needed. A factory may also be registered for an interface or parent class — it then applies to all of its implementations.

### Customizing default mappers inferred from types

[](#customizing-default-mappers-inferred-from-types)

To customize how default mappers are inferred from types, you need to implement `ShipMonk\InputMapper\Compiler\MapperFactory\MapperCompilerFactory` and `MapperCompilerFactoryProvider`.

Then pass your factory provider to the `MapperProvider` — the same provider serves both the input and output directions:

```
$mapperProvider = new ShipMonk\InputMapper\Runtime\MapperProvider(
    tempDir: $tempDir,
    autoRefresh: $autoRefresh,
    mapperCompilerFactoryProvider: new MyCustomMapperCompilerFactoryProvider(),
);
```

Contributing
------------

[](#contributing)

- Check your code by `composer check`
- Autofix coding-style by `composer fix:cs`
- All functionality must be tested

###  Health Score

58

—

FairBetter than 98% of packages

Maintenance97

Actively maintained with recent releases

Popularity39

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 81.8% 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 ~59 days

Recently: every ~49 days

Total

20

Last Release

23d ago

Major Versions

0.9.3 → 2.0.02026-04-09

### Community

Maintainers

![](https://www.gravatar.com/avatar/5b545e3f9d982d538f11bc42b3dc2d186f706cef92c8bc8bc8f8788b08186ea5?d=identicon)[janedbal](/maintainers/janedbal)

![](https://www.gravatar.com/avatar/7a4170ebe9281cb76be91fe00f8eee307beb3e0744dfd40ba89d9c856372c7eb?d=identicon)[shipmonk](/maintainers/shipmonk)

---

Top Contributors

[![JanTvrdik](https://avatars.githubusercontent.com/u/175109?v=4)](https://github.com/JanTvrdik "JanTvrdik (198 commits)")[![janedbal](https://avatars.githubusercontent.com/u/1993453?v=4)](https://github.com/janedbal "janedbal (23 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (17 commits)")[![olsavmic](https://avatars.githubusercontent.com/u/3778875?v=4)](https://github.com/olsavmic "olsavmic (3 commits)")[![repli2dev](https://avatars.githubusercontent.com/u/475549?v=4)](https://github.com/repli2dev "repli2dev (1 commits)")

---

Tags

arrayinputmappermappingobject

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/shipmonk-input-mapper/health.svg)

```
[![Health](https://phpackages.com/badges/shipmonk-input-mapper/health.svg)](https://phpackages.com/packages/shipmonk-input-mapper)
```

###  Alternatives

[rector/rector-src

Instant Upgrade and Automated Refactoring of any PHP code

136406.3k14](/packages/rector-rector-src)[zircote/swagger-php

Generate interactive documentation for your RESTful API using PHP attributes (preferred) or PHPDoc annotations

5.3k144.5M602](/packages/zircote-swagger-php)[dedoc/scramble

Automatic generation of API documentation for Laravel applications.

2.1k11.2M100](/packages/dedoc-scramble)[deptrac/deptrac

Deptrac is a static code analysis tool that helps to enforce rules for dependencies between software layers.

3.0k8.8M118](/packages/deptrac-deptrac)[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M346](/packages/psalm-plugin-laravel)[dereuromark/cakephp-ide-helper

CakePHP IdeHelper Plugin to improve auto-completion

1882.3M44](/packages/dereuromark-cakephp-ide-helper)

PHPackages © 2026

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