Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Routing;
4
 
5
use ArrayObject;
6
use Closure;
7
use Illuminate\Container\Container;
8
use Illuminate\Contracts\Events\Dispatcher;
9
use Illuminate\Contracts\Routing\BindingRegistrar;
10
use Illuminate\Contracts\Routing\Registrar as RegistrarContract;
11
use Illuminate\Contracts\Support\Arrayable;
12
use Illuminate\Contracts\Support\Jsonable;
13
use Illuminate\Contracts\Support\Responsable;
14
use Illuminate\Database\Eloquent\Model;
15
use Illuminate\Http\JsonResponse;
16
use Illuminate\Http\Request;
17
use Illuminate\Http\Response;
18
use Illuminate\Routing\Events\RouteMatched;
19
use Illuminate\Routing\Events\Routing;
20
use Illuminate\Support\Arr;
21
use Illuminate\Support\Collection;
22
use Illuminate\Support\Str;
23
use Illuminate\Support\Stringable;
24
use Illuminate\Support\Traits\Macroable;
25
use JsonSerializable;
26
use Psr\Http\Message\ResponseInterface as PsrResponseInterface;
27
use ReflectionClass;
28
use stdClass;
29
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
30
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
31
 
32
/**
33
 * @mixin \Illuminate\Routing\RouteRegistrar
34
 */
