From 05e8cbe75661ae7f3e647004a3f3cea4215595af Mon Sep 17 00:00:00 2001 From: "Carlos A. B. Carucce" Date: Tue, 2 Jul 2024 21:01:00 -0300 Subject: [PATCH 1/8] creating method: ServiceContainer::hydrate --- src/ServiceContainer.php | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/ServiceContainer.php b/src/ServiceContainer.php index a6ca560..ee96b45 100644 --- a/src/ServiceContainer.php +++ b/src/ServiceContainer.php @@ -107,6 +107,53 @@ public function has(string $id): bool || class_exists($id); } + /** + * @template T + * + * @param T $classOrObject + * @param array $data + * + * @return T + * @throws ContainerException + * + */ + public function hydrate(string|object $classOrObject, array $data = []): mixed + { + try { + $instance = $classOrObject; + + if (is_string($instance)) { + $arguments = []; + $reflection = new ReflectionClass($instance); + + if ($reflection->hasMethod('__construct')) { + $reflectionMethod = $reflection->getMethod('__construct'); + $argumentsMap = []; + + foreach ($reflectionMethod->getParameters() as $parameter) { + $reflectionType = $parameter->getType(); + + $argumentsMap[$parameter->getName()] = [ + 'type' => $reflectionType->getName(), + 'isBuiltin' => $reflectionType->isBuiltin(), + ]; + } + + foreach (array_intersect_key($data, $argumentsMap) as $key => $value) { + $arguments[$key] = !$argumentsMap[$key]['isBuiltin'] ? + $this->hydrate($argumentsMap[$key]['type'], $value ?? []) : $value; + } + } + + $instance = $this->get($instance, $arguments); + } + + return $instance; + } catch (Exception $e) { + throw new ContainerException('Could not hydrate object', $e->getCode(), $e); + } + } + /** * @param ReflectionMethod|ReflectionFunction $reflectionMethod * @param array $arguments From 0c8c1ccca7dba6f7efc8c8378f4aeb60d19c945b Mon Sep 17 00:00:00 2001 From: "Carlos Alberto B. Carucce" Date: Fri, 5 Jul 2024 20:15:55 -0300 Subject: [PATCH 2/8] creating "ArrayOf" annotation --- src/Annotations/ArrayOf.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/Annotations/ArrayOf.php diff --git a/src/Annotations/ArrayOf.php b/src/Annotations/ArrayOf.php new file mode 100644 index 0000000..96271c4 --- /dev/null +++ b/src/Annotations/ArrayOf.php @@ -0,0 +1,14 @@ + Date: Fri, 5 Jul 2024 20:17:03 -0300 Subject: [PATCH 3/8] Hydrate scenarios with arrays/iterators --- src/ServiceContainer.php | 132 +++++++++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 27 deletions(-) diff --git a/src/ServiceContainer.php b/src/ServiceContainer.php index ee96b45..51d00bc 100644 --- a/src/ServiceContainer.php +++ b/src/ServiceContainer.php @@ -17,6 +17,7 @@ use ReflectionMethod; use Webdevcave\DirectoryCrawler\Crawler; use Webdevcave\SimpleCache\MemoryCache; +use Webdevcave\Yadic\Annotations\ArrayOf; use Webdevcave\Yadic\Annotations\Inject; use Webdevcave\Yadic\Annotations\Provides; use Webdevcave\Yadic\Annotations\Singleton; @@ -113,42 +114,22 @@ public function has(string $id): bool * @param T $classOrObject * @param array $data * - * @return T + * @return T|T[] * @throws ContainerException * */ public function hydrate(string|object $classOrObject, array $data = []): mixed { try { - $instance = $classOrObject; - - if (is_string($instance)) { - $arguments = []; - $reflection = new ReflectionClass($instance); - - if ($reflection->hasMethod('__construct')) { - $reflectionMethod = $reflection->getMethod('__construct'); - $argumentsMap = []; - - foreach ($reflectionMethod->getParameters() as $parameter) { - $reflectionType = $parameter->getType(); - - $argumentsMap[$parameter->getName()] = [ - 'type' => $reflectionType->getName(), - 'isBuiltin' => $reflectionType->isBuiltin(), - ]; - } - - foreach (array_intersect_key($data, $argumentsMap) as $key => $value) { - $arguments[$key] = !$argumentsMap[$key]['isBuiltin'] ? - $this->hydrate($argumentsMap[$key]['type'], $value ?? []) : $value; - } - } + if (array_is_list($data)) { + return $this->hydrateArray($classOrObject, $data); + } - $instance = $this->get($instance, $arguments); + if (is_string($classOrObject)) { + return $this->hydrateByClassName($classOrObject, $data); } - return $instance; + return $this->hydrateExistingInstance($classOrObject, $data); } catch (Exception $e) { throw new ContainerException('Could not hydrate object', $e->getCode(), $e); } @@ -267,4 +248,101 @@ public function addAlias(string $alias, string $concrete): void { $this->aliases[$alias] = $concrete; } + + /** + * @template T + * + * @param T $classOrObject + * @param array $data + * + * @return T[] + */ + private function hydrateArray(string|object $classOrObject, array $data): mixed + { + if (is_object($classOrObject)) { + $classOrObject = get_class($classOrObject); + } + + $objects = []; + + foreach ($data as $item) { + $objects[] = $this->hydrateByClassName($classOrObject, $item); + } + + return $objects; + } + + /** + * @param string $className + * @param array $data + * + * @throws ContainerException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws ReflectionException + * + * @return mixed + */ + private function hydrateByClassName(string $className, array $data): mixed + { + $arguments = []; + $reflection = new ReflectionClass($className); + + if ($reflection->hasMethod('__construct')) { + $reflectionMethod = $reflection->getMethod('__construct'); + $argumentsMap = []; + + foreach ($reflectionMethod->getParameters() as $parameter) { + $reflectionType = $parameter->getType(); + $type = $reflectionType->getName(); + $arrayType = null; + + if ( + ($type === 'array' || $type === 'iterable') + && !empty($arrayOf = $parameter->getAttributes(ArrayOf::class)) + ) { + $arrayType = $arrayOf[0]->newInstance()->target; + } + + $argumentsMap[$parameter->getName()] = [ + 'type' => $type, + 'arrayType' => $arrayType, + 'isBuiltin' => $reflectionType->isBuiltin(), + ]; + } + + foreach (array_intersect_key($data, $argumentsMap) as $key => $value) { + $map = $argumentsMap[$key]; + + if (!is_null($map['arrayType']) && is_iterable($value)) { + $arguments[$key] = []; + + foreach ($value as $item) { + $arguments[$key][] = $this->hydrateByClassName($map['arrayType'], $item); + } + + continue; + } + + $arguments[$key] = !$map['isBuiltin'] ? + $this->hydrateByClassName($map['type'], $value ?? []) : $value; + } + } + + return $this->get($className, $arguments); + } + + /** + * @param object $instance + * @param array $data + * + * @throws Exception + * + * @return mixed + */ + private function hydrateExistingInstance(object $instance, array $data): mixed + { + //TODO implementation + throw new Exception('Not implemented'); + } } From 6104fa55080142e7bda268bf69f9be8d6db4569d Mon Sep 17 00:00:00 2001 From: "Carlos A. B. Carucce" Date: Fri, 5 Jul 2024 22:44:51 -0300 Subject: [PATCH 4/8] ServiceContainer: - reorder methods - discard existing instances hydration for now --- src/ServiceContainer.php | 161 +++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 91 deletions(-) diff --git a/src/ServiceContainer.php b/src/ServiceContainer.php index 51d00bc..fd5be4e 100644 --- a/src/ServiceContainer.php +++ b/src/ServiceContainer.php @@ -47,6 +47,26 @@ public function __construct(CacheInterface $cache = null) $this->singletons = $cache->get('singletons', []); } + /** + * @throws InvalidArgumentException + */ + public function __destruct() + { + $this->cache->set('aliases', $this->aliases); + $this->cache->set('singletons', $this->singletons); + } + + /** + * @param string $alias + * @param string $concrete + * + * @return void + */ + public function addAlias(string $alias, string $concrete): void + { + $this->aliases[$alias] = $concrete; + } + /** * Finds an entry of the container by its identifier and returns it. * @@ -111,88 +131,26 @@ public function has(string $id): bool /** * @template T * - * @param T $classOrObject + * @param T $className * @param array $data * - * @return T|T[] * @throws ContainerException * + * @return T|T[] */ - public function hydrate(string|object $classOrObject, array $data = []): mixed + public function hydrate(string $className, array $data = []): mixed { try { if (array_is_list($data)) { - return $this->hydrateArray($classOrObject, $data); + return $this->hydrateArray($className, $data); } - if (is_string($classOrObject)) { - return $this->hydrateByClassName($classOrObject, $data); - } - - return $this->hydrateExistingInstance($classOrObject, $data); + return $this->hydrateByClassName($className, $data); } catch (Exception $e) { throw new ContainerException('Could not hydrate object', $e->getCode(), $e); } } - /** - * @param ReflectionMethod|ReflectionFunction $reflectionMethod - * @param array $arguments - * - * @throws NotFoundExceptionInterface - * @throws ReflectionException - * @throws ContainerExceptionInterface - * - * @return array - */ - private function createArguments( - ReflectionMethod|ReflectionFunction $reflectionMethod, - array $arguments = [] - ): array { - foreach ($reflectionMethod->getParameters() as $reflectionParameter) { - $argumentName = $reflectionParameter->getName(); - - if (isset($arguments[$argumentName])) { - continue; - } - - if ($reflectionParameter->hasType()) { - $reflectionType = $reflectionParameter->getType(); - $typeAlias = null; - - $injectAttrs = $reflectionParameter->getAttributes(Inject::class); - if (!empty($injectAttrs)) { - $injectAttrs[0]->newInstance()->index; - } - - if ($typeAlias || !$reflectionType->isBuiltin()) { - $arguments[$argumentName] = $this->get($typeAlias ?? $reflectionType->getName()); - continue; - } - - if ( - !$reflectionParameter->isDefaultValueAvailable() - && !$reflectionParameter->allowsNull() - ) { - throw new Exception("Could not inject parameter: $argumentName"); - } - - $arguments[$argumentName] = $reflectionParameter->getDefaultValue(); - } - } - - return $arguments; - } - - /** - * @throws InvalidArgumentException - */ - public function __destruct() - { - $this->cache->set('aliases', $this->aliases); - $this->cache->set('singletons', $this->singletons); - } - /** * @param callable $function * @param array $arguments @@ -239,14 +197,53 @@ public function loadDefinitionsFromDirectory(string $directory, string $namespac } /** - * @param string $alias - * @param string $concrete + * @param ReflectionMethod|ReflectionFunction $reflectionMethod + * @param array $arguments * - * @return void + * @throws NotFoundExceptionInterface + * @throws ReflectionException + * @throws ContainerExceptionInterface + * + * @return array */ - public function addAlias(string $alias, string $concrete): void + private function createArguments( + ReflectionMethod|ReflectionFunction $reflectionMethod, + array $arguments = [] + ): array { - $this->aliases[$alias] = $concrete; + foreach ($reflectionMethod->getParameters() as $reflectionParameter) { + $argumentName = $reflectionParameter->getName(); + + if (isset($arguments[$argumentName])) { + continue; + } + + if ($reflectionParameter->hasType()) { + $reflectionType = $reflectionParameter->getType(); + $typeAlias = null; + + $injectAttrs = $reflectionParameter->getAttributes(Inject::class); + if (!empty($injectAttrs)) { + $injectAttrs[0]->newInstance()->index; + } + + if ($typeAlias || !$reflectionType->isBuiltin()) { + $arguments[$argumentName] = $this->get($typeAlias ?? $reflectionType->getName()); + continue; + } + + if ( + !$reflectionParameter->isDefaultValueAvailable() + && !$reflectionParameter->allowsNull() + ) { + throw new Exception("Could not inject parameter: $argumentName"); + } + + $arguments[$argumentName] = $reflectionParameter->getDefaultValue(); + } + } + + return $arguments; } /** @@ -257,12 +254,8 @@ public function addAlias(string $alias, string $concrete): void * * @return T[] */ - private function hydrateArray(string|object $classOrObject, array $data): mixed + private function hydrateArray(string $classOrObject, array $data): mixed { - if (is_object($classOrObject)) { - $classOrObject = get_class($classOrObject); - } - $objects = []; foreach ($data as $item) { @@ -331,18 +324,4 @@ private function hydrateByClassName(string $className, array $data): mixed return $this->get($className, $arguments); } - - /** - * @param object $instance - * @param array $data - * - * @throws Exception - * - * @return mixed - */ - private function hydrateExistingInstance(object $instance, array $data): mixed - { - //TODO implementation - throw new Exception('Not implemented'); - } } From 7e4b62e4a3622f62b1024f921fb2da6601b509aa Mon Sep 17 00:00:00 2001 From: "Carlos A. B. Carucce" Date: Fri, 5 Jul 2024 22:45:13 -0300 Subject: [PATCH 5/8] ServiceContainer: hydration tests --- tests/ExampleNamespace/Candidate.php | 17 +++++++++++ tests/ExampleNamespace/Skill.php | 12 ++++++++ tests/ServiceContainerTest.php | 44 ++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 tests/ExampleNamespace/Candidate.php create mode 100644 tests/ExampleNamespace/Skill.php diff --git a/tests/ExampleNamespace/Candidate.php b/tests/ExampleNamespace/Candidate.php new file mode 100644 index 0000000..f79a7e7 --- /dev/null +++ b/tests/ExampleNamespace/Candidate.php @@ -0,0 +1,17 @@ +container->get(ClassD::class); } + public function testHydrationForVector() + { + $data = [ + ['title' => 'PHP'], + ['title' => 'Java'], + ['title' => 'Rust'], + ['title' => 'React'], + ]; + $instances = $this->container->hydrate(Skill::class, $data); + + $this->assertContainsOnlyInstancesOf(Skill::class, $instances); + + foreach ($data as $key => $skill) { + $this->assertEquals($skill['title'], $instances[$key]->title); + } + } + + public function testHydrationForMatrix() + { + $data = [ + 'name' => 'John Doe', + 'age' => 25, + 'skills' => [ + ['title' => 'PHP'], + ['title' => 'Java'], + ['title' => 'Rust'], + ['title' => 'React'], + ], + ]; + $instance = $this->container->hydrate(Candidate::class, $data); + + $this->assertInstanceOf(Candidate::class, $instance); + $this->assertEquals($data['name'], $instance->name); + $this->assertEquals($data['age'], $instance->age); + + foreach ($data['skills'] as $key => $skill) { + $this->assertEquals($skill['title'], $instance->skills[$key]->title); + } + } + protected function setUp(): void { $this->container = new ServiceContainer(); From e416c08c8d0125b78dad6c95eb605abcd9dd409a Mon Sep 17 00:00:00 2001 From: "Carlos A. B. Carucce" Date: Fri, 5 Jul 2024 22:47:56 -0300 Subject: [PATCH 6/8] apply style fixes --- src/ServiceContainer.php | 5 ++--- tests/ExampleNamespace/Candidate.php | 5 ++--- tests/ExampleNamespace/Skill.php | 5 ++--- tests/ServiceContainerTest.php | 4 ++-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/ServiceContainer.php b/src/ServiceContainer.php index fd5be4e..910cb7c 100644 --- a/src/ServiceContainer.php +++ b/src/ServiceContainer.php @@ -209,8 +209,7 @@ public function loadDefinitionsFromDirectory(string $directory, string $namespac private function createArguments( ReflectionMethod|ReflectionFunction $reflectionMethod, array $arguments = [] - ): array - { + ): array { foreach ($reflectionMethod->getParameters() as $reflectionParameter) { $argumentName = $reflectionParameter->getName(); @@ -298,7 +297,7 @@ private function hydrateByClassName(string $className, array $data): mixed } $argumentsMap[$parameter->getName()] = [ - 'type' => $type, + 'type' => $type, 'arrayType' => $arrayType, 'isBuiltin' => $reflectionType->isBuiltin(), ]; diff --git a/tests/ExampleNamespace/Candidate.php b/tests/ExampleNamespace/Candidate.php index f79a7e7..a4ded9e 100644 --- a/tests/ExampleNamespace/Candidate.php +++ b/tests/ExampleNamespace/Candidate.php @@ -11,7 +11,6 @@ public function __construct( public ?int $age = null, #[ArrayOf(Skill::class)] public array $skills = [] - ) - { + ) { } -} \ No newline at end of file +} diff --git a/tests/ExampleNamespace/Skill.php b/tests/ExampleNamespace/Skill.php index f7cd24b..1a1c5af 100644 --- a/tests/ExampleNamespace/Skill.php +++ b/tests/ExampleNamespace/Skill.php @@ -6,7 +6,6 @@ class Skill { public function __construct( public string $title, - ) - { + ) { } -} \ No newline at end of file +} diff --git a/tests/ServiceContainerTest.php b/tests/ServiceContainerTest.php index f6f5c0b..01316e4 100644 --- a/tests/ServiceContainerTest.php +++ b/tests/ServiceContainerTest.php @@ -119,8 +119,8 @@ public function testHydrationForVector() public function testHydrationForMatrix() { $data = [ - 'name' => 'John Doe', - 'age' => 25, + 'name' => 'John Doe', + 'age' => 25, 'skills' => [ ['title' => 'PHP'], ['title' => 'Java'], From 44436ed38ea6c9a53e6b30ff47b9f265e2834618 Mon Sep 17 00:00:00 2001 From: "Carlos A. B. Carucce" Date: Fri, 5 Jul 2024 22:52:39 -0300 Subject: [PATCH 7/8] updating keywords --- composer.json | 3 ++- composer.lock | 40 ++++++++++++++++++++-------------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/composer.json b/composer.json index 5f27121..ec2f891 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ "autowiring", "repository", "container", - "service-container" + "service-container", + "hydration" ], "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 7c9e71f..d056274 100644 --- a/composer.lock +++ b/composer.lock @@ -272,16 +272,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { @@ -292,7 +292,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -324,9 +324,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "phar-io/manifest", @@ -448,16 +448,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.14", + "version": "10.1.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b" + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", - "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", + "reference": "5da8b1728acd1e6ffdf2ff32ffbdfd04307f26ae", "shasum": "" }, "require": { @@ -514,7 +514,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.15" }, "funding": [ { @@ -522,7 +522,7 @@ "type": "github" } ], - "time": "2024-03-12T15:33:41+00:00" + "time": "2024-06-29T08:25:15+00:00" }, { "name": "phpunit/php-file-iterator", @@ -769,16 +769,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.24", + "version": "10.5.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015" + "reference": "831bf82312be6037e811833ddbea0b8de60ea314" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f124e3e3e561006047b532fd0431bf5bb6b9015", - "reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/831bf82312be6037e811833ddbea0b8de60ea314", + "reference": "831bf82312be6037e811833ddbea0b8de60ea314", "shasum": "" }, "require": { @@ -850,7 +850,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.24" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.25" }, "funding": [ { @@ -866,7 +866,7 @@ "type": "tidelift" } ], - "time": "2024-06-20T13:09:54+00:00" + "time": "2024-07-03T05:49:17+00:00" }, { "name": "rregeer/phpunit-coverage-check", @@ -1890,5 +1890,5 @@ "php": ">=8.1" }, "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.6.0" } From 56551ca7e2dd993438dbeaa366314fde41742a32 Mon Sep 17 00:00:00 2001 From: "Carlos A. B. Carucce" Date: Fri, 5 Jul 2024 23:18:11 -0300 Subject: [PATCH 8/8] updating README: examples for hydration and method invoking --- README.md | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a1cf483..999b118 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![codecov](https://codecov.io/github/WebdevCave/yadic-php/graph/badge.svg?token=6GLECJQG16)](https://codecov.io/github/WebdevCave/yadic-php) This is a simple to use, yet powerful service container that provides a seamless way to automate dependency injection -with auto-wiring. +featuring auto-wiring and object hydration. ```bash composer require webdevcave/yadic @@ -18,6 +18,8 @@ Alternatively, you can clone the repository or download the source files directl ## Usage +### Autowiring + ```php addAlias(StorageInterface::class, Storage::class); var_dump($container->get(MyController::class)->save()); //bool(true) ``` +### Invoking a method ft. autowiring + +```php +$arguments = ['nonInjectableArgument' => 'value']; //optional +$container->invoke([$instance, 'methodName'], $arguments); +``` + +### Hydration + +```php +//Class declarations: + +use Webdevcave\Yadic\Annotations\ArrayOf; + +class Candidate +{ + public function __construct( + public ?string $name = null, + public ?int $age = null, + #[ArrayOf(Skill::class)] + public array $skills = [] + ) { + } +} + +class Skill +{ + public function __construct( + public string $title, + ) { + } +} + +// Hydration example 1: +$data = [ + 'name' => 'John Doe', + 'age' => 25, + 'skills' => [ + ['title' => 'PHP'], + ['title' => 'Java'], + ['title' => 'Rust'], + ['title' => 'React'], + ], +]; +$instance = $container->hydrate(Candidate::class, $data); + +//Results output +/* +print_r($instance); +This test printed output: +Candidate Object +( + [name] => John Doe + [age] => 25 + [skills] => Array + ( + [0] => Skill Object + ( + [title] => PHP + ) + + [1] => Skill Object + ( + [title] => Java + ) + + [2] => Skill Object + ( + [title] => Rust + ) + + [3] => Skill Object + ( + [title] => React + ) + + ) + +) + */ + +// Hydration example 2: +$data = [ + [ + 'name' => 'Foo', + //... + ], + [ + 'name' => 'Bar', + //... + ] +]; +$instances = $container->hydrate(Candidate::class, $data); +//Results output +/* +print_r($instances); +This test printed output: +Array +( + [0] => Candidate Object + ( + [name] => Foo + //... + ) + + [1] => Candidate Object + ( + [name] => Bar + //... + ) +) + */ +``` + ## Contributing Contributions are welcome! If you find any issues or have suggestions for improvements,