├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── UPGRADE.md
├── composer.json
└── src
├── Definition.php
├── Facades
└── Factory.php
├── Factory.php
├── FactoryContract.php
└── FactoryServiceProvider.php
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Laravel Array Factory Change Log
2 | ================================
3 |
4 | 1.2.6, March 6, 2025
5 | --------------------
6 |
7 | - Enh: Added support for "illuminate/support" 12.0 (klimov-paul)
8 |
9 |
10 | 1.2.5, March 25, 2024
11 | ---------------------
12 |
13 | - Enh: Added support for "illuminate/support" 11.0 (klimov-paul)
14 |
15 |
16 | 1.2.4, February 24, 2023
17 | ------------------------
18 |
19 | - Enh #1: Added support for "illuminate/support" 10.0 (klimov-paul)
20 |
21 |
22 | 1.2.3, February 9, 2022
23 | -----------------------
24 |
25 | - Enh: Added support for "illuminate/support" 9.0 (klimov-paul)
26 |
27 |
28 | 1.2.2, September 9, 2020
29 | ------------------------
30 |
31 | - Enh: Added support for "illuminate/support" 8.0 (klimov-paul)
32 |
33 |
34 | 1.2.1, March 4, 2020
35 | --------------------
36 |
37 | - Enh: Added support for "illuminate/support" 7.0 (klimov-paul)
38 |
39 |
40 | 1.2.0, September 6, 2019
41 | ------------------------
42 |
43 | - Enh: Added support for "illuminate/support" 6.0 (klimov-paul)
44 |
45 |
46 | 1.1.0, March 6, 2019
47 | --------------------
48 |
49 | - Enh: Added support for "illuminate/support" 5.8 (klimov-paul)
50 |
51 |
52 | 1.0.1, March 6, 2019
53 | --------------------
54 |
55 | - Bug: Fixed `FactoryServiceProvider` is not loaded because lack of `provides()` declaration (klimov-paul)
56 |
57 |
58 | 1.0.0, February 19, 2019
59 | ------------------------
60 |
61 | - Initial release.
62 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | This is free software. It is released under the terms of the
2 | following BSD License.
3 |
4 | Copyright © 2019 by Illuminatech (https://github.com/illuminatech)
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions
9 | are met:
10 |
11 | * Redistributions of source code must retain the above copyright
12 | notice, this list of conditions and the following disclaimer.
13 | * Redistributions in binary form must reproduce the above copyright
14 | notice, this list of conditions and the following disclaimer in
15 | the documentation and/or other materials provided with the
16 | distribution.
17 | * Neither the name of Illuminatech nor the names of its
18 | contributors may be used to endorse or promote products derived
19 | from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | POSSIBILITY OF SUCH DAMAGE.
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Laravel Array Factory
6 |
7 |
8 |
9 | This extension allows DI aware object creation from array definition.
10 |
11 | For license information check the [LICENSE](LICENSE.md)-file.
12 |
13 | [](https://packagist.org/packages/illuminatech/array-factory)
14 | [](https://packagist.org/packages/illuminatech/array-factory)
15 | [](https://github.com/illuminatech/array-factory/actions)
16 |
17 |
18 | Installation
19 | ------------
20 |
21 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
22 |
23 | Either run
24 |
25 | ```
26 | php composer.phar require --prefer-dist illuminatech/array-factory
27 | ```
28 |
29 | or add
30 |
31 | ```json
32 | "illuminatech/array-factory": "*"
33 | ```
34 |
35 | to the require section of your composer.json.
36 |
37 |
38 | Usage
39 | -----
40 |
41 | This extension allows DI aware object creation from array definition.
42 | Creation is performed by factory defined via `\Illuminatech\ArrayFactory\FactoryContract` contract.
43 | `\Illuminatech\ArrayFactory\Factory` can be used for particular implementation.
44 | Such factory allows creation of any object from its array definition.
45 | Keys in definition array are processed by following rules:
46 |
47 | - '__class': string, full qualified name of the class to be instantiated.
48 | - '__construct()': array, arguments to be bound during constructor invocation.
49 | - 'methodName()': array, list of arguments to be passed to the object method, which name defined via key.
50 | - 'fieldOrProperty': mixed, value to be assigned to the public field or passed to the setter method.
51 | - '()': callable, PHP callback to be invoked once object has been instantiated and all other configuration applied to it.
52 |
53 | Imagine we have the following class defined at our project:
54 |
55 | ```php
56 | condition = $condition;
73 | }
74 |
75 | public function setType(string $type)
76 | {
77 | $this->type = $type;
78 | }
79 |
80 | public function getType(): string
81 | {
82 | return $this->type;
83 | }
84 |
85 | public function color(string $color): self
86 | {
87 | $this->color = $color;
88 |
89 | return $this;
90 | }
91 |
92 | public function startEngine(): self
93 | {
94 | $this->engineRunning = true;
95 |
96 | return $this;
97 | }
98 | }
99 | ```
100 |
101 | Instance of such class can be instantiated using array factory in the following way:
102 |
103 | ```php
104 | make([
109 | '__class' => Car::class, // class name
110 | '__construct()' => ['condition' => 'good'], // constructor arguments
111 | 'registrationNumber' => 'AB1234', // set public field `Car::$registrationNumber`
112 | 'type' => 'sedan', // pass value to the setter `Car::setType()`
113 | 'color()' => ['red'], // pass arguments to the method `Car::color()`
114 | '()' => function (Car $car) {
115 | // final adjustments to be made after object creation and other config application:
116 | $car->startEngine();
117 | },
118 | ]);
119 | ```
120 |
121 | The main benefit of array object definition is lazy loading: you can define entire object configuration as a mere array
122 | without even loading the class source file, and then instantiate actual object only in case it becomes necessary.
123 |
124 | Defined array configuration can be adjusted, applying default values for it. For example:
125 |
126 | ```php
127 | 'AB1234',
133 | 'type' => 'sedan',
134 | 'color()' => ['red'],
135 | ];
136 |
137 | // ...
138 |
139 | $defaultCarConfig = [
140 | '__class' => Car::class,
141 | 'type' => 'sedan',
142 | 'condition' => 'good',
143 | ];
144 |
145 | $car = $factory->make(array_merge($defaultCarConfig, $config));
146 | ```
147 |
148 | You may use `\Illuminatech\ArrayFactory\Facades\Factory` facade for quick access to the factory functionality.
149 | For example:
150 |
151 | ```php
152 | Car::class,
158 | 'registrationNumber' => 'AB1234',
159 | 'type' => 'sedan',
160 | ]);
161 | ```
162 |
163 |
164 | ## Service configuration
165 |
166 | The most common use case for array factory is creation of the universal configuration for particular application service.
167 | Imagine we create a library providing geo-location by IP address detection. Since there are plenty of external services
168 | and means to solve this task, we have created some high level contract, like following:
169 |
170 | ```php
171 | app->singleton(DetectorContract::class, function ($app) {
200 | $factory = new Factory($app);
201 |
202 | $factory->make(array_merge(
203 | ['__class' => DefaultDetector::class], // default config
204 | $app->config->get('geoip', []) // developer defined config
205 | ));
206 | });
207 | }
208 | }
209 | ```
210 |
211 | This allows developer to specify any particular detector class to be used along with its configuration. The actual
212 | configuration file 'config/geoip.php' may look like following:
213 |
214 | ```php
215 | \MyVendor\GeoLocation\SomeExternalApiDetector::class,
220 | 'apiEndpoint' => 'https://some.external.service/api',
221 | 'apiKey' => env('SOME_EXTERNAL_API_KEY'),
222 | ];
223 | ```
224 |
225 | It can also look like following:
226 |
227 | ```php
228 | \MyVendor\GeoLocation\LocalFileDetector::class,
233 | 'geoipDatabaseFile' => __DIR__.'/geoip/local.db',
234 | ];
235 | ```
236 |
237 | Both configuration will work fine with the service provider we created, and same will be for countless other possible
238 | configurations for different geo-location detectors, which may not even exist yet.
239 |
240 | **Heads up!** Remember to avoid usage of `\Closure`, while creating application configuration, otherwise you will
241 | face the error during configuration caching.
242 |
243 |
244 | ## Interaction with DI container
245 |
246 | `\Illuminatech\ArrayFactory\Factory` is DI aware: it performs object instantiation via `\Illuminate\Contracts\Container\Container::make()`.
247 | Thus bindings set within the container will affect object creation. For example:
248 |
249 | ```php
250 | bind(Car::class, function() {
260 | $car = new Car();
261 | $car->setType('by-di-container');
262 |
263 | return $car;
264 | });
265 |
266 | /* @var $car Car */
267 | $car = $factory->make([
268 | '__class' => Car::class,
269 | 'registrationNumber' => 'AB1234',
270 | ]);
271 |
272 | var_dump($car->getType()); // outputs: 'by-di-container'
273 | ```
274 |
275 | > Note: obviously, in case there is a DI container binding for the instantiated class, the key '__construct()' inside
276 | array configuration will be ignored.
277 |
278 | DI container is also used during configuration method invocations, allowing automatic arguments injection. For example:
279 |
280 | ```php
281 | carRents[] = ['car' => $car, 'price' => $price];
293 | }
294 | }
295 |
296 | $container = Container::getInstance();
297 |
298 | $factory = new Factory($container);
299 |
300 | $container->bind(Car::class, function() {
301 | $car = new Car();
302 | $car->setType('by-di-container');
303 |
304 | return $car;
305 | });
306 |
307 | /* @var $person Person */
308 | $person = $factory->make([
309 | '__class' => Person::class,
310 | 'rentCar()' => ['price' => 12],
311 | ]);
312 |
313 | var_dump($person->carRents[0]['car']->getType()); // outputs: 'by-di-container'
314 | var_dump($person->carRents[0]['price']); // outputs: '12'
315 | ```
316 |
317 | Note that final handler callback ('()' configuration key) is not DI aware and does not provide binding for its arguments.
318 | However, the factory instance is always passed as its second argument, allowing you to access to its DI container if needed.
319 | Following code will produce the same result as the one from previous example:
320 |
321 | ```php
322 | make([
333 | '__class' => Person::class,
334 | '()' => function (Person $person, Factory $factory) {
335 | $factory->getContainer()->call([$person, 'rentCar'], ['price' => 12]);
336 | },
337 | ]);
338 | ```
339 |
340 |
341 | ## Standalone configuration
342 |
343 | You may use array factory to configure or re-configure already existing objects. For example:
344 |
345 | ```php
346 | setType('sedan');
354 | $car->color('red');
355 |
356 | /* @var $car Car */
357 | $car = $factory->configure($car, [
358 | 'type' => 'hatchback',
359 | 'color()' => ['green'],
360 | ]);
361 |
362 | var_dump($car->getType()); // outputs: 'hatchback'
363 | var_dump($car->getColor()); // outputs: 'green'
364 | ```
365 |
366 |
367 | ## Type ensuring
368 |
369 | You may add extra check whether created object matches particular base class or interface, using `ensure()` method.
370 | For example:
371 |
372 | ```php
373 | ensure(
384 | [
385 | '__class' => RedisStore::class,
386 | ],
387 | Store::class
388 | );
389 |
390 | // throws an exception:
391 | $cache = $factory->ensure(
392 | [
393 | '__class' => Carbon::class,
394 | ],
395 | Store::class
396 | );
397 | ```
398 |
399 | ## Immutable methods handling
400 |
401 | `\Illuminatech\ArrayFactory\Factory` handles immutable methods during object configuration, returning new object
402 | from their invocations. For example: in case we have following class:
403 |
404 | ```php
405 | type = $type;
413 |
414 | return $new;
415 | }
416 |
417 | public function color(string $color): self
418 | {
419 | $new = clone $this; // immutability
420 | $new->color = $color;
421 |
422 | return $new;
423 | }
424 | }
425 | ```
426 |
427 | The following configuration will be applied correctly:
428 |
429 | ```php
430 | make([
438 | '__class' => CarImmutable::class,
439 | 'type' => 'sedan',
440 | 'color()' => ['green'],
441 | ]);
442 |
443 | var_dump($car->getType()); // outputs: 'sedan'
444 | var_dump($car->getColor()); // outputs: 'green'
445 | ```
446 |
447 | > Note: since there could be immutable method invocations during configuration, you should always use result
448 | of `\Illuminatech\ArrayFactory\FactoryContract::configure()` method instead of its argument.
449 |
450 |
451 | ## Recursive make
452 |
453 | For complex object, which stores other object as its inner property, there may be need to configure both host and resident
454 | objects using array definition and resolve them both via array factory. For this case definition like following may
455 | be created:
456 |
457 | ```php
458 | Car::class,
462 | // ...
463 | 'engine' => [
464 | '__class' => InternalCombustionEngine::class,
465 | // ...
466 | ],
467 | ];
468 | ```
469 |
470 | However, nested definitions are not resolved by array factory automatically. Following example will not instantiate
471 | engine instance:
472 |
473 | ```php
474 | Car::class,
482 | // ...
483 | 'engine' => [
484 | '__class' => InternalCombustionEngine::class,
485 | // ...
486 | ],
487 | ];
488 |
489 | $car = $factory->make($config);
490 | var_dump($car->engine); // outputs array
491 | ```
492 |
493 | This is done in order to allow setup of the slave internal configuration into created object, so it be can resolved
494 | in lazy way according to its own internal logic.
495 |
496 | However, you may enforce resolving of the nested definition wrapping it into `\Illuminatech\ArrayFactory\Definition` instance.
497 | For example:
498 |
499 | ```php
500 | Car::class,
509 | // ...
510 | 'engine' => new Definition([
511 | '__class' => InternalCombustionEngine::class,
512 | // ...
513 | ]),
514 | ];
515 |
516 | $car = $factory->make($config);
517 | var_dump($car->engine); // outputs object
518 | ```
519 |
--------------------------------------------------------------------------------
/UPGRADE.md:
--------------------------------------------------------------------------------
1 | Upgrading Instructions for Laravel Array Factory
2 | ================================================
3 |
4 | !!!IMPORTANT!!!
5 |
6 | The following upgrading instructions are cumulative. That is,
7 | if you want to upgrade from version A to version C and there is
8 | version B between A and C, you need to following the instructions
9 | for both A and B.
10 |
11 | Upgrade from 1.0.1
12 | ------------------
13 |
14 | * "illuminate/support" package requirements were raised to 6.0. Make sure to upgrade your code accordingly.
15 |
16 | Upgrade from 1.0.1
17 | ------------------
18 |
19 | * "illuminate/support" package requirements were raised to 5.8. Make sure to upgrade your code accordingly.
20 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "illuminatech/array-factory",
3 | "description": "Allows DI aware object creation from array definition",
4 | "keywords": ["laravel", "factory", "array", "configuration", "config"],
5 | "license": "BSD-3-Clause",
6 | "support": {
7 | "issues": "https://github.com/illuminatech/array-factory/issues",
8 | "wiki": "https://github.com/illuminatech/array-factory/wiki",
9 | "source": "https://github.com/illuminatech/array-factory"
10 | },
11 | "authors": [
12 | {
13 | "name": "Paul Klimov",
14 | "email": "klimov.paul@gmail.com"
15 | }
16 | ],
17 | "require": {
18 | "illuminate/support": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0"
19 | },
20 | "require-dev": {
21 | "illuminate/container": "*",
22 | "phpunit/phpunit": "^7.5 || ^8.0 || ^9.3 || ^10.5"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "Illuminatech\\ArrayFactory\\": "src"
27 | }
28 | },
29 | "autoload-dev": {
30 | "psr-4": {
31 | "Illuminatech\\ArrayFactory\\Test\\": "tests"
32 | }
33 | },
34 | "extra": {
35 | "branch-alias": {
36 | "dev-master": "1.0.x-dev"
37 | },
38 | "laravel": {
39 | "providers": [
40 | "Illuminatech\\ArrayFactory\\FactoryServiceProvider"
41 | ]
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/src/Definition.php:
--------------------------------------------------------------------------------
1 | make([
24 | * '__class' => Car::class,
25 | * '__construct()' => [
26 | * 'engine' => new Definition(Engine::class),
27 | * ],
28 | * 'driver' => new Definition([
29 | * '__class' => Person::class,
30 | * 'name' => 'John Doe',
31 | * ]),
32 | * ]);
33 | * ```
34 | *
35 | * @see FactoryContract::make()
36 | *
37 | * @author Paul Klimov
38 | * @since 1.0
39 | */
40 | class Definition
41 | {
42 | /**
43 | * @var array|string raw definition for {@see FactoryContract::make()}
44 | */
45 | public $definition;
46 |
47 | /**
48 | * Definition constructor.
49 | *
50 | * @param array|string $definition array factory compatible definition.
51 | */
52 | public function __construct($definition)
53 | {
54 | $this->definition = $definition;
55 | }
56 |
57 | /**
58 | * Restores class state after using `var_export()`.
59 | * @see var_export()
60 | *
61 | * @param array $state state to be restored from.
62 | * @return static restored instance.
63 | * @throws InvalidArgumentException when $state property does not contain `id` parameter.
64 | */
65 | public static function __set_state($state)
66 | {
67 | if (! isset($state['definition'])) {
68 | throw new InvalidArgumentException(
69 | 'Failed to instantiate class "'.get_called_class().'". Required parameter "definition" is missing.'
70 | );
71 | }
72 |
73 | return new self($state['definition']);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Facades/Factory.php:
--------------------------------------------------------------------------------
1 |
26 | * @since 1.0
27 | */
28 | class Factory extends Facade
29 | {
30 | /**
31 | * Get the registered name of the component.
32 | *
33 | * @return string
34 | */
35 | protected static function getFacadeAccessor()
36 | {
37 | return FactoryContract::class;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Factory.php:
--------------------------------------------------------------------------------
1 |
21 | * @since 1.0
22 | */
23 | class Factory implements FactoryContract
24 | {
25 | /**
26 | * @var \Illuminate\Contracts\Container\Container DI container to be used.
27 | */
28 | private $container;
29 |
30 | /**
31 | * Constructor.
32 | *
33 | * @param Container|null $container DI container to be used.
34 | */
35 | public function __construct(Container $container = null)
36 | {
37 | if ($container !== null) {
38 | $this->setContainer($container);
39 | }
40 | }
41 |
42 | /**
43 | * @return Container used DI container.
44 | */
45 | public function getContainer(): Container
46 | {
47 | if ($this->container === null) {
48 | $this->container = $this->defaultContainer();
49 | }
50 |
51 | return $this->container;
52 | }
53 |
54 | /**
55 | * @param Container $container DI container to be used.
56 | * @return static self reference.
57 | */
58 | public function setContainer(Container $container): self
59 | {
60 | $this->container = $container;
61 |
62 | return $this;
63 | }
64 |
65 | /**
66 | * {@inheritdoc}
67 | */
68 | public function make($definition, Container $container = null)
69 | {
70 | $container = $container ?? $this->getContainer();
71 |
72 | if ($definition instanceof Definition) {
73 | $definition = $definition->definition;
74 | }
75 |
76 | if (is_array($definition)) {
77 | $class = Arr::pull($definition, '__class');
78 | if ($class === null) {
79 | throw new InvalidArgumentException('Array definition must contain "__class" key.');
80 | }
81 | $constructArgs = Arr::pull($definition, '__construct()', []);
82 | $config = $definition;
83 | } else {
84 | $class = $definition;
85 | $constructArgs = [];
86 | $config = [];
87 | }
88 |
89 | $constructArgs = $this->makeIfDefinitionArray($constructArgs, $container);
90 |
91 | $object = $container->make($class, $constructArgs);
92 |
93 | return $this->configure($object, $config);
94 | }
95 |
96 | /**
97 | * {@inheritdoc}
98 | */
99 | public function configure($object, iterable $config, Container $container = null)
100 | {
101 | $finalHandler = null;
102 |
103 | foreach ($config as $action => $arguments) {
104 | if ($action === '()') {
105 | $finalHandler = $arguments;
106 |
107 | continue;
108 | }
109 |
110 | if (substr($action, -2) === '()') {
111 | // method call
112 | $container = $container ?? $this->getContainer();
113 |
114 | $result = $container->call([$object, substr($action, 0, -2)], $this->makeIfDefinitionArray($arguments, $container));
115 |
116 | // handle immutable methods
117 | $object = $this->chooseNewObject($object, $result);
118 |
119 | continue;
120 | }
121 |
122 | if (method_exists($object, $setter = 'set'.$action)) {
123 | // setter
124 | $result = call_user_func([$object, $setter], $this->makeIfDefinition($arguments, $container));
125 |
126 | // handle immutable methods
127 | $object = $this->chooseNewObject($object, $result);
128 |
129 | continue;
130 | }
131 |
132 | // property
133 | if (property_exists($object, $action) || method_exists($object, '__set')) {
134 | $object->$action = $this->makeIfDefinition($arguments, $container);
135 |
136 | continue;
137 | }
138 |
139 | throw new InvalidArgumentException('Class "'.get_class($object).'" does not have property "'.$action.'"');
140 | }
141 |
142 | if ($finalHandler !== null) {
143 | $result = call_user_func($finalHandler, $object, $this);
144 |
145 | // handle possible immutability
146 | $object = $this->chooseNewObject($object, $result);
147 | }
148 |
149 | return $object;
150 | }
151 |
152 | /**
153 | * {@inheritdoc}
154 | */
155 | public function ensure($reference, string $type = null, Container $container = null)
156 | {
157 | if (! is_object($reference)) {
158 | $reference = $this->make($reference, $container);
159 | }
160 |
161 | if ($type !== null) {
162 | if (! $reference instanceof $type) {
163 | throw new InvalidArgumentException('Reference "'.get_class($reference).'" does not match type "'.$type.'"');
164 | }
165 | }
166 |
167 | return $reference;
168 | }
169 |
170 | /**
171 | * Picks the new object to be used from original trusted one and new possible candidate.
172 | * This method is used to handle possible immutable creating methods, when method invocation
173 | * does not alters object state, but creates new object instead.
174 | *
175 | * @param object $original original object.
176 | * @param object|mixed $candidate candidate value.
177 | * @return object new object to be used.
178 | */
179 | private function chooseNewObject($original, $candidate)
180 | {
181 | if (is_object($candidate) && $candidate !== $original && get_class($candidate) === get_class($original)) {
182 | return $candidate;
183 | }
184 |
185 | return $original;
186 | }
187 |
188 | /**
189 | * Checks if given value is a array factory compatible definition, performs make if it is, skips - if not.
190 | *
191 | * @param mixed $candidate candidate value to be checked.
192 | * @param Container $container DI container to be used for making object.
193 | * @return object|mixed resolved object or intact candidate.
194 | */
195 | private function makeIfDefinition($candidate, Container $container = null)
196 | {
197 | $container = $container ?? $this->getContainer();
198 |
199 | if ($candidate instanceof Definition) {
200 | return $this->make($candidate->definition, $container);
201 | }
202 |
203 | return $candidate;
204 | }
205 |
206 | /**
207 | * Iterates over definition candidates, performing build of the values, which are valid definitions.
208 | *
209 | * @param iterable $candidates candidate values to be checked.
210 | * @param Container $container DI container to be used for making objects.
211 | * @return array
212 | */
213 | private function makeIfDefinitionArray(iterable $candidates, Container $container = null): array
214 | {
215 | $container = $container ?? $this->getContainer();
216 |
217 | $result = [];
218 | foreach ($candidates as $key => $value) {
219 | $result[$key] = $this->makeIfDefinition($value, $container);
220 | }
221 |
222 | return $result;
223 | }
224 |
225 | /**
226 | * Returns default DI container to be used for {@see $container}.
227 | *
228 | * @return Container DI container instance.
229 | */
230 | protected function defaultContainer(): Container
231 | {
232 | return \Illuminate\Container\Container::getInstance();
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/src/FactoryContract.php:
--------------------------------------------------------------------------------
1 | make([
28 | * '__class' => Item::class,
29 | * '__construct()' => ['constructorArgument' => 'initial'],
30 | * 'publicField' => 'value assigned to public field',
31 | * 'virtualProperty' => 'value passed to setter method',
32 | * 'someMethod()' => ['argument1' => 'value1', 'argument2' => 'value2'],
33 | * '()' => function (Item $item) {
34 | * // final adjustments
35 | * },
36 | * ]);
37 | * ```
38 | *
39 | * @see Definition
40 | *
41 | * @author Paul Klimov
42 | * @since 1.0
43 | */
44 | interface FactoryContract
45 | {
46 | /**
47 | * Creates new object from the definition.
48 | *
49 | * @param array|string $definition
50 | * @param Container|null $container DI container instance.
51 | * @return object created object.
52 | */
53 | public function make($definition, Container $container = null);
54 |
55 | /**
56 | * Configures existing object applying given configuration to it.
57 | *
58 | * @param object $object object to be configured.
59 | * @param iterable $config configuration to be applied.
60 | * @param Container|null $container DI container instance.
61 | * @return object configured object.
62 | */
63 | public function configure($object, iterable $config, Container $container = null);
64 |
65 | /**
66 | * Resolves the specified reference into the actual object and makes sure it is of the specified type.
67 | *
68 | * @param array|object|string $reference an object or its factory-compatible definition.
69 | * @param string|null $type the class/interface name to be checked. If null, type check will not be performed.
70 | * @param Container|null $container DI container instance.
71 | * @return object created object.
72 | */
73 | public function ensure($reference, string $type = null, Container $container = null);
74 | }
75 |
--------------------------------------------------------------------------------
/src/FactoryServiceProvider.php:
--------------------------------------------------------------------------------
1 |
20 | * @since 1.0
21 | */
22 | class FactoryServiceProvider extends ServiceProvider implements DeferrableProvider
23 | {
24 | /**
25 | * {@inheritdoc}
26 | */
27 | public function register()
28 | {
29 | $this->app->singleton(FactoryContract::class, function ($app) {
30 | return new Factory($app);
31 | });
32 | }
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public function provides()
38 | {
39 | return [
40 | FactoryContract::class,
41 | ];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------