Passed
Push — master ( 52e05d...6f0859 )
by
unknown
08:31 queued 03:15
created

ConnectionException::getReasonPhrase()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 8
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Datamate\SeafileApi\Exception;
6
7
use Datamate\SeafileApi\Exception;
8
9
/**
10
 * Class ConnectionException.
11
 *
12
 * @internal
13
 */
14
final class ConnectionException extends Exception {
15
	/**
16
	 * HTTP status code of the response.
17
	 */
18
	private ?int $responseCode = null;
19
20
	/**
21
	 * HTTP raw response (if any).
22
	 */
23
	private ?string $responseBodyRaw = null;
24
25
	private const HTTP_STATUS = [
26
		200 => 'OK',
27
		201 => 'Created',
28
		202 => 'Accepted',
29
		301 => 'Moved Permanently',
30
		400 => 'Bad Request',
31
		401 => 'Unauthorized',
32
		403 => 'Forbidden',
33
		404 => 'Not Found',
34
		409 => 'Conflict',
35
		429 => 'Too Many Requests',
36
		440 => 'REPO_PASSWD_REQUIRED',
37
		441 => 'REPO_PASSWD_MAGIC_REQUIRED',
38
		500 => 'Internal Server Error',
39
		520 => 'OPERATION_FAILED',
40
	];
41
42
	/**
43
	 * @param int         $code       HTTP status code, e.g. curl_getinfo($curl)['http_code']
44
	 * @param bool|string $curlResult return value from curl_exec();
45
	 *
46
	 * @return no-return
0 ignored issues
show
Documentation Bug introduced by
The doc comment no-return at position 0 could not be parsed: Unknown type name 'no-return' at position 0 in no-return.
Loading history...
47
	 *
48
	 * @throws ConnectionException
49
	 */
50
	public static function throwCurlResult(int $code, bool|string $curlResult): never {
51
		$exception = new self(self::reasonPhrase($code), $code);
52
		$exception->responseCode = $code;
53
		$exception->responseBodyRaw = is_string($curlResult) ? $curlResult : null;
54
55
		throw $exception;
56
	}
57
58
	private static function reasonPhrase(int $code): string {
59
		return sprintf('%s %s', $code, self::HTTP_STATUS[$code] ?? "UNKNOWN_PHRASE");
60
	}
61
62
	public function __construct(string $message = "", int $code = 0, ?\Throwable $previous = null) {
63
		// trigger E_USER_NOTICE if code is not known
64
		$isHttpCode = $code >= 100 && $code < 600;
65
		$isKnownHttpCode = $isHttpCode && isset(self::HTTP_STATUS[$code]);
66
		$isKnownCode = $code === -1 || $isKnownHttpCode;
67
		$this->responseCode = $isHttpCode ? $code : null;
68
		$isKnownCode || trigger_error(sprintf("%s: Unknown code: %s (%s)", self::class, $code, gettype($code)), E_USER_NOTICE);
69
70
		parent::__construct($message, $code, $previous);
71
	}
72
73
	public function getStatusCode(): ?int {
74
		return $this->responseCode;
75
	}
76
77
	/**
78
	 * @throws ConnectionException
79
	 */
80
	public function assertStatusCode(int $code): void {
81
		if ($this->responseCode !== $code) {
82
			throw $this;
83
		}
84
	}
85
86
	/**
87
	 * The raw response body.
88
	 */
89
	public function getRawResponse(): ?string {
90
		return $this->responseBodyRaw;
91
	}
92
93
	/**
94
	 * Response body parsed as JSON Object.
95
	 *
96
	 * @return null|object - null either if the parsed response is NULL or if it can't be parsed as object
97
	 */
98
	public function tryParsedResponse(): ?object {
99
		/**
100
		 * @noinspection JsonEncodingApiUsageInspection
101
		 * @noinspection RedundantSuppression
102
		 */
103
		$result = json_decode((string) $this->responseBodyRaw, false);
104
105
		return is_object($result) ? $result : null;
106
	}
107
108
	public function getReasonPhrase(): ?string {
109
		$code = $this->responseCode;
110
111
		if (!is_int($code)) {
112
			return null;
113
		}
114
115
		return self::HTTP_STATUS[$code] ?? null;
116
	}
117
118
	/**
119
	 * A seafile JSON response may contain error information, try to get them.
120
	 *
121
	 * It is not fool-proof or overly complete but often more informative than just looking at JSON response dumps.
122
	 *
123
	 * @return null|array|string[] messages, null if n/a otherwise array of messages (which can not be empty)
124
	 */
125
	public function tryApiErrorMessages(): ?array {
126
		$response = $this->tryParsedResponse();
127
		if ($response === null) {
128
			return null;
129
		}
130
131
		$buffer = [];
132
133
		// {"error_msg": ... }
134
		if (isset($response->error_msg) && is_string($response->error_msg)) {
135
			$buffer[] = $response->error_msg;
136
		}
137
138
		// {"detail": "Invalid token header. No credentials provided."}
139
		if (isset($response->detail) && is_string($response->detail)) {
140
			$buffer[] = $response->detail;
141
		}
142
143
		// {"non_field_errors": [ "string...", ...]}
144
		if (isset($response->non_field_errors) && is_array($response->non_field_errors)) {
145
			foreach ($response->non_field_errors as $message) {
146
				if (is_string($message)) {
147
					$buffer[] = $message;
148
				}
149
			}
150
		}
151
152
		return empty($buffer) ? null : $buffer;
153
	}
154
}
155