diff --git a/src/Menu.php b/src/Menu.php index 5d32bfb..f89a039 100644 --- a/src/Menu.php +++ b/src/Menu.php @@ -28,10 +28,10 @@ abstract class Menu */ public function __construct(BaseMenu $builder = null) { - $this->builder = $builder ?? BaseMenu::new()->setActive( - current_url(), - (new URI(base_url()))->getPath() ?? '/' - ); + $current = $this->detectCurrent(); + $root = (new URI(site_url()))->getPath() ?? '/'; + + $this->builder = $builder ?? BaseMenu::new()->setActive($current, $root); foreach (class_uses_recursive($this) as $trait) { @@ -59,4 +59,40 @@ public function builder(): BaseMenu * @return string */ abstract public function __toString(): string; + + /** + * Returns the current URL to use for determining + * which menu items should be active. + * Due to this bug: + * - https://github.com/codeigniter4/CodeIgniter4/issues/4116 + * ...we cannot use current_url(). This method can be + * replaced if that bug is fixed or if we get this: + * - https://github.com/codeigniter4/CodeIgniter4/pull/4647 + * + * @return string + * @internal + */ + protected function detectCurrent(): string + { + // Force path discovery in a new IncomingRequest + $request = service('request', null, false); + $path = ltrim($request->detectPath($request->config->uriProtocol), '/'); + + // Build the full URL based on the config and path + $url = rtrim($request->config->baseURL, '/ ') . '/'; + + // Check for an index page + if ($request->config->indexPage !== '') + { + $url .= $request->config->indexPage; + + // If there is a path then we need a separator + if ($path !== '') + { + $url .= '/'; + } + } + + return (string) (new URI($url . $path)); + } } diff --git a/tests/_support/MenusTestCase.php b/tests/_support/MenusTestCase.php index 1dfcaa5..f60c473 100644 --- a/tests/_support/MenusTestCase.php +++ b/tests/_support/MenusTestCase.php @@ -32,9 +32,8 @@ protected function setUp(): void Factories::injectMock('config', 'App', $config); // Set a current URL for checking "active" links - $request = Services::request(); - $request->uri = new URI(site_url('current')); - Services::injectMock('request', $request); + $_SERVER['REQUEST_URI'] = '/current'; + Services::injectMock('request', null); // Create some Menu aliases for testing $config = new MenusConfig(); diff --git a/tests/menu/MenuTest.php b/tests/menu/MenuTest.php index 042c8bd..cb3cc7b 100644 --- a/tests/menu/MenuTest.php +++ b/tests/menu/MenuTest.php @@ -1,5 +1,6 @@ menu = new class extends Menu { + return new class extends Menu { public function __toString(): string { return $this->builder - ->link(site_url('/'), 'Home') + ->link(site_url(''), 'Home') ->link(site_url('/current'), 'Grain') ->render(); } @@ -32,7 +28,7 @@ public function __toString(): string public function testGetBuilder() { - $result = $this->menu->builder(); + $result = $this->menu()->builder(); $this->assertInstanceOf(BaseMenu::class, $result); } @@ -55,7 +51,19 @@ public function __toString(): string public function testGetUsesCurrentUrl() { $expected = '
'; - $result = $this->menu->__toString(); + $result = $this->menu()->__toString(); + + $this->assertSame($expected, $result); + } + + public function testGetUsesIndexPage() + { + $config = config('App'); + $config->indexPage = 'index.php'; + Services::injectMock('request', null); + + $expected = ''; + $result = $this->menu()->__toString(); $this->assertSame($expected, $result); }