35
class Router implements BindingRegistrar, RegistrarContract
36
{
37
    use Macroable {
38
        __call as macroCall;
39
    }
40
 
41
    /**
42
     * The event dispatcher instance.
43
     *
44
     * @var \Illuminate\Contracts\Events\Dispatcher
45
     */
46
    protected $events;
47
 
48
    /**
49
     * The IoC container instance.
50
     *
51
     * @var \Illuminate\Container\Container
52
     */
53
    protected $container;
54
 
55
    /**
56
     * The route collection instance.
57
     *
58
     * @var \Illuminate\Routing\RouteCollectionInterface
59
     */
60
    protected $routes;
61
 
62
    /**
63
     * The currently dispatched route instance.
64
     *
65
     * @var \Illuminate\Routing\Route|null
66
     */
67
    protected $current;
68
 
69
    /**
70
     * The request currently being dispatched.
71
     *
72
     * @var \Illuminate\Http\Request
73
     */
74
    protected $currentRequest;
75
 
76
    /**
77
     * All of the short-hand keys for middlewares.
78
     *
79
     * @var array
80
     */
81
    protected $middleware = [];
82
 
83
    /**
84
     * All of the middleware groups.
85
     *
86
     * @var array
87
     */
88
    protected $middlewareGroups = [];
89
 
90
    /**
91
     * The priority-sorted list of middleware.
92
     *
93
     * Forces the listed middleware to always be in the given order.
94
     *
95
     * @var array
96
     */
97
    public $middlewarePriority = [];
98
 
99
    /**
100
     * The registered route value binders.
101
     *
102
     * @var array
103
     */
104
    protected $binders = [];
105
 
106
    /**
107
     * The globally available parameter patterns.
108
     *
109
     * @var array
110
     */
111
    protected $patterns = [];
112
 
113
    /**
114
     * The route group attribute stack.
115
     *
116
     * @var array
117
     */
118
    protected $groupStack = [];
119
 
120
    /**
121
     * All of the verbs supported by the router.
122
     *
123
     * @var string[]
124
     */
125
    public static $verbs = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
126
 
127
    /**
128
     * Create a new Router instance.
129
     *
130
     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
131
     * @param  \Illuminate\Container\Container|null  $container
132
     * @return void
133
     */
134
    public function __construct(Dispatcher $events, Container $container = null)
135
    {
136
        $this->events = $events;
137
        $this->routes = new RouteCollection;
138
        $this->container = $container ?: new Container;
139
    }
140
 
141
    /**
142
     * Register a new GET route with the router.
143
     *
144
     * @param  string  $uri
145
     * @param  array|string|callable|null  $action
146
     * @return \Illuminate\Routing\Route
147
     */
148
    public function get($uri, $action = null)
149
    {
150
        return $this->addRoute(['GET', 'HEAD'], $uri, $action);
151
    }
152
 
153
    /**
154
     * Register a new POST route with the router.
155
     *
156
     * @param  string  $uri
157
     * @param  array|string|callable|null  $action
158
     * @return \Illuminate\Routing\Route
159
     */
160
    public function post($uri, $action = null)
161
    {
162
        return $this->addRoute('POST', $uri, $action);
163
    }
164
 
165
    /**
166
     * Register a new PUT route with the router.
167
     *
168
     * @param  string  $uri
169
     * @param  array|string|callable|null  $action
170
     * @return \Illuminate\Routing\Route
171
     */
172
    public function put($uri, $action = null)
173
    {
174
        return $this->addRoute('PUT', $uri, $action);
175
    }
176
 
177
    /**
178
     * Register a new PATCH route with the router.
179
     *
180
     * @param  string  $uri
181
     * @param  array|string|callable|null  $action
182
     * @return \Illuminate\Routing\Route
183
     */
184
    public function patch($uri, $action = null)
185
    {
186
        return $this->addRoute('PATCH', $uri, $action);
187
    }
188
 
189
    /**
190
     * Register a new DELETE route with the router.
191
     *
192
     * @param  string  $uri
193
     * @param  array|string|callable|null  $action
194
     * @return \Illuminate\Routing\Route
195
     */
196
    public function delete($uri, $action = null)
197
    {
198
        return $this->addRoute('DELETE', $uri, $action);
199
    }
200
 
201
    /**
202
     * Register a new OPTIONS route with the router.
203
     *
204
     * @param  string  $uri
205
     * @param  array|string|callable|null  $action
206
     * @return \Illuminate\Routing\Route
207
     */
208
    public function options($uri, $action = null)
209
    {
210
        return $this->addRoute('OPTIONS', $uri, $action);
211
    }
212
 
213
    /**
214
     * Register a new route responding to all verbs.
215
     *
216
     * @param  string  $uri
217
     * @param  array|string|callable|null  $action
218
     * @return \Illuminate\Routing\Route
219
     */
220
    public function any($uri, $action = null)
221
    {
222
        return $this->addRoute(self::$verbs, $uri, $action);
223
    }
224
 
225
    /**
226
     * Register a new Fallback route with the router.
227
     *
228
     * @param  array|string|callable|null  $action
229
     * @return \Illuminate\Routing\Route
230
     */
231
    public function fallback($action)
232
    {
233
        $placeholder = 'fallbackPlaceholder';
234
 
235
        return $this->addRoute(
236
            'GET', "{{$placeholder}}", $action
237
        )->where($placeholder, '.*')->fallback();
238
    }
239
 
240
    /**
241
     * Create a redirect from one URI to another.
242
     *
243
     * @param  string  $uri
244
     * @param  string  $destination
245
     * @param  int  $status
246
     * @return \Illuminate\Routing\Route
247
     */
248
    public function redirect($uri, $destination, $status = 302)
249
    {
250
        return $this->any($uri, '\Illuminate\Routing\RedirectController')
251
                ->defaults('destination', $destination)
252
                ->defaults('status', $status);
253
    }
254
 
255
    /**
256
     * Create a permanent redirect from one URI to another.
257
     *
258
     * @param  string  $uri
259
     * @param  string  $destination
260
     * @return \Illuminate\Routing\Route
261
     */
262
    public function permanentRedirect($uri, $destination)
263
    {
264
        return $this->redirect($uri, $destination, 301);
265
    }
266
 
267
    /**
268
     * Register a new route that returns a view.
269
     *
270
     * @param  string  $uri
271
     * @param  string  $view
272
     * @param  array  $data
273
     * @param  int|array  $status
274
     * @param  array  $headers
275
     * @return \Illuminate\Routing\Route
276
     */
277
    public function view($uri, $view, $data = [], $status = 200, array $headers = [])
278
    {
279
        return $this->match(['GET', 'HEAD'], $uri, '\Illuminate\Routing\ViewController')
280
                ->setDefaults([
281
                    'view' => $view,
282
                    'data' => $data,
283
                    'status' => is_array($status) ? 200 : $status,
284
                    'headers' => is_array($status) ? $status : $headers,
285
                ]);
286
    }
287
 
288
    /**
289
     * Register a new route with the given verbs.
290
     *
291
     * @param  array|string  $methods
292
     * @param  string  $uri
293
     * @param  array|string|callable|null  $action
294
     * @return \Illuminate\Routing\Route
295
     */
296
    public function match($methods, $uri, $action = null)
297
    {
298
        return $this->addRoute(array_map('strtoupper', (array) $methods), $uri, $action);
299
    }
300
 
301
    /**
302
     * Register an array of resource controllers.
303
     *
304
     * @param  array  $resources
305
     * @param  array  $options
306
     * @return void
307
     */
308
    public function resources(array $resources, array $options = [])
309
    {
310
        foreach ($resources as $name => $controller) {
311
            $this->resource($name, $controller, $options);
312
        }
313
    }
314
 
315
    /**
316
     * Route a resource to a controller.
317
     *
318
     * @param  string  $name
319
     * @param  string  $controller
320
     * @param  array  $options
321
     * @return \Illuminate\Routing\PendingResourceRegistration
322
     */
323
    public function resource($name, $controller, array $options = [])
324
    {
325
        if ($this->container && $this->container->bound(ResourceRegistrar::class)) {
326
            $registrar = $this->container->make(ResourceRegistrar::class);
327
        } else {
328
            $registrar = new ResourceRegistrar($this);
329
        }
330
 
331
        return new PendingResourceRegistration(
332
            $registrar, $name, $controller, $options
333
        );
334
    }
335
 
336
    /**
337
     * Register an array of API resource controllers.
338
     *
339
     * @param  array  $resources
340
     * @param  array  $options
341
     * @return void
342
     */
343
    public function apiResources(array $resources, array $options = [])
344
    {
345
        foreach ($resources as $name => $controller) {
346
            $this->apiResource($name, $controller, $options);
347
        }
348
    }
349
 
350
    /**
351
     * Route an API resource to a controller.
352
     *
353
     * @param  string  $name
354
     * @param  string  $controller
355
     * @param  array  $options
356
     * @return \Illuminate\Routing\PendingResourceRegistration
357
     */
358
    public function apiResource($name, $controller, array $options = [])
359
    {
360
        $only = ['index', 'show', 'store', 'update', 'destroy'];
361
 
362
        if (isset($options['except'])) {
363
            $only = array_diff($only, (array) $options['except']);
364
        }
365
 
366
        return $this->resource($name, $controller, array_merge([
367
            'only' => $only,
368
        ], $options));
369
    }
370
 
371
    /**
372
     * Register an array of singleton resource controllers.
373
     *
374
     * @param  array  $singletons
375
     * @param  array  $options
376
     * @return void
377
     */
378
    public function singletons(array $singletons, array $options = [])
379
    {
380
        foreach ($singletons as $name => $controller) {
381
            $this->singleton($name, $controller, $options);
382
        }
383
    }
384
 
385
    /**
386
     * Route a singleton resource to a controller.
387
     *
388
     * @param  string  $name
389
     * @param  string  $controller
390
     * @param  array  $options
391
     * @return \Illuminate\Routing\PendingSingletonResourceRegistration
392
     */
393
    public function singleton($name, $controller, array $options = [])
394
    {
395
        if ($this->container && $this->container->bound(ResourceRegistrar::class)) {
396
            $registrar = $this->container->make(ResourceRegistrar::class);
397
        } else {
398
            $registrar = new ResourceRegistrar($this);
399
        }
400
 
401
        return new PendingSingletonResourceRegistration(
402
            $registrar, $name, $controller, $options
403
        );
404
    }
405
 
406
    /**
407
     * Register an array of API singleton resource controllers.
408
     *
409
     * @param  array  $singletons
410
     * @param  array  $options
411
     * @return void
412
     */
413
    public function apiSingletons(array $singletons, array $options = [])
414
    {
415
        foreach ($singletons as $name => $controller) {
416
            $this->apiSingleton($name, $controller, $options);
417
        }
418
    }
419
 
420
    /**
421
     * Route an API singleton resource to a controller.
422
     *
423
     * @param  string  $name
424
     * @param  string  $controller
425
     * @param  array  $options
426
     * @return \Illuminate\Routing\PendingSingletonResourceRegistration
427
     */
428
    public function apiSingleton($name, $controller, array $options = [])
429
    {
430
        $only = ['show', 'update', 'destroy'];
431
 
432
        if (isset($options['except'])) {
433
            $only = array_diff($only, (array) $options['except']);
434
        }
435
 
436
        return $this->singleton($name, $controller, array_merge([
437
            'only' => $only,
438
            'apiSingleton' => true,
439
        ], $options));
440
    }
441
 
442
    /**
443
     * Create a route group with shared attributes.
444
     *
445
     * @param  array  $attributes
446
     * @param  \Closure|array|string  $routes
447
     * @return $this
448
     */
449
    public function group(array $attributes, $routes)
450
    {
451
        foreach (Arr::wrap($routes) as $groupRoutes) {
452
            $this->updateGroupStack($attributes);
453
 
454
            // Once we have updated the group stack, we'll load the provided routes and
455
            // merge in the group's attributes when the routes are created. After we
456
            // have created the routes, we will pop the attributes off the stack.
457
            $this->loadRoutes($groupRoutes);
458
 
459
            array_pop($this->groupStack);
460
        }
461
 
462
        return $this;
463
    }
464
 
465
    /**
466
     * Update the group stack with the given attributes.
467
     *
468
     * @param  array  $attributes
469
     * @return void
470
     */
471
    protected function updateGroupStack(array $attributes)
472
    {
473
        if ($this->hasGroupStack()) {
474
            $attributes = $this->mergeWithLastGroup($attributes);
475
        }
476
 
477
        $this->groupStack[] = $attributes;
478
    }
479
 
480
    /**
481
     * Merge the given array with the last group stack.
482
     *
483
     * @param  array  $new
484
     * @param  bool  $prependExistingPrefix
485
     * @return array
486
     */
487
    public function mergeWithLastGroup($new, $prependExistingPrefix = true)
488
    {
489
        return RouteGroup::merge($new, end($this->groupStack), $prependExistingPrefix);
490
    }
491
 
492
    /**
493
     * Load the provided routes.
494
     *
495
     * @param  \Closure|string  $routes
496
     * @return void
497
     */
498
    protected function loadRoutes($routes)
499
    {
500
        if ($routes instanceof Closure) {
501
            $routes($this);
502
        } else {
503
            (new RouteFileRegistrar($this))->register($routes);
504
        }
505
    }
506
 
507
    /**
508
     * Get the prefix from the last group on the stack.
509
     *
510
     * @return string
511
     */
512
    public function getLastGroupPrefix()
513
    {
514
        if ($this->hasGroupStack()) {
515
            $last = end($this->groupStack);
516
 
517
            return $last['prefix'] ?? '';
518
        }
519
 
520
        return '';
521
    }
522
 
523
    /**
524
     * Add a route to the underlying route collection.
525
     *
526
     * @param  array|string  $methods
527
     * @param  string  $uri
528
     * @param  array|string|callable|null  $action
529
     * @return \Illuminate\Routing\Route
530
     */
531
    public function addRoute($methods, $uri, $action)
532
    {
533
        return $this->routes->add($this->createRoute($methods, $uri, $action));
534
    }
535
 
536
    /**
537
     * Create a new route instance.
538
     *
539
     * @param  array|string  $methods
540
     * @param  string  $uri
541
     * @param  mixed  $action
542
     * @return \Illuminate\Routing\Route
543
     */
544
    protected function createRoute($methods, $uri, $action)
545
    {
546
        // If the route is routing to a controller we will parse the route action into
547
        // an acceptable array format before registering it and creating this route
548
        // instance itself. We need to build the Closure that will call this out.
549
        if ($this->actionReferencesController($action)) {
550
            $action = $this->convertToControllerAction($action);
551
        }
552
 
553
        $route = $this->newRoute(
554
            $methods, $this->prefix($uri), $action
555
        );
556
 
557
        // If we have groups that need to be merged, we will merge them now after this
558
        // route has already been created and is ready to go. After we're done with
559
        // the merge we will be ready to return the route back out to the caller.
560
        if ($this->hasGroupStack()) {
561
            $this->mergeGroupAttributesIntoRoute($route);
562
        }
563
 
564
        $this->addWhereClausesToRoute($route);
565
 
566
        return $route;
567
    }
568
 
569
    /**
570
     * Determine if the action is routing to a controller.
571
     *
572
     * @param  mixed  $action
573
     * @return bool
574
     */
575
    protected function actionReferencesController($action)
576
    {
577
        if (! $action instanceof Closure) {
578
            return is_string($action) || (isset($action['uses']) && is_string($action['uses']));
579
        }
580
 
581
        return false;
582
    }
583
 
584
    /**
585
     * Add a controller based route action to the action array.
586
     *
587
     * @param  array|string  $action
588
     * @return array
589
     */
590
    protected function convertToControllerAction($action)
591
    {
592
        if (is_string($action)) {
593
            $action = ['uses' => $action];
594
        }
595
 
596
        // Here we'll merge any group "controller" and "uses" statements if necessary so that
597
        // the action has the proper clause for this property. Then, we can simply set the
598
        // name of this controller on the action plus return the action array for usage.
599
        if ($this->hasGroupStack()) {
600
            $action['uses'] = $this->prependGroupController($action['uses']);
601
            $action['uses'] = $this->prependGroupNamespace($action['uses']);
602
        }
603
 
604
        // Here we will set this controller name on the action array just so we always
605
        // have a copy of it for reference if we need it. This can be used while we
606
        // search for a controller name or do some other type of fetch operation.
607
        $action['controller'] = $action['uses'];
608
 
609
        return $action;
610
    }
611
 
612
    /**
613
     * Prepend the last group namespace onto the use clause.
614
     *
615
     * @param  string  $class
616
     * @return string
617
     */
618
    protected function prependGroupNamespace($class)
619
    {
620
        $group = end($this->groupStack);
621
 
622
        return isset($group['namespace']) && ! str_starts_with($class, '\\') && ! str_starts_with($class, $group['namespace'])
623
                ? $group['namespace'].'\\'.$class : $class;
624
    }
625
 
626
    /**
627
     * Prepend the last group controller onto the use clause.
628
     *
629
     * @param  string  $class
630
     * @return string
631
     */
632
    protected function prependGroupController($class)
633
    {
634
        $group = end($this->groupStack);
635
 
636
        if (! isset($group['controller'])) {
637
            return $class;
638
        }
639
 
640
        if (class_exists($class)) {
641
            return $class;
642
        }
643
 
644
        if (str_contains($class, '@')) {
645
            return $class;
646
        }
647
 
648
        return $group['controller'].'@'.$class;
649
    }
650
 
651
    /**
652
     * Create a new Route object.
653
     *
654
     * @param  array|string  $methods
655
     * @param  string  $uri
656
     * @param  mixed  $action
657
     * @return \Illuminate\Routing\Route
658
     */
659
    public function newRoute($methods, $uri, $action)
660
    {
661
        return (new Route($methods, $uri, $action))
662
                    ->setRouter($this)
663
                    ->setContainer($this->container);
664
    }
665
 
666
    /**
667
     * Prefix the given URI with the last prefix.
668
     *
669
     * @param  string  $uri
670
     * @return string
671
     */
672
    protected function prefix($uri)
673
    {
674
        return trim(trim($this->getLastGroupPrefix(), '/').'/'.trim($uri, '/'), '/') ?: '/';
675
    }
676
 
677
    /**
678
     * Add the necessary where clauses to the route based on its initial registration.
679
     *
680
     * @param  \Illuminate\Routing\Route  $route
681
     * @return \Illuminate\Routing\Route
682
     */
683
    protected function addWhereClausesToRoute($route)
684
    {
685
        $route->where(array_merge(
686
            $this->patterns, $route->getAction()['where'] ?? []
687
        ));
688
 
689
        return $route;
690
    }
691
 
692
    /**
693
     * Merge the group stack with the controller action.
694
     *
695
     * @param  \Illuminate\Routing\Route  $route
696
     * @return void
697
     */
698
    protected function mergeGroupAttributesIntoRoute($route)
699
    {
700
        $route->setAction($this->mergeWithLastGroup(
701
            $route->getAction(),
702
            $prependExistingPrefix = false
703
        ));
704
    }
705
 
706
    /**
707
     * Return the response returned by the given route.
708
     *
709
     * @param  string  $name
710
     * @return \Symfony\Component\HttpFoundation\Response
711
     */
712
    public function respondWithRoute($name)
713
    {
714
        $route = tap($this->routes->getByName($name))->bind($this->currentRequest);
715
 
716
        return $this->runRoute($this->currentRequest, $route);
717
    }
718
 
719
    /**
720
     * Dispatch the request to the application.
721
     *
722
     * @param  \Illuminate\Http\Request  $request
723
     * @return \Symfony\Component\HttpFoundation\Response
724
     */
725
    public function dispatch(Request $request)
726
    {
727
        $this->currentRequest = $request;
728
 
729
        return $this->dispatchToRoute($request);
730
    }
731
 
732
    /**
733
     * Dispatch the request to a route and return the response.
734
     *
735
     * @param  \Illuminate\Http\Request  $request
736
     * @return \Symfony\Component\HttpFoundation\Response
737
     */
738
    public function dispatchToRoute(Request $request)
739
    {
740
        return $this->runRoute($request, $this->findRoute($request));
741
    }
742
 
743
    /**
744
     * Find the route matching a given request.
745
     *
746
     * @param  \Illuminate\Http\Request  $request
747
     * @return \Illuminate\Routing\Route
748
     */
749
    protected function findRoute($request)
750
    {
751
        $this->events->dispatch(new Routing($request));
752
 
753
        $this->current = $route = $this->routes->match($request);
754
 
755
        $route->setContainer($this->container);
756
 
757
        $this->container->instance(Route::class, $route);
758
 
759
        return $route;
760
    }
761
 
762
    /**
763
     * Return the response for the given route.
764
     *
765
     * @param  \Illuminate\Http\Request  $request
766
     * @param  \Illuminate\Routing\Route  $route
767
     * @return \Symfony\Component\HttpFoundation\Response
768
     */
769
    protected function runRoute(Request $request, Route $route)
770
    {
771
        $request->setRouteResolver(fn () => $route);
772
 
773
        $this->events->dispatch(new RouteMatched($route, $request));
774
 
775
        return $this->prepareResponse($request,
776
            $this->runRouteWithinStack($route, $request)
777
        );
778
    }
779
 
780
    /**
781
     * Run the given route within a Stack "onion" instance.
782
     *
783
     * @param  \Illuminate\Routing\Route  $route
784
     * @param  \Illuminate\Http\Request  $request
785
     * @return mixed
786
     */
787
    protected function runRouteWithinStack(Route $route, Request $request)
788
    {
789
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
790
                                $this->container->make('middleware.disable') === true;
791
 
792
        $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
793
 
794
        return (new Pipeline($this->container))
795
                        ->send($request)
796
                        ->through($middleware)
797
                        ->then(fn ($request) => $this->prepareResponse(
798
                            $request, $route->run()
799
                        ));
800
    }
801
 
802
    /**
803
     * Gather the middleware for the given route with resolved class names.
804
     *
805
     * @param  \Illuminate\Routing\Route  $route
806
     * @return array
807
     */
808
    public function gatherRouteMiddleware(Route $route)
809
    {
810
        return $this->resolveMiddleware($route->gatherMiddleware(), $route->excludedMiddleware());
811
    }
812
 
813
    /**
814
     * Resolve a flat array of middleware classes from the provided array.
815
     *
816
     * @param  array  $middleware
817
     * @param  array  $excluded
818
     * @return array
819
     */
820
    public function resolveMiddleware(array $middleware, array $excluded = [])
821
    {
822
        $excluded = collect($excluded)->map(function ($name) {
823
            return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
824
        })->flatten()->values()->all();
825
 
826
        $middleware = collect($middleware)->map(function ($name) {
827
            return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
828
        })->flatten()->reject(function ($name) use ($excluded) {
829
            if (empty($excluded)) {
830
                return false;
831
            }
832
 
833
            if ($name instanceof Closure) {
834
                return false;
835
            }
836
 
837
            if (in_array($name, $excluded, true)) {
838
                return true;
839
            }
840
 
841
            if (! class_exists($name)) {
842
                return false;
843
            }
844
 
845
            $reflection = new ReflectionClass($name);
846
 
847
            return collect($excluded)->contains(
848
                fn ($exclude) => class_exists($exclude) && $reflection->isSubclassOf($exclude)
849
            );
850
        })->values();
851
 
852
        return $this->sortMiddleware($middleware);
853
    }
854
 
855
    /**
856
     * Sort the given middleware by priority.
857
     *
858
     * @param  \Illuminate\Support\Collection  $middlewares
859
     * @return array
860
     */
861
    protected function sortMiddleware(Collection $middlewares)
862
    {
863
        return (new SortedMiddleware($this->middlewarePriority, $middlewares))->all();
864
    }
865
 
866
    /**
867
     * Create a response instance from the given value.
868
     *
869
     * @param  \Symfony\Component\HttpFoundation\Request  $request
870
     * @param  mixed  $response
871
     * @return \Symfony\Component\HttpFoundation\Response
872
     */
873
    public function prepareResponse($request, $response)
874
    {
875
        return static::toResponse($request, $response);
876
    }
877
 
878
    /**
879
     * Static version of prepareResponse.
880
     *
881
     * @param  \Symfony\Component\HttpFoundation\Request  $request
882
     * @param  mixed  $response
883
     * @return \Symfony\Component\HttpFoundation\Response
884
     */
885
    public static function toResponse($request, $response)
886
    {
887
        if ($response instanceof Responsable) {
888
            $response = $response->toResponse($request);
889
        }
890
 
891
        if ($response instanceof PsrResponseInterface) {
892
            $response = (new HttpFoundationFactory)->createResponse($response);
893
        } elseif ($response instanceof Model && $response->wasRecentlyCreated) {
894
            $response = new JsonResponse($response, 201);
895
        } elseif ($response instanceof Stringable) {
896
            $response = new Response($response->__toString(), 200, ['Content-Type' => 'text/html']);
897
        } elseif (! $response instanceof SymfonyResponse &&
898
                   ($response instanceof Arrayable ||
899
                    $response instanceof Jsonable ||
900
                    $response instanceof ArrayObject ||
901
                    $response instanceof JsonSerializable ||
902
                    $response instanceof stdClass ||
903
                    is_array($response))) {
904
            $response = new JsonResponse($response);
905
        } elseif (! $response instanceof SymfonyResponse) {
906
            $response = new Response($response, 200, ['Content-Type' => 'text/html']);
907
        }
908
 
909
        if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) {
910
            $response->setNotModified();
911
        }
912
 
913
        return $response->prepare($request);
914
    }
