|
1 <?php |
|
2 |
|
3 /* |
|
4 * This file is part of the Symfony package. |
|
5 * |
|
6 * (c) Fabien Potencier <fabien@symfony.com> |
|
7 * |
|
8 * For the full copyright and license information, please view the LICENSE |
|
9 * file that was distributed with this source code. |
|
10 */ |
|
11 |
|
12 namespace Symfony\Component\HttpFoundation; |
|
13 |
|
14 /** |
|
15 * RequestMatcher compares a pre-defined set of checks against a Request instance. |
|
16 * |
|
17 * @author Fabien Potencier <fabien@symfony.com> |
|
18 * |
|
19 * @api |
|
20 */ |
|
21 class RequestMatcher implements RequestMatcherInterface |
|
22 { |
|
23 private $path; |
|
24 private $host; |
|
25 private $methods; |
|
26 private $ip; |
|
27 private $attributes; |
|
28 |
|
29 public function __construct($path = null, $host = null, $methods = null, $ip = null, array $attributes = array()) |
|
30 { |
|
31 $this->path = $path; |
|
32 $this->host = $host; |
|
33 $this->methods = $methods; |
|
34 $this->ip = $ip; |
|
35 $this->attributes = $attributes; |
|
36 } |
|
37 |
|
38 /** |
|
39 * Adds a check for the URL host name. |
|
40 * |
|
41 * @param string $regexp A Regexp |
|
42 */ |
|
43 public function matchHost($regexp) |
|
44 { |
|
45 $this->host = $regexp; |
|
46 } |
|
47 |
|
48 /** |
|
49 * Adds a check for the URL path info. |
|
50 * |
|
51 * @param string $regexp A Regexp |
|
52 */ |
|
53 public function matchPath($regexp) |
|
54 { |
|
55 $this->path = $regexp; |
|
56 } |
|
57 |
|
58 /** |
|
59 * Adds a check for the client IP. |
|
60 * |
|
61 * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24 |
|
62 */ |
|
63 public function matchIp($ip) |
|
64 { |
|
65 $this->ip = $ip; |
|
66 } |
|
67 |
|
68 /** |
|
69 * Adds a check for the HTTP method. |
|
70 * |
|
71 * @param string|array $method An HTTP method or an array of HTTP methods |
|
72 */ |
|
73 public function matchMethod($method) |
|
74 { |
|
75 $this->methods = array_map('strtoupper', is_array($method) ? $method : array($method)); |
|
76 } |
|
77 |
|
78 /** |
|
79 * Adds a check for request attribute. |
|
80 * |
|
81 * @param string $key The request attribute name |
|
82 * @param string $regexp A Regexp |
|
83 */ |
|
84 public function matchAttribute($key, $regexp) |
|
85 { |
|
86 $this->attributes[$key] = $regexp; |
|
87 } |
|
88 |
|
89 /** |
|
90 * {@inheritdoc} |
|
91 * |
|
92 * @api |
|
93 */ |
|
94 public function matches(Request $request) |
|
95 { |
|
96 if (null !== $this->methods && !in_array($request->getMethod(), $this->methods)) { |
|
97 return false; |
|
98 } |
|
99 |
|
100 foreach ($this->attributes as $key => $pattern) { |
|
101 if (!preg_match('#'.str_replace('#', '\\#', $pattern).'#', $request->attributes->get($key))) { |
|
102 return false; |
|
103 } |
|
104 } |
|
105 |
|
106 if (null !== $this->path) { |
|
107 $path = str_replace('#', '\\#', $this->path); |
|
108 |
|
109 if (!preg_match('#'.$path.'#', $request->getPathInfo())) { |
|
110 return false; |
|
111 } |
|
112 } |
|
113 |
|
114 if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#', $request->getHost())) { |
|
115 return false; |
|
116 } |
|
117 |
|
118 if (null !== $this->ip && !$this->checkIp($request->getClientIp(), $this->ip)) { |
|
119 return false; |
|
120 } |
|
121 |
|
122 return true; |
|
123 } |
|
124 |
|
125 protected function checkIp($requestIp, $ip) |
|
126 { |
|
127 // IPv6 address |
|
128 if (false !== strpos($requestIp, ':')) { |
|
129 return $this->checkIp6($requestIp, $ip); |
|
130 } else { |
|
131 return $this->checkIp4($requestIp, $ip); |
|
132 } |
|
133 } |
|
134 |
|
135 protected function checkIp4($requestIp, $ip) |
|
136 { |
|
137 if (false !== strpos($ip, '/')) { |
|
138 list($address, $netmask) = explode('/', $ip); |
|
139 |
|
140 if ($netmask < 1 || $netmask > 32) { |
|
141 return false; |
|
142 } |
|
143 } else { |
|
144 $address = $ip; |
|
145 $netmask = 32; |
|
146 } |
|
147 |
|
148 return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask); |
|
149 } |
|
150 |
|
151 /** |
|
152 * @author David Soria Parra <dsp at php dot net> |
|
153 * @see https://github.com/dsp/v6tools |
|
154 */ |
|
155 protected function checkIp6($requestIp, $ip) |
|
156 { |
|
157 if (!defined('AF_INET6')) { |
|
158 throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".'); |
|
159 } |
|
160 |
|
161 list($address, $netmask) = explode('/', $ip); |
|
162 |
|
163 $bytes_addr = unpack("n*", inet_pton($address)); |
|
164 $bytes_test = unpack("n*", inet_pton($requestIp)); |
|
165 |
|
166 for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) { |
|
167 $left = $netmask - 16 * ($i-1); |
|
168 $left = ($left <= 16) ?: 16; |
|
169 $mask = ~(0xffff >> $left) & 0xffff; |
|
170 if (($bytes_addr[$i] & $mask) != ($bytes_test[$i] & $mask)) { |
|
171 return false; |
|
172 } |
|
173 } |
|
174 |
|
175 return true; |
|
176 } |
|
177 } |