Passed
Push — main ( d3d78c...a401b7 )
by Thomas
03:23
created

App::logger()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Conia\Chuck;
6
7
use Closure;
8
use Conia\Chuck\Di\Entry;
9
use Conia\Chuck\Error\ErrorRenderer;
10
use Conia\Chuck\Error\Handler;
11
use Conia\Chuck\Factory;
12
use Conia\Chuck\Http\Emitter;
13
use Conia\Chuck\Middleware;
14
use Conia\Chuck\Registry;
15
use Conia\Chuck\Renderer\HtmlErrorRenderer;
16
use Conia\Chuck\Renderer\HtmlRenderer;
17
use Conia\Chuck\Renderer\JsonErrorRenderer;
18
use Conia\Chuck\Renderer\JsonRenderer;
19
use Conia\Chuck\Renderer\Renderer;
20
use Conia\Chuck\Renderer\TextErrorRenderer;
21
use Conia\Chuck\Renderer\TextRenderer;
22
use Conia\Chuck\Routing\AddsRoutes;
23
use Conia\Chuck\Routing\RouteAdder;
24
use Psr\Container\ContainerInterface as PsrContainer;
25
use Psr\Http\Message\ResponseInterface as PsrResponse;
26
use Psr\Http\Message\ServerRequestInterface as PsrServerRequest;
27
use Psr\Http\Server\MiddlewareInterface as PsrMiddleware;
28
use Psr\Log\LoggerInterface as PsrLogger;
29
30
/** @psalm-api */
31
class App implements RouteAdder
32
{
33
    use AddsRoutes;
34
35
    /** @psalm-param non-falsy-string|list{non-falsy-string, ...}|Closure|Middleware|PsrMiddleware|null $errorHandler */
36 32
    public function __construct(
37
        protected Router $router,
38
        protected Registry $registry,
39
        protected string|array|Closure|Middleware|PsrMiddleware|null $errorHandler = null,
40
    ) {
41 32
        self::initializeRegistry($registry, $router);
42
43 32
        if (!is_null($errorHandler)) {
44
            // The error handler should be the first middleware
45 29
            $router->middleware($errorHandler);
46
        }
47
    }
48
49 29
    public static function create(?PsrContainer $container = null): self
50
    {
51 29
        $registry = new Registry($container);
52 29
        $router = new Router();
53
54 29
        return new self($router, $registry, Handler::class);
55
    }
56
57 14
    public function router(): Router
58
    {
59 14
        return $this->router;
60
    }
61
62 9
    public function registry(): Registry
63
    {
64 9
        return $this->registry;
65
    }
66
67
    /** @psalm-param Closure(Router $router):void $creator */
68 1
    public function routes(Closure $creator, string $cacheFile = '', bool $shouldCache = true): void
69
    {
70 1
        $this->router->routes($creator, $cacheFile, $shouldCache);
71
    }
72
73 20
    public function addRoute(Route $route): Route
74
    {
75 20
        return $this->router->addRoute($route);
76
    }
77
78 1
    public function addGroup(Group $group): void
79
    {
80 1
        $this->router->addGroup($group);
81
    }
82
83 1
    public function group(
84
        string $patternPrefix,
85
        Closure $createClosure,
86
        string $namePrefix = '',
87
    ): Group {
88 1
        $group = new Group($patternPrefix, $createClosure, $namePrefix);
89 1
        $this->router->addGroup($group);
90
91 1
        return $group;
92
    }
93
94 1
    public function staticRoute(
95
        string $prefix,
96
        string $path,
97
        string $name = '',
98
    ): void {
99 1
        $this->router->addStatic($prefix, $path, $name);
100
    }
101
102
    /** @psalm-param non-falsy-string|list{non-falsy-string, ...}|Closure|Middleware|PsrMiddleware ...$middleware */
103 5
    public function middleware(string|array|Closure|Middleware|PsrMiddleware ...$middleware): void
104
    {
105 5
        $this->router->middleware(...$middleware);
106
    }
107
108
    /**
109
     * @psalm-param non-empty-string $name
110
     * @psalm-param non-empty-string $class
111
     */
112 1
    public function renderer(string $name, string $class): Entry
113
    {
114 1
        return $this->registry->tag(Renderer::class)->add($name, $class);
115
    }
116
117
    /**
118
     * @psalm-param non-empty-string $contentType
119
     * @psalm-param non-empty-string $renderer
120
     */
121 1
    public function errorRenderer(string $contentType, string $renderer, mixed ...$args): Entry
122
    {
123 1
        return $this->registry->tag(Handler::class)
124 1
            ->add($contentType, ErrorRenderer::class)->args(renderer: $renderer, args: $args);
125
    }
126
127 1
    public function logger(callable $callback): void
128
    {
129 1
        $this->registry->add(PsrLogger::class, Closure::fromCallable($callback));
130
    }
131
132
    /**
133
     * @psalm-param non-empty-string $key
134
     * @psalm-param class-string|object $value
135
     */
136 4
    public function register(string $key, object|string $value): Entry
137
    {
138 4
        return $this->registry->add($key, $value);
139
    }
140
141 11
    public function run(): PsrResponse|false
142
    {
143 11
        $factory = $this->registry->get(Factory::class);
144 11
        assert($factory instanceof Factory);
145 11
        $serverRequest = $factory->request();
146 11
        $request = new Request($serverRequest);
147
148 11
        $this->registry->add(PsrServerRequest::class, $serverRequest);
149 11
        $this->registry->add($serverRequest::class, $serverRequest);
150 11
        $this->registry->add(Request::class, $request);
151
152 11
        $response = $this->router->dispatch($request, $this->registry);
153
154 11
        return (new Emitter())->emit($response) ? $response : false;
155
    }
156
157 120
    public static function initializeRegistry(
158
        Registry $registry,
159
        Router $router,
160
    ): void {
161 120
        $registry->add(Router::class, $router);
162 120
        $registry->add($router::class, $router);
163
164 120
        $registry->add(Factory::class, \Conia\Chuck\Psr\Nyholm::class);
165 120
        $registry->add(Response::class)->constructor('fromFactory');
166
167
        // Add default renderers
168 120
        $rendererTag = $registry->tag(Renderer::class);
169 120
        $rendererTag->add('text', TextRenderer::class);
170 120
        $rendererTag->add('json', JsonRenderer::class);
171 120
        $rendererTag->add('html', HtmlRenderer::class);
172 120
        $rendererTag->add('textError', TextErrorRenderer::class);
173 120
        $rendererTag->add('jsonError', JsonErrorRenderer::class);
174 120
        $rendererTag->add('htmlError', HtmlErrorRenderer::class);
175
176
        // Register mimetypes which are compared to the Accept header on error.
177
        // If the header matches a registered Renderer
178 120
        $handlerTag = $registry->tag(Handler::class);
179 120
        $handlerTag->add('text/plain', ErrorRenderer::class)->args(renderer: 'textError', args: []);
180 120
        $handlerTag->add('text/html', ErrorRenderer::class)->args(renderer: 'htmlError', args: []);
181 120
        $handlerTag->add('application/json', ErrorRenderer::class)->args(renderer: 'jsonError', args: []);
182
    }
183
}
184