Passed
Push — master ( b7467b...db04eb )
by Atanas
02:41
created

Url::getArguments()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 4
nop 1
dl 0
loc 18
rs 9.2
c 0
b 0
f 0
ccs 0
cts 13
cp 0
crap 20
1
<?php
2
3
namespace Obsidian\Routing\Conditions;
4
5
use Obsidian\Url as UrlUtility;
6
use Obsidian\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
		(?<=/)                    # lookbehind for a 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 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
	public function __construct( $url ) {
49
		if ( $url !== static::WILDCARD ) {
50
			$url = UrlUtility::addLeadingSlash( $url );
51
			$url = UrlUtility::addTrailingSlash( $url );
52
		}
53
		$this->url = $url;
54
	}
55
56
	/**
57
	 * {@inheritDoc}
58
	 */
59
	public function satisfied( Request $request ) {
60
		if ( $this->url === static::WILDCARD ) {
61
			return true;
62
		}
63
64
		$validation_regex = $this->getValidationRegex( $this->getUrl() );
65
		$url = UrlUtility::getCurrentPath( $request );
66
		return (bool) preg_match( $validation_regex, $url );
67
	}
68
69
	/**
70
	 * {@inheritDoc}
71
	 */
72
	public function getArguments( Request $request ) {
73
		$validation_regex = $this->getValidationRegex( $this->getUrl() );
74
		$url = UrlUtility::getCurrentPath( $request );
75
		$matches = [];
76
		$success = preg_match( $validation_regex, $url, $matches );
77
78
		if ( ! $success ) {
79
			return []; // this should not normally happen
80
		}
81
82
		$arguments = [];
83
		$parameter_names = $this->getParameterNames( $this->getUrl() );
84
		foreach ( $parameter_names as $parameter_name ) {
85
			$arguments[] = ! empty( $matches[ $parameter_name ] ) ? $matches[ $parameter_name ] : '';
86
		}
87
88
		return $arguments;
89
	}
90
91
	/**
92
	 * Return the url for this condition
93
	 *
94
	 * @return string
95
	 */
96
	public function getUrl() {
97
		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
	public function concatenate( Url $url ) {
107
		return new static( UrlUtility::removeTrailingSlash( $this->getUrl() ) . $url->getUrl() );
108
	}
109
110
	/**
111
	 * Return parameter names as defined in the url
112
	 *
113
	 * @param  string   $url
114
	 * @return string[]
115
	 */
116
	protected function getParameterNames( $url ) {
117
		$matches = [];
118
		preg_match_all( $this->url_regex, $url, $matches );
119
		return $matches['name'];
120
	}
121
122
	/**
123
	 * Return 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
	public function getValidationRegex( $url, $wrap = true ) {
130
		$parameters = [];
131
132
		// Replace all parameters with placeholders
133
		$validation_regex = preg_replace_callback( $this->url_regex, function( $matches ) use ( &$parameters ) {
134
			$name = $matches['name'];
135
			$optional = ! empty( $matches['optional'] );
136
			$regex = ! empty( $matches['regex'] ) ? $matches['regex'] : $this->parameter_regex;
137
			$replacement = '(?P<' . $name . '>' . $regex . ')';
138
			if ( $optional ) {
139
				$replacement .= '?';
140
			}
141
142
			$placeholder = '___placeholder_' . sha1( count( $parameters) . '_' . $replacement . '_' . uniqid() ) . '___';
143
			$parameters[ $placeholder ] = $replacement;
144
			return $placeholder;
145
		}, $url );
146
147
		// quote the remaining string so that it does not get evaluated as regex
148
		$validation_regex = preg_quote( $validation_regex, '~' );
149
150
		// replace the placeholders with the real parameter regexes
151
		$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
		$validation_regex = $validation_regex . '?';
155
156
		// make sure the regex matches the entire url
157
		$validation_regex = '^' . $validation_regex . '$';
158
159
		if ( $wrap ) {
160
			$validation_regex = '~' . $validation_regex . '~';
161
		}
162
163
		return $validation_regex;
164
	}
165
}
166