915
 
916
    /**
917
     * Substitute the route bindings onto the route.
918
     *
919
     * @param  \Illuminate\Routing\Route  $route
920
     * @return \Illuminate\Routing\Route
921
     *
922
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
923
     * @throws \Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException
924
     */
925
    public function substituteBindings($route)
926
    {
927
        foreach ($route->parameters() as $key => $value) {
928
            if (isset($this->binders[$key])) {
929
                $route->setParameter($key, $this->performBinding($key, $value, $route));
930
            }
931
        }
932
 
933
        return $route;
934
    }
935
 
936
    /**
937
     * Substitute the implicit route bindings for the given route.
938
     *
939
     * @param  \Illuminate\Routing\Route  $route
940
     * @return void
941
     *
942
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
943
     * @throws \Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException
944
     */
945
    public function substituteImplicitBindings($route)
946
    {
947
        ImplicitRouteBinding::resolveForRoute($this->container, $route);
948
    }
949
 
950
    /**
951
     * Call the binding callback for the given key.
952
     *
953
     * @param  string  $key
954
     * @param  string  $value
955
     * @param  \Illuminate\Routing\Route  $route
956
     * @return mixed
957
     *
958
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
959
     */
960
    protected function performBinding($key, $value, $route)
961
    {
962
        return call_user_func($this->binders[$key], $value, $route);
963
    }
