Completed
Push — master ( 89529b...271c2f )
by Derek Stephen
04:26
created

Dispatcher   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 83.64%

Importance

Changes 8
Bugs 1 Features 0
Metric Value
wmc 26
lcom 1
cbo 8
dl 0
loc 228
ccs 92
cts 110
cp 0.8364
rs 10
c 8
b 1
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A checkControllerExists() 0 4 1
A sendResponse() 0 5 1
A __construct() 0 18 1
A checkNavigator() 0 17 3
A checkActionExists() 0 4 1
A getResponseBody() 0 21 4
A fireCannons() 0 21 2
A setHeaders() 0 6 2
A plunderEnemyShip() 0 13 2
A sinkingShip() 0 15 2
A templateCheck() 0 11 2
A getTemplateName() 0 9 3
A setNotFound() 0 9 2
1
<?php
2
3
namespace Bone\Mvc;
4
5
use Bone\Filter;
6
use Exception;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Bone\Mvc\Exception.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
7
use ReflectionClass;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Zend\Diactoros\Response\SapiEmitter;
11
12
/**
13
 * Class Dispatcher
14
 * @package Bone\Mvc
15
 */
16
class Dispatcher
17
{
18
    // Garrrr! An arrrray!
19
    private $config = array();
20
21
    /** @var ServerRequestInterface $request */
22
    private $request;
23
24
    /** @var Controller */
25
    private $controller;
26
27
    /** @var ResponseInterface $response */
28
    private $response;
29
30
    /**
31
     * Dispatcher constructor.
32
     * @param ServerRequestInterface $request
33
     * @param ResponseInterface $response
34
     * @throws Filter\Exception
35
     */
36 11
    public function __construct(ServerRequestInterface $request, ResponseInterface $response)
37 11
    {
38 11
        $this->request = $request;
39 11
        $this->response = $response;
40
41 11
        $router = new Router($request);
42 11
        $router->parseRoute();
43
44
        // what controller be we talkin' about?
45 11
        $filtered = Filter::filterString($router->getController(), 'DashToCamelCase');
46 11
        $this->config['controller_name'] = '\App\Controller\\' . ucwords($filtered) . 'Controller';
47
48
        // whit be yer action ?
49 11
        $filtered = Filter::filterString($router->getAction(), 'DashToCamelCase');
50 11
        $this->config['action_name'] = $filtered . 'Action';
51 11
        $this->config['controller'] = $router->getController();
52 11
        $this->config['action'] = $router->getAction();
53 11
    }
54
55
56
    /**
57
     *  Gaaarrr! Check the Navigator be readin' the map!
58
     * @return null|void
59
     */
60 2
    public function checkNavigator()
61 2
    {
62
        // can we find th' darned controller?
63 1
        if (!$this->checkControllerExists()) {
64 1
            $this->setNotFound();
65 1
            return;
66
        }
67
68
        // gaaarr! there be the controller!
69 1
        $this->controller = new $this->config['controller_name']($this->request);
70
71
        // where's the bloody action?
72 1
        if (!$this->checkActionExists()) {
73 1
            $this->setNotFound();
74
        }
75 1
        return null;
76
    }
77
78
79
    /**
80
     * @return bool
81
     */
82 2
    private function checkControllerExists()
83 2
    {
84 2
        return class_exists($this->config['controller_name']);
85
    }
86
87
88
    /**
89
     * @return bool
90
     */
91 2
    private function checkActionExists()
92 2
    {
93 2
        return method_exists($this->controller, $this->config['action_name']);
94
    }
95
96
97
    /**
98
     * @return string
99
     * @throws \Exception
100
     */
101 2
    private function getResponseBody()
102 2
    {
103
        /** @var \stdClass $view_vars */
104 2
        $view_vars = $this->controller->view;
105
106 2
        $response_body = $this->controller->getBody();
107
108 2
        if ($this->controller->hasViewEnabled()) {
109 2
            $view = $this->config['controller'] . '/' . $this->config['action'];
110
            try {
111 2
                $response_body = $this->controller->getViewEngine()->render($view, (array) $view_vars);
112
            } catch (Exception $e) {
113
                throw $e;
114
            }
115
        }
116
117 2
        if ($this->controller->hasLayoutEnabled()) {
118 2
            $response_body = $this->templateCheck($this->controller, $response_body);
119
        }
120 2
        return $response_body;
121
    }
122
123
124
    /**
125
     *
126
     */
127 2
    public function fireCannons()
128 2
    {
129
        try {
130
            // Garr! Check the route with th' navigator
131 1
            $this->checkNavigator();
132
133
            // Fire cannons t' th' controller action
134 1
            $this->plunderEnemyShip();
135
136
            // See what treasure we have plundered
137 1
            $booty = $this->getResponseBody();
138
        } catch (Exception $e) {
139
            $booty = $this->sinkingShip($e);
140
        }
141
142
143
        // report back to th' cap'n
144 1
        $this->response->getBody()->write($booty);
145 1
        $this->setHeaders();
146 1
        $this->sendResponse();
147 1
    }
148
149 1
    private function sendResponse()
150 1
    {
151 1
        $emitter = new SapiEmitter();
152 1
        $emitter->emit($this->response);
153 1
    }
154
155 1
    private function setHeaders()
156 1
    {
157 1
        foreach ($this->controller->getHeaders() as $key => $value) {
158 1
            $this->response = $this->response->withHeader($key, $value);
159
        }
160 1
    }
161
162
163 2
    private function plunderEnemyShip()
164 2
    {
165
        // run th' controller action
166 2
        $action = $this->config['action_name'];
167 2
        $this->controller->init();
168 2
        $vars = $this->controller->$action();
169 2
        if (is_array($vars)) {
170 1
            $viewVars = (array) $this->controller->view;
171 1
            $view = (object) array_merge($vars, $viewVars);
172 1
            $this->controller->view =$view;
173
        }
174 2
        $this->controller->postDispatch();
175 2
    }
176
177
    /**
178
     * @param Exception $e
179
     * @return string
180
     * @throws Exception
181
     */
182
    public function sinkingShip(Exception $e)
183
    {
184
        $controllerName = class_exists('\App\Controller\ErrorController') ? 'App\Controller\ErrorController' : 'Bone\Mvc\Controller';
185
        $this->controller = new $controllerName($this->request);
186
        $this->controller->setParam('error', $e);
187
        $reflection = new ReflectionClass(get_class($this->controller));
188
        $method = $reflection->getMethod('errorAction');
189
        $method->setAccessible(true);
190
        $method->invokeArgs($this->controller, []);
191
        $this->controller->error = $e;
192
        $this->config['controller'] = 'error';
193
        $this->config['action'] = 'error';
194
        $this->response = $this->response->withStatus(500);
195
        return $this->getResponseBody();
196
    }
197
198
199
    /**
200
     * @param Controller $controller
201
     * @param string $content
202
     * @return string
203
     */
204 3
    private function templateCheck($controller, $content)
205 3
    {
206 3
        $response_body = '';
207
        //check we be usin' th' templates in th' config
208 3
        $templates = Registry::ahoy()->get('templates');
209 3
        $template = $this->getTemplateName($templates);
210 3
        if ($template !== null) {
211 3
            $response_body = $controller->getViewEngine()->render('layouts/' . $template, array('content' => $content));
212
        }
213 3
        return $response_body;
214
    }
215
216
    /**
217
     * @param mixed $templates
218
     * @return string|null
219
     */
220 4
    private function getTemplateName($templates)
221 4
    {
222 4
        if (is_null($templates)) {
223 1
            return null;
224 4
        } elseif (is_array($templates)) {
225 1
            return (string) $templates[0];
226
        }
227 4
        return (string) $templates;
228
    }
229
230
    /**
231
     * Sets controller to error and action to not found
232
     * @return void
233
     */
234 2
    private function setNotFound()
235 2
    {
236 2
        $this->config['controller_name'] = class_exists('\App\Controller\ErrorController') ? '\App\Controller\ErrorController' : '\Bone\Mvc\Controller';
237 2
        $this->config['action_name'] = 'notFoundAction';
238 2
        $this->config['controller'] = 'error';
239 2
        $this->config['action'] = 'not-found';
240 2
        $this->controller = new $this->config['controller_name']($this->request);
241 2
        $this->response = $this->response->withStatus(404);
242 2
    }
243
}
244