Passed
Push — master ( a32cdb...3df0af )
by Atanas
01:56
created

ConditionFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
crap 2
1
<?php
2
3
namespace WPEmerge\Routing\Conditions;
4
5
use Closure;
6
use Exception;
7
use ReflectionClass;
8
use WPEmerge\Facades\Framework;
9
use WPEmerge\Routing\Conditions\CustomCondition;
10
use WPEmerge\Routing\Conditions\MultipleCondition;
11
use WPEmerge\Routing\Conditions\UrlCondition;
12
13
/**
14
 * Check against the current url
15
 */
16
class ConditionFactory {
17
	const NEGATE_CONDITION_PREFIX = '!';
18
19
	/**
20
	 * Registered condition types.
21
	 *
22
	 * @var array<string, string>
23
	 */
24
	protected $condition_types = [];
25
26
	/**
27
	 * Constructor.
28
	 *
29
	 * @codeCoverageIgnore
30
	 * @param array<string, string> $condition_types
31
	 */
32
	public function __construct( $condition_types ) {
33
		$this->condition_types = $condition_types;
34
	}
35
36
	/**
37
	 * Create a new condition.
38
	 *
39
	 * @throws InvalidRouteConditionException
40
	 * @param  string|array|Closure           $options
41
	 * @return ConditionInterface
42
	 */
43 13
	public function make( $options ) {
44 13
		if ( is_string( $options ) ) {
45 2
			return $this->makeFromUrl( $options );
46
		}
47
48 11
		if ( is_array( $options ) ) {
49 9
			return $this->makeFromArray( $options );
50
		}
51
52 2
		if ( $options instanceof Closure ) {
53 1
			return $this->makeFromClosure( $options );
54
		}
55
56 1
		throw new InvalidRouteConditionException( 'Invalid condition options supplied.' );
57
	}
58
59
	/**
60
	 * Get condition class for condition type.
61
	 *
62
	 * @param  string      $condition_type
63
	 * @return string|null
64
	 */
65
	protected function getConditionTypeClass( $condition_type ) {
66
		if ( ! isset( $this->condition_types[ $condition_type ] ) ) {
67
			return null;
68
		}
69
70
		return $this->condition_types[ $condition_type ];
71
	}
72
73
	/**
74
	 * Check if the passed argument is a registered condition type.
75
	 *
76
	 * @param  mixed   $condition_type
77
	 * @return boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be false|string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
78
	 */
79 6
	protected function conditionTypeRegistered( $condition_type ) {
80 6
		if ( ! is_string( $condition_type ) ) {
81 1
			return false;
82
		}
83
84 5
		return $this->getConditionTypeClass( $condition_type );
85
	}
86
87
	/**
88
	 * Check if a condition is negated.
89
	 *
90
	 * @param  mixed   $condition
91
	 * @return boolean
92
	 */
93 1
	protected function isNegatedCondition( $condition ) {
94
		return (
95 1
			is_string( $condition )
96 1
			&&
97 1
			substr( $condition, 0, strlen( static::NEGATE_CONDITION_PREFIX ) ) === static::NEGATE_CONDITION_PREFIX
98 1
		);
99
	}
100
101
	/**
102
	 * Parse a negated condition and its arguments.
103
	 *
104
	 * @param  string $type
105
	 * @param  array  $arguments
106
	 * @return array
107
	 */
108 1
	protected function parseNegatedCondition( $type, $arguments ) {
109 1
		$negated_type = substr( $type, strlen( static::NEGATE_CONDITION_PREFIX ) );
110 1
		$arguments = array_merge( [ $negated_type ], $arguments );
111 1
		$type = 'negate';
112
113 1
		return ['type' => $type, 'arguments' => $arguments];
114
	}
115
116
	/**
117
	 * Parse the condition type and its arguments from an options array.
118
	 *
119
	 * @throws Exception
120
	 * @param  array $options
121
	 * @return array
122
	 */
123 7
	protected function parseConditionOptions( $options ) {
124 7
		$type = $options[0];
125 7
		$arguments = array_values( array_slice( $options, 1 ) );
126
127 7
		if ( $this->isNegatedCondition( $type ) ) {
128 1
			return $this->parseNegatedCondition( $type, $arguments );
129
		}
130
131 7
		if ( ! $this->conditionTypeRegistered( $type ) ) {
132 3
			if ( is_callable( $type ) ) {
133 2
				return ['type' => 'custom', 'arguments' => $options];
134
			}
135
136 1
			throw new Exception( 'Unknown condition type specified: ' . $type );
137
		}
138
139 4
		return ['type' => $type, 'arguments' => $arguments ];
140
	}
141
142
	/**
143
	 * Create a new condition from a url.
144
	 *
145
	 * @param  string             $url
146
	 * @return ConditionInterface
147
	 */
148 1
	protected function makeFromUrl( $url ) {
149 1
		return new UrlCondition( $url );
150
	}
151
152
	/**
153
	 * Create a new condition from an array.
154
	 *
155
	 * @throws Exception
156
	 * @param  array               $options
157
	 * @return ConditionInterface
158
	 */
159 9
	protected function makeFromArray( $options ) {
160 9
		if ( count( $options ) === 0 ) {
161 1
			throw new Exception( 'No condition type specified.' );
162
		}
163
164 8
		if ( is_array( $options[0] ) ) {
165 1
			return $this->makeFromArrayOfConditions( $options );
166
		}
167
168 8
		$condition_options = $this->parseConditionOptions( $options );
169 7
		$condition_class = $this->getConditionTypeClass( $condition_options['type'] );
170
171 7
		$reflection = new ReflectionClass( $condition_class );
172 7
		$condition = $reflection->newInstanceArgs( $condition_options['arguments'] );
173 7
		return $condition;
174
	}
175
176
	/**
177
	 * Create a new condition from an array of conditions.
178
	 *
179
	 * @param  array               $options
180
	 * @return ConditionInterface
181
	 */
182 1
	protected function makeFromArrayOfConditions( $options ) {
183 1
		return new MultipleCondition( $options );
184
	}
185
186
	/**
187
	 * Create a new condition from a closure.
188
	 *
189
	 * @param  Closure            $closure
190
	 * @return ConditionInterface
191
	 */
192 1
	protected function makeFromClosure( Closure $closure ) {
193 1
		return new CustomCondition( $closure );
194
	}
195
}
196