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