Passed
Push — master ( 517cb3...2c2725 )
by Atanas
02:15
created

Url::getValidationRegex()   B

Complexity

Conditions 4
Paths 2

Size

Total Lines 36
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 20
nc 2
nop 2
dl 0
loc 36
ccs 22
cts 22
cp 1
crap 4
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
namespace WPEmerge\Routing\Conditions;
4
5
use WPEmerge\Url as UrlUtility;
6
use WPEmerge\Request;
7
8
/**
9
 * Check against the current url
10
 */
11
class Url 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  Url $url
104
	 * @return Url
105
	 */
106 1
	public function concatenate( Url $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
	 * Get regex to test whether normal urls match the parameter-based one
124
	 *
125
	 * @param  string  $url
126
	 * @param  boolean $wrap
127
	 * @return string
128
	 */
129 1
	public function getValidationRegex( $url, $wrap = true ) {
130 1
		$parameters = [];
131
132
		// Replace all parameters with placeholders
133 1
		$validation_regex = preg_replace_callback( $this->url_regex, function( $matches ) use ( &$parameters ) {
134 1
			$name = $matches['name'];
135 1
			$optional = ! empty( $matches['optional'] );
136 1
			$regex = ! empty( $matches['regex'] ) ? $matches['regex'] : $this->parameter_regex;
137 1
			$replacement = '/(?P<' . $name . '>' . $regex . ')';
138 1
			if ( $optional ) {
139 1
				$replacement = '(?:' . $replacement . ')?';
140 1
			}
141
142 1
			$placeholder = '___placeholder_' . sha1( count( $parameters) . '_' . $replacement . '_' . uniqid() ) . '___';
143 1
			$parameters[ $placeholder ] = $replacement;
144 1
			return $placeholder;
145 1
		}, $url );
146
147
		// quote the remaining string so that it does not get evaluated as regex
148 1
		$validation_regex = preg_quote( $validation_regex, '~' );
149
150
		// replace the placeholders with the real parameter regexes
151 1
		$validation_regex = str_replace( array_keys( $parameters ), array_values( $parameters ), $validation_regex );
152
153
		// add a question mark to the end to make the trailing slash optional
154 1
		$validation_regex = $validation_regex . '?';
155
156
		// make sure the regex matches the entire url
157 1
		$validation_regex = '^' . $validation_regex . '$';
158
159 1
		if ( $wrap ) {
160 1
			$validation_regex = '~' . $validation_regex . '~';
161 1
		}
162
163 1
		return $validation_regex;
164
	}
165
}
166