-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
updated Router to properly translate uri dashes that map to controller subdirs #4307
Conversation
@paulbalandan @kenjis @michalsn Could someone review this PR to fix #4294? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've looked at the changes and it looks good to me. I agree with the changes you've made. I have a minor cosmetic request. Change $segmentConvert
to simply $segment
. :)
I'll check first if this method is covered by tests or not before approving this for merge.
Can you add a test case for this one similar to here https://github.com/codeigniter4/CodeIgniter4/blob/develop/tests/system/Router/RouterTest.php#L215-L227 but using the underscored subfolder as in your case. It should assert that it will still get the correct controller and method. |
Thank you, @paulbalandan!
Please note that I chose a different variable name to distinguish this value from the raw segments referred to elsewhere. It was my feeling that this more clearly indicated that the segment had been changed with ucfirst and with the dash-to-underscore replacement. I'd like to keep it because I think it makes the intent of the code much clearer. It was pretty difficult to understand the intent of this code because there are so few comments and the variables and functions are poorly named. In particular, the function I have another question: may I remove the ucfirst call in setDirectory? This function is only called in the validateRequest function, where I have already applied ucfirst to the supplied parameter, and in system/Test/FeatureTestTrait.php where the supplied parameter is
I have run the phpunit test and it passed all the previous tests, but please do run any additional tests:
I'll work on adding another test as you directed. |
The tests run pretty smoothly and in fact the coverage increased for |
406f845
to
8176e9c
Compare
@paulbalandan I have committed another set of changes. NOTE that it implements more stringent enforcement of PSR-4 compliance in that it will throw PageNotFoundExceptions for controller names which are not acceptable PHP class names. This has the benefit of rejecting any Note that I did change |
// if a prior directory value has been set, just return segments and get out of here | ||
if (isset($this->directory)) | ||
{ | ||
return $segments; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary? As this is untested, can you add a test case covering this possibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@paulbalandan thank you for your detailed review.
This particular bit of code is not something I've written. I don't know why (or even if) it's necessary. There might be a situation where someone wants to set Router::directory
and just have all the segments represent controller/method/params?
I have reviewed the code and it looks like the only place that Router::directory
ever gets set is in the Router::setDirectory
method which is only called in two places. The first is right there in that same scanControllers/validateRequest function. The second is in system/Test/FeatureTestTrait.php. In that latter case, it gets set to null
which would cause isset($this->directory)
to return false:
$v = null;
var_dump(isset($v)); // outputs bool(false)
I do not know under what circumstances anyone might want to call setDirectory prior to calling scanControllers or autoRoute function. I can imagine that it might be possible to instantiate a Router object somewhere, set its directory, and want to feed it parameters, but I can't seem to figure out any situation where CodeIgniter does this or might do it? It might be possible for a single Router instance to call autoRoute or scanControllers twice, but I honestly don't know -- and I don't know why it would be called with a different $segments
parameter the second time.
It was in there when I began working on this function, and it seemed safest to leave it in. On further reflection, being able to set an arbitrary directory location for a controller and then passing it arbitrary parameters seems unsafe from a security perspective.
If we want to create a test to make sure this code runs, this will do it:
public function testRouterPriorDirectory()
{
$router = new Router($this->collection, $this->request);
$router->setDirectory('foo/bar/baz', false, true);
$router->handle('Some_controller/some_method/param1/param2/param3');
var_dump($router->directory());
var_dump($router->controllerName());
var_dump($router->methodName());
}
Currently, the output of those last lines:
string(12) "foo/bar/baz/"
string(15) "Some_controller"
string(11) "some_method"
I could add this test and create assertions to expect those values, but I'm not certain why we would want to allow that behavior or if those are the desired values. Personally, I think it'd be safer to remove this check and force any autoRoute or scanControllers functionality to constrain code execution to the controllers folder.
if ($validate) | ||
{ | ||
$segments = explode('/', trim($dir, '/')); | ||
foreach ($segments as $segment) | ||
{ | ||
if (! $this->isValidSegment($segment)) | ||
{ | ||
return; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This addition is not tested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added RouterTest::testSetDirectoryValid
and RouterTest::testSetDirectoryInvalid
which test the setDirectory function to run this block of code for both valid and invalid directories.
9ee8ecb
to
f1dc863
Compare
@paulbalandan I've pushed another commit and I think it resolves the issues you mentioned in your review -- for some reason github only offers me a "resolve conversation" button rather than continuing our discussion. I hope you will please take a look and let me know if you have further concerns. |
system/Router/Router.php
Outdated
*/ | ||
private function isValidSegment(string $segment): bool | ||
{ | ||
return (bool)preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $segment); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CS: Add space after the cast. (bool) preg_match
{ | ||
$this->setDirectory(array_shift($segments), true); | ||
$this->setDirectory($segmentConvert, true, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering. You added a third param to setDirectory
optionally set to true
but purposely set it to false
here. As you have promoted a stricter check on the segments, no validation here seems a bit off. Or am I missing something?
185619b
to
9e0c7b3
Compare
@paulbalandan sorry I'm slow to respond. For some reason github isn't letting me respond directly under your review comment so I'm commenting here.
I have added the space, committed to my local repo, rebased on the latest develop branch of CI4, and pushed. Please let me know if you have additional requests, questions, or issues.
By default, setDirectory has |
I have no idea why these checks failed. They seem totally unrelated to my PR. |
It is probably because ubuntu is now updated to 20.04. |
Ubuntu latest is now 20.04 so only 7.4+ is built in in the image. We need to specify the extensions we need for lower php versions. I'll send a PR. |
Please rebase, |
…r subdirectories. fix issue codeigniter4#4294
…alidSegment, tweaks to enforce PSR4 compliance in directory/namespace segments
… in isValidSegment, add back deprecated validateRequest function, add RouterTest methods for better code coverage
9e0c7b3
to
a06f1f1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rebase,
I have rebased and force-pushed. Please let me know if you have any additional thoughts.
if ($validate) | ||
{ | ||
$segments = explode('/', trim($dir, '/')); | ||
foreach ($segments as $segment) | ||
{ | ||
if (! $this->isValidSegment($segment)) | ||
{ | ||
return; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added RouterTest::testSetDirectoryValid
and RouterTest::testSetDirectoryInvalid
which test the setDirectory function to run this block of code for both valid and invalid directories.
$router->setDirectory('foo/bar/baz', false, true); | ||
$router->handle('Some_controller/some_method/param1/param2/param3'); | ||
|
||
$this->assertEquals('foo/bar/baz/', $router->directory()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sneakyimp Why is this test necessary?
Why does setDirectory()
take precedence over URI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've found your answer.
https://github.com/codeigniter4/CodeIgniter4/pull/4307/files#r584375184
Fixes #4294
Description
I have made a minor tweak to CodeIgniter\Router::validateRequest so that dashes in a subdirectory are mapped to underscores if
translateURIDashes
=true
. This appears to correctly auto-route requests when you have a dash in the URI that corresponds to a controller directory.Checklist: