Completed
Push — 3.x ( d1adb1...66933d )
by Grégoire
03:32
created

RouteCollection::resolveElement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Route;
15
16
use Symfony\Component\Routing\Route;
17
18
/**
19
 * @final since sonata-project/admin-bundle 3.52
20
 *
21
 * @author Thomas Rabaix <[email protected]>
22
 */
23
class RouteCollection
24
{
25
    /**
26
     * @var Route[]
27
     */
28
    protected $elements = [];
29
30
    /**
31
     * @var string
32
     */
33
    protected $baseCodeRoute;
34
35
    /**
36
     * @var string
37
     */
38
    protected $baseRouteName;
39
40
    /**
41
     * @var string
42
     */
43
    protected $baseControllerName;
44
45
    /**
46
     * @var string
47
     */
48
    protected $baseRoutePattern;
49
50
    /**
51
     * @var Route[]
52
     */
53
    private $cachedElements = [];
54
55
    /**
56
     * @param string $baseCodeRoute
57
     * @param string $baseRouteName
58
     * @param string $baseRoutePattern
59
     * @param string $baseControllerName
60
     */
61
    public function __construct($baseCodeRoute, $baseRouteName, $baseRoutePattern, $baseControllerName)
62
    {
63
        $this->baseCodeRoute = $baseCodeRoute;
64
        $this->baseRouteName = $baseRouteName;
65
        $this->baseRoutePattern = $baseRoutePattern;
66
        $this->baseControllerName = $baseControllerName;
67
    }
68
69
    /**
70
     * Add route.
71
     *
72
     * @param string $name
73
     * @param string $pattern   Pattern (will be automatically combined with @see $this->baseRoutePattern and $name
74
     * @param string $host
75
     * @param string $condition
76
     *
77
     * @return RouteCollection
78
     */
79
    public function add(
80
        $name,
81
        $pattern = null,
82
        array $defaults = [],
83
        array $requirements = [],
84
        array $options = [],
85
        $host = '',
86
        array $schemes = [],
87
        array $methods = [],
88
        $condition = ''
89
    ) {
90
        $pattern = $this->baseRoutePattern.'/'.($pattern ?: $name);
91
        $code = $this->getCode($name);
92
        $routeName = $this->baseRouteName.'_'.$name;
93
94
        if (!isset($defaults['_controller'])) {
95
            $actionJoiner = false === strpos($this->baseControllerName, '\\') ? ':' : '::';
96
            if (':' !== $actionJoiner && false !== strpos($this->baseControllerName, ':')) {
97
                $actionJoiner = ':';
98
            }
99
100
            $defaults['_controller'] = $this->baseControllerName.$actionJoiner.$this->actionify($code);
101
        }
102
103
        if (!isset($defaults['_sonata_admin'])) {
104
            $defaults['_sonata_admin'] = $this->baseCodeRoute;
105
        }
106
107
        $defaults['_sonata_name'] = $routeName;
108
109
        $element = static function () use ($pattern, $defaults, $requirements, $options, $host, $schemes, $methods, $condition) {
110
            return new Route($pattern, $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
111
        };
112
        $this->addElement($code, $element);
113
114
        return $this;
115
    }
116
117
    /**
118
     * @param string $name
119
     *
120
     * @return string
121
     */
122
    public function getCode($name)
123
    {
124
        if (false !== strrpos($name, '.')) {
125
            return $name;
126
        }
127
128
        return $this->baseCodeRoute.'.'.$name;
129
    }
130
131
    /**
132
     * @return RouteCollection
133
     */
134
    public function addCollection(self $collection)
135
    {
136
        foreach ($collection->getElements() as $code => $element) {
137
            $this->addElement($code, $element);
138
        }
139
140
        return $this;
141
    }
142
143
    /**
144
     * @return Route[]
145
     */
146
    public function getElements()
147
    {
148
        foreach ($this->elements as $code => $element) {
149
            $this->resolveElement($code);
150
        }
151
152
        return $this->elements;
153
    }
154
155
    /**
156
     * @param string $name
157
     *
158
     * @return bool
159
     */
160
    public function has($name)
161
    {
162
        return \array_key_exists($this->getCode($name), $this->elements);
163
    }
164
165
    final public function hasCached(string $name): bool
166
    {
167
        return \array_key_exists($this->getCode($name), $this->cachedElements);
168
    }
169
170
    /**
171
     * @param string $name
172
     *
173
     * @throws \InvalidArgumentException
174
     *
175
     * @return Route
176
     */
177
    public function get($name)
178
    {
179
        if ($this->has($name)) {
180
            $code = $this->getCode($name);
181
            $this->resolveElement($code);
182
183
            return $this->elements[$code];
184
        }
185
186
        throw new \InvalidArgumentException(sprintf('Element "%s" does not exist.', $name));
187
    }
188
189
    /**
190
     * @param string $name
191
     *
192
     * @return RouteCollection
193
     */
194
    public function remove($name)
195
    {
196
        unset($this->elements[$this->getCode($name)]);
197
198
        return $this;
199
    }
200
201
    /**
202
     * @throws \InvalidArgumentException
203
     */
204
    final public function restore(string $name): self
205
    {
206
        if ($this->hasCached($name)) {
207
            $code = $this->getCode($name);
208
            $this->addElement($code, $this->cachedElements[$code]);
0 ignored issues
show
Documentation introduced by
$this->cachedElements[$code] is of type object<Symfony\Component\Routing\Route>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
209
210
            return $this;
211
        }
212
213
        throw new \InvalidArgumentException(sprintf('Element "%s" does not exist in cache.', $name));
214
    }
215
216
    /**
217
     * Remove all routes except routes in $routeList.
218
     *
219
     * @param string[]|string $routeList
220
     *
221
     * @return RouteCollection
222
     */
223
    public function clearExcept($routeList)
224
    {
225
        if (!\is_array($routeList)) {
226
            $routeList = [$routeList];
227
        }
228
229
        $routeCodeList = [];
230
        foreach ($routeList as $name) {
231
            $routeCodeList[] = $this->getCode($name);
232
        }
233
234
        $elements = $this->elements;
235
        foreach ($elements as $code => $element) {
236
            if (!\in_array($code, $routeCodeList, true)) {
237
                unset($this->elements[$code]);
238
            }
239
        }
240
241
        return $this;
242
    }
243
244
    /**
245
     * Remove all routes.
246
     *
247
     * @return RouteCollection
248
     */
249
    public function clear()
250
    {
251
        $this->elements = [];
252
253
        return $this;
254
    }
255
256
    /**
257
     * Convert a word in to the format for a symfony action action_name => actionName.
258
     *
259
     * @param string $action Word to actionify
260
     *
261
     * @return string Actionified word
262
     */
263
    public function actionify($action)
264
    {
265
        if (false !== ($pos = strrpos($action, '.'))) {
266
            $action = substr($action, $pos + 1);
267
        }
268
269
        // if this is a service rather than just a controller name, the suffix
270
        // Action is not automatically appended to the method name
271
        if (false === strpos($this->baseControllerName, ':')) {
272
            $action .= 'Action';
273
        }
274
275
        return lcfirst(str_replace(' ', '', ucwords(strtr($action, '_-', '  '))));
276
    }
277
278
    /**
279
     * @return string
280
     */
281
    public function getBaseCodeRoute()
282
    {
283
        return $this->baseCodeRoute;
284
    }
285
286
    /**
287
     * @return string
288
     */
289
    public function getBaseControllerName()
290
    {
291
        return $this->baseControllerName;
292
    }
293
294
    /**
295
     * @return string
296
     */
297
    public function getBaseRouteName()
298
    {
299
        return $this->baseRouteName;
300
    }
301
302
    /**
303
     * @return string
304
     */
305
    public function getBaseRoutePattern()
306
    {
307
        return $this->baseRoutePattern;
308
    }
309
310
    /**
311
     * @param Route|callable $element
312
     */
313
    final protected function addElement(string $code, $element): void
314
    {
315
        $this->elements[$code] = $element;
316
        $this->updateCachedElement($code);
317
    }
318
319
    final protected function updateCachedElement(string $code): void
320
    {
321
        $this->cachedElements[$code] = $this->elements[$code];
322
    }
323
324
    private function resolveElement(string $code): void
325
    {
326
        $element = $this->elements[$code];
327
328
        if (\is_callable($element)) {
329
            $this->elements[$code] = $element();
330
            $this->updateCachedElement($code);
331
        }
332
    }
333
}
334