964
 
965
    /**
966
     * Register a route matched event listener.
967
     *
968
     * @param  string|callable  $callback
969
     * @return void
970
     */
971
    public function matched($callback)
972
    {
973
        $this->events->listen(Events\RouteMatched::class, $callback);
974
    }
975
 
976
    /**
977
     * Get all of the defined middleware short-hand names.
978
     *
979
     * @return array
980
     */
981
    public function getMiddleware()
982
    {
983
        return $this->middleware;
984
    }
985
 
986
    /**
987
     * Register a short-hand name for a middleware.
988
     *
989
     * @param  string  $name
990
     * @param  string  $class
991
     * @return $this
992
     */
993
    public function aliasMiddleware($name, $class)
994
    {
995
        $this->middleware[$name] = $class;
996
 
997
        return $this;
998
    }
999
 
1000
    /**
1001
     * Check if a middlewareGroup with the given name exists.
1002
     *
1003
     * @param  string  $name
1004
     * @return bool
1005
     */
1006
    public function hasMiddlewareGroup($name)
1007
    {
1008
        return array_key_exists($name, $this->middlewareGroups);
1009
    }
1010
 
1011
    /**
1012
     * Get all of the defined middleware groups.
1013
     *
1014
     * @return array
1015
     */
1016
    public function getMiddlewareGroups()
