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

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

zhalil/mapper
=============

1.0(2mo ago)12PHPPHP ^8.2

Since Feb 26Pushed 2mo agoCompare

[ Source](https://github.com/zhalil/mapper)[ Packagist](https://packagist.org/packages/zhalil/mapper)[ RSS](/packages/zhalil-mapper/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (2)Versions (2)Used By (0)

Zhalil Mapper
=============

[](#zhalil-mapper)

PHP-библиотека для маппинга данных в объекты с автоматической валидацией. Требует PHP 8.2+.

Установка
---------

[](#установка)

```
composer require zhalil/mapper
```

Быстрый старт
-------------

[](#быстрый-старт)

```
use Zhalil\Mapper\Validation\Attribute\{Required, Email, Min};
use function Zhalil\Mapper\map;

class UserDTO
{
    #[Required]
    #[Min(2)]
    public string $name;

    #[Required]
    #[Email]
    public string $email;

    public ?int $age = null;
}

$user = map([
    'name' => 'John',
    'email' => 'john@example.com',
])->to(UserDTO::class);

echo $user->name; // John
```

---

Содержание
----------

[](#содержание)

- [Маппинг](#%D0%BC%D0%B0%D0%BF%D0%BF%D0%B8%D0%BD%D0%B3)
- [Стратегии именования](#%D1%81%D1%82%D1%80%D0%B0%D1%82%D0%B5%D0%B3%D0%B8%D0%B8-%D0%B8%D0%BC%D0%B5%D0%BD%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)
- [Атрибуты маппинга](#%D0%B0%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%D1%8B-%D0%BC%D0%B0%D0%BF%D0%BF%D0%B8%D0%BD%D0%B3%D0%B0)
- [Валидация](#%D0%B2%D0%B0%D0%BB%D0%B8%D0%B4%D0%B0%D1%86%D0%B8%D1%8F)
- [Коллекции](#%D0%BA%D0%BE%D0%BB%D0%BB%D0%B5%D0%BA%D1%86%D0%B8%D0%B8)
- [Сериализация (объект → массив)](#%D1%81%D0%B5%D1%80%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F-%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82--%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2)
- [Обработка ошибок](#%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0-%D0%BE%D1%88%D0%B8%D0%B1%D0%BE%D0%BA)
- [Кастомные кастеры](#%D0%BA%D0%B0%D1%81%D1%82%D0%BE%D0%BC%D0%BD%D1%8B%D0%B5-%D0%BA%D0%B0%D1%81%D1%82%D0%B5%D1%80%D1%8B)

---

Маппинг
-------

[](#маппинг)

### Базовое использование

[](#базовое-использование)

```
use function Zhalil\Mapper\map;

class Product
{
    public string $name;
    public float $price;
    public ?string $description = null;
}

$product = map([
    'name' => 'iPhone',
    'price' => 999.99,
])->to(Product::class);
```

### Вложенные объекты

[](#вложенные-объекты)

Маппер автоматически создаёт вложенные объекты по типу свойства:

```
class Address
{
    public string $city;
    public string $street;
}

class User
{
    public string $name;
    public Address $address;
}

$user = map([
    'name' => 'John',
    'address' => [
        'city' => 'Moscow',
        'street' => 'Tverskaya',
    ],
])->to(User::class);

echo $user->address->city; // Moscow
```

### Массивы объектов

[](#массивы-объектов)

Используйте атрибут `#[ArrayOf]` или PHPDoc `@var ClassName[]`:

```
use Zhalil\Mapper\Attribute\ArrayOf;

class Order
{
    public int $id;

    /** @var Item[] */
    public array $items;
}

class Item
{
    public string $name;
    public int $quantity;
}

$order = map([
    'id' => 1,
    'items' => [
        ['name' => 'Product 1', 'quantity' => 2],
        ['name' => 'Product 2', 'quantity' => 1],
    ],
])->to(Order::class);
```

Или с атрибутом:

```
class Order
{
    public int $id;

    #[ArrayOf(Item::class)]
    public array $items;
}
```

---

Стратегии именования
--------------------

[](#стратегии-именования)

### Доступные стратегии

[](#доступные-стратегии)

СтратегияОписаниеПример`CamelCaseNamingStrategy`По умолчанию, без изменений`firstName` → `firstName``SnakeCaseNamingStrategy`Преобразует в snake\_case`firstName` → `first_name`### Использование на уровне класса

[](#использование-на-уровне-класса)

```
use Zhalil\Mapper\Attribute\MapInputName;
use Zhalil\Mapper\NamingStrategy\SnakeCaseNamingStrategy;

#[MapInputName(SnakeCaseNamingStrategy::class)]
class UserDTO
{
    public string $firstName;
    public string $lastName;
}

$user = map([
    'first_name' => 'John',
    'last_name' => 'Doe',
])->to(UserDTO::class);
```

### Переопределение на уровне свойства

[](#переопределение-на-уровне-свойства)

```
#[MapInputName(SnakeCaseNamingStrategy::class)]
class UserDTO
{
    public string $firstName;

    #[MapInputName(CamelCaseNamingStrategy::class)]
    public string $lastName;
}

$user = map([
    'first_name' => 'John',
    'lastName' => 'Doe',
])->to(UserDTO::class);
```

---

Атрибуты маппинга
-----------------

[](#атрибуты-маппинга)

### \#\[MapFrom\]

[](#mapfrom)

Явное указание ключа во входных данных:

```
use Zhalil\Mapper\Attribute\MapFrom;

class User
{
    #[MapFrom('user_name')]
    public string $name;

    #[MapFrom('user_email')]
    public string $email;
}
```

### \#\[MapTo\]

[](#mapto)

Явное указание ключа при сериализации:

```
use Zhalil\Mapper\Attribute\MapTo;

class User
{
    #[MapTo('user_name')]
    public string $name;
}

$user = map(['name' => 'John'])->to(User::class);
$data = map($user)->toArray();
// ['user_name' => 'John']
```

### \#\[Strict\]

[](#strict)

Строгий режим — выбрасывает исключение при отсутствии обязательного поля:

```
use Zhalil\Mapper\Attribute\Strict;

#[Strict]
class User
{
    public string $name; // Обязательно
    public ?string $email = null; // Опционально (nullable)
}

// Выбросит MappingException, т.к. 'name' отсутствует
map([])->to(User::class);
```

Можно применять к отдельным свойствам:

```
class User
{
    #[Strict]
    public string $name;

    public ?string $email = null;
}
```

### \#\[Hidden\]

[](#hidden)

Скрыть свойство при сериализации:

```
use Zhalil\Mapper\Attribute\Hidden;

class User
{
    public string $name;

    #[Hidden]
    public string $password;
}

$user = map(['name' => 'John', 'password' => 'secret'])->to(User::class);
map($user)->toArray(); // ['name' => 'John'] — password скрыт
```

### \#\[MapOutputName\]

[](#mapoutputname)

Стратегия именования при сериализации (объект → массив):

```
use Zhalil\Mapper\Attribute\MapOutputName;
use Zhalil\Mapper\NamingStrategy\SnakeCaseNamingStrategy;

#[MapOutputName(SnakeCaseNamingStrategy::class)]
class User
{
    public string $firstName;
    public string $lastName;
}

$user = map(['firstName' => 'John'])->to(User::class);
map($user)->toArray(); // ['first_name' => 'John', 'last_name' => null]
```

### \#\[CastWith\]

[](#castwith)

Кастомное преобразование значения:

```
use Zhalil\Mapper\Attribute\CastWith;
use Zhalil\Mapper\Contract\CasterInterface;

class UpperCaseCaster implements CasterInterface
{
    public function cast(mixed $input): mixed
    {
        return strtoupper($input);
    }
}

class User
{
    #[CastWith(UpperCaseCaster::class)]
    public string $name;
}

$user = map(['name' => 'john'])->to(User::class);
echo $user->name; // JOHN
```

---

Валидация
---------

[](#валидация)

### Правила валидации

[](#правила-валидации)

АтрибутПараметрыОписание`#[Required]`—Поле обязательно`#[Nullable]`—Поле может быть null`#[Email]`—Валидный email`#[Url]`—Валидный URL`#[Min(int|float)]`$minМинимальное значение/длина`#[Max(int|float)]`$maxМаксимальное значение/длина`#[Between(int, int)]`$min, $maxДиапазон значений`#[Size(int)]`$sizeТочное значение/длина`#[In(...$values)]`$valuesЗначение должно быть в списке`#[NotIn(...$values)]`$valuesЗначение не должно быть в списке`#[Regex(string)]`$patternСоответствие регулярному выражению`#[Enum(string)]`$enumClassЗначение из enum`#[Confirmed]`—Подтверждение (field + field\_confirmation)`#[StringType]`—Строка`#[IntegerType]`—Целое число`#[Numeric]`—Число (int или float)`#[BooleanType]`—Булево значение`#[ArrayType]`—Массив`#[Date]`—Валидная дата`#[DateFormat(string)]`$formatДата в формате`#[After(string)]`$dateДата после указанной`#[Before(string)]`$dateДата до указанной`#[AfterOrEqual(string)]`$dateДата после или равна`#[BeforeOrEqual(string)]`$dateДата до или равна`#[DateEquals(string)]`$dateДата равна`#[Uuid]`—Валидный UUID`#[Ip]`—Валидный IP`#[Ipv4]`—Валидный IPv4`#[Ipv6]`—Валидный IPv6`#[MacAddress]`—Валидный MAC-адрес`#[Ulid]`—Валидный ULID`#[Timezone]`—Валидный часовой пояс`#[Json]`—Валидный JSON`#[Alpha]`—Только буквы`#[AlphaNumeric]`—Буквы и цифры`#[AlphaDash]`—Буквы, цифры, дефис, подчёркивание`#[Uppercase]`—Верхний регистр`#[Lowercase]`—Нижний регистр`#[StartsWith(string)]`$prefixНачинается с`#[EndsWith(string)]`$suffixЗаканчивается на`#[DoesntStartWith(string)]`$prefixНе начинается с`#[DoesntEndWith(string)]`$suffixНе заканчивается на`#[Digits(int)]`$countТочное количество цифр`#[DigitsBetween(int, int)]`$min, $maxДиапазон цифр`#[Same(string)]`$fieldРавно другому полю`#[Different(string)]`$fieldОтлично от другого поля`#[GreaterThan(string)]`$fieldБольше другого поля`#[GreaterThanOrEqualTo(string)]`$fieldБольше или равно`#[LessThan(string)]`$fieldМеньше другого поля`#[LessThanOrEqualTo(string)]`$fieldМеньше или равно`#[MultipleOf(int)]`$valueКратно числу`#[Prohibited]`—Запрещено`#[ProhibitedIf(string, ...$values)]`$field, $valuesЗапрещено, если поле = значение`#[ProhibitedUnless(string, ...$values)]`$field, $valuesЗапрещено, если поле ≠ значение### Password

[](#password)

```
use Zhalil\Mapper\Validation\Attribute\Password;

class UserDTO
{
    #[Password(
        min: 8,          // Минимум 8 символов
        letters: true,   // Хотя бы одна буква
        mixedCase: true, // Верхний и нижний регистр
        numbers: true,   // Хотя бы одна цифра
        symbols: true    // Хотя бы один символ
    )]
    public string $password;
}
```

### Условные правила

[](#условные-правила)

```
use Zhalil\Mapper\Validation\Attribute\Required;
use Zhalil\Mapper\Validation\Rule\{RequiredIf, RequiredWith, RequiredWithout};

class PaymentDTO
{
    public ?string $paymentMethod = null;

    #[Required]
    public string $cardNumber;
}

// Или через атрибуты условий (требуют реализации атрибутов):
// #[RequiredIf('paymentMethod', 'card')]
// #[RequiredWith('otherField')]
// #[RequiredWithout('otherField')]
```

### Примеры валидации

[](#примеры-валидации)

```
use Zhalil\Mapper\Validation\Attribute\{Required, Email, Min, Max, Between, In, Regex};

class CreateUserDTO
{
    #[Required]
    #[Min(2)]
    #[Max(50)]
    public string $name;

    #[Required]
    #[Email]
    public string $email;

    #[Between(18, 100)]
    public int $age;

    #[In('admin', 'user', 'guest')]
    public string $role;

    #[Regex('/^[A-Z]{2}\d{6}$/')]
    public ?string $passportNumber = null;
}
```

---

Коллекции
---------

[](#коллекции)

Для маппинга массива объектов используйте `collection()`:

```
class User
{
    public string $name;
    public string $email;
}

$users = map([
    ['name' => 'John', 'email' => 'john@example.com'],
    ['name' => 'Jane', 'email' => 'jane@example.com'],
])->collection()->to(User::class);

// $users — массив объектов User
```

---

Сериализация (объект → массив)
------------------------------

[](#сериализация-объект--массив)

Маппер поддерживает преобразование объектов обратно в массивы с учётом атрибутов.

### Базовое использование

[](#базовое-использование-1)

```
class User
{
    public string $name;
    public string $email;
}

$user = map(['name' => 'John', 'email' => 'john@example.com'])->to(User::class);
$data = map($user)->toArray();
// ['name' => 'John', 'email' => 'john@example.com']
```

### toJson()

[](#tojson)

```
$json = map($user)->toJson();
```

### Атрибуты сериализации

[](#атрибуты-сериализации)

#### \#\[MapTo\] — переименование ключа

[](#mapto--переименование-ключа)

```
use Zhalil\Mapper\Attribute\MapTo;

class User
{
    #[MapTo('user_name')]
    public string $name;
}

$user = map(['name' => 'John'])->to(User::class);
$data = map($user)->toArray();
// ['user_name' => 'John']
```

#### \#\[Hidden\] — скрыть свойство

[](#hidden--скрыть-свойство)

```
use Zhalil\Mapper\Attribute\Hidden;

class User
{
    public string $name;

    #[Hidden]
    public string $password;
}

$user = map(['name' => 'John', 'password' => 'secret'])->to(User::class);
$data = map($user)->toArray();
// ['name' => 'John'] — password скрыт
```

#### \#\[MapOutputName\] — стратегия именования

[](#mapoutputname--стратегия-именования)

Преобразует имена свойств при сериализации:

```
use Zhalil\Mapper\Attribute\MapOutputName;
use Zhalil\Mapper\NamingStrategy\SnakeCaseNamingStrategy;

#[MapOutputName(SnakeCaseNamingStrategy::class)]
class User
{
    public string $firstName;
    public string $lastName;
}

$user = map(['firstName' => 'John', 'lastName' => 'Doe'])->to(User::class);
$data = map($user)->toArray();
// ['first_name' => 'John', 'last_name' => 'Doe']
```

Переопределение на уровне свойства:

```
#[MapOutputName(SnakeCaseNamingStrategy::class)]
class User
{
    public string $firstName;

    #[MapOutputName(CamelCaseNamingStrategy::class)]
    public string $lastName;
}

$user = map(['firstName' => 'John', 'lastName' => 'Doe'])->to(User::class);
$data = map($user)->toArray();
// ['first_name' => 'John', 'lastName' => 'Doe']
```

### Автоматическая сериализация типов

[](#автоматическая-сериализация-типов)

ТипРезультат`BackedEnum``$enum->value``UnitEnum``$enum->name``DateTimeInterface`ISO 8601 форматВложенный объектРекурсивный toArray()Массив объектовРекурсивная обработка```
enum Status: string
{
    case Active = 'active';
}

class Address
{
    public string $city;
}

class User
{
    public string $name;
    public Status $status;
    public \DateTime $createdAt;
    public Address $address;
}

$user = map([
    'name' => 'John',
    'status' => 'active',
    'createdAt' => '2024-01-01',
    'address' => ['city' => 'Moscow'],
])->to(User::class);

$data = map($user)->toArray();
// [
//     'name' => 'John',
//     'status' => 'active',
//     'createdAt' => '2024-01-01T00:00:00+00:00',
//     'address' => ['city' => 'Moscow']
// ]
```

### Сериализация коллекций

[](#сериализация-коллекций)

```
$users = [/* массив объектов User */];
$data = map($users)->collection()->toArray();
// Массив массивов
```

---

Обработка ошибок
----------------

[](#обработка-ошибок)

### MappingException

[](#mappingexception)

Выбрасывается при ошибках маппинга:

```
use Zhalil\Mapper\Exception\MappingException;

try {
    $user = map([])->to(User::class);
} catch (MappingException $e) {
    echo $e->getMessage();
    // "Failed to map data to User. Missing required fields: ['name', 'email']"

    $missing = $e->getMissingFields(); // ['name', 'email']
    $provided = $e->getProvidedData(); // []
}
```

### ValidationException

[](#validationexception)

Выбрасывается при ошибках валидации:

```
use Zhalil\Mapper\Exception\ValidationException;

try {
    $user = map([
        'name' => 'J',
        'email' => 'invalid',
    ])->to(UserDTO::class);
} catch (ValidationException $e) {
    $errors = $e->errors(); // ErrorBag

    $errors->all();      // ['name' => ['...'], 'email' => ['...']]
    $errors->has('email'); // true
    $errors->get('email'); // ['This value is not a valid email address.']
    $errors->isEmpty();    // false
    $errors->count();      // 2
    $errors->toJson();     // JSON-представление
}
```

---

Кастомные кастеры
-----------------

[](#кастомные-кастеры)

Создайте класс, реализующий `CasterInterface`:

```
use Zhalil\Mapper\Contract\CasterInterface;

class DateTimeCaster implements CasterInterface
{
    public function cast(mixed $input): mixed
    {
        if ($input instanceof \DateTimeInterface) {
            return $input;
        }

        return new \DateTime($input);
    }
}
```

Использование:

```
use Zhalil\Mapper\Attribute\CastWith;

class Event
{
    public string $title;

    #[CastWith(DateTimeCaster::class)]
    public \DateTime $startsAt;
}

$event = map([
    'title' => 'Conference',
    'startsAt' => '2024-06-15 10:00:00',
])->to(Event::class);

echo $event->startsAt->format('Y-m-d'); // 2024-06-15
```

---

Полный пример
-------------

[](#полный-пример)

```
use Zhalil\Mapper\Attribute\{MapInputName, MapOutputName, MapFrom, Hidden, Strict, ArrayOf};
use Zhalil\Mapper\NamingStrategy\SnakeCaseNamingStrategy;
use Zhalil\Mapper\Validation\Attribute\{Required, Email, Min, Max, Password};
use function Zhalil\Mapper\map;

class Address
{
    public string $city;
    public string $street;
}

#[MapInputName(SnakeCaseNamingStrategy::class)]
#[MapOutputName(SnakeCaseNamingStrategy::class)]
#[Strict]
class CreateUserDTO
{
    #[Required]
    #[Min(2)]
    #[Max(50)]
    public string $firstName;

    #[Required]
    #[Min(2)]
    #[Max(50)]
    public string $lastName;

    #[Required]
    #[Email]
    public string $email;

    #[Password(min: 8, letters: true, numbers: true)]
    public string $password;

    #[Hidden]
    public string $passwordHash;

    #[ArrayOf(Address::class)]
    public array $addresses = [];

    public function __construct()
    {
        $this->passwordHash = '';
    }
}

try {
    $dto = map([
        'first_name' => 'John',
        'last_name' => 'Doe',
        'email' => 'john@example.com',
        'password' => 'Secure123',
        'addresses' => [
            ['city' => 'Moscow', 'street' => 'Tverskaya'],
        ],
    ])->to(CreateUserDTO::class);

    echo $dto->firstName; // John
    echo $dto->addresses[0]->city; // Moscow

    $data = map($dto)->toArray();
    // passwordHash скрыт, ключи в snake_case:
    // ['first_name' => 'John', 'last_name' => 'Doe', 'email' => '...', ...]

} catch (\Zhalil\Mapper\Exception\ValidationException $e) {
    foreach ($e->errors()->all() as $field => $messages) {
        foreach ($messages as $message) {
            echo "$field: $message\n";
        }
    }
} catch (\Zhalil\Mapper\Exception\MappingException $e) {
    echo "Mapping error: " . $e->getMessage();
}
```

---

Тестирование
------------

[](#тестирование)

```
composer test
```

---

Лицензия
--------

[](#лицензия)

MIT

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance84

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

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

Unknown

Total

1

Last Release

82d ago

### Community

Maintainers

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

---

Top Contributors

[![zhalil](https://avatars.githubusercontent.com/u/43891851?v=4)](https://github.com/zhalil "zhalil (6 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

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

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

PHPackages © 2026

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