{"id":28391426,"url":"https://github.com/shipmonk-rnd/input-mapper","last_synced_at":"2026-04-09T16:19:59.082Z","repository":{"id":156209979,"uuid":"628877796","full_name":"shipmonk-rnd/input-mapper","owner":"shipmonk-rnd","description":"Performant array-to-object mapper supporting generics, array shapes, optional fields and much more!","archived":false,"fork":false,"pushed_at":"2025-05-14T13:34:47.000Z","size":443,"stargazers_count":27,"open_issues_count":6,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-31T19:34:52.478Z","etag":null,"topics":["array","input","mapper","mapping","object"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shipmonk-rnd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-04-17T07:03:52.000Z","updated_at":"2025-05-14T13:34:51.000Z","dependencies_parsed_at":"2024-02-15T21:26:19.753Z","dependency_job_id":"d6a126b4-e750-4a37-8b63-b919e3de2f0b","html_url":"https://github.com/shipmonk-rnd/input-mapper","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/shipmonk-rnd/input-mapper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipmonk-rnd%2Finput-mapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipmonk-rnd%2Finput-mapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipmonk-rnd%2Finput-mapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipmonk-rnd%2Finput-mapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shipmonk-rnd","download_url":"https://codeload.github.com/shipmonk-rnd/input-mapper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shipmonk-rnd%2Finput-mapper/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261955882,"owners_count":23235970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array","input","mapper","mapping","object"],"created_at":"2025-05-31T09:12:51.739Z","updated_at":"2026-04-09T16:19:59.077Z","avatar_url":"https://github.com/shipmonk-rnd.png","language":"PHP","readme":"# ShipMonk Input Mapper\n\nBidirectional 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).\n\n\n## Installation:\n\n```sh\ncomposer require shipmonk/input-mapper\n```\n\n\n## Features\n\n### Built-in mappers\n\nInput Mapper comes with built-in mappers for the following types:\n\n* `array`, `bool`, `float`, `int`, `mixed`, `string`, `list`\n* `positive-int`, `negative-int`, `int\u003cTMin, TMax\u003e`, `non-empty-string`, `non-empty-list`\n* `array\u003cV\u003e`, `array\u003cK, V\u003e`, `list\u003cV\u003e`, `non-empty-list\u003cV\u003e`\n* `array{K1: V1, ...}`\n* `?T`, `Optional\u003cT\u003e`\n* `DateTimeInterface`, `DateTimeImmutable`\n* `BackedEnum`\n* and most importantly classes with public constructor\n\nAll built-in mappers support both input (array → object) and output (object → array) directions.\n\nYou can write your own mappers or replace the default mappers with your own.\n\n### Built-in validators\n\nInput Mapper comes with some built-in validators (input mapping only):\n\n* int validators:\n  * `AssertInt16`\n  * `AssertInt32`\n  * `AssertIntRange`\n  * `AssertPositiveInt`\n  * `AssertNegativeInt`\n  * `AssertNonNegativeInt`\n  * `AssertNonPositiveInt`\n  * `AssertIntMultipleOf`\n* float validators:\n  * `AssertFloatRange`\n  * `AssertPositiveFloat`\n  * `AssertNegativeFloat`\n  * `AssertNonNegativeFloat`\n  * `AssertNonPositiveFloat`\n  * `AssertFloatMultipleOf`\n* string validators:\n  * `AssertStringLength`\n  * `AssertStringMatches`\n  * `AssertStringNonEmpty`\n  * `AssertUrl`\n* list validators:\n  * `AssertListItem`\n  * `AssertListLength`\n  * `AssertUniqueItems` (compares items by `===`)\n* date time validators:\n  * `AssertDateTimeRange`\n\nYou can write your own validators if you need more.\n\n## Usage:\n\n### Write Input Class\n\nTo use Input Mapper, write a class with a public constructor and add either native or PHPDoc types to all constructor parameters.\n\nOptional fields can either be marked with `#[Optional]` attribute (allowing you to specify a default value),\nor if you need to distinguish between default and missing values, you can wrap the type with `ShipMonk\\InputMapper\\Runtime\\Optional` class.\n\n```php\nuse ShipMonk\\InputMapper\\Compiler\\Mapper\\Optional;\n\nclass Person\n{\n    public function __construct(\n        public readonly string $name,\n\n        public readonly int $age,\n\n        #[Optional]\n        public readonly ?string $email,\n\n        /** @var list\u003cstring\u003e */\n        public readonly array $hobbies,\n\n        /** @var list\u003cself\u003e */\n        #[Optional(default: [])]\n        public readonly array $friends,\n    ) {}\n}\n```\n\nBy default, any extra properties are not allowed. You can change that by adding `#[AllowExtraKeys]` over the class.\n\n### Map Input\n\nTo map input data (e.g. JSON) to objects, use `MapperProvider`:\n\n```php\n$tempDir = sys_get_temp_dir() . '/input-mapper';\n$autoRefresh = true; // MUST be set to false in production\n$mapperProvider = new ShipMonk\\InputMapper\\Runtime\\MapperProvider($tempDir, $autoRefresh);\n$mapper = $mapperProvider-\u003egetInputMapper(Person::class);\n\ntry {\n    $person = $mapper-\u003emap([\n        'name' =\u003e 'John',\n        'age' =\u003e 30,\n        'hobbies' =\u003e ['hiking', 'reading'],\n        'friends' =\u003e [\n            [\n                'name' =\u003e 'Jane',\n                'age' =\u003e 28,\n                'hobbies' =\u003e ['hiking', 'reading'],\n            ],\n            [\n                'name' =\u003e 'Jack',\n                'age' =\u003e 28,\n                'hobbies' =\u003e ['hiking', 'reading'],\n            ],\n        ],\n    ]);\n} catch (\\ShipMonk\\InputMapper\\Runtime\\Exception\\MappingFailedException $e) {\n    // $e-\u003egetMessage() // programmer readable error message in English\n    // $e-\u003egetPath() // path of the problematic field for example ['friends', 0, 'name']\n    // ...\n}\n```\n\n### Map Output\n\nTo convert objects back to plain arrays (e.g. for JSON serialization), use the same `MapperProvider`:\n\n```php\n$mapper = $mapperProvider-\u003egetOutputMapper(Person::class);\n\n$data = $mapper-\u003emap($person);\n// ['name' =\u003e 'John', 'age' =\u003e 30, 'email' =\u003e null, 'hobbies' =\u003e ['hiking', 'reading'], 'friends' =\u003e [...]]\n```\n\nThe 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.\n\n### Adding Validation Rules\n\nYou can add validation rules by adding attributes to constructor parameters.\n\nFor example, to validate that `age` is between 18 and 99, you can add the `AssertIntRange` attribute to the constructor parameter:\n\n```php\nuse ShipMonk\\InputMapper\\Compiler\\Validator\\Int\\AssertIntRange;\n\nclass Person\n{\n    public function __construct(\n        public readonly string $name,\n\n        #[AssertIntRange(gte: 18, lte: 99)]\n        public readonly int $age,\n    ) {}\n}\n```\n\n### Renaming keys\n\nIf the input keys do not match the property names, you can use the `#[SourceKey]` attribute to specify the key name:\n\n```php\nuse ShipMonk\\InputMapper\\Compiler\\Mapper\\Object\\SourceKey;\n\nclass Person\n{\n    public function __construct(\n        #[SourceKey('full_name')]\n        public readonly string $name,\n    ) {}\n}\n```\n\n### Parsing polymorphic classes (subtypes with a common parent)\n\nIf you need to parse a hierarchy of classes, you can use the `#[Discriminator]` attribute.\n(The discriminator field does not need to be mapped to a property if `#[AllowExtraKeys]` is used.)\n\n```php\nuse ShipMonk\\InputMapper\\Compiler\\Mapper\\Object\\Discriminator;\n\n#[Discriminator(\n    key: 'type', // key to use for mapping\n    mapping: [\n        'car' =\u003e Car::class,\n        'truck' =\u003e Truck::class,\n    ]\n)]\nabstract class Vehicle {\n    public function __construct(\n        public readonly string $type,\n    ) {}\n}\n\nclass Car extends Vehicle {\n\n    public function __construct(\n        string $type,\n        public readonly string $color,\n    ) {\n        parent::__construct($type);\n    }\n\n}\n\nclass Truck extends Vehicle {\n\n    public function __construct(\n        string $type,\n        public readonly string $color,\n    ) {\n        parent::__construct($type);\n    }\n\n}\n```\n\nor, with enum:\n\n```php\nuse ShipMonk\\InputMapper\\Compiler\\Mapper\\Object\\Discriminator;\n\nenum VehicleType: string {\n    case Car = 'car';\n    case Truck = 'truck';\n}\n\n#[Discriminator(\n    key: 'type', // key to use for mapping\n    mapping: [\n        VehicleType::Car-\u003evalue =\u003e Car::class,\n        VehicleType::Truck-\u003evalue =\u003e Truck::class,\n    ]\n)]\nabstract class Vehicle {\n    public function __construct(\n        VehicleType $type,\n    ) {}\n}\n\nclass Car extends Vehicle {\n\n    public function __construct(\n        VehicleType $type,\n        public readonly string $color,\n    ) {\n        parent::__construct($type);\n    }\n\n}\n\nclass Truck extends Vehicle {\n\n    public function __construct(\n        VehicleType $type,\n        public readonly string $color,\n    ) {\n        parent::__construct($type);\n    }\n\n}\n```\n\n### Using custom mappers\n\nTo map classes with your custom mapper, you need to implement the `InputMapper` or `OutputMapper` interface and register it with the corresponding provider:\n\n```php\nclass MyCustomInputMapper implements ShipMonk\\InputMapper\\Runtime\\InputMapper\n{\n    public function map(mixed $data, array $path = []): mixed\n    {\n        return MyCustomClass::createFrom($data);\n    }\n}\n\n$inputMapperProvider-\u003eregisterFactory(MyCustomClass::class, function () {\n    return new MyCustomInputMapper();\n});\n```\n\n### Customizing default mappers inferred from types\n\nTo customize how default mappers are inferred from types, you need to implement `ShipMonk\\InputMapper\\Compiler\\MapperFactory\\MapperCompilerFactory` and `MapperCompilerFactoryProvider`.\n\nThen register your factory provider with the corresponding provider:\n\n```php\n$mapperCompilerFactoryProvider = new MyCustomMapperCompilerFactoryProvider();\n\n$inputMapperProvider = new ShipMonk\\InputMapper\\Runtime\\InputMapperProvider(\n    tempDir: $tempDir,\n    autoRefresh: $autoRefresh,\n    mapperCompilerFactoryProvider: $mapperCompilerFactoryProvider,\n);\n\n$outputMapperProvider = new ShipMonk\\InputMapper\\Runtime\\OutputMapperProvider(\n    tempDir: $tempDir,\n    autoRefresh: $autoRefresh,\n    mapperCompilerFactoryProvider: $mapperCompilerFactoryProvider,\n);\n```\n\n\n## Contributing\n- Check your code by `composer check`\n- Autofix coding-style by `composer fix:cs`\n- All functionality must be tested\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshipmonk-rnd%2Finput-mapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshipmonk-rnd%2Finput-mapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshipmonk-rnd%2Finput-mapper/lists"}