forked from bcit-ci/CodeIgniter
-
Notifications
You must be signed in to change notification settings - Fork 4
DrF Reverse Routing
Derek Jones edited this page Jul 5, 2012
·
29 revisions
DrF Reverse Routing is an extension of the URL Helper that overrides the site_url function to provide a reverse lookup. If a custom route can be found that matches the given "standard CodeIgniter URI", a custom route URI will be created and returned. Otherwise, a standard site_url will be returned.
- Translates a "standard CodeIgniter URI" into a custom route if possible. Standard, in this case, means controller/function/param1/param2/.../paramN
- Fails gracefully, providing a standard site_url if no route can be found/translated
- Works automatically with all of URL Helper's functions
- Concept inspired by CakePHP's reverse routing
- place MY_url_helper.php in your system/application/helpers folder
- include the URL helper in your controller ```php $this->load->helper( 'url' );
- use URL Helper's functions as you normally would
## Example
- setup a Test Controller
```php
<?php
class Test extends Controller{
function Test()
{
parent::Controller();
}
function testFunc( $param1 ){
echo 'You passed in: ' . $param1;
}
function redirect()
{
$this->load->helper( 'url' );
redirect( 'test/testFunc/bar' );
}
}
?>
- create a route for the test function
...
$route['foo/(:any)'] = 'test/testFunc/$1';
...
- Go to http://www.example.com/index.php/test/redirect. Replace example.com with your server name
- You should be redirected to http://www.example.com/index.php/foo/bar
File:DrF_Reverse_Routing.20090331.zip
2009.03.31 by AJ Heller aj@drfloob.com Fixed a bug that did not handle unordered backreferences correctly (when $2 comes before $1, for example. Bug reported by Johan André View original report
system/application/helpers/MY_url_helper.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* DrF Reverse Routing
*
* @author AJ Heller <aj@drfloob.com>
* @link http://drfloob.com/
*/
// ------------------------------------------------------------------------
/**
* (Reverse) Site_URL
*
* Returns a custom routed URL if one exists. Returns a normal site_url otherwise.
* It works by translating the passed-in URI into a custom route URI, if possible.
* This function does not handle ANY regex used without capture-groups and back-references.
* Visit http://drfloob.com/codeIgniter/reverse_redirect to learn why
*
* @access public
* @param $uri The standard CI URL, e.g. controller/function/param1
* @param $method
* @param $http_response_code
*/
function site_url($uri = '')
{
$Router =& load_class('Router');
// $uri is expected to be a string, in the form of controller/function/param1
// trim leading and trailing slashes, just in case
$uri = trim($uri,'/');
$routes = $Router->routes;
$reverseRoutes = array_flip( $routes );
unset( $routes['default_controller'], $routes['scaffolding_trigger'] );
// Loop through all routes to check for back-references, then see if the user-supplied URI matches one
foreach ($routes as $key => $val)
{
// bailing if route contains ungrouped regex, otherwise this fails badly
if( preg_match( '/[^\(][.+?{\:]/', $key ) )
continue;
// Do we have a back-reference?
if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE)
{
// Find all back-references in custom route and CI route
preg_match_all( '/\(.+?\)/', $key, $keyRefs );
preg_match_all( '/\$.+?/', $val, $valRefs );
$keyRefs = $keyRefs[0];
// Create URI Regex, to test passed-in uri against a custom route's CI ( standard ) route
$uriRegex = $val;
// Extract positional parameters (backreferences), and order them such that
// the keys of $goodValRefs dirrectly mirror the correct value in $keyRefs
$goodValRefs = array();
foreach ($valRefs[0] as $ref) {
$tempKey = substr($ref, 1);
if (is_numeric($tempKey)) {
--$tempKey;
$goodValRefs[$tempKey] = $ref;
}
}
// Replaces back-references in CI route with custom route's regex [ $1 replaced with (:num), for example ]
foreach ($goodValRefs as $tempKey => $ref) {
if (isset($keyRefs[$tempKey])) {
$uriRegex = str_replace($ref, $keyRefs[$tempKey], $uriRegex);
}
}
// replace :any and :num with .+ and [0-9]+, respectively
$uriRegex = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $uriRegex));
// regex creation is finished. Test it against uri
if (preg_match('#^'.$uriRegex.'$#', $uri)){
// A match was found. We can now build the custom URI
// We need to create a custom route back-referenced regex, to plug user's uri params into the new routed uri.
// First, find all custom route strings between capture groups
$key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key));
$routeString = preg_split( '/\(.+?\)/', $key );
// build regex using original CI route's back-references
$replacement = '';
$rsEnd = count( $routeString ) - 1;
// merge route strings with original back-references, 1-for-1, like a zipper
for( $i = 0; $i < $rsEnd; $i++ ){
$replacement .= $routeString[$i] . $valRefs[0][$i];
}
$replacement .= $routeString[$rsEnd];
/*
At this point,our variables are defined as:
$uriRegex: regex to match against user-supplied URI
$replacement: custom route regex, replacing capture-groups with back-references
All that's left to do is create the custom URI, and return the site_url
*/
$customURI = preg_replace( '#^'.$uriRegex.'$#', $replacement, $uri );
return normal_site_url( $customURI );
}
}
// If there is a literal match AND no back-references are setup, and we are done
else if($val == $uri)
return normal_site_url( $key );
}
return normal_site_url( $uri );
}
function normal_site_url($uri = '')
{
$CI =& get_instance();
return $CI->config->site_url($uri);
}