Completed
Push — master ( 9d25e6...5574d1 )
by Mikael
02:43
created

CDispatcherBasic   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 53.09%

Importance

Changes 13
Bugs 1 Features 0
Metric Value
wmc 25
c 13
b 1
f 0
lcom 1
cbo 1
dl 0
loc 218
ccs 43
cts 81
cp 0.5309
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A isValidController() 0 4 1
A setActionName() 0 4 1
A setParams() 0 4 1
A prepareName() 0 10 2
A setControllerName() 0 10 2
A isCallable() 0 13 3
C isCallableOrException() 0 45 8
A dispatch() 0 11 3
A forward() 0 21 4
1
<?php
2
3
namespace Anax\MVC;
4
5
/**
6
 * Dispatching to controllers.
7
 *
8
 */
9
class CDispatcherBasic implements \Anax\DI\IInjectionAware
10
{
11
    use \Anax\DI\TInjectionAware;
12
13
14
15
    /**
16
     * Properties
17
     *
18
     */
19
    private $controllerName;    // Name of controller
20
    private $controller;        // Actual controller
21
    private $action;            // Name of action
22
    private $params;            // Params
23
24
25
26
    /**
27
     * Prepare the name.
28
     *
29
     * @param string $name to prepare.
30
     *
31
     * @return string as the prepared name.
32
     */
33 1
    public function prepareName($name)
34
    {
35 1
        $name = empty($name) ? 'index' : $name;
36 1
        $name = strtolower($name);
37 1
        $name = str_replace(['-', '_'], ' ', $name);
38 1
        $name = ucwords($name);
39 1
        $name = str_replace(' ', '', $name);
40
        
41 1
        return $name;
42
    }
43
44
45
46
    /**
47
     * Set the name of the controller.
48
     *
49
     * @param string $name of the controller, defaults to 'index'.
50
     *
51
     * @return void
52
     */
53 1
    public function setControllerName($name = 'index')
54
    {
55 1
        $name = $this->prepareName($name) . 'Controller';
56
57 1
        $this->controllerName = $name;
58
59 1
        $this->controller = $this->di->has($name)
60 1
            ? $this->di->get($name)
61 1
            : null;
62 1
    }
63
64
65
66
    /**
67
     * Check if a controller exists with this name.
68
     *
69
     * @return bool
70
     */
71 2
    public function isValidController()
72
    {
73 2
        return is_object($this->controller);
74
    }
75
76
77
78
    /**
79
     * Set the name of the action.
80
     *
81
     * @param string $name of the action, defaults to 'index'.
82
     *
83
     * @return void
84
     */
85 1
    public function setActionName($name = 'index')
86
    {
87 1
        $this->action = lcfirst($this->prepareName($name)) . 'Action';
88 1
    }
89
90
91
92
    /**
93
     * Set the params.
94
     *
95
     * @param array $params all parameters, defaults to empty.
96
     *
97
     * @return void
98
     */
99
    public function setParams($params = [])
100
    {
101
        $this->params = $params;
102
    }
103
104
105
106
    /**
107
     * Dispatch to a controller, action with parameters.
108
     *
109
     * @return bool
110
     */
111
    public function isCallable()
112
    {
113
        if (!method_exists($this->controller, $this->action)) {
114
            return false;
115
        }
116
117
        $reflection = new \ReflectionMethod($this->controller, $this->action);
118
        if (!$reflection->isPublic()) {
119
            return false;
120
        }
121
122
        return true;
123
    }
124
125
126
    /**
127
     * Inspect if callable and throw exception if parts is not callable.
128
     *
129
     * @return void
130
     * @throws \Exception
131
     */
132 2
    public function isCallableOrException()
133
    {
134 2
        $validController = $this->isValidController();
135
136 2
        $isMethod   = null;
137 2
        $isCallable = null;
138
139 2
        if ($validController) {
140
            $isMethod   = method_exists($this->controller, $this->action);
141
            $isCallable = $this->isCallable();
142
        }
143
144 2
        if (!($isMethod && $isCallable)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $isMethod of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
Bug Best Practice introduced by
The expression $isCallable of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
145
            $msg =
146
                "Trying to dispatch/forward to a non callable item. Controllername = '"
147 2
                . $this->controllerName
148 2
                . "', Action = '"
149 2
                . $this->action
150 2
                . "'."
151 2
            ;
152
153 2
            $not = $validController ? "" : "NOT";
154
            $msg .=
155 2
                " The controller named '$this->controllerName' does $not exist as part of of the
156
                service-container \$di.
157 2
                ";
158
159 2
            $services = $this->di->getServices();
160 2
            natcasesort($services);
161 2
            $services = implode("\n", $services);
162 2
            $msg .= " Loaded services are: <pre>$services</pre>\n";
163
164 2
            if ($validController) {
165
                $not = $isMethod ? "" : "NOT";
166
                $msg .= " The method '$this->action' does $not exist in the class '$this->controllerName'.";
167
168
                $not = $isCallable ? "" : "NOT";
169
                $msg .=
170
                    " The method '$this->action' is $not callable in the class '$this->controllerName'
171
                    (taking magic methods into consideration).";
172
            }
173
174 2
            throw new \Exception($msg);
175
        }
176
    }
177
178
179
180
    /**
181
     * Dispatch to a controller, action with parameters.
182
     *
183
     * @return mixed result from dispatched controller action.
184
     */
185 2
    public function dispatch()
186
    {
187 2
        $handler = [$this->controller, 'initialize'];
188 2
        if (method_exists($this->controller, 'initialize') && is_callable($handler)) {
189
            call_user_func($handler);
190
        }
191
192 2
        $this->isCallableOrException();
193
194
        return call_user_func_array([$this->controller, $this->action], $this->params);
195
    }
196
197
198
    /**
199
     * Forward to a controller, action with parameters.
200
     *
201
     * @param array $forward with details for controller, action, parameters.
202
     *
203
     * @return mixed result from dispatched controller action.
204
     */
205
    public function forward($forward = [])
206
    {
207
        $controller = isset($forward['controller'])
208
            ? $forward['controller']
209
            : null;
210
211
        $action = isset($forward['action'])
212
            ? $forward['action']
213
            : null;
214
        
215
        $params = isset($forward['params'])
216
            ? $forward['params']
217
            : [];
218
219
        $this->setControllerName($controller);
220
        $this->setActionName($action);
221
        $this->setParams($params);
222
223
        $this->isCallableOrException();
224
        return $this->dispatch();
225
    }
226
}
227