ClassBinding   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 82
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 13
eloc 28
c 1
b 0
f 0
dl 0
loc 82
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A resolve() 0 14 4
B __construct() 0 27 9
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cakasim\Payone\Sdk\Container\Binding;
6
7
use Cakasim\Payone\Sdk\Container\Container;
8
use Cakasim\Payone\Sdk\Container\ContainerException;
9
use ReflectionClass;
10
use ReflectionException;
11
12
/**
13
 * A binding that uses a class whose instance
14
 * will be the the underlying value.
15
 *
16
 * @author Fabian Böttcher <[email protected]>
17
 * @since 0.1.0
18
 */
19
class ClassBinding implements BindingInterface
20
{
21
    use ProvidesParameterDi;
22
23
    /**
24
     * @var Container The container that uses this binding.
25
     */
26
    protected $container;
27
28
    /**
29
     * @var ReflectionClass The reflection of the concrete class whose instance is the underlying value.
30
     */
31
    protected $concrete;
32
33
    /**
34
     * @var string[] The concrete class constructor's parameter types.
35
     */
36
    protected $parameters = [];
37
38
    /**
39
     * @var bool|mixed Whether a singleton should be used or the singleton itself.
40
     */
41
    protected $singleton;
42
43
    /**
44
     * Constructs the ClassBinding.
45
     *
46
     * @param Container $container The container that uses this binding.
47
     * @param string $abstract The class / interface type the concrete is bound to.
48
     * @param string $concrete The class type which is the concrete of the abstract.
49
     * @param bool $singleton Whether or not to use the underlying value as singleton.
50
     *
51
     * @throws ContainerException If the provided abstract or concrete do not satisfy the requirements.
52
     */
53
    public function __construct(Container $container, string $abstract, string $concrete, bool $singleton)
54
    {
55
        $this->container = $container;
56
        $this->singleton = $singleton;
57
58
        if (!interface_exists($abstract) && !class_exists($abstract)) {
59
            throw new ContainerException("Cannot create binding for '{$abstract}', the type '{$abstract}' does not exist.");
60
        }
61
62
        try {
63
            $this->concrete = new ReflectionClass($concrete);
64
        } catch (ReflectionException $e) {
65
            throw new ContainerException("Cannot create binding for '{$abstract}', the type '{$concrete}' does not exist.", $e);
66
        }
67
68
        if ($concrete !== $abstract && !$this->concrete->isSubclassOf($abstract)) {
69
            throw new ContainerException("Cannot create binding for '{$abstract}', the type '{$concrete}' must be an instance of '{$abstract}'.");
70
        }
71
72
        if (!$this->concrete->isInstantiable()) {
73
            throw new ContainerException("Cannot create binding for '{$abstract}', the type '{$concrete}' must be instantiable.");
74
        }
75
76
        $constructor = $this->concrete->getConstructor();
77
78
        if ($constructor && !$this->validateDiParameters($constructor->getParameters(), $this->parameters)) {
79
            throw new ContainerException("Cannot create binding for '{$abstract}', all constructor parameters of '{$concrete}' must have class type hints.");
80
        }
81
    }
82
83
    /**
84
     * @throws ContainerException If resolving fails.
85
     * @inheritDoc
86
     */
87
    public function resolve()
88
    {
89
        if ($this->singleton && $this->singleton !== true) {
90
            return $this->singleton;
91
        }
92
93
        $parameters = $this->resolveDiParameters($this->container, $this->parameters);
94
        $concrete = $this->concrete->newInstanceArgs($parameters);
95
96
        if ($this->singleton === true) {
97
            $this->singleton = $concrete;
98
        }
99
100
        return $concrete;
101
    }
102
}
103