1017
    {
1018
        return $this->middlewareGroups;
1019
    }
1020
 
1021
    /**
1022
     * Register a group of middleware.
1023
     *
1024
     * @param  string  $name
1025
     * @param  array  $middleware
1026
     * @return $this
1027
     */
1028
    public function middlewareGroup($name, array $middleware)
1029
    {
1030
        $this->middlewareGroups[$name] = $middleware;
1031
 
1032
        return $this;
1033
    }
1034
 
1035
    /**
1036
     * Add a middleware to the beginning of a middleware group.
1037
     *
1038
     * If the middleware is already in the group, it will not be added again.
1039
     *
1040
     * @param  string  $group
1041
     * @param  string  $middleware
1042
     * @return $this
1043
     */
1044
    public function prependMiddlewareToGroup($group, $middleware)
1045
    {
1046
        if (isset($this->middlewareGroups[$group]) && ! in_array($middleware, $this->middlewareGroups[$group])) {
1047
            array_unshift($this->middlewareGroups[$group], $middleware);
1048
        }
1049
 
1050
        return $this;
1051
    }
1052
 
1053
    /**
1054
     * Add a middleware to the end of a middleware group.
1055
     *
1056
     * If the middleware is already in the group, it will not be added again.
1057
     *
1058
     * @param  string  $group
1059
     * @param  string  $middleware
1060
     * @return $this
1061
     */
