Create interface for static Module calls

This commit is contained in:
Philipp 2021-11-14 20:28:36 +01:00
parent 96996790f1
commit 018275919c
No known key found for this signature in database
GPG key ID: 24A7501396EB5432
8 changed files with 170 additions and 93 deletions

View file

@ -46,5 +46,6 @@ $a->runFrontend(
$dice->create(\Friendica\Core\PConfig\Capability\IManagePersonalConfigValues::class),
$dice->create(\Friendica\Security\Authentication::class),
$dice->create(\Friendica\App\Page::class),
$dice,
$start_time
);

View file

@ -21,6 +21,7 @@
namespace Friendica;
use Dice\Dice;
use Exception;
use Friendica\App\Arguments;
use Friendica\App\BaseURL;
@ -575,7 +576,7 @@ class App
* @throws HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public function runFrontend(App\Module $module, App\Router $router, IManagePersonalConfigValues $pconfig, Authentication $auth, App\Page $page, float $start_time)
public function runFrontend(App\Module $module, App\Router $router, IManagePersonalConfigValues $pconfig, Authentication $auth, App\Page $page, Dice $dice, float $start_time)
{
$this->profiler->set($start_time, 'start');
$this->profiler->set(microtime(true), 'classinit');
@ -702,11 +703,11 @@ class App
$page['page_title'] = $moduleName;
if (!$this->mode->isInstall() && !$this->mode->has(App\Mode::MAINTENANCEDISABLED)) {
$module = new Module('maintenance', Maintenance::class);
$module = new Module('maintenance', new Maintenance());
} 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);
$module = $module->determineClass($this->args, $router, $this->config, $dice);
}
// Let the module run it's internal process (init, get, post, ...)

View file

