Completed
Push — master ( 076975...358320 )
by Marcin
13s queued 11s
created

ExceptionHandlerHelper::error()   B

Complexity

Conditions 9
Paths 144

Size

Total Lines 76
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 9

Importance

Changes 15
Bugs 0 Features 0
Metric Value
cc 9
eloc 38
c 15
b 0
f 0
nc 144
nop 4
dl 0
loc 76
ccs 37
cts 37
cp 1
crap 9
rs 7.4631

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace MarcinOrlowski\ResponseBuilder;
5
6
/**
7
 * Exception handler using ResponseBuilder to return JSON even in such hard tines
8
 *
9
 * @package   MarcinOrlowski\ResponseBuilder
10
 *
11
 * @author    Marcin Orlowski <mail (#) marcinOrlowski (.) com>
12
 * @copyright 2016-2019 Marcin Orlowski
13
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
14
 * @link      https://github.com/MarcinOrlowski/laravel-api-response-builder
15
 */
16
17
use Exception;
18
use Illuminate\Auth\AuthenticationException;
19
use Illuminate\Support\Facades\Config;
20
use Illuminate\Support\Facades\Lang;
21
use Illuminate\Validation\ValidationException;
22
use Symfony\Component\HttpFoundation\Response as HttpResponse;
23
use Symfony\Component\HttpKernel\Exception\HttpException;
24
25
/**
26
 * Class ExceptionHandlerHelper
27
 */
