CallableBinding   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 82
Duplicated Lines 0 %

Importance

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

2 Methods

Rating   Name   Duplication   Size   Complexity  
A resolve() 0 22 6
A __construct() 0 14 3
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 ReflectionException;
10
use ReflectionFunction;
11
12
/**
13
 * A binding that uses a callable to resolve
14
 * to the underlying value.
15
 *
16
 * @author Fabian Böttcher <[email protected]>
17
 * @since 0.1.0
18
 */
19
class CallableBinding 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 string The class / interface type the concrete is bound to.
30
     */
31
    protected $abstract;
32
33
    /**
34
     * @var ReflectionFunction The reflection of the callable that returns the underlying value.
35
     */
36
    protected $concrete;
37
38
    /**
39
     * @var string[] The callable's parameter types.
40
     */
41
    protected $parameters = [];
42
43
    /**
44
     * @var bool|mixed Whether a singleton should be used or the singleton itself.
45
     */
46
    protected $singleton;
47
48
    /**
49
     * Constructs the CallableBinding.
50
     *
51
     * @param Container $container The container that uses this binding.
52
     * @param string $abstract The class / interface type the concrete is bound to.
53
     * @param callable $concrete The callable that returns the underlying value.
54
     * @param bool $singleton Whether or not to use the underlying value as singleton.
55
     *
56
     * @throws ContainerException If the provided callable does not satisfy the requirements.
57
     */
58
    public function __construct(Container $container, string $abstract, callable $concrete, bool $singleton)
59
    {
60
        $this->container = $container;
61
        $this->abstract = $abstract;
62
        $this->singleton = $singleton;
63
64
        try {
65
            $this->concrete = new ReflectionFunction($concrete);
66
        } catch (ReflectionException $e) {
67
            throw new ContainerException("Cannot create binding for '{$abstract}', failed to create reflection of the callable.");
68
        }
69
70
        if (!$this->validateDiParameters($this->concrete->getParameters(), $this->parameters)) {
71
            throw new ContainerException("Cannot create binding for '{$abstract}', all parameters of the callable must be type hinted with classes.");
72
        }
73
    }
74
75
    /**
76
     * @throws ContainerException If resolving fails.
77
     * @inheritDoc
78
     */
79
    public function resolve()
80
    {
81
        if ($this->singleton && $this->singleton !== true) {
82
            return $this->singleton;
83
        }
84
85
        $parameters = $this->resolveDiParameters($this->container, $this->parameters);
86
        $concrete = $this->concrete->invokeArgs($parameters);
87
88
        if (!is_object($concrete)) {
89
            throw new ContainerException("Failed resolving of callable bound to '{$this->abstract}', the callable must return an object.");
90
        }
91
92
        if (!($concrete instanceof $this->abstract)) {
93
            throw new ContainerException("Failed resolving of callable bound to '{$this->abstract}', the return value must be an instance of '{$this->abstract}'.");
94
        }
95
96
        if ($this->singleton === true) {
97
            $this->singleton = $concrete;
98
        }
99
100
        return $concrete;
101
    }
102
}
103