From 9a1ffb5db4625ddd3be36d89e5bccb043c6b6a35 Mon Sep 17 00:00:00 2001 From: Lonnie Ezell Date: Thu, 29 Jun 2017 16:31:52 -0500 Subject: [PATCH] New discoverLocal method on RouteCollection to auto-scan code module route files. --- application/Config/Routes.php | 1 + system/Config/Services.php | 2 +- system/Router/RouteCollection.php | 83 ++++++++++++- tests/_support/Config/Routes.php | 7 ++ tests/system/CodeIgniterTest.php | 3 +- tests/system/CommonFunctionsTest.php | 6 +- tests/system/Router/RouteCollectionTest.php | 127 +++++++++++++------- tests/system/Router/RouterTest.php | 4 +- user_guide_src/source/general/modules.rst | 21 ++-- user_guide_src/source/general/routing.rst | 10 ++ 10 files changed, 197 insertions(+), 67 deletions(-) create mode 100644 tests/_support/Config/Routes.php diff --git a/application/Config/Routes.php b/application/Config/Routes.php index d10976275bfd..bbb60524799a 100644 --- a/application/Config/Routes.php +++ b/application/Config/Routes.php @@ -64,6 +64,7 @@ $routes->setTranslateURIDashes(false); $routes->set404Override(); $routes->setAutoRoute(true); +$routes->discoverLocal(false); /** * -------------------------------------------------------------------- diff --git a/system/Config/Services.php b/system/Config/Services.php index ccc9e317e3d3..b8d86de3303f 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -457,7 +457,7 @@ public static function routes($getShared = true) return self::getSharedInstance('routes'); } - return new \CodeIgniter\Router\RouteCollection(); + return new \CodeIgniter\Router\RouteCollection(self::locator()); } //-------------------------------------------------------------------- diff --git a/system/Router/RouteCollection.php b/system/Router/RouteCollection.php index 6ec5c276029b..f191396bec7f 100644 --- a/system/Router/RouteCollection.php +++ b/system/Router/RouteCollection.php @@ -35,6 +35,7 @@ * @since Version 3.0.0 * @filesource */ +use CodeIgniter\Autoloader\FileLocator; /** * Class RouteCollection @@ -166,15 +167,34 @@ class RouteCollection implements RouteCollectionInterface */ protected $currentOptions = null; + /** + * Determines whether locally specified, PSR4 + * compatible code is automatically scanned + * for addition routes in a {namespace}/Config/Routes.php file. + * + * @var bool + */ + protected $discoverLocal = false; + + /** + * A little performance booster. + * @var bool + */ + protected $didDiscover = false; + + protected $fileLocator; + //-------------------------------------------------------------------- /** * Constructor */ - public function __construct() + public function __construct(FileLocator $locator) { // Get HTTP verb $this->HTTPVerb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli'; + + $this->fileLocator = $locator; } //-------------------------------------------------------------------- @@ -332,6 +352,62 @@ public function get404Override() //-------------------------------------------------------------------- + /** + * If true, will attempt to auto-discover new route files + * based on any PSR4 namespaces that have been set + * in Config/Autoload.php. + * + * @param bool $discover + * + * @return $this + */ + public function discoverLocal(bool $discover) + { + $this->discoverLocal = $discover; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Will attempt to discover any additional routes, either through + * the local PSR4 namespaces, or through selected Composer packages. + * (Composer coming soon...) + */ + protected function discoverRoutes() + { + if ($this->didDiscover) return; + + // We need this var in local scope + // so route files can access it. + $routes = $this; + + /* + * Discover Local Files + */ + if ($this->discoverLocal === true) + { + $files = $this->fileLocator->search('Config/Routes.php'); + + foreach ($files as $file) + { + // Don't include our main file again... + if ($file == APPPATH.'Config/Routes.php') continue; + + include $file; + } + } + + /* + * Discover Composer files (coming soon) + */ + + $this->didDiscover = true; + } + + //-------------------------------------------------------------------- + /** * Sets the default constraint to be used in the system. Typically * for use with the 'resources' method. @@ -418,6 +494,11 @@ public function shouldAutoRoute(): bool */ public function getRoutes(): array { + // Since this is the entry point for the Router, + // take a moment to do any route discovery + // we might need to do. + $this->discoverRoutes(); + $routes = []; foreach ($this->routes as $r) diff --git a/tests/_support/Config/Routes.php b/tests/_support/Config/Routes.php new file mode 100644 index 000000000000..2369e33b22b7 --- /dev/null +++ b/tests/_support/Config/Routes.php @@ -0,0 +1,7 @@ +add('testing', 'TestController::index'); diff --git a/tests/system/CodeIgniterTest.php b/tests/system/CodeIgniterTest.php index c7e6ff8516ef..2908393201d2 100644 --- a/tests/system/CodeIgniterTest.php +++ b/tests/system/CodeIgniterTest.php @@ -1,5 +1,6 @@ setAutoRoute(false); $routes->set404Override(function() { echo '404 Override by Closure.'; diff --git a/tests/system/CommonFunctionsTest.php b/tests/system/CommonFunctionsTest.php index e1e5cf79323b..91646ccfee98 100644 --- a/tests/system/CommonFunctionsTest.php +++ b/tests/system/CommonFunctionsTest.php @@ -70,7 +70,7 @@ public function testRedirectReturnsNamedRouteFirst() $_SERVER['REQUEST_METHOD'] = 'GET'; $response = $this->createMock(\CodeIgniter\HTTP\Response::class); - $routes = new \CodeIgniter\Router\RouteCollection(); + $routes = new \CodeIgniter\Router\RouteCollection(new \CodeIgniter\Autoloader\MockFileLocator(new \Config\Autoload())); \CodeIgniter\Services::injectMock('response', $response); \CodeIgniter\Services::injectMock('routes', $routes); @@ -87,7 +87,7 @@ public function testRedirectReverseRouting() $_SERVER['REQUEST_METHOD'] = 'GET'; $response = $this->createMock(\CodeIgniter\HTTP\Response::class); - $routes = new \CodeIgniter\Router\RouteCollection(); + $routes = new \CodeIgniter\Router\RouteCollection(new \CodeIgniter\Autoloader\MockFileLocator(new \Config\Autoload())); \CodeIgniter\Services::injectMock('response', $response); \CodeIgniter\Services::injectMock('routes', $routes); @@ -104,7 +104,7 @@ public function testRedirectNormalRouting() $_SERVER['REQUEST_METHOD'] = 'GET'; $response = $this->createMock(\CodeIgniter\HTTP\Response::class); - $routes = new \CodeIgniter\Router\RouteCollection(); + $routes = new \CodeIgniter\Router\RouteCollection(new \CodeIgniter\Autoloader\MockFileLocator(new \Config\Autoload())); \CodeIgniter\Services::injectMock('response', $response); \CodeIgniter\Services::injectMock('routes', $routes); diff --git a/tests/system/Router/RouteCollectionTest.php b/tests/system/Router/RouteCollectionTest.php index 749ca02b6336..22f42c8761a7 100644 --- a/tests/system/Router/RouteCollectionTest.php +++ b/tests/system/Router/RouteCollectionTest.php @@ -1,5 +1,8 @@ APPPATH.'Config', + 'App' => APPPATH, + ]; + $config = array_merge($config, $defaults); + + $autoload = new \Config\Autoload(); + $autoload->psr4 = $config; + + $loader = new MockFileLocator($autoload); + $loader->setFiles($files); + + return new RouteCollection($loader); + } + public function testBasicAdd() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->add('home', '\my\controller'); @@ -37,7 +57,7 @@ public function testBasicAdd() public function testAddPrefixesDefaultNamespaceWhenNoneExist() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->add('home', 'controller'); @@ -54,7 +74,7 @@ public function testAddPrefixesDefaultNamespaceWhenNoneExist() public function testAddIgnoresDefaultNamespaceWhenExists() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->add('home', 'my\controller'); @@ -73,7 +93,7 @@ public function testAddWorksWithCurrentHTTPMethods() { $_SERVER['REQUEST_METHOD'] = 'GET'; - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->match(['get'], 'home', 'controller'); @@ -90,7 +110,7 @@ public function testAddWorksWithCurrentHTTPMethods() public function testAddWithLeadingSlash() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->add('/home', 'controller'); @@ -109,7 +129,7 @@ public function testMatchIgnoresInvalidHTTPMethods() { $_SERVER['REQUEST_METHOD'] = 'GET'; - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->match(['put'], 'home', 'controller'); @@ -124,7 +144,7 @@ public function testAddWorksWithArrayOFHTTPMethods() { $_SERVER['REQUEST_METHOD'] = 'POST'; - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->add('home', 'controller', ['get', 'post']); @@ -141,7 +161,7 @@ public function testAddWorksWithArrayOFHTTPMethods() public function testAddReplacesDefaultPlaceholders() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->add('home/(:any)', 'controller'); @@ -158,7 +178,7 @@ public function testAddReplacesDefaultPlaceholders() public function testAddReplacesCustomPlaceholders() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->addPlaceholder('smiley', ':-)'); $routes->add('home/(:smiley)', 'controller'); @@ -176,7 +196,7 @@ public function testAddReplacesCustomPlaceholders() public function testAddRecognizesCustomNamespaces() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->setDefaultNamespace('\CodeIgniter'); $routes->add('home', 'controller'); @@ -194,7 +214,7 @@ public function testAddRecognizesCustomNamespaces() public function testSetDefaultControllerStoresIt() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->setDefaultController('godzilla'); $this->assertEquals('godzilla', $routes->getDefaultController()); @@ -204,7 +224,7 @@ public function testSetDefaultControllerStoresIt() public function testSetDefaultMethodStoresIt() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->setDefaultMethod('biggerBox'); $this->assertEquals('biggerBox', $routes->getDefaultMethod()); @@ -214,7 +234,7 @@ public function testSetDefaultMethodStoresIt() public function testTranslateURIDashesWorks() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->setTranslateURIDashes(true); $this->assertEquals(true, $routes->shouldTranslateURIDashes()); @@ -224,7 +244,7 @@ public function testTranslateURIDashesWorks() public function testAutoRouteStoresIt() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->setAutoRoute(true); $this->assertEquals(true, $routes->shouldAutoRoute()); @@ -234,7 +254,7 @@ public function testAutoRouteStoresIt() public function testGroupingWorks() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->group('admin', function($routes) { @@ -252,7 +272,7 @@ public function testGroupingWorks() public function testGroupGetsSanitized() { - $routes = new RouteCollection(); + $routes = $this->getCollector(); $routes->group('