squidit/array-to-object
Composer 安装命令:
composer require squidit/array-to-object
包简介
Hydrate array to object using typed object properties
README 文档
README
Hydrate typed objects from associative arrays, dotted arrays, or DTO-like source objects by mapping input keys or property names to corresponding typed target properties. The library also includes a helper base class for DTO-to-JSON output.
The supplied keys or source-object property names must match the names of the target object properties.
Installation
v3.* (PHP 8.4+)
composer require squidit/array-to-object:^3.0
v2.* (PHP 8.2 / 8.3)
composer require squidit/array-to-object:^2.0
Usage - example (multi dimensional array):
<?php declare(strict_types=1); use SquidIT\Hydrator\ArrayToObject; use SquidIT\Hydrator\Class\ClassInfoGenerator; use SquidIT\Hydrator\Tests\Unit\ExampleObjects\Car\Complete\CarComplete; $classInfoGenerator = new ClassInfoGenerator(); $hydrator = new ArrayToObject($classInfoGenerator); $data = [ 'color' => 'black', 'nrOfDoors' => 4, 'mileagePerLiter' => 16.3, 'passengerList' => ['melvin', 'bert'], 'manufacturer' => [ 'addressLine1' => 'Beautiful Street 123', 'addressLine2' => 'Apartment 1234', 'city' => 'Rotterdam', 'employeeList' => [ ['employeeName' => 'cecil'], ['employeeName' => 'melvin'], ], ], 'interCoolers' => [ [ 'speedRangeMinRpm' => 200, 'speedRangeMaxRpm' => 2160, 'isWaterCooled' => true, 'speedCategory' => 'fast', ], [ 'speedRangeMinRpm' => 100, 'speedRangeMaxRpm' => 2200, 'isWaterCooled' => false, 'speedCategory' => 'slow', ], ], 'countryEntryDate' => '2015-06-01 13:45:01', 'extraInfo' => null, ]; $carComplete = $hydrator->hydrate($data, CarComplete::class); var_dump($carComplete);
Usage - example (array dot notation):
<?php declare(strict_types=1); use SquidIT\Hydrator\Class\ClassInfoGenerator; use SquidIT\Hydrator\DotNotationArrayToObject; use SquidIT\Hydrator\Property\DotNotationFormat; use SquidIT\Hydrator\Tests\Unit\ExampleObjects\Car\Complete\CarComplete; $classInfoGenerator = new ClassInfoGenerator(); $hydratorJavaScript = new DotNotationArrayToObject($classInfoGenerator, DotNotationFormat::JAVASCRIPT); $hydratorExplode = new DotNotationArrayToObject($classInfoGenerator, DotNotationFormat::EXPLODE); $dataDotNotationJavascript = [ 'color' => 'black', 'nrOfDoors' => 4, 'mileagePerLiter' => 16.3, 'passengerList' => ['melvin', 'bert'], 'manufacturer.addressLine1' => 'Beautiful Street 123', 'manufacturer.addressLine2' => 'Apartment 1234', 'manufacturer.city' => 'Rotterdam', 'manufacturer.employeeList[0].employeeName' => 'cecil', 'manufacturer.employeeList[1].employeeName' => 'melvin', 'interCoolers[0].speedRangeMinRpm' => 200, 'interCoolers[0].speedRangeMaxRpm' => 2160, 'interCoolers[0].isWaterCooled' => true, 'interCoolers[0].speedCategory' => 'fast', 'interCoolers[1].speedRangeMinRpm' => 100, 'interCoolers[1].speedRangeMaxRpm' => 2200, 'interCoolers[1].isWaterCooled' => false, 'interCoolers[1].speedCategory' => 'slow', 'countryEntryDate' => '2015-06-01 13:45:01', 'extraInfo' => null, ]; $dataDotNotationExplode = [ 'color' => 'black', 'nrOfDoors' => 4, 'mileagePerLiter' => 16.3, 'passengerList' => ['melvin', 'bert'], 'manufacturer.addressLine1' => 'Beautiful Street 123', 'manufacturer.addressLine2' => 'Apartment 1234', 'manufacturer.city' => 'Rotterdam', 'manufacturer.employeeList.0.employeeName' => 'cecil', 'manufacturer.employeeList.1.employeeName' => 'melvin', 'interCoolers.0.speedRangeMinRpm' => 200, 'interCoolers.0.speedRangeMaxRpm' => 2160, 'interCoolers.0.isWaterCooled' => true, 'interCoolers.0.speedCategory' => 'fast', 'interCoolers.1.speedRangeMinRpm' => 100, 'interCoolers.1.speedRangeMaxRpm' => 2200, 'interCoolers.1.isWaterCooled' => false, 'interCoolers.1.speedCategory' => 'slow', 'countryEntryDate' => '2015-06-01 13:45:01', 'extraInfo' => null, ]; $carComplete = $hydratorJavaScript->hydrate($dataDotNotationJavascript, CarComplete::class); $carComplete = $hydratorExplode->hydrate($dataDotNotationExplode, CarComplete::class); var_dump($carComplete);
output
object(SquidIT\Hydrator\Tests\Unit\ExampleObjects\Car\Complete\CarComplete)#6 (9) {
["color"]=>
string(5) "black"
["nrOfDoors"]=>
int(4)
["mileagePerLiter"]=>
float(16.3)
["passengerList"]=>
array(2) {
[0]=>
string(6) "melvin"
[1]=>
string(4) "bert"
}
["manufacturer"]=>
object(SquidIT\Hydrator\Tests\Unit\ExampleObjects\Manufacturer\Honda)#21 (4) {
["addressLine1"]=>
string(20) "Beautiful Street 123"
["addressLine2"]=>
string(14) "Apartment 1234"
["city"]=>
string(9) "Rotterdam"
["employeeList"]=>
array(2) {
[0]=>
object(SquidIT\Hydrator\Tests\Unit\ExampleObjects\Manufacturer\Employee)#24 (1) {
["employeeName"]=>
string(5) "cecil"
}
[1]=>
object(SquidIT\Hydrator\Tests\Unit\ExampleObjects\Manufacturer\Employee)#10 (1) {
["employeeName"]=>
string(6) "melvin"
}
}
}
["interCoolers"]=>
array(2) {
[0]=>
object(SquidIT\Hydrator\Tests\Unit\ExampleObjects\Car\Parts\InterCooler)#14 (4) {
["speedRangeMinRpm"]=>
int(200)
["speedRangeMaxRpm"]=>
int(2160)
["isWaterCooled"]=>
bool(true)
["speedCategory"]=>
enum(SquidIT\Hydrator\Tests\Unit\ExampleObjects\Car\Speed::FAST)
}
[1]=>
object(SquidIT\Hydrator\Tests\Unit\ExampleObjects\Car\Parts\InterCooler)#44 (4) {
["speedRangeMinRpm"]=>
int(100)
["speedRangeMaxRpm"]=>
int(2200)
["isWaterCooled"]=>
bool(false)
["speedCategory"]=>
enum(SquidIT\Hydrator\Tests\Unit\ExampleObjects\Car\Speed::SLOW)
}
}
["countryEntryDate"]=>
object(DateTimeImmutable)#39 (3) {
["date"]=>
string(26) "2015-06-01 13:45:01.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}
["extraInfo"]=>
NULL
["isInsured"]=>
bool(true) <--- object default property and was not present in our data array
}
If you only need to convert dotted keys into a nested array, use DotNotationToMultiDimensional directly:
use SquidIT\Hydrator\DotNotationToMultiDimensional; $dotNotationToMultiDimensional = new DotNotationToMultiDimensional($dataDotNotationJavascript); $nestedData = $dotNotationToMultiDimensional->convert();
Pass DotNotationFormat::EXPLODE as the second constructor argument when using explode-style dotted keys.
Usage - example (DTO/object input):
When the source data already arrives as an object, use DtoToObject. The source-object property names must match the
target-object property names. Nested hydration and type casting work the same way as with array input.
<?php declare(strict_types=1); use SquidIT\Hydrator\Class\ClassInfoGenerator; use SquidIT\Hydrator\DtoToObject; final class CarInputDto { public function __construct( public string $color, public int $nrOfDoors, ) {} } final class CarSummary { public function __construct( public string $color, public int $nrOfDoors, ) {} } $sourceDto = new CarInputDto('black', 4); $hydrator = new DtoToObject(new ClassInfoGenerator()); $carSummary = $hydrator->hydrate($sourceDto, CarSummary::class);
Hydrating multiple objects
ArrayToObject, DotNotationArrayToObject, and DtoToObject all provide hydrateMulti() for indexed lists of
source records:
$arrayHydrator = new ArrayToObject(new ClassInfoGenerator()); $dtoHydrator = new DtoToObject(new ClassInfoGenerator()); $carCompleteList = $arrayHydrator->hydrateMulti([$data, $data], CarComplete::class); $carSummaryList = $dtoHydrator->hydrateMulti([$sourceDto, $sourceDto], CarSummary::class);
Nested objects
If a class property contains a nested object, the hydrator can infer the object type by reading the property type.
In the example below, the Car::class contains a named property manufacturer which is of type Honda::class.
When hydrating, we need to provide all data required to create a Honda object.
class Car { public function __construct( public string $color, public Honda $manufacturer, ) {} } $data = [ 'color' => 'black', 'manufacturer' => [ // <-- Honda::class 'name' => 'Beautiful Street 124', 'city' => 'Rotterdam', 'employeeList' => [] ], ];
Nested objects: Array of objects
If a class property contains an array of objects, we need to add a property attribute:
SquidIT\Hydrator\Attributes\ArrayOf([CLASSNAME]).
In the example below, the Honda::class contains a property employeeList which should contain an array of
Employee::class objects.
By adding the property attribute SquidIT\Hydrator\Attributes\ArrayOf(Employee::class) our hydrator knows how to hydrate
array data found under the 'employeeList' object property.
use SquidIT\Hydrator\Attributes\ArrayOf; class Honda implements ManufacturerInterface { /** * @param array<int, Employee> $employeeList */ public function __construct( public string $name, public string $city, #[ArrayOf(Employee::class)] public array $employeeList, ) {} }
Default values
When input omits a property, the hydrator uses the target object's default value when one exists. Defaults can come from constructor-promoted properties or regular declared properties.
final class Car { public function __construct( public string $color, public bool $isInsured = true, ) {} }
Object defaults are cloned during hydration, so each hydrated object receives its own default object instance instead of sharing mutable state with other hydrated objects.
Hydration lifecycle
Target objects are created without executing the constructor body, then their typed properties are assigned directly.
This means reflected default values are honored, but constructor side effects and constructor-only validation logic do
not run during hydration. Use ObjectValidatorInterface for checks that must happen after hydration.
Object validation
If an object needs validation after hydration, implement SquidIT\Hydrator\Interface\ObjectValidatorInterface.
The hydrator calls validate() after all properties have been hydrated. When validation fails, throw a
\SquidIT\Hydrator\Exceptions\ValidationFailureException. The supplied PathTracker can be used to include the
property path in the exception message, including nested object and array positions.
use SquidIT\Hydrator\Exceptions\ValidationFailureException; use SquidIT\Hydrator\Interface\ObjectValidatorInterface; use SquidIT\Hydrator\Property\PathTracker; class CarWithCustomEngine implements ObjectValidatorInterface { public function __construct( public int $engineDisplacementInCc, ) {} /** * @throws ValidationFailureException */ public function validate(PathTracker $pathTracker): void { if ($this->engineDisplacementInCc < 600 || $this->engineDisplacementInCc > 8000) { $propertyPath = $pathTracker->getPath('engineDisplacementInCc'); throw new ValidationFailureException( sprintf('Invalid value received for property: %s, value needs to be between 600 and 8000', $propertyPath) ); } } }
User-safe error messages
By default, hydration errors include implementation detail that is useful for developers. If the message may reach an end user, enable safe error messages when creating the hydrator:
use SquidIT\Hydrator\ArrayToObject; use SquidIT\Hydrator\Class\ClassInfoGenerator; use SquidIT\Hydrator\DotNotationArrayToObject; use SquidIT\Hydrator\DtoToObject; use SquidIT\Hydrator\Property\DotNotationFormat; $arrayHydrator = new ArrayToObject(new ClassInfoGenerator(), true); $dtoHydrator = new DtoToObject(new ClassInfoGenerator(), true); $dotNotationHydrator = new DotNotationArrayToObject( new ClassInfoGenerator(), DotNotationFormat::JAVASCRIPT, true, );
With safe messages enabled, a missing nested value is reported using a user-facing property path, for example:
Path: manufacturer.employeeList[0].employeeName - no data supplied for required property.
DTO to JSON output
When a DTO needs predictable JSON output, extend SquidIT\Hydrator\Abstract\AbstractObjectToDto.
If the DTO class must be declared readonly, extend
SquidIT\Hydrator\Abstract\AbstractReadOnlyObjectToDto instead. PHP only allows a readonly class to extend another
readonly class, and both abstract classes provide the same toArray() and jsonSerialize() behavior.
These abstract classes are specifically intended to help DTOs prepare JSON-friendly output through PHP's
JsonSerializable flow.
Public and protected properties are included automatically. DateTimeImmutable values are formatted as
Y-m-d\TH:i:s.u, backed enums are converted to their scalar values, and private properties are excluded.
Use toArray() when you need a deep nested array without embedded DTO objects; jsonSerialize() delegates
to the same conversion.
<?php declare(strict_types=1); use DateTimeImmutable; use SquidIT\Hydrator\Abstract\AbstractObjectToDto; final class CarDto extends AbstractObjectToDto { public function __construct( public string $color, public DateTimeImmutable $createdAt, ) {} } $carDto = new CarDto('black', new DateTimeImmutable('2026-05-18 12:34:56.123456')); echo json_encode($carDto, JSON_THROW_ON_ERROR); // {"color":"black","createdAt":"2026-05-18T12:34:56.123456"} $carDto->toArray(); // ['color' => 'black', 'createdAt' => '2026-05-18T12:34:56.123456']
Readonly DTOs use the readonly base class:
use DateTimeImmutable; use SquidIT\Hydrator\Abstract\AbstractReadOnlyObjectToDto; final readonly class ReadOnlyCarDto extends AbstractReadOnlyObjectToDto { public function __construct( public string $color, public DateTimeImmutable $createdAt, ) {} }
If a DTO needs a different JSON date format, override the protected format constant:
final class PublicCarDto extends AbstractObjectToDto { protected const string DATE_TIME_FORMAT = 'Y-m-d'; }
Type casting/juggling array values into object properties
It is important to note that the hydrator will only work on classes that only contain typed properties.
If a non typed property is found an SquidIT\Hydrator\Exceptions\AmbiguousTypeException exception will be thrown.
The hydrator supports casting into the following property types
int:
if a string contains only digits (plus and minus signs are allowed)
bool:
The following values will be cast to true
1[int]'true'[string]'1'[string]'y'[string]'yes'[string]
The following values will be cast to false
0[int]'false'[string]'0'[string]'n'[string]'no'[string]
DateTimeImmutable::class:
Any string value supported by strtotime()
please note: as author of this library I feel no need to support the DateTime::class
BackedEnum:
Any integer of string backed enum value
UnionTypes:
❌ Union types are not supported because we are unable to infer concrete object type implementation.
Upgrading
Update v1.* => V2.*
Interface change
Adjust all references:
- From: \SquidIT\Hydrator\ArrayToObjectHydratorInterface
- To: \SquidIT\Hydrator\Interface\ArrayToObjectHydratorInterface
Update v2.* => v3.*
- Drops support for PHP 8.2 and 8.3, requires PHP 8.4+.
- Hydration hot path was reworked. Cached/warm hydration benchmarks are roughly 30% faster than v2, and ~40% faster than v2 prior to its mutation-removal patch. No public API changes; existing v2 code keeps working on PHP 8.4+.
squidit/array-to-object 适用场景与选型建议
squidit/array-to-object 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.53k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2023 年 11 月 07 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「object」 「hydrator」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 squidit/array-to-object 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 squidit/array-to-object 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 squidit/array-to-object 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A set of useful PHP classes.
Hydrator to map or extract an extract object
Runn Me! Value Objects Library
A simple library that uses the hydration pattern to fill objects given different sources of data.
Laravel SDK mapping data in object
Doctrine ORM hydrators for Laminas
统计信息
- 总下载量: 1.53k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 18
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2023-11-07