Passed
Push — master ( 685651...ff2960 )
by Atanas
02:52
created

ConditionFactory::condition()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package   WPEmerge
4
 * @author    Atanas Angelov <[email protected]>
5
 * @copyright 2018 Atanas Angelov
6
 * @license   https://www.gnu.org/licenses/gpl-2.0.html GPL-2.0
7
 * @link      https://wpemerge.com/
8
 */
9
10
namespace WPEmerge\Routing\Conditions;
11
12
use Closure;
13
use ReflectionClass;
14
use ReflectionException;
15
use WPEmerge\Exceptions\Exception;
16
17
/**
18
 * Check against the current url
19
 */
20
class ConditionFactory {
21
	const NEGATE_CONDITION_PREFIX = '!';
22
23
	/**
24
	 * Registered condition types.
25
	 *
26
	 * @var array<string, string>
27
	 */
28
	protected $condition_types = [];
29
30
	/**
31
	 * Constructor.
32
	 *
33
	 * @codeCoverageIgnore
34
	 * @param array<string, string> $condition_types
35
	 */
36
	public function __construct( $condition_types ) {
37
		$this->condition_types = $condition_types;
38
	}
39
40
	/**
41
	 * Create a new condition.
42
	 *
43
	 * @throws Exception
44
	 * @throws InvalidRouteConditionException
45
	 * @param  string|array|Closure           $options
46
	 * @return ConditionInterface
47
	 */
48 14
	public function make( $options ) {
49 14
		if ( is_string( $options ) ) {
50 2
			return $this->makeFromUrl( $options );
51
		}
52
53 12
		if ( is_array( $options ) ) {
54 10
			return $this->makeFromArray( $options );
55
		}
56
57 2
		if ( $options instanceof Closure ) {
58 1
			return $this->makeFromClosure( $options );
59
		}
60
61 1
		throw new InvalidRouteConditionException( 'Invalid condition options supplied.' );
62
	}
63
64
	/**
65
	 * Ensure value is a condition.
66
	 *
67
	 * @param  string|array|Closure|ConditionInterface $options
68
	 * @return ConditionInterface
69
	 */
70 1
	public function condition( $value ) {
71 1
		if ( $value instanceof ConditionInterface ) {
72 1
			return $value;
73
		}
74
75 1
		return $this->make( $value );
76
	}
77
78
	/**
79
	 * Get condition class for condition type.
80
	 *
81
	 * @param  string      $condition_type
82
	 * @return string|null
83
	 */
84 2
	protected function getConditionTypeClass( $condition_type ) {
85 2
		if ( ! isset( $this->condition_types[ $condition_type ] ) ) {
86 1
			return null;
87
		}
88
89 1
		return $this->condition_types[ $condition_type ];
90
	}
91
92
	/**
93
	 * Check if the passed argument is a registered condition type.
94
	 *
95
	 * @param  mixed   $condition_type
96
	 * @return boolean
97
	 */
98 6
	protected function conditionTypeRegistered( $condition_type ) {
99 6
		if ( ! is_string( $condition_type ) ) {
100 1
			return false;
101
		}
102
103 5
		return $this->getConditionTypeClass( $condition_type ) !== null;
104
	}
105
106
	/**
107
	 * Check if a condition is negated.
108
	 *
109
	 * @param  mixed   $condition
110
	 * @return boolean
111
	 */
112 1
	protected function isNegatedCondition( $condition ) {
113
		return (
114 1
			is_string( $condition )
115 1
			&&
116 1
			strpos( $condition, static::NEGATE_CONDITION_PREFIX ) === 0
117 1
		);
118
	}
119
120
	/**
121
	 * Parse a negated condition and its arguments.
122
	 *
123
	 * @param  string $type
124
	 * @param  array  $arguments
125
	 * @return array
126
	 */
127 1
	protected function parseNegatedCondition( $type, $arguments ) {
128 1
		$negated_type = substr( $type, strlen( static::NEGATE_CONDITION_PREFIX ) );
129 1
		$arguments = array_merge( [ $negated_type ], $arguments );
130
131 1
		$type = 'negate';
132 1
		$condition = call_user_func( [$this, 'make'], $arguments );
133
134 1
		return ['type' => $type, 'arguments' => [$condition]];
135
	}
136
137
	/**
138
	 * Parse the condition type and its arguments from an options array.
139
	 *
140
	 * @throws Exception
141
	 * @param  array $options
142
	 * @return array
143
	 */
144 7
	protected function parseConditionOptions( $options ) {
145 7
		$type = $options[0];
146 7
		$arguments = array_values( array_slice( $options, 1 ) );
147
148 7
		if ( $this->isNegatedCondition( $type ) ) {
149 1
			return $this->parseNegatedCondition( $type, $arguments );
150
		}
151
152 7
		if ( ! $this->conditionTypeRegistered( $type ) ) {
153 3
			if ( is_callable( $type ) ) {
154 2
				return ['type' => 'custom', 'arguments' => $options];
155
			}
156
157 1
			throw new Exception( 'Unknown condition type specified: ' . $type );
158
		}
159
160 4
		return ['type' => $type, 'arguments' => $arguments ];
161
	}
162
163
	/**
164
	 * Create a new condition from a url.
165
	 *
166
	 * @param  string             $url
167
	 * @return ConditionInterface
168
	 */
169 1
	protected function makeFromUrl( $url ) {
170 1
		return new UrlCondition( $url );
171
	}
172
173
	/**
174
	 * Create a new condition from an array.
175
	 *
176
	 * @throws Exception
177
	 * @param  array               $options
178
	 * @return ConditionInterface
179
	 */
180 10
	protected function makeFromArray( $options ) {
181 10
		if ( count( $options ) === 0 ) {
182 1
			throw new Exception( 'No condition type specified.' );
183
		}
184
185 9
		if ( is_array( $options[0] ) ) {
186 1
			return $this->makeFromArrayOfConditions( $options );
187
		}
188
189 9
		$condition_options = $this->parseConditionOptions( $options );
190 8
		$condition_class = $this->getConditionTypeClass( $condition_options['type'] );
191
192
		try {
193 8
			$reflection = new ReflectionClass( $condition_class );
194 7
			return $reflection->newInstanceArgs( $condition_options['arguments'] );
195 1
		} catch ( ReflectionException $e ) {
196 1
			throw new Exception( 'Condition class "' . $condition_class . '" does not exist.' );
197
		}
198
	}
199
200
	/**
201
	 * Create a new condition from an array of conditions.
202
	 *
203
	 * @param  array               $options
204
	 * @return ConditionInterface
205
	 */
206
	protected function makeFromArrayOfConditions( $options ) {
207 1
		$conditions = array_map( function ( $condition ) {
208 1
			if ( $condition instanceof ConditionInterface ) {
209
				return $condition;
210
			}
211 1
			return $this->make( $condition );
212 1
		}, $options );
213
214 1
		return new MultipleCondition( $conditions );
215
	}
216
217
	/**
218
	 * Create a new condition from a closure.
219
	 *
220
	 * @param  Closure            $closure
221
	 * @return ConditionInterface
222
	 */
223 1
	protected function makeFromClosure( Closure $closure ) {
224 1
		return new CustomCondition( $closure );
225
	}
226
227
	/**
228
	 * Merge group condition attribute.
229
	 *
230
	 * @param  string|array|Closure|ConditionInterface $old
231
	 * @param  string|array|Closure|ConditionInterface $new
232
	 * @return ConditionInterface|null
233
	 */
234 1
	public function merge( $old, $new ) {
235 1
		if ( empty( $old ) ) {
236 1
			if ( empty( $new ) ) {
237 1
				return null;
238
			}
239 1
			return $this->condition( $new );
240 1
		} else if ( empty( $new ) ) {
241 1
			return $this->condition( $old );
242
		}
243
244 1
		return $this->mergeConditions( $this->condition( $old ), $this->condition( $new ) );
245
	}
246
247
	/**
248
	 * Merge condition instances.
249
	 *
250
	 * @param  ConditionInterface $old
251
	 * @param  ConditionInterface $new
252
	 * @return ConditionInterface
253
	 */
254 1
	public function mergeConditions( ConditionInterface $old, ConditionInterface $new ) {
255 1
		if ( $old instanceof UrlCondition && $new instanceof UrlCondition ) {
256 1
			return $old->concatenate( $new->getUrl(), $new->getUrlWhere() );
257
		}
258
259 1
		return $this->makeFromArrayOfConditions( [$old, $new] );
260
	}
261
}
262