Create interface for static Module calls
authorPhilipp <admin@philipp.info>
Sun, 14 Nov 2021 19:28:36 +0000 (20:28 +0100)
committerPhilipp <admin@philipp.info>
Sun, 14 Nov 2021 22:30:04 +0000 (23:30 +0100)
index.php
src/App.php
src/App/Module.php
src/App/Page.php
src/BaseModule.php
src/Capabilities/ICanHandleRequests.php [new file with mode: 0644]
tests/src/App/ModeTest.php
tests/src/App/ModuleTest.php

index 4f690ed..cfb5937 100644 (file)
--- a/index.php
+++ b/index.php
@@ -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
 );
index 75f5314..63d28fc 100644 (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, ...)
index 5b7c3d1..98d2d62 100644 (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());
        }
 }
index fcb2d18..c20f26c 100644 (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);
index ced1e55..90ba9e1 100644 (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.
diff --git a/src/Capabilities/ICanHandleRequests.php b/src/Capabilities/ICanHandleRequests.php
new file mode 100644 (file)
index 0000000..0e1582b
--- /dev/null
@@ -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;
+}
index 9f38a25..2437192 100644 (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);
index c5d4e11..7f5d65f 100644 (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());
+
+               $module = (new App\Module($name))->determineClass(new App\Arguments('', $command), $router, $config, $dice);
 
-               self::assertEquals($assert, $module->getClassName());
+               self::assertEquals($assert, $module->getClass()::getClassName());
        }
 
        /**