Completed
Push — master ( 41a1dd...1768a7 )
by Jesse
02:09
created

src/AutoWiring.php (2 issues)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stratadox\Di;
6
7
use Psr\Container\ContainerInterface;
0 ignored issues
show
This use statement conflicts with another class in this namespace, Stratadox\Di\ContainerInterface. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use ReflectionClass;
9
10
final class AutoWiring implements ContainerInterface
11
{
12
    private $container;
13
    private $links;
14
15
    private function __construct(ContainerInterface $container, array $links)
16
    {
17
        $this->container = $container;
18
        $this->links = $links;
19
    }
20
21
    public static function the(ContainerInterface $container) : self
22
    {
23
        return new self($container, []);
24
    }
25
26
    public function link(string $interface, string $class) : self
27
    {
28
        return new self($this->container, [$interface => $class] + $this->links);
29
    }
30
31
    public function get($theService)
32
    {
33
        if (!$this->container->has($theService)) {
34
            $this->resolve($theService);
35
        }
36
        return $this->container->get($theService);
37
    }
38
39
    public function has($theService) : bool
40
    {
41
        return class_exists($theService) || interface_exists($theService);
42
    }
43
44
    private function resolve(string $service)
45
    {
46
        if (interface_exists($service)) {
47
            $this->resolveInterface($service);
48
        } else {
49
            $this->resolveClass($service);
50
        }
51
    }
52
53
    private function resolveInterface(string $service)
54
    {
55
        $class = $this->links[$service];
56
        $this->resolveClass($class);
57
        $this->container->set($service, function () use ($class) {
0 ignored issues
show
The method set() does not exist on Psr\Container\ContainerInterface. It seems like you code against a sub-type of Psr\Container\ContainerInterface such as Stratadox\Di\Container. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

57
        $this->container->/** @scrutinizer ignore-call */ 
58
                          set($service, function () use ($class) {
Loading history...
58
            return $this->container->get($class);
59
        });
60
    }
61
62
    private function resolveClass(string $service)
63
    {
64
        $constructor = (new ReflectionClass($service))->getConstructor();
65
        $dependencies = [];
66
        if (isset($constructor)) {
67
            foreach ($constructor->getParameters() as $parameter) {
68
                $dependency = (string) $parameter->getType();
69
                $this->resolve($dependency);
70
                $dependencies[] = $dependency;
71
            }
72
        }
73
        $this->container->set($service,
74
            function () use ($service, $dependencies) {
75
                $parameters = [];
76
                foreach ($dependencies as $dependency) {
77
                    $parameters[] = $this->container->get($dependency);
78
                }
79
                return new $service(...$parameters);
80
            }
81
        );
82
    }
83
}
84