Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
100.00% |
35 / 35 |
|
100.00% |
28 / 28 |
|
78.95% |
15 / 19 |
|
100.00% |
12 / 12 |
CRAP | |
100.00% |
1 / 1 |
| Group | |
100.00% |
35 / 35 |
|
100.00% |
28 / 28 |
|
78.95% |
15 / 19 |
|
100.00% |
12 / 12 |
19.70 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| register | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| controller | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| middleware | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| before | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| after | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| addRoute | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| group | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| collect | |
100.00% |
7 / 7 |
|
100.00% |
10 / 10 |
|
20.00% |
1 / 5 |
|
100.00% |
1 / 1 |
7.61 | |||
| assertCollecting | |
100.00% |
2 / 2 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| forwardRoute | |
100.00% |
4 / 4 |
|
100.00% |
4 / 4 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| applyTo | |
100.00% |
6 / 6 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Celema\Router; |
| 6 | |
| 7 | use Celema\Router\Exception\RuntimeException; |
| 8 | use Closure; |
| 9 | use Override; |
| 10 | use Psr\Http\Server\MiddlewareInterface as Middleware; |
| 11 | |
| 12 | /** @psalm-api */ |
| 13 | final class Group implements RouteAdder |
| 14 | { |
| 15 | use AddsBeforeAfter { |
| 16 | before as private addBeforeHandler; |
| 17 | after as private addAfterHandler; |
| 18 | } |
| 19 | use AddsMiddleware { |
| 20 | middleware as private addMiddlewareHandlers; |
| 21 | } |
| 22 | use AddsRoutes; |
| 23 | |
| 24 | /** @var list<Route|Group> */ |
| 25 | private array $entries = []; |
| 26 | |
| 27 | private ?string $controller = null; |
| 28 | private bool $collecting = false; |
| 29 | |
| 30 | /** |
| 31 | * Groups are callback-scoped. Use Router::group() or nested Group::group() |
| 32 | * so the router controls registration, ordering, and group finalization. |
| 33 | */ |
| 34 | private function __construct( |
| 35 | private readonly RouteAdder $routeAdder, |
| 36 | private readonly string $patternPrefix, |
| 37 | private readonly Closure $createClosure, |
| 38 | private readonly string $namePrefix, |
| 39 | ) {} |
| 40 | |
| 41 | /** @internal */ |
| 42 | public static function register( |
| 43 | RouteAdder $adder, |
| 44 | string $patternPrefix, |
| 45 | Closure $createClosure, |
| 46 | string $namePrefix = '', |
| 47 | ): void { |
| 48 | new self($adder, $patternPrefix, $createClosure, $namePrefix)->collect(); |
| 49 | } |
| 50 | |
| 51 | public function controller(string $controller): static |
| 52 | { |
| 53 | $this->assertCollecting(); |
| 54 | $this->controller = $controller; |
| 55 | |
| 56 | return $this; |
| 57 | } |
| 58 | |
| 59 | public function middleware(Middleware ...$middleware): static |
| 60 | { |
| 61 | $this->assertCollecting(); |
| 62 | |
| 63 | return $this->addMiddlewareHandlers(...$middleware); |
| 64 | } |
| 65 | |
| 66 | public function before(Before $beforeHandler): static |
| 67 | { |
| 68 | $this->assertCollecting(); |
| 69 | |
| 70 | return $this->addBeforeHandler($beforeHandler); |
| 71 | } |
| 72 | |
| 73 | public function after(After $afterHandler): static |
| 74 | { |
| 75 | $this->assertCollecting(); |
| 76 | |
| 77 | return $this->addAfterHandler($afterHandler); |
| 78 | } |
| 79 | |
| 80 | #[Override] |
| 81 | public function addRoute(Route $route): Route |
| 82 | { |
| 83 | $this->assertCollecting(); |
| 84 | $this->entries[] = $route; |
| 85 | |
| 86 | return $route; |
| 87 | } |
| 88 | |
| 89 | #[Override] |
| 90 | public function group( |
| 91 | string $patternPrefix, |
| 92 | Closure $createClosure, |
| 93 | string $namePrefix = '', |
| 94 | ): void { |
| 95 | $this->assertCollecting(); |
| 96 | $this->entries[] = new self($this, $patternPrefix, $createClosure, $namePrefix); |
| 97 | } |
| 98 | |
| 99 | private function collect(): void |
| 100 | { |
| 101 | $this->collecting = true; |
| 102 | |
| 103 | try { |
| 104 | ($this->createClosure)($this); |
| 105 | } finally { |
| 106 | $this->collecting = false; |
| 107 | } |
| 108 | |
| 109 | foreach ($this->entries as $entry) { |
| 110 | if ($entry instanceof Route) { |
| 111 | $this->forwardRoute($entry); |
| 112 | } else { |
| 113 | $entry->collect(); |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | private function assertCollecting(): void |
| 119 | { |
| 120 | if (!$this->collecting) { |
| 121 | throw new RuntimeException('Cannot modify group outside the group callback.'); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | private function forwardRoute(Route $route): void |
| 126 | { |
| 127 | $this->applyTo($route); |
| 128 | |
| 129 | if ($this->routeAdder instanceof self) { |
| 130 | $this->routeAdder->forwardRoute($route); |
| 131 | } else { |
| 132 | $this->routeAdder->addRoute($route); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | private function applyTo(Route $route): void |
| 137 | { |
| 138 | $route->prefix($this->patternPrefix, $this->namePrefix); |
| 139 | |
| 140 | if ($this->controller !== null) { |
| 141 | $route->controller($this->controller); |
| 142 | } |
| 143 | |
| 144 | $route->replaceMiddleware(array_merge($this->middleware, $route->getMiddleware())); |
| 145 | $route->setBeforeHandlers($this->mergeBeforeHandlers($route->beforeHandlers())); |
| 146 | $route->setAfterHandlers($this->mergeAfterHandlers($route->afterHandlers())); |
| 147 | } |
| 148 | } |
Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 35 | private readonly RouteAdder $routeAdder, |
| 36 | private readonly string $patternPrefix, |
| 37 | private readonly Closure $createClosure, |
| 38 | private readonly string $namePrefix, |
| 39 | ) {} |
| 81 | public function addRoute(Route $route): Route |
| 82 | { |
| 83 | $this->assertCollecting(); |
| 84 | $this->entries[] = $route; |
| 85 | |
| 86 | return $route; |
| 87 | } |
| 73 | public function after(After $afterHandler): static |
| 74 | { |
| 75 | $this->assertCollecting(); |
| 76 | |
| 77 | return $this->addAfterHandler($afterHandler); |
| 78 | } |
| 136 | private function applyTo(Route $route): void |
| 137 | { |
| 138 | $route->prefix($this->patternPrefix, $this->namePrefix); |
| 139 | |
| 140 | if ($this->controller !== null) { |
| 141 | $route->controller($this->controller); |
| 142 | } |
| 143 | |
| 144 | $route->replaceMiddleware(array_merge($this->middleware, $route->getMiddleware())); |
| 144 | $route->replaceMiddleware(array_merge($this->middleware, $route->getMiddleware())); |
| 145 | $route->setBeforeHandlers($this->mergeBeforeHandlers($route->beforeHandlers())); |
| 146 | $route->setAfterHandlers($this->mergeAfterHandlers($route->afterHandlers())); |
| 147 | } |
| 136 | private function applyTo(Route $route): void |
| 137 | { |
| 138 | $route->prefix($this->patternPrefix, $this->namePrefix); |
| 139 | |
| 140 | if ($this->controller !== null) { |
| 144 | $route->replaceMiddleware(array_merge($this->middleware, $route->getMiddleware())); |
| 145 | $route->setBeforeHandlers($this->mergeBeforeHandlers($route->beforeHandlers())); |
| 146 | $route->setAfterHandlers($this->mergeAfterHandlers($route->afterHandlers())); |
| 147 | } |
| 120 | if (!$this->collecting) { |
| 121 | throw new RuntimeException('Cannot modify group outside the group callback.'); |
| 120 | if (!$this->collecting) { |
| 123 | } |
| 66 | public function before(Before $beforeHandler): static |
| 67 | { |
| 68 | $this->assertCollecting(); |
| 69 | |
| 70 | return $this->addBeforeHandler($beforeHandler); |
| 71 | } |
| 101 | $this->collecting = true; |
| 102 | |
| 103 | try { |
| 104 | ($this->createClosure)($this); |
| 105 | } finally { |
| 106 | $this->collecting = false; |
| 101 | $this->collecting = true; |
| 102 | |
| 103 | try { |
| 104 | ($this->createClosure)($this); |
| 105 | } finally { |
| 105 | } finally { |
| 109 | foreach ($this->entries as $entry) { |
| 109 | foreach ($this->entries as $entry) { |
| 110 | if ($entry instanceof Route) { |
| 110 | if ($entry instanceof Route) { |
| 111 | $this->forwardRoute($entry); |
| 109 | foreach ($this->entries as $entry) { |
| 109 | foreach ($this->entries as $entry) { |
| 109 | foreach ($this->entries as $entry) { |
| 110 | if ($entry instanceof Route) { |
| 111 | $this->forwardRoute($entry); |
| 112 | } else { |
| 113 | $entry->collect(); |
| 114 | } |
| 115 | } |
| 116 | } |
| 101 | $this->collecting = true; |
| 102 | |
| 103 | try { |
| 104 | ($this->createClosure)($this); |
| 105 | } finally { |
| 105 | } finally { |
| 109 | foreach ($this->entries as $entry) { |
| 109 | foreach ($this->entries as $entry) { |
| 110 | if ($entry instanceof Route) { |
| 109 | foreach ($this->entries as $entry) { |
| 110 | if ($entry instanceof Route) { |
| 111 | $this->forwardRoute($entry); |
| 112 | } else { |
| 113 | $entry->collect(); |
| 109 | foreach ($this->entries as $entry) { |
| 109 | foreach ($this->entries as $entry) { |
| 109 | foreach ($this->entries as $entry) { |
| 110 | if ($entry instanceof Route) { |
| 111 | $this->forwardRoute($entry); |
| 112 | } else { |
| 113 | $entry->collect(); |
| 114 | } |
| 115 | } |
| 116 | } |
| 101 | $this->collecting = true; |
| 102 | |
| 103 | try { |
| 104 | ($this->createClosure)($this); |
| 105 | } finally { |
| 105 | } finally { |
| 109 | foreach ($this->entries as $entry) { |
| 109 | foreach ($this->entries as $entry) { |
| 109 | foreach ($this->entries as $entry) { |
| 110 | if ($entry instanceof Route) { |
| 111 | $this->forwardRoute($entry); |
| 112 | } else { |
| 113 | $entry->collect(); |
| 114 | } |
| 115 | } |
| 116 | } |
| 101 | $this->collecting = true; |
| 102 | |
| 103 | try { |
| 104 | ($this->createClosure)($this); |
| 105 | } finally { |
| 105 | } finally { |
| 109 | foreach ($this->entries as $entry) { |
| 109 | foreach ($this->entries as $entry) { |
| 110 | if ($entry instanceof Route) { |
| 111 | $this->forwardRoute($entry); |
| 112 | } else { |
| 113 | $entry->collect(); |
| 114 | } |
| 115 | } |
| 116 | } |
| 51 | public function controller(string $controller): static |
| 52 | { |
| 53 | $this->assertCollecting(); |
| 54 | $this->controller = $controller; |
| 55 | |
| 56 | return $this; |
| 57 | } |
| 125 | private function forwardRoute(Route $route): void |
| 126 | { |
| 127 | $this->applyTo($route); |
| 128 | |
| 129 | if ($this->routeAdder instanceof self) { |
| 129 | if ($this->routeAdder instanceof self) { |
| 130 | $this->routeAdder->forwardRoute($route); |
| 134 | } |
| 125 | private function forwardRoute(Route $route): void |
| 126 | { |
| 127 | $this->applyTo($route); |
| 128 | |
| 129 | if ($this->routeAdder instanceof self) { |
| 132 | $this->routeAdder->addRoute($route); |
| 133 | } |
| 134 | } |
| 134 | } |
| 91 | string $patternPrefix, |
| 92 | Closure $createClosure, |
| 93 | string $namePrefix = '', |
| 94 | ): void { |
| 95 | $this->assertCollecting(); |
| 96 | $this->entries[] = new self($this, $patternPrefix, $createClosure, $namePrefix); |
| 97 | } |
| 59 | public function middleware(Middleware ...$middleware): static |
| 60 | { |
| 61 | $this->assertCollecting(); |
| 62 | |
| 63 | return $this->addMiddlewareHandlers(...$middleware); |
| 64 | } |
| 43 | RouteAdder $adder, |
| 44 | string $patternPrefix, |
| 45 | Closure $createClosure, |
| 46 | string $namePrefix = '', |
| 47 | ): void { |
| 48 | new self($adder, $patternPrefix, $createClosure, $namePrefix)->collect(); |
| 49 | } |