From b62ed44d2d024badc7afffdacdbfb82516a967b1 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 3 Apr 2019 23:32:10 -0400 Subject: [PATCH 1/8] [Composer] Add dependency nikic/fast-route - Update composer.lock with new dependency --- composer.json | 1 + composer.lock | 104 +++++++++++++++++++++++--------------------------- 2 files changed, 48 insertions(+), 57 deletions(-) diff --git a/composer.json b/composer.json index 765ec23a24..3f910cbe92 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "michelf/php-markdown": "^1.7", "mobiledetect/mobiledetectlib": "2.8.*", "monolog/monolog": "^1.24", + "nikic/fast-route": "^1.3", "paragonie/random_compat": "^2.0", "pear/text_languagedetect": "1.*", "psr/container": "^1.0", diff --git a/composer.lock b/composer.lock index ff38c81050..8595e3ddde 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8897c1f6912cc9b889534a8c59deead1", + "content-hash": "cf9846983bd7e4eb34f93ab6b493736b", "packages": [ { "name": "asika/simple-console", @@ -887,6 +887,52 @@ ], "time": "2018-11-05T09:00:11+00:00" }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "FastRoute\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "time": "2018-02-13T20:26:39+00:00" + }, { "name": "npm-asset/cropperjs", "version": "1.2.2", @@ -1083,22 +1129,6 @@ "require": { "npm-asset/ev-emitter": ">=1.0.0,<2.0.0" }, - "require-dev": { - "npm-asset/chalk": ">=1.1.1,<2.0.0", - "npm-asset/cheerio": ">=0.19.0,<0.20.0", - "npm-asset/gulp": ">=3.9.0,<4.0.0", - "npm-asset/gulp-jshint": ">=1.11.2,<2.0.0", - "npm-asset/gulp-json-lint": ">=0.1.0,<0.2.0", - "npm-asset/gulp-rename": ">=1.2.2,<2.0.0", - "npm-asset/gulp-replace": ">=0.5.4,<0.6.0", - "npm-asset/gulp-requirejs-optimize": "dev-github:metafizzy/gulp-requirejs-optimize", - "npm-asset/gulp-uglify": ">=1.4.2,<2.0.0", - "npm-asset/gulp-util": ">=3.0.7,<4.0.0", - "npm-asset/highlight.js": ">=8.9.1,<9.0.0", - "npm-asset/marked": ">=0.3.5,<0.4.0", - "npm-asset/minimist": ">=1.2.0,<2.0.0", - "npm-asset/transfob": ">=1.0.0,<2.0.0" - }, "type": "npm-asset-library", "extra": { "npm-asset-bugs": { @@ -1144,14 +1174,6 @@ "reference": null, "shasum": "2736e332aaee73ccf0a14a5f0066391a0a13f4a3" }, - "require-dev": { - "npm-asset/grunt": "~0.4.2", - "npm-asset/grunt-contrib-cssmin": "~0.9.0", - "npm-asset/grunt-contrib-jshint": "~0.6.3", - "npm-asset/grunt-contrib-less": "~0.11.0", - "npm-asset/grunt-contrib-uglify": "~0.4.0", - "npm-asset/grunt-contrib-watch": "~0.6.1" - }, "type": "npm-asset-library", "extra": { "npm-asset-bugs": { @@ -1185,32 +1207,6 @@ "reference": null, "shasum": "2c89d6889b5eac522a7eea32c14521559c6cbf02" }, - "require-dev": { - "npm-asset/commitplease": "2.0.0", - "npm-asset/core-js": "0.9.17", - "npm-asset/grunt": "0.4.5", - "npm-asset/grunt-babel": "5.0.1", - "npm-asset/grunt-cli": "0.1.13", - "npm-asset/grunt-compare-size": "0.4.0", - "npm-asset/grunt-contrib-jshint": "0.11.2", - "npm-asset/grunt-contrib-uglify": "0.9.2", - "npm-asset/grunt-contrib-watch": "0.6.1", - "npm-asset/grunt-git-authors": "2.0.1", - "npm-asset/grunt-jscs": "2.1.0", - "npm-asset/grunt-jsonlint": "1.0.4", - "npm-asset/grunt-npmcopy": "0.1.0", - "npm-asset/gzip-js": "0.3.2", - "npm-asset/jsdom": "5.6.1", - "npm-asset/load-grunt-tasks": "1.0.0", - "npm-asset/qunit-assert-step": "1.0.3", - "npm-asset/qunitjs": "1.17.1", - "npm-asset/requirejs": "2.1.17", - "npm-asset/sinon": "1.10.3", - "npm-asset/sizzle": "2.2.1", - "npm-asset/strip-json-comments": "1.0.3", - "npm-asset/testswarm": "1.1.0", - "npm-asset/win-spawn": "2.0.0" - }, "type": "npm-asset-library", "extra": { "npm-asset-bugs": { @@ -1361,12 +1357,6 @@ "reference": null, "shasum": "06f0335f16e353a695e7206bf50503cb523a6ee5" }, - "require-dev": { - "npm-asset/grunt": "~0.4.1", - "npm-asset/grunt-contrib-connect": "~0.5.0", - "npm-asset/grunt-contrib-jshint": "~0.7.1", - "npm-asset/grunt-contrib-uglify": "~0.2.7" - }, "type": "npm-asset-library", "extra": { "npm-asset-bugs": { From 61b6ab7e3e074d1e8341a8689a5d2f9f5da48083 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 3 Apr 2019 23:35:24 -0400 Subject: [PATCH 2/8] Add Router to App - Create new Friendica\App\Router class - Add route_collection hook - Add route dispatching in App->runFrontend --- src/App.php | 43 +++++++++++++++++++++++++++++++++---------- src/App/Router.php | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 src/App/Router.php diff --git a/src/App.php b/src/App.php index 7ed0377130..f698d06c82 100644 --- a/src/App.php +++ b/src/App.php @@ -8,8 +8,10 @@ use Detection\MobileDetect; use DOMDocument; use DOMXPath; use Exception; +use FastRoute\RouteCollector; use Friendica\Core\Config\Cache\IConfigCache; use Friendica\Core\Config\Configuration; +use Friendica\Core\Hook; use Friendica\Core\Theme; use Friendica\Database\DBA; use Friendica\Model\Profile; @@ -35,7 +37,6 @@ use Psr\Log\LoggerInterface; */ class App { - public $module_loaded = false; public $module_class = null; public $query_string = ''; public $page = []; @@ -1247,8 +1248,13 @@ class App $this->module = "login"; } + $router = new App\Router(); + $this->collectRoutes($router->routeCollector); + + $this->module_class = $router->getModuleClass($this->cmd); + $privateapps = $this->config->get('config', 'private_addons', false); - if (Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) { + if (!$this->module_class && Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) { //Check if module is an app and if public access to apps is allowed or not if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) { info(Core\L10n::t("You must be logged in to use addons. ")); @@ -1257,24 +1263,21 @@ class App if (function_exists($this->module . '_module')) { LegacyModule::setModuleFile("addon/{$this->module}/{$this->module}.php"); $this->module_class = 'Friendica\\LegacyModule'; - $this->module_loaded = true; } } } // Controller class routing - if (! $this->module_loaded && class_exists('Friendica\\Module\\' . ucfirst($this->module))) { + if (!$this->module_class && class_exists('Friendica\\Module\\' . ucfirst($this->module))) { $this->module_class = 'Friendica\\Module\\' . ucfirst($this->module); - $this->module_loaded = true; } /* If not, next look for a 'standard' program module in the 'mod' directory * We emulate a Module class through the LegacyModule class */ - if (! $this->module_loaded && file_exists("mod/{$this->module}.php")) { + if (!$this->module_class && file_exists("mod/{$this->module}.php")) { LegacyModule::setModuleFile("mod/{$this->module}.php"); $this->module_class = 'Friendica\\LegacyModule'; - $this->module_loaded = true; } /* The URL provided does not resolve to a valid module. @@ -1286,7 +1289,7 @@ class App * * Otherwise we are going to emit a 404 not found. */ - if (! $this->module_loaded) { + if (!$this->module_class) { // Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit. if (!empty($_SERVER['QUERY_STRING']) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { exit(); @@ -1310,7 +1313,7 @@ class App $content = ''; // Initialize module that can set the current theme in the init() method, either directly or via App->profile_uid - if ($this->module_loaded) { + if ($this->module_class) { $this->page['page_title'] = $this->module; $placeholder = ''; @@ -1336,7 +1339,7 @@ class App $func($this); } - if ($this->module_loaded) { + if ($this->module_class) { if (! $this->error && $_SERVER['REQUEST_METHOD'] === 'POST') { Core\Hook::callAll($this->module . '_mod_post', $_POST); call_user_func([$this->module_class, 'post']); @@ -1502,4 +1505,24 @@ class App $this->internalRedirect($toUrl); } } + + /** + * @brief Static declaration of Friendica routes. + * + * Supports: + * - Route groups + * - Variable parts + * Disregards: + * - HTTP method other than GET + * - Named parameters + * + * Handler must be the name of a class extending Friendica\BaseModule. + * + * @param RouteCollector $routeCollector + * @throws InternalServerErrorException + */ + private function collectRoutes(RouteCollector $routeCollector) + { + Hook::callAll('route_collection', $routeCollector); + } } diff --git a/src/App/Router.php b/src/App/Router.php new file mode 100644 index 0000000000..1281001a3b --- /dev/null +++ b/src/App/Router.php @@ -0,0 +1,43 @@ +routeCollector = $routeCollector; + } + + public function getModuleClass($cmd) + { + $cmd = '/' . ltrim($cmd, '/'); + + $dispatcher = new \FastRoute\Dispatcher\GroupCountBased($this->routeCollector->getData()); + + $moduleClass = null; + + // @TODO: Enable method-specific modules + $httpMethod = 'GET'; + $routeInfo = $dispatcher->dispatch($httpMethod, $cmd); + if ($routeInfo[0] === Dispatcher::FOUND) { + $moduleClass = $routeInfo[1]; + } + + return $moduleClass; + } +} \ No newline at end of file From 993f4e5fccc7bbece249da4888b37a7f4e16f343 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 3 Apr 2019 23:35:54 -0400 Subject: [PATCH 3/8] Add sample route for /itemsource - Fix missing item guid not displaying in Module\Itemsource --- src/App.php | 2 ++ src/Module/Itemsource.php | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/App.php b/src/App.php index f698d06c82..d5698bdb76 100644 --- a/src/App.php +++ b/src/App.php @@ -1523,6 +1523,8 @@ class App */ private function collectRoutes(RouteCollector $routeCollector) { + $routeCollector->addRoute(['GET', 'POST'], '/itemsource[/{guid}]', Module\Itemsource::class); + Hook::callAll('route_collection', $routeCollector); } } diff --git a/src/Module/Itemsource.php b/src/Module/Itemsource.php index 12ce04f95c..f92baa987c 100644 --- a/src/Module/Itemsource.php +++ b/src/Module/Itemsource.php @@ -37,7 +37,6 @@ class Itemsource extends \Friendica\BaseModule $conversation = Model\Conversation::getByItemUri($item['uri']); - $guid = $item['guid']; $item_id = $item['id']; $item_uri = $item['uri']; $source = $conversation['source']; From e281b23672e1bc88a60af4a5d961d999e6171628 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 3 Apr 2019 23:45:55 -0400 Subject: [PATCH 4/8] Added doc for route_collection hook --- doc/Addons.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/Addons.md b/doc/Addons.md index 5d4be6db16..b47c32e6d6 100644 --- a/doc/Addons.md +++ b/doc/Addons.md @@ -433,7 +433,11 @@ For `select`, **field** is: - [3] (String): Additional help text; Optional, default is none. - [4] (Array): Associative array of options. Item key is option value, item value is option label; Mandatory. +### route_collection +Called just before dispatching the router. +Hook data is a `\FastRoute\RouterCollector` object that should be used to add addon routes pointing to classes. +**Notice**: The class whose name is provided in the route handler must be reachable via auto-loader. ## Complete list of hook callbacks @@ -610,6 +614,7 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep- Hook::callAll('load_config'); Hook::callAll('head'); Hook::callAll('footer'); + Hook::callAll('route_collection'); ### src/Model/Item.php From 350d3b51d43b75032bcc386b0303d774559929e3 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 3 Apr 2019 23:46:28 -0400 Subject: [PATCH 5/8] Added router to CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 337cca4289..39de513712 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ Version 2019.06 (UNRELEASED) (2019-06-?) Added syslog and stream Logger [nupplaphil] Added storage move cronjob [MrPetovan] Added collapsible panel for connector permission fields [MrPetovan] + Added rule-based router [MrPetovan] Closed Issues: 6303, 6478, 6319, 6921, 6903 From 42b360f479ce7f5635aed2edfe9a1be84d258d26 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 4 Apr 2019 07:29:26 -0400 Subject: [PATCH 6/8] Implement feedback changes to App\Router - Add class PhpDoc - Switch Router->routeCollector scope to protected - Added getter for Router->routeCollector - Added EOF new line - Removed unused use statement - Improved App->collectRoutes PhpDoc --- src/App.php | 5 +++-- src/App/Router.php | 20 +++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/App.php b/src/App.php index d5698bdb76..da9239c685 100644 --- a/src/App.php +++ b/src/App.php @@ -1249,7 +1249,7 @@ class App } $router = new App\Router(); - $this->collectRoutes($router->routeCollector); + $this->collectRoutes($router->getRouteCollector()); $this->module_class = $router->getModuleClass($this->cmd); @@ -1507,7 +1507,7 @@ class App } /** - * @brief Static declaration of Friendica routes. + * Static declaration of Friendica routes. * * Supports: * - Route groups @@ -1518,6 +1518,7 @@ class App * * Handler must be the name of a class extending Friendica\BaseModule. * + * @brief Static declaration of Friendica routes. * @param RouteCollector $routeCollector * @throws InternalServerErrorException */ diff --git a/src/App/Router.php b/src/App/Router.php index 1281001a3b..7753b6bbb9 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -7,12 +7,21 @@ use FastRoute\DataGenerator\GroupCountBased; use FastRoute\Dispatcher; use FastRoute\RouteCollector; use FastRoute\RouteParser\Std; -use Friendica\Module\Itemsource; +/** + * Wrapper for FastRoute\Router + * + * This wrapper only makes use of a subset of the router features, mainly parses a route rule to return the relevant + * module class. + * + * Actual routes are defined in App->collectRoutes. + * + * @package Friendica\App + */ class Router { /** @var RouteCollector */ - public $routeCollector; + protected $routeCollector; public function __construct(RouteCollector $routeCollector = null) { @@ -23,6 +32,11 @@ class Router $this->routeCollector = $routeCollector; } + public function getRouteCollector() + { + return $this->routeCollector; + } + public function getModuleClass($cmd) { $cmd = '/' . ltrim($cmd, '/'); @@ -40,4 +54,4 @@ class Router return $moduleClass; } -} \ No newline at end of file +} From 73835118550f4bf9c0df510283c905f89224d612 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 4 Apr 2019 07:30:48 -0400 Subject: [PATCH 7/8] Added test case for Router->getModuleClass --- tests/src/App/RouterTest.php | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/src/App/RouterTest.php diff --git a/tests/src/App/RouterTest.php b/tests/src/App/RouterTest.php new file mode 100644 index 0000000000..5a573bda95 --- /dev/null +++ b/tests/src/App/RouterTest.php @@ -0,0 +1,42 @@ +getRouteCollector(); + $routeCollector->addRoute(['GET'], '/', 'IndexModuleClassName'); + $routeCollector->addRoute(['GET'], '/test', 'TestModuleClassName'); + $routeCollector->addRoute(['GET'], '/test/sub', 'TestSubModuleClassName'); + $routeCollector->addRoute(['GET'], '/optional[/option]', 'OptionalModuleClassName'); + $routeCollector->addRoute(['GET'], '/variable/{var}', 'VariableModuleClassName'); + $routeCollector->addRoute(['GET'], '/optionalvariable[/{option}]', 'OptionalVariableModuleClassName'); + $routeCollector->addRoute(['POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'], '/unsupported', 'UnsupportedMethodModuleClassName'); + + $this->assertEquals('IndexModuleClassName', $router->getModuleClass('/')); + + $this->assertEquals('TestModuleClassName', $router->getModuleClass('/test')); + $this->assertNull($router->getModuleClass('/tes')); + + $this->assertEquals('TestSubModuleClassName', $router->getModuleClass('/test/sub')); + + $this->assertEquals('OptionalModuleClassName', $router->getModuleClass('/optional')); + $this->assertEquals('OptionalModuleClassName', $router->getModuleClass('/optional/option')); + $this->assertNull($router->getModuleClass('/optional/opt')); + + $this->assertEquals('VariableModuleClassName', $router->getModuleClass('/variable/123abc')); + $this->assertNull($router->getModuleClass('/variable')); + + $this->assertEquals('OptionalVariableModuleClassName', $router->getModuleClass('/optionalvariable')); + $this->assertEquals('OptionalVariableModuleClassName', $router->getModuleClass('/optionalvariable/123abc')); + + $this->assertNull($router->getModuleClass('/unsupported')); + } +} From d6d31f43a1a4d55ce56c1945b2c6b5b5a8728ee0 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 5 Apr 2019 23:16:12 -0400 Subject: [PATCH 8/8] Add App\Router dependency injection to App - Moved collectRoutes method from App to App\Router --- src/App.php | 63 +++++++++++++------------- src/App/Router.php | 26 +++++++++++ src/Factory/DependencyFactory.php | 3 +- tests/include/ApiTest.php | 3 +- tests/src/Database/DBATest.php | 3 +- tests/src/Database/DBStructureTest.php | 3 +- 6 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/App.php b/src/App.php index da9239c685..1649a87454 100644 --- a/src/App.php +++ b/src/App.php @@ -78,6 +78,11 @@ class App */ private $mode; + /** + * @var App\Router + */ + private $router; + /** * @var string The App URL path */ @@ -173,6 +178,11 @@ class App return $this->mode; } + public function getRouter() + { + return $this->router; + } + /** * Register a stylesheet file path to be included in the tag of every page. * Inclusion is done in App->initHead(). @@ -218,20 +228,22 @@ class App * * @param Configuration $config The Configuration * @param App\Mode $mode The mode of this Friendica app + * @param App\Router $router The router of this Friendica app * @param LoggerInterface $logger The current app logger * @param Profiler $profiler The profiler of this application * @param bool $isBackend Whether it is used for backend or frontend (Default true=backend) * * @throws Exception if the Basepath is not usable */ - public function __construct(Configuration $config, App\Mode $mode, LoggerInterface $logger, Profiler $profiler, $isBackend = true) + public function __construct(Configuration $config, App\Mode $mode, App\Router $router, LoggerInterface $logger, Profiler $profiler, $isBackend = true) { BaseObject::setApp($this); - $this->logger = $logger; $this->config = $config; - $this->profiler = $profiler; $this->mode = $mode; + $this->router = $router; + $this->profiler = $profiler; + $this->logger = $logger; $this->checkBackend($isBackend); $this->checkFriendicaApp(); @@ -1248,14 +1260,24 @@ class App $this->module = "login"; } - $router = new App\Router(); - $this->collectRoutes($router->getRouteCollector()); + /* + * ROUTING + * + * From the request URL, routing consists of obtaining the name of a BaseModule-extending class of which the + * post() and/or content() static methods can be respectively called to produce a data change or an output. + */ - $this->module_class = $router->getModuleClass($this->cmd); + // First we try explicit routes defined in App\Router + $this->router->collectRoutes(); - $privateapps = $this->config->get('config', 'private_addons', false); + Hook::callAll('route_collection', $this->router->getRouteCollector()); + + $this->module_class = $this->router->getModuleClass($this->cmd); + + // Then we try addon-provided modules that we wrap in the LegacyModule class if (!$this->module_class && Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) { //Check if module is an app and if public access to apps is allowed or not + $privateapps = $this->config->get('config', 'private_addons', false); if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) { info(Core\L10n::t("You must be logged in to use addons. ")); } else { @@ -1267,12 +1289,12 @@ class App } } - // Controller class routing + // Then we try name-matching a Friendica\Module class if (!$this->module_class && class_exists('Friendica\\Module\\' . ucfirst($this->module))) { $this->module_class = 'Friendica\\Module\\' . ucfirst($this->module); } - /* If not, next look for a 'standard' program module in the 'mod' directory + /* Finally, we look for a 'standard' program module in the 'mod' directory * We emulate a Module class through the LegacyModule class */ if (!$this->module_class && file_exists("mod/{$this->module}.php")) { @@ -1505,27 +1527,4 @@ class App $this->internalRedirect($toUrl); } } - - /** - * Static declaration of Friendica routes. - * - * Supports: - * - Route groups - * - Variable parts - * Disregards: - * - HTTP method other than GET - * - Named parameters - * - * Handler must be the name of a class extending Friendica\BaseModule. - * - * @brief Static declaration of Friendica routes. - * @param RouteCollector $routeCollector - * @throws InternalServerErrorException - */ - private function collectRoutes(RouteCollector $routeCollector) - { - $routeCollector->addRoute(['GET', 'POST'], '/itemsource[/{guid}]', Module\Itemsource::class); - - Hook::callAll('route_collection', $routeCollector); - } } diff --git a/src/App/Router.php b/src/App/Router.php index 7753b6bbb9..be85fcd9dc 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -7,6 +7,7 @@ use FastRoute\DataGenerator\GroupCountBased; use FastRoute\Dispatcher; use FastRoute\RouteCollector; use FastRoute\RouteParser\Std; +use Friendica\Module; /** * Wrapper for FastRoute\Router @@ -23,6 +24,25 @@ class Router /** @var RouteCollector */ protected $routeCollector; + /** + * Static declaration of Friendica routes. + * + * Supports: + * - Route groups + * - Variable parts + * Disregards: + * - HTTP method other than GET + * - Named parameters + * + * Handler must be the name of a class extending Friendica\BaseModule. + * + * @brief Static declaration of Friendica routes. + */ + public function collectRoutes() + { + $this->routeCollector->addRoute(['GET', 'POST'], '/itemsource[/{guid}]', Module\Itemsource::class); + } + public function __construct(RouteCollector $routeCollector = null) { if (!$routeCollector) { @@ -37,6 +57,12 @@ class Router return $this->routeCollector; } + /** + * Returns the relevant module class name for the given page URI or NULL if no route rule matched. + * + * @param string $cmd The path component of the request URL without the query string + * @return string|null A Friendica\BaseModule-extending class name if a route rule matched + */ public function getModuleClass($cmd) { $cmd = '/' . ltrim($cmd, '/'); diff --git a/src/Factory/DependencyFactory.php b/src/Factory/DependencyFactory.php index 63defd95f5..0f33e095bc 100644 --- a/src/Factory/DependencyFactory.php +++ b/src/Factory/DependencyFactory.php @@ -24,6 +24,7 @@ class DependencyFactory { $basePath = BasePath::create($directory, $_SERVER); $mode = new App\Mode($basePath); + $router = new App\Router(); $configLoader = new Config\ConfigFileLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); @@ -34,6 +35,6 @@ class DependencyFactory $logger = Factory\LoggerFactory::create($channel, $config, $profiler); Factory\LoggerFactory::createDev($channel, $config, $profiler); - return new App($config, $mode, $logger, $profiler, $isBackend); + return new App($config, $mode, $router, $logger, $profiler, $isBackend); } } diff --git a/tests/include/ApiTest.php b/tests/include/ApiTest.php index 80a25c20c1..ecfe3e9621 100644 --- a/tests/include/ApiTest.php +++ b/tests/include/ApiTest.php @@ -50,6 +50,7 @@ class ApiTest extends DatabaseTest { $basePath = BasePath::create(dirname(__DIR__) . '/../'); $mode = new App\Mode($basePath); + $router = new App\Router(); $configLoader = new ConfigFileLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); @@ -57,7 +58,7 @@ class ApiTest extends DatabaseTest $config = Factory\ConfigFactory::createConfig($configCache); Factory\ConfigFactory::createPConfig($configCache); $logger = Factory\LoggerFactory::create('test', $config, $profiler); - $this->app = new App($config, $mode, $logger, $profiler, false); + $this->app = new App($config, $mode, $router, $logger, $profiler, false); parent::setUp(); diff --git a/tests/src/Database/DBATest.php b/tests/src/Database/DBATest.php index c941377219..889ae6af0d 100644 --- a/tests/src/Database/DBATest.php +++ b/tests/src/Database/DBATest.php @@ -15,6 +15,7 @@ class DBATest extends DatabaseTest { $basePath = BasePath::create(dirname(__DIR__) . '/../../'); $mode = new App\Mode($basePath); + $router = new App\Router(); $configLoader = new ConfigFileLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); @@ -22,7 +23,7 @@ class DBATest extends DatabaseTest $config = Factory\ConfigFactory::createConfig($configCache); Factory\ConfigFactory::createPConfig($configCache); $logger = Factory\LoggerFactory::create('test', $config, $profiler); - $this->app = new App($config, $mode, $logger, $profiler, false); + $this->app = new App($config, $mode, $router, $logger, $profiler, false); parent::setUp(); diff --git a/tests/src/Database/DBStructureTest.php b/tests/src/Database/DBStructureTest.php index 152014c114..ec1531783e 100644 --- a/tests/src/Database/DBStructureTest.php +++ b/tests/src/Database/DBStructureTest.php @@ -15,6 +15,7 @@ class DBStructureTest extends DatabaseTest { $basePath = BasePath::create(dirname(__DIR__) . '/../../'); $mode = new App\Mode($basePath); + $router = new App\Router(); $configLoader = new ConfigFileLoader($basePath, $mode); $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); @@ -22,7 +23,7 @@ class DBStructureTest extends DatabaseTest $config = Factory\ConfigFactory::createConfig($configCache); Factory\ConfigFactory::createPConfig($configCache); $logger = Factory\LoggerFactory::create('test', $config, $profiler); - $this->app = new App($config, $mode, $logger, $profiler, false); + $this->app = new App($config, $mode, $router, $logger, $profiler, false); parent::setUp(); }