diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 340a5f815d..53618ebe21 100755 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -134,7 +134,7 @@ class Request 'SERVER_NAME' => 'localhost', 'SERVER_PORT' => 80, 'HTTP_HOST' => 'localhost', - 'HTTP_USER_AGENT' => 'Symfony/X.X', + 'HTTP_USER_AGENT' => 'Symfony/2.X', 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5', 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', diff --git a/src/Symfony/Component/HttpFoundation/RequestMatcher.php b/src/Symfony/Component/HttpFoundation/RequestMatcher.php new file mode 100644 index 0000000000..9f02f366cf --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/RequestMatcher.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * RequestMatcher compares a pre-defined set of checks against a Request instance. + * + * @author Fabien Potencier + */ +class RequestMatcher implements RequestMatcherInterface +{ + protected $path; + protected $host; + protected $methods; + protected $ip; + + /** + * Adds a check for the URL host name. + * + * @param string $regexp A Regexp + */ + public function matchHost($regexp) + { + $this->host = $regexp; + } + + /** + * Adds a check for the URL path info. + * + * @param string $regexp A Regexp + */ + public function matchPath($regexp) + { + $this->path = $regexp; + } + + /** + * Adds a check for the client IP. + * + * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24 + */ + public function matchIp($ip) + { + $this->ip = $ip; + } + + /** + * Adds a check for the HTTP method. + * + * @param string|array An HTTP method or an array of HTTP methods + */ + public function matchMethod($method) + { + $this->methods = array_map(function ($m) { return strtolower($m); }, is_array($method) ? $method : array($method)); + } + + /** + * {@inheritdoc} + */ + public function matches(Request $request) + { + if (null !== $this->methods && !in_array(strtolower($request->getMethod()), $this->methods)) { + return false; + } + + if (null !== $this->path && !preg_match($this->path, $request->getPathInfo())) { + return false; + } + + if (null !== $this->host && !preg_match($this->host, $request->getHost())) { + return false; + } + + if (null !== $this->ip && !$this->checkIp($this->host, $request->getClientIp())) { + return false; + } + + return true; + } + + protected function checkIp($ip) + { + if (false !== strpos($this->ip, '/')) { + list($address, $netmask) = $this->ip; + + if ($netmask <= 0) { + return false; + } + } else { + $address = $this->ip; + $netmask = 1; + } + + return 0 === substr_compare(sprintf('%032b', ip2long($ip)), sprintf('%032b', ip2long($address)), 0, $netmask); + } +} diff --git a/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php b/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php new file mode 100644 index 0000000000..1ba6ece563 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * RequestMatcherInterface is an interface for strategies to match a Request. + * + * @author Fabien Potencier + */ +interface RequestMatcherInterface +{ + /** + * Decides whether the rule(s) implemented by the strategy matches the supplied request. + * + * @param Request $request The request to check for a match + * + * @return Boolean true if the request matches, false otherwise + */ + function matches(Request $request); +} diff --git a/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php b/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php new file mode 100644 index 0000000000..b92a7a8705 --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\RequestMatcher; +use Symfony\Component\HttpFoundation\Request; + +class RequestMatcherTest extends \PHPUnit_Framework_TestCase +{ + public function testIp() + { + $matcher = new RequestMatcher(); + + $matcher->matchIp('192.168.1.1/1'); + $request = Request::create('', 'get', array(), array(), array(), array('REMOTE_ADDR' => '192.168.1.1')); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchIp('192.168.1.0/24'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchIp('1.2.3.4/1'); + $this->assertFalse($matcher->matches($request)); + } + + public function testMethod() + { + $matcher = new RequestMatcher(); + + $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)); + } + + public function testHost() + { + $matcher = new RequestMatcher(); + + $matcher->matchHost('#.*\.example\.com#i'); + $request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com')); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchMethod('#sensio\.com#i'); + $this->assertFalse($matcher->matches($request)); + } + + public function testPath() + { + $matcher = new RequestMatcher(); + + $matcher->matchPath('#^/admin#'); + $request = Request::create('/admin/foo'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchMethod('#^/blog#i'); + $this->assertFalse($matcher->matches($request)); + } +}