Passed
Push — master ( d2008b...aa9f10 )
by Atanas
01:54
created

UrlCondition::getValidationRegex()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 11
nc 2
nop 2
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
ccs 12
cts 12
cp 1
crap 2
1
<?php
2
3
namespace WPEmerge\Routing\Conditions;
4
5
use WPEmerge\Helpers\Url as UrlUtility;
6
use WPEmerge\Requests\Request;
7
8
/**
9
 * Check against the current url
10
 */
11
class UrlCondition implements ConditionInterface {
12
	const WILDCARD = '*';
13
14
	/**
15
	 * URL to check against
16
	 *
17
	 * @var string
18
	 */
19
	protected $url = '';
20
21
	/**
22
	 * Regex to detect parameters in urls
23
	 *
24
	 * @var string
25
	 */
26
	protected $url_regex = '~
27
		(?:/)                     # match leading slash
28
		(?:\{)                    # require opening curly brace
29
			(?P<name>[a-z]\w*)    # require a string starting with a-z and followed by any number of word characters for the parameter name
30
			(?P<optional>\?)?     # optionally allow the user to mark the parameter as option using literal ?
31
			(?::(?P<regex>.*?))?  # optionally allow the user to supply a regex to match the argument against
32
		(?:\})                    # require closing curly brace
33
		(?=/)                     # lookahead for a trailing slash
34
	~ix';
35
36
	/**
37
	 * Regex to detect valid parameters in url segments
38
	 *
39
	 * @var string
40
	 */
41
	protected $parameter_regex = '[^/]+';
42
43
	/**
44
	 * Constructor
45
	 *
46
	 * @param string $url
47
	 */
48 2
	public function __construct( $url ) {
49 2
		if ( $url !== static::WILDCARD ) {
50 1
			$url = UrlUtility::addLeadingSlash( $url );
51 1
			$url = UrlUtility::addTrailingSlash( $url );
52 1
		}
53 2
		$this->url = $url;
54 2
	}
55
56
	/**
57
	 * {@inheritDoc}
58
	 */
59 2
	public function isSatisfied( Request $request ) {
60 2
		if ( $this->getUrl() === static::WILDCARD ) {
61 1
			return true;
62
		}
63
64 1
		$validation_regex = $this->getValidationRegex( $this->getUrl() );
65 1
		$url = UrlUtility::getPath( $request );
66 1
		return (bool) preg_match( $validation_regex, $url );
67
	}
68
69
	/**
70
	 * {@inheritDoc}
71
	 */
72 1
	public function getArguments( Request $request ) {
73 1
		$validation_regex = $this->getValidationRegex( $this->getUrl() );
74 1
		$url = UrlUtility::getPath( $request );
75 1
		$matches = [];
76 1
		$success = preg_match( $validation_regex, $url, $matches );
77
78 1
		if ( ! $success ) {
79 1
			return []; // this should not normally happen
80
		}
81
82 1
		$arguments = [];
83 1
		$parameter_names = $this->getParameterNames( $this->getUrl() );
84 1
		foreach ( $parameter_names as $parameter_name ) {
85 1
			$arguments[] = ! empty( $matches[ $parameter_name ] ) ? $matches[ $parameter_name ] : '';
86 1
		}
87
88 1
		return $arguments;
89
	}
90
91
	/**
92
	 * Get the url for this condition
93
	 *
94
	 * @return string
95
	 */
96 2
	public function getUrl() {
97 2
		return $this->url;
98
	}
99
100
	/**
101
	 * Concatenate 2 url conditions into a new one
102
	 *
103
	 * @param  UrlCondition $url
104
	 * @return UrlCondition
105
	 */
106 1
	public function concatenate( UrlCondition $url ) {
107 1
		return new static( UrlUtility::removeTrailingSlash( $this->getUrl() ) . $url->getUrl() );
108
	}
109
110
	/**
111
	 * Get parameter names as defined in the url
112
	 *
113
	 * @param  string   $url
114
	 * @return string[]
115
	 */
116 1
	protected function getParameterNames( $url ) {
117 1
		$matches = [];
118 1
		preg_match_all( $this->url_regex, $url, $matches );
119 1
		return $matches['name'];
120
	}
121
122
	/**
123
	 * Validation regex replace callback
124
	 *
125
	 * @param  array  $matches
126
	 * @param  array  $parameters
127
	 * @return string
128
	 */
129 1
	protected function replaceRegexParameterWithPlaceholder( $matches, &$parameters ) {
130 1
		$name = $matches['name'];
131 1
		$optional = ! empty( $matches['optional'] );
132 1
		$regex = ! empty( $matches['regex'] ) ? $matches['regex'] : $this->parameter_regex;
133
134 1
		$replacement = '/(?P<' . $name . '>' . $regex . ')';
135
136 1
		if ( $optional ) {
137 1
			$replacement = '(?:' . $replacement . ')?';
138 1
		}
139
140 1
		$placeholder = '___placeholder_' . sha1( count( $parameters) . '_' . $replacement . '_' . uniqid() ) . '___';
141 1
		$parameters[ $placeholder ] = $replacement;
142
143 1
		return $placeholder;
144
	}
145
146
	/**
147
	 * Get regex to test whether normal urls match the parameter-based one
148
	 *
149
	 * @param  string  $url
150
	 * @param  boolean $wrap
151
	 * @return string
152
	 */
153 1
	public function getValidationRegex( $url, $wrap = true ) {
154 1
		$parameters = [];
155
156
		// Replace all parameters with placeholders
157 1
		$validation_regex = preg_replace_callback( $this->url_regex, function( $matches ) use ( &$parameters ) {
158 1
			return $this->replaceRegexParameterWithPlaceholder( $matches, $parameters );
159 1
		}, $url );
160
161
		// quote the remaining string so that it does not get evaluated as regex
162 1
		$validation_regex = preg_quote( $validation_regex, '~' );
163
164
		// replace the placeholders with the real parameter regexes
165 1
		$validation_regex = str_replace( array_keys( $parameters ), array_values( $parameters ), $validation_regex );
166
167
		// match the entire url; make trailing slash optional
168 1
		$validation_regex = '^' . $validation_regex . '?$';
169
170 1
		if ( $wrap ) {
171 1
			$validation_regex = '~' . $validation_regex . '~';
172 1
		}
173
174 1
		return $validation_regex;
175
	}
176
}
177