28
class ExceptionHandlerHelper
29
{
30
	/**
31
	 * Exception types
32
	 */
33
	public const TYPE_HTTP_NOT_FOUND_KEY           = 'http_not_found';
34
	public const TYPE_HTTP_SERVICE_UNAVAILABLE_KEY = 'http_service_unavailable';
35
	public const TYPE_HTTP_UNAUTHORIZED_KEY        = 'authentication_exception';
36
	public const TYPE_HTTP_EXCEPTION_KEY           = 'http_exception';
37
	public const TYPE_VALIDATION_EXCEPTION_KEY     = 'validation_exception';
38
	public const TYPE_UNCAUGHT_EXCEPTION_KEY       = 'uncaught_exception';
39
	public const TYPE_AUTHENTICATION_EXCEPTION_KEY = 'authentication_exception';
40
41
	/**
42
	 * Render an exception into valid API response.
43
	 *
44
	 * @param \Illuminate\Http\Request $request   Request object
45
	 * @param \Exception               $exception Exception
46
	 *
47
	 * @return HttpResponse
48
	 */
49 2
	public static function render(/** @scrutinizer ignore-unused */ $request, Exception $exception): HttpResponse
50
	{
51 2
		$result = null;
52
53 2
		if ($exception instanceof HttpException) {
54 1
			switch ($exception->getStatusCode()) {
55 1
				case HttpResponse::HTTP_NOT_FOUND:
56 1
					$result = static::error($exception, static::TYPE_HTTP_NOT_FOUND_KEY,
57 1
						BaseApiCodes::EX_HTTP_NOT_FOUND(), HttpResponse::HTTP_NOT_FOUND);
58 1
					break;
59
60 1
				case HttpResponse::HTTP_SERVICE_UNAVAILABLE:
61 1
					$result = static::error($exception, static::TYPE_HTTP_SERVICE_UNAVAILABLE_KEY,
62 1
						BaseApiCodes::EX_HTTP_SERVICE_UNAVAILABLE(), HttpResponse::HTTP_SERVICE_UNAVAILABLE);
63 1
					break;
64
65 1
				case HttpResponse::HTTP_UNAUTHORIZED:
66 1
					$result = static::error($exception, static::TYPE_HTTP_UNAUTHORIZED_KEY,
67 1
						BaseApiCodes::EX_AUTHENTICATION_EXCEPTION(), HttpResponse::HTTP_UNAUTHORIZED);
68 1
					break;
69
70
				default:
71 1
					$result = static::error($exception, static::TYPE_HTTP_EXCEPTION_KEY,
72 1
						BaseApiCodes::EX_HTTP_EXCEPTION(), HttpResponse::HTTP_BAD_REQUEST);
73 1
					break;
74
			}
75 2
		} elseif ($exception instanceof ValidationException) {
76 1
			$result = static::error($exception, static::TYPE_VALIDATION_EXCEPTION_KEY,
77 1
				BaseApiCodes::EX_VALIDATION_EXCEPTION(), HttpResponse::HTTP_BAD_REQUEST);
78
		}
79
80 2
		if ($result === null) {
81 2
			$result = static::error($exception, static::TYPE_UNCAUGHT_EXCEPTION_KEY,
82 2
				BaseApiCodes::EX_UNCAUGHT_EXCEPTION(), HttpResponse::HTTP_INTERNAL_SERVER_ERROR);
83
		}
84
85 2
		return $result;
86
	}
87
88
	/**
89
	 * Convert an authentication exception into an unauthenticated response.
90
	 *
91
	 * @param \Illuminate\Http\Request                 $request
92
	 * @param \Illuminate\Auth\AuthenticationException $exception
93
	 *
94
	 * @return HttpResponse
95
	 */
96 1
	protected function unauthenticated(/** @scrutinizer ignore-unused */ $request, AuthenticationException $exception): HttpResponse
97
	{
98 1
		return static::error($exception, 'authentication_exception', BaseApiCodes::EX_AUTHENTICATION_EXCEPTION());
99
	}
100
101
	/**
102
	 * Process singe error and produce valid API response
103
	 *
104
	 * @param Exception $exception            Exception to be processed
105
	 * @param string    $exception_config_key Category of the exception
106
	 * @param integer   $fallback_api_code    API code to return
107
	 * @param integer   $fallback_http_code   HTTP code to return
108
	 *
109
	 * @return HttpResponse
110
	 */
111 5
	protected static function error(Exception $exception, $exception_config_key, $fallback_api_code,
112
	                                $fallback_http_code = ResponseBuilder::DEFAULT_HTTP_CODE_ERROR): HttpResponse
113
	{
114
		// common prefix for config key
115 5
		$base_config_key = sprintf('%s.exception', ResponseBuilder::CONF_EXCEPTION_HANDLER_KEY);
116
117
		// get API and HTTP codes from exception handler config or use fallback values if no such
118
		// config fields are set.
119 5
		$api_code = Config::get("{$base_config_key}.{$exception_config_key}.code", $fallback_api_code);
120 5
		$http_code = Config::get("{$base_config_key}.{$exception_config_key}.http_code", $fallback_http_code);
121
122
		// check if we now have valid HTTP error code for this case or need to make one up.
123 5
		if ($http_code < ResponseBuilder::ERROR_HTTP_CODE_MIN) {
124
			// no code, let's try to get the exception status
125 2
			$http_code = ($exception instanceof \Symfony\Component\HttpKernel\Exception\HttpException)
126 2
				? $exception->getStatusCode()
127 2
				: $exception->getCode();
128
		}
129
130
		// can it be considered valid HTTP error code?
131 5
		if ($http_code < ResponseBuilder::ERROR_HTTP_CODE_MIN) {
132 1
			$http_code = $fallback_http_code;
133
		}
134
135
		// let's figure out what event we are handling now
136
		$known_codes = [
137 5
			self::TYPE_HTTP_NOT_FOUND_KEY           => BaseApiCodes::EX_HTTP_NOT_FOUND(),
138 5
			self::TYPE_HTTP_SERVICE_UNAVAILABLE_KEY => BaseApiCodes::EX_HTTP_SERVICE_UNAVAILABLE(),
139 5
			self::TYPE_UNCAUGHT_EXCEPTION_KEY       => BaseApiCodes::EX_UNCAUGHT_EXCEPTION(),
140 5
			self::TYPE_AUTHENTICATION_EXCEPTION_KEY => BaseApiCodes::EX_AUTHENTICATION_EXCEPTION(),
141 5
			self::TYPE_VALIDATION_EXCEPTION_KEY     => BaseApiCodes::EX_VALIDATION_EXCEPTION(),
142 5
			self::TYPE_HTTP_EXCEPTION_KEY           => BaseApiCodes::EX_HTTP_EXCEPTION(),
143
		];
144 5
		$base_api_code = BaseApiCodes::NO_ERROR_MESSAGE();
145 5
		foreach ($known_codes as $item_config_key => $item_api_code) {
146 5
			if ($api_code === Config::get("{$base_config_key}.{$item_config_key}.code", $item_api_code)) {
147 5
				$base_api_code = $api_code;
148 5
				break;
149
			}
150
		}
151
152
		/** @var array|null $data Optional payload to return */
153 5
		$data = null;
154 5
		if ($api_code === Config::get("{$base_config_key}.validation_exception.code", BaseApiCodes::EX_VALIDATION_EXCEPTION())) {
155
			/** @var ValidationException $exception */
156 1
			$data = [ResponseBuilder::KEY_MESSAGES => $exception->validator->errors()->messages()];
157
		}
158
159 5
		$key = BaseApiCodes::getCodeMessageKey($api_code) ?? BaseApiCodes::getCodeMessageKey($base_api_code);
160
161
		// let's build error error_message
162 5
		$error_message = $exception->getMessage();
163
164
		// if we do not have any error_message in the hand yet, we need to fall back to built-in string configured
165
		// for this particular exception.
166 5
		if ($error_message === '') {
167 4
			$error_message = Lang::get($key, [
168 4
				'api_code' => $api_code,
169 4
				'message'  => get_class($exception),
170
			]);
171
		}
172
173
		// if we have trace data debugging enabled, let's gather some debug
174
		// info and add to the response.
175 5
		$trace_data = null;
176 5
		if (Config::get(ResponseBuilder::CONF_KEY_DEBUG_EX_TRACE_ENABLED, false)) {
177
			$trace_data = [
178 1
				Config::get(ResponseBuilder::CONF_KEY_DEBUG_EX_TRACE_KEY, ResponseBuilder::KEY_TRACE) => [
179 1
					ResponseBuilder::KEY_CLASS => get_class($exception),
180 1
					ResponseBuilder::KEY_FILE  => $exception->getFile(),
181 1
					ResponseBuilder::KEY_LINE  => $exception->getLine(),
182
				],
183
			];
184
		}
185
186 5
		return ResponseBuilder::errorWithMessageAndDataAndDebug($api_code, $error_message, $data, $http_code, null, $trace_data);
187
	}
188
189
}
190