Resolver   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 105
Duplicated Lines 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 55
c 1
b 1
f 0
dl 0
loc 105
rs 10
wmc 30

2 Methods

Rating   Name   Duplication   Size   Complexity  
B autowireArguments() 0 38 10
D autowireArgument() 0 45 20
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Biurad opensource projects.
7
 *
8
 * PHP version 7.2 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Biurad\DependencyInjection;
19
20
use Nette\DI\MissingServiceException;
21
use Nette\DI\Resolver as NetteResolver;
22
use Nette\DI\ServiceCreationException;
23
use Nette\Utils\Reflection;
24
use ReflectionFunctionAbstract;
25
use ReflectionParameter;
26
27
/**
28
 * Services resolver
29
 *
30
 * @internal
31
 */
32
class Resolver extends NetteResolver
33
{
34
    /**
35
     * Add missing arguments using autowiring.
36
     *
37
     * @param ReflectionFunctionAbstract $method
38
     * @param mixed[] $arguments
39
     * @param (callable(string $type, bool $single): object|object[]|null) $getter
0 ignored issues
show
Documentation Bug introduced by
The doc comment (callable(string at position 1 could not be parsed: Expected ')' at position 1, but found 'callable'.
Loading history...
40
     *
41
     * @return mixed[]
42
     */
43
    public static function autowireArguments(
44
        ReflectionFunctionAbstract $method,
45
        array $arguments,
46
        callable $getter
47
    ): array {
48
        $optCount = 0;
49
        $num = -1;
50
        $res = [];
51
52
        foreach ($method->getParameters() as $num => $param) {
53
            $paramName = $param->name;
54
            if (!$param->isVariadic() && array_key_exists($paramName, $arguments)) {
55
                $res[$num] = $arguments[$paramName];
56
                unset($arguments[$paramName], $arguments[$num]);
57
            } elseif (array_key_exists($num, $arguments)) {
58
                $res[$num] = $arguments[$num];
59
                unset($arguments[$num]);
60
            } else {
61
                $res[$num] = self::autowireArgument($param, $getter);
62
            }
63
64
            $optCount = $param->isOptional() && $res[$num] === ($param->isDefaultValueAvailable() ? Reflection::getParameterDefaultValue($param) : null)
65
                ? $optCount + 1
66
                : 0;
67
        }
68
69
        // extra parameters
70
        while (array_key_exists(++$num, $arguments)) {
71
            $res[$num] = $arguments[$num];
72
            unset($arguments[$num]);
73
            $optCount = 0;
74
        }
75
76
        if ($optCount) {
77
            $res = array_slice($res, 0, -$optCount);
78
        }
79
80
        return $res;
81
    }
82
83
    /**
84
	 * Resolves missing argument using autowiring.
85
     * @param ReflectionParameter $parameter
86
	 * @param (callable(string $type, bool $single): object|object[]|null)  $getter
0 ignored issues
show
Documentation Bug introduced by
The doc comment (callable(string at position 1 could not be parsed: Expected ')' at position 1, but found 'callable'.
Loading history...
87
     *
88
	 * @throws ServiceCreationException
89
     *
90
	 * @return mixed
91
	 */
92
	private static function autowireArgument(ReflectionParameter $parameter, callable $getter)
93
	{
94
		$type = Reflection::getParameterType($parameter);
95
		$method = $parameter->getDeclaringFunction();
96
		$desc = Reflection::toString($parameter);
97
98
		if ($type && !Reflection::isBuiltinType($type)) {
99
			try {
100
				$res = $getter($type, true);
101
			} catch (MissingServiceException $e) {
102
				$res = null;
103
			} catch (ServiceCreationException $e) {
104
				throw new ServiceCreationException("{$e->getMessage()} (needed by $desc)", 0, $e);
105
			}
106
            
107
			if ($res !== null || $parameter->allowsNull()) {
108
				return $res;
109
			} elseif (class_exists($type) || interface_exists($type)) {
110
				throw new ServiceCreationException("Service of type $type needed by $desc not found. Did you add it to configuration file?");
111
			} else {
112
				throw new ServiceCreationException("Class $type needed by $desc not found. Check type hint and 'use' statements.");
113
			}
114
115
		} elseif (
116
			$method instanceof \ReflectionMethod
117
			&& $type === 'array'
118
			&& preg_match('#@param[ \t]+([\w\\\\]+)\[\][ \t]+\$' . $parameter->name . '#', (string) $method->getDocComment(), $m)
119
			&& ($itemType = Reflection::expandClassName($m[1], $method->getDeclaringClass()))
120
			&& (class_exists($itemType) || interface_exists($itemType))
121
		) {
122
			return $getter($itemType, false);
123
124
		} elseif (
125
			($type && $parameter->allowsNull())
126
			|| $parameter->isOptional()
127
			|| $parameter->isDefaultValueAvailable()
128
		) {
129
			// !optional + defaultAvailable = func($a = null, $b) since 5.4.7
130
			// optional + !defaultAvailable = i.e. Exception::__construct, mysqli::mysqli, ...
131
			return $parameter->isDefaultValueAvailable()
132
				? Reflection::getParameterDefaultValue($parameter)
133
				: null;
134
135
		} else {
136
			throw new ServiceCreationException("Parameter $desc has no class type hint or default value, so its value must be specified.");
137
		}
138
	}
139
}
140