Completed
Push — 1.0 ( 72442c...fc9602 )
by David
04:09 queued 01:58
created

ServiceProviderLoader::discover()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 11
rs 9.4285
cc 3
eloc 7
nc 3
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace TheCodingMachine\Yaco\ServiceProvider;
5
6
7
use Puli\Discovery\Api\Discovery;
8
use Puli\Discovery\Binding\ClassBinding;
9
use TheCodingMachine\Yaco\Compiler;
10
use TheCodingMachine\Yaco\Definition\AliasDefinition;
11
use TheCodingMachine\Yaco\Definition\FactoryCallDefinition;
12
use TheCodingMachine\Yaco\Definition\Reference;
13
14
class ServiceProviderLoader
15
{
16
    /**
17
     * @var Compiler
18
     */
19
    private $compiler;
20
21
    /**
22
     * @param Compiler $compiler
23
     */
24
    public function __construct(Compiler $compiler)
25
    {
26
        $this->compiler = $compiler;
27
    }
28
29
    /**
30
     * Discovers service provider class names using Puli.
31
     *
32
     * @param Discovery $discovery
33
     * @return string[] Returns an array of service providers.
34
     */
35
    public function discover(Discovery $discovery) : array {
36
        $bindings = $discovery->findBindings('container-interop/service-provider');
37
        $serviceProviders = [];
38
39
        foreach ($bindings as $binding) {
40
            if ($binding instanceof ClassBinding) {
41
                $serviceProviders[] = $binding->getClassName();
42
            }
43
        }
44
        return $serviceProviders;
45
    }
46
47
    /**
48
     * Discovers and loads the service providers using Puli.
49
     *
50
     * @param Discovery $discovery
51
     */
52
    public function discoverAndLoad(Discovery $discovery) {
53
        $serviceProviders = $this->discover($discovery);
54
55
        foreach ($serviceProviders as $serviceProvider) {
56
            $this->load($serviceProvider);
57
        }
58
    }
59
60
    public function load(string $serviceProviderClassName)
61
    {
62
        if (!class_exists($serviceProviderClassName)) {
63
            throw new InvalidArgumentException(sprintf('ServiceProviderLoader::load expects a valid class name. Could not find class "%s"', $serviceProviderClassName));
64
        }
65
66
        $serviceFactories = call_user_func([$serviceProviderClassName, 'getServices']);
67
68
        foreach ($serviceFactories as $serviceName => $methodName) {
69
            $this->registerService($serviceName, $serviceProviderClassName, $methodName);
70
        }
71
    }
72
73
    private function registerService(string $serviceName, string $className, string $methodName) {
74
        if (!$this->compiler->has($serviceName)) {
75
            $factoryDefinition = new FactoryCallDefinition($serviceName, $className, $methodName, [new ContainerDefinition()]);
0 ignored issues
show
Documentation introduced by
$className is of type string, but the function expects a object<TheCodingMachine\...ion\ReferenceInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
76
77
            $this->compiler->addDumpableDefinition($factoryDefinition);
78
        } else {
79
            // The new service will be created under the name 'xxx_decorated_y'
80
            // The old service will be moved to the name 'xxx_decorated_y.inner'
81
            // This old service will be accessible through a callback represented by 'xxx_decorated_y.callbackwrapper'
82
            // The $servicename becomes an alias pointing to 'xxx_decorated_y'
83
84
            $previousDefinition = $this->compiler->getDumpableDefinition($serviceName);
85
            while ($previousDefinition instanceof Reference) {
86
                $previousDefinition = $this->compiler->getDumpableDefinition($previousDefinition->getAlias());
87
            }
88
89
            $oldServiceName = $serviceName;
90
            $serviceName = $this->getDecoratedServiceName($serviceName);
91
            $innerName = $serviceName.'.inner';
92
            $callbackWrapperName = $serviceName.'.callbackwrapper';
93
94
            $innerDefinition = new FactoryCallDefinition($innerName, $previousDefinition->getFactory(), $previousDefinition->getMethodName(), $previousDefinition->getMethodArguments());
95
96
97
            $callbackWrapperDefinition = new CallbackWrapperDefinition($callbackWrapperName, $innerDefinition);
98
99
            $factoryDefinition = new FactoryCallDefinition($serviceName, $className, $methodName, [new ContainerDefinition(), $callbackWrapperDefinition]);
0 ignored issues
show
Documentation introduced by
$className is of type string, but the function expects a object<TheCodingMachine\...ion\ReferenceInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
100
101
102
            $this->compiler->addDumpableDefinition($factoryDefinition);
103
            $this->compiler->addDumpableDefinition($innerDefinition);
104
            $this->compiler->addDumpableDefinition($callbackWrapperDefinition);
105
            $this->compiler->addDumpableDefinition(new AliasDefinition($oldServiceName, $serviceName));
106
        }
107
108
    }
109
110
    private function getDecoratedServiceName(string $serviceName) : string {
111
        $counter = 1;
112
        while ($this->compiler->has($serviceName.'_decorated_'.$counter)) {
113
            $counter++;
114
        }
115
        return $serviceName.'_decorated_'.$counter;
116
    }
117
}