diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40a512f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor +/composer.phar +/composer.lock diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..fe392b9 --- /dev/null +++ b/composer.json @@ -0,0 +1,28 @@ +{ + "name": "bjoern-buettner/dependency-injector", + "description": "description", + "license": "MIT", + "authors": [ + { + "name": "Björn Büttner", + "email": "email@example.com" + } + ], + "require": { + "php": "^8.1", + "symfony/string": "^6.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "autoload": { + "psr-4": { + "Me\\BjoernBuettner\\DependencyInjector\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Me\\BjoernBuettner\\DependencyInjector\\": "test/" + } + } +} \ No newline at end of file diff --git a/src/DependencyBuilder.php b/src/DependencyBuilder.php new file mode 100644 index 0000000..94d2953 --- /dev/null +++ b/src/DependencyBuilder.php @@ -0,0 +1,106 @@ + + */ + private array $cache = []; + + /** + * @param array $params + * @param array $interfaces + * @param array $factories + */ + public function __construct( + private readonly array $params = [], + private readonly array $interfaces = [], + private readonly array $factories = [], + ) { + } + + /** + * @throws ReflectionException + */ + private function getParamValue(ReflectionParameter $param, array $variables, string $key): mixed + { + if (isset($variables[$key])) { + return $variables[$key]; + } + if ($type = $param->getType()) { + if (!$type->isBuiltin()) { + return $this->build($type->getName()); + } + $envName = (new UnicodeString($param->getName()))->snake()->upper()->toString(); + if (isset($_ENV[$envName])) { + return match ($param->getType()->getName()) { + 'int' => (int)$_ENV[$envName], + 'float' => (float)$_ENV[$envName], + 'bool' => $_ENV[$envName] === 'true', + 'array' => explode(',', $_ENV[$envName]), + default => (string)$_ENV[$envName], + }; + } + if ($type->allowsNull()) { + return null; + } + } + if ($param->isDefaultValueAvailable()) { + return $param->getDefaultValue(); + } + throw new UnresolvableParameter("Cannot resolve parameter {$param->getName()}."); + } + /** + * @throws ReflectionException + */ + public function build(string $class): object + { + if (isset($this->cache[$class])) { + return $this->cache[$class]; + } + if (isset($this->factories[$class])) { + return $this->cache[$class] = $this->build($this->factories[$class])->get(); + } + if (isset($this->interfaces[$class])) { + return $this->cache[$class] = $this->build($this->interfaces[$class]); + } + $rc = new ReflectionClass($class); + $constructor = $rc->getConstructor(); + if (!$constructor) { + return $this->cache[$class] = $rc->newInstance(); + } + $params = $constructor->getParameters(); + $args = []; + foreach ($params as $param) { + $args[] = $this->getParamValue($param, $this->params, $class . '.' . $param->getName()); + } + return $this->cache[$class] = $rc->newInstanceArgs($args); + } + + /** + * @param array $variables + * @throws ReflectionException + */ + public function call(string $class, string $method, array $variables = []): string + { + $object = $this->build($class); + $rm = new ReflectionMethod($object, $method); + $params = $rm->getParameters(); + $args = []; + foreach ($params as $param) { + $args[] = $this->getParamValue($param, $variables, $param->getName()); + } + return $rm->invokeArgs($object, $args); + } +} diff --git a/src/UnresolvableParameter.php b/src/UnresolvableParameter.php new file mode 100644 index 0000000..3dd0144 --- /dev/null +++ b/src/UnresolvableParameter.php @@ -0,0 +1,10 @@ +