diff --git a/bin/auth_ejabberd.php b/bin/auth_ejabberd.php index bc976e61a4..595ccf204a 100755 --- a/bin/auth_ejabberd.php +++ b/bin/auth_ejabberd.php @@ -52,4 +52,4 @@ $container = \Friendica\Core\DiceContainer::fromBasePath(dirname(__DIR__)); $app = \Friendica\App::fromContainer($container); -$app->processEjabberd(); +$app->processEjabberd($_SERVER); diff --git a/bin/console.php b/bin/console.php index ad612f4363..a20f3f7e02 100755 --- a/bin/console.php +++ b/bin/console.php @@ -19,4 +19,4 @@ $container = \Friendica\Core\DiceContainer::fromBasePath(dirname(__DIR__)); $app = \Friendica\App::fromContainer($container); -$app->processConsole($_SERVER['argv'] ?? []); +$app->processConsole($_SERVER); diff --git a/bin/daemon.php b/bin/daemon.php index c3d945d941..162a92948f 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -26,11 +26,13 @@ require dirname(__DIR__) . '/vendor/autoload.php'; fwrite(STDOUT, '`bin/daemon.php` is deprecated since 2024.02 and will be removed in 5 months, please use `bin/console.php daemon` instead.' . \PHP_EOL); +// BC: Add console command as second argument $argv = $_SERVER['argv'] ?? []; array_splice($argv, 1, 0, "daemon"); +$_SERVER['argv'] = $argv; $container = \Friendica\Core\DiceContainer::fromBasePath(dirname(__DIR__)); $app = \Friendica\App::fromContainer($container); -$app->processConsole($argv); +$app->processConsole($_SERVER); diff --git a/bin/jetstream.php b/bin/jetstream.php index 2aa1886c6e..c97557dbb0 100755 --- a/bin/jetstream.php +++ b/bin/jetstream.php @@ -21,11 +21,13 @@ require dirname(__DIR__) . '/vendor/autoload.php'; fwrite(STDOUT, '`bin/jetstream.php` is deprecated since 2024.02 and will be removed in 5 months, please use `bin/console.php jetstream` instead.' . \PHP_EOL); +// BC: Add console command as second argument $argv = $_SERVER['argv'] ?? []; array_splice($argv, 1, 0, "jetstream"); +$_SERVER['argv'] = $argv; $container = \Friendica\Core\DiceContainer::fromBasePath(dirname(__DIR__)); $app = \Friendica\App::fromContainer($container); -$app->processConsole($argv); +$app->processConsole($_SERVER); diff --git a/bin/worker.php b/bin/worker.php index f099bc8e66..f319bd766f 100755 --- a/bin/worker.php +++ b/bin/worker.php @@ -23,11 +23,13 @@ require dirname(__DIR__) . '/vendor/autoload.php'; fwrite(STDOUT, '`bin/worker.php` is deprecated since 2024.02 and will be removed in 5 months, please use `bin/console.php worker` instead.' . \PHP_EOL); +// BC: Add console command as second argument $argv = $_SERVER['argv'] ?? []; array_splice($argv, 1, 0, "worker"); +$_SERVER['argv'] = $argv; $container = \Friendica\Core\DiceContainer::fromBasePath(dirname(__DIR__)); $app = \Friendica\App::fromContainer($container); -$app->processConsole($argv); +$app->processConsole($_SERVER); diff --git a/doc/Developers-Intro.md b/doc/Developers-Intro.md index c500b27741..99e84739d0 100644 --- a/doc/Developers-Intro.md +++ b/doc/Developers-Intro.md @@ -156,3 +156,97 @@ If you are interested in improving those clients, please contact the developers * iOS: *currently no client* * SailfishOS: **Friendiy** [src](https://kirgroup.com/projects/fabrixxm/harbour-friendly) - developed by [Fabio](https://kirgroup.com/profile/fabrixxm/profile) * Windows: **Friendica Mobile** for Windows versions [before 8.1](http://windowsphone.com/s?appid=e3257730-c9cf-4935-9620-5261e3505c67) and [Windows 10](https://www.microsoft.com/store/apps/9nblggh0fhmn) - developed by [Gerhard Seeber](http://mozartweg.dyndns.org/friendica/profile/gerhard/profile) + +## Backward compatibility + +### Backward Compatibility Promise + +Friendica can be extended by addons. +These addons relies on many classes and conventions from Friendica. +As developers our work on Friendica should not break things in the addons without giving the addon maintainers a chance to fix their addons. +Our goal is to build trust for the addon maintainers but also allow Friendica developers to move on. +This is called the Backward Compatibility Promise. + +Inspired by the [Symonfy BC promise](https://symfony.com/doc/current/contributing/code/bc.html) we promise BC for every class, interface, trait, enum, function, constant, etc., but with the exception of: + +- Classes, interfaces, traits, enums, functions, methods, properties and constants marked as `@internal` or `@private` +- Extending or modifying a `final` class or method in any way +- Calling `private` methods (via Reflection) +- Accessing `private` properties (via Reflection) +- Accessing `private` methods (via Reflection) +- Accessing `private` constants (via Reflection) +- New properties on overridden `protected` methods +- Possible name collisions with new methods in an extended class (addon developers should prefix their custom methods in the extending classes in an appropriate way) +- Dropping support for every PHP version that has reached end of life + +### Deprecation and removing features + +As the development goes by Friendica needs to get rid of old code and concepts. +This will be done in 3 steps to give addon maintainers a chance to adjust their addons. + +**1. Label deprecation** + +If we as the Friendica maintainers decide to remove some functions, classes, interface, etc. we start this by adding a `@deprecated` PHPDoc note on the code. +For instance the class `Friendica\Core\Logger` should be removed, so we add the following note with a possible replacement: + +```php +/** + * Logger functions + * + * @deprecated 2025.02 Use constructor injection or `DI::logger()` instead + */ +class Logger {/* ... */} +``` + +This way addon developers might be notified early by their IDE or other tools that the usage of the class is deprecated. +In Friendica we can now start to replace all occurrences and usage of this class with the alternative. + +The deprecation label COULD be remain over multiple releases. +As long as the code that is labeled with `@deprecated` is used inside Friendica or the official addon repository, it SHOULD NOT be hard deprecated. + +**2. Hard deprecation** + +If the deprecated code is no longer used inside Friendica or the official addons it MUST be hard deprecated. +The code MUST NOT be deleted. +Starting from the next release, it MUST be stay for at least 5 months. +Hard deprecated code COULD remain longer than 5 months, depending on when a release appears. +Addon developer MUST NOT consider that they have more than 5 months to adjust their code. + +Hard deprecation code means that the code triggers an `E_USER_DEPRECATION` error if it is called. +For instance with the deprecated class `Friendica\Core\Logger` the call of every method should trigger an error: + +```php +/** + * Logger functions + * + * @deprecated 2025.02 Use constructor injection or `DI::logger()` instead + */ +class Logger { + public static function info(string $message, array $context = []) + { + trigger_error('Class `' . __CLASS__ . '` is deprecated since 2025.05 and will be removed after 5 months, use constructor injection or `DI::logger()` instead.', E_USER_DEPRECATED); + + self::getInstance()->info($message, $context); + } + + /* ... */ +} +``` + +This way the maintainer or users of addons will be notified that the addon will stop working in one of the next releases. +The addon maintainer now has at least 5 months or at least one release to fix the deprecations. + +Please note that the deprecation message contains the release that will be released next. +In the example the code was hard deprecated with release `2025.05`, so it COULD be removed earliest with the `2025.11` release. + +**3. Code Removing** + +We promise BC for deprecated code for at least 5 months, starting from the release the deprecation was announced. +After this time the deprecated code COULD be remove within the next release. + +Breaking changes MUST be happen only in a new release but MUST be hard deprecated first. +The BC promise refers only to releases, respective the `stable` branch. +Deprecated code on other branches like `develop` or RC branches could be removed earlier. +This is not a BC break as long as the release will be published 5 months after the hard deprecation. + +If a release breaks BC without deprecation or earlier than 5 months, this SHOULD considered as a bug and BC SHOULD be restored in a bugfix release. diff --git a/src/App.php b/src/App.php index b756a847d9..5623f9a413 100644 --- a/src/App.php +++ b/src/App.php @@ -165,16 +165,18 @@ class App $this->session = $this->container->create(IHandleUserSessions::class); $this->appHelper = $this->container->create(AppHelper::class); - $this->loadSetupForFrontend( - $request, + $this->load( + $request->getServerParams(), $this->container->create(DbaDefinition::class), $this->container->create(ViewDefinition::class), + $this->mode, + $this->config, + $this->profiler, + $this->appHelper, ); $this->registerTemplateEngine(); - $this->mode->setExecutor(Mode::INDEX); - $this->runFrontend( $this->container->create(IManagePersonalConfigValues::class), $this->container->create(Page::class), @@ -188,8 +190,10 @@ class App /** * @internal */ - public function processConsole(array $argv): void + public function processConsole(array $serverParams): void { + $argv = $serverParams['argv'] ?? []; + $this->setupContainerForAddons(); $this->setupLogChannel($this->determineLogChannel($argv)); @@ -198,6 +202,16 @@ class App $this->registerErrorHandler(); + $this->load( + $serverParams, + $this->container->create(DbaDefinition::class), + $this->container->create(ViewDefinition::class), + $this->container->create(Mode::class), + $this->container->create(IManageConfigValues::class), + $this->container->create(Profiler::class), + $this->container->create(AppHelper::class), + ); + $this->registerTemplateEngine(); (\Friendica\Core\Console::create($this->container, $argv))->execute(); @@ -206,7 +220,7 @@ class App /** * @internal */ - public function processEjabberd(): void + public function processEjabberd(array $serverParams): void { $this->setupContainerForAddons(); @@ -216,6 +230,16 @@ class App $this->registerErrorHandler(); + $this->load( + $serverParams, + $this->container->create(DbaDefinition::class), + $this->container->create(ViewDefinition::class), + $this->container->create(Mode::class), + $this->container->create(IManageConfigValues::class), + $this->container->create(Profiler::class), + $this->container->create(AppHelper::class), + ); + /** @var BasePath */ $basePath = $this->container->create(BasePath::class); @@ -285,14 +309,21 @@ class App /** * Load the whole app instance */ - private function loadSetupForFrontend(ServerRequestInterface $request, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition) - { - if ($this->config->get('system', 'ini_max_execution_time') !== false) { - set_time_limit((int)$this->config->get('system', 'ini_max_execution_time')); + private function load( + array $serverParams, + DbaDefinition $dbaDefinition, + ViewDefinition $viewDefinition, + Mode $mode, + IManageConfigValues $config, + Profiler $profiler, + AppHelper $appHelper + ): void { + if ($config->get('system', 'ini_max_execution_time') !== false) { + set_time_limit((int) $config->get('system', 'ini_max_execution_time')); } - if ($this->config->get('system', 'ini_pcre_backtrack_limit') !== false) { - ini_set('pcre.backtrack_limit', (int)$this->config->get('system', 'ini_pcre_backtrack_limit')); + if ($config->get('system', 'ini_pcre_backtrack_limit') !== false) { + ini_set('pcre.backtrack_limit', (int) $config->get('system', 'ini_pcre_backtrack_limit')); } // Normally this constant is defined - but not if "pcntl" isn't installed @@ -303,11 +334,11 @@ class App // Ensure that all "strtotime" operations do run timezone independent date_default_timezone_set('UTC'); - $this->profiler->reset(); + $profiler->reset(); - if ($this->mode->has(Mode::DBAVAILABLE)) { + if ($mode->has(Mode::DBAVAILABLE)) { Core\Hook::loadHooks(); - $loader = (new Config())->createConfigFileManager($this->appHelper->getBasePath(), $request->getServerParams()); + $loader = (new Config())->createConfigFileManager($appHelper->getBasePath(), $serverParams); Core\Hook::callAll('load_config', $loader); // Hooks are now working, reload the whole definitions with hook enabled @@ -315,7 +346,7 @@ class App $viewDefinition->load(true); } - $this->loadDefaultTimezone(); + $this->loadDefaultTimezone($config, $appHelper); } /** @@ -325,16 +356,16 @@ class App * * @global string $default_timezone */ - private function loadDefaultTimezone() + private function loadDefaultTimezone(IManageConfigValues $config, AppHelper $appHelper) { - if ($this->config->get('system', 'default_timezone')) { - $timezone = $this->config->get('system', 'default_timezone', 'UTC'); + if ($config->get('system', 'default_timezone')) { + $timezone = $config->get('system', 'default_timezone', 'UTC'); } else { global $default_timezone; $timezone = $default_timezone ?? '' ?: 'UTC'; } - $this->appHelper->setTimeZone($timezone); + $appHelper->setTimeZone($timezone); } /** @@ -361,6 +392,8 @@ class App float $start_time, ServerRequestInterface $request ) { + $this->mode->setExecutor(Mode::INDEX); + $httpInput = new HTTPInputData($request->getServerParams()); $serverVars = $request->getServerParams(); $queryVars = $request->getQueryParams();