Type   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 29
lcom 1
cbo 0
dl 0
loc 157
ccs 52
cts 52
cp 1
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A get_type() 0 3 1
A is_model() 0 8 2
A create_model() 0 3 1
A validate_elements() 0 5 2
B validate_element() 0 21 10
A determine() 0 15 6
A non_scalar_type_exists() 0 5 3
A determine_scalar() 0 15 3
1
<?php
2
3
namespace Intraxia\Jaxion\Axolotl;
4
5
use InvalidArgumentException;
6
use ReflectionClass;
7
8
/**
9
 * Class Type
10
 *
11
 * Responsible for validating new items against a type.
12
 *
13
 * @package    Intraxia\Jaxion
14
 * @subpackage Axolotl
15
 */
16
class Type {
17
18
	/**
19
	 * Type to validate against.
20
	 *
21
	 * @var string
22
	 */
23
	private $type;
24
25
	/**
26
	 * Type constructor.
27
	 *
28
	 * @param string $type
29
	 * @param bool   $key_type
30
	 */
31 417
	public function __construct( $type, $key_type = false ) {
32 417
		$this->type = $this->determine( $type, $key_type );
33 393
	}
34
35
	/**
36
	 * Get validation type.
37
	 *
38
	 * @return string
39
	 */
40 189
	public function get_type() {
41 189
		return $this->type;
42
	}
43
44
	/**
45
	 * Returns whether the type is an Axolotl model.
46
	 *
47
	 * @return bool
48
	 */
49 309
	public function is_model() {
50 309
		if ( ! class_exists( $this->type ) ) {
51 276
			return false;
52
		}
53
54 39
		$reflection = new ReflectionClass( $this->type );
55 39
		return $reflection->isSubclassOf( 'Intraxia\Jaxion\Axolotl\Model' );
56
	}
57
58
	/**
59
	 * Create a new model from the given data.
60
	 *
61
	 * @param array $data Data for the model.
62
	 *
63
	 * @return Model
64
	 */
65 9
	public function create_model( array $data ) {
66 9
		return new $this->type( $data );
67
	}
68
69
	/**
70
	 * Validates an array of element.
71
	 *
72
	 * @param array $elements Elements to be validated.
73
	 *
74
	 * @throws InvalidArgumentException
75
	 */
76 222
	public function validate_elements( array $elements ) {
77 222
		foreach ( $elements as $element ) {
78 222
			$this->validate_element( $element );
79 146
		}
80 219
	}
81
82
	/**
83
	 * Validate whether the
84
	 *
85
	 * @param mixed $element Element to validate.
86
	 *
87
	 * @throws InvalidArgumentException
88
	 */
89 351
	public function validate_element( $element ) {
90 351
		$type = gettype( $element );
91 351
		$callable = $this->type === 'callable';
92 351
		$is_object = 'object' === $type;
93 351
		$loose_check = $this->type === 'object';
94
95
		// callable must be callable
96 351
		if ( $callable && ! is_callable( $element ) ) {
97 3
			throw new InvalidArgumentException( 'Item must be callable' );
98
		}
99
100
		// target isn't callable, object must be an instance of target
101 351
		if ( ! $loose_check && ! $callable && $is_object && ! is_a( $element, $this->type ) ) {
102 3
			throw new InvalidArgumentException( "Item is not type or subtype of $this->type" );
103
		}
104
105
		// a non callable, non object type should match the target string
106 348
		if ( ! $callable && ! $is_object && $type !== $this->type ) {
107 15
			throw new InvalidArgumentException( "Item is not of type: $this->type" );
108
		}
109 339
	}
110
111
	/**
112
	 * Determine the type to validate against.
113
	 *
114
	 * @param string $type     Type to determine.
115
	 * @param bool   $key_type Whether the type is for keys.
116
	 *
117
	 * @return string
118
	 *
119
	 * @throws InvalidArgumentException
120
	 */
121 417
	private function determine( $type, $key_type = false ) {
122 417
		if ( ! $key_type && $this->non_scalar_type_exists( $type ) ) {
123 57
			return $type;
124
		}
125
126 372
		if ( $scalar_type = $this->determine_scalar( $type ) ) {
127 351
			if ( $key_type && (in_array( $scalar_type, array( 'double', 'boolean' ) )) ) {
128 3
				throw new InvalidArgumentException( 'This type is not supported as a key.' );
129
			}
130
131 348
			return $scalar_type;
132
		}
133
134 21
		throw new InvalidArgumentException( 'This type does not exist.' );
135
	}
136
137
	/**
138
	 * Determines whether the given type exists.
139
	 *
140
	 * @param string $type Type to check.
141
	 *
142
	 * @return bool
143
	 */
144 396
	private function non_scalar_type_exists( $type ) {
145 396
		return class_exists( $type )
146 363
				|| interface_exists( $type )
147 396
				|| in_array( $type, array( 'array', 'object', 'callable' ) );
148
	}
149
150
	/**
151
	 * Returns the type if it's scalar, otherwise, returns null.
152
	 *
153
	 * @param string $type Type to check.
154
	 *
155
	 * @return string|null
156
	 */
157 372
	private function determine_scalar( $type ) {
158
		$synonyms = array(
159 372
			'int' => 'integer',
160 248
			'float' => 'double',
161 248
			'bool' => 'boolean',
162 248
		);
163
164 372
		if ( array_key_exists( $type, $synonyms ) ) {
165 288
			$type = $synonyms[ $type ];
166 192
		}
167
168 372
		return in_array( $type, array( 'string', 'integer', 'double', 'boolean' ) ) ?
169 365
			$type :
170 372
			null;
171
	}
172
}
173