Passed
Push — main ( f41f09...08852a )
by Thomas
02:36
created

App   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 164
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 18
eloc 53
c 1
b 0
f 0
dl 0
loc 164
ccs 66
cts 66
cp 1
rs 10

16 Methods

Rating   Name   Duplication   Size   Complexity  
A registry() 0 3 1
A addRoute() 0 3 1
A run() 0 12 1
A staticRoute() 0 6 1
A middleware() 0 3 1
A __construct() 0 6 1
A renderer() 0 3 1
A viewAttr() 0 3 1
A logger() 0 3 1
A create() 0 13 2
A register() 0 3 1
A initializeRegistry() 0 33 2
A group() 0 9 1
A addGroup() 0 3 1
A config() 0 3 1
A router() 0 3 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 Conia\Chuck\ViewAttrInterface;
0 ignored issues
show
Bug introduced by
The type Conia\Chuck\ViewAttrInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
30
{
31
    use AddsRoutes;
32
33 26
    public function __construct(
34
        protected Config $config,
35
        protected Router $router,
36
        protected Registry $registry,
37
    ) {
38 26
        $this->initializeRegistry();
39
    }
40
41 24
    public static function create(?Config $config = null, ?ContainerInterface $container = null): static
42
    {
43 24
        if (!$config) {
44 18
            $config = new Config('chuck', debug: false);
45
        }
46
47 24
        $registry = new Registry($container);
48 24
        $router = new Router();
49
50 24
        $errorHandler = new ErrorHandler($config, $registry);
51 24
        $errorHandler->setup();
52
53 24
        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 5
    public function registry(): Registry
67
    {
68 5
        return $this->registry;
69
    }
70
71 15
    public function addRoute(Route $route): void
72
    {
73 15
        $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 = null,
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 = null,
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
    /**
123
     * @psalm-param non-empty-string $name
124
     * @psalm-param non-empty-string $class
125
     */
126 1
    public function viewAttr(string $name, string $class): Entry
127
    {
128 1
        return $this->registry->tag(ViewAttributeInterface::class)->add($name, $class)->asIs();
129
    }
130
131
    /** @param callable(mixed ...$args):LoggerInterface $callable */
132 1
    public function logger(callable $callback): void
133
    {
134 1
        $this->registry->add(LoggerInterface::class, Closure::fromCallable($callback));
135
    }
136
137
    /**
138
     * @psalm-param non-empty-string $key
139
     * @psalm-param class-string|object $value
140
     */
141 1
    public function register(string $key, object|string $value): Entry
142
    {
143 1
        return $this->registry->add($key, $value);
144
    }
145
146 6
    public function run(): Response
147
    {
148 6
        $serverRequest = $this->registry->get(ServerRequestInterface::class);
149 6
        assert($serverRequest instanceof ServerRequestInterface);
150 6
        $request = new Request($serverRequest);
151 6
        $this->registry->add(Request::class, $request);
152
153 6
        $response = $this->router->dispatch($request, $this->registry);
154
155 6
        (new Emitter())->emit($response->psr7());
156
157 6
        return $response;
158
    }
159
160 26
    protected function initializeRegistry(): void
161
    {
162 26
        $registry = $this->registry;
163
164 26
        $registry->add(Config::class, $this->config);
165 26
        $registry->add($this->config::class, $this->config);
166 26
        $registry->add(Router::class, $this->router);
167 26
        $registry->add($this->router::class, $this->router);
168 26
        $registry->add(App::class, $this);
169
170 26
        $registry->add(ResponseFactoryInterface::class, \Nyholm\Psr7\Factory\Psr17Factory::class);
171 26
        $registry->add(StreamFactoryInterface::class, \Nyholm\Psr7\Factory\Psr17Factory::class);
172 26
        $registry->add(ResponseFactory::class, new ResponseFactory($this->registry));
173 26
        $registry->add(ServerRequestInterface::class, function (): ServerRequestInterface {
174
            try {
175 6
                $psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
176 6
                $creator = new \Nyholm\Psr7Server\ServerRequestCreator(
177 6
                    $psr17Factory, // ServerRequestFactory
178 6
                    $psr17Factory, // UriFactory
179 6
                    $psr17Factory, // UploadedFileFactory
180 6
                    $psr17Factory  // StreamFactory
181 6
                );
182
183 6
                return $creator->fromGlobals();
184
                // @codeCoverageIgnoreStart
185
            } catch (Throwable) {
186
                throw new RuntimeException('Install nyholm/psr7-server');
187
                // @codeCoverageIgnoreEnd
188
            }
189 26
        });
190
191 26
        $registry->tag(Renderer::class)->add('text', TextRenderer::class)->asIs();
192 26
        $registry->tag(Renderer::class)->add('json', JsonRenderer::class)->asIs();
193
    }
194
}
195