diff --git a/src/Symfony/Component/HttpFoundation/RequestMatcher.php b/src/Symfony/Component/HttpFoundation/RequestMatcher.php index 6673cbf3ea..1c159f6a3d 100644 --- a/src/Symfony/Component/HttpFoundation/RequestMatcher.php +++ b/src/Symfony/Component/HttpFoundation/RequestMatcher.php @@ -20,19 +20,49 @@ namespace Symfony\Component\HttpFoundation; */ class RequestMatcher implements RequestMatcherInterface { + /** + * @var string + */ private $path; - private $host; - private $methods; - private $ip; - private $attributes; + /** + * @var string + */ + private $host; + + /** + * @var array + */ + private $methods = array(); + + /** + * @var string + */ + private $ip; + + /** + * Attributes. + * + * @var array + */ + private $attributes = array(); + + /** + * @param string|null $path + * @param string|null $host + * @param string|string[]|null $methods + * @param string|null $ip + * @param array $attributes + */ public function __construct($path = null, $host = null, $methods = null, $ip = null, array $attributes = array()) { - $this->path = $path; - $this->host = $host; - $this->methods = $methods; - $this->ip = $ip; - $this->attributes = $attributes; + $this->matchPath($path); + $this->matchHost($host); + $this->matchMethod($methods); + $this->matchIp($ip); + foreach ($attributes as $k => $v) { + $this->matchAttribute($k, $v); + } } /** @@ -68,11 +98,11 @@ class RequestMatcher implements RequestMatcherInterface /** * Adds a check for the HTTP method. * - * @param string|array $method An HTTP method or an array of HTTP methods + * @param string|string[]|null $method An HTTP method or an array of HTTP methods */ public function matchMethod($method) { - $this->methods = array_map('strtoupper', is_array($method) ? $method : array($method)); + $this->methods = array_map('strtoupper', (array) $method); } /** @@ -93,7 +123,7 @@ class RequestMatcher implements RequestMatcherInterface */ public function matches(Request $request) { - if (null !== $this->methods && !in_array($request->getMethod(), $this->methods)) { + if ($this->methods && !in_array($request->getMethod(), $this->methods)) { return false; } @@ -111,7 +141,7 @@ class RequestMatcher implements RequestMatcherInterface } } - if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#', $request->getHost())) { + if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#i', $request->getHost())) { return false; } @@ -122,6 +152,14 @@ class RequestMatcher implements RequestMatcherInterface return true; } + /** + * Validates an IP address. + * + * @param string $requestIp + * @param string $ip + * + * @return boolean True valid, false if not. + */ protected function checkIp($requestIp, $ip) { // IPv6 address @@ -132,6 +170,14 @@ class RequestMatcher implements RequestMatcherInterface } } + /** + * Validates an IPv4 address. + * + * @param string $requestIp + * @param string $ip + * + * @return boolean True valid, false if not. + */ protected function checkIp4($requestIp, $ip) { if (false !== strpos($ip, '/')) { @@ -149,8 +195,15 @@ class RequestMatcher implements RequestMatcherInterface } /** + * Validates an IPv6 address. + * * @author David Soria Parra * @see https://github.com/dsp/v6tools + * + * @param string $requestIp + * @param string $ip + * + * @return boolean True valid, false if not. */ protected function checkIp6($requestIp, $ip) { @@ -158,16 +211,25 @@ class RequestMatcher implements RequestMatcherInterface throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".'); } - list($address, $netmask) = explode('/', $ip, 2); + if (false !== strpos($ip, '/')) { + list($address, $netmask) = explode('/', $ip, 2); - $bytes_addr = unpack("n*", inet_pton($address)); - $bytes_test = unpack("n*", inet_pton($requestIp)); + if ($netmask < 1 || $netmask > 128) { + return false; + } + } else { + $address = $ip; + $netmask = 128; + } + + $bytesAddr = unpack("n*", inet_pton($address)); + $bytesTest = unpack("n*", inet_pton($requestIp)); for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) { $left = $netmask - 16 * ($i-1); $left = ($left <= 16) ? $left : 16; $mask = ~(0xffff >> $left) & 0xffff; - if (($bytes_addr[$i] & $mask) != ($bytes_test[$i] & $mask)) { + if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) { return false; } } @@ -175,3 +237,4 @@ class RequestMatcher implements RequestMatcherInterface return true; } } + diff --git a/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php b/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php index d807d22893..db2405c679 100644 --- a/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php +++ b/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php @@ -66,6 +66,9 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase return array( array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'), array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'), + array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'), + array(true, '0:0:0:0:0:0:0:1', '::1'), + array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'), ); } @@ -89,38 +92,59 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase } } - public function testMethod() + /** + * @dataProvider testMethodFixtures + */ + public function testMethod($requestMethod, $matcherMethod, $isMatch) { $matcher = new RequestMatcher(); + $matcher->matchMethod($matcherMethod); + $request = Request::create('', $requestMethod); + $this->assertSame($isMatch, $matcher->matches($request)); - $matcher->matchMethod('get'); - $request = Request::create('', 'get'); - $this->assertTrue($matcher->matches($request)); - - $matcher->matchMethod('post'); - $this->assertFalse($matcher->matches($request)); - - $matcher->matchMethod(array('get', 'post')); - $this->assertTrue($matcher->matches($request)); + $matcher = new RequestMatcher(null, null, $matcherMethod); + $request = Request::create('', $requestMethod); + $this->assertSame($isMatch, $matcher->matches($request)); } - public function testHost() + public function testMethodFixtures() + { + return array( + array('get', 'get', true), + array('get', array('get', 'post'), true), + array('get', 'post', false), + array('get', 'GET', true), + array('get', array('GET', 'POST'), true), + array('get', 'POST', false), + ); + } + + /** + * @dataProvider testHostFixture + */ + public function testHost($pattern, $isMatch) { $matcher = new RequestMatcher(); - $request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com')); - $matcher->matchHost('.*\.example\.com'); - $this->assertTrue($matcher->matches($request)); + $matcher->matchHost($pattern); + $this->assertSame($isMatch, $matcher->matches($request)); - $matcher->matchHost('\.example\.com$'); - $this->assertTrue($matcher->matches($request)); + $matcher= new RequestMatcher(null, $pattern); + $this->assertSame($isMatch, $matcher->matches($request)); + } - $matcher->matchHost('^.*\.example\.com$'); - $this->assertTrue($matcher->matches($request)); - - $matcher->matchMethod('.*\.sensio\.com'); - $this->assertFalse($matcher->matches($request)); + public function testHostFixture() + { + return array( + array('.*\.example\.com', true), + array('\.example\.com$', true), + array('^.*\.example\.com$', true), + array('.*\.sensio\.com', false), + array('.*\.example\.COM', true), + array('\.example\.COM$', true), + array('^.*\.example\.COM$', true), + array('.*\.sensio\.COM', false), ); } public function testPath() @@ -175,3 +199,4 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase $this->assertFalse($matcher->matches($request)); } } +