Bug when routing to a controller inside a subfolder |
|||
|---|---|---|---|
| Date: | 09/25/2007 | Severity: | Minor |
| Status: | Resolved | Reporter: | Ekoka |
| Version: | 1.5.4 | ||
| Keywords: | Libraries, URI Class | ||
| Forum Thread: | http://codeigniter.com/forums/viewthread/50795/ | ||
Description
I’m surprised that there isn’t yet an official report for this one.
when rerouting a request to a controller that is inside a subfolder, there’s a mapping error when looking for arguments to the action method. The arguments list is shifted by 1. Resulting in the first vanishing, 2nd becoming 1st, etc.
From my inspection of the code (in v1.5.4 svn) I gathered a few points:
1- This bug involves 2 files: URI.php and CodeIgniter/codeigniter.php.
2- During the mapping process from route to controller, the original $segments keeps track of everything that was passed through the url (including the subdirectory).
3- Meanwhile the rerouted $rsegments is transformed to only keep track of the controller, action and variables and loses the subfolder information which is assigned to another variable (Router::$directory). This operation is supposed to make the mapping process transparent to the rest of the application. At this point there should be no distinction made between requests for a controller in a subfolder and normal requests, since the latter simply have a $directory == ‘’.
4- Yet, inside the file system/codeigniter/CodeIgniter.php, $rsegments is treated differently depending on which types of request has been sent.
call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, (($RTR->fetch_directory() == ‘’) ? 2 : 3)));
5- Further more, the bug has been obscured due to an unexpected behavior of the PHP array_diff() function, used in the _reindex_segments() method ( formerly declared in Router.php 1.5.3 and now belonging to URI.php 1.5.4 svn).
To illustrate, take 2 arrays:
$segments = array(’my_directory’, ’my_controller’, ’my_method’, ’var1’, ’var2’);
$rsegments = array(’my_controller’, ’my_method’, ’var1’, ’var2’);
array_diff($rsegments,$segments) will return 0, when we’re in fact expecting 1.
whereas
array_diff($segments,$rsegments) will return 1.
The false assumptions made about array_diff() is the reason why some of the rerouted subfolder/controller “accidentally” work.
Proposed fix (only for the current svn version):
1- In URI.php change the snippet of code in _reindex_segments() to accurately compare the 2 segment arrays from :
$diff = (count(array_diff($this->rsegments, $this->segments)) == 0) ? FALSE : TRUE;
to:
$diff = (count(array_diff($this->segments, $this->rsegments)) == 0) ? FALSE : TRUE;
2- In system/codeigniter/CodeIgniter.php, change the snippet of code that slice $rsegments to find the request’s arguments. Whether the controller is in a subdirectory or not, $rsegments will no longer have that information and should be treated the same regardless.
From:
call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, (($RTR->fetch_directory() == ‘’) ? 2 : 3)));
To:
call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));
Code Sample
/* routes.php */
$route['subfolder/controllerX/actionA/(.*)/(.*)'] = "subfolder/controller_x/method_z/$1/$2";
/* subfolder/Controller_x */
class Controller_x extends Controller{
function method_z($var1=null,$var2=null){
echo '$var1: '. $var1 . '<br>';
echo '$var2: '. $var2;
exit;
}
}
/* url sent */
http://somehost/my_ci_app/subfolder/controllerx/actionA/34/58
Expected Result
$var1: 34
$var2: 58
Actual Result
$var1: 58
$var2:
Comment on Bug Report
| Posted by: George Tavas on 1 January 2008 9:11am | |
|
|
Yes i have found that bug too, but your suggestion is OK when have to do with subdirectories, i.e:route[‘hello/master’] = ‘hello/index/master’; the URI::rsegment(2) method will return “master” intead of index. and that because the rsegments array have all the values of the segments array. Let’s try the following code: error_reporting(E_ALL);
echo $diff; the result will be True because the array_diff will return an empty array. In your example it works just because the array_diff didn’t found the “controllerX” segment. I currently replace the array_diff with the array_intersect function to force the segments re-indexing every time. |
| Posted by: Ekoka on 1 January 2008 6:59pm | |
|
|
the URI::rsegment tracks the rerouted segment, i.e. ‘hello/index/master’, so in this case URI::rsegment(2) = ‘index’; I extensively use routing and subdirectories and I’ve never had a problem with my solution. http://codeigniter.com/user_guide/libraries/uri.html |
| Posted by: Ekoka on 1 January 2008 7:18pm | |
|
|
sorry, actually i’m confusing things here: the URI::segments will track the rerouted url (‘hello/index/master’). The URI::rsegments only tracks the controller/methods/param_1/.../param_N. In your case the content of URI::rsegments depends on the structure of your controllers folder. If ‘hello’ is a subfolder of application/controllers/ then the rsegments will contain ‘index’ as a controller and ‘master’ as a method with no parameters. If on the other hand the router finds no subfolder called ‘hello’, it assumes then that ‘hello’ must be the controller, ‘index’ the method and ‘master’ the parameter to pass to that method. |
| Posted by: Ekoka on 1 January 2008 11:29pm | |
|
|
Sorry again, my head is slowly coming out of the festivities (january 1st). I’m now paying a closer attention to what you wrote. First I’d like correct what I’ve said earlier for the sake of not confusing anyone. The URI::$segments tracks the original segments in the url that was sent to the server, while the URI::$rsegments tracks only the final controller, method and parameters as described in my previous post. Now paying closer attention to the issue you just pointed, i think you’re probably right, but i don’t think array_intersect() will solve the problem. When i think about it,the main issue here is to insure that the $segments array and the $rsegments array are really not the same in terms of content, number of elements and order or those elements in each array. So I would think that a better check to ensure that difference would be :
$diff = (count(array_diff_assoc($segments,$rsegments)) == 0 ) && |
| Posted by: George Tavas on 6 January 2008 3:56am | |
|
|
Anyway, but why we must check this everytime? |
| Posted by: Derek Jones on 30 January 2008 12:15am | |
|
|
I had committed a fix to a related bug earlier today revolving around this $diff computation, and have now also committed a fix for the improper array slicing of the rsegment array in CodeIgniter.php. So this issue should be wrapped up in the SVN. |
