Completed
Pull Request — master (#16)
by Flo
07:29
created

Dispatcher   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 7
dl 0
loc 182
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A handleFormRequest() 0 19 4
A __construct() 0 5 1
B run() 0 31 4
A getRoute() 0 16 4
A getDirectMatch() 0 19 3
A getVariableMatch() 0 23 3
1
<?php
2
/**
3
 * Class Dispatcher
4
 *
5
 * @package Faulancer\Controller
6
 * @author Florian Knapp <[email protected]>
7
 */
8
namespace Faulancer\Controller;
9
10
use Faulancer\Exception\ClassNotFoundException;
11
use Faulancer\Exception\DispatchFailureException;
12
use Faulancer\Form\AbstractFormHandler;
13
use Faulancer\Http\Request;
14
use Faulancer\Http\Response;
15
use Faulancer\Exception\MethodNotFoundException;
16
use Faulancer\Service\Config;
17
use Faulancer\Service\ResponseService;
18
use Faulancer\ServiceLocator\ServiceLocator;
19
use Faulancer\Session\SessionManager;
20
21
/**
22
 * Class Dispatcher
23
 */
24
class Dispatcher
25
{
26
27
    /**
28
     * The current request object
29
     *
30
     * @var Request
31
     */
32
    protected $request;
33
34
    /**
35
     * The configuration object
36
     *
37
     * @var Config
38
     */
39
    protected $config;
40
41
    /**
42
     * Dispatcher constructor.
43
     *
44
     * @param Request $request
45
     * @param Config  $config
46
     */
47
    public function __construct(Request $request, Config $config)
48
    {
49
        $this->request = $request;
50
        $this->config  = $config;
51
    }
52
53
    /**
54
     * Bootstrap for every route call
55
     *
56
     * @return Response|mixed
57
     * @throws MethodNotFoundException
58
     * @throws ClassNotFoundException
59
     * @throws DispatchFailureException
60
     */
61
    public function run()
62
    {
63
        if ($formRequest = $this->handleFormRequest()) {
64
            return $formRequest;
65
        }
66
67
        /** @var ResponseService $response */
68
        $response = ServiceLocator::instance()->get(ResponseService::class);
69
70
        try {
71
72
            $target = $this->getRoute($this->request->getUri());
73
            $class  = $target['class'];
74
            $action = $target['action'] . 'Action';
75
76
            $class = new $class($this->request);
77
78
            if (isset($target['var'])) {
79
                $response->setContent(call_user_func_array([$class, $action], $target['var']));
80
            } else {
81
                $response->setContent($class->$action());
82
            }
83
84
        } catch (MethodNotFoundException $e) {
85
86
            throw new DispatchFailureException();
87
88
        }
89
90
        return $response;
91
    }
92
93
    /**
94
     * Get data for specific route path
95
     *
96
     * @param string $path
97
     *
98
     * @return array
99
     * @throws MethodNotFoundException
100
     */
101
    private function getRoute($path)
102
    {
103
        $routes = $this->config->get('routes');
104
105
        foreach ($routes as $name => $data) {
106
107
            if ($target = $this->getDirectMatch($path, $data)) {
108
                return $target;
109
            } else if ($target = $this->getVariableMatch($path, $data)) {
110
                return $target;
111
            }
112
113
        }
114
115
        throw new MethodNotFoundException('No matching route for path "' . $path . '" found');
116
    }
117
118
    /**
119
     * Determines if we have a direct/static route match
120
     *
121
     * @param string $uri  The request uri
122
     * @param array  $data The result from ClassParser
123
     *
124
     * @return array
125
     * @throws MethodNotFoundException
126
     */
127
    private function getDirectMatch($uri, array $data) :array
128
    {
129
        if ($uri === $data['path']) {
130
131
            if (strcasecmp($data['method'], $this->request->getMethod()) === 0) {
132
133
                return [
134
                    'class'  => $data['controller'],
135
                    'action' => $data['action']
136
                ];
137
138
            }
139
140
            throw new MethodNotFoundException('Non valid request method available.');
141
142
        }
143
144
        return [];
145
    }
146
147
    /**
148
     * Determines if we have a variable route match
149
     *
150
     * @param string $uri
151
     * @param array  $data
152
     *
153
     * @return array
154
     * @throws MethodNotFoundException
155
     */
156
    private function getVariableMatch($uri, array $data) :array
157
    {
158
        if ($data['path'] === '/') {
159
            return [];
160
        }
161
162
        $var   = [];
163
        $regex = str_replace(['/', '___'], ['\/', '+'], $data['path']);
164
165
        if (preg_match('|^' . $regex . '$|', $uri, $var)) {
166
167
            array_splice($var, 0, 1);
168
169
            return [
170
                'class'  => $data['controller'],
171
                'action' => $data['action'],
172
                'var'    => $var
173
            ];
174
175
        }
176
177
        return [];
178
    }
179
180
    /**
181
     * Detect a form request
182
     *
183
     * @return boolean
184
     */
185
    private function handleFormRequest()
186
    {
187
        if (strpos($this->request->getUri(), '/formrequest/') !== 0 && $this->request->isPost()) {
188
            return false;
189
        }
190
191
        $handlerName  = ucfirst(str_replace('/formrequest/', '', $this->request->getUri()));
192
        $handlerClass = '\\' . $this->config->get('namespacePrefix') . '\\Form\\' . $handlerName . 'Handler';
193
194
        if (class_exists($handlerClass)) {
195
196
            /** @var AbstractFormHandler $handler */
197
            $handler = new $handlerClass($this->request);
198
            return $handler->run();
199
200
        }
201
202
        return false;
203
    }
204
205
}