Skip to content

Instantly share code, notes, and snippets.

@chdemko
Created June 8, 2012 13:27
Show Gist options
  • Save chdemko/2895611 to your computer and use it in GitHub Desktop.
Save chdemko/2895611 to your computer and use it in GitHub Desktop.
Replacement for parseRoute function
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Basic Web application router class for the Joomla Platform.
*
* @package Joomla.Platform
* @subpackage Application
* @since 12.3
*/
class JApplicationWebRouterBase extends JApplicationWebRouter
{
/**
* @var array An array of rules, each rule being an associative array('regex'=> $regex, 'vars' => $vars, 'controller' => $controller)
* for routing the request.
* @since 12.3
*/
protected $maps = array();
/**
* Add a route map to the router. If the pattern already exists it will be overwritten.
*
* @param string $pattern The route pattern to use for matching.
* @param string $controller The controller name to map to the given pattern.
*
* @return JApplicationWebRouter This object for method chaining.
*
* @since 12.3
*/
public function addMap($pattern, $controller)
{
// Sanitize and explode the pattern.
$pattern = explode('/', trim(parse_url((string) $pattern, PHP_URL_PATH), ' /'));
// Prepare the route variables
$vars = array();
// Initialize regular expression
$regex = array();
// Loop on each segment
foreach ($pattern as $segment)
{
// Match any number of segments
if ($segment == '*')
{
$regex[] = '.*';
}
// Match segment '\*'
elseif ($segment == '\*')
{
$regex[] = '/';
$regex[] = '\*';
}
// Match unnamed var
elseif ($segment == ':')
{
$regex[] = '/';
$regex[] = '[^/]*';
}
// Match named var
elseif ($segment[0] == ':')
{
$vars[] = substr($segment, 1);
$regex[] = '/';
$regex[] = '([^/]*)';
}
// Match segment starting with '\\:'
elseif ($segment[0] == '\\' && $segment[1] == ':')
{
$regex[] = '/';
$regex[] = preg_quote(substr($segment, 1));
}
// Regular segment
else
{
$regex[] = '/';
$regex[] = preg_quote($segment);
}
}
// Sanitize regex
if ($regex[0] == '/')
{
$regex = array_slice($regex, 1);
}
$this->maps[] = array(
'regex' => chr(1) . '^' . implode($regex) . '$' . chr(1),
'vars' => $vars,
'controller' => (string) $controller
);
return $this;
}
/**
* Add a route map to the router. If the pattern already exists it will be overwritten.
*
* @param array $maps A list of route maps to add to the router as $pattern => $controller.
*
* @return JApplicationWebRouter This object for method chaining.
*
* @since 12.3
*/
public function addMaps($maps)
{
foreach ($maps as $pattern => $controller)
{
$this->addMap($pattern, $controller);
}
return $this;
}
/**
* Parse the given route and return the name of a controller mapped to the given route.
*
* @param string $route The route string for which to find and execute a controller.
*
* @return string The controller name for the given route excluding prefix.
*
* @since 12.3
* @throws InvalidArgumentException
*/
protected function parseRoute($route)
{
// Initialize variables.
$controller = false;
// Sanitize and explode the route.
$route = trim(parse_url($route, PHP_URL_PATH), ' /');
// If the route is empty then simply return the default route. No parsing necessary.
if ($route == '')
{
return $this->default;
}
// Iterate through all of the known route maps looking for a match.
foreach ($this->maps as $rule)
{
if (preg_match($rule['regex'], $route, $matches))
{
// If we have gotten this far then we have a positive match.
$controller = $rule['controller'];
// Time to set the input variables.
// We are only going to set them if they don't already exist to avoid overwriting things.
foreach ($rule['vars'] as $i => $var)
{
$this->input->def($var, $matches[$i + 1]);
// Don't forget to do an explicit set on the GET superglobal.
$this->input->get->def($var, $matches[$i + 1]);
}
break;
}
}
// We were unable to find a route match for the request. Panic.
if (!$controller)
{
throw new InvalidArgumentException(sprintf('Unable to handle request for route `%s`.', $route), 404);
}
return $controller;
}
}
<?php
/**
* @package Joomla.UnitTest
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
/**
* Test class for JApplicationWebRouterBase.
*
* @package Joomla.UnitTest
* @subpackage Application
* @since 12.3
*/
class JApplicationWebRouterBaseTest extends TestCase
{
/**
* @var JApplicationWebRouterBase The object to be tested.
* @since 12.3
*/
private $_instance;
/**
* @var JInput The JInput object to be inspected for route variables.
* @since 12.3
*/
private $_input;
/**
* Provides test data for route parsing.
*
* @return array
*
* @since 12.3
*/
public static function getParseRouteData()
{
// Route, Exception, ControllerName, InputData, MapSet
return array(
array('', false, 'home', array(), 1),
array('articles/4', true, 'home', array(), 1),
array('', false, 'index', array(), 2),
array('login', false, 'login', array(), 2),
array('articles', false, 'articles', array(), 2),
array('articles/4', false, 'article', array('article_id' => 4), 2),
array('articles/4/crap', true, '', array(), 2),
array('test', true, '', array(), 2),
array('test/foo', true, '', array(), 2),
array('test/foo/path', true, '', array(), 2),
array('test/foo/path/bar', false, 'test', array('seg1' => 'foo', 'seg2' => 'bar'), 2),
array('content/article-1/*', false, 'content', array(), 2),
array('content/cat-1/article-1', false, 'article', array('category' => 'cat-1', 'article' => 'article-1'), 2),
array('content/cat-1/cat-2/article-1', false, 'article', array('category' => 'cat-2', 'article' => 'article-1'), 2),
array('content/cat-1/cat-2/cat-3/article-1', false, 'article', array('category' => 'cat-3', 'article' => 'article-1'), 2)
);
}
/**
* Tests the addMap method.
*
* @return void
*
* @covers JApplicationWebRouterBase::addMap
* @since 12.3
*/
public function testAddMap()
{
$this->assertAttributeEmpty('maps', $this->_instance);
$this->_instance->addMap('foo', 'MyApplicationFoo');
$this->assertAttributeEquals(array(array('regex' => chr(1) . '^foo$' . chr(1), 'vars' => array(), 'controller' => 'MyApplicationFoo')), 'maps', $this->_instance);
}
/**
* Tests the addMaps method.
*
* @return void
*
* @covers JApplicationWebRouterBase::addMaps
* @since 12.3
*/
public function testAddMaps()
{
$maps = array(
'login' => 'login',
'logout' => 'logout',
'requests' => 'requests',
'requests/:request_id' => 'request'
);
$rules = array(
array(
'regex' => chr(1) . '^login$' . chr(1),
'vars' => array(),
'controller' => 'login'
),
array(
'regex' => chr(1) . '^logout$' . chr(1),
'vars' => array(),
'controller' => 'logout'
),
array(
'regex' => chr(1) . '^requests$' . chr(1),
'vars' => array(),
'controller' => 'requests'
),
array(
'regex' => chr(1) . '^requests/([^/]*)$' . chr(1),
'vars' => array('request_id'),
'controller' => 'request'
)
);
$this->assertAttributeEmpty('maps', $this->_instance);
$this->_instance->addMaps($maps);
$this->assertAttributeEquals($rules, 'maps', $this->_instance);
}
/**
* Tests the JApplicationWebRouterBase::parseRoute method.
*
* @param string $r The route to parse.
* @param boolean $e True if an exception is expected.
* @param string $c The expected controller name.
* @param array $i The expected input object data.
* @param integer $m The map set to use for setting up the router.
*
* @return void
*
* @covers JApplicationWebRouterBase::parseRoute
* @dataProvider getParseRouteData
* @since 12.3
*/
public function testParseRoute($r, $e, $c, $i, $m)
{
// Setup the router maps.
$mapSetup = 'setMaps' . $m;
$this->$mapSetup();
// If we should expect an exception set that up.
if ($e)
{
$this->setExpectedException('InvalidArgumentException');
}
// Execute the route parsing.
$actual = TestReflection::invoke($this->_instance, 'parseRoute', $r);
// Test the assertions.
$this->assertEquals($c, $actual, 'Incorrect controller name found.');
$this->assertAttributeEquals($i, 'data', $this->_input, 'The input data is incorrect.');
}
/**
* Setup the router maps to option 1.
*
* This has no routes but has a default controller for the home page.
*
* @return void
*
* @since 12.3
*/
protected function setMaps1()
{
$this->_instance->addMaps(array());
$this->_instance->setDefaultController('home');
}
/**
* Setup the router maps to option 2.
*
* @return void
*
* @since 12.3
*/
protected function setMaps2()
{
$this->_instance->addMaps(
array(
'login' => 'login',
'logout' => 'logout',
'articles' => 'articles',
'articles/:article_id' => 'article',
'test/:seg1/path/:seg2' => 'test',
'content/:/\*' => 'content',
'content/*/:category/:article' => 'article'
)
);
$this->_instance->setDefaultController('index');
}
/**
* Prepares the environment before running a test.
*
* @return void
*
* @since 12.3
*/
protected function setUp()
{
parent::setUp();
// Construct the clean JInput object.
$this->_input = new JInput(array());
$this->_instance = new JApplicationWebRouterBase($this->getMockWeb(), $this->_input);
}
/**
* Cleans up the environment after running a test.
*
* @return void
*
* @since 12.3
*/
protected function tearDown()
{
$this->_instance = null;
parent::tearDown();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment