Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
43 / 43
96.67% covered (success)
96.67%
29 / 30
86.96% covered (warning)
86.96%
20 / 23
94.12% covered (success)
94.12%
16 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
App
100.00% covered (success)
100.00%
43 / 43
96.67% covered (success)
96.67%
29 / 30
86.96% covered (warning)
86.96%
20 / 23
100.00% covered (success)
100.00%
17 / 17
23.07
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 load
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 router
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 factory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addRoute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 group
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 staticRoute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMiddleware
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 errorHandler
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
50.00% covered (danger)
50.00%
1 / 2
100.00% covered (success)
100.00%
1 / 1
2.50
 emitter
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 middleware
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 logger
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 container
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 register
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 initializeContainer
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 run
100.00% covered (success)
100.00%
12 / 12
85.71% covered (warning)
85.71%
6 / 7
50.00% covered (danger)
50.00%
2 / 4
100.00% covered (success)
100.00%
1 / 1
4.12
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Core;
6
7use Celema\Container\Container;
8use Celema\Container\Entry;
9use Celema\Core\Emitter\Emitter;
10use Celema\Core\Emitter\Sapi;
11use Celema\Core\Error\Handler as ErrorHandler;
12use Celema\Core\Factory\Factory;
13use Celema\Core\Factory\Nyholm;
14use Celema\Router\AddsBeforeAfter;
15use Celema\Router\AddsRoutes;
16use Celema\Router\Dispatcher;
17use Celema\Router\Route;
18use Celema\Router\RouteAdder;
19use Celema\Router\Router;
20use Celema\Router\RoutingHandler;
21use Closure;
22use Override;
23use Psr\Container\ContainerInterface as PsrContainer;
24use Psr\Http\Message\ResponseInterface as Response;
25use Psr\Http\Message\ServerRequestInterface as Request;
26use Psr\Http\Server\MiddlewareInterface as Middleware;
27use Psr\Log\LoggerInterface as Logger;
28
29/** @api */
30class App implements RouteAdder
31{
32    use AddsRoutes;
33    use AddsBeforeAfter;
34
35    protected readonly Dispatcher $dispatcher;
36    protected ?ErrorHandler $errorHandler = null;
37    protected Emitter $emitter;
38
39    public function __construct(
40        protected readonly Factory $factory,
41        protected readonly Router $router,
42        protected readonly Container $container,
43    ) {
44        $this->dispatcher = new Dispatcher();
45        $this->emitter = new Sapi();
46        $this->initializeContainer();
47    }
48
49    public function load(Plugin $plugin): void
50    {
51        $plugin->load($this);
52    }
53
54    public static function create(?PsrContainer $container = null): self
55    {
56        return new self(
57            new Nyholm(),
58            new Router(),
59            new Container(container: $container),
60        );
61    }
62
63    public function router(): Router
64    {
65        return $this->router;
66    }
67
68    public function factory(): Factory
69    {
70        return $this->factory;
71    }
72
73    #[Override]
74    public function addRoute(Route $route): Route
75    {
76        return $this->router->addRoute($route);
77    }
78
79    #[Override]
80    public function group(
81        string $patternPrefix,
82        Closure $createClosure,
83        string $namePrefix = '',
84    ): void {
85        $this->router->group($patternPrefix, $createClosure, $namePrefix);
86    }
87
88    public function staticRoute(
89        string $prefix,
90        string $path,
91        string $name = '',
92    ): void {
93        $this->router->addStatic($prefix, $path, $name);
94    }
95
96    public function getMiddleware(): array
97    {
98        return $this->dispatcher->getMiddleware();
99    }
100
101    public function errorHandler(?ErrorHandler $handler = null): ?ErrorHandler
102    {
103        if ($handler !== null) {
104            $this->errorHandler = $handler;
105        }
106
107        return $this->errorHandler;
108    }
109
110    public function emitter(?Emitter $emitter = null): Emitter
111    {
112        if ($emitter !== null) {
113            $this->emitter = $emitter;
114        }
115
116        return $this->emitter;
117    }
118
119    public function middleware(Middleware ...$middleware): void
120    {
121        $this->dispatcher->middleware(...$middleware);
122    }
123
124    public function logger(Logger|callable $logger): void
125    {
126        if ($logger instanceof Logger) {
127            $this->container->add(Logger::class, $logger);
128        } else {
129            $this->container->add(Logger::class, Closure::fromCallable($logger));
130        }
131    }
132
133    public function container(): Container
134    {
135        return $this->container;
136    }
137
138    /**
139     * @param non-empty-string $key
140     * @param class-string|object $value
141     */
142    public function register(string $key, object|string $value): Entry
143    {
144        return $this->container->add($key, $value);
145    }
146
147    public function initializeContainer(): void
148    {
149        $this->container->add(Router::class, $this->router);
150        $this->container->add($this->router::class, $this->router);
151
152        $this->container->add(Factory::class, $this->factory);
153        $this->container->add($this->factory::class, $this->factory);
154    }
155
156    public function run(?Request $request = null): Response|false
157    {
158        $request ??= $this->factory->serverRequest();
159        $this->dispatcher->setBeforeHandlers($this->beforeHandlers);
160        $this->dispatcher->setAfterHandlers($this->afterHandlers);
161        $handler = new RoutingHandler(
162            $this->router,
163            $this->dispatcher,
164            $this->container,
165        );
166        $response = $this->errorHandler
167            ? $this->errorHandler->process($request, $handler)
168            : $handler->handle($request);
169
170        return $this->emitter->emit($response, $request->getMethod() === 'HEAD') ? $response : false;
171    }
172}