@ -21,8 +21,9 @@
namespace Friendica\App;
use Dice\Dice;
use Friendica\App;
use Friendica\BaseModule;
use Friendica\Capabilities\ICanHandleRequests;
use Friendica\Core;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\LegacyModule;
@ -80,15 +81,10 @@ class Module
private $module;
/**
* @var BaseModule The module class
* @var ICanHandleRequests The module class
*/
private $module_class;
/**
* @var array The module parameters
*/
private $module_parameters;
/**
* @var bool true, if the module is a backend module
*/
@ -108,21 +104,13 @@ class Module
}
/**
* @return string The base class name
* @return ICanHandleRequests The base class name
*/
public function getClassName()
public function getClass(): ICanHandleRequests
{
return $this->module_class;
}
/**
* @return array The module parameters extracted from the route
*/
public function getParameters()
{
return $this->module_parameters;
}
/**
* @return bool True, if the current module is a backend module
* @see Module::BACKEND_MODULES for a list
@ -132,11 +120,12 @@ class Module
return $this->isBackend;
}
public function __construct(string $module = self::DEFAULT, string $moduleClass = self::DEFAULT_CLASS, array $moduleParameters = [], bool $isBackend = false, bool $printNotAllowedAddon = false)
public function __construct(string $module = self::DEFAULT, ICanHandleRequests $module_class = null, bool $isBackend = false, bool $printNotAllowedAddon = false)
{
$defaultClass = static::DEFAULT_CLASS;
$this->module = $module;
$this->module_class = $moduleClass;
$this->module_parameters = $moduleParameters;
$this->module_class = $module_class ?? new $defaultClass();
$this->isBackend = $isBackend;
$this->printNotAllowedAddon = $printNotAllowedAddon;
}
@ -164,21 +153,22 @@ class Module
$isBackend = in_array($module, Module::BACKEND_MODULES);;
return new Module($module, $this->module_class, [], $isBackend, $this->printNotAllowedAddon);
return new Module($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 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 Module The determined module of this call
*
* @throws \Exception
*/
public function determineClass(Arguments $args, Router $router, IManageConfigValues $config)
public function determineClass(Arguments $args, Router $router, IManageConfigValues $config, Dice $dice)
{
$printNotAllowedAddon = false;
@ -222,7 +212,10 @@ class Module
$module_class = $module_class ?: PageNotFound::class;
}
return new Module($this->module, $module_class, $module_parameters, $this->isBackend, $printNotAllowedAddon);
/** @var ICanHandleRequests $module */
$module = $dice->create($module_class, [$module_parameters]);
return new Module($this->module, $module, $this->isBackend, $printNotAllowedAddon);
}
/**
@ -304,32 +297,32 @@ class Module
Core\Hook::callAll($this->module . '_mod_init', $placeholder);
call_user_func([$this->module_class, 'init'], $this->module_parameters);
$this->module_class::init($this->module_class::getParameters());
$profiler->set(microtime(true) - $timestamp, 'init');
if ($server['REQUEST_METHOD'] === Router::DELETE) {
call_user_func([$this->module_class, 'delete'], $this->module_parameters);
$this->module_class::delete($this->module_class::getParameters());
}
if ($server['REQUEST_METHOD'] === Router::PATCH) {
call_user_func([$this->module_class, 'patch'], $this->module_parameters);
$this->module_class::patch($this->module_class::getParameters());
}
if ($server['REQUEST_METHOD'] === Router::POST) {
Core\Hook::callAll($this->module . '_mod_post', $post);
call_user_func([$this->module_class, 'post'], $this->module_parameters);
$this->module_class::post($this->module_class::getParameters());
}
if ($server['REQUEST_METHOD'] === Router::PUT) {
call_user_func([$this->module_class, 'put'], $this->module_parameters);
$this->module_class::put($this->module_class::getParameters());
}
Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder);
call_user_func([$this->module_class, 'afterpost'], $this->module_parameters);
$this->module_class::afterpost($this->module_class::getParameters());
// "rawContent" is especially meant for technical endpoints.
// This endpoint doesn't need any theme initialization or other comparable stuff.
call_user_func([$this->module_class, 'rawContent'], $this->module_parameters);
$this->module_class::rawContent($this->module_class::getParameters());
}
}

View file

@ -347,13 +347,13 @@ class Page implements ArrayAccess
$content = '';
try {
$moduleClass = $module->getClassName();
$moduleClass = $module->getClass();
$arr = ['content' => $content];
Hook::callAll($moduleClass . '_mod_content', $arr);
Hook::callAll( $moduleClass::getClassName() . '_mod_content', $arr);
$content = $arr['content'];
$arr = ['content' => call_user_func([$moduleClass, 'content'], $module->getParameters())];
Hook::callAll($moduleClass . '_mod_aftercontent', $arr);
$arr = ['content' => $moduleClass::content($moduleClass::getParameters())];
Hook::callAll($moduleClass::getClassName() . '_mod_aftercontent', $arr);
$content .= $arr['content'];
} catch (HTTPException $e) {
$content = ModuleHTTPException::content($e);

View file

@ -21,6 +21,7 @@
namespace Friendica;
use Friendica\Capabilities\ICanHandleRequests;
use Friendica\Core\Logger;
use Friendica\Model\User;
@ -33,23 +34,33 @@ use Friendica\Model\User;
*
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
abstract class BaseModule
abstract class BaseModule implements ICanHandleRequests
{
/** @var array */
protected static $parameters = [];
public function __construct(array $parameters = [])
{
static::$parameters = $parameters;
}
/**
* Initialization method common to both content() and post()
*
* Extend this method if you need to do any shared processing before both
* content() or post()
* @return array
*/
public static function getParameters(): array
{
return self::$parameters;
}
/**
* {@inheritDoc}
*/
public static function init(array $parameters = [])
{
}
/**
* Module GET method to display raw content from technical endpoints
*
* Extend this method if the module is supposed to return communication data,
* e.g. from protocol implementations.
* {@inheritDoc}
*/
public static function rawContent(array $parameters = [])
{
@ -58,46 +69,29 @@ abstract class BaseModule
}
/**
* Module GET method to display any content
*
* Extend this method if the module is supposed to return any display
* through a GET request. It can be an HTML page through templating or a
* XML feed or a JSON output.
*
* @return string
* {@inheritDoc}
*/
public static function content(array $parameters = [])
{
$o = '';
return $o;
return '';
}
/**
* Module DELETE method to process submitted data
*
* Extend this method if the module is supposed to process DELETE requests.
* Doesn't display any content
* {@inheritDoc}
*/
public static function delete(array $parameters = [])
{
}
/**
* Module PATCH method to process submitted data
*
* Extend this method if the module is supposed to process PATCH requests.
* Doesn't display any content
* {@inheritDoc}
*/
public static function patch(array $parameters = [])
{
}
/**
* Module POST method to process submitted data
*
* Extend this method if the module is supposed to process POST requests.
* Doesn't display any content
* {@inheritDoc}
*/
public static function post(array $parameters = [])
{
@ -105,24 +99,25 @@ abstract class BaseModule
}
/**
* Called after post()
*
* Unknown purpose
* {@inheritDoc}
*/
public static function afterpost(array $parameters = [])
{
}
/**
* Module PUT method to process submitted data
*
* Extend this method if the module is supposed to process PUT requests.
* Doesn't display any content
* {@inheritDoc}
*/
public static function put(array $parameters = [])
{
}
/** Gets the name of the current class */
public static function getClassName(): string
{
return static::class;
}
/*
* Functions used to protect against Cross-Site Request Forgery
* The security token has to base on at least one value that an attacker can't know - here it's the session ID and the private key.

View file

@ -0,0 +1,79 @@
<?php
namespace Friendica\Capabilities;
/**
* This interface provides the capability to handle requests from clients and returns the desired outcome
*/
interface ICanHandleRequests
{
/**
* Initialization method common to both content() and post()
*
* Extend this method if you need to do any shared processing before both
* content() or post()
*/
public static function init(array $parameters = []);
/**
* Module GET method to display raw content from technical endpoints
*
* Extend this method if the module is supposed to return communication data,
* e.g. from protocol implementations.
*/
public static function rawContent(array $parameters = []);
/**
* Module GET method to display any content
*
* Extend this method if the module is supposed to return any display
* through a GET request. It can be an HTML page through templating or a
* XML feed or a JSON output.
*
* @return string
*/
public static function content(array $parameters = []);
/**
* Module DELETE method to process submitted data
*
* Extend this method if the module is supposed to process DELETE requests.
* Doesn't display any content
*/
public static function delete(array $parameters = []);
/**
* Module PATCH method to process submitted data
*
* Extend this method if the module is supposed to process PATCH requests.
* Doesn't display any content
*/
public static function patch(array $parameters = []);
/**
* Module POST method to process submitted data
*
* Extend this method if the module is supposed to process POST requests.
* Doesn't display any content
*/
public static function post(array $parameters = []);
/**
* Called after post()
*
* Unknown purpose
*/
public static function afterpost(array $parameters = []);
/**
* Module PUT method to process submitted data
*
* Extend this method if the module is supposed to process PUT requests.
* Doesn't display any content
*/
public static function put(array $parameters = []);
public static function getClassName(): string;
public static function getParameters(): array;
}

