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

UrlCondition::getUrlWhere()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
ccs 2
cts 2
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 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, HasUrlWhereInterface {
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
	 * @param array<string, string> $where
63
	 */
64
	public function __construct( $url, $where = [] ) {
65
		$this->setUrl( $url );
66
		$this->setUrlWhere( $where );
67
	}
68
69
	/**
70
	 * {@inheritDoc}
71
	 */
72 1
	protected function whereIsSatisfied( RequestInterface $request ) {
73 1
		$where = $this->getUrlWhere();
74 1
		$arguments = $this->getArguments( $request );
75
76 1
		foreach ( $where as $parameter => $regex ) {
77 1
			$value = Arr::get( $arguments, $parameter, '' );
78
79 1
			if ( ! preg_match( $regex, $value ) ) {
80 1
				return false;
81
			}
82 1
		}
83
84 1
		return true;
85
	}
86
87
	/**
88
	 * {@inheritDoc}
89
	 */
90 3
	public function isSatisfied( RequestInterface $request ) {
91 3
		if ( $this->getUrl() === static::WILDCARD ) {
92 1
			return true;
93
		}
94
95 2
		$validation_regex = $this->getValidationRegex( $this->getUrl() );
96 2
		$url = UrlUtility::getPath( $request );
97 2
		$match = (bool) preg_match( $validation_regex, $url );
98
99 2
		if ( ! $match || empty( $this->getUrlWhere() ) ) {
100 2
			return $match;
101
		}
102
103 1
		return $this->whereIsSatisfied( $request );
104
	}
105
106
	/**
107
	 * {@inheritDoc}
108
	 */
109 1
	public function getArguments( RequestInterface $request ) {
110 1
		$validation_regex = $this->getValidationRegex( $this->getUrl() );
111 1
		$url = UrlUtility::getPath( $request );
112 1
		$matches = [];
113 1
		$success = preg_match( $validation_regex, $url, $matches );
114
115 1
		if ( ! $success ) {
116 1
			return []; // this should not normally happen
117
		}
118
119 1
		$arguments = [];
120 1
		$parameter_names = $this->getParameterNames( $this->getUrl() );
121 1
		foreach ( $parameter_names as $parameter_name ) {
122 1
			$arguments[ $parameter_name ] = isset( $matches[ $parameter_name ] ) ? $matches[ $parameter_name ] : '';
123 1
		}
124
125 1
		return $arguments;
126
	}
127
128
	/**
129
	 * Get the url for this condition.
130
	 *
131
	 * @return string
132
	 */
133 2
	public function getUrl() {
134 2
		return $this->url;
135
	}
136
137
	/**
138
	 * Set the url for this condition.
139
	 *
140
	 * @param  string $url
141
	 * @return void
142
	 */
143 2
	public function setUrl( $url ) {
144 2
		if ( $url !== static::WILDCARD ) {
145 1
			$url = UrlUtility::addLeadingSlash( UrlUtility::addTrailingSlash( $url ) );
146 1
		}
147
148 2
		$this->url = $url;
149 2
	}
150
151
	/**
152
	 * {@inheritDoc}
153
	 */
154 1
	public function getUrlWhere() {
155 1
		return $this->url_where;
156
	}
157
158
	/**
159
	 * {@inheritDoc}
160
	 */
161 1
	public function setUrlWhere( $where ) {
162 1
		$this->url_where = $where;
163 1
	}
164
165
	/**
166
	 * Append a url to this one returning a new instance.
167
	 *
168
	 * @param  string                $url
169
	 * @param  array<string, string> $where
170
	 * @return static
171
	 */
172 3
	public function concatenate( $url, $where = [] ) {
173 3
		if ( $this->getUrl() === static::WILDCARD || $url === static::WILDCARD ) {
174 1
			return new static( static::WILDCARD );
175
		}
176
177 2
		$leading = UrlUtility::addLeadingSlash( UrlUtility::removeTrailingSlash( $this->getUrl() ), true );
178 2
		$trailing = UrlUtility::addLeadingSlash( UrlUtility::addTrailingSlash( $url ) );
179
180 2
		$concatenated = new static( $leading . $trailing, array_merge(
181 2
			$this->getUrlWhere(),
182
			$where
183 2
		) );
184
185 2
		return $concatenated;
186
	}
187
188
	/**
189
	 * Get parameter names as defined in the url.
190
	 *
191
	 * @param  string   $url
192
	 * @return string[]
193
	 */
194 1
	protected function getParameterNames( $url ) {
195 1
		$matches = [];
196 1
		preg_match_all( $this->url_regex, $url, $matches );
197 1
		return $matches['name'];
198
	}
199
200
	/**
201
	 * Validation regex replace callback.
202
	 *
203
	 * @param  array  $matches
204
	 * @param  array  $parameters
205
	 * @return string
206
	 */
207 1
	protected function replaceRegexParameterWithPlaceholder( $matches, &$parameters ) {
208 1
		$name = $matches['name'];
209 1
		$optional = ! empty( $matches['optional'] );
210
211 1
		$replacement = '/(?P<' . $name . '>' . $this->parameter_regex . ')';
212
213 1
		if ( $optional ) {
214 1
			$replacement = '(?:' . $replacement . ')?';
215 1
		}
216
217 1
		$hash = sha1( implode( '_', [
218 1
			count( $parameters ),
219 1
			$replacement,
220 1
			uniqid( 'wpemerge_', true ),
221 1
		] ) );
222 1
		$placeholder = '___placeholder_' . $hash . '___';
223 1
		$parameters[ $placeholder ] = $replacement;
224
225 1
		return $placeholder;
226
	}
227
228
	/**
229
	 * Get regex to test whether normal urls match the parameter-based one.
230
	 *
231
	 * @param  string  $url
232
	 * @param  boolean $wrap
233
	 * @return string
234
	 */
235 1
	public function getValidationRegex( $url, $wrap = true ) {
236 1
		$parameters = [];
237
238
		// Replace all parameters with placeholders
239 1
		$validation_regex = preg_replace_callback( $this->url_regex, function ( $matches ) use ( &$parameters ) {
240 1
			return $this->replaceRegexParameterWithPlaceholder( $matches, $parameters );
241 1
		}, $url );
242
243
		// quote the remaining string so that it does not get evaluated as regex
244 1
		$validation_regex = preg_quote( $validation_regex, '~' );
245
246
		// replace the placeholders with the real parameter regexes
247 1
		$validation_regex = str_replace( array_keys( $parameters ), array_values( $parameters ), $validation_regex );
248
249
		// match the entire url; make trailing slash optional
250 1
		$validation_regex = '^' . $validation_regex . '?$';
251
252 1
		if ( $wrap ) {
253 1
			$validation_regex = '~' . $validation_regex . '~';
254 1
		}
255
256 1
		return $validation_regex;
257
	}
258
}
259