Passed
Push — main ( 113eee...af6a91 )
by Thomas
04:24
created

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