Invokable   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 92.31%

Importance

Changes 0
Metric Value
dl 0
loc 118
ccs 36
cts 39
cp 0.9231
rs 10
c 0
b 0
f 0
wmc 22
lcom 1
cbo 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A callable() 0 4 1
A isFunction() 0 4 1
A reflection() 0 10 3
C normalizeCallable() 0 37 11
A reflectionCallable() 0 11 4
1
<?php declare(strict_types = 1);
2
3
namespace Venta\Container;
4
5
use Closure;
6
use InvalidArgumentException;
7
use ReflectionFunction;
8
use ReflectionFunctionAbstract;
9
use ReflectionMethod;
10
11
/**
12
 * Class Invokable
13
 *
14
 * @package Venta\Container
15
 */
16
class Invokable
17
{
18
19
    /**
20
     * @var callable
21
     */
22
    private $callable;
23
24
    /**
25
     * @var ReflectionFunctionAbstract
26
     */
27
    private $reflection;
28
29
    /**
30
     * ReflectedCallable constructor.
31
     *
32
     * @param callable|ReflectionFunctionAbstract $callable
33
     */
34 62
    public function __construct($callable)
35
    {
36 62
        if ($callable instanceof ReflectionFunctionAbstract) {
37 13
            $this->callable = $this->reflectionCallable($callable);
38 13
            $this->reflection = $callable;
39
        } else {
40 53
            $this->callable = $this->normalizeCallable($callable);
41
        }
42 55
    }
43
44
    /**
45
     * @return callable
46
     */
47 49
    public function callable()
48
    {
49 49
        return $this->callable;
50
    }
51
52
    /**
53
     * @return bool
54
     */
55 40
    public function isFunction(): bool
56
    {
57 40
        return !is_array($this->callable);
58
    }
59
60
    /**
61
     * @return ReflectionFunctionAbstract|ReflectionMethod|ReflectionFunction
62
     */
63 39
    public function reflection(): ReflectionFunctionAbstract
64
    {
65 39
        if (empty($this->reflection)) {
66 30
            $this->reflection = $this->isFunction()
67 13
                ? new ReflectionFunction($this->callable)
68 17
                : new ReflectionMethod($this->callable[0], $this->callable[1]);
69
        }
70
71 39
        return $this->reflection;
72
    }
73
74
    /**
75
     * @param $callable
76
     * @return callable
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use Closure|string|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
77
     * @throws InvalidArgumentException
78
     */
79 53
    private function normalizeCallable($callable)
80
    {
81 53
        if (is_object($callable)) {
82 18
            if ($callable instanceof Closure) {
83 13
                return $callable;
84
            } else {
85 5
                if (!method_exists($callable, '__invoke')) {
86 1
                    throw new InvalidArgumentException('Invalid callable provided.');
87
                }
88
89
                // Callable object is an instance with magic __invoke() method.
90 4
                return [$callable, '__invoke'];
91
            }
92
        }
93
94 36
        if (is_string($callable)) {
95
            // Existing function is always callable.
96 22
            if (function_exists($callable)) {
97 7
                return $callable;
98
            }
99 15
            if (method_exists($callable, '__invoke')) {
100
                // We allow to call class by name if `__invoke()` method is implemented.
101 2
                return [$callable, '__invoke'];
102
            }
103 13
            if (strpos($callable, '::') !== false) {
104
                // Replace "ClassName::methodName" string with ["ClassName", "methodName"] array.
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
105 10
                $callable = explode('::', $callable);
106
            }
107
        }
108
109
        // Is correct callable array.
110 28
        if (is_array($callable) && isset($callable[0], $callable[1]) && method_exists($callable[0], $callable[1])) {
111 22
            return $callable;
112
        }
113
114 7
        throw new InvalidArgumentException('Invalid callable provided.');
115
    }
116
117
    /**
118
     * @param ReflectionFunctionAbstract $reflection
119
     * @return array|Closure|string
120
     * @throws InvalidArgumentException
121
     */
122 13
    private function reflectionCallable(ReflectionFunctionAbstract $reflection)
123
    {
124 13
        if ($reflection instanceof ReflectionMethod) {
125 13
            return [$reflection->class, $reflection->name];
126
        }
127
        if ($reflection instanceof ReflectionFunction) {
128
            return $reflection->isClosure() ? $reflection->getClosure() : $reflection->name;
129
        }
130
131
        throw new InvalidArgumentException('Invalid callable provided.');
132
    }
133
}