1062
    public function pushMiddlewareToGroup($group, $middleware)
1063
    {
1064
        if (! array_key_exists($group, $this->middlewareGroups)) {
1065
            $this->middlewareGroups[$group] = [];
1066
        }
1067
 
1068
        if (! in_array($middleware, $this->middlewareGroups[$group])) {
1069
            $this->middlewareGroups[$group][] = $middleware;
1070
        }
1071
 
1072
        return $this;
1073
    }
1074
 
1075
    /**
1076
     * Remove the given middleware from the specified group.
1077
     *
1078
     * @param  string  $group
1079
     * @param  string  $middleware
1080
     * @return $this
1081
     */
1082
    public function removeMiddlewareFromGroup($group, $middleware)
1083
    {
1084
        if (! $this->hasMiddlewareGroup($group)) {
1085
            return $this;
1086
        }
1087
 
1088
        $reversedMiddlewaresArray = array_flip($this->middlewareGroups[$group]);
1089
 
1090
        if (! array_key_exists($middleware, $reversedMiddlewaresArray)) {
1091
            return $this;
1092
        }
1093
 
1094
        $middlewareKey = $reversedMiddlewaresArray[$middleware];
1095
 
1096
        unset($this->middlewareGroups[$group][$middlewareKey]);
1097
 
1098
        return $this;
1099
    }
