Skip to content

Commit

Permalink
[HttpFoundation] added request matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Sep 2, 2010
1 parent af96c87 commit 4f33761
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Symfony/Component/HttpFoundation/Request.php
Expand Up @@ -134,7 +134,7 @@ static public function create($uri, $method = 'get', $parameters = array(), $coo
'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',
Expand Down
105 changes: 105 additions & 0 deletions src/Symfony/Component/HttpFoundation/RequestMatcher.php
@@ -0,0 +1,105 @@
<?php

namespace Symfony\Component\HttpFoundation;

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* 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 <fabien.potencier@symfony-project.com>
*/
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);
}
}
29 changes: 29 additions & 0 deletions src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php
@@ -0,0 +1,29 @@
<?php

namespace Symfony\Component\HttpFoundation;

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* 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 <fabien.potencier@symfony-project.com>
*/
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);
}
@@ -0,0 +1,72 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* 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));
}
}

0 comments on commit 4f33761

Please sign in to comment.