Skip to content

Commit

Permalink
prepare to release (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
smpl authored Jun 24, 2023
2 parents 9f16646 + af050bc commit 05efa17
Show file tree
Hide file tree
Showing 21 changed files with 289 additions and 188 deletions.
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
* export-ignore

/src** -export-ignore
/template** -export-ignore
/composer.json -export-ignore
/readme.md -export-ignore
/LICENSE -export-ignore
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
/vendor/
/composer.lock
composer.lock
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ install:
composer install
test: phpcs phpstan phpunit infection clean
clean:
rm tests/ExampleCompiled.php
rm -f tests/ExampleCompiled.php
update:
composer update
phpcs:
Expand Down
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
"email": "[email protected]"
}
],
"minimum-stability": "stable",
"config": {
"allow-plugins": {
"infection/extension-installer": true
Expand Down
3 changes: 0 additions & 3 deletions src/ArrayCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ public function __construct(ContainerInterface $container)
$this->container = $container;
}

/**
* @noinspection PhpMissingReturnTypeInspection
*/
public function get($id)
{
if (!array_key_exists($id, $this->values)) {
Expand Down
162 changes: 162 additions & 0 deletions src/Compiler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php

declare(strict_types=1);

namespace Cekta\DI;

use InvalidArgumentException;
use UnexpectedValueException;

class Compiler
{
/**
* @var array<string, mixed>
*/
private array $params;
/**
* @var array<string, string>
*/
private array $alias;
/**
* @var array<string, callable>
*/
private array $definitions;
private string $fqcn;
/**
* @var array<string>
*/
private array $shared = [];
/**
* @var array<string, array<array{'name': string, 'variadic': bool, parameter: string}>>
*/
private array $dependenciesMap = [];
private Reflection $reflection;
private array $stack = [];

/**
* @param array<string, mixed> $params
* @param array<string, string> $alias
* @param array<string, callable> $definitions
* @param string $fqcn
*/
public function __construct(array $params, array $alias, array $definitions, string $fqcn)
{
$this->params = $params;
$this->alias = $alias;
$this->definitions = $definitions;
$this->fqcn = $fqcn;
}

/**
* @param array<string> $containers
* @return string|false
*/
public function __invoke(array $containers): string|false
{
$this->reflection = new Reflection();
$namespace = $this->getNamespace();
$class = $this->getClass();
$alias = $this->alias;
$definitions = $this->definitions;
$containers = $this->generateContainers($containers);
ob_start();
include __DIR__ . '/../template/container.compiler';
return ob_get_clean();
}

/**
* @return string
*/
private function getNamespace(): string
{
$position = strrpos($this->fqcn, '\\');
if ($position === false) {
throw new InvalidArgumentException("Invalid fqcn: `$this->fqcn` must contain \\");
}
return substr($this->fqcn, 0, $position);
}

/**
* @return string
*/
private function getClass(): string
{
return substr($this->fqcn, strrpos($this->fqcn, '\\') + 1);
}

/**
* @param string[] $targets
* @return array<string, string>
*/
private function generateContainers(array $targets): array
{
$this->generateMap($targets);
$containers = [];
foreach (array_merge($targets, $this->shared, $this->alias) as $target) {
$containers[$target] = $this->buildContainer($target);
}
return $containers;
}

/**
* @param array<string> $containers
*/
private function generateMap(array $containers): void
{
foreach ($containers as $container) {
if (array_key_exists($container, $this->alias)) {
$container = $this->alias[$container];
}
if (
array_key_exists($container, $this->params)
|| array_key_exists($container, $this->definitions)
|| array_key_exists($container, $this->shared)
) {
continue;
}
if (array_key_exists($container, $this->dependenciesMap)) {
$this->shared[] = $container;
continue;
}
if ($this->reflection->isInstantiable($container)) {
$this->dependenciesMap[$container] = $this->reflection->getDependencies($container);
/** @var array<string> $new_containers */
$new_containers = [];
foreach ($this->dependenciesMap[$container] as $dependency) {
$new_containers[] = array_key_exists($dependency['parameter'], $this->alias) ?
$dependency['parameter'] : $dependency['name'];
}
$this->generateMap($new_containers);
continue;
}
throw new UnexpectedValueException("`$container` is cant be resolved");
}
}

private function buildContainer(string $target): string
{
if (
(in_array($target, $this->shared) && count($this->stack) !== 0)
|| array_key_exists($target, $this->alias)
|| array_key_exists($target, $this->definitions)
|| array_key_exists($target, $this->params)
) {
return "\$this->get('$target')";
}
$this->stack[] = $target;
$container = "new \\$target(";
if (array_key_exists($target, $this->dependenciesMap)) {
foreach ($this->dependenciesMap[$target] as $dependency) {
$name = array_key_exists(
$dependency['parameter'],
$this->alias
) ? $dependency['parameter'] : $dependency['name'];
$variadic = $dependency['variadic'] === true ? '...' : '';
$container .= "$variadic{$this->buildContainer($name)}, ";
}
}
$container .= ')';
array_pop($this->stack);
return $container;
}
}
10 changes: 5 additions & 5 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Cekta\DI\Strategy\Autowiring;
use Cekta\DI\Strategy\Definition;
use Cekta\DI\Strategy\Implementation;
use Cekta\DI\Strategy\Alias;
use Cekta\DI\Strategy\KeyValue;
use Psr\Container\ContainerInterface;

Expand All @@ -14,16 +14,16 @@ class Container implements ContainerInterface

/**
* @param array<string, mixed> $params
* @param array<string, string> $interfaces
* @param array<string, string> $alias
* @param array<string, callable> $definitions
*/
public function __construct(array $params = [], array $interfaces = [], array $definitions = [])
public function __construct(array $params = [], array $alias = [], array $definitions = [])
{
$this->container = new ArrayCache(new InfiniteRecursionDetector(new Strategy(
new KeyValue($params),
new Definition($definitions, $this),
new Implementation($interfaces, $this),
new Autowiring(new Reflection($this), $this),
new Alias($alias, $this),
new Autowiring(new Reflection(), $this, $alias),
)));
}

Expand Down
118 changes: 2 additions & 116 deletions src/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

namespace Cekta\DI;

use InvalidArgumentException;
use Psr\Container\ContainerInterface;
use ReflectionException;
use UnexpectedValueException;

/**
Expand All @@ -24,18 +22,7 @@ class ContainerBuilder
* @var array<string, callable>
*/
private array $definitions = [];
/**
* @var array<string>
*/
private array $shared = [];

/**
* @var array<string, array<array{'name': string, 'variadic': bool}>>
*/
private array $dependenciesMap = [];
private string $fqcn = 'App\Container';
private Reflection $reflection;
private array $stack = [];

public function build(): ContainerInterface
{
Expand Down Expand Up @@ -88,111 +75,10 @@ public function fqcn(string $fqcn): self
/**
* @param array<string> $containers
* @return string|false
* @throws ReflectionException
*/
public function compile(array $containers): string|false
{
$this->reflection = new Reflection(new Container($this->params, $this->alias, $this->definitions));
$namespace = $this->getNamespace();
$class = $this->getClass();
$alias = $this->alias;
$definitions = $this->definitions;
$containers = $this->generateContainers($containers);
ob_start();
include __DIR__ . '/../template/container.compiler';
return ob_get_clean();
}

/**
* @return string
*/
private function getNamespace(): string
{
$position = strrpos($this->fqcn, '\\');
if ($position === false) {
throw new InvalidArgumentException("Invalid fqcn: `$this->fqcn` must contain \\");
}
return substr($this->fqcn, 0, $position);
}

/**
* @return string
*/
private function getClass(): string
{
return substr($this->fqcn, strrpos($this->fqcn, '\\') + 1);
}

/**
* @param string[] $targets
* @return array<string, string>
* @throws ReflectionException
*/
private function generateContainers(array $targets): array
{
$this->generateMap($targets);
$containers = [];
foreach (array_merge($targets, $this->shared, $this->alias) as $target) {
$containers[$target] = $this->buildContainer($target);
}
return $containers;
}

/**
* @param array<string> $containers
* @throws ReflectionException
*/
private function generateMap(array $containers): void
{
foreach ($containers as $container) {
if (array_key_exists($container, $this->alias)) {
$container = $this->alias[$container];
}
if (
array_key_exists($container, $this->params)
|| array_key_exists($container, $this->definitions)
|| array_key_exists($container, $this->shared)
) {
continue;
}
if (array_key_exists($container, $this->dependenciesMap)) {
$this->shared[] = $container;
continue;
}
if ($this->reflection->isInstantiable($container)) {
$this->dependenciesMap[$container] = $this->reflection->getDependencies($container);
/** @var array<string> $new_containers */
$new_containers = array_map(static function (array $container) {
return $container['name'];
}, $this->dependenciesMap[$container]);
$this->generateMap($new_containers);
continue;
}
throw new UnexpectedValueException("`$container` is cant be resolved");
}
}

private function buildContainer(string $target): string
{
if (
(in_array($target, $this->shared) && count($this->stack) !== 0)
|| array_key_exists($target, $this->alias)
|| array_key_exists($target, $this->definitions)
|| array_key_exists($target, $this->params)
) {
return "\$this->get('$target')";
}
$this->stack[] = $target;
$container = "new \\$target(";
if (array_key_exists($target, $this->dependenciesMap)) {
foreach ($this->dependenciesMap[$target] as $dependency) {
$name = $dependency['name'];
$variadic = $dependency['variadic'] === true ? '...' : '';
$container .= "$variadic{$this->buildContainer($name)}, ";
}
}
$container .= ')';
array_pop($this->stack);
return $container;
$compiler = new Compiler($this->params, $this->alias, $this->definitions, $this->fqcn);
return $compiler($containers);
}
}
2 changes: 1 addition & 1 deletion src/Exception/InfiniteRecursion.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ class InfiniteRecursion extends RuntimeException implements ContainerExceptionIn
public function __construct(string $id, array $calls)
{
$callsString = implode(', ', $calls);
parent::__construct("Infinite recursion for `{$id}`, calls: `{$callsString}`");
parent::__construct("Infinite recursion for `$id`, calls: `$callsString`");
}
}
2 changes: 1 addition & 1 deletion src/Exception/NotFound.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ class NotFound extends RuntimeException implements NotFoundExceptionInterface
{
public function __construct(string $id)
{
parent::__construct("Container `{$id}` not found");
parent::__construct("Container `$id` not found");
}
}
Loading

0 comments on commit 05efa17

Please sign in to comment.