1100
 
1101
    /**
1102
     * Flush the router's middleware groups.
1103
     *
1104
     * @return $this
1105
     */
1106
    public function flushMiddlewareGroups()
1107
    {
1108
        $this->middlewareGroups = [];
1109
 
1110
        return $this;
1111
    }
1112
 
1113
    /**
1114
     * Add a new route parameter binder.
1115
     *
1116
     * @param  string  $key
1117
     * @param  string|callable  $binder
1118
     * @return void
1119
     */
1120
    public function bind($key, $binder)
1121
    {
1122
        $this->binders[str_replace('-', '_', $key)] = RouteBinding::forCallback(
1123
            $this->container, $binder
1124
        );
1125
    }
1126
 
1127
    /**
1128
     * Register a model binder for a wildcard.
1129
     *
1130
     * @param  string  $key
1131
     * @param  string  $class
1132
     * @param  \Closure|null  $callback
1133
     * @return void
1134
     */
1135
    public function model($key, $class, Closure $callback = null)
1136
    {
1137
        $this->bind($key, RouteBinding::forModel($this->container, $class, $callback));
1138
    }
1139
 
1140
    /**
1141
     * Get the binding callback for a given binding.
1142
     *
1143
     * @param  string  $key
1144
     * @return \Closure|null
1145
     */
1146
    public function getBindingCallback($key)
1147
    {
1148
        if (isset($this->binders[$key = str_replace('-', '_', $key)])) {
1149
            return $this->binders[$key];
1150
        }
1151
    }
1152
 
1153
    /**
1154
     * Get the global "where" patterns.
1155
     *
1156
     * @return array
1157
     */
1158
    public function getPatterns()
1159
    {
1160
        return $this->patterns;
1161
    }
1162
 
1163
    /**
1164
     * Set a global where pattern on all routes.
1165
     *
1166
     * @param  string  $key
1167
     * @param  string  $pattern
1168
     * @return void
1169
     */
1170
    public function pattern($key, $pattern)
1171
    {
1172
        $this->patterns[$key] = $pattern;
1173
    }
1174
 
1175
    /**
1176
     * Set a group of global where patterns on all routes.
1177
     *
1178
     * @param  array  $patterns
1179
     * @return void
1180
     */
1181
    public function patterns($patterns)
1182
    {
1183
        foreach ($patterns as $key => $pattern) {
1184
            $this->pattern($key, $pattern);
1185
        }
1186
    }
1187
 
1188
    /**
1189
     * Determine if the router currently has a group stack.
1190
     *
1191
     * @return bool
1192
     */
1193
    public function hasGroupStack()
1194
    {
1195
        return ! empty($this->groupStack);
1196
    }
1197
 
1198
    /**
1199
     * Get the current group stack for the router.
1200
     *
1201
     * @return array
1202
     */
1203
    public function getGroupStack()
1204
    {
1205
        return $this->groupStack;
1206
    }
1207
 
1208
    /**
1209
     * Get a route parameter for the current route.
1210
     *
1211
     * @param  string  $key
1212
     * @param  string|null  $default
1213
     * @return mixed
1214
     */
1215
    public function input($key, $default = null)
1216
    {
1217
        return $this->current()->parameter($key, $default);
1218
    }
1219
 
1220
    /**
1221
     * Get the request currently being dispatched.
1222
     *
1223
     * @return \Illuminate\Http\Request
1224
     */
1225
    public function getCurrentRequest()
1226
    {
1227
        return $this->currentRequest;
1228
    }
1229
 
1230
    /**
1231
     * Get the currently dispatched route instance.
1232
     *
1233
     * @return \Illuminate\Routing\Route|null
1234
     */
1235
    public function getCurrentRoute()
1236
    {
1237
        return $this->current();
1238
    }
1239
 
1240
    /**
1241
     * Get the currently dispatched route instance.
1242
     *
1243
     * @return \Illuminate\Routing\Route|null
1244
     */
1245
    public function current()
1246
    {
1247
        return $this->current;
1248
    }
1249
 
1250
    /**
1251
     * Check if a route with the given name exists.
1252
     *
1253
     * @param  string|array  $name
1254
     * @return bool
1255
     */
1256
    public function has($name)
1257
    {
1258
        $names = is_array($name) ? $name : func_get_args();
1259
 
1260
        foreach ($names as $value) {
1261
            if (! $this->routes->hasNamedRoute($value)) {
1262
                return false;
1263
            }
1264
        }
1265
 
1266
        return true;
1267
    }
1268
 
1269
    /**
1270
     * Get the current route name.
1271
     *
1272
     * @return string|null
1273
     */
1274
    public function currentRouteName()
1275
    {
1276
        return $this->current() ? $this->current()->getName() : null;
1277
    }
1278
 
1279
    /**
1280
     * Alias for the "currentRouteNamed" method.
1281
     *
1282
     * @param  mixed  ...$patterns
1283
     * @return bool
1284
     */
1285
    public function is(...$patterns)
1286
    {
1287
        return $this->currentRouteNamed(...$patterns);
1288
    }
1289
 
