Passed
Branch refactor/route-groups (411840)
by Atanas
01:44
created

UrlCondition::isSatisfied()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.0218

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 3
nop 1
dl 0
loc 14
ccs 8
cts 9
cp 0.8889
crap 4.0218
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 WPEmerge\Helpers\Url as UrlUtility;
13
use WPEmerge\Requests\RequestInterface;
14
use WPEmerge\Support\Arr;
15
16
/**
17
 * Check against the current url
18
 */
19
class UrlCondition implements ConditionInterface, HasUrlInterface {
20
	const WILDCARD = '*';
21
22
	/**
23
	 * URL to check against.
24
	 *
25
	 * @var string
26
	 */
27
	protected $url = '';
28
29
	/**
30
	 * URL where.
31
	 *
32
	 * @var array<string,string>
33
	 */
34
	protected $url_where = [];
35
36
	/**
37
	 * Regex to detect parameters in urls.
38
	 *
39
	 * @var string
40
	 */
41
	protected $url_regex = '~
42
		(?:/)                     # match leading slash
43
		(?:\{)                    # opening curly brace
44
			(?P<name>[a-z]\w*)    # string starting with a-z and followed by word characters for the parameter name
45
			(?P<optional>\?)?     # optionally allow the user to mark the parameter as option using literal ?
46
		(?:\})                    # closing curly brace
47
		(?=/)                     # lookahead for a trailing slash
48
	~ix';
49
50
	/**
51
	 * Regex to detect valid parameters in url segments.
52
	 *
53
	 * @var string
54
	 */
55
	protected $parameter_regex = '[^/]+';
56
57
	/**
58
	 * Constructor.
59
	 *
60
	 * @codeCoverageIgnore
61
	 * @param string $url
62
	 */
63
	public function __construct( $url ) {
64
		$this->setUrl( $url );
65
	}
66
67
	/**
68
	 * {@inheritDoc}
69
	 */
70
	protected function whereIsSatisfied( RequestInterface $request ) {
71
		$where = $this->getUrlWhere();
72
		$arguments = $this->getArguments( $request );
73
74
		foreach ( $where as $parameter => $regex ) {
75
			$value = Arr::get( $arguments, $parameter, '' );
76
77
			if ( ! preg_match( $regex, $value ) ) {
78
				return false;
79
			}
80
		}
81
82
		return true;
83
	}
84
85
	/**
86
	 * {@inheritDoc}
87
	 */
88 2
	public function isSatisfied( RequestInterface $request ) {
89 2
		if ( $this->getUrl() === static::WILDCARD ) {
90 1
			return true;
91
		}
92
93 1
		$validation_regex = $this->getValidationRegex( $this->getUrl() );
94 1
		$url = UrlUtility::getPath( $request );
95 1
		$match = (bool) preg_match( $validation_regex, $url );
96
97 1
		if ( ! $match || empty( $this->getUrlWhere() ) ) {
98 1
			return $match;
99
		}
100
101
		return $this->whereIsSatisfied( $request );
102
	}
103
104
	/**
105
	 * {@inheritDoc}
106
	 */
107 1
	public function getArguments( RequestInterface $request ) {
108 1
		$validation_regex = $this->getValidationRegex( $this->getUrl() );
109 1
		$url = UrlUtility::getPath( $request );
110 1
		$matches = [];
111 1
		$success = preg_match( $validation_regex, $url, $matches );
112
113 1
		if ( ! $success ) {
114 1
			return []; // this should not normally happen
115
		}
116
117 1
		$arguments = [];
118 1
		$parameter_names = $this->getParameterNames( $this->getUrl() );
119 1
		foreach ( $parameter_names as $parameter_name ) {
120 1
			$arguments[ $parameter_name ] = ! empty( $matches[ $parameter_name ] ) ? $matches[ $parameter_name ] : '';
121
		}
122
123 1
		return $arguments;
124
	}
125
126
	/**
127
	 * Get the url for this condition.
128
	 *
129
	 * @return string
130
	 */
131 2
	public function getUrl() {
132 2
		return $this->url;
133
	}
134
135
	/**
136
	 * Set the url for this condition.
137
	 *
138
	 * @param  string $url
139
	 * @return void
140
	 */
141 2
	public function setUrl( $url ) {
142 2
		if ( $url !== static::WILDCARD ) {
143 1
			$url = UrlUtility::addLeadingSlash( UrlUtility::addTrailingSlash( $url ) );
144
		}
145
146 2
		$this->url = $url;
147 2
	}
148
149
	/**
150
	 * {@inheritDoc}
151
	 */
152 1
	public function getUrlWhere() {
153 1
		return $this->url_where;
154
	}
155
156
	/**
157
	 * {@inheritDoc}
158
	 */
159 1
	public function setUrlWhere( $where ) {
160 1
		$this->url_where = $where;
161 1
	}
162
163
	/**
164
	 * Append a url to this one returning a new instance.
165
	 *
166
	 * @param  string $url
167
	 * @return static
168
	 */
169 1
	public function concatenateUrl( $url ) {
170 1
		if ( $this->getUrl() === static::WILDCARD || $url === static::WILDCARD ) {
171
			return new static( static::WILDCARD );
172
		}
173
174 1
		$leading = UrlUtility::addLeadingSlash( UrlUtility::removeTrailingSlash( $this->getUrl() ), true );
175 1
		$trailing = UrlUtility::addLeadingSlash( UrlUtility::addTrailingSlash( $url ) );
176
177 1
		return new static( $leading . $trailing );
178
	}
179
180
	/**
181
	 * Get parameter names as defined in the url.
182
	 *
183
	 * @param  string   $url
184
	 * @return string[]
185
	 */
186 1
	protected function getParameterNames( $url ) {
187 1
		$matches = [];
188 1
		preg_match_all( $this->url_regex, $url, $matches );
189 1
		return $matches['name'];
190
	}
191
192
	/**
193
	 * Validation regex replace callback.
194
	 *
195
	 * @param  array  $matches
196
	 * @param  array  $parameters
197
	 * @return string
198
	 */
199 1
	protected function replaceRegexParameterWithPlaceholder( $matches, &$parameters ) {
200 1
		$name = $matches['name'];
201 1
		$optional = ! empty( $matches['optional'] );
202
203 1
		$replacement = '/(?P<' . $name . '>' . $this->parameter_regex . ')';
204
205 1
		if ( $optional ) {
206 1
			$replacement = '(?:' . $replacement . ')?';
207
		}
208
209 1
		$hash = sha1( implode( '_', [
210 1
			count( $parameters ),
211 1
			$replacement,
212 1
			uniqid( 'wpemerge_', true ),
213
		] ) );
214 1
		$placeholder = '___placeholder_' . $hash . '___';
215 1
		$parameters[ $placeholder ] = $replacement;
216
217 1
		return $placeholder;
218
	}
219
220
	/**
221
	 * Get regex to test whether normal urls match the parameter-based one.
222
	 *
223
	 * @param  string  $url
224
	 * @param  boolean $wrap
225
	 * @return string
226
	 */
227 1
	public function getValidationRegex( $url, $wrap = true ) {
228 1
		$parameters = [];
229
230
		// Replace all parameters with placeholders
231 1
		$validation_regex = preg_replace_callback( $this->url_regex, function ( $matches ) use ( &$parameters ) {
232 1
			return $this->replaceRegexParameterWithPlaceholder( $matches, $parameters );
233 1
		}, $url );
234
235
		// quote the remaining string so that it does not get evaluated as regex
236
		$validation_regex = preg_quote( $validation_regex, '~' );
237
238
		// replace the placeholders with the real parameter regexes
239
		$validation_regex = str_replace( array_keys( $parameters ), array_values( $parameters ), $validation_regex );
240
241
		// match the entire url; make trailing slash optional
242
		$validation_regex = '^' . $validation_regex . '?$';
243
244
		if ( $wrap ) {
245
			$validation_regex = '~' . $validation_regex . '~';
246
		}
247
248
		return $validation_regex;
249
	}
250
}
251