Completed
Push — master ( bac48d...74ca95 )
by Théo
10s
created

BaseParametersResolver::resolveString()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 0
loc 22
ccs 7
cts 7
cp 1
rs 9.2
cc 3
eloc 15
nc 2
nop 2
crap 3
1
<?php
2
3
/*
4
 * This file is part of the LaravelYaml package.
5
 *
6
 * (c) Théo FIDRY <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Fidry\LaravelYaml\DependencyInjection\Resolver;
13
14
use Fidry\LaravelYaml\Exception\DependencyInjection\Resolver\ParameterCircularReferenceException;
15
use Fidry\LaravelYaml\Exception\DependencyInjection\Resolver\RuntimeException;
16
use Fidry\LaravelYaml\Exception\Exception;
17
use Fidry\LaravelYaml\Exception\ParameterNotFoundException;
18
use Illuminate\Contracts\Config\Repository as ConfigRepository;
19
use Symfony\Component\ExpressionLanguage\Expression;
20
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
21
22
/**
23
 * @author Théo FIDRY <[email protected]>
24
 */
25
final class BaseParametersResolver implements ParametersResolverInterface
26
{
27
    /**
28
     * @var ConfigRepository
29
     */
30
    private $config;
31
32
    /**
33
     * @var string
34
     */
35
    private $defaultValue;
36
37
    /**
38
     * @var ExpressionLanguage|null
39
     */
40
    private $expressionLanguage;
41
42
    /**
43
     * @var array|null
44 6
     */
45
    private $parameters;
46 6
47 6
    /**
48 6
     * @var array
49
     */
50
    private $resolved = [];
51
52
    public function __construct(ConfigRepository $config)
53
    {
54
        $this->config = $config;
55
        $this->defaultValue = spl_object_hash(new \stdClass());
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     *
61 6
     * @param array $parameters
62
     *
63 6
     * @return array
64 6
     *
65 6
     * @throws ParameterCircularReferenceException
66 2
     * @throws ParameterNotFoundException
67 1
     * @throws Exception
68
     */
69 2
    public function resolve(array $parameters)
70
    {
71
        $this->parameters = $parameters;
72
        foreach ($this->parameters as $key => $value) {
73
            $resolving = $this->addToResolving([], $key);
74
            $value = $this->resolveValue($value, $resolving);
75
            $this->resolved[$key] = $value;
76
        }
77
78
        return $this->resolved;
79
    }
80 6
81
    /**
82 6
     * @param mixed $value
83 2
     * @param array $resolving
84
     *
85
     * @return mixed
86 6
     * @throws ParameterCircularReferenceException
87 2
     * @throws ParameterNotFoundException
88
     */
89
    private function resolveValue($value, $resolving = [])
90 6
    {
91 6
        if (is_bool($value) || is_numeric($value)) {
92
            return $value;
93
        }
94 2
95
        if (is_array($value)) {
96
            return $this->resolveArray($value, $resolving);
97 2
        }
98
        
99 2
        if ($value instanceof Expression) {
100 2
            return $this->getExpressionLanguage()->evaluate($value, array('container' => $this));
101 2
        }
102 1
103
        if (is_string($value)) {
104 2
            return $this->resolveString($value, $resolving);
105
        }
106
107
        return $value;
108
    }
109
110 View Code Duplication
    private function resolveArray(array $arrayValue, array $resolving)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
111
    {
112
        $resolvedValue = [];
113
        foreach ($arrayValue as $key => $value) {
114
            $resolvedValue[$key] = $this->resolveValue($value, $resolving);
115 6
        }
116
117 6
        return $resolvedValue;
118 2
    }
119 2
120
    /**
121
     * @param $value
122
     * @param $resolving
123
     *
124 6
     * @return array|mixed
125
     * @throws ParameterCircularReferenceException
126
     * @throws ParameterNotFoundException
127 6
     */
128 4
    private function resolveString($value, array $resolving)
129
    {
130
        if (preg_match('/^%([^%\s]+)%$/', $value, $match)) {
131 4
            $key = $match[1];
132 2
            $resolving = $this->addToResolving($resolving, $key);
133
            return $this->resolveStringKey($key, $resolving);
134
        }
135 4
        $self = $this;
136
        return preg_replace_callback(
137
            '/%%|%([^%\s]+)%/',
138
            function ($match) use ($self, $resolving, $value) {
139
                // skip %%
140
                if (false === isset($match[1])) {
141
                    return '%%';
142
                }
143
                $key = $match[1];
144
                $resolving = $this->addToResolving($resolving, $key);
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $resolving, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
145
                return $this->resolveStringKey($key, $resolving);
146 4
            },
147
            $value
148 4
        );
149 2
    }
150
    
151
    /**
152 4
     * @param $value
153 2
     * @param $resolving
154 1
     *
155 2
     * @return array|mixed
156 1
     * @throws ParameterCircularReferenceException
157 2
     * @throws ParameterNotFoundException
158 1
     */
159 1
    private function resolveStringKey($value, array $resolving)
160
    {
161 4
        if (0 === preg_match('/^%([^%\s]+)%$/', $value, $match)) {
162 4
            if (false === array_key_exists($value, $resolving)) {
163
                return $value;
164 2
            }
165
            $key = $value;
166
        } else {
167
            $key = $match[1];
168
        }
169
        if (array_key_exists($key, $this->parameters)) {
170
            return $this->resolveParameter($key, $resolving);
171
        }
172
        if ($this->config->has($key)) {
173 4
            return $this->config->get($key);
174
        }
175 4
176 4
        return $this->resolveEnvironmentValue($key);
177 4
    }
178 2
179
    /**
180
     * @param string $key
181 2
     * @param array  $resolving
182
     *
183
     * @return array|mixed
184
     * @throws ParameterCircularReferenceException
185
     * @throws ParameterNotFoundException
186
     */
187
    private function resolveParameter($key, array $resolving)
188
    {
189
        if (array_key_exists($key, $this->resolved)) {
190
            return $this->resolved[$key];
191
        }
192
193
        if (array_key_exists($key, $resolving) && $resolving[$key] >= 10) {
194
            throw new ParameterCircularReferenceException(
195
                sprintf(
196
                    'Circular reference detected for the parameter "%s" while resolving [%s]',
197
                    $key,
198
                    implode(', ', array_keys($resolving))
199
                )
200
            );
201
        }
202
        $resolving = $this->addToResolving($resolving, $key);
203
        $this->resolved[$key] = $this->resolveValue($this->parameters[$key], $resolving);
204
205
        return $this->resolved[$key];
206
    }
207
208
    /**
209
     * @param string $key
210
     *
211
     * @return string|int|bool|null
212
     * @throws ParameterNotFoundException
213
     */
214 View Code Duplication
    private function resolveEnvironmentValue($key)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
215
    {
216
        $environmentKey = strtoupper(str_replace('.', '_', $key));
217
        $value = env($environmentKey, $this->defaultValue);
218
        if ($this->defaultValue !== $value) {
219
            return $value;
220
        }
221
222
        throw new ParameterNotFoundException(sprintf('No parameter "%s" found', $key));
223
    }
224
225
    /**
226
     * @return ExpressionLanguage
227
     * @throws RuntimeException
228
     */
229
    private function getExpressionLanguage()
230
    {
231
        if (null === $this->expressionLanguage) {
232
            if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
233
                throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
234
            }
235
            $this->expressionLanguage = new ExpressionLanguage();
236
        }
237
238
        return $this->expressionLanguage;
239
    }
240
241
    /**
242
     * @param array  $resolving
243
     * @param string $key
244
     *
245
     * @return array
246
     */
247
    private function addToResolving(array $resolving, $key)
248
    {
249
        if (array_key_exists($key, $resolving)) {
250
            $resolving[$key]++;
251
        } else {
252
            $resolving[$key] = 1;
253
        }
254
        return $resolving;
255
    }
256
}
257