1290
    /**
1291
     * Determine if the current route matches a pattern.
1292
     *
1293
     * @param  mixed  ...$patterns
1294
     * @return bool
1295
     */
1296
    public function currentRouteNamed(...$patterns)
1297
    {
1298
        return $this->current() && $this->current()->named(...$patterns);
1299
    }
1300
 
1301
    /**
1302
     * Get the current route action.
1303
     *
1304
     * @return string|null
1305
     */
1306
    public function currentRouteAction()
1307
    {
1308
        if ($this->current()) {
1309
            return $this->current()->getAction()['controller'] ?? null;
1310
        }
1311
    }
1312
 
1313
    /**
1314
     * Alias for the "currentRouteUses" method.
1315
     *
1316
     * @param  array  ...$patterns
1317
     * @return bool
1318
     */
1319
    public function uses(...$patterns)
1320
    {
1321
        foreach ($patterns as $pattern) {
1322
            if (Str::is($pattern, $this->currentRouteAction())) {
1323
                return true;
1324
            }
1325
        }
1326
 
1327
        return false;
1328
    }
1329
 
1330
    /**
1331
     * Determine if the current route action matches a given action.
1332
     *
1333
     * @param  string  $action
1334
     * @return bool
1335
     */
1336
    public function currentRouteUses($action)
1337
    {
1338
        return $this->currentRouteAction() == $action;
1339
    }
1340
 
1341
    /**
1342
     * Set the unmapped global resource parameters to singular.
1343
     *
1344
     * @param  bool  $singular
1345
     * @return void
1346
     */
1347
    public function singularResourceParameters($singular = true)
1348
    {
1349
        ResourceRegistrar::singularParameters($singular);
1350
    }
1351
 
1352
    /**
1353
     * Set the global resource parameter mapping.
1354
     *
1355
     * @param  array  $parameters
1356
     * @return void
1357
     */
1358
    public function resourceParameters(array $parameters = [])
1359
    {
1360
        ResourceRegistrar::setParameters($parameters);
1361
    }
1362
 
1363
    /**
1364
     * Get or set the verbs used in the resource URIs.
1365
     *
1366
     * @param  array  $verbs
1367
     * @return array|null
1368
     */
1369
    public function resourceVerbs(array $verbs = [])
1370
    {
1371
        return ResourceRegistrar::verbs($verbs);
1372
    }
1373
 
1374
    /**
1375
     * Get the underlying route collection.
1376
     *
1377
     * @return \Illuminate\Routing\RouteCollectionInterface
1378
     */
1379
    public function getRoutes()
1380
    {
1381
        return $this->routes;
1382
    }
1383
 
1384
    /**
1385
     * Set the route collection instance.
1386
     *
1387
     * @param  \Illuminate\Routing\RouteCollection  $routes
1388
     * @return void
1389
     */
1390
    public function setRoutes(RouteCollection $routes)
1391
    {
1392
        foreach ($routes as $route) {
1393
            $route->setRouter($this)->setContainer($this->container);
1394
        }
1395
 
1396
        $this->routes = $routes;
1397
 
1398
        $this->container->instance('routes', $this->routes);
1399
    }
1400
 
1401
    /**
1402
     * Set the compiled route collection instance.
1403
     *
1404
     * @param  array  $routes
1405
     * @return void
1406
     */
1407
    public function setCompiledRoutes(array $routes)
1408
    {
1409
        $this->routes = (new CompiledRouteCollection($routes['compiled'], $routes['attributes']))
1410
            ->setRouter($this)
1411
            ->setContainer($this->container);
1412
 
1413
        $this->container->instance('routes', $this->routes);
1414
    }
1415
 
1416
    /**
1417
     * Remove any duplicate middleware from the given array.
1418
     *
1419
     * @param  array  $middleware
1420
     * @return array
1421
     */
1422
    public static function uniqueMiddleware(array $middleware)
1423
    {
1424
        $seen = [];
1425
        $result = [];
1426
 
1427
        foreach ($middleware as $value) {
1428
            $key = \is_object($value) ? \spl_object_id($value) : $value;
1429
 
1430
            if (! isset($seen[$key])) {
1431
                $seen[$key] = true;
1432
                $result[] = $value;
1433
            }
1434
        }
1435
 
1436
        return $result;
1437
    }
1438
 
1439
    /**
1440
     * Set the container instance used by the router.
1441
     *
1442
     * @param  \Illuminate\Container\Container  $container
1443
     * @return $this
1444
     */
1445
    public function setContainer(Container $container)
1446
    {
1447
        $this->container = $container;
1448
 
1449
        return $this;
1450
    }
1451
 
1452
    /**
1453
     * Dynamically handle calls into the router instance.
1454
     *
1455
     * @param  string  $method
1456
     * @param  array  $parameters
1457
     * @return mixed
1458
     */
1459
    public function __call($method, $parameters)
1460
    {
1461
        if (static::hasMacro($method)) {
1462
            return $this->macroCall($method, $parameters);
1463
        }
1464
 
1465
        if ($method === 'middleware') {
1466
            return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
1467
        }
1468
 
1469
        if ($method !== 'where' && Str::startsWith($method, 'where')) {
1470
            return (new RouteRegistrar($this))->{$method}(...$parameters);
1471
        }
1472
 
1473
        return (new RouteRegistrar($this))->attribute($method, array_key_exists(0, $parameters) ? $parameters[0] : true);
1474
    }
1475
}