From b5d2d32b443f7aa8ec6e0e91f36da3cc0a0f2a82 Mon Sep 17 00:00:00 2001
From: Philipp
Date: Fri, 19 Nov 2021 22:47:49 +0100
Subject: [PATCH] Split and delete `ModuleController` - $moduleName is part of
the argument string => App\Arguments - $isBackend boolean already part of
App\Mode::isBackend() - $module is now the direct return of
App\Router::getModule() - ModuleController::run() moved to BaseModule::run()
---
index.php | 2 -
src/App.php | 15 +-
src/App/Arguments.php | 31 ++-
src/App/Mode.php | 39 ++-
src/App/ModuleController.php | 321 -------------------------
src/App/Page.php | 28 +--
src/App/Router.php | 84 ++++++-
src/BaseModule.php | 93 +++++++
src/Content/Nav.php | 4 +-
src/Core/ACL.php | 4 +-
src/DI.php | 8 -
src/Security/Authentication.php | 2 +-
static/dependencies.config.php | 7 +-
tests/src/App/ModeTest.php | 30 +--
tests/src/App/ModuleControllerTest.php | 219 -----------------
tests/src/App/RouterTest.php | 203 +++-------------
view/theme/frio/php/default.php | 2 +-
17 files changed, 299 insertions(+), 793 deletions(-)
delete mode 100644 src/App/ModuleController.php
delete mode 100644 tests/src/App/ModuleControllerTest.php
diff --git a/index.php b/index.php
index 0afd2c7d3e..95a1306b39 100644
--- a/index.php
+++ b/index.php
@@ -41,11 +41,9 @@ $a = \Friendica\DI::app();
\Friendica\DI::mode()->setExecutor(\Friendica\App\Mode::INDEX);
$a->runFrontend(
- $dice->create(\Friendica\App\ModuleController::class),
$dice->create(\Friendica\App\Router::class),
$dice->create(\Friendica\Core\PConfig\Capability\IManagePersonalConfigValues::class),
$dice->create(\Friendica\Security\Authentication::class),
$dice->create(\Friendica\App\Page::class),
- $dice,
$start_time
);
diff --git a/src/App.php b/src/App.php
index f4534c0158..9083e98ad6 100644
--- a/src/App.php
+++ b/src/App.php
@@ -21,11 +21,9 @@
namespace Friendica;
-use Dice\Dice;
use Exception;
use Friendica\App\Arguments;
use Friendica\App\BaseURL;
-use Friendica\App\ModuleController;
use Friendica\Core\Config\Factory\Config;
use Friendica\Module\Maintenance;
use Friendica\Security\Authentication;
@@ -567,7 +565,6 @@ class App
*
* This probably should change to limit the size of this monster method.
*
- * @param App\ModuleController $module The determined module
* @param App\Router $router
* @param IManagePersonalConfigValues $pconfig
* @param Authentication $auth The Authentication backend of the node
@@ -576,12 +573,12 @@ class App
* @throws HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- public function runFrontend(App\ModuleController $module, App\Router $router, IManagePersonalConfigValues $pconfig, Authentication $auth, App\Page $page, Dice $dice, float $start_time)
+ public function runFrontend(App\Router $router, IManagePersonalConfigValues $pconfig, Authentication $auth, App\Page $page, float $start_time)
{
$this->profiler->set($start_time, 'start');
$this->profiler->set(microtime(true), 'classinit');
- $moduleName = $module->getName();
+ $moduleName = $this->args->getModuleName();
try {
// Missing DB connection: ERROR
@@ -703,20 +700,20 @@ class App
$page['page_title'] = $moduleName;
if (!$this->mode->isInstall() && !$this->mode->has(App\Mode::MAINTENANCEDISABLED)) {
- $module = new ModuleController('maintenance', new Maintenance($this->l10n));
+ $module = new Maintenance($this->l10n);
} else {
// determine the module class and save it to the module instance
// @todo there's an implicit dependency due SESSION::start(), so it has to be called here (yet)
- $module = $module->determineClass($this->args, $router, $this->config, $dice);
+ $module = $router->getModule();
}
// Let the module run it's internal process (init, get, post, ...)
- $module->run($this->l10n, $this->baseURL, $this->logger, $this->profiler, $_SERVER, $_POST);
+ $module->run($this->baseURL, $this->args, $this->logger, $this->profiler, $_SERVER, $_POST);
} catch (HTTPException $e) {
(new ModuleHTTPException())->rawContent($e);
}
- $page->run($this, $this->baseURL, $this->mode, $module, $this->l10n, $this->profiler, $this->config, $pconfig);
+ $page->run($this, $this->baseURL, $this->args, $this->mode, $module, $this->l10n, $this->profiler, $this->config, $pconfig);
}
/**
diff --git a/src/App/Arguments.php b/src/App/Arguments.php
index ae6c64a4f3..19f8e92123 100644
--- a/src/App/Arguments.php
+++ b/src/App/Arguments.php
@@ -30,6 +30,8 @@ namespace Friendica\App;
*/
class Arguments
{
+ const DEFAULT_MODULE = 'home';
+
/**
* @var string The complete query string
*/
@@ -38,6 +40,10 @@ class Arguments
* @var string The current Friendica command
*/
private $command;
+ /**
+ * @var string The name of the current module
+ */
+ private $moduleName;
/**
* @var array The arguments of the current execution
*/
@@ -47,10 +53,11 @@ class Arguments
*/
private $argc;
- public function __construct(string $queryString = '', string $command = '', array $argv = [], int $argc = 0)
+ public function __construct(string $queryString = '', string $command = '', string $moduleName = '', array $argv = [], int $argc = 0)
{
$this->queryString = $queryString;
$this->command = $command;
+ $this->moduleName = $moduleName;
$this->argv = $argv;
$this->argc = $argc;
}
@@ -71,6 +78,14 @@ class Arguments
return $this->command;
}
+ /**
+ * @return string The module name based on the arguments
+ */
+ public function getModuleName(): string
+ {
+ return $this->moduleName;
+ }
+
/**
* @return array All arguments of this call
*/
@@ -172,6 +187,18 @@ class Arguments
$queryString = $command . ($queryParameters ? '?' . http_build_query($queryParameters) : '');
- return new Arguments($queryString, $command, $argv, $argc);
+ if ($argc > 0) {
+ $module = str_replace('.', '_', $argv[0]);
+ $module = str_replace('-', '_', $module);
+ } else {
+ $module = self::DEFAULT_MODULE;
+ }
+
+ // Compatibility with the Firefox App
+ if (($module == "users") && ($command == "users/sign_in")) {
+ $module = "login";
+ }
+
+ return new Arguments($queryString, $command, $module, $argv, $argc);
}
}
diff --git a/src/App/Mode.php b/src/App/Mode.php
index 4a1213ae12..5d26a2d45e 100644
--- a/src/App/Mode.php
+++ b/src/App/Mode.php
@@ -25,6 +25,7 @@ use Detection\MobileDetect;
use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Database\Database;
use Friendica\Util\BasePath;
+use phpDocumentor\Reflection\Types\Static_;
/**
* Mode of the current Friendica Node
@@ -46,6 +47,38 @@ class Mode
const BACKEND_CONTENT_TYPES = ['application/jrd+json', 'text/xml',
'application/rss+xml', 'application/atom+xml', 'application/activity+json'];
+ /**
+ * A list of modules, which are backend methods
+ *
+ * @var array
+ */
+ const BACKEND_MODULES = [
+ '_well_known',
+ 'api',
+ 'dfrn_notify',
+ 'feed',
+ 'fetch',
+ 'followers',
+ 'following',
+ 'hcard',
+ 'hostxrd',
+ 'inbox',
+ 'manifest',
+ 'nodeinfo',
+ 'noscrape',
+ 'objects',
+ 'outbox',
+ 'poco',
+ 'post',
+ 'pubsub',
+ 'pubsubhubbub',
+ 'receive',
+ 'rsd_xml',
+ 'salmon',
+ 'statistics_json',
+ 'xrd',
+ ];
+
/***
* @var int The mode of this Application
*
@@ -140,13 +173,13 @@ class Mode
* Checks if the site is called via a backend process
*
* @param bool $isBackend True, if the call is from a backend script (daemon, worker, ...)
- * @param ModuleController $module The pre-loaded module (just name, not class!)
* @param array $server The $_SERVER variable
+ * @param Arguments $args The Friendica App arguments
* @param MobileDetect $mobileDetect The mobile detection library
*
* @return Mode returns the determined mode
*/
- public function determineRunMode(bool $isBackend, ModuleController $module, array $server, MobileDetect $mobileDetect)
+ public function determineRunMode(bool $isBackend, array $server, Arguments $args, MobileDetect $mobileDetect)
{
foreach (self::BACKEND_CONTENT_TYPES as $type) {
if (strpos(strtolower($server['HTTP_ACCEPT'] ?? ''), $type) !== false) {
@@ -154,7 +187,7 @@ class Mode
}
}
- $isBackend = $isBackend || $module->isBackend();
+ $isBackend = $isBackend || in_array($args->getModuleName(), static::BACKEND_MODULES);
$isMobile = $mobileDetect->isMobile();
$isTablet = $mobileDetect->isTablet();
$isAjax = strtolower($server['HTTP_X_REQUESTED_WITH'] ?? '') == 'xmlhttprequest';
diff --git a/src/App/ModuleController.php b/src/App/ModuleController.php
deleted file mode 100644
index ae27236398..0000000000
--- a/src/App/ModuleController.php
+++ /dev/null
@@ -1,321 +0,0 @@
-.
- *
- */
-
-namespace Friendica\App;
-
-use Dice\Dice;
-use Friendica\App;
-use Friendica\Capabilities\ICanHandleRequests;
-use Friendica\Core;
-use Friendica\Core\Config\Capability\IManageConfigValues;
-use Friendica\LegacyModule;
-use Friendica\Module\Home;
-use Friendica\Module\HTTPException\MethodNotAllowed;
-use Friendica\Module\HTTPException\PageNotFound;
-use Friendica\Network\HTTPException\MethodNotAllowedException;
-use Friendica\Network\HTTPException\NoContentException;
-use Friendica\Network\HTTPException\NotFoundException;
-use Friendica\Util\Profiler;
-use Psr\Log\LoggerInterface;
-
-/**
- * Holds the common context of the current, loaded module
- */
-class ModuleController
-{
- const DEFAULT = 'home';
- const DEFAULT_CLASS = Home::class;
- /**
- * A list of modules, which are backend methods
- *
- * @var array
- */
- const BACKEND_MODULES = [
- '_well_known',
- 'api',
- 'dfrn_notify',
- 'feed',
- 'fetch',
- 'followers',
- 'following',
- 'hcard',
- 'hostxrd',
- 'inbox',
- 'manifest',
- 'nodeinfo',
- 'noscrape',
- 'objects',
- 'outbox',
- 'poco',
- 'post',
- 'pubsub',
- 'pubsubhubbub',
- 'receive',
- 'rsd_xml',
- 'salmon',
- 'statistics_json',
- 'xrd',
- ];
-
- /**
- * @var string The module name
- */
- private $moduleName;
-
- /**
- * @var ?ICanHandleRequests The module object
- */
- private $module;
-
- /**
- * @var bool true, if the module is a backend module
- */
- private $isBackend;
-
- /**
- * @var bool true, if the loaded addon is private, so we have to print out not allowed
- */
- private $printNotAllowedAddon;
-
- /**
- * @return string
- */
- public function getName()
- {
- return $this->moduleName;
- }
-
- /**
- * @return ?ICanHandleRequests The base module object
- */
- public function getModule(): ?ICanHandleRequests
- {
- return $this->module;
- }
-
- /**
- * @return bool True, if the current module is a backend module
- * @see ModuleController::BACKEND_MODULES for a list
- */
- public function isBackend()
- {
- return $this->isBackend;
- }
-
- public function __construct(string $moduleName = self::DEFAULT, ?ICanHandleRequests $module = null, bool $isBackend = false, bool $printNotAllowedAddon = false)
- {
- $this->moduleName = $moduleName;
- $this->module = $module;
- $this->isBackend = $isBackend;
- $this->printNotAllowedAddon = $printNotAllowedAddon;
- }
-
- /**
- * Determines the current module based on the App arguments and the server variable
- *
- * @param Arguments $args The Friendica arguments
- *
- * @return ModuleController The module with the determined module
- */
- public function determineName(Arguments $args)
- {
- if ($args->getArgc() > 0) {
- $module = str_replace('.', '_', $args->get(0));
- $module = str_replace('-', '_', $module);
- } else {
- $module = self::DEFAULT;
- }
-
- // Compatibility with the Firefox App
- if (($module == "users") && ($args->getCommand() == "users/sign_in")) {
- $module = "login";
- }
-
- $isBackend = in_array($module, ModuleController::BACKEND_MODULES);
-
- return new ModuleController($module, null, $isBackend, $this->printNotAllowedAddon);
- }
-
- /**
- * Determine the class of the current module
- *
- * @param Arguments $args The Friendica execution arguments
- * @param Router $router The Friendica routing instance
- * @param IManageConfigValues $config The Friendica Configuration
- * @param Dice $dice The Dependency Injection container
- *
- * @return ModuleController The determined module of this call
- *
- * @throws \Exception
- */
- public function determineClass(Arguments $args, Router $router, IManageConfigValues $config, Dice $dice)
- {
- $printNotAllowedAddon = false;
-
- $module_class = null;
- $module_parameters = [];
- /**
- * 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.
- **/
- try {
- $module_class = $router->getModuleClass($args->getCommand());
- $module_parameters[] = $router->getModuleParameters();
- } catch (MethodNotAllowedException $e) {
- $module_class = MethodNotAllowed::class;
- } catch (NotFoundException $e) {
- // Then we try addon-provided modules that we wrap in the LegacyModule class
- if (Core\Addon::isEnabled($this->moduleName) && file_exists("addon/{$this->moduleName}/{$this->moduleName}.php")) {
- //Check if module is an app and if public access to apps is allowed or not
- $privateapps = $config->get('config', 'private_addons', false);
- if ((!local_user()) && Core\Hook::isAddonApp($this->moduleName) && $privateapps) {
- $printNotAllowedAddon = true;
- } else {
- include_once "addon/{$this->moduleName}/{$this->moduleName}.php";
- if (function_exists($this->moduleName . '_module')) {
- $module_parameters[] = "addon/{$this->moduleName}/{$this->moduleName}.php";
- $module_class = LegacyModule::class;
- }
- }
- }
-
- /* Finally, we look for a 'standard' program module in the 'mod' directory
- * We emulate a Module class through the LegacyModule class
- */
- if (!$module_class && file_exists("mod/{$this->moduleName}.php")) {
- $module_parameters[] = "mod/{$this->moduleName}.php";
- $module_class = LegacyModule::class;
- }
-
- $module_class = $module_class ?: PageNotFound::class;
- }
-
- /** @var ICanHandleRequests $module */
- $module = $dice->create($module_class, $module_parameters);
-
- return new ModuleController($this->moduleName, $module, $this->isBackend, $printNotAllowedAddon);
- }
-
- /**
- * Run the determined module class and calls all hooks applied to
- *
- * @param \Friendica\Core\L10n $l10n The L10n instance
- * @param App\BaseURL $baseUrl The Friendica Base URL
- * @param LoggerInterface $logger The Friendica logger
- * @param array $server The $_SERVER variable
- * @param array $post The $_POST variables
- *
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
- */
- public function run(Core\L10n $l10n, App\BaseURL $baseUrl, LoggerInterface $logger, Profiler $profiler, array $server, array $post)
- {
- if ($this->printNotAllowedAddon) {
- notice($l10n->t("You must be logged in to use addons. "));
- }
-
- /* The URL provided does not resolve to a valid module.
- *
- * On Dreamhost sites, quite often things go wrong for no apparent reason and they send us to '/internal_error.html'.
- * We don't like doing this, but as it occasionally accounts for 10-20% or more of all site traffic -
- * we are going to trap this and redirect back to the requested page. As long as you don't have a critical error on your page
- * this will often succeed and eventually do the right thing.
- *
- * Otherwise we are going to emit a 404 not found.
- */
- if ($this->module === PageNotFound::class) {
- $queryString = $server['QUERY_STRING'];
- // Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit.
- if (!empty($queryString) && preg_match('/{[0-9]}/', $queryString) !== 0) {
- exit();
- }
-
- if (!empty($queryString) && ($queryString === 'q=internal_error.html') && isset($dreamhost_error_hack)) {
- $logger->info('index.php: dreamhost_error_hack invoked.', ['Original URI' => $server['REQUEST_URI']]);
- $baseUrl->redirect($server['REQUEST_URI']);
- }
-
- $logger->debug('index.php: page not found.', ['request_uri' => $server['REQUEST_URI'], 'address' => $server['REMOTE_ADDR'], 'query' => $server['QUERY_STRING']]);
- }
-
- // @see https://github.com/tootsuite/mastodon/blob/c3aef491d66aec743a3a53e934a494f653745b61/config/initializers/cors.rb
- if (substr($_REQUEST['pagename'] ?? '', 0, 12) == '.well-known/') {
- header('Access-Control-Allow-Origin: *');
- header('Access-Control-Allow-Headers: *');
- header('Access-Control-Allow-Methods: ' . Router::GET);
- header('Access-Control-Allow-Credentials: false');
- } elseif (substr($_REQUEST['pagename'] ?? '', 0, 8) == 'profile/') {
- header('Access-Control-Allow-Origin: *');
- header('Access-Control-Allow-Headers: *');
- header('Access-Control-Allow-Methods: ' . Router::GET);
- header('Access-Control-Allow-Credentials: false');
- } elseif (substr($_REQUEST['pagename'] ?? '', 0, 4) == 'api/') {
- header('Access-Control-Allow-Origin: *');
- header('Access-Control-Allow-Headers: *');
- header('Access-Control-Allow-Methods: ' . implode(',', Router::ALLOWED_METHODS));
- header('Access-Control-Allow-Credentials: false');
- header('Access-Control-Expose-Headers: Link');
- } elseif (substr($_REQUEST['pagename'] ?? '', 0, 11) == 'oauth/token') {
- header('Access-Control-Allow-Origin: *');
- header('Access-Control-Allow-Headers: *');
- header('Access-Control-Allow-Methods: ' . Router::POST);
- header('Access-Control-Allow-Credentials: false');
- }
-
- // @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
- // @todo Check allowed methods per requested path
- if ($server['REQUEST_METHOD'] === Router::OPTIONS) {
- header('Allow: ' . implode(',', Router::ALLOWED_METHODS));
- throw new NoContentException();
- }
-
- $placeholder = '';
-
- $profiler->set(microtime(true), 'ready');
- $timestamp = microtime(true);
-
- Core\Hook::callAll($this->moduleName . '_mod_init', $placeholder);
-
- $profiler->set(microtime(true) - $timestamp, 'init');
-
- if ($server['REQUEST_METHOD'] === Router::DELETE) {
- $this->module->delete();
- }
-
- if ($server['REQUEST_METHOD'] === Router::PATCH) {
- $this->module->patch();
- }
-
- if ($server['REQUEST_METHOD'] === Router::POST) {
- Core\Hook::callAll($this->moduleName . '_mod_post', $post);
- $this->module->post();
- }
-
- if ($server['REQUEST_METHOD'] === Router::PUT) {
- $this->module->put();
- }
-
- // "rawContent" is especially meant for technical endpoints.
- // This endpoint doesn't need any theme initialization or other comparable stuff.
- $this->module->rawContent();
- }
-}
diff --git a/src/App/Page.php b/src/App/Page.php
index c1a0e4aa54..0efdc12051 100644
--- a/src/App/Page.php
+++ b/src/App/Page.php
@@ -25,6 +25,7 @@ use ArrayAccess;
use DOMDocument;
use DOMXPath;
use Friendica\App;
+use Friendica\Capabilities\ICanHandleRequests;
use Friendica\Content\Nav;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
@@ -191,14 +192,14 @@ class Page implements ArrayAccess
* - head.tpl template
*
* @param App $app The Friendica App instance
- * @param ModuleController $module The loaded Friendica module
+ * @param Arguments $args The Friendica App Arguments
* @param L10n $l10n The l10n language instance
* @param IManageConfigValues $config The Friendica configuration
* @param IManagePersonalConfigValues $pConfig The Friendica personal configuration (for user)
*
* @throws HTTPException\InternalServerErrorException
*/
- private function initHead(App $app, ModuleController $module, L10n $l10n, IManageConfigValues $config, IManagePersonalConfigValues $pConfig)
+ private function initHead(App $app, Arguments $args, L10n $l10n, IManageConfigValues $config, IManagePersonalConfigValues $pConfig)
{
$interval = ((local_user()) ? $pConfig->get(local_user(), 'system', 'update_interval') : 40000);
@@ -212,8 +213,8 @@ class Page implements ArrayAccess
}
// Default title: current module called
- if (empty($this->page['title']) && $module->getName()) {
- $this->page['title'] = ucfirst($module->getName());
+ if (empty($this->page['title']) && $args->getModuleName()) {
+ $this->page['title'] = ucfirst($args->getModuleName());
}
// Prepend the sitename to the page title
@@ -337,22 +338,20 @@ class Page implements ArrayAccess
* - module content
* - hooks for content
*
- * @param ModuleController $module The module
+ * @param ICanHandleRequests $module The module
* @param Mode $mode The Friendica execution mode
*
* @throws HTTPException\InternalServerErrorException
*/
- private function initContent(ModuleController $module, Mode $mode)
+ private function initContent(ICanHandleRequests $module, Mode $mode)
{
$content = '';
try {
- $moduleClass = $module->getModule();
-
$arr = ['content' => $content];
- Hook::callAll($moduleClass->getClassName() . '_mod_content', $arr);
+ Hook::callAll($module->getClassName() . '_mod_content', $arr);
$content = $arr['content'];
- $content .= $module->getModule()->content();
+ $content .= $module->content();
} catch (HTTPException $e) {
$content = (new ModuleHTTPException())->content($e);
}
@@ -389,17 +388,18 @@ class Page implements ArrayAccess
*
* @param App $app The Friendica App
* @param BaseURL $baseURL The Friendica Base URL
+ * @param Arguments $args The Friendica App arguments
* @param Mode $mode The current node mode
- * @param ModuleController $module The loaded Friendica module
+ * @param ICanHandleRequests $module The loaded Friendica module
* @param L10n $l10n The l10n language class
* @param IManageConfigValues $config The Configuration of this node
* @param IManagePersonalConfigValues $pconfig The personal/user configuration
*
* @throws HTTPException\InternalServerErrorException
*/
- public function run(App $app, BaseURL $baseURL, Mode $mode, ModuleController $module, L10n $l10n, Profiler $profiler, IManageConfigValues $config, IManagePersonalConfigValues $pconfig)
+ public function run(App $app, BaseURL $baseURL, Arguments $args, Mode $mode, ICanHandleRequests $module, L10n $l10n, Profiler $profiler, IManageConfigValues $config, IManagePersonalConfigValues $pconfig)
{
- $moduleName = $module->getName();
+ $moduleName = $args->getModuleName();
/* Create the page content.
* Calls all hooks which are including content operations
@@ -429,7 +429,7 @@ class Page implements ArrayAccess
* all the module functions have executed so that all
* theme choices made by the modules can take effect.
*/
- $this->initHead($app, $module, $l10n, $config, $pconfig);
+ $this->initHead($app, $args, $l10n, $config, $pconfig);
/* Build the page ending -- this is stuff that goes right before
* the closing
">
+ ">
t('Skip to main content'); ?>