ConditionFactory   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 239
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 33
eloc 68
dl 0
loc 239
ccs 73
cts 73
cp 1
rs 9.76
c 0
b 0
f 0

14 Methods

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