Validator   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 11
Bugs 0 Features 1
Metric Value
wmc 20
eloc 35
c 11
b 0
f 1
dl 0
loc 190
ccs 45
cts 45
cp 1
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A assertIsArray() 0 3 1
A assertIsString() 0 3 1
A assertIsBool() 0 3 1
A assertIsObject() 0 3 1
A assertIsInt() 0 3 1
A assertInstanceOf() 0 5 2
A assertOkHttpCode() 0 4 1
A assertIsType() 0 7 2
A assertArrayHasNoMixedKeys() 0 15 5
A assertIsIntRange() 0 12 4
A assertErrorHttpCode() 0 5 1
1
<?php
2
declare(strict_types=1);
3
4
namespace MarcinOrlowski\ResponseBuilder;
5
6
/**
7
 * Laravel API Response Builder
8
 *
9
 * @package   MarcinOrlowski\ResponseBuilder
10
 *
11
 * @author    Marcin Orlowski <mail (#) marcinOrlowski (.) com>
12
 * @copyright 2016-2021 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 MarcinOrlowski\ResponseBuilder\Exceptions as Ex;
18
use MarcinOrlowski\ResponseBuilder\ResponseBuilder as RB;
19
20
/**
21
 * Data validator helper
22
 */
23
final class Validator
24
{
25
	/**
26
	 * Checks if given $val is of type boolean
27
	 *
28
	 * @param string $var_name Name of the key to be used if exception is thrown.
29
	 * @param mixed  $value    Variable to be asserted.
30
	 *
31
	 * @return void
32
	 *
33
	 * @throws \InvalidArgumentException
34
	 */
35
	public static function assertIsBool(string $var_name, $value): void
36
	{
37
		self::assertIsType($var_name, $value, [Type::BOOLEAN], Ex\NotBooleanException::class);
38
	}
39
40
	/**
41
	 * Checks if given $val is of type integer
42
	 *
43
	 * @param string $var_name Name of the key to be used if exception is thrown.
44
	 * @param mixed  $value    Variable to be asserted.
45
	 *
46 2
	 * @return void
47
	 *
48 2
	 * @throws \InvalidArgumentException
49 1
	 */
50
	public static function assertIsInt(string $var_name, $value): void
51
	{
52
		self::assertIsType($var_name, $value, [Type::INTEGER], Ex\NotBooleanException::class);
53
	}
54
55
	/**
56
	 * Checks if given $val is of type array
57
	 *
58
	 * @param string $var_name Name of the key to be used if exception is thrown.
59
	 * @param mixed  $value    Variable to be asserted.
60
	 *
61 87
	 * @return void
62
	 *
63 87
	 * @throws \InvalidArgumentException
64 87
	 */
65
	public static function assertIsArray(string $var_name, $value): void
66
	{
67
		self::assertIsType($var_name, $value, [Type::ARRAY], Ex\NotArrayException::class);
68
	}
69
70
	/**
71
	 * Checks if given $val is an object
72
	 *
73
	 * @param string $var_name Name of the key to be used if exception is thrown.
74
	 * @param mixed  $value    Variable to be asserted.
75
	 *
76 2
	 * @return void
77
	 *
78 2
	 * @throws \InvalidArgumentException
79 1
	 */
80
	public static function assertIsObject(string $var_name, $value): void
81
	{
82
		self::assertIsType($var_name, $value, [Type::OBJECT], Ex\NotObjectException::class);
83
	}
84
85
	/**
86
	 * Checks if given $val is of type string
87
	 *
88
	 * @param string $var_name Label or name of the variable to be used in exception message (if thrown).
89
	 * @param mixed  $value    Variable to be asserted.
90
	 *
91 14
	 * @return void
92
	 *
93 14
	 * @throws \InvalidArgumentException
94 13
	 */
95
	public static function assertIsString(string $var_name, $value): void
96
	{
97
		self::assertIsType($var_name, $value, [Type::STRING], Ex\NotStringException::class);
98
	}
99
100
	/**
101
	 * @param string $var_name Label or name of the variable to be used in exception message (if thrown).
102
	 * @param mixed  $value    Variable to be asserted.
103
	 * @param int    $min      Min allowed value (inclusive)
104
	 * @param int    $max      Max allowed value (inclusive)
105
	 *
106 13
	 * @return void
107
	 *
108 13
	 * @throws \InvalidArgumentException
109 10
	 * @throws \RuntimeException
110
	 */
111
	public static function assertIsIntRange(string $var_name, $value, int $min, int $max): void
112
	{
113
		self::assertIsInt($var_name, $value);
114
115
		if ($min > $max) {
116
			throw new \InvalidArgumentException(
117
				\sprintf('%s: Invalid range for "%s". Ensure bound values are not swapped.', __FUNCTION__, $var_name));
118
		}
119
120
		if (($min > $value) || ($value > $max)) {
121
			throw new \OutOfBoundsException(
122 87
				\sprintf('Value of "%s" (%d) is out of bounds. Must be between %d-%d inclusive.', $var_name, $value, $min, $max));
123
		}
124 87
	}
125
126 87
	/**
127 1
	 * Checks if $item (of name $key) is of type that is include in $allowed_types.
128 1
	 *
129
	 * @param string $var_name      Label or name of the variable to be used in exception message (if thrown).
130
	 * @param mixed  $value         Variable to be asserted.
131 87
	 * @param array  $allowed_types Array of allowed types for $value, i.e. [Type::INTEGER]
132 4
	 * @param string $ex_class      Name of exception class (which implements InvalidTypeExceptionContract) to
133 4
	 *                              be used when assertion fails. In that case object of that class will be
134
	 *                              instantiated and thrown.
135 87
	 *
136
	 * @return void
137
	 *
138
	 * @throws \InvalidArgumentException
139
	 */
140
	public static function assertIsType(string $var_name, $value, array $allowed_types,
141
	                                    string $ex_class = Ex\InvalidTypeException::class): void
142
	{
143
		$type = \gettype($value);
144
		if (!\in_array($type, $allowed_types, true)) {
145
			// FIXME we need to ensure $ex_class implements InvalidTypeExceptionContract at some point.
146
			throw new $ex_class($var_name, $type, $allowed_types);
147
		}
148 87
	}
149
150 87
	/**
151 87
	 * Ensures given $http_code is valid code for error response.
152 13
	 *
153 13
	 * @param int $http_code
154 13
	 */
155
	public static function assertErrorHttpCode(int $http_code): void
156
	{
157 87
		self::assertIsInt('http_code', $http_code);
158
		self::assertIsIntRange('http_code', $http_code,
159
			RB::ERROR_HTTP_CODE_MIN, RB::ERROR_HTTP_CODE_MAX);
160
	}
161
162
	/**
163
	 * Ensures given $http_code is valid for response indicating sucessful operation.
164 10
	 *
165
	 * @param int $http_code
166 10
	 */
167 10
	public static function assertOkHttpCode(int $http_code): void
168 10
	{
169 10
		self::assertIsInt('http_code', $http_code);
170
		self::assertIsIntRange('http_code', $http_code, 200, 299);
171
	}
172
173
	/**
174
	 * Ensures $obj (that is value coming from variable, which name is passed in $label) is instance of $cls class.
175
	 *
176 8
	 * @param string $var_name Name of variable that the $obj value is coming from. Used for exception message.
177
	 * @param object $obj      Object to check instance of
178 8
	 * @param string $cls      Target class we want to check $obj agains.
179 8
	 */
180 8
	public static function assertInstanceOf(string $var_name, object $obj, string $cls): void
181
	{
182
		if (!($obj instanceof $cls)) {
183
			throw new \InvalidArgumentException(
184
				\sprintf('"%s" must be instance of "%s".', $var_name, $cls)
185
			);
186
		}
187
	}
188
189 4
	/**
190
	 * Ensure that we either have array with user provided keys i.e. ['foo'=>'bar'], which will then
191 4
	 * be turned into JSON object or array without user specified keys (['bar']) which we would return as JSON
192 1
	 * array. But you can't mix these two as the final JSON would not produce predictable results.
193 1
	 *
194
	 * @param array $data
195
	 *
196 3
	 * @throws Ex\ArrayWithMixedKeysException
197
	 */
198
	public static function assertArrayHasNoMixedKeys(array $data): void
199
	{
200
		$string_keys_cnt = 0;
201
		$int_keys_cnt = 0;
202
		foreach (\array_keys($data) as $key) {
203
			if (\is_int($key)) {
204
				if ($string_keys_cnt > 0) {
205
					throw new Ex\ArrayWithMixedKeysException();
206
				}
207
				$int_keys_cnt++;
208
			} else {
209
				if ($int_keys_cnt > 0) {
210
					throw new Ex\ArrayWithMixedKeysException();
211
				}
212
				$string_keys_cnt++;
213
			}
214
		}
215
	}
216
}
217