Completed
Pull Request — master (#23)
by Kenji
04:01
created

Error::getMessages()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 26
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 26
rs 8.439
cc 5
eloc 12
nc 8
nop 2
1
<?php
2
/**
3
 * @package    Fuel\Foundation
4
 * @version    2.0
5
 * @author     Fuel Development Team
6
 * @license    MIT License
7
 * @copyright  2010 - 2014 Fuel Development Team
8
 * @link       http://fuelphp.com
9
 */
10
11
namespace Fuel\Foundation;
12
13
use Whoops\Run;
14
use Whoops\Handler\PrettyPageHandler;
15
use Whoops\Handler\JsonResponseHandler;
16
use Whoops\Util\Misc;
17
use Fuel\Foundation\Whoops\ProductionHandler;
18
19
/**
20
 * Error class, implements the Whoops error handler
21
 *
22
 * @package  Fuel\Foundation
23
 *
24
 * @since  2.0.0
25
 */
26
class Error
27
{
28
	/**
29
	 * @var  Whoops\Run  the Whoops error handler instance
30
	 */
31
	protected $whoops;
32
33
	/**
34
	 * @var  Whoops\Handler\PrettyPageHandler  the current page handler
35
	 */
36
	protected $pagehandler;
37
38
	/**
39
	 * Initialization, set the Error handler
40
	 *
41
	 * @since  2.0.0
42
	 */
43
	public function __construct()
44
	{
45
		// are we in a CLi environment?
46
		if ((bool) defined('STDIN'))
47
		{
48
			$this->commandlineHandler();
49
		}
50
51
		// is this an ajax call?
52
		elseif(false)
53
		{
54
			$this->ajaxCallHandler();
55
		}
56
57
		// load the default interactive error handler
58
		else
59
		{
60
			$this->interactiveHandler();
61
		}
62
	}
63
64
	/**
65
	 * Load the correct file with translations, based on the locale passed
66
	 *
67
	 * @return  Whoops\Handler\PrettyPageHandler
68
	 *
69
	 * @since  2.0.0
70
	 */
71
	public function handler()
72
	{
73
		return $this->pagehandler;
74
	}
75
76
	/**
77
	 * Error handler for when the framework is used in CLi environments
78
	 *
79
	 * @since  2.0.0
80
	 */
81
	protected function commandlineHandler()
82
	{
83
	}
84
85
	/**
86
	 * Error handler for when the framework is used in Ajax environments
87
	 *
88
	 * @since  2.0.0
89
	 */
90
	protected function ajaxCallHandler()
91
	{
92
	}
93
94
	/**
95
	 * Error handler for when the framework is used in Web environments
96
	 *
97
	 * @since  2.0.0
98
	 */
99
	protected function interactiveHandler()
100
	{
101
		// use the framework default Whoops error handler
102
		$this->whoops = new Run;
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Whoops\Run() of type object<Whoops\Run> is incompatible with the declared type object<Fuel\Foundation\Whoops\Run> of property $whoops.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
103
104
		$this->whoops->writeToOutput(false);
105
		$this->whoops->allowQuit(false);
106
107
		// define the default page handler
108
		$this->pagehandler = new PrettyPageHandler;
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Whoops\Handler\PrettyPageHandler() of type object<Whoops\Handler\PrettyPageHandler> is incompatible with the declared type object<Fuel\Foundation\W...dler\PrettyPageHandler> of property $pagehandler.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
109
		$this->pagehandler->addResourcePath(__DIR__.DS.'Whoops'.DS.'resources');
110
111
		$this->pagehandler->addDataTableCallback('Application', function()
112
		{
113
			$application = \Application::getInstance();
114
			$environment = $application->getEnvironment();
115
			$request     = \Request::getInstance();
116
			$route = $request ? $request->getRoute() : null;
117
118
			return array(
119
				'Active application'    => $application ? $application->getName() : '',
120
				'Application namespace' => $route ? rtrim($route->namespace, '\\') : '',
121
 				'Environment'           => $environment ? $environment->getName() : '',
122
			);
123
		});
124
125
		$this->pagehandler->addDataTableCallback('Current Request', function()
126
		{
127
			$request = \Request::getInstance();
128
			$route = $request ? $request->getRoute() : null;
129
			$controller = $route ? $route->controller : '';
130
			$parameters = $route ? $route->parameters : array();
131
			array_shift($parameters);
132
133
			return array(
134
				'Original URI'          => $route ? $route->uri : '',
135
				'Mapped URI'            => $route ? $route->translation : '',
136
				'Controller'            => $controller,
137
				'Action'                => $controller ? ('action'.$route->action) : '',
138
				'HTTP Method'           => $request ? $request->getInput()->getMethod() : '',
139
				'Parameters'            => $parameters,
140
			);
141
		});
142
143
		$this->pagehandler->addDataTableCallback('Request Parameters', function()
144
		{
145
			$request = \Request::getInstance();
146
			return $request ? $request->getInput()->getParam()->getContents() : '';
147
		});
148
149
		$this->pagehandler->addDataTableCallback('Permanent Session Data', function()
150
		{
151
			if ($application = \Application::getInstance())
152
			{
153
				if ($session = $application->getSession())
154
				{
155
					return $session->getContents();
156
				}
157
			}
158
			return 'no session active';
159
		});
160
161
		$this->pagehandler->addDataTableCallback('Flash Session Data', function()
162
		{
163
			if ($application = \Application::getInstance())
164
			{
165
				if ($session = $application->getSession())
166
				{
167
					return $session->get('flash');
168
				}
169
			}
170
			return 'no session active';
171
		});
172
173
		$this->pagehandler->addDataTableCallback('Defined Cookies', function()
174
		{
175
			$result = array();
176
			if ($input = \Input::getInstance())
177
			{
178
				foreach ($input->getCookie() as $cookie)
179
				{
180
					$result[$cookie->getName()] = $cookie->getValue();
181
					if ($cookie->isDeleted())
182
					{
183
						$result[$cookie->getName()] .= '; State=Deleted';
184
					}
185
					else
186
					{
187
						$result[$cookie->getName()] .= '; State='.($cookie->isNew() ? 'New' : 'Request');
188
					}
189
				}
190
			}
191
			return $result;
192
		});
193
194
		$this->pagehandler->addDataTableCallback('Uploaded Files', function()
195
		{
196
			$result = array();
197
			if ($input = \Input::getInstance())
198
			{
199
				foreach ($input->getFile() as $file)
200
				{
201
					$result[] = $file;
202
				}
203
			}
204
			return $result;
205
		});
206
207
		$this->pagehandler->addDataTableCallback('Server Data', function()
208
		{
209
			return $_SERVER;
210
		});
211
212
		$this->whoops->pushHandler($this->pagehandler);
0 ignored issues
show
Documentation introduced by
$this->pagehandler is of type object<Whoops\Handler\PrettyPageHandler>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
213
214
		// next on the stack goes the JSON handler, to deal with AJAX requests
215
		if (Misc::isAjaxRequest())
216
		{
217
			$jsonHandler = new JsonResponseHandler;
218
			// $jsonHandler->addTraceToOutput(true);
219
			$this->whoops->pushHandler($jsonHandler);
0 ignored issues
show
Documentation introduced by
$jsonHandler is of type object<Whoops\Handler\JsonResponseHandler>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
220
		}
221
222
		// add the Fuel production handler
223
		$productionHandler = new ProductionHandler;
224
		$this->whoops->pushHandler($productionHandler);
0 ignored issues
show
Documentation introduced by
$productionHandler is of type object<Fuel\Foundation\Whoops\ProductionHandler>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
225
226
		// activate the error handler
227
		$this->whoops->register();
228
229
		// set a custom handler, so we can deal with translations
230
		$current_handler = set_exception_handler(function($e) use(&$current_handler)
0 ignored issues
show
Unused Code introduced by
$current_handler is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
231
		{
232
			// get the locale
233
			if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
234
			{
235
				// if the locale is set to C, default to English
236
				if (($locale = getenv('LC_ALL')) === 'C')
237
				{
238
					$locale = 'English';
239
				}
240
			}
241
			else
242
			{
243
				// if the locale is set to C, default to en_US
244
				if (($locale = setlocale(LC_MESSAGES, null)) === 'C')
245
				{
246
					$locale = 'en_US';
247
				}
248
			}
249
250
			// get access to the exception's error message
251
			$reflection = new \ReflectionClass($e);
252
			$property = $reflection->getProperty("message");
253
			$property->setAccessible(true);
254
255
			// get the translations for the current locale
256
			if ($translations = $this->getMessages($locale, true))
257
			{
258
				// does the error message exist?
259
				$messageId = substr($e->getMessage(), 0,7);
260
				if (isset($translations[$messageId]))
261
				{
262
					// swap the original message for the translated one
263
					$property->setValue($e, $this->setMessage($translations[$messageId], $e->getMessage()));
264
				}
265
			}
266
267
			// call the original error handler with the translated exception message
268
			$result = call_user_func($current_handler, $e);
269
270
			// re-enable output buffering, then send the response for the handlers out
271
			ob_start();
272
			echo $result;
273
		});
274
	}
275
276
	/**
277
	 * Load the correct file with translations, based on the locale passed
278
	 *
279
	 * @return  mixed  array of message translations, or false if none are found
280
	 *
281
	 * @since  2.0.0
282
	 */
283
	protected function getMessages($locale, $shorten = false)
284
	{
285
		$baseDir = realpath(__DIR__.DS.'..'.DS.'translations');
286
287
		$lookup = array($locale);
288
		if ($shorten)
289
		{
290
			$lookup[] = substr($locale, 0, 2);
291
		}
292
293
		foreach($lookup as $lang)
294
		{
295
			if (is_file($lang = $baseDir.DS.$lang.'.php'))
296
			{
297
				$translations = include $lang;
298
				if (is_string($translations))
299
				{
300
					return $this->getMessages($translations);
301
				}
302
				return $translations;
303
			}
304
		}
305
306
		// nothing found
307
		return false;
308
	}
309
310
	/**
311
	 * Convert the original message to the translated message
312
	 *
313
	 * @param  string  $translation  message in the current locale
314
	 * @param  string  $original     original error message
315
	 *
316
	 * @return  string  translated error message
317
	 *
318
	 * @since  2.0.0
319
	 */
320
	protected function setMessage($translation, $original)
321
	{
322
		// strip any parameters from the original message and unify the message for translation
323
		if (preg_match_all('~\[(.*?)\]~', $original, $matches) and ! empty($matches[1]))
324
		{
325
			$params = $matches[1];
326
			$message = preg_replace_callback('~\[(.*?)\]~', function($matches) { static $c = 0; return '"%'.(++$c).'$s"'; }, $original);
0 ignored issues
show
Unused Code introduced by
$message is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Unused Code introduced by
The parameter $matches is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
327
		}
328
329
		// put the parameters back in if needed
330
		if ( ! empty($params))
331
		{
332
			$translation = vsprintf($translation, $params);
333
		}
334
335
		return $translation;
336
	}
337
}
338