Completed
Push — master ( b69bc5...eb8d06 )
by Derek Stephen
02:28
created

Dispatcher   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 227
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 100%

Importance

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

13 Methods

Rating   Name   Duplication   Size   Complexity  
A checkControllerExists() 0 4 1
A __construct() 0 18 1
A checkNavigator() 0 17 3
A checkActionExists() 0 4 1
A getResponseBody() 0 21 4
A fireCannons() 0 20 2
A sendResponse() 0 5 1
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 3
    private function getResponseBody()
102 3
    {
103
        /** @var \stdClass $view_vars */
104 3
        $view_vars = $this->controller->view;
105
106 3
        $response_body = $this->controller->getBody();
107
108 3
        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 1
            } catch (Exception $e) {
113 1
                throw $e;
114
            }
115
        }
116
117 3
        if ($this->controller->hasLayoutEnabled()) {
118 2
            $response_body = $this->templateCheck($this->controller, $response_body);
119
        }
120 3
        return $response_body;
121
    }
122
123
124
    /**
125
     * @throws Exception
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 1
        } catch (Exception $e) {
139 1
            $booty = $this->sinkingShip($e);
140
        }
141
        
142
        // report back to th' cap'n
143 1
        $this->response->getBody()->write($booty);
144 1
        $this->setHeaders();
145 1
        $this->sendResponse();
146 1
    }
147
148 1
    private function sendResponse()
149 1
    {
150 1
        $emitter = new SapiEmitter();
151 1
        $emitter->emit($this->response);
152 1
    }
153
154 1
    private function setHeaders()
155 1
    {
156 1
        foreach ($this->controller->getHeaders() as $key => $value) {
157 1
            $this->response = $this->response->withHeader($key, $value);
158
        }
159 1
    }
160
161
162 2
    private function plunderEnemyShip()
163 2
    {
164
        // run th' controller action
165 2
        $action = $this->config['action_name'];
166 2
        $this->controller->init();
167 2
        $vars = $this->controller->$action();
168 2
        if (is_array($vars)) {
169 1
            $viewVars = (array) $this->controller->view;
170 1
            $view = (object) array_merge($vars, $viewVars);
171 1
            $this->controller->view =$view;
172
        }
173 2
        $this->controller->postDispatch();
174 2
    }
175
176
    /**
177
     * @param Exception $e
178
     * @return string
179
     * @throws Exception
180
     */
181 2
    public function sinkingShip(Exception $e)
182 2
    {
183 1
        $controllerName = class_exists('\App\Controller\ErrorController') ? 'App\Controller\ErrorController' : 'Bone\Mvc\Controller';
184 1
        $this->controller = new $controllerName($this->request);
185 1
        $this->controller->setParam('error', $e);
186 1
        $reflection = new ReflectionClass(get_class($this->controller));
187 1
        $method = $reflection->getMethod('errorAction');
188 1
        $method->setAccessible(true);
189 1
        $method->invokeArgs($this->controller, []);
190 1
        $this->controller->error = $e;
191 1
        $this->config['controller'] = 'error';
192 1
        $this->config['action'] = 'error';
193 1
        $this->response = $this->response->withStatus(500);
194 1
        return $this->getResponseBody();
195
    }
196
197
198
    /**
199
     * @param Controller $controller
200
     * @param string $content
201
     * @return string
202
     */
203 3
    private function templateCheck($controller, $content)
204 3
    {
205 3
        $response_body = '';
206
        //check we be usin' th' templates in th' config
207 3
        $templates = Registry::ahoy()->get('templates');
208 3
        $template = $this->getTemplateName($templates);
209 3
        if ($template !== null) {
210 3
            $response_body = $controller->getViewEngine()->render('layouts/' . $template, array('content' => $content));
211
        }
212 3
        return $response_body;
213
    }
214
215
    /**
216
     * @param mixed $templates
217
     * @return string|null
218
     */
219 4
    private function getTemplateName($templates)
220 4
    {
221 4
        if (is_null($templates)) {
222 1
            return null;
223 4
        } elseif (is_array($templates)) {
224 1
            return (string) $templates[0];
225
        }
226 4
        return (string) $templates;
227
    }
228
229
    /**
230
     * Sets controller to error and action to not found
231
     * @return void
232
     */
233 2
    private function setNotFound()
234 2
    {
235 2
        $this->config['controller_name'] = class_exists('\App\Controller\ErrorController') ? '\App\Controller\ErrorController' : '\Bone\Mvc\Controller';
236 2
        $this->config['action_name'] = 'notFoundAction';
237 2
        $this->config['controller'] = 'error';
238 2
        $this->config['action'] = 'not-found';
239 2
        $this->controller = new $this->config['controller_name']($this->request);
240 2
        $this->response = $this->response->withStatus(404);
241 2
    }
242
}
243