Completed
Pull Request — master (#15)
by James
05:52 queued 02:57
created

Type::determine()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 8
nc 4
nop 2
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
	public function __construct( $type ) {
31
		$this->type = $this->determine( $type );
32
	}
33
34
	/**
35
	 * Get validation type.
36
	 *
37
	 * @return string
38
	 */
39
	public function get_type() {
40
		return $this->type;
41
	}
42
43
	/**
44
	 * Returns whether the type is an Axolotl model.
45
	 *
46
	 * @return bool
47
	 */
48
	public function is_model() {
49
		if ( ! class_exists( $this->type ) ) {
50
			return false;
51
		}
52
53
		$reflection = new ReflectionClass( $this->type );
54
		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
	public function create_model( array $data ) {
65
		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
	public function validate_elements( array $elements ) {
76
		foreach ( $elements as $element ) {
77
			$this->validate_element( $element );
78
		}
79
	}
80
81
	/**
82
	 * Validate whether the
83
	 *
84
	 * @param mixed $element Element to validate.
85
	 *
86
	 * @throws InvalidArgumentException
87
	 */
88
	public function validate_element( $element ) {
89
		$type = gettype( $element );
90
		$callable = $this->type === 'callable';
91
		$is_object = 'object' === $type;
92
		$loose_check = $this->type === 'object';
93
94
		// callable must be callable
95
		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
		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
		if ( ! $callable && ! $is_object && $type !== $this->type ) {
106
			throw new InvalidArgumentException( "Item is not of type: $this->type" );
107
		}
108
	}
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
	private function determine( $type, $key_type = false ) {
121
		if ( ! $key_type && $this->non_scalar_type_exists( $type ) ) {
122
			return $type;
123
		}
124
125
		if ( $scalar_type = $this->determine_scalar( $type ) ) {
126
			if ( $key_type && (in_array( $scalar_type, [ 'double', 'boolean' ] )) ) {
127
				throw new InvalidArgumentException( 'This type is not supported as a key.' );
128
			}
129
130
			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
	private function non_scalar_type_exists( $type ) {
144
		return class_exists( $type )
145
				|| interface_exists( $type )
146
				|| in_array( $type, [ '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
	private function determine_scalar( $type ) {
157
		$synonyms = array(
158
			'int' => 'integer',
159
			'float' => 'double',
160
			'bool' => 'boolean',
161
		);
162
163
		if ( array_key_exists( $type, $synonyms ) ) {
164
			$type = $synonyms[ $type ];
165
		}
166
167
		return in_array( $type, array( 'string', 'integer', 'double', 'boolean' ) ) ?
168
			$type :
169
			null;
170
	}
171
}
172