Completed
Branch refactor/kernels (a21eb4)
by Atanas
02:06
created

ConditionFactory::parseNegatedCondition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

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