View file

@ -218,7 +218,7 @@ class ModeTest extends MockedTest
public function testIsBackendButIndex()
{
$server = [];
$module = new Module(Module::DEFAULT, Module::DEFAULT_CLASS, [], true);
$module = new Module(Module::DEFAULT, null, true);
$mobileDetect = new MobileDetect();
$mode = (new Mode())->determineRunMode(false, $module, $server, $mobileDetect);
@ -232,7 +232,7 @@ class ModeTest extends MockedTest
public function testIsNotBackend()
{
$server = [];
$module = new Module(Module::DEFAULT, Module::DEFAULT_CLASS, [], false);
$module = new Module(Module::DEFAULT, null, false);
$mobileDetect = new MobileDetect();
$mode = (new Mode())->determineRunMode(false, $module, $server, $mobileDetect);
@ -250,7 +250,7 @@ class ModeTest extends MockedTest
'HTTP_X_REQUESTED_WITH' => 'xmlhttprequest',
];
$module = new Module(Module::DEFAULT, Module::DEFAULT_CLASS, [], false);
$module = new Module(Module::DEFAULT, null, false);
$mobileDetect = new MobileDetect();
$mode = (new Mode())->determineRunMode(true, $module, $server, $mobileDetect);
@ -264,7 +264,7 @@ class ModeTest extends MockedTest
public function testIsNotAjax()
{
$server = [];
$module = new Module(Module::DEFAULT, Module::DEFAULT_CLASS, [], false);
$module = new Module(Module::DEFAULT, null, false);
$mobileDetect = new MobileDetect();
$mode = (new Mode())->determineRunMode(true, $module, $server, $mobileDetect);
@ -278,7 +278,7 @@ class ModeTest extends MockedTest
public function testIsMobileIsTablet()
{
$server = [];
$module = new Module(Module::DEFAULT, Module::DEFAULT_CLASS, [], false);
$module = new Module(Module::DEFAULT, null, false);
$mobileDetect = Mockery::mock(MobileDetect::class);
$mobileDetect->shouldReceive('isMobile')->andReturn(true);
$mobileDetect->shouldReceive('isTablet')->andReturn(true);
@ -296,7 +296,7 @@ class ModeTest extends MockedTest
public function testIsNotMobileIsNotTablet()
{
$server = [];
$module = new Module(Module::DEFAULT, Module::DEFAULT_CLASS, [], false);
$module = new Module(Module::DEFAULT, null, false);
$mobileDetect = Mockery::mock(MobileDetect::class);
$mobileDetect->shouldReceive('isMobile')->andReturn(false);
$mobileDetect->shouldReceive('isTablet')->andReturn(false);

View file

@ -21,6 +21,7 @@
namespace Friendica\Test\src\App;
use Dice\Dice;
use Friendica\App;
use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Config\Capability\IManageConfigValues;
@ -38,7 +39,7 @@ class ModuleTest extends DatabaseTest
{
self::assertEquals($assert['isBackend'], $module->isBackend());
self::assertEquals($assert['name'], $module->getName());
self::assertEquals($assert['class'], $module->getClassName());
self::assertEquals($assert['class'], $module->getClass());
}
/**
@ -48,21 +49,25 @@ class ModuleTest extends DatabaseTest
{
$module = new App\Module();
$defaultClass = App\Module::DEFAULT_CLASS;
self::assertModule([
'isBackend' => false,
'name' => App\Module::DEFAULT,
'class' => App\Module::DEFAULT_CLASS,
'class' => new $defaultClass(),
], $module);
}
public function dataModuleName()
{
$defaultClass = App\Module::DEFAULT_CLASS;
return [
'default' => [
'assert' => [
'isBackend' => false,
'name' => 'network',
'class' => App\Module::DEFAULT_CLASS,
'class' => new $defaultClass(),
],
'args' => new App\Arguments('network/data/in',
'network/data/in',
@ -73,7 +78,7 @@ class ModuleTest extends DatabaseTest
'assert' => [
'isBackend' => false,
'name' => 'with_strike_and_point',
'class' => App\Module::DEFAULT_CLASS,
'class' => new $defaultClass(),
],
'args' => new App\Arguments('with-strike.and-point/data/in',
'with-strike.and-point/data/in',
@ -84,7 +89,7 @@ class ModuleTest extends DatabaseTest
'assert' => [
'isBackend' => false,
'name' => App\Module::DEFAULT,
'class' => App\Module::DEFAULT_CLASS,
'class' => new $defaultClass(),
],
'args' => new App\Arguments(),
],
@ -92,7 +97,7 @@ class ModuleTest extends DatabaseTest
'assert' => [
'isBackend' => false,
'name' => App\Module::DEFAULT,
'class' => App\Module::DEFAULT_CLASS,
'class' => new $defaultClass(),
],
'args' => new App\Arguments(),
],
@ -100,7 +105,7 @@ class ModuleTest extends DatabaseTest
'assert' => [
'isBackend' => true,
'name' => App\Module::BACKEND_MODULES[0],
'class' => App\Module::DEFAULT_CLASS,
'class' => new $defaultClass(),
],
'args' => new App\Arguments(App\Module::BACKEND_MODULES[0] . '/data/in',
App\Module::BACKEND_MODULES[0] . '/data/in',
@ -111,7 +116,7 @@ class ModuleTest extends DatabaseTest
'assert' => [
'isBackend' => false,
'name' => 'login',
'class' => App\Module::DEFAULT_CLASS,
'class' => new $defaultClass(),
],
'args' => new App\Arguments('users/sign_in',
'users/sign_in',
@ -187,9 +192,12 @@ class ModuleTest extends DatabaseTest
$router = (new App\Router([], __DIR__ . '/../../../static/routes.config.php', $l10n, $cache, $lock));
$module = (new App\Module($name))->determineClass(new App\Arguments('', $command), $router, $config);
$dice = Mockery::mock(Dice::class);
$dice->shouldReceive('create')->andReturn(new $assert());
self::assertEquals($assert, $module->getClassName());
$module = (new App\Module($name))->determineClass(new App\Arguments('', $command), $router, $config, $dice);
self::assertEquals($assert, $module->getClass()::getClassName());
}
/**