Passed
Push — main ( d7f329...a4880b )
by Zsolt
08:35
created

Application::dispatch()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 16
c 1
b 0
f 0
nc 5
nop 0
dl 0
loc 19
rs 9.4222
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * darkmatter framework
7
 * by Zsolt Sándor <[email protected]>
8
 *
9
 * @link https://elevenone.space
10
 * @license MIT
11
 */
12
13
namespace App;
14
15
use DarkMatter\Http\Request;
16
use DarkMatter\Http\Response;
17
use DarkMatter\Components\Router\RouterInterface;
18
use DarkMatter\Components\Router\Router;
19
use DarkMatter\Components\Logger\LoggerInterface;
20
use DarkMatter\Exception\Application\DarkMatterException;
21
use DarkMatter\Exception\ExceptionHandlerInterface;
22
use DarkMatter\Exception\Http\BadRequestException;
23
use DarkMatter\Exception\Http\MethodNotAllowedException;
24
use DarkMatter\Exception\Http\NotFoundException;
25
26
27
28
29
class Application
30
{
31
    /**
32
     * @var array $config
33
     */
34
    public $config;
35
36
    /**
37
     * @var Request $request
38
     */
39
    public $request;
40
41
    /**
42
     * @var RouterInterface $router
43
     */
44
    public $router;
45
46
    /**
47
     * @var LoggerInterface $logger
48
     */
49
    public $logger;
50
51
    /**
52
     * @var ExceptionHandlerInterface $exceptionHandler
53
     */
54
    public $exceptionHandler;
55
56
    public function __construct(
57
        array $config,
58
        Request $request,
59
        RouterInterface $router,
60
        LoggerInterface $logger,
61
        ExceptionHandlerInterface $exceptionHandler
62
    ) {
63
        $this->config = $config;
64
        $this->router = $router;
65
        $this->request = $request;
66
        $this->logger = $logger;
67
        $this->exceptionHandler = $exceptionHandler;
68
    }
69
70
    /**
71
     * Runs the application and passes all errors to exception handler.
72
     *
73
     * @return Response
74
     */
75
    public function run(): Response
76
    {
77
        try {
78
            $response = $this->dispatch();
79
            $this->send($response);
80
        } catch (\Error $e) {
81
            $response = $this->exceptionHandler->handleError($e);
82
            $this->send($response);
83
        } catch (\Exception $e) {
84
            $response = $this->exceptionHandler->handleException($e);
85
            $this->send($response);
86
        }
87
88
        return $response;
89
    }
90
91
    /**
92
     * Analyzes the request using the router and calls corresponding action.
93
     * Throws HTTP exceptions in case request could not be assigned to an action.
94
95
     * @throws BadRequestException
96
     * @throws MethodNotAllowedException
97
     * @throws NotFoundException
98
     * @throws DarkMatterException
99
     * @return Response
100
     */
101
    protected function dispatch(): Response
102
    {
103
        $httpMethod = $this->request->getRequestMethod();
104
        $uri = $this->request->getRequestUri();
105
        $routeInfo = $this->router->dispatch($httpMethod, $uri);
106
        if (!isset($routeInfo[0])) {
107
            throw new BadRequestException('Unable to parse request.');
108
        }
109
        switch ($routeInfo[0]) {
110
            case Router::NOT_FOUND:
111
                throw new NotFoundException('Page not found.');
112
            case Router::METHOD_NOT_ALLOWED:
113
                throw new MethodNotAllowedException('Method not allowed');
114
            case Router::FOUND:
115
                $action = $routeInfo[1];
116
                $arguments = $routeInfo[2];
117
                return $this->callAction($action, $arguments);
118
            default:
119
                throw new BadRequestException('Unable to parse request.');
120
        }
121
    }
122
123
    /**
124
     * Calls an action.
125
     *
126
     * @param string $handler
127
     * @param array $arguments
128
     * @throws DarkMatterException
129
     * @return Response
130
     */
131
    public function callAction(string $handler, array $arguments = []): Response
132
    {
133
        if (!class_exists($handler)) {
134
            throw new DarkMatterException('Action class not found.');
135
        }
136
137
        /** @var \DarkMatter\Action\ActionInterface $action */
138
        $action = new $handler($this->config, $this->logger, $this->request);
139
        return $action->__invoke($arguments);
140
    }
141
142
    /**
143
     * Sends response to client.
144
     *
145
     * @param Response $response
146
     * @return void
147
     */
148
    public function send(Response $response): void
149
    {
150
        // send http header:
151
        $httpHeader = sprintf(
152
            'HTTP/%s %d %s',
153
            $response->getProtocolVersion(),
154
            $response->getStatus(),
155
            $response->getStatusMessage()
156
        );
157
        header($httpHeader, true);
158
159
        // send additional headers:
160
        foreach ($response->getHeaders() as $name => $value) {
161
            header(sprintf('%s: %s', $name, $value), true);
162
        }
163
164
        // send body:
165
        echo $response->getBody();
166
    }
167
}
168