Completed
Push — master ( 38faae...bfe46f )
by Timothy
03:58
created

src/Aviat/AnimeClient/Dispatcher.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Hummingbird Anime Client
4
 *
5
 * An API client for Hummingbird to manage anime and manga watch lists
6
 *
7
 * @package     HummingbirdAnimeClient
8
 * @author      Timothy J. Warren
9
 * @copyright   Copyright (c) 2015 - 2016
10
 * @link        https://github.com/timw4mail/HummingBirdAnimeClient
11
 * @license     MIT
12
 */
13
namespace Aviat\AnimeClient;
14
15
use Aura\Web\Request;
16
use Aura\Web\Response;
17
18
use Aviat\Ion\Di\ContainerInterface;
19
use Aviat\AnimeClient\AnimeClient;
20
21
/**
22
 * Basic routing/ dispatch
23
 */
24
class Dispatcher extends RoutingBase {
25
26
	/**
27
	 * The route-matching object
28
	 * @var object $router
29
	 */
30
	protected $router;
31
32
	/**
33
	 * Class wrapper for input superglobals
34
	 * @var object
35
	 */
36
	protected $request;
37
38
	/**
39
	 * Routes added to router
40
	 * @var array $output_routes
41
	 */
42
	protected $output_routes;
43
44
	/**
45
	 * Constructor
46
	 *
47
	 * @param ContainerInterface $container
48
	 */
49
	public function __construct(ContainerInterface $container)
50
	{
51
		parent::__construct($container);
52
		$this->router = $container->get('aura-router');
53
		$this->request = $container->get('request');
54
55
		$this->output_routes = $this->_setup_routes();
56
	}
57
58
	/**
59
	 * Get the current route object, if one matches
60
	 *
61
	 * @return object
62
	 */
63
	public function get_route()
64
	{
65
		$error_handler = $this->container->get('error-handler');
66
67
		$raw_route = $this->request->url->get(PHP_URL_PATH);
68
		$route_path = "/" . trim($raw_route, '/');
69
70
		$error_handler->addDataTable('Route Info', [
71
			'route_path' => $route_path
72
		]);
73
74
		return $this->router->match($route_path, $_SERVER);
75
	}
76
77
	/**
78
	 * Get list of routes applied
79
	 *
80
	 * @return array
81
	 */
82
	public function get_output_routes()
83
	{
84
		return $this->output_routes;
85
	}
86
87
	/**
88
	 * Handle the current route
89
	 *
90
	 * @codeCoverageIgnore
91
	 * @param object|null $route
92
	 * @return void
93
	 */
94
	public function __invoke($route = NULL)
95
	{
96
		$error_handler = $this->container->get('error-handler');
97
		$controller_name = AnimeClient::DEFAULT_CONTROLLER;
98
		$action_method = AnimeClient::NOT_FOUND_METHOD;
99
		$params = [];
100
101
		if (is_null($route))
102
		{
103
			$route = $this->get_route();
104
			$error_handler->addDataTable('route_args', (array)$route);
105
		}
106
107
		if ( ! $route)
108
		{
109
			$failure = $this->router->getFailedRoute();
110
			$error_handler->addDataTable('failed_route', (array)$failure);
111
			$action_method = AnimeClient::ERROR_MESSAGE_METHOD;
112
113
			switch(TRUE)
114
			{
115
				case $failure->failedMethod():
116
					$params['title'] = '405 Method Not Allowed';
117
					$params['message'] = 'Invalid HTTP Verb';
118
				break;
119
120
				case $failure->failedAccept():
121
					$params['title'] = '406 Not Acceptable';
122
					$params['message'] = 'Unacceptable content type';
123
				break;
124
125
				default:
126
					$action_method = AnimeClient::NOT_FOUND_METHOD;
127
				break;
128
			}
129
		}
130
		else
131
		{
132
			if (isset($route->params['controller']))
133
			{
134
				$controller_name = $route->params['controller'];
135
			}
136
137
			if (isset($route->params['action']))
138
			{
139
				$action_method = $route->params['action'];
140
			}
141
142
			if (is_null($controller_name))
143
			{
144
				throw new \LogicException("Missing controller");
145
			}
146
147
			if (strpos($controller_name, '\\') === FALSE)
148
			{
149
				$map = $this->get_controller_list();
150
				$controller_name = $map[$controller_name];
151
			}
152
153
			$params = (isset($route->params['params'])) ? $route->params['params'] : [];
154
155
			if ( ! empty($route->tokens))
156
			{
157
				foreach ($route->tokens as $key => $v)
158
				{
159
					if (array_key_exists($key, $route->params))
160
					{
161
						$params[$key] = $route->params[$key];
162
					}
163
				}
164
			}
165
		}
166
167
		$controller = new $controller_name($this->container);
168
169
		// Run the appropriate controller method
170
		$error_handler->addDataTable('controller_args', $params);
171
		call_user_func_array([$controller, $action_method], $params);
172
	}
173
174
	/**
175
	 * Get the type of route, to select the current controller
176
	 *
177
	 * @return string
178
	 */
179
	public function get_controller()
180
	{
181
		$route_type = $this->__get('default_list');
182
		$request_uri = $this->request->url->get(PHP_URL_PATH);
183
		$path = trim($request_uri, '/');
184
185
		$segments = explode('/', $path);
186
		$controller = reset($segments);
187
188
		if (empty($controller))
189
		{
190
			$controller = $route_type;
191
		}
192
193
		return $controller;
194
	}
195
196
	/**
197
	 * Get the list of controllers in the default namespace
198
	 *
199
	 * @return array
200
	 */
201
	public function get_controller_list()
202
	{
203
		$default_namespace = AnimeClient::DEFAULT_CONTROLLER_NAMESPACE;
204
		$path = str_replace('\\', '/', $default_namespace);
205
		$path = trim($path, '/');
206
		$actual_path = \_dir(AnimeClient::SRC_DIR, $path);
207
208
		$class_files = glob("{$actual_path}/*.php");
209
210
		$controllers = [];
211
212
		foreach ($class_files as $file)
213
		{
214
			$raw_class_name = basename(str_replace(".php", "", $file));
215
			$path = strtolower(basename($raw_class_name));
216
			$class_name = trim($default_namespace . '\\' . $raw_class_name, '\\');
217
218
			$controllers[$path] = $class_name;
219
		}
220
221
		return $controllers;
222
	}
223
224
	/**
225
	 * Select controller based on the current url, and apply its relevent routes
226
	 *
227
	 * @return array
228
	 */
229
	protected function _setup_routes()
230
	{
231
		$route_type = $this->get_controller();
232
233
		// Add routes
234
		$routes = [];
235
		foreach ($this->routes as $name => &$route)
236
		{
237
			$path = $route['path'];
238
			unset($route['path']);
239
240
			$controller_map = $this->get_controller_list();
241
			if (array_key_exists($route_type, $controller_map))
242
			{
243
				$controller_class = $controller_map[$route_type];
244
			}
245
246
			// Prepend the controller to the route parameters
247
			$route['controller'] = $controller_class;
0 ignored issues
show
The variable $controller_class does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
248
249
			// Select the appropriate router method based on the http verb
250
			$add = (array_key_exists('verb', $route))
251
				? "add" . ucfirst(strtolower($route['verb']))
252
				: "addGet";
253
254
			// Add the route to the router object
255
			if ( ! array_key_exists('tokens', $route))
256
			{
257
				$routes[] = $this->router->$add($name, $path)->addValues($route);
258
			}
259
			else
260
			{
261
				$tokens = $route['tokens'];
262
				unset($route['tokens']);
263
264
				$routes[] = $this->router->$add($name, $path)
265
					->addValues($route)
266
					->addTokens($tokens);
267
			}
268
		}
269
270
		return $routes;
271
	}
272
}
273
// End of Dispatcher.php