Completed
Pull Request — master (#15)
by James
05:56
created

Type   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 156
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 88.46%

Importance

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