diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..754617f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,8 @@
+/tests export-ignore
+/.gitignore export-ignore
+/.travis.yml export-ignore
+/phpunit.xml export-ignore
+/phpcs.xml export-ignore
+/PULL_REQUEST_TEMPLATE.md export-ignore
+/CODE_OF_CONDUCT.md export-ignore
+/CONTRIBUTING.md export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..de4a392
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/vendor
+/composer.lock
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f7c093f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: php
+php:
+ - '7.2'
+ - '7.3'
+
+before_script:
+ - yes '' | pecl install yaml
+ - composer install
+
+script:
+ - composer validate --strict
+ - vendor/bin/phpunit --coverage-text
+ - vendor/bin/phpcs src/ tests/
+
+cache:
+ directories:
+ - $HOME/.composer/cache
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..a213aef
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,26 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## 1.0.0 - 2019-11-17
+### Added
+- Created the initial implementation of the ObjectFactory.
+
+### Changed
+- Nothing
+
+### Deprecated
+- Nothing
+
+### Removed
+- Nothing
+
+### Fixed
+- Nothing
+
+### Security
+- Nothing
+
+[Unreleased]: https://github.com/ulrack/object-factory/compare/1.0.0...HEAD
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..040dd3e
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at info@jyxon.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..7921a76
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,28 @@
+# Contributing
+
+To contribute to this package, please keep to these guidelines.
+
+- Fork the package.
+- Create a branch per feature.
+- Commit your changes to these branches.
+- Create a pull request per feature to the master branch of the original repository.
+
+## Pull requests
+
+Pull request should follow these rules, before they can get accepted.
+
+- Follow the [pull request template](PULL_REQUEST_TEMPLATE.md).
+- Contains a short but complete description.
+- Has passed all test command listed bellow.
+
+## Running Tests
+
+``` bash
+$ vendor/bin/phpunit --coverage-text
+$ vendor/bin/phpcs src/ tests/
+```
+
+## Notes
+
+Multiple commits per feature are allowed, but please provide a good description in your pull request.
+This description will be used to squash your feature into the master branch.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..457440e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) Jyxon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..ce6ccf9
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,18 @@
+# (bugfix / feature / refactor / etc.): Short descriptive title
+
+## Description
+
+Fixes #
+
+Changes proposed in this pull request:
+-
+-
+-
+
+Make sure you can check the following boxes before submitting the pull request:
+- [] The proposed changes work with the dependencies from composer.
+- [] The proposed changes have been thoroughly tested.
+- [] The proposed changes don't alter the code in such a manner that it conflicts with the initial purpose of the package.
+
+Optional checkbox
+- [] Backwards incompatible
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..320e806
--- /dev/null
+++ b/README.md
@@ -0,0 +1,164 @@
+[![Build Status](https://travis-ci.com/ulrack/object-factory.svg?branch=master)](https://travis-ci.com/ulrack/object-factory)
+
+# Ulrack Object Factory
+
+The Object Factory package contains an implementation for creating objects,
+based on configuration.
+
+## Installation
+
+To install the package run the following command:
+
+```
+composer require ulrack/object-factory
+```
+
+## Usage
+
+The package provides a [Analyser](src/Component/Analyser/ClassAnalyser.php)
+class, which retrieves an instantiation signature of a class.
+This analyser is used by the [ObjectFactory](src/Factory/ObjectFactory.php) to
+determine the order of the provided parameter from the configuration.
+The analyser expects an implementation of the StorageInterface from the
+`ulrack/storage` package.
+This implementation can be used to store previous analyses and retrieve them at
+a later point (caching mechanisms e.g.).
+
+Creating an ObjectFactory can simply be done with the following snippet:
+```php
+create(
+ ObjectStorage::class,
+ [
+ 'data' => ['foo']
+ ]
+);
+```
+
+A key-value structure is used for the parameters provided to the ObjectFactory.
+If (in this case the ObjectStorage class) expects a `$data` parameter of the
+type array in the `__construct` method, then the structure of the `$parameter`
+parameter will be as follows:
+```php
+$parameters = [
+ 'data' => [/** Value of $data here. */]
+];
+```
+
+For variadic parameters, this structure is the same.
+
+### Object nesting
+
+Some objects require other objects in their `__construct` method. With the
+ObjectFactory it is also possible to create these object, with the correct
+configuration.
+
+There are two ways to create the objects.
+
+#### Configuration declaration
+
+It is possible to completely configure the nested objects, expected by the method.
+Instead of passing along the variable as is, a array is used with one expected
+and one optional node. The expected node is `class`, this array node should contain
+the string representation of the expected class. The optional node is `parameters`,
+this array node will contain the objects' instantiation parameters. If none are
+required, then this can be left empty or undeclared.
+
+To create the ObjectFactory with a (full) configuration declaration, would look
+like this:
+```php
+create(
+ ObjectFactory::class,
+ [
+ 'classAnalyser' => [
+ 'class' => ClassAnalyser::class,
+ 'parameters' => [
+ 'analysisStorage' => [
+ 'class' => ObjectStorage::class,
+ ],
+ ],
+ ],
+ ]
+);
+```
+
+The configuration declaration could technically be infinitely deep.
+
+#### Object declaration
+
+It is also possible to re-use a previously generated or instantiation instance
+of a class. This can be done, by simply passing along the object in the parameters.
+
+```php
+create(
+ ObjectFactory::class,
+ [
+ 'classAnalyser' => $classAnalyser
+ ]
+);
+```
+
+Both of these declaration methods can be used and mixed throughout the declaration.
+
+## Change log
+
+Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
+
+## Contributing
+
+Please see [CONTRIBUTING](CONTRIBUTING.md) and [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) for details.
+
+## MIT License
+
+Copyright (c) Jyxon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..dfd7d34
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,57 @@
+{
+ "name": "ulrack/object-factory",
+ "description": "An object factory for PHP applications.",
+ "keywords": ["object-factory"],
+ "type": "library",
+ "license": "MIT",
+ "prefer-stable": true,
+ "minimum-stability": "stable",
+ "require":
+ {
+ "php": "^7.2",
+ "ulrack/configuration": "^1.0",
+ "ulrack/storage": "^1.0"
+ },
+ "authors": [
+ {
+ "name": "Ulrack",
+ "homepage": "https://www.ulrack.com/",
+ "role": "Developer"
+ }],
+ "config":
+ {
+ "sort-packages": true
+ },
+ "autoload":
+ {
+ "psr-4":
+ {
+ "Ulrack\\ObjectFactory\\": "src/"
+ }
+ },
+ "autoload-dev":
+ {
+ "psr-4":
+ {
+ "Ulrack\\ObjectFactory\\Tests\\": "tests/"
+ }
+ },
+ "archive":
+ {
+ "exclude": [
+ "/tests",
+ "/.gitignore",
+ "/.travis.yml",
+ "/phpunit.xml",
+ "/phpcs.xml",
+ "/PULL_REQUEST_TEMPLATE.md",
+ "/CODE_OF_CONDUCT.md",
+ "/CONTRIBUTING.md"
+ ]
+ },
+ "require-dev":
+ {
+ "phpunit/phpunit": "^8.0",
+ "squizlabs/php_codesniffer": "^3.4"
+ }
+}
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..60dd6ba
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..e1e548a
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,15 @@
+
+
+
+ tests
+
+
+
+
+ src
+
+
+
diff --git a/src/Common/ClassAnalyserInterface.php b/src/Common/ClassAnalyserInterface.php
new file mode 100644
index 0000000..f0becce
--- /dev/null
+++ b/src/Common/ClassAnalyserInterface.php
@@ -0,0 +1,16 @@
+analysisStorage = $analysisStorage;
+ }
+
+ /**
+ * Analyses the constructor of a class and returns a configuration array.
+ *
+ * @return array
+ *
+ * @throws NonInstantiableClassException When the analysed class is not instantiable.
+ */
+ public function analyse(string $class): array
+ {
+ if (!$this->analysisStorage->has($class)) {
+ $this->analysisStorage->set(
+ $class,
+ $this->reflect($class)
+ );
+ }
+
+ return $this->analysisStorage->get($class);
+ }
+
+ /**
+ * Retrieves a class analysis.
+ *
+ * @param string $class
+ *
+ * @return array
+ *
+ * @throws NonInstantiableClassException When the analysed class is not instantiable.
+ */
+ private function reflect(string $class): array
+ {
+ $reflection = new ReflectionClass($class);
+
+ if ($reflection->isInstantiable()) {
+ $constructor = $reflection->getConstructor();
+ $parameters = [];
+
+ if ($constructor !== null) {
+ foreach ($constructor->getParameters() as $parameter) {
+ $type = $parameter->getType();
+ $parameters[$parameter->getName()] = [
+ 'type' => $type->__toString(),
+ 'builtin' => $type->isBuiltin(),
+ 'allowsNull' => $parameter->allowsNull(),
+ 'isOptional' => $parameter->isOptional(),
+ 'isVariadic' => $parameter->isVariadic(),
+ 'default' => $parameter->isDefaultValueAvailable()
+ ? $parameter->getDefaultValue()
+ : null,
+ ];
+ }
+ }
+
+ return $parameters;
+ }
+
+ throw new NonInstantiableClassException(
+ $class
+ );
+ }
+}
diff --git a/src/Exception/CanNotCreateObjectException.php b/src/Exception/CanNotCreateObjectException.php
new file mode 100644
index 0000000..cc48f13
--- /dev/null
+++ b/src/Exception/CanNotCreateObjectException.php
@@ -0,0 +1,38 @@
+classAnalyser = $classAnalyser;
+ }
+
+ /**
+ * Creates an instance of an object.
+ *
+ * @param string $class
+ * @param array $parameters
+ *
+ * @return object
+ *
+ * @throws CanNotCreateObjectException When the object can not be created.
+ */
+ public function create(string $class, array $parameters): object
+ {
+ try {
+ $config = $this->classAnalyser->analyse($class);
+ } catch (Throwable $exception) {
+ throw new CanNotCreateObjectException(
+ $class,
+ [],
+ $parameters,
+ $exception
+ );
+ }
+
+ $invokeParameters = [];
+ foreach ($config as $parameterName => $parameterConfig) {
+ if (!isset($parameters[$parameterName])) {
+ if (!$parameterConfig['allowsNull']
+ && !$parameterConfig['isOptional']) {
+ throw new CanNotCreateObjectException(
+ $class,
+ $parameterConfig,
+ $parameters
+ );
+ }
+
+ $invokeParameters[] = $parameterConfig['default'];
+
+ continue;
+ }
+
+ if ($parameterConfig['isVariadic']) {
+ $variadicParameters = [];
+ foreach ($parameters[$parameterName] as $variadicParameter) {
+ try {
+ $invokeParameters[] = $this->parameterByType(
+ $parameterConfig,
+ $variadicParameter
+ );
+ } catch (Throwable $exception) {
+ throw new CanNotCreateObjectException(
+ $class,
+ $parameterConfig,
+ $parameters,
+ $exception
+ );
+ }
+ }
+
+ continue;
+ }
+
+ try {
+ $invokeParameters[] = $this->parameterByType($parameterConfig, $parameters[$parameterName]);
+ } catch (Throwable $exception) {
+ throw new CanNotCreateObjectException(
+ $class,
+ $parameterConfig,
+ $parameters,
+ $exception
+ );
+ }
+ }
+
+ return new $class(...$invokeParameters);
+ }
+
+ /**
+ * Validates and creates objects by it's type.
+ *
+ * @param array $parameterConfig
+ * @param mixed $parameter
+ *
+ * @return mixed
+ *
+ * @throws InvalidParameterTypeException When the type can not be resolved.
+ */
+ private function parameterByType(array $parameterConfig, $parameter)
+ {
+ if ($parameterConfig['builtin']) {
+ if (gettype(
+ $parameter
+ ) === $parameterConfig['type']) {
+ return $parameter;
+ }
+ throw new InvalidParameterTypeException($parameterConfig, $parameter);
+ }
+
+ if (!is_a(
+ $parameter,
+ $parameterConfig['type']
+ )) {
+ if (is_array($parameter)) {
+ try {
+ return $this->create(
+ $parameter['class'],
+ $parameter['parameters'] ?? []
+ );
+ } catch (Throwable $exception) {
+ throw new InvalidParameterTypeException(
+ $parameterConfig,
+ $parameter,
+ $exception
+ );
+ }
+ }
+
+ throw new InvalidParameterTypeException($parameterConfig, $parameter);
+ }
+
+ return $parameter;
+ }
+}
diff --git a/tests/Component/Analyser/ClassAnalyserTest.php b/tests/Component/Analyser/ClassAnalyserTest.php
new file mode 100644
index 0000000..a4ec340
--- /dev/null
+++ b/tests/Component/Analyser/ClassAnalyserTest.php
@@ -0,0 +1,50 @@
+assertInstanceOf(ClassAnalyser::class, $subject);
+
+ $result = $subject->analyse(ObjectStorage::class);
+
+ $this->assertEquals([
+ 'data' => [
+ 'type' => 'array',
+ 'builtin' => true,
+ 'allowsNull' => false,
+ 'isOptional' => true,
+ 'isVariadic' => false,
+ 'default' => [],
+ ]
+ ], $result);
+
+ $this->expectException(NonInstantiableClassException::class);
+
+ $subject->analyse(ClassAnalyserInterface::class);
+ }
+}
diff --git a/tests/Factory/ObjectFactoryTest.php b/tests/Factory/ObjectFactoryTest.php
new file mode 100644
index 0000000..c9392b5
--- /dev/null
+++ b/tests/Factory/ObjectFactoryTest.php
@@ -0,0 +1,252 @@
+assertInstanceOf(
+ ObjectFactory::class,
+ $subject
+ );
+
+ $this->assertInstanceOf(
+ ObjectFactory::class,
+ $subject->create(
+ ObjectFactory::class,
+ [
+ 'classAnalyser' => [
+ 'class' => ClassAnalyser::class,
+ 'parameters' => [
+ 'analysisStorage' => [
+ 'class' => ObjectStorage::class,
+ ],
+ ],
+ ],
+ ]
+ )
+ );
+ }
+
+ /**
+ * @return void
+ *
+ * @covers ::__construct
+ * @covers ::create
+ */
+ public function testNotInstantiableClass(): void
+ {
+ $subject = new ObjectFactory(new ClassAnalyser(new ObjectStorage()));
+
+ $this->expectException(CanNotCreateObjectException::class);
+
+ $subject->create(
+ ClassAnalyserInterface::class,
+ []
+ );
+ }
+
+ /**
+ * @return void
+ *
+ * @covers ::__construct
+ * @covers ::create
+ */
+ public function testNotProvidedParameter(): void
+ {
+ $subject = new ObjectFactory(new ClassAnalyser(new ObjectStorage()));
+
+ $this->expectException(CanNotCreateObjectException::class);
+
+ $subject->create(
+ ObjectFactory::class,
+ []
+ );
+ }
+
+ /**
+ * @return void
+ *
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::parameterByType
+ */
+ public function testInvalidTypeParameter(): void
+ {
+ $subject = new ObjectFactory(new ClassAnalyser(new ObjectStorage()));
+
+ $this->expectException(CanNotCreateObjectException::class);
+
+ $subject->create(
+ ObjectStorage::class,
+ [
+ 'data' => 'foo',
+ ]
+ );
+ }
+
+ /**
+ * @return void
+ *
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::parameterByType
+ */
+ public function testValidTypeParameter(): void
+ {
+ $subject = new ObjectFactory(new ClassAnalyser(new ObjectStorage()));
+
+ $this->assertInstanceOf(
+ ObjectStorage::class,
+ $subject->create(
+ ObjectStorage::class,
+ [
+ 'data' => ['foo' => 'bar'],
+ ]
+ )
+ );
+ }
+
+ /**
+ * @return void
+ *
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::parameterByType
+ */
+ public function testInvalidAggregatedInstanceParameter(): void
+ {
+ $subject = new ObjectFactory(new ClassAnalyser(new ObjectStorage()));
+
+ $this->expectException(CanNotCreateObjectException::class);
+
+ $subject->create(
+ ObjectFactory::class,
+ [
+ 'classAnalyser' => [
+ 'class' => ClassAnalyser::class,
+ 'parameters' => [],
+ ],
+ ]
+ );
+ }
+
+ /**
+ * @return void
+ *
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::parameterByType
+ */
+ public function testInvalidConfiguredInstanceParameter(): void
+ {
+ $subject = new ObjectFactory(new ClassAnalyser(new ObjectStorage()));
+
+ $this->expectException(CanNotCreateObjectException::class);
+
+ $subject->create(
+ ObjectFactory::class,
+ [
+ 'classAnalyser' => 'analyser',
+ ]
+ );
+ }
+
+ /**
+ * @return void
+ *
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::parameterByType
+ */
+ public function testValidPassthruInstanceParameter(): void
+ {
+ $subject = new ObjectFactory(new ClassAnalyser(new ObjectStorage()));
+
+ $this->assertInstanceOf(
+ ObjectFactory::class,
+ $subject->create(
+ ObjectFactory::class,
+ [
+ 'classAnalyser' => new ClassAnalyser(new ObjectStorage()),
+ ]
+ )
+ );
+ }
+
+ /**
+ * @return void
+ *
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::parameterByType
+ */
+ public function testVariadicParameter(): void
+ {
+ $subject = new ObjectFactory(new ClassAnalyser(new ObjectStorage()));
+
+ $result = $subject->create(
+ VariadicTestClass::class,
+ [
+ 'foo' => ['foo', 'bar', 'baz'],
+ ]
+ );
+
+ $this->assertInstanceOf(
+ VariadicTestClass::class,
+ $result
+ );
+
+ $this->assertEquals(
+ $result->getFoo(),
+ ['foo', 'bar', 'baz']
+ );
+ }
+
+ /**
+ * @return void
+ *
+ * @covers ::__construct
+ * @covers ::create
+ * @covers ::parameterByType
+ */
+ public function testVariadicParameterFailure(): void
+ {
+ $subject = new ObjectFactory(new ClassAnalyser(new ObjectStorage()));
+
+ $this->expectException(CanNotCreateObjectException::class);
+
+ $subject->create(
+ VariadicTestClass::class,
+ [
+ 'foo' => ['foo', 1, 'baz'],
+ ]
+ );
+ }
+}
diff --git a/tests/MockObjects/VariadicTestClass.php b/tests/MockObjects/VariadicTestClass.php
new file mode 100644
index 0000000..3c70feb
--- /dev/null
+++ b/tests/MockObjects/VariadicTestClass.php
@@ -0,0 +1,36 @@
+foo = $foo;
+ }
+
+ /**
+ * Retrieves foo.
+ *
+ * @return array
+ */
+ public function getFoo(): array
+ {
+ return $this->foo;
+ }
+}