Passed
Push — main ( 4bfbca...f41f09 )
by Thomas
03:11
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 0
Metric Value
eloc 1
c 0
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\Exception\RuntimeException;
9
use Conia\Chuck\MiddlewareInterface;
10
use Conia\Chuck\Registry\Entry;
11
use Conia\Chuck\Registry\Registry;
12
use Conia\Chuck\Renderer\JsonRenderer;
13
use Conia\Chuck\Renderer\Renderer;
14
use Conia\Chuck\Renderer\TextRenderer;
15
use Conia\Chuck\ResponseFactory;
16
use Conia\Chuck\Routing\AddsRoutes;
17
use Conia\Chuck\Routing\Group;
18
use Conia\Chuck\Routing\Route;
19
use Conia\Chuck\Routing\Router;
20
use Psr\Container\ContainerInterface;
21
use Psr\Http\Message\ResponseFactoryInterface;
22
use Psr\Http\Message\ServerRequestInterface;
23
use Psr\Http\Message\StreamFactoryInterface;
24
use Psr\Log\LoggerInterface;
25
use Throwable;
26
27
/** @psalm-consistent-constructor */
28
class App
29
{
30
    use AddsRoutes;
31
32 25
    public function __construct(
33
        protected Config $config,
34
        protected Router $router,
35
        protected Registry $registry,
36
    ) {
37 25
        $this->initializeRegistry();
38
    }
39
40 23
    public static function create(?Config $config = null, ?ContainerInterface $container = null): static
41
    {
42 23
        if (!$config) {
43 17
            $config = new Config('chuck', debug: false);
44
        }
45
46 23
        $registry = new Registry($container);
47 23
        $router = new Router();
48
49 23
        $errorHandler = new ErrorHandler($config, $registry);
50 23
        $errorHandler->setup();
51
52 23
        return new static($config, $router, $registry);
53
    }
54
55 13
    public function router(): Router
56
    {
57 13
        return $this->router;
58
    }
59
60 2
    public function config(): Config
61
    {
62 2
        return $this->config;
63
    }
64
65 4
    public function registry(): Registry
66
    {
67 4
        return $this->registry;
68
    }
69
70 15
    public function addRoute(Route $route): void
71
    {
72 15
        $this->router->addRoute($route);
73
    }
74
75 1
    public function addGroup(Group $group): void
76
    {
77 1
        $this->router->addGroup($group);
78
    }
79
80 1
    public function group(
81
        string $patternPrefix,
82
        Closure $createClosure,
83
        ?string $namePrefix = null,
84
    ): Group {
85 1
        $group = new Group($patternPrefix, $createClosure, $namePrefix);
86 1
        $this->router->addGroup($group);
87
88 1
        return $group;
89
    }
90
91 1
    public function staticRoute(
92
        string $prefix,
93
        string $path,
94
        ?string $name = null,
95
    ): void {
96 1
        $this->router->addStatic($prefix, $path, $name);
97
    }
98
99
    /**
100
     * @param MiddlewareInterface|callable(
101
     *     Request,
102
     *     callable
103
     * ):\Conia\Chuck\Response $middleware
104
     *
105
     * TODO: Why can't we import the custom psalm type MiddlewareCallable from MiddlewareInterface
106
     */
107 5
    public function middleware(MiddlewareInterface|callable ...$middleware): void
108
    {
109 5
        $this->router->middleware(...$middleware);
110
    }
111
112
    /**
113
     * @psalm-param non-empty-string $name
114
     * @psalm-param non-empty-string $class
115
     */
116 1
    public function renderer(string $name, string $class): Entry
117
    {
118 1
        return $this->registry->tag(Renderer::class)->add($name, $class)->asIs();
119
    }
120
121
    /** @param callable(mixed ...$args):LoggerInterface $callable */
122 1
    public function logger(callable $callback): void
123
    {
124 1
        $this->registry->add(LoggerInterface::class, Closure::fromCallable($callback));
125
    }
126
127
    /**
128
     * @psalm-param non-empty-string $key
129
     * @psalm-param class-string|object $value
130
     */
131 1
    public function register(string $key, object|string $value): Entry
132
    {
133 1
        return $this->registry->add($key, $value);
134
    }
135
136 6
    public function run(): Response
137
    {
138 6
        $serverRequest = $this->registry->get(ServerRequestInterface::class);
139 6
        assert($serverRequest instanceof ServerRequestInterface);
140 6
        $request = new Request($serverRequest);
141 6
        $this->registry->add(Request::class, $request);
142
143 6
        $response = $this->router->dispatch($request, $this->registry);
144
145 6
        (new Emitter())->emit($response->psr7());
146
147 6
        return $response;
148
    }
149
150 25
    protected function initializeRegistry(): void
151
    {
152 25
        $registry = $this->registry;
153
154 25
        $registry->add(Config::class, $this->config);
155 25
        $registry->add($this->config::class, $this->config);
156 25
        $registry->add(Router::class, $this->router);
157 25
        $registry->add($this->router::class, $this->router);
158 25
        $registry->add(App::class, $this);
159
160 25
        $registry->add(ResponseFactoryInterface::class, \Nyholm\Psr7\Factory\Psr17Factory::class);
161 25
        $registry->add(StreamFactoryInterface::class, \Nyholm\Psr7\Factory\Psr17Factory::class);
162 25
        $registry->add(ResponseFactory::class, new ResponseFactory($this->registry));
163 25
        $registry->add(ServerRequestInterface::class, function (): ServerRequestInterface {
164
            try {
165 6
                $psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
166 6
                $creator = new \Nyholm\Psr7Server\ServerRequestCreator(
167 6
                    $psr17Factory, // ServerRequestFactory
168 6
                    $psr17Factory, // UriFactory
169 6
                    $psr17Factory, // UploadedFileFactory
170 6
                    $psr17Factory  // StreamFactory
171 6
                );
172
173 6
                return $creator->fromGlobals();
174
                // @codeCoverageIgnoreStart
175
            } catch (Throwable) {
176
                throw new RuntimeException('Install nyholm/psr7-server');
177
                // @codeCoverageIgnoreEnd
178
            }
179 25
        });
180
181 25
        $registry->tag(Renderer::class)->add('text', TextRenderer::class)->asIs();
182 25
        $registry->tag(Renderer::class)->add('json', JsonRenderer::class)->asIs();
183
    }
184
}
185