Passed
Push — dev ( b5e062...e11300 )
by Marcin
07:59
created

Validator::assertIsBool()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
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 2
	 * @return void
32
	 *
33 2
	 * @throws Ex\InvalidTypeException
34 1
	 * @throws Ex\NotBooleanException
35
	 *
36
	 */
37
	public static function assertIsBool(string $var_name, $value): void
38
	{
39
		self::assertIsType($var_name, $value, [Type::BOOLEAN], Ex\NotBooleanException::class);
40
	}
41
42
	/**
43
	 * Checks if given $val is of type integer
44
	 *
45
	 * @param string $var_name Name of the key to be used if exception is thrown.
46 103
	 * @param mixed  $value    Variable to be asserted.
47
	 *
48 103
	 * @return void
49 103
	 *
50
	 * @throws Ex\InvalidTypeException
51
	 * @throws Ex\NotIntegerException
52
	 */
53
	public static function assertIsInt(string $var_name, $value): void
54
	{
55
		self::assertIsType($var_name, $value, [Type::INTEGER], Ex\NotIntegerException::class);
56
	}
57
58
	/**
59
	 * Checks if given $val is of type array
60
	 *
61 24
	 * @param string $var_name Name of the key to be used if exception is thrown.
62
	 * @param mixed  $value    Variable to be asserted.
63 24
	 *
64 22
	 * @return void
65
	 *
66
	 * @throws Ex\InvalidTypeException
67
	 * @throws Ex\NotArrayException
68
	 */
69
	public static function assertIsArray(string $var_name, $value): void
70
	{
71
		self::assertIsType($var_name, $value, [Type::ARRAY], Ex\NotArrayException::class);
72
	}
73
74
	/**
75
	 * Checks if given $val is an object
76 14
	 *
77
	 * @param string $var_name Name of the key to be used if exception is thrown.
78 14
	 * @param mixed  $value    Variable to be asserted.
79 13
	 *
80
	 * @return void
81
	 *
82
	 * @throws Ex\InvalidTypeException
83
	 * @throws Ex\NotObjectException
84
	 */
85
	public static function assertIsObject(string $var_name, $value): void
86
	{
87
		self::assertIsType($var_name, $value, [Type::OBJECT], Ex\NotObjectException::class);
88
	}
89
90
	/**
91 13
	 * Checks if given $cls_cls_or_obj is either an object or name of existing class.
92
	 *
93 13
	 * @param string        $var_name
94 10
	 * @param string|object $cls_or_obj
95
	 *
96
	 * @return void
97
	 *
98
	 * @throws Ex\InvalidTypeException
99
	 * @throws Ex\ClassNotFound
100
	 */
101
	public static function assertIsObjectOrExistingClass(string $var_name, $cls_or_obj): void
102
	{
103
		self::assertIsType($var_name, $cls_or_obj, [Type::EXISTING_CLASS, Type::OBJECT]);
104
	}
105
106
	/**
107 103
	 * Checks if given $val is of type string
108
	 *
109 103
	 * @param string $var_name Label or name of the variable to be used in exception message (if thrown).
110
	 * @param mixed  $value    Variable to be asserted.
111 103
	 *
112 1
	 * @return void
113 1
	 *
114
	 * @throws Ex\InvalidTypeException
115
	 * @throws Ex\NotStringException
116 103
	 */
117 5
	public static function assertIsString(string $var_name, $value): void
118 5
	{
119
		self::assertIsType($var_name, $value, [Type::STRING], Ex\NotStringException::class);
120 103
	}
121
122
	/**
123
	 * @param string $var_name Label or name of the variable to be used in exception message (if thrown).
124
	 * @param mixed  $value    Variable to be asserted.
125
	 * @param int    $min      Min allowed value (inclusive)
126
	 * @param int    $max      Max allowed value (inclusive)
127
	 *
128
	 * @return void
129
	 *
130
	 * @throws \InvalidArgumentException
131
	 * @throws \RuntimeException
132
	 */
133
	public static function assertIsIntRange(string $var_name, $value, int $min, int $max): void
134 103
	{
135
		self::assertIsInt($var_name, $value);
136
137 103
		if ($min > $max) {
138 103
			throw new \InvalidArgumentException(
139 11
				\sprintf('%s: Invalid range for "%s". Ensure bound values are not swapped.', __FUNCTION__, $var_name));
140
		}
141 103
142
		if (($min > $value) || ($value > $max)) {
143
			throw new \OutOfBoundsException(
144
				\sprintf('Value of "%s" (%d) is out of bounds. Must be between %d-%d inclusive.', $var_name, $value, $min, $max));
145
		}
146
	}
147
148 10
	/**
149
	 * Checks if $item (of name $key) is of type that is include in $allowed_types (there's `OR` connection
150 10
	 * between specified types).
151 10
	 *
152 10
	 * @param string $var_name      Label or name of the variable to be used in exception message (if thrown).
153 10
	 * @param mixed  $value         Variable to be asserted.
154
	 * @param array  $allowed_types Array of allowed types for $value, i.e. [Type::INTEGER]
155
	 * @param string $ex_class      Name of exception class (which implements InvalidTypeExceptionContract) to
156
	 *                              be used when assertion fails. In that case object of that class will be
157
	 *                              instantiated and thrown.
158
	 *
159
	 * @return void
160 13
	 *
161
	 * @throws Ex\InvalidTypeException
162 13
	 */
163 13
	public static function assertIsType(string $var_name, $value, array $allowed_types,
164 13
	                                    string $ex_class = Ex\InvalidTypeException::class): void
165
	{
166
		// Type::EXISTING_CLASS is artificial type, so we need separate logic to handle it.
167
		$tmp = $allowed_types;
168
		$idx = array_search(Type::EXISTING_CLASS, $tmp, true);
169
		if ($idx !== false) {
170
			// Remove the type, so gettype() test loop won't see it.
171
			unset($tmp[$idx]);
172
			if (is_string($value) && class_exists($value)) {
173 4
				// It's existing class, no need to test further.
174
				return;
175 4
			}
176 1
		}
177 1
178
		$type = \gettype($value);
179
		if (!empty($tmp)) {
180 3
			if (!\in_array($type, $allowed_types, true)) {
181
				// FIXME we need to ensure $ex_class implements InvalidTypeExceptionContract at some point.
182
				throw new $ex_class($var_name, $type, $allowed_types);
183
			}
184
		} else {
185
			// FIXME we need to ensure $ex_class implements InvalidTypeExceptionContract at some point.
186
			throw new Ex\ClassNotFound($var_name, $type, $allowed_types);
187
		}
188
	}
189
190
	/**
191 11
	 * Ensures given $http_code is valid code for error response.
192
	 *
193 11
	 * @param int $http_code
194 11
	 */
195 11
	public static function assertErrorHttpCode(int $http_code): void
196 11
	{
197 7
		self::assertIsInt('http_code', $http_code);
198 1
		self::assertIsIntRange('http_code', $http_code,
199
			RB::ERROR_HTTP_CODE_MIN, RB::ERROR_HTTP_CODE_MAX);
200 6
	}
201
202 7
	/**
203 1
	 * Ensures given $http_code is valid for response indicating sucessful operation.
204
	 *
205 6
	 * @param int $http_code
206
	 */
207
	public static function assertOkHttpCode(int $http_code): void
208 9
	{
209
		self::assertIsInt('http_code', $http_code);
210
		self::assertIsIntRange('http_code', $http_code, 200, 299);
211
	}
212
213
	/**
214
	 * Ensures $obj (that is value coming from variable, which name is passed in $label) is instance of $cls class.
215
	 *
216
	 * @param string $var_name Name of variable that the $obj value is coming from. Used for exception message.
217
	 * @param object $obj      Object to check instance of
218
	 * @param string $cls      Target class we want to check $obj agains.
219
	 */
220
	public static function assertInstanceOf(string $var_name, object $obj, string $cls): void
221
	{
222
		if (!($obj instanceof $cls)) {
223
			throw new \InvalidArgumentException(
224
				\sprintf('"%s" must be instance of "%s".', $var_name, $cls)
225
			);
226
		}
227
	}
228
229
	/**
230
	 * Ensure that we either have array with user provided keys i.e. ['foo'=>'bar'], which will then
231
	 * be turned into JSON object or array without user specified keys (['bar']) which we would return as JSON
232
	 * array. But you can't mix these two as the final JSON would not produce predictable results.
233
	 *
234
	 * @param array $data
235
	 *
236
	 * @throws Ex\ArrayWithMixedKeysException
237
	 */
238
	public static function assertArrayHasNoMixedKeys(array $data): void
239
	{
240
		$string_keys_cnt = 0;
241
		$int_keys_cnt = 0;
242
		foreach (\array_keys($data) as $key) {
243
			if (\is_int($key)) {
244
				if ($string_keys_cnt > 0) {
245
					throw new Ex\ArrayWithMixedKeysException();
246
				}
247
				$int_keys_cnt++;
248
			} else {
249
				if ($int_keys_cnt > 0) {
250
					throw new Ex\ArrayWithMixedKeysException();
251
				}
252
				$string_keys_cnt++;
253
			}
254
		}
255
	}
256
}
257