From 14b76e48f085a48ea44812cdc9057c7404f0102a Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 2 Jul 2023 23:56:56 +0200 Subject: [PATCH 01/24] Introduce dynamic hook loading - Dynamically load addon files - Dynamically load hooks - Rewrite Logger-logic to use new hook logic (Monolog is working again) --- bin/auth_ejabberd.php | 6 +- bin/console.php | 6 +- bin/daemon.php | 6 +- bin/worker.php | 6 +- index.php | 3 + .../Addon/Capabilities/ICanLoadAddons.php | 37 ++++++ .../AddonInvalidConfigFileException.php} | 12 +- src/Core/Addon/Model/AddonLoader.php | 70 ++++++++++ src/Core/Hooks/Capabilities/HookType.php | 39 ++++++ .../Capabilities/ICanCreateInstances.php | 59 +++++++++ ...nstances.php => ICanRegisterInstances.php} | 40 ++---- .../Exceptions/HookConfigException.php} | 8 +- src/Core/Hooks/Model/DiceInstanceManager.php | 111 ++++++++++++++++ src/Core/Hooks/Model/InstanceManager.php | 104 --------------- src/Core/Hooks/Util/HookFileManager.php | 118 +++++++++++++++++ .../Capabilities/ICheckLoggerSettings.php | 42 ++++++ .../Capabilities/IHaveCallIntrospections.php | 2 +- src/Core/Logger/Capabilities/LogChannel.php | 43 +++++++ .../Logger/Exception/LogLevelException.php | 5 +- .../Exception/LoggerArgumentException.php | 5 +- src/Core/Logger/Exception/LoggerException.php | 5 +- .../Exception/LoggerUnusableException.php | 35 +++++ .../Factory/AbstractLoggerTypeFactory.php | 80 ++++++++++++ src/Core/Logger/Factory/Logger.php | 120 ++---------------- src/Core/Logger/Factory/ProfilerLogger.php | 53 ++++++++ src/Core/Logger/Factory/StreamLogger.php | 100 +++++++++++++++ src/Core/Logger/Factory/SyslogLogger.php | 60 +++++++++ src/Core/Logger/Type/README.md | 26 ---- src/Core/Logger/Type/StreamLogger.php | 81 ++---------- src/Core/Logger/Type/SyslogLogger.php | 29 ++--- src/{ => Core/Logger}/Util/FileSystem.php | 6 +- src/Core/Logger/Util/LoggerSettingsCheck.php | 91 +++++++++++++ src/DI.php | 23 ++-- src/Module/Admin/Summary.php | 36 +----- static/dependencies.config.php | 82 +++++++----- static/hooks.config.php | 37 ++++++ .../Util/Hooks/InstanceMocks/FakeInstance.php | 4 +- .../Core/Hooks/Model/InstanceManagerTest.php | 38 +++--- tests/src/Core/Logger/StreamLoggerTest.php | 4 +- 39 files changed, 1163 insertions(+), 469 deletions(-) create mode 100644 src/Core/Addon/Capabilities/ICanLoadAddons.php rename src/Core/{Hooks/Capabilities/IAmAStrategy.php => Addon/Exception/AddonInvalidConfigFileException.php} (71%) create mode 100644 src/Core/Addon/Model/AddonLoader.php create mode 100644 src/Core/Hooks/Capabilities/HookType.php create mode 100644 src/Core/Hooks/Capabilities/ICanCreateInstances.php rename src/Core/Hooks/Capabilities/{ICanManageInstances.php => ICanRegisterInstances.php} (55%) rename src/Core/{Logger/Exception/LoggerInvalidException.php => Hooks/Exceptions/HookConfigException.php} (82%) create mode 100644 src/Core/Hooks/Model/DiceInstanceManager.php delete mode 100644 src/Core/Hooks/Model/InstanceManager.php create mode 100644 src/Core/Hooks/Util/HookFileManager.php create mode 100644 src/Core/Logger/Capabilities/ICheckLoggerSettings.php create mode 100644 src/Core/Logger/Capabilities/LogChannel.php create mode 100644 src/Core/Logger/Exception/LoggerUnusableException.php create mode 100644 src/Core/Logger/Factory/AbstractLoggerTypeFactory.php create mode 100644 src/Core/Logger/Factory/ProfilerLogger.php create mode 100644 src/Core/Logger/Factory/StreamLogger.php create mode 100644 src/Core/Logger/Factory/SyslogLogger.php delete mode 100644 src/Core/Logger/Type/README.md rename src/{ => Core/Logger}/Util/FileSystem.php (94%) create mode 100644 src/Core/Logger/Util/LoggerSettingsCheck.php create mode 100644 static/hooks.config.php diff --git a/bin/auth_ejabberd.php b/bin/auth_ejabberd.php index 40e7d3b97..b923009e6 100755 --- a/bin/auth_ejabberd.php +++ b/bin/auth_ejabberd.php @@ -58,6 +58,7 @@ if (php_sapi_name() !== 'cli') { use Dice\Dice; use Friendica\App\Mode; +use Friendica\Core\Logger\Capabilities\LogChannel; use Friendica\Security\ExAuth; use Psr\Log\LoggerInterface; @@ -78,7 +79,10 @@ chdir($directory); require dirname(__DIR__) . '/vendor/autoload.php'; $dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php'); -$dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['auth_ejabberd']]); +/** @var \Friendica\Core\Addon\Capabilities\ICanLoadAddons $addonLoader */ +$addonLoader = $dice->create(\Friendica\Core\Addon\Capabilities\ICanLoadAddons::class); +$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies')); +$dice = $dice->addRule(LoggerInterface::class,['constructParams' => [LogChannel::AUTH_JABBERED]]); \Friendica\DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); diff --git a/bin/console.php b/bin/console.php index e1bc6b498..b2cde1082 100755 --- a/bin/console.php +++ b/bin/console.php @@ -26,13 +26,17 @@ if (php_sapi_name() !== 'cli') { } use Dice\Dice; +use Friendica\Core\Logger\Capabilities\LogChannel; use Friendica\DI; use Psr\Log\LoggerInterface; require dirname(__DIR__) . '/vendor/autoload.php'; $dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php'); -$dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['console']]); +/** @var \Friendica\Core\Addon\Capabilities\ICanLoadAddons $addonLoader */ +$addonLoader = $dice->create(\Friendica\Core\Addon\Capabilities\ICanLoadAddons::class); +$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies')); +$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [LogChannel::CONSOLE]]); /// @fixme Necessary until Hooks inside the Logger can get loaded without the DI-class DI::init($dice); diff --git a/bin/daemon.php b/bin/daemon.php index e550aea89..1f0bb7079 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -38,7 +38,6 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Util\DateTimeFormat; -use Psr\Log\LoggerInterface; // Get options $shortopts = 'f'; @@ -60,7 +59,10 @@ if (!file_exists('index.php') && (sizeof($_SERVER['argv']) != 0)) { require dirname(__DIR__) . '/vendor/autoload.php'; $dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php'); -$dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['daemon']]); +/** @var \Friendica\Core\Addon\Capabilities\ICanLoadAddons $addonLoader */ +$addonLoader = $dice->create(\Friendica\Core\Addon\Capabilities\ICanLoadAddons::class); +$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies')); +$dice = $dice->addRule(LoggerInterface::class,['constructParams' => [Logger\Capabilities\LogChannel::DAEMON]]); DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); diff --git a/bin/worker.php b/bin/worker.php index a74213248..18a41e064 100755 --- a/bin/worker.php +++ b/bin/worker.php @@ -29,6 +29,7 @@ if (php_sapi_name() !== 'cli') { use Dice\Dice; use Friendica\App; use Friendica\App\Mode; +use Friendica\Core\Logger\Capabilities\LogChannel; use Friendica\Core\Update; use Friendica\Core\Worker; use Friendica\DI; @@ -54,7 +55,10 @@ if (!file_exists("index.php") && (sizeof($_SERVER["argv"]) != 0)) { require dirname(__DIR__) . '/vendor/autoload.php'; $dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php'); -$dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['worker']]); +/** @var \Friendica\Core\Addon\Capabilities\ICanLoadAddons $addonLoader */ +$addonLoader = $dice->create(\Friendica\Core\Addon\Capabilities\ICanLoadAddons::class); +$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies')); +$dice = $dice->addRule(LoggerInterface::class,['constructParams' => [LogChannel::WORKER]]); DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); diff --git a/index.php b/index.php index 90df9c00e..ce82470b6 100644 --- a/index.php +++ b/index.php @@ -30,6 +30,9 @@ if (!file_exists(__DIR__ . '/vendor/autoload.php')) { require __DIR__ . '/vendor/autoload.php'; $dice = (new Dice())->addRules(include __DIR__ . '/static/dependencies.config.php'); +/** @var \Friendica\Core\Addon\Capabilities\ICanLoadAddons $addonLoader */ +$addonLoader = $dice->create(\Friendica\Core\Addon\Capabilities\ICanLoadAddons::class); +$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies')); $dice = $dice->addRule(Friendica\App\Mode::class, ['call' => [['determineRunMode', [false, $_SERVER], Dice::CHAIN_CALL]]]); \Friendica\DI::init($dice); diff --git a/src/Core/Addon/Capabilities/ICanLoadAddons.php b/src/Core/Addon/Capabilities/ICanLoadAddons.php new file mode 100644 index 000000000..a28826ffb --- /dev/null +++ b/src/Core/Addon/Capabilities/ICanLoadAddons.php @@ -0,0 +1,37 @@ +. + * + */ + +namespace Friendica\Core\Addon\Capabilities; + +/** + * Interface for loading Addons specific content + */ +interface ICanLoadAddons +{ + /** + * Returns a merged config array of all active addons for a given config-name + * + * @param string $configName The config-name (config-file at the static directory, like 'hooks' => '{addon}/static/hooks.config.php) + * + * @return array the merged array + */ + public function getActiveAddonConfig(string $configName): array; +} diff --git a/src/Core/Hooks/Capabilities/IAmAStrategy.php b/src/Core/Addon/Exception/AddonInvalidConfigFileException.php similarity index 71% rename from src/Core/Hooks/Capabilities/IAmAStrategy.php rename to src/Core/Addon/Exception/AddonInvalidConfigFileException.php index 017cb56c4..3bb41be53 100644 --- a/src/Core/Hooks/Capabilities/IAmAStrategy.php +++ b/src/Core/Addon/Exception/AddonInvalidConfigFileException.php @@ -19,11 +19,17 @@ * */ -namespace Friendica\Core\Hooks\Capabilities; +namespace Friendica\Core\Addon\Exception; + +use Throwable; /** - * All classes, implementing this interface are valid Strategies for Hook calls + * Exception in case one or more config files of the addons are invalid */ -interface IAmAStrategy +class AddonInvalidConfigFileException extends \RuntimeException { + public function __construct($message = "", $code = 0, Throwable $previous = null) + { + parent::__construct($message, 500, $previous); + } } diff --git a/src/Core/Addon/Model/AddonLoader.php b/src/Core/Addon/Model/AddonLoader.php new file mode 100644 index 000000000..707601aef --- /dev/null +++ b/src/Core/Addon/Model/AddonLoader.php @@ -0,0 +1,70 @@ +. + * + */ + +namespace Friendica\Core\Addon\Model; + +use Friendica\Core\Addon\Capabilities\ICanLoadAddons; +use Friendica\Core\Addon\Exception\AddonInvalidConfigFileException; +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Util\Strings; + +class AddonLoader implements ICanLoadAddons +{ + const STATIC_PATH = 'static'; + /** @var string */ + protected $basePath; + /** @var IManageConfigValues */ + protected $config; + + public function __construct(string $basePath, IManageConfigValues $config) + { + $this->basePath = $basePath; + $this->config = $config; + } + + /** {@inheritDoc} */ + public function getActiveAddonConfig(string $configName): array + { + $addons = array_keys(array_filter($this->config->get('addons') ?? [])); + $returnConfig = []; + + foreach ($addons as $addon) { + $addonName = Strings::sanitizeFilePathItem(trim($addon)); + + $configFile = $this->basePath . '/addon/' . $addonName . '/' . static::STATIC_PATH . '/' . $configName . '.config.php'; + + if (!file_exists($configFile)) { + // Addon unmodified, skipping + continue; + } + + $config = include $configFile; + + if (!is_array($config)) { + throw new AddonInvalidConfigFileException('Error loading config file ' . $configFile); + } + + $returnConfig = array_merge_recursive($returnConfig, $config); + } + + return $returnConfig; + } +} diff --git a/src/Core/Hooks/Capabilities/HookType.php b/src/Core/Hooks/Capabilities/HookType.php new file mode 100644 index 000000000..6fea41e17 --- /dev/null +++ b/src/Core/Hooks/Capabilities/HookType.php @@ -0,0 +1,39 @@ +. + * + */ + +namespace Friendica\Core\Hooks\Capabilities; + +interface HookType +{ + /** + * Defines the key for the list of strategy-hooks. + * + * @see https://refactoring.guru/design-patterns/strategy + */ + const STRATEGY = 'strategy'; + /** + * Defines the key for the list of decorator-hooks. + * + * @see https://refactoring.guru/design-patterns/decorator + */ + const DECORATOR = 'decorator'; + const EVENT = 'event'; +} diff --git a/src/Core/Hooks/Capabilities/ICanCreateInstances.php b/src/Core/Hooks/Capabilities/ICanCreateInstances.php new file mode 100644 index 000000000..6e5d8beb4 --- /dev/null +++ b/src/Core/Hooks/Capabilities/ICanCreateInstances.php @@ -0,0 +1,59 @@ +. + * + */ + +namespace Friendica\Core\Hooks\Capabilities; + +use Friendica\Core\Hooks\Exceptions\HookInstanceException; + +/** + * creates special instance and decorator treatments for given classes + */ +interface ICanCreateInstances +{ + /** + * Returns a new instance of a given class for the corresponding name + * + * The instance will be build based on the registered strategy and the (unique) name + * + * In case, there are registered decorators for this class as well, all decorators of the list will be wrapped + * around the instance before returning it + * + * @param string $class The fully-qualified name of the given class or interface which will get returned + * @param string $name An arbitrary identifier to find a concrete instance strategy. + * @param array $arguments Additional arguments, which can be passed to the constructor of "$class" at runtime + * + * @return object The concrete instance of the type "$class" + */ + public function createWithName(string $class, string $name, array $arguments = []): object; + + /** + * Returns a new instance of a given class + * + * In case, there are registered decorators for this class as well, all decorators of the list will be wrapped + * around the instance before returning it + * + * @param string $class The fully-qualified name of the given class or interface which will get returned + * @param array $arguments Additional arguments, which can be passed to the constructor of "$class" at runtime + * + * @return object The concrete instance of the type "$class" + */ + public function create(string $class, array $arguments = []): object; +} diff --git a/src/Core/Hooks/Capabilities/ICanManageInstances.php b/src/Core/Hooks/Capabilities/ICanRegisterInstances.php similarity index 55% rename from src/Core/Hooks/Capabilities/ICanManageInstances.php rename to src/Core/Hooks/Capabilities/ICanRegisterInstances.php index bdad1b923..23a77ab35 100644 --- a/src/Core/Hooks/Capabilities/ICanManageInstances.php +++ b/src/Core/Hooks/Capabilities/ICanRegisterInstances.php @@ -21,61 +21,43 @@ namespace Friendica\Core\Hooks\Capabilities; -use Friendica\Core\Hooks\Exceptions\HookInstanceException; use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException; /** - * Managing special instance and decorator treatments for classes + * Register strategies and decorator/treatment handling for given classes */ -interface ICanManageInstances +interface ICanRegisterInstances { /** * Register a class(strategy) for a given interface with a unique name. * * @see https://refactoring.guru/design-patterns/strategy * - * @param string $interface The interface, which the given class implements - * @param string $name An arbitrary identifier for the given class, which will be used for factories, dependency injections etc. - * @param string $class The fully-qualified given class name - * @param ?array $arguments Additional arguments, which can be passed to the constructor + * @param string $interface The interface, which the given class implements + * @param string $class The fully-qualified given class name + * A placeholder for dependencies is possible as well + * @param ?string $name An arbitrary identifier for the given class, which will be used for factories, dependency injections etc. * * @return $this This interface for chain-calls * * @throws HookRegisterArgumentException in case the given class for the interface isn't valid or already set */ - public function registerStrategy(string $interface, string $name, string $class, array $arguments = null): self; + public function registerStrategy(string $interface, string $class, ?string $name = null): self; /** * Register a new decorator for a given class or interface - * @see https://refactoring.guru/design-patterns/decorator + * + * @see https://refactoring.guru/design-patterns/decorator * * @note Decorator attach new behaviors to classes without changing them or without letting them know about it. * * @param string $class The fully-qualified class or interface name, which gets decorated by a class * @param string $decoratorClass The fully-qualified name of the class which mimics the given class or interface and adds new functionality - * @param array $arguments Additional arguments, which can be passed to the constructor of "decoratorClass" + * A placeholder for dependencies is possible as well * * @return $this This interface for chain-calls * * @throws HookRegisterArgumentException in case the given class for the class or interface isn't valid */ - public function registerDecorator(string $class, string $decoratorClass, array $arguments = []): self; - - /** - * Returns a new instance of a given class for the corresponding name - * - * The instance will be build based on the registered strategy and the (unique) name - * - * In case, there are registered decorators for this class as well, all decorators of the list will be wrapped - * around the instance before returning it - * - * @param string $class The fully-qualified name of the given class or interface which will get returned - * @param string $name An arbitrary identifier to find a concrete instance strategy. - * @param array $arguments Additional arguments, which can be passed to the constructor of "$class" at runtime - * - * @return object The concrete instance of the type "$class" - * - * @throws HookInstanceException In case the class cannot get created - */ - public function getInstance(string $class, string $name, array $arguments = []): object; + public function registerDecorator(string $class, string $decoratorClass): self; } diff --git a/src/Core/Logger/Exception/LoggerInvalidException.php b/src/Core/Hooks/Exceptions/HookConfigException.php similarity index 82% rename from src/Core/Logger/Exception/LoggerInvalidException.php rename to src/Core/Hooks/Exceptions/HookConfigException.php index db6ecb78c..ceca721b1 100644 --- a/src/Core/Logger/Exception/LoggerInvalidException.php +++ b/src/Core/Hooks/Exceptions/HookConfigException.php @@ -19,13 +19,11 @@ * */ -namespace Friendica\Core\Logger\Exception; +namespace Friendica\Core\Hooks\Exceptions; -use Throwable; - -class LoggerInvalidException extends \RuntimeException +class HookConfigException extends \RuntimeException { - public function __construct($message = "", Throwable $previous = null) + public function __construct($message = "", \Throwable $previous = null) { parent::__construct($message, 500, $previous); } diff --git a/src/Core/Hooks/Model/DiceInstanceManager.php b/src/Core/Hooks/Model/DiceInstanceManager.php new file mode 100644 index 000000000..381e0a7fe --- /dev/null +++ b/src/Core/Hooks/Model/DiceInstanceManager.php @@ -0,0 +1,111 @@ +. + * + */ + +namespace Friendica\Core\Hooks\Model; + +use Dice\Dice; +use Friendica\Core\Hooks\Capabilities\ICanCreateInstances; +use Friendica\Core\Hooks\Capabilities\ICanRegisterInstances; +use Friendica\Core\Hooks\Exceptions\HookInstanceException; +use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException; +use Friendica\Core\Hooks\Util\HookFileManager; + +/** + * This class represents an instance register, which uses Dice for creation + * + * @see Dice + */ +class DiceInstanceManager implements ICanCreateInstances, ICanRegisterInstances +{ + protected $instance = []; + protected $decorator = []; + + /** @var Dice */ + protected $dice; + + public function __construct(Dice $dice, HookFileManager $hookFileManager) + { + $this->dice = $dice; + $hookFileManager->setupHooks($this); + } + + /** {@inheritDoc} */ + public function registerStrategy(string $interface, string $class, ?string $name = null): ICanRegisterInstances + { + if (!empty($this->instance[$interface][$name])) { + throw new HookRegisterArgumentException(sprintf('A class with the name %s is already set for the interface %s', $name, $interface)); + } + + $this->instance[$interface][$name] = $class; + + return $this; + } + + /** {@inheritDoc} */ + public function registerDecorator(string $class, string $decoratorClass): ICanRegisterInstances + { + $this->decorator[$class][] = $decoratorClass; + + return $this; + } + + /** {@inheritDoc} */ + public function create(string $class, array $arguments = []): object + { + $instanceClassName = $class; + $instanceRule = $this->dice->getRule($instanceClassName) ?? []; + + $instanceRule = array_replace_recursive($instanceRule, [ + 'constructParams' => $arguments, + ]); + + $this->dice = $this->dice->addRule($instanceClassName, $instanceRule); + + foreach ($this->decorator[$class] ?? [] as $decorator) { + $dependencyRule = $this->dice->getRule($decorator); + for ($i = 0; $i < count($dependencyRule['call'] ?? []); $i++) { + $dependencyRule['call'][$i][1] = [[Dice::INSTANCE => $instanceClassName]]; + } + $dependencyRule['constructParams'] = $arguments; + $dependencyRule['substitutions'] = [ + $class => $instanceClassName, + ]; + + $this->dice = $this->dice->addRule($decorator, $dependencyRule); + + $instanceClassName = $decorator; + } + + return $this->dice->create($instanceClassName); + } + + /** {@inheritDoc} */ + public function createWithName(string $class, string $name, array $arguments = []): object + { + if (empty($this->instance[$class][$name])) { + throw new HookInstanceException(sprintf('The class with the name %s isn\'t registered for the class or interface %s', $name, $class)); + } + + $instanceClassName = $this->instance[$class][$name]; + + return $this->create($instanceClassName, $arguments); + } +} diff --git a/src/Core/Hooks/Model/InstanceManager.php b/src/Core/Hooks/Model/InstanceManager.php deleted file mode 100644 index 7bfcfa420..000000000 --- a/src/Core/Hooks/Model/InstanceManager.php +++ /dev/null @@ -1,104 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Hooks\Model; - -use Dice\Dice; -use Friendica\Core\Hooks\Capabilities\IAmAStrategy; -use Friendica\Core\Hooks\Capabilities\ICanManageInstances; -use Friendica\Core\Hooks\Exceptions\HookInstanceException; -use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException; - -/** {@inheritDoc} */ -class InstanceManager implements ICanManageInstances -{ - protected $instance = []; - protected $instanceArguments = []; - protected $decorator = []; - - /** @var Dice */ - protected $dice; - - public function __construct(Dice $dice) - { - $this->dice = $dice; - } - - /** {@inheritDoc} */ - public function registerStrategy(string $interface, string $name, string $class, array $arguments = null): ICanManageInstances - { - if (!is_a($class, $interface, true)) { - throw new HookRegisterArgumentException(sprintf('%s is not a valid class for the interface %s', $class, $interface)); - } - - if (!is_a($class, IAmAStrategy::class, true)) { - throw new HookRegisterArgumentException(sprintf('%s does not inherit from the marker interface %s', $class, IAmAStrategy::class)); - } - - if (!empty($this->instance[$interface][$name])) { - throw new HookRegisterArgumentException(sprintf('A class with the name %s is already set for the interface %s', $name, $interface)); - } - - $this->instance[$interface][$name] = $class; - $this->instanceArguments[$interface][$name] = $arguments; - - return $this; - } - - /** {@inheritDoc} */ - public function registerDecorator(string $class, string $decoratorClass, array $arguments = []): ICanManageInstances - { - if (!is_a($decoratorClass, $class, true)) { - throw new HookRegisterArgumentException(sprintf('%s is not a valid substitution for the given class or interface %s', $decoratorClass, $class)); - } - - $this->decorator[$class][] = [ - 'class' => $decoratorClass, - 'arguments' => $arguments, - ]; - - return $this; - } - - /** {@inheritDoc} */ - public function getInstance(string $class, string $name, array $arguments = []): object - { - if (empty($this->instance[$class][$name])) { - throw new HookInstanceException(sprintf('The class with the name %s isn\'t registered for the class or interface %s', $name, $class)); - } - - $instance = $this->dice->create($this->instance[$class][$name], array_merge($this->instanceArguments[$class][$name] ?? [], $arguments)); - - foreach ($this->decorator[$class] ?? [] as $decorator) { - $this->dice = $this->dice->addRule($class, [ - 'instanceOf' => $decorator['class'], - 'constructParams' => empty($decorator['arguments']) ? null : $decorator['arguments'], - /// @todo maybe support call structures for hooks as well in a later stage - could make factory calls easier - 'call' => null, - 'substitutions' => [$class => $instance], - ]); - - $instance = $this->dice->create($class); - } - - return $instance; - } -} diff --git a/src/Core/Hooks/Util/HookFileManager.php b/src/Core/Hooks/Util/HookFileManager.php new file mode 100644 index 000000000..357567857 --- /dev/null +++ b/src/Core/Hooks/Util/HookFileManager.php @@ -0,0 +1,118 @@ +. + * + */ + +namespace Friendica\Core\Hooks\Util; + +use Friendica\Core\Addon\Capabilities\ICanLoadAddons; +use Friendica\Core\Hooks\Capabilities\HookType; +use Friendica\Core\Hooks\Capabilities\ICanRegisterInstances; +use Friendica\Core\Hooks\Exceptions\HookConfigException; + +/** + * Manage all hooks.config.php files + */ +class HookFileManager +{ + const STATIC_DIR = 'static'; + const CONFIG_NAME = 'hooks'; + + /** @var ICanLoadAddons */ + protected $addonLoader; + /** @var array */ + protected $hookConfig = []; + /** @var string */ + protected $basePath; + + public function __construct(string $basePath, ICanLoadAddons $addonLoader) + { + $this->basePath = $basePath; + $this->addonLoader = $addonLoader; + } + + /** + * Loads all kinds of hooks and registers the corresponding instances + * + * @param ICanRegisterInstances $instanceRegister The instance register + * + * @return void + */ + public function setupHooks(ICanRegisterInstances $instanceRegister) + { + // In case it wasn't used before, reload the whole hook config + if (empty($this->hookConfig)) { + $this->reloadHookConfig(); + } + + foreach ($this->hookConfig as $hookType => $classList) { + switch ($hookType) { + case HookType::STRATEGY: + foreach ($classList as $interface => $strategy) { + foreach ($strategy as $dependencyName => $names) { + if (is_array($names)) { + foreach ($names as $name) { + $instanceRegister->registerStrategy($interface, $dependencyName, $name); + } + } else { + $instanceRegister->registerStrategy($interface, $dependencyName, $names); + } + } + } + break; + case HookType::DECORATOR: + foreach ($classList as $interface => $decorators) { + if (is_array($decorators)) { + foreach ($decorators as $decorator) { + $instanceRegister->registerDecorator($interface, $decorator); + } + } else { + $instanceRegister->registerDecorator($interface, $decorators); + } + } + break; + } + } + } + + /** + * Reloads all hook config files into the config cache for later usage + * + * Merges all hook configs from every addon - if present - as well + * + * @return void + */ + protected function reloadHookConfig() + { + // load core hook config + $configFile = $this->basePath . '/' . static::STATIC_DIR . '/' . static::CONFIG_NAME . '.config.php'; + + if (!file_exists($configFile)) { + throw new HookConfigException(sprintf('config file %s does not exit.', $configFile)); + } + + $config = include $configFile; + + if (!is_array($config)) { + throw new HookConfigException('Error loading config file ' . $configFile); + } + + $this->hookConfig = array_merge_recursive($config, $this->addonLoader->getActiveAddonConfig(static::CONFIG_NAME)); + } +} diff --git a/src/Core/Logger/Capabilities/ICheckLoggerSettings.php b/src/Core/Logger/Capabilities/ICheckLoggerSettings.php new file mode 100644 index 000000000..532425ec0 --- /dev/null +++ b/src/Core/Logger/Capabilities/ICheckLoggerSettings.php @@ -0,0 +1,42 @@ +. + * + */ + +namespace Friendica\Core\Logger\Capabilities; + +/** + * Whenever a logging specific check is necessary, use this interface to encapsulate and centralize this logic + */ +interface ICheckLoggerSettings +{ + /** + * Checks if the logfile is set and usable + * + * @return string|null null in case everything is ok, otherwise returns the error + */ + public function checkLogfile(): ?string; + + /** + * Checks if the debugging logfile is usable in case it is set! + * + * @return string|null null in case everything is ok, otherwise returns the error + */ + public function checkDebugLogfile(): ?string; +} diff --git a/src/Core/Logger/Capabilities/IHaveCallIntrospections.php b/src/Core/Logger/Capabilities/IHaveCallIntrospections.php index 83d75f976..7f884bf64 100644 --- a/src/Core/Logger/Capabilities/IHaveCallIntrospections.php +++ b/src/Core/Logger/Capabilities/IHaveCallIntrospections.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Capabilities/LogChannel.php b/src/Core/Logger/Capabilities/LogChannel.php new file mode 100644 index 000000000..54ae03217 --- /dev/null +++ b/src/Core/Logger/Capabilities/LogChannel.php @@ -0,0 +1,43 @@ +. + * + */ + +namespace Friendica\Core\Logger\Capabilities; + +/** + * An enum class for the Log channels + */ +interface LogChannel +{ + /** @var string channel for the auth_ejabbered script */ + public const AUTH_JABBERED = 'auth_ejabberd'; + /** @var string Default channel in case it isn't set explicit */ + public const DEFAULT = self::APP; + /** @var string channel for console execution */ + public const CONSOLE = 'console'; + /** @var string channel for developer focused logging */ + public const DEV = 'dev'; + /** @var string channel for daemon executions */ + public const DAEMON = 'daemon'; + /** @var string channel for worker execution */ + public const WORKER = 'worker'; + /** @var string channel for frontend app executions */ + public const APP = 'app'; +} diff --git a/src/Core/Logger/Exception/LogLevelException.php b/src/Core/Logger/Exception/LogLevelException.php index 36b2c31cf..39b2d17a9 100644 --- a/src/Core/Logger/Exception/LogLevelException.php +++ b/src/Core/Logger/Exception/LogLevelException.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -23,6 +23,9 @@ namespace Friendica\Core\Logger\Exception; use Throwable; +/** + * Exception in case the loglevel isn't set or isn't valid + */ class LogLevelException extends \InvalidArgumentException { public function __construct($message = "", Throwable $previous = null) diff --git a/src/Core/Logger/Exception/LoggerArgumentException.php b/src/Core/Logger/Exception/LoggerArgumentException.php index a0b9949b0..08eabe84a 100644 --- a/src/Core/Logger/Exception/LoggerArgumentException.php +++ b/src/Core/Logger/Exception/LoggerArgumentException.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -23,6 +23,9 @@ namespace Friendica\Core\Logger\Exception; use Throwable; +/** + * Exception in case an argument of a logger class isn't valid + */ class LoggerArgumentException extends \InvalidArgumentException { public function __construct($message = "", Throwable $previous = null) diff --git a/src/Core/Logger/Exception/LoggerException.php b/src/Core/Logger/Exception/LoggerException.php index 7e3637327..13bcff8f3 100644 --- a/src/Core/Logger/Exception/LoggerException.php +++ b/src/Core/Logger/Exception/LoggerException.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -23,6 +23,9 @@ namespace Friendica\Core\Logger\Exception; use Throwable; +/** + * A generic exception of the logging namespace + */ class LoggerException extends \Exception { public function __construct($message = "", Throwable $previous = null) diff --git a/src/Core/Logger/Exception/LoggerUnusableException.php b/src/Core/Logger/Exception/LoggerUnusableException.php new file mode 100644 index 000000000..29336e086 --- /dev/null +++ b/src/Core/Logger/Exception/LoggerUnusableException.php @@ -0,0 +1,35 @@ +. + * + */ + +namespace Friendica\Core\Logger\Exception; + +use Throwable; + +/** + * Exception in case the used logging instance is unusable because of some circumstances + */ +class LoggerUnusableException extends \RuntimeException +{ + public function __construct($message = "", Throwable $previous = null) + { + parent::__construct($message, 500, $previous); + } +} diff --git a/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php b/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php new file mode 100644 index 000000000..a6ff5eebb --- /dev/null +++ b/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php @@ -0,0 +1,80 @@ +. + * + */ + +namespace Friendica\Core\Logger\Factory; + +use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections; +use Psr\Log\LogLevel; + +/** + * Abstract class for creating logger types, which includes common necessary logic/content + */ +abstract class AbstractLoggerTypeFactory +{ + /** @var string */ + protected $channel; + /** @var IHaveCallIntrospections */ + protected $introspection; + + /** + * @param string $channel The channel for the logger + */ + public function __construct(IHaveCallIntrospections $introspection, string $channel) + { + $this->channel = $channel; + $this->introspection = $introspection; + } + + /** + * Mapping a legacy level to the PSR-3 compliant levels + * + * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel + * + * @param string $level the level to be mapped + * + * @return string the PSR-3 compliant level + */ + protected static function mapLegacyConfigDebugLevel(string $level): string + { + switch ($level) { + // legacy WARNING + case "0": + return LogLevel::ERROR; + // legacy INFO + case "1": + return LogLevel::WARNING; + // legacy TRACE + case "2": + return LogLevel::NOTICE; + // legacy DEBUG + case "3": + return LogLevel::INFO; + // legacy DATA + case "4": + // legacy ALL + case "5": + return LogLevel::DEBUG; + // default if nothing set + default: + return $level; + } + } +} diff --git a/src/Core/Logger/Factory/Logger.php b/src/Core/Logger/Factory/Logger.php index 2821a813c..3fde878a3 100644 --- a/src/Core/Logger/Factory/Logger.php +++ b/src/Core/Logger/Factory/Logger.php @@ -22,134 +22,38 @@ namespace Friendica\Core\Logger\Factory; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Hooks\Capabilities\ICanManageInstances; -use Friendica\Core\Logger\Exception\LogLevelException; -use Friendica\Core\Logger\Type\ProfilerLogger; -use Friendica\Core\Logger\Type\StreamLogger; -use Friendica\Core\Logger\Type\SyslogLogger; +use Friendica\Core\Hooks\Capabilities\ICanCreateInstances; +use Friendica\Core\Logger\Capabilities\LogChannel; use Psr\Log\LoggerInterface; -use Psr\Log\LogLevel; use Psr\Log\NullLogger; +use Throwable; /** - * A logger factory + * The logger factory for the core logging instances */ class Logger { - const DEV_CHANNEL = 'dev'; - - /** @var string The log-channel (app, worker, ...) */ + /** @var string The channel */ protected $channel; - /** @var ICanManageInstances */ - protected $instanceManager; - /** @var IManageConfigValues */ - protected $config; - public function __construct(string $channel, ICanManageInstances $instanceManager, IManageConfigValues $config, string $logfile = null) + public function __construct(string $channel = LogChannel::DEFAULT) { - $this->channel = $channel; - $this->instanceManager = $instanceManager; - $this->config = $config; - - $this->instanceManager - ->registerStrategy(LoggerInterface::class, 'syslog', SyslogLogger::class) - ->registerStrategy(LoggerInterface::class, 'stream', StreamLogger::class, isset($logfile) ? [$logfile] : null); - - if ($this->config->get('system', 'profiling') ?? false) { - $this->instanceManager->registerDecorator(LoggerInterface::class, ProfilerLogger::class); - } + $this->channel = $channel; } - /** - * Creates a new PSR-3 compliant logger instances - * - * @param string|null $loglevel (optional) A given loglevel in case the loglevel in the config isn't applicable - * - * @return LoggerInterface The PSR-3 compliant logger instance - */ - public function create(string $loglevel = null): LoggerInterface + public function create(ICanCreateInstances $createInstances, IManageConfigValues $config): LoggerInterface { - if (empty($this->config->get('system', 'debugging') ?? false)) { + if (empty($config->get('system', 'debugging') ?? false)) { return new NullLogger(); } - $loglevel = $loglevel ?? static::mapLegacyConfigDebugLevel($this->config->get('system', 'loglevel')); - $name = $this->config->get('system', 'logger_config') ?? 'stream'; + $name = $config->get('system', 'logger_config') ?? ''; try { - /** @var LoggerInterface */ - return $this->instanceManager->getInstance(LoggerInterface::class, $name, [$this->channel, $loglevel]); - } catch (LogLevelException $exception) { - // If there's a wrong config value for loglevel, try again with standard - $logger = $this->create(LogLevel::NOTICE); - $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]); - return $logger; - } catch (\Throwable $e) { + return $createInstances->createWithName(LoggerInterface::class, $name, [$this->channel]); + } catch (Throwable $e) { // No logger ... return new NullLogger(); } } - - /** - * Creates a new PSR-3 compliant develop logger - * - * If you want to debug only interactions from your IP or the IP of a remote server for federation debug, - * you'll use this logger instance for the duration of your work. - * - * It should never get filled during normal usage of Friendica - * - * @return LoggerInterface The PSR-3 compliant logger instance - * @throws \Exception - */ - public function createDev() - { - $debugging = $this->config->get('system', 'debugging'); - $stream = $this->config->get('system', 'dlogfile'); - $developerIp = $this->config->get('system', 'dlogip'); - - if ((!isset($developerIp) || !$debugging) && - (!is_file($stream) || is_writable($stream))) { - return new NullLogger(); - } - - $name = $this->config->get('system', 'logger_config') ?? 'stream'; - - /** @var LoggerInterface */ - return $this->instanceManager->getInstance(LoggerInterface::class, $name, [self::DEV_CHANNEL, LogLevel::DEBUG, $stream]); - } - - /** - * Mapping a legacy level to the PSR-3 compliant levels - * - * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel - * - * @param string $level the level to be mapped - * - * @return string the PSR-3 compliant level - */ - private static function mapLegacyConfigDebugLevel(string $level): string - { - switch ($level) { - // legacy WARNING - case "0": - return LogLevel::ERROR; - // legacy INFO - case "1": - return LogLevel::WARNING; - // legacy TRACE - case "2": - return LogLevel::NOTICE; - // legacy DEBUG - case "3": - return LogLevel::INFO; - // legacy DATA - case "4": - // legacy ALL - case "5": - return LogLevel::DEBUG; - // default if nothing set - default: - return $level; - } - } } diff --git a/src/Core/Logger/Factory/ProfilerLogger.php b/src/Core/Logger/Factory/ProfilerLogger.php new file mode 100644 index 000000000..3d126921d --- /dev/null +++ b/src/Core/Logger/Factory/ProfilerLogger.php @@ -0,0 +1,53 @@ +. + * + */ + +namespace Friendica\Core\Logger\Factory; + +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Logger\Type\ProfilerLogger as ProfilerLoggerClass; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +/** + * The logger factory for the ProfilerLogger + * + * @see ProfilerLoggerClass + */ +class ProfilerLogger extends AbstractLoggerTypeFactory +{ + /** + * Wraps a given Logger with profiling information in case profiling is enabled + * + * @param IManageConfigValues $config The system configuration + * @param LoggerInterface $logger The given logger class, which should get wrapped + * @param Profiler $profiler The profiler utility + * + * @return LoggerInterface The PSR-3 compliant logger instance + */ + public function create(IManageConfigValues $config, LoggerInterface $logger, Profiler $profiler): LoggerInterface + { + if ($config->get('system', 'profiling') ?? false) { + return $logger; + } else { + return new ProfilerLoggerClass($logger, $profiler); + } + } +} diff --git a/src/Core/Logger/Factory/StreamLogger.php b/src/Core/Logger/Factory/StreamLogger.php new file mode 100644 index 000000000..85475b3af --- /dev/null +++ b/src/Core/Logger/Factory/StreamLogger.php @@ -0,0 +1,100 @@ +. + * + */ + +namespace Friendica\Core\Logger\Factory; + +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Logger\Capabilities\LogChannel; +use Friendica\Core\Logger\Exception\LoggerArgumentException; +use Friendica\Core\Logger\Exception\LoggerException; +use Friendica\Core\Logger\Type\StreamLogger as StreamLoggerClass; +use Friendica\Core\Logger\Util\FileSystem; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use Psr\Log\NullLogger; + +/** + * The logger factory for the StreamLogger instance + * + * @see StreamLoggerClass + */ +class StreamLogger extends AbstractLoggerTypeFactory +{ + /** + * Creates a new PSR-3 compliant stream logger instance + * + * @param IManageConfigValues $config The system configuration + * @param string|null $logfile (optional) A given logfile which should be used as stream (e.g. in case of + * developer logging) + * @param string|null $channel (optional) A given channel in case it is different from the default + * + * @return LoggerInterface The PSR-3 compliant logger instance + * + * @throws LoggerException in case the logger cannot get created + */ + public function create(IManageConfigValues $config, string $logfile = null, string $channel = null): LoggerInterface + { + $fileSystem = new FileSystem(); + + $logfile = $logfile ?? $config->get('system', 'logfile'); + if ((@file_exists($logfile) && !@is_writable($logfile)) && !@is_writable(basename($logfile))) { + throw new LoggerArgumentException(sprintf('%s is not a valid logfile', $logfile)); + } + + $loglevel = static::mapLegacyConfigDebugLevel($config->get('system', 'loglevel')); + + if (array_key_exists($loglevel, StreamLoggerClass::levelToInt)) { + $loglevel = StreamLoggerClass::levelToInt[$loglevel]; + } else { + $loglevel = StreamLoggerClass::levelToInt[LogLevel::NOTICE]; + } + + $stream = $fileSystem->createStream($logfile); + + return new StreamLoggerClass($channel ?? $this->channel, $this->introspection, $stream, $loglevel, getmypid()); + } + + /** + * Creates a new PSR-3 compliant develop logger + * + * If you want to debug only interactions from your IP or the IP of a remote server for federation debug, + * you'll use this logger instance for the duration of your work. + * + * It should never get filled during normal usage of Friendica + * + * @return LoggerInterface The PSR-3 compliant logger instance + * + * @throws LoggerException + */ + public function createDev(IManageConfigValues $config) + { + $debugging = $config->get('system', 'debugging'); + $logfile = $config->get('system', 'dlogfile'); + $developerIp = $config->get('system', 'dlogip'); + + if ((!isset($developerIp) || !$debugging) && + (!is_file($logfile) || is_writable($logfile))) { + return new NullLogger(); + } + + return $this->create($config, $logfile, LogChannel::DEV); + } +} diff --git a/src/Core/Logger/Factory/SyslogLogger.php b/src/Core/Logger/Factory/SyslogLogger.php new file mode 100644 index 000000000..5a8789584 --- /dev/null +++ b/src/Core/Logger/Factory/SyslogLogger.php @@ -0,0 +1,60 @@ +. + * + */ + +namespace Friendica\Core\Logger\Factory; + +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Logger\Exception\LoggerException; +use Friendica\Core\Logger\Type\SyslogLogger as SyslogLoggerClass; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +/** + * The logger factory for the SyslogLogger instance + * + * @see SyslogLoggerClass + */ +class SyslogLogger extends AbstractLoggerTypeFactory +{ + /** + * Creates a new PSR-3 compliant syslog logger instance + * + * @param IManageConfigValues $config The system configuration + * + * @return LoggerInterface The PSR-3 compliant logger instance + * + * @throws LoggerException in case the logger cannot get created + */ + public function create(IManageConfigValues $config): LoggerInterface + { + $logOpts = $config->get('system', 'syslog_flags') ?? SyslogLoggerClass::DEFAULT_FLAGS; + $logFacility = $config->get('system', 'syslog_facility') ?? SyslogLoggerClass::DEFAULT_FACILITY; + $loglevel = SyslogLogger::mapLegacyConfigDebugLevel($config->get('system', 'loglevel')); + + if (!array_key_exists($loglevel, SyslogLoggerClass::logLevels)) { + $loglevel = SyslogLoggerClass::logLevels[$loglevel]; + } else { + $loglevel = SyslogLoggerClass::logLevels[LogLevel::NOTICE]; + } + + return new SyslogLoggerClass($this->channel, $this->introspection, $loglevel, $logOpts, $logFacility); + } +} diff --git a/src/Core/Logger/Type/README.md b/src/Core/Logger/Type/README.md deleted file mode 100644 index b204353c0..000000000 --- a/src/Core/Logger/Type/README.md +++ /dev/null @@ -1,26 +0,0 @@ -## Friendica\Util\Logger - -This namespace contains the different implementations of a Logger. - -### Configuration guideline - -The following settings are possible for `logger_config`: -- [`stream`](StreamLogger.php): A small logger for files or streams -- [`syslog`](SyslogLogger.php): Prints the logging output into the syslog - -[`VoidLogger`](VoidLogger.php) is a fallback logger without any function if no debugging is enabled. - -[`ProfilerLogger`](ProfilerLogger.php) is a wrapper around an existing logger in case profiling is enabled for Friendica. -Every log call will be saved to the `Profiler` with a timestamp. - -### Implementation guideline - -Each logging implementation should pe capable of printing at least the following information: -- An unique ID for each Request/Call -- The process ID (PID) -- A timestamp of the logging entry -- The critically of the log entry -- A log message -- A context of the log message (f.e which user) - -If possible, a Logger should extend [`AbstractLogger`](AbstractLogger.php), because it contains additional, Friendica specific business logic for each logging call. diff --git a/src/Core/Logger/Type/StreamLogger.php b/src/Core/Logger/Type/StreamLogger.php index d56f5b20e..87f1a3937 100644 --- a/src/Core/Logger/Type/StreamLogger.php +++ b/src/Core/Logger/Type/StreamLogger.php @@ -21,20 +21,16 @@ namespace Friendica\Core\Logger\Type; -use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Hooks\Capabilities\IAmAStrategy; use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections; -use Friendica\Core\Logger\Exception\LoggerArgumentException; use Friendica\Core\Logger\Exception\LoggerException; use Friendica\Core\Logger\Exception\LogLevelException; use Friendica\Util\DateTimeFormat; -use Friendica\Util\FileSystem; use Psr\Log\LogLevel; /** * A Logger instance for logging into a stream (file, stdout, stderr) */ -class StreamLogger extends AbstractLogger implements IAmAStrategy +class StreamLogger extends AbstractLogger { /** * The minimum loglevel at which this logger will be triggered @@ -42,12 +38,6 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy */ private $logLevel; - /** - * The file URL of the stream (if needed) - * @var string - */ - private $url; - /** * The stream, where the current logger is writing into * @var resource @@ -60,16 +50,11 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy */ private $pid; - /** - * @var FileSystem - */ - private $fileSystem; - /** * Translates LogLevel log levels to integer values * @var array */ - private $levelToInt = [ + public const levelToInt = [ LogLevel::EMERGENCY => 0, LogLevel::ALERT => 1, LogLevel::CRITICAL => 2, @@ -84,41 +69,20 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy * {@inheritdoc} * @param string $level The minimum loglevel at which this logger will be triggered * - * @throws LoggerArgumentException - * @throws LogLevelException + * @throws LoggerException */ - public function __construct(string $channel, IManageConfigValues $config, IHaveCallIntrospections $introspection, FileSystem $fileSystem, string $level = LogLevel::DEBUG) + public function __construct(string $channel, IHaveCallIntrospections $introspection, $stream, int $logLevel, int $pid) { - $this->fileSystem = $fileSystem; - - $stream = $this->logfile ?? $config->get('system', 'logfile'); - if ((@file_exists($stream) && !@is_writable($stream)) && !@is_writable(basename($stream))) { - throw new LoggerArgumentException(sprintf('%s is not a valid logfile', $stream)); - } - parent::__construct($channel, $introspection); - if (is_resource($stream)) { - $this->stream = $stream; - } elseif (is_string($stream)) { - $this->url = $stream; - } else { - throw new LoggerArgumentException('A stream must either be a resource or a string.'); - } - - $this->pid = getmypid(); - if (array_key_exists($level, $this->levelToInt)) { - $this->logLevel = $this->levelToInt[$level]; - } else { - throw new LogLevelException(sprintf('The level "%s" is not valid.', $level)); - } - - $this->checkStream(); + $this->stream = $stream; + $this->pid = $pid; + $this->logLevel = $logLevel; } public function close() { - if ($this->url && is_resource($this->stream)) { + if (is_resource($this->stream)) { fclose($this->stream); } @@ -139,18 +103,16 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy */ protected function addEntry($level, string $message, array $context = []) { - if (!array_key_exists($level, $this->levelToInt)) { + if (!array_key_exists($level, static::levelToInt)) { throw new LogLevelException(sprintf('The level "%s" is not valid.', $level)); } - $logLevel = $this->levelToInt[$level]; + $logLevel = static::levelToInt[$level]; if ($logLevel > $this->logLevel) { return; } - $this->checkStream(); - $formattedLog = $this->formatLog($level, $message, $context); fwrite($this->stream, $formattedLog); } @@ -185,27 +147,4 @@ class StreamLogger extends AbstractLogger implements IAmAStrategy return $logMessage; } - - /** - * Checks the current stream - * - * @throws LoggerException - * @throws LoggerArgumentException - */ - private function checkStream() - { - if (is_resource($this->stream)) { - return; - } - - if (empty($this->url)) { - throw new LoggerArgumentException('Missing stream URL.'); - } - - try { - $this->stream = $this->fileSystem->createStream($this->url); - } catch (\UnexpectedValueException $exception) { - throw new LoggerException('Cannot create stream.', $exception); - } - } } diff --git a/src/Core/Logger/Type/SyslogLogger.php b/src/Core/Logger/Type/SyslogLogger.php index 3c9ab581a..88dc1964d 100644 --- a/src/Core/Logger/Type/SyslogLogger.php +++ b/src/Core/Logger/Type/SyslogLogger.php @@ -21,8 +21,6 @@ namespace Friendica\Core\Logger\Type; -use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Hooks\Capabilities\IAmAStrategy; use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections; use Friendica\Core\Logger\Exception\LoggerException; use Friendica\Core\Logger\Exception\LogLevelException; @@ -32,7 +30,7 @@ use Psr\Log\LogLevel; * A Logger instance for syslogging (fast, but simple) * @see http://php.net/manual/en/function.syslog.php */ -class SyslogLogger extends AbstractLogger implements IAmAStrategy +class SyslogLogger extends AbstractLogger { const IDENT = 'Friendica'; @@ -45,7 +43,7 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy * Translates LogLevel log levels to syslog log priorities. * @var array */ - private $logLevels = [ + public const logLevels = [ LogLevel::DEBUG => LOG_DEBUG, LogLevel::INFO => LOG_INFO, LogLevel::NOTICE => LOG_NOTICE, @@ -60,7 +58,7 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy * Translates log priorities to string outputs * @var array */ - private $logToString = [ + protected const logToString = [ LOG_DEBUG => 'DEBUG', LOG_INFO => 'INFO', LOG_NOTICE => 'NOTICE', @@ -101,19 +99,18 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy /** * {@inheritdoc} - * @param string $level The minimum loglevel at which this logger will be triggered * - * @throws LogLevelException - * @throws LoggerException + * @param string $logLevel The minimum loglevel at which this logger will be triggered + * @param string $logOptions + * @param string $logFacility */ - public function __construct(string $channel, IManageConfigValues $config, IHaveCallIntrospections $introspection, string $level = LogLevel::NOTICE) + public function __construct(string $channel, IHaveCallIntrospections $introspection, string $logLevel, string $logOptions, string $logFacility) { parent::__construct($channel, $introspection); - $this->logOpts = $config->get('system', 'syslog_flags') ?? static::DEFAULT_FLAGS; - $this->logFacility = $config->get('system', 'syslog_facility') ?? static::DEFAULT_FACILITY; - $this->logLevel = $this->mapLevelToPriority($level); - $this->introspection->addClasses([self::class]); + $this->logOpts = $logOptions; + $this->logFacility = $logFacility; + $this->logLevel = $logLevel; } /** @@ -149,11 +146,11 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy */ public function mapLevelToPriority(string $level): int { - if (!array_key_exists($level, $this->logLevels)) { + if (!array_key_exists($level, static::logLevels)) { throw new LogLevelException(sprintf('The level "%s" is not valid.', $level)); } - return $this->logLevels[$level]; + return static::logLevels[$level]; } /** @@ -202,7 +199,7 @@ class SyslogLogger extends AbstractLogger implements IAmAStrategy $record = array_merge($record, ['uid' => $this->logUid]); $logMessage = $this->channel . ' '; - $logMessage .= '[' . $this->logToString[$level] . ']: '; + $logMessage .= '[' . static::logToString[$level] . ']: '; $logMessage .= $this->psrInterpolate($message, $context) . ' '; $logMessage .= $this->jsonEncodeArray($context) . ' - '; $logMessage .= $this->jsonEncodeArray($record); diff --git a/src/Util/FileSystem.php b/src/Core/Logger/Util/FileSystem.php similarity index 94% rename from src/Util/FileSystem.php rename to src/Core/Logger/Util/FileSystem.php index a21e7fb60..3793ac4a9 100644 --- a/src/Util/FileSystem.php +++ b/src/Core/Logger/Util/FileSystem.php @@ -19,10 +19,10 @@ * */ -namespace Friendica\Util; +namespace Friendica\Core\Logger\Util; /** - * Util class for filesystem manipulation + * Util class for filesystem manipulation for Logger classes */ class FileSystem { @@ -38,7 +38,7 @@ class FileSystem * * @return string The directory name (empty if no directory is found, like urls) */ - public function createDir(string $file) + public function createDir(string $file): string { $dirname = null; $pos = strpos($file, '://'); diff --git a/src/Core/Logger/Util/LoggerSettingsCheck.php b/src/Core/Logger/Util/LoggerSettingsCheck.php new file mode 100644 index 000000000..7782216da --- /dev/null +++ b/src/Core/Logger/Util/LoggerSettingsCheck.php @@ -0,0 +1,91 @@ +. + * + */ + +namespace Friendica\Core\Logger\Util; + +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\L10n; +use Friendica\Core\Logger\Capabilities\ICheckLoggerSettings; +use Friendica\Core\Logger\Exception\LoggerUnusableException; + +/** {@inheritDoc} */ +class LoggerSettingsCheck implements ICheckLoggerSettings +{ + /** @var IManageConfigValues */ + protected $config; + /** @var $fileSystem */ + protected $fileSystem; + /** @var L10n */ + protected $l10n; + + public function __construct(IManageConfigValues $config, FileSystem $fileSystem, L10n $l10n) + { + $this->config = $config; + $this->fileSystem = $fileSystem; + $this->l10n = $l10n; + } + + /** {@inheritDoc} */ + public function checkLogfile(): ?string + { + // Check logfile permission + if ($this->config->get('system', 'debugging')) { + $file = $this->config->get('system', 'logfile'); + + try { + $stream = $this->fileSystem->createStream($file); + + if (!isset($stream)) { + throw new LoggerUnusableException('Stream is null.'); + } + } catch (\Throwable $exception) { + return $this->l10n->t('The logfile \'%s\' is not usable. No logging possible (error: \'%s\')', $file, $exception->getMessage()); + } + } + + return null; + } + + /** {@inheritDoc} */ + public function checkDebugLogfile(): ?string + { + // Check logfile permission + if ($this->config->get('system', 'debugging')) { + $file = $this->config->get('system', 'dlogfile'); + + if (empty($file)) { + return null; + } + + try { + $stream = $this->fileSystem->createStream($file); + + if (!isset($stream)) { + throw new LoggerUnusableException('Stream is null.'); + } + } catch (\Throwable $exception) { + return $this->l10n->t('The debug logfile \'%s\' is not usable. No logging possible (error: \'%s\')', $file, $exception->getMessage()); + } + } + + return null; + } +} diff --git a/src/DI.php b/src/DI.php index 8d706ed37..ad8745622 100644 --- a/src/DI.php +++ b/src/DI.php @@ -22,10 +22,12 @@ namespace Friendica; use Dice\Dice; -use Friendica\Core\Session\Capability\IHandleSessions; -use Friendica\Core\Session\Capability\IHandleUserSessions; -use Friendica\Navigation\SystemMessages; -use Psr\Log\LoggerInterface; +use \Friendica\Core\Logger\Capabilities\ICheckLoggerSettings; +use \Friendica\Core\Logger\Util\LoggerSettingsCheck; +use \Friendica\Core\Session\Capability\IHandleSessions; +use \Friendica\Core\Session\Capability\IHandleUserSessions; +use \Friendica\Navigation\SystemMessages; +use \Psr\Log\LoggerInterface; /** * This class is capable of getting all dynamic created classes @@ -295,6 +297,11 @@ abstract class DI static::init($flushDice); } + public static function loggCheck(): ICheckLoggerSettings + { + return self::$dice->create(LoggerSettingsCheck::class); + } + /** * @return LoggerInterface */ @@ -692,14 +699,6 @@ abstract class DI return self::$dice->create(Util\DateTimeFormat::class); } - /** - * @return Util\FileSystem - */ - public static function fs() - { - return self::$dice->create(Util\FileSystem::class); - } - /** * @return Util\Profiler */ diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index d872f4825..80437305d 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -79,7 +79,7 @@ class Summary extends BaseAdmin // Check if github.com/friendica/stable/VERSION is higher then // the local version of Friendica. Check is opt-in, source may be stable or develop branch if (DI::config()->get('system', 'check_new_version_url', 'none') != 'none') { - $gitversion = DI::keyValue()->get('git_friendica_version') ?? ''; + $gitversion = DI::keyValue()->get('git_friendica_version') ?? ''; if (version_compare(App::VERSION, $gitversion) < 0) { $warningtext[] = DI::l10n()->t('There is a new version of Friendica available for download. Your current version is %1$s, upstream version is %2$s', App::VERSION, $gitversion); @@ -126,35 +126,11 @@ class Summary extends BaseAdmin } // Check logfile permission - if (DI::config()->get('system', 'debugging')) { - $file = DI::config()->get('system', 'logfile'); - - $fileSystem = DI::fs(); - - try { - $stream = $fileSystem->createStream($file); - - if (!isset($stream)) { - throw new ServiceUnavailableException('Stream is null.'); - } - - } catch (\Throwable $exception) { - $warningtext[] = DI::l10n()->t('The logfile \'%s\' is not usable. No logging possible (error: \'%s\')', $file, $exception->getMessage()); - } - - $file = DI::config()->get('system', 'dlogfile'); - - try { - if (!empty($file)) { - $stream = $fileSystem->createStream($file); - - if (!isset($stream)) { - throw new ServiceUnavailableException('Stream is null.'); - } - } - } catch (\Throwable $exception) { - $warningtext[] = DI::l10n()->t('The debug logfile \'%s\' is not usable. No logging possible (error: \'%s\')', $file, $exception->getMessage()); - } + if (($return = DI::loggCheck()->checkLogfile()) !== null) { + $warningtext[] = $return; + } + if (($return = DI::loggCheck()->checkDebugLogfile()) !== null) { + $warningtext[] = $return; } // check legacy basepath settings diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 089116642..2b246f845 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -37,8 +37,9 @@ use Dice\Dice; use Friendica\App; use Friendica\Core\Cache; use Friendica\Core\Config; -use Friendica\Core\Hooks\Capabilities\ICanManageInstances; -use Friendica\Core\Hooks\Model\InstanceManager; +use Friendica\Core\Hooks\Capabilities\ICanCreateInstances; +use Friendica\Core\Hooks\Capabilities\ICanRegisterInstances; +use Friendica\Core\Hooks\Model\DiceInstanceManager; use Friendica\Core\PConfig; use Friendica\Core\L10n; use Friendica\Core\Lock; @@ -62,6 +63,13 @@ return [ // one instance for the whole execution 'shared' => true, ], + \Friendica\Core\Addon\Capabilities\ICanLoadAddons::class => [ + 'instanceOf' => \Friendica\Core\Addon\Model\AddonLoader::class, + 'constructParams' => [ + [Dice::INSTANCE => '$basepath'], + [Dice::INSTANCE => Dice::SELF], + ], + ], '$basepath' => [ 'instanceOf' => Util\BasePath::class, 'call' => [ @@ -78,8 +86,24 @@ return [ $_SERVER ] ], - ICanManageInstances::class => [ - 'instanceOf' => InstanceManager::class, + DiceInstanceManager::class => [ + 'constructParams' => [ + [Dice::INSTANCE => Dice::SELF], + ] + ], + \Friendica\Core\Hooks\Util\HookFileManager::class => [ + 'constructParams' => [ + [Dice::INSTANCE => '$basepath'], + ], + ], + ICanRegisterInstances::class => [ + 'instanceOf' => DiceInstanceManager::class, + 'constructParams' => [ + [Dice::INSTANCE => Dice::SELF], + ], + ], + ICanCreateInstances::class => [ + 'instanceOf' => DiceInstanceManager::class, 'constructParams' => [ [Dice::INSTANCE => Dice::SELF], ], @@ -156,40 +180,40 @@ return [ [Dice::INSTANCE => '$basepath'], ], ], - /** - * Create a Logger, which implements the LoggerInterface - * - * Same as: - * $loggerFactory = new Factory\LoggerFactory(); - * $logger = $loggerFactory->create($channel, $configuration, $profiler); - * - * Attention1: We can use DICE for detecting dependencies inside "chained" calls too - * Attention2: The variable "$channel" is passed inside the creation of the dependencies per: - * $app = $dice->create(App::class, [], ['$channel' => 'index']); - * and is automatically passed as an argument with the same name - */ - LoggerInterface::class => [ + \Psr\Log\LoggerInterface::class => [ 'instanceOf' => \Friendica\Core\Logger\Factory\Logger::class, - 'constructParams' => [ - 'index', - ], 'call' => [ ['create', [], Dice::CHAIN_CALL], ], ], - '$devLogger' => [ - 'instanceOf' => \Friendica\Core\Logger\Factory\Logger::class, - 'constructParams' => [ - 'dev', - ], + \Friendica\Core\Logger\Type\SyslogLogger::class => [ + 'instanceOf' => \Friendica\Core\Logger\Factory\SyslogLogger::class, 'call' => [ - ['createDev', [], Dice::CHAIN_CALL], - ] + ['create', [], Dice::CHAIN_CALL], + ], + ], + \Friendica\Core\Logger\Type\StreamLogger::class => [ + 'instanceOf' => \Friendica\Core\Logger\Factory\StreamLogger::class, + 'call' => [ + ['create', [], Dice::CHAIN_CALL], + ], + ], + \Friendica\Core\Logger\Type\ProfilerLogger::class => [ + 'instanceOf' => \Friendica\Core\Logger\Factory\ProfilerLogger::class, + 'call' => [ + ['create', [], Dice::CHAIN_CALL], + ], ], \Friendica\Core\Logger\Capabilities\IHaveCallIntrospections::class => [ - 'instanceOf' => \Friendica\Core\Logger\Util\Introspection::class, + 'instanceOf' => \Friendica\Core\Logger\Util\Introspection::class, 'constructParams' => [ - \Friendica\Core\Logger\Util\Introspection::IGNORE_CLASS_LIST, + \Friendica\Core\Logger\Capabilities\IHaveCallIntrospections::IGNORE_CLASS_LIST, + ], + ], + '$devLogger' => [ + 'instanceOf' => \Friendica\Core\Logger\Factory\StreamLogger::class, + 'call' => [ + ['createDev', [], Dice::CHAIN_CALL], ], ], Cache\Capability\ICanCache::class => [ diff --git a/static/hooks.config.php b/static/hooks.config.php new file mode 100644 index 000000000..ca8863e0f --- /dev/null +++ b/static/hooks.config.php @@ -0,0 +1,37 @@ +. + * + */ + +use Friendica\Core\Hooks\Capabilities\HookType as H; + +return [ + H::STRATEGY => [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + \Friendica\Core\Logger\Type\SyslogLogger::class => ['syslog'], + \Friendica\Core\Logger\Type\StreamLogger::class => ['stream'], + ], + ], + H::DECORATOR => [ + \Psr\Log\LoggerInterface::class => [ + \Friendica\Core\Logger\Type\ProfilerLogger::class, + ], + ], +]; diff --git a/tests/Util/Hooks/InstanceMocks/FakeInstance.php b/tests/Util/Hooks/InstanceMocks/FakeInstance.php index ff99002f7..9a0e9dd6f 100644 --- a/tests/Util/Hooks/InstanceMocks/FakeInstance.php +++ b/tests/Util/Hooks/InstanceMocks/FakeInstance.php @@ -21,9 +21,7 @@ namespace Friendica\Test\Util\Hooks\InstanceMocks; -use Friendica\Core\Hooks\Capabilities\IAmAStrategy; - -class FakeInstance implements IAmADecoratedInterface, IAmAStrategy +class FakeInstance { protected $aText = null; protected $cBool = null; diff --git a/tests/src/Core/Hooks/Model/InstanceManagerTest.php b/tests/src/Core/Hooks/Model/InstanceManagerTest.php index 4e4c0135c..200500d63 100644 --- a/tests/src/Core/Hooks/Model/InstanceManagerTest.php +++ b/tests/src/Core/Hooks/Model/InstanceManagerTest.php @@ -22,7 +22,7 @@ namespace Friendica\Test\src\Core\Hooks\Model; use Dice\Dice; -use Friendica\Core\Hooks\Model\InstanceManager; +use Friendica\Core\Hooks\Model\DiceInstanceManager; use Friendica\Test\MockedTest; use Friendica\Test\Util\Hooks\InstanceMocks\FakeInstance; use Friendica\Test\Util\Hooks\InstanceMocks\FakeInstanceDecorator; @@ -32,12 +32,12 @@ class InstanceManagerTest extends MockedTest { public function testEqualButNotSameInstance() { - $instance = new InstanceManager(new Dice()); + $instance = new DiceInstanceManager(new Dice()); $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class); - $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake'); - $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake'); + $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake'); + $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake'); self::assertEquals($getInstanceA, $getInstanceB); self::assertNotSame($getInstanceA, $getInstanceB); @@ -81,7 +81,7 @@ class InstanceManagerTest extends MockedTest */ public function testInstanceWithConstructorAnonymArgs(string $aString = null, bool $cBool = null, string $bString = null) { - $instance = new InstanceManager(new Dice()); + $instance = new DiceInstanceManager(new Dice()); $args = []; @@ -98,9 +98,9 @@ class InstanceManagerTest extends MockedTest $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args); /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake'); + $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake'); /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake'); + $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake'); self::assertEquals($getInstanceA, $getInstanceB); self::assertNotSame($getInstanceA, $getInstanceB); @@ -117,7 +117,7 @@ class InstanceManagerTest extends MockedTest */ public function testInstanceConstructorAndGetInstanceWithNamedArgs(string $aString = null, bool $cBool = null, string $bString = null) { - $instance = new InstanceManager(new Dice()); + $instance = new DiceInstanceManager(new Dice()); $args = []; @@ -131,9 +131,9 @@ class InstanceManagerTest extends MockedTest $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args); /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); self::assertEquals($getInstanceA, $getInstanceB); self::assertNotSame($getInstanceA, $getInstanceB); @@ -150,7 +150,7 @@ class InstanceManagerTest extends MockedTest */ public function testInstanceWithTwoStrategies(string $aString = null, bool $cBool = null, string $bString = null) { - $instance = new InstanceManager(new Dice()); + $instance = new DiceInstanceManager(new Dice()); $args = []; @@ -165,9 +165,9 @@ class InstanceManagerTest extends MockedTest $instance->registerStrategy(IAmADecoratedInterface::class, 'fake23', FakeInstance::class, $args); /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake23', [$bString]); + $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake23', [$bString]); self::assertEquals($getInstanceA, $getInstanceB); self::assertNotSame($getInstanceA, $getInstanceB); @@ -184,7 +184,7 @@ class InstanceManagerTest extends MockedTest */ public function testDecorator(string $aString = null, bool $cBool = null, string $bString = null) { - $instance = new InstanceManager(new Dice()); + $instance = new DiceInstanceManager(new Dice()); $args = []; @@ -202,9 +202,9 @@ class InstanceManagerTest extends MockedTest $instance->registerDecorator(IAmADecoratedInterface::class, FakeInstanceDecorator::class, [$prefix]); /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake23', [$bString]); + $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake23', [$bString]); self::assertEquals(2, FakeInstanceDecorator::$countInstance); self::assertEquals($getInstanceA, $getInstanceB); @@ -222,7 +222,7 @@ class InstanceManagerTest extends MockedTest */ public function testTwoDecoratorWithPrefix(string $aString = null, bool $cBool = null, string $bString = null) { - $instance = new InstanceManager(new Dice()); + $instance = new DiceInstanceManager(new Dice()); $args = []; @@ -241,9 +241,9 @@ class InstanceManagerTest extends MockedTest $instance->registerDecorator(IAmADecoratedInterface::class, FakeInstanceDecorator::class); /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake23', [$bString]); + $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake23', [$bString]); self::assertEquals(4, FakeInstanceDecorator::$countInstance); self::assertEquals($getInstanceA, $getInstanceB); diff --git a/tests/src/Core/Logger/StreamLoggerTest.php b/tests/src/Core/Logger/StreamLoggerTest.php index 1ddddf4c1..c8fac4939 100644 --- a/tests/src/Core/Logger/StreamLoggerTest.php +++ b/tests/src/Core/Logger/StreamLoggerTest.php @@ -24,7 +24,7 @@ namespace Friendica\Test\src\Core\Logger; use Friendica\Core\Logger\Exception\LoggerArgumentException; use Friendica\Core\Logger\Exception\LoggerException; use Friendica\Core\Logger\Exception\LogLevelException; -use Friendica\Util\FileSystem; +use Friendica\Core\Logger\Util\FileSystem; use Friendica\Test\Util\VFSTrait; use Friendica\Core\Logger\Type\StreamLogger; use org\bovigo\vfs\vfsStream; @@ -41,7 +41,7 @@ class StreamLoggerTest extends AbstractLoggerTest private $logfile; /** - * @var Filesystem + * @var \Friendica\Core\Logger\Util\Filesystem */ private $fileSystem; From 89a31e43b250e3b95266077214edd34ab4f15c7e Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 3 Jul 2023 00:06:17 +0200 Subject: [PATCH 02/24] Fix license check --- src/Core/Addon/Capabilities/ICanLoadAddons.php | 2 +- src/Core/Addon/Model/AddonLoader.php | 2 +- src/Core/Logger/Capabilities/ICheckLoggerSettings.php | 2 +- src/Core/Logger/Capabilities/IHaveCallIntrospections.php | 2 +- src/Core/Logger/Capabilities/LogChannel.php | 2 +- src/Core/Logger/Exception/LogLevelException.php | 2 +- src/Core/Logger/Exception/LoggerArgumentException.php | 2 +- src/Core/Logger/Exception/LoggerException.php | 2 +- src/Core/Logger/Exception/LoggerUnusableException.php | 2 +- src/Core/Logger/Factory/ProfilerLogger.php | 2 +- src/Core/Logger/Factory/StreamLogger.php | 2 +- src/Core/Logger/Factory/SyslogLogger.php | 2 +- static/hooks.config.php | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Core/Addon/Capabilities/ICanLoadAddons.php b/src/Core/Addon/Capabilities/ICanLoadAddons.php index a28826ffb..9c9d1e841 100644 --- a/src/Core/Addon/Capabilities/ICanLoadAddons.php +++ b/src/Core/Addon/Capabilities/ICanLoadAddons.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Addon/Model/AddonLoader.php b/src/Core/Addon/Model/AddonLoader.php index 707601aef..952246af0 100644 --- a/src/Core/Addon/Model/AddonLoader.php +++ b/src/Core/Addon/Model/AddonLoader.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Capabilities/ICheckLoggerSettings.php b/src/Core/Logger/Capabilities/ICheckLoggerSettings.php index 532425ec0..fb93d18e8 100644 --- a/src/Core/Logger/Capabilities/ICheckLoggerSettings.php +++ b/src/Core/Logger/Capabilities/ICheckLoggerSettings.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Capabilities/IHaveCallIntrospections.php b/src/Core/Logger/Capabilities/IHaveCallIntrospections.php index 7f884bf64..83d75f976 100644 --- a/src/Core/Logger/Capabilities/IHaveCallIntrospections.php +++ b/src/Core/Logger/Capabilities/IHaveCallIntrospections.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Capabilities/LogChannel.php b/src/Core/Logger/Capabilities/LogChannel.php index 54ae03217..5fceae42a 100644 --- a/src/Core/Logger/Capabilities/LogChannel.php +++ b/src/Core/Logger/Capabilities/LogChannel.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Exception/LogLevelException.php b/src/Core/Logger/Exception/LogLevelException.php index 39b2d17a9..a79db80f5 100644 --- a/src/Core/Logger/Exception/LogLevelException.php +++ b/src/Core/Logger/Exception/LogLevelException.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Exception/LoggerArgumentException.php b/src/Core/Logger/Exception/LoggerArgumentException.php index 08eabe84a..bbc1fc066 100644 --- a/src/Core/Logger/Exception/LoggerArgumentException.php +++ b/src/Core/Logger/Exception/LoggerArgumentException.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Exception/LoggerException.php b/src/Core/Logger/Exception/LoggerException.php index 13bcff8f3..742557678 100644 --- a/src/Core/Logger/Exception/LoggerException.php +++ b/src/Core/Logger/Exception/LoggerException.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Exception/LoggerUnusableException.php b/src/Core/Logger/Exception/LoggerUnusableException.php index 29336e086..46e49fd41 100644 --- a/src/Core/Logger/Exception/LoggerUnusableException.php +++ b/src/Core/Logger/Exception/LoggerUnusableException.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Factory/ProfilerLogger.php b/src/Core/Logger/Factory/ProfilerLogger.php index 3d126921d..8c07b51b6 100644 --- a/src/Core/Logger/Factory/ProfilerLogger.php +++ b/src/Core/Logger/Factory/ProfilerLogger.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Factory/StreamLogger.php b/src/Core/Logger/Factory/StreamLogger.php index 85475b3af..6da4b8cd8 100644 --- a/src/Core/Logger/Factory/StreamLogger.php +++ b/src/Core/Logger/Factory/StreamLogger.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/src/Core/Logger/Factory/SyslogLogger.php b/src/Core/Logger/Factory/SyslogLogger.php index 5a8789584..bccf4daf5 100644 --- a/src/Core/Logger/Factory/SyslogLogger.php +++ b/src/Core/Logger/Factory/SyslogLogger.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as diff --git a/static/hooks.config.php b/static/hooks.config.php index ca8863e0f..185bc5e48 100644 --- a/static/hooks.config.php +++ b/static/hooks.config.php @@ -2,7 +2,7 @@ /** * @copyright Copyright (C) 2010-2023, the Friendica project * - * @license GNU AGPL version 3 or any later version + * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as From 82f3e4ad869b35068ac17fe8e24c6c02e41689d7 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 3 Jul 2023 00:08:58 +0200 Subject: [PATCH 03/24] Make PHP-CS happy --- src/Core/Hooks/Capabilities/ICanCreateInstances.php | 2 -- src/Core/Hooks/Model/DiceInstanceManager.php | 2 +- src/Core/Logger/Capabilities/LogChannel.php | 2 +- src/Core/Logger/Factory/SyslogLogger.php | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Core/Hooks/Capabilities/ICanCreateInstances.php b/src/Core/Hooks/Capabilities/ICanCreateInstances.php index 6e5d8beb4..971430474 100644 --- a/src/Core/Hooks/Capabilities/ICanCreateInstances.php +++ b/src/Core/Hooks/Capabilities/ICanCreateInstances.php @@ -21,8 +21,6 @@ namespace Friendica\Core\Hooks\Capabilities; -use Friendica\Core\Hooks\Exceptions\HookInstanceException; - /** * creates special instance and decorator treatments for given classes */ diff --git a/src/Core/Hooks/Model/DiceInstanceManager.php b/src/Core/Hooks/Model/DiceInstanceManager.php index 381e0a7fe..efbee444a 100644 --- a/src/Core/Hooks/Model/DiceInstanceManager.php +++ b/src/Core/Hooks/Model/DiceInstanceManager.php @@ -35,7 +35,7 @@ use Friendica\Core\Hooks\Util\HookFileManager; */ class DiceInstanceManager implements ICanCreateInstances, ICanRegisterInstances { - protected $instance = []; + protected $instance = []; protected $decorator = []; /** @var Dice */ diff --git a/src/Core/Logger/Capabilities/LogChannel.php b/src/Core/Logger/Capabilities/LogChannel.php index 5fceae42a..31915168b 100644 --- a/src/Core/Logger/Capabilities/LogChannel.php +++ b/src/Core/Logger/Capabilities/LogChannel.php @@ -29,7 +29,7 @@ interface LogChannel /** @var string channel for the auth_ejabbered script */ public const AUTH_JABBERED = 'auth_ejabberd'; /** @var string Default channel in case it isn't set explicit */ - public const DEFAULT = self::APP; + public const DEFAULT = self::APP; /** @var string channel for console execution */ public const CONSOLE = 'console'; /** @var string channel for developer focused logging */ diff --git a/src/Core/Logger/Factory/SyslogLogger.php b/src/Core/Logger/Factory/SyslogLogger.php index bccf4daf5..385625862 100644 --- a/src/Core/Logger/Factory/SyslogLogger.php +++ b/src/Core/Logger/Factory/SyslogLogger.php @@ -37,7 +37,7 @@ class SyslogLogger extends AbstractLoggerTypeFactory /** * Creates a new PSR-3 compliant syslog logger instance * - * @param IManageConfigValues $config The system configuration + * @param IManageConfigValues $config The system configuration * * @return LoggerInterface The PSR-3 compliant logger instance * @@ -45,7 +45,7 @@ class SyslogLogger extends AbstractLoggerTypeFactory */ public function create(IManageConfigValues $config): LoggerInterface { - $logOpts = $config->get('system', 'syslog_flags') ?? SyslogLoggerClass::DEFAULT_FLAGS; + $logOpts = $config->get('system', 'syslog_flags') ?? SyslogLoggerClass::DEFAULT_FLAGS; $logFacility = $config->get('system', 'syslog_facility') ?? SyslogLoggerClass::DEFAULT_FACILITY; $loglevel = SyslogLogger::mapLegacyConfigDebugLevel($config->get('system', 'loglevel')); From e9699b8e559a6fe5c15b794939536ddbf0e26be5 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 3 Jul 2023 00:12:59 +0200 Subject: [PATCH 04/24] Some more PHP-CS --- src/Core/Hooks/Util/HookFileManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Hooks/Util/HookFileManager.php b/src/Core/Hooks/Util/HookFileManager.php index 357567857..eda7e9388 100644 --- a/src/Core/Hooks/Util/HookFileManager.php +++ b/src/Core/Hooks/Util/HookFileManager.php @@ -31,7 +31,7 @@ use Friendica\Core\Hooks\Exceptions\HookConfigException; */ class HookFileManager { - const STATIC_DIR = 'static'; + const STATIC_DIR = 'static'; const CONFIG_NAME = 'hooks'; /** @var ICanLoadAddons */ From e92d25a258f35172f4bb18809db805bfce146dcc Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 3 Jul 2023 00:17:01 +0200 Subject: [PATCH 05/24] Remove unnecessary trailing slashes --- src/DI.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DI.php b/src/DI.php index ad8745622..c62b5e8d7 100644 --- a/src/DI.php +++ b/src/DI.php @@ -22,12 +22,12 @@ namespace Friendica; use Dice\Dice; -use \Friendica\Core\Logger\Capabilities\ICheckLoggerSettings; -use \Friendica\Core\Logger\Util\LoggerSettingsCheck; -use \Friendica\Core\Session\Capability\IHandleSessions; -use \Friendica\Core\Session\Capability\IHandleUserSessions; -use \Friendica\Navigation\SystemMessages; -use \Psr\Log\LoggerInterface; +use Friendica\Core\Logger\Capabilities\ICheckLoggerSettings; +use Friendica\Core\Logger\Util\LoggerSettingsCheck; +use Friendica\Core\Session\Capability\IHandleSessions; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Navigation\SystemMessages; +use Psr\Log\LoggerInterface; /** * This class is capable of getting all dynamic created classes From 0ac247550bdba80ac0247d25cf61847a7fca093d Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 5 Jul 2023 20:58:29 +0200 Subject: [PATCH 06/24] Doc --- doc/AddonsStrategyDecorator.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 doc/AddonsStrategyDecorator.md diff --git a/doc/AddonsStrategyDecorator.md b/doc/AddonsStrategyDecorator.md new file mode 100644 index 000000000..7e0d53e32 --- /dev/null +++ b/doc/AddonsStrategyDecorator.md @@ -0,0 +1,12 @@ +Friendica strategy and decorator Hooks +=========================================== + +* [Home](help) + +## Strategy hooks + +## Decorator hooks + +## hook.config.php + +## Create new hooks From 7bfc3c0ae01f4c6b7b3e51b5f84452469cb3f04f Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 16 Jul 2023 18:38:34 +0200 Subject: [PATCH 07/24] Add doc --- doc/AddonsStrategyDecorator.md | 151 ++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/doc/AddonsStrategyDecorator.md b/doc/AddonsStrategyDecorator.md index 7e0d53e32..51456da6c 100644 --- a/doc/AddonsStrategyDecorator.md +++ b/doc/AddonsStrategyDecorator.md @@ -5,8 +5,155 @@ Friendica strategy and decorator Hooks ## Strategy hooks +This type of hook is based on the [Strategy Design Pattern](https://refactoring.guru/design-patterns/strategy). + +A strategy class defines a possible implementation of a given interface based on a unique name. +Every name is possible as long as it's unique and not `null`. +Using an empty name (`''`) is possible as well and should be used as the "default" implementation. +To register a strategy, use the [`ICanRegisterInstance`](../src/Core/Hooks/Capabilities/ICanRegisterInstances.php) interface. + +After registration, a caller can automatically create this instance with the [`ICanCreateInstances`](../src/Core/Hooks/Capabilities/ICanCreateInstances.php) interface and the chosen name. + +This is useful in case there are different, possible implementations for the same purpose, like for logging, locking, caching, ... + +Normally, a config entry is used to choose the right implementation at runtime. +And if no config entry is set, the "default" implementation should be used. + +### Example + +```php +interface ExampleInterface +{ + public function testMethod(); +} + +public class ConcreteClassA implements ExampleInterface +{ + public function testMethod() + { + echo "concrete class A"; + } +} + +public class ConcreteClassB implements ExampleInterface +{ + public function testMethod() + { + echo "concrete class B"; + } +} + +/** @var \Friendica\Core\Hooks\Capabilities\ICanRegisterInstances $instanceRegister */ +$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassA::class, 'A'); +$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassB::class, 'B'); + +/** @var \Friendica\Core\Hooks\Capabilities\ICanCreateInstances $instanceManager */ +/** @var ConcreteClassA $concreteClass */ +$concreteClass = $instanceManager->createWithName(ExampleInterface::class, 'A'); + +$concreteClass->testMethod(); +// output: +// "concrete class A"; +``` + ## Decorator hooks -## hook.config.php +This type of hook is based on the [Decorator Design Pattern](https://refactoring.guru/design-patterns/decorator). -## Create new hooks +A decorator class extends a given strategy instance (see [Strategy hooks](#strategy-hooks)]). +To register a decorator, use the [`ICanRegisterInstance`](../src/Core/Hooks/Capabilities/ICanRegisterInstances.php) interface. + +After registration, a caller can automatically create an instance with the [`ICanCreateInstances`](../src/Core/Hooks/Capabilities/ICanCreateInstances.php) interface and the decorator will wrap its logic around the call. + +This is useful in case you want to extend a given class but the given class isn't responsible for these business logic. Or you want to extend an interface without knowing the concrete implementation. +For example profiling logger calls, Friendica is using a [`ProfilerLogger`](../src/Core/Logger/Type/ProfilerLogger.php), which wraps all other logging implementations and traces each log call. + +Normally, a config entry is used to enable/disable decorator. + +### Example + +```php +interface ExampleInterface +{ + public function testMethod(); +} + +public class ConcreteClassA implements ExampleInterface +{ + public function testMethod() + { + echo "concrete class A"; + } +} + +public class DecoratorClassA implements ExampleInterface +{ + /** @var ExampleInterface */ + protected $example; + + public function __construct(ExampleInterface $example) + { + $this->example = $example; + } + + public function testMethod() + { + echo "decorated!\n"; + $this->example->testMethod(); + } +} + +/** @var \Friendica\Core\Hooks\Capabilities\ICanRegisterInstances $instanceRegister */ +$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassA::class, 'A'); +$instanceRegister->registerDecorator(ExampleInterface::class, DecoratorClassA::class); + +/** @var \Friendica\Core\Hooks\Capabilities\ICanCreateInstances $instanceManager */ +/** @var ConcreteClassA $concreteClass */ +$concreteClass = $instanceManager->createWithName(ExampleInterface::class, 'A'); + +$concreteClass->testMethod(); +// output: +// "decorated!" +// "concrete class A"; +``` + +## hooks.config.php + +To avoid registering all strategies and decorators manually inside the code, Friendica introduced the [`hooks.config.php`](../static/hooks.config.php) file. + +There, you can register all kind of strategies and decorators in one file. + +### [`HookType::STRATEGY`](../src/Core/Hooks/Capabilities/HookType.php) + +For each given interface, a list of key-value pairs can be set, where the key is the concrete implementation class and the value is an array of unique names. + +### [`HookType::DECORATOR`](../src/Core/Hooks/Capabilities/HookType.php) + +For each given interface, a list of concrete decorator classes can be set. + +### Example + +```php +use Friendica\Core\Hooks\Capabilities\HookType as H; + +return [ + H::STRATEGY => [ + ExampleInterface::class => [ + ConcreteClassA::class => ['A'], + ConcreteClassB::class => ['B'], + ], + ], + H::DECORATOR => [ + ExampleInterface::class => [ + DecoratorClassA::class, + ], + ], +]; +``` + +## Addons + +The hook logic is useful for decoupling the Friendica core logic, but its primary goal is to modularize Friendica in creating addons. + +Therefor you can either use the interfaces directly as shown above, or you can place your own `hooks.config.php` file inside a `static` directory directly under your addon core directory. +Friendica will automatically search these config files for each **activated** addon and register the given hooks. From ddeef9387fc77830815e5d4a38532e78845f7040 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 16 Jul 2023 18:38:41 +0200 Subject: [PATCH 08/24] improve hooks.config.php --- static/hooks.config.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/static/hooks.config.php b/static/hooks.config.php index 185bc5e48..bc235913c 100644 --- a/static/hooks.config.php +++ b/static/hooks.config.php @@ -20,18 +20,20 @@ */ use Friendica\Core\Hooks\Capabilities\HookType as H; +use Friendica\Core\Logger\Type; +use Psr\Log; return [ H::STRATEGY => [ - \Psr\Log\LoggerInterface::class => [ - \Psr\Log\NullLogger::class => [''], - \Friendica\Core\Logger\Type\SyslogLogger::class => ['syslog'], - \Friendica\Core\Logger\Type\StreamLogger::class => ['stream'], + Log\LoggerInterface::class => [ + Log\NullLogger::class => [''], + Type\SyslogLogger::class => ['syslog'], + Type\StreamLogger::class => ['stream'], ], ], H::DECORATOR => [ - \Psr\Log\LoggerInterface::class => [ - \Friendica\Core\Logger\Type\ProfilerLogger::class, + Log\LoggerInterface::class => [ + Type\ProfilerLogger::class, ], ], ]; From 1b4eaa6a1000fe7e3f7df44ea8345319144c3ded Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 16 Jul 2023 18:40:38 +0200 Subject: [PATCH 09/24] Update messages.po --- view/lang/C/messages.po | 314 ++++++++++++++++++++-------------------- 1 file changed, 157 insertions(+), 157 deletions(-) diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index 445ffcbd3..a017e59dc 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2023.09-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-07-09 18:36-0400\n" +"POT-Creation-Date: 2023-07-16 16:40+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -292,9 +292,9 @@ msgid "Insert web link" msgstr "" #: mod/message.php:201 mod/message.php:357 mod/photos.php:1301 -#: src/Content/Conversation.php:392 src/Content/Conversation.php:1506 +#: src/Content/Conversation.php:392 src/Content/Conversation.php:1508 #: src/Module/Item/Compose.php:206 src/Module/Post/Edit.php:145 -#: src/Module/Profile/UnkMail.php:154 src/Object/Post.php:574 +#: src/Module/Profile/UnkMail.php:154 src/Object/Post.php:571 msgid "Please wait" msgstr "" @@ -315,7 +315,7 @@ msgstr "" #: src/Module/Moderation/Report/Create.php:211 #: src/Module/Moderation/Report/Create.php:263 #: src/Module/Profile/Profile.php:274 src/Module/Profile/UnkMail.php:155 -#: src/Module/Settings/Profile/Index.php:230 src/Object/Post.php:1090 +#: src/Module/Settings/Profile/Index.php:230 src/Object/Post.php:1087 #: view/theme/duepuntozero/config.php:85 view/theme/frio/config.php:171 #: view/theme/quattro/config.php:87 view/theme/vier/config.php:135 msgid "Submit" @@ -600,33 +600,33 @@ msgstr "" #: mod/photos.php:1139 mod/photos.php:1195 mod/photos.php:1275 #: src/Module/Contact.php:619 src/Module/Item/Compose.php:188 -#: src/Object/Post.php:1087 +#: src/Object/Post.php:1084 msgid "This is you" msgstr "" #: mod/photos.php:1141 mod/photos.php:1197 mod/photos.php:1277 -#: src/Object/Post.php:568 src/Object/Post.php:1089 +#: src/Object/Post.php:565 src/Object/Post.php:1086 msgid "Comment" msgstr "" #: mod/photos.php:1143 mod/photos.php:1199 mod/photos.php:1279 #: src/Content/Conversation.php:407 src/Module/Calendar/Event/Form.php:248 #: src/Module/Item/Compose.php:201 src/Module/Post/Edit.php:165 -#: src/Object/Post.php:1103 +#: src/Object/Post.php:1100 msgid "Preview" msgstr "" #: mod/photos.php:1144 src/Content/Conversation.php:360 -#: src/Module/Post/Edit.php:130 src/Object/Post.php:1091 +#: src/Module/Post/Edit.php:130 src/Object/Post.php:1088 msgid "Loading..." msgstr "" -#: mod/photos.php:1236 src/Content/Conversation.php:1422 -#: src/Object/Post.php:263 +#: mod/photos.php:1236 src/Content/Conversation.php:1424 +#: src/Object/Post.php:260 msgid "Select" msgstr "" -#: mod/photos.php:1237 src/Content/Conversation.php:1423 +#: mod/photos.php:1237 src/Content/Conversation.php:1425 #: src/Module/Moderation/Users/Active.php:136 #: src/Module/Moderation/Users/Blocked.php:136 #: src/Module/Moderation/Users/Index.php:151 @@ -634,19 +634,19 @@ msgstr "" msgid "Delete" msgstr "" -#: mod/photos.php:1298 src/Object/Post.php:405 +#: mod/photos.php:1298 src/Object/Post.php:402 msgid "Like" msgstr "" -#: mod/photos.php:1299 src/Object/Post.php:405 +#: mod/photos.php:1299 src/Object/Post.php:402 msgid "I like this (toggle)" msgstr "" -#: mod/photos.php:1300 src/Object/Post.php:406 +#: mod/photos.php:1300 src/Object/Post.php:403 msgid "Dislike" msgstr "" -#: mod/photos.php:1302 src/Object/Post.php:406 +#: mod/photos.php:1302 src/Object/Post.php:403 msgid "I don't like this (toggle)" msgstr "" @@ -662,97 +662,97 @@ msgstr "" msgid "Apologies but the website is unavailable at the moment." msgstr "" -#: src/App/Page.php:247 +#: src/App/Page.php:248 msgid "Delete this item?" msgstr "" -#: src/App/Page.php:248 +#: src/App/Page.php:249 msgid "" "Block this author? They won't be able to follow you nor see your public " "posts, and you won't be able to see their posts and their notifications." msgstr "" -#: src/App/Page.php:249 +#: src/App/Page.php:250 msgid "" "Ignore this author? You won't be able to see their posts and their " "notifications." msgstr "" -#: src/App/Page.php:250 +#: src/App/Page.php:251 msgid "Collapse this author's posts?" msgstr "" -#: src/App/Page.php:252 +#: src/App/Page.php:253 msgid "Like not successful" msgstr "" -#: src/App/Page.php:253 +#: src/App/Page.php:254 msgid "Dislike not successful" msgstr "" -#: src/App/Page.php:254 +#: src/App/Page.php:255 msgid "Sharing not successful" msgstr "" -#: src/App/Page.php:255 +#: src/App/Page.php:256 msgid "Attendance unsuccessful" msgstr "" -#: src/App/Page.php:256 +#: src/App/Page.php:257 msgid "Backend error" msgstr "" -#: src/App/Page.php:257 +#: src/App/Page.php:258 msgid "Network error" msgstr "" -#: src/App/Page.php:260 +#: src/App/Page.php:261 msgid "Drop files here to upload" msgstr "" -#: src/App/Page.php:261 +#: src/App/Page.php:262 msgid "Your browser does not support drag and drop file uploads." msgstr "" -#: src/App/Page.php:262 +#: src/App/Page.php:263 msgid "" "Please use the fallback form below to upload your files like in the olden " "days." msgstr "" -#: src/App/Page.php:263 +#: src/App/Page.php:264 msgid "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB." msgstr "" -#: src/App/Page.php:264 +#: src/App/Page.php:265 msgid "You can't upload files of this type." msgstr "" -#: src/App/Page.php:265 +#: src/App/Page.php:266 msgid "Server responded with {{statusCode}} code." msgstr "" -#: src/App/Page.php:266 +#: src/App/Page.php:267 msgid "Cancel upload" msgstr "" -#: src/App/Page.php:267 +#: src/App/Page.php:268 msgid "Upload canceled." msgstr "" -#: src/App/Page.php:268 +#: src/App/Page.php:269 msgid "Are you sure you want to cancel this upload?" msgstr "" -#: src/App/Page.php:269 +#: src/App/Page.php:270 msgid "Remove file" msgstr "" -#: src/App/Page.php:270 +#: src/App/Page.php:271 msgid "You can't upload any more files." msgstr "" -#: src/App/Page.php:348 +#: src/App/Page.php:349 msgid "toggle mobile" msgstr "" @@ -769,31 +769,31 @@ msgstr "" msgid "You must be logged in to use addons. " msgstr "" -#: src/BaseModule.php:400 +#: src/BaseModule.php:401 msgid "" "The form security token was not correct. This probably happened because the " "form has been opened for too long (>3 hours) before submitting it." msgstr "" -#: src/BaseModule.php:427 +#: src/BaseModule.php:428 msgid "All contacts" msgstr "" -#: src/BaseModule.php:432 src/Content/Widget.php:243 src/Core/ACL.php:195 +#: src/BaseModule.php:433 src/Content/Widget.php:243 src/Core/ACL.php:195 #: src/Module/Contact.php:415 src/Module/PermissionTooltip.php:127 #: src/Module/PermissionTooltip.php:149 msgid "Followers" msgstr "" -#: src/BaseModule.php:437 src/Content/Widget.php:244 src/Module/Contact.php:418 +#: src/BaseModule.php:438 src/Content/Widget.php:244 src/Module/Contact.php:418 msgid "Following" msgstr "" -#: src/BaseModule.php:442 src/Content/Widget.php:245 src/Module/Contact.php:421 +#: src/BaseModule.php:443 src/Content/Widget.php:245 src/Module/Contact.php:421 msgid "Mutual friends" msgstr "" -#: src/BaseModule.php:450 +#: src/BaseModule.php:451 msgid "Common" msgstr "" @@ -1226,7 +1226,7 @@ msgid "Visible to everybody" msgstr "" #: src/Content/Conversation.php:330 src/Module/Item/Compose.php:200 -#: src/Object/Post.php:1102 +#: src/Object/Post.php:1099 msgid "Please enter a image/video/audio/webpage URL:" msgstr "" @@ -1271,52 +1271,52 @@ msgid "attach file" msgstr "" #: src/Content/Conversation.php:365 src/Module/Item/Compose.php:190 -#: src/Module/Post/Edit.php:171 src/Object/Post.php:1092 +#: src/Module/Post/Edit.php:171 src/Object/Post.php:1089 msgid "Bold" msgstr "" #: src/Content/Conversation.php:366 src/Module/Item/Compose.php:191 -#: src/Module/Post/Edit.php:172 src/Object/Post.php:1093 +#: src/Module/Post/Edit.php:172 src/Object/Post.php:1090 msgid "Italic" msgstr "" #: src/Content/Conversation.php:367 src/Module/Item/Compose.php:192 -#: src/Module/Post/Edit.php:173 src/Object/Post.php:1094 +#: src/Module/Post/Edit.php:173 src/Object/Post.php:1091 msgid "Underline" msgstr "" #: src/Content/Conversation.php:368 src/Module/Item/Compose.php:193 -#: src/Module/Post/Edit.php:174 src/Object/Post.php:1096 +#: src/Module/Post/Edit.php:174 src/Object/Post.php:1093 msgid "Quote" msgstr "" #: src/Content/Conversation.php:369 src/Module/Item/Compose.php:194 -#: src/Module/Post/Edit.php:175 src/Object/Post.php:1097 +#: src/Module/Post/Edit.php:175 src/Object/Post.php:1094 msgid "Add emojis" msgstr "" #: src/Content/Conversation.php:370 src/Module/Item/Compose.php:195 -#: src/Object/Post.php:1095 +#: src/Object/Post.php:1092 msgid "Content Warning" msgstr "" #: src/Content/Conversation.php:371 src/Module/Item/Compose.php:196 -#: src/Module/Post/Edit.php:176 src/Object/Post.php:1098 +#: src/Module/Post/Edit.php:176 src/Object/Post.php:1095 msgid "Code" msgstr "" #: src/Content/Conversation.php:372 src/Module/Item/Compose.php:197 -#: src/Object/Post.php:1099 +#: src/Object/Post.php:1096 msgid "Image" msgstr "" #: src/Content/Conversation.php:373 src/Module/Item/Compose.php:198 -#: src/Module/Post/Edit.php:177 src/Object/Post.php:1100 +#: src/Module/Post/Edit.php:177 src/Object/Post.php:1097 msgid "Link" msgstr "" #: src/Content/Conversation.php:374 src/Module/Item/Compose.php:199 -#: src/Module/Post/Edit.php:178 src/Object/Post.php:1101 +#: src/Module/Post/Edit.php:178 src/Object/Post.php:1098 msgid "Link or Media" msgstr "" @@ -1386,111 +1386,111 @@ msgstr "" msgid "Delete Selected Items" msgstr "" -#: src/Content/Conversation.php:724 src/Content/Conversation.php:727 -#: src/Content/Conversation.php:730 src/Content/Conversation.php:733 -#: src/Content/Conversation.php:736 +#: src/Content/Conversation.php:728 src/Content/Conversation.php:731 +#: src/Content/Conversation.php:734 src/Content/Conversation.php:737 +#: src/Content/Conversation.php:740 #, php-format msgid "You had been addressed (%s)." msgstr "" -#: src/Content/Conversation.php:739 +#: src/Content/Conversation.php:743 #, php-format msgid "You are following %s." msgstr "" -#: src/Content/Conversation.php:742 +#: src/Content/Conversation.php:746 msgid "You subscribed to one or more tags in this post." msgstr "" -#: src/Content/Conversation.php:761 +#: src/Content/Conversation.php:765 #, php-format msgid "%s reshared this." msgstr "" -#: src/Content/Conversation.php:763 +#: src/Content/Conversation.php:767 msgid "Reshared" msgstr "" -#: src/Content/Conversation.php:763 +#: src/Content/Conversation.php:767 #, php-format msgid "Reshared by %s <%s>" msgstr "" -#: src/Content/Conversation.php:766 +#: src/Content/Conversation.php:770 #, php-format msgid "%s is participating in this thread." msgstr "" -#: src/Content/Conversation.php:769 +#: src/Content/Conversation.php:773 msgid "Stored for general reasons" msgstr "" -#: src/Content/Conversation.php:772 +#: src/Content/Conversation.php:776 msgid "Global post" msgstr "" -#: src/Content/Conversation.php:775 +#: src/Content/Conversation.php:779 msgid "Sent via an relay server" msgstr "" -#: src/Content/Conversation.php:775 +#: src/Content/Conversation.php:779 #, php-format msgid "Sent via the relay server %s <%s>" msgstr "" -#: src/Content/Conversation.php:778 +#: src/Content/Conversation.php:782 msgid "Fetched" msgstr "" -#: src/Content/Conversation.php:778 +#: src/Content/Conversation.php:782 #, php-format msgid "Fetched because of %s <%s>" msgstr "" -#: src/Content/Conversation.php:781 +#: src/Content/Conversation.php:785 msgid "Stored because of a child post to complete this thread." msgstr "" -#: src/Content/Conversation.php:784 +#: src/Content/Conversation.php:788 msgid "Local delivery" msgstr "" -#: src/Content/Conversation.php:787 +#: src/Content/Conversation.php:791 msgid "Stored because of your activity (like, comment, star, ...)" msgstr "" -#: src/Content/Conversation.php:790 +#: src/Content/Conversation.php:794 msgid "Distributed" msgstr "" -#: src/Content/Conversation.php:793 +#: src/Content/Conversation.php:797 msgid "Pushed to us" msgstr "" -#: src/Content/Conversation.php:1450 src/Object/Post.php:248 +#: src/Content/Conversation.php:1452 src/Object/Post.php:248 msgid "Pinned item" msgstr "" -#: src/Content/Conversation.php:1466 src/Object/Post.php:518 -#: src/Object/Post.php:519 +#: src/Content/Conversation.php:1468 src/Object/Post.php:515 +#: src/Object/Post.php:516 #, php-format msgid "View %s's profile @ %s" msgstr "" -#: src/Content/Conversation.php:1479 src/Object/Post.php:506 +#: src/Content/Conversation.php:1481 src/Object/Post.php:503 msgid "Categories:" msgstr "" -#: src/Content/Conversation.php:1480 src/Object/Post.php:507 +#: src/Content/Conversation.php:1482 src/Object/Post.php:504 msgid "Filed under:" msgstr "" -#: src/Content/Conversation.php:1488 src/Object/Post.php:532 +#: src/Content/Conversation.php:1490 src/Object/Post.php:529 #, php-format msgid "%s from %s" msgstr "" -#: src/Content/Conversation.php:1504 +#: src/Content/Conversation.php:1506 msgid "View in context" msgstr "" @@ -1700,7 +1700,7 @@ msgstr "" msgid "Collapse" msgstr "" -#: src/Content/Item.php:434 src/Object/Post.php:487 +#: src/Content/Item.php:434 src/Object/Post.php:484 msgid "Languages" msgstr "" @@ -2833,6 +2833,16 @@ msgstr "" msgid "Dec" msgstr "" +#: src/Core/Logger/Util/LoggerSettingsCheck.php:60 +#, php-format +msgid "The logfile '%s' is not usable. No logging possible (error: '%s')" +msgstr "" + +#: src/Core/Logger/Util/LoggerSettingsCheck.php:85 +#, php-format +msgid "The debug logfile '%s' is not usable. No logging possible (error: '%s')" +msgstr "" + #: src/Core/Renderer.php:89 src/Core/Renderer.php:118 src/Core/Renderer.php:147 #: src/Core/Renderer.php:181 src/Render/FriendicaSmartyEngine.php:60 msgid "" @@ -3407,7 +3417,7 @@ msgstr "" msgid "Title/Description:" msgstr "" -#: src/Model/Profile.php:1025 src/Module/Admin/Summary.php:221 +#: src/Model/Profile.php:1025 src/Module/Admin/Summary.php:197 #: src/Module/Moderation/Report/Create.php:280 #: src/Module/Moderation/Summary.php:77 msgid "Summary" @@ -3735,7 +3745,7 @@ msgstr "" #: src/Module/Admin/Federation.php:210 src/Module/Admin/Logs/Settings.php:85 #: src/Module/Admin/Logs/View.php:83 src/Module/Admin/Queue.php:72 #: src/Module/Admin/Site.php:398 src/Module/Admin/Storage.php:138 -#: src/Module/Admin/Summary.php:220 src/Module/Admin/Themes/Details.php:90 +#: src/Module/Admin/Summary.php:196 src/Module/Admin/Themes/Details.php:90 #: src/Module/Admin/Themes/Index.php:111 src/Module/Admin/Tos.php:77 #: src/Module/Moderation/Users/Create.php:61 #: src/Module/Moderation/Users/Pending.php:96 @@ -5224,50 +5234,40 @@ msgid "" "href=\"%s\">the installation page for help." msgstr "" -#: src/Module/Admin/Summary.php:142 -#, php-format -msgid "The logfile '%s' is not usable. No logging possible (error: '%s')" -msgstr "" - -#: src/Module/Admin/Summary.php:156 -#, php-format -msgid "The debug logfile '%s' is not usable. No logging possible (error: '%s')" -msgstr "" - -#: src/Module/Admin/Summary.php:172 +#: src/Module/Admin/Summary.php:148 #, php-format msgid "" "Friendica's system.basepath was updated from '%s' to '%s'. Please remove the " "system.basepath from your db to avoid differences." msgstr "" -#: src/Module/Admin/Summary.php:180 +#: src/Module/Admin/Summary.php:156 #, php-format msgid "" "Friendica's current system.basepath '%s' is wrong and the config file '%s' " "isn't used." msgstr "" -#: src/Module/Admin/Summary.php:188 +#: src/Module/Admin/Summary.php:164 #, php-format msgid "" "Friendica's current system.basepath '%s' is not equal to the config file " "'%s'. Please fix your configuration." msgstr "" -#: src/Module/Admin/Summary.php:199 +#: src/Module/Admin/Summary.php:175 msgid "Message queues" msgstr "" -#: src/Module/Admin/Summary.php:205 +#: src/Module/Admin/Summary.php:181 msgid "Server Settings" msgstr "" -#: src/Module/Admin/Summary.php:223 +#: src/Module/Admin/Summary.php:199 msgid "Version" msgstr "" -#: src/Module/Admin/Summary.php:227 +#: src/Module/Admin/Summary.php:203 msgid "Active addons" msgstr "" @@ -5878,7 +5878,7 @@ msgid "Only show blocked contacts" msgstr "" #: src/Module/Contact.php:369 src/Module/Contact.php:441 -#: src/Object/Post.php:365 +#: src/Object/Post.php:362 msgid "Ignored" msgstr "" @@ -6562,7 +6562,7 @@ msgstr "" msgid "Posts that mention or involve you" msgstr "" -#: src/Module/Conversation/Network.php:289 src/Object/Post.php:377 +#: src/Module/Conversation/Network.php:289 src/Object/Post.php:374 msgid "Starred" msgstr "" @@ -11500,234 +11500,234 @@ msgstr "" msgid "Edit" msgstr "" -#: src/Object/Post.php:252 +#: src/Object/Post.php:261 msgid "Delete globally" msgstr "" -#: src/Object/Post.php:252 +#: src/Object/Post.php:261 msgid "Remove locally" msgstr "" -#: src/Object/Post.php:271 +#: src/Object/Post.php:268 #, php-format msgid "Block %s" msgstr "" -#: src/Object/Post.php:276 +#: src/Object/Post.php:273 #, php-format msgid "Ignore %s" msgstr "" -#: src/Object/Post.php:281 +#: src/Object/Post.php:278 #, php-format msgid "Collapse %s" msgstr "" -#: src/Object/Post.php:285 +#: src/Object/Post.php:282 msgid "Report post" msgstr "" -#: src/Object/Post.php:290 +#: src/Object/Post.php:287 msgid "Save to folder" msgstr "" -#: src/Object/Post.php:330 +#: src/Object/Post.php:327 msgid "I will attend" msgstr "" -#: src/Object/Post.php:330 +#: src/Object/Post.php:327 msgid "I will not attend" msgstr "" -#: src/Object/Post.php:330 +#: src/Object/Post.php:327 msgid "I might attend" msgstr "" -#: src/Object/Post.php:360 +#: src/Object/Post.php:357 msgid "Ignore thread" msgstr "" -#: src/Object/Post.php:361 +#: src/Object/Post.php:358 msgid "Unignore thread" msgstr "" -#: src/Object/Post.php:362 +#: src/Object/Post.php:359 msgid "Toggle ignore status" msgstr "" -#: src/Object/Post.php:372 +#: src/Object/Post.php:369 msgid "Add star" msgstr "" -#: src/Object/Post.php:373 +#: src/Object/Post.php:370 msgid "Remove star" msgstr "" -#: src/Object/Post.php:374 +#: src/Object/Post.php:371 msgid "Toggle star status" msgstr "" -#: src/Object/Post.php:385 +#: src/Object/Post.php:382 msgid "Pin" msgstr "" -#: src/Object/Post.php:386 +#: src/Object/Post.php:383 msgid "Unpin" msgstr "" -#: src/Object/Post.php:387 +#: src/Object/Post.php:384 msgid "Toggle pin status" msgstr "" -#: src/Object/Post.php:390 +#: src/Object/Post.php:387 msgid "Pinned" msgstr "" -#: src/Object/Post.php:395 +#: src/Object/Post.php:392 msgid "Add tag" msgstr "" -#: src/Object/Post.php:408 +#: src/Object/Post.php:405 msgid "Quote share this" msgstr "" -#: src/Object/Post.php:408 +#: src/Object/Post.php:405 msgid "Quote Share" msgstr "" -#: src/Object/Post.php:411 +#: src/Object/Post.php:408 msgid "Reshare this" msgstr "" -#: src/Object/Post.php:411 +#: src/Object/Post.php:408 msgid "Reshare" msgstr "" -#: src/Object/Post.php:412 +#: src/Object/Post.php:409 msgid "Cancel your Reshare" msgstr "" -#: src/Object/Post.php:412 +#: src/Object/Post.php:409 msgid "Unshare" msgstr "" -#: src/Object/Post.php:463 +#: src/Object/Post.php:460 #, php-format msgid "%s (Received %s)" msgstr "" -#: src/Object/Post.php:469 +#: src/Object/Post.php:466 msgid "Comment this item on your system" msgstr "" -#: src/Object/Post.php:469 +#: src/Object/Post.php:466 msgid "Remote comment" msgstr "" -#: src/Object/Post.php:491 +#: src/Object/Post.php:488 msgid "Share via ..." msgstr "" -#: src/Object/Post.php:491 +#: src/Object/Post.php:488 msgid "Share via external services" msgstr "" -#: src/Object/Post.php:520 +#: src/Object/Post.php:517 msgid "to" msgstr "" -#: src/Object/Post.php:521 +#: src/Object/Post.php:518 msgid "via" msgstr "" -#: src/Object/Post.php:522 +#: src/Object/Post.php:519 msgid "Wall-to-Wall" msgstr "" -#: src/Object/Post.php:523 +#: src/Object/Post.php:520 msgid "via Wall-To-Wall:" msgstr "" -#: src/Object/Post.php:569 +#: src/Object/Post.php:566 #, php-format msgid "Reply to %s" msgstr "" -#: src/Object/Post.php:572 +#: src/Object/Post.php:569 msgid "More" msgstr "" -#: src/Object/Post.php:590 +#: src/Object/Post.php:587 msgid "Notifier task is pending" msgstr "" -#: src/Object/Post.php:591 +#: src/Object/Post.php:588 msgid "Delivery to remote servers is pending" msgstr "" -#: src/Object/Post.php:592 +#: src/Object/Post.php:589 msgid "Delivery to remote servers is underway" msgstr "" -#: src/Object/Post.php:593 +#: src/Object/Post.php:590 msgid "Delivery to remote servers is mostly done" msgstr "" -#: src/Object/Post.php:594 +#: src/Object/Post.php:591 msgid "Delivery to remote servers is done" msgstr "" -#: src/Object/Post.php:614 +#: src/Object/Post.php:611 #, php-format msgid "%d comment" msgid_plural "%d comments" msgstr[0] "" msgstr[1] "" -#: src/Object/Post.php:615 +#: src/Object/Post.php:612 msgid "Show more" msgstr "" -#: src/Object/Post.php:616 +#: src/Object/Post.php:613 msgid "Show fewer" msgstr "" -#: src/Object/Post.php:652 +#: src/Object/Post.php:649 #, php-format msgid "Reshared by: %s" msgstr "" -#: src/Object/Post.php:657 +#: src/Object/Post.php:654 #, php-format msgid "Viewed by: %s" msgstr "" -#: src/Object/Post.php:662 +#: src/Object/Post.php:659 #, php-format msgid "Liked by: %s" msgstr "" -#: src/Object/Post.php:667 +#: src/Object/Post.php:664 #, php-format msgid "Disliked by: %s" msgstr "" -#: src/Object/Post.php:672 +#: src/Object/Post.php:669 #, php-format msgid "Attended by: %s" msgstr "" -#: src/Object/Post.php:677 +#: src/Object/Post.php:674 #, php-format msgid "Maybe attended by: %s" msgstr "" -#: src/Object/Post.php:682 +#: src/Object/Post.php:679 #, php-format msgid "Not attended by: %s" msgstr "" -#: src/Object/Post.php:687 +#: src/Object/Post.php:684 #, php-format msgid "Reacted with %s by: %s" msgstr "" From c3075d3ebaef9953104c38be870d39bd9f31bd5c Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 16 Jul 2023 18:41:05 +0200 Subject: [PATCH 10/24] add docker-translate command --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 6e0d9f984..60094d5bd 100644 --- a/composer.json +++ b/composer.json @@ -134,6 +134,7 @@ "scripts": { "test": "phpunit", "lint": "find . -name \\*.php -not -path './vendor/*' -not -path './view/asset/*' -print0 | xargs -0 -n1 php -l", + "docker:translate": "docker run --rm -v $PWD:/data -w /data friendicaci/transifex bin/run_xgettext.sh", "cs:install": "@composer install --working-dir=bin/dev/php-cs-fixer", "cs:check": [ "@cs:install", From bd455e75c0970033c6d438b33ed8711ea069a259 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 16 Jul 2023 20:50:04 +0200 Subject: [PATCH 11/24] Update woodpecker PHP version --- .woodpecker/.phpunit.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker/.phpunit.yml b/.woodpecker/.phpunit.yml index a2f8f45d7..8f89e4b5b 100644 --- a/.woodpecker/.phpunit.yml +++ b/.woodpecker/.phpunit.yml @@ -7,9 +7,9 @@ matrix: - PHP_MAJOR_VERSION: 8.0 PHP_VERSION: 8.0.29 - PHP_MAJOR_VERSION: 8.1 - PHP_VERSION: 8.1.20 + PHP_VERSION: 8.1.21 - PHP_MAJOR_VERSION: 8.2 - PHP_VERSION: 8.2.7 + PHP_VERSION: 8.2.8 # This forces PHP Unit executions at the "opensocial" labeled location (because of much more power...) labels: From a9178e6982811ff4bdc83e70a67ade0e7f6cb6f9 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 16 Jul 2023 22:05:59 +0200 Subject: [PATCH 12/24] Add tests for AddonFiles --- .../src/Core/Addon/Model/AddonLoaderTest.php | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 tests/src/Core/Addon/Model/AddonLoaderTest.php diff --git a/tests/src/Core/Addon/Model/AddonLoaderTest.php b/tests/src/Core/Addon/Model/AddonLoaderTest.php new file mode 100644 index 000000000..4496cd42f --- /dev/null +++ b/tests/src/Core/Addon/Model/AddonLoaderTest.php @@ -0,0 +1,212 @@ +. + * + */ + +namespace Friendica\Test\src\Core\Addon\Model; + +use Friendica\Core\Addon\Exception\AddonInvalidConfigFileException; +use Friendica\Core\Addon\Model\AddonLoader; +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Test\MockedTest; +use Friendica\Test\Util\VFSTrait; +use org\bovigo\vfs\vfsStream; + +class AddonLoaderTest extends MockedTest +{ + use VFSTrait; + + protected $structure = [ + 'addon' => [ + 'testaddon1' => [ + 'static' => [], + ], + 'testaddon2' => [ + 'static' => [], + ], + 'testaddon3' => [], + ] + ]; + + protected $addons = [ + 'testaddon1', + 'testaddon2', + 'testaddon3', + ]; + + protected $content = << [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], +]; +EOF; + + protected function setUp(): void + { + parent::setUp(); + + $this->setUpVfsDir(); + } + + public function dataHooks(): array + { + return [ + 'normal' => [ + 'structure' => $this->structure, + 'enabled' => $this->addons, + 'files' => [ + 'addon/testaddon1/static/hooks.config.php' => $this->content, + ], + 'assertion' => [ + \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], + ], + ], + 'double' => [ + 'structure' => $this->structure, + 'enabled' => $this->addons, + 'files' => [ + 'addon/testaddon1/static/hooks.config.php' => $this->content, + 'addon/testaddon2/static/hooks.config.php' => $this->content, + ], + 'assertion' => [ + \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => ['', ''], + ], + ], + ], + ], + 'wrongName' => [ + 'structure' => $this->structure, + 'enabled' => $this->addons, + 'files' => [ + 'addon/testaddon1/static/wrong.config.php' => $this->content, + ], + 'assertion' => [ + ], + ], + 'doubleNutOnlyOneEnabled' => [ + 'structure' => $this->structure, + 'enabled' => ['testaddon1'], + 'files' => [ + 'addon/testaddon1/static/hooks.config.php' => $this->content, + 'addon/testaddon2/static/hooks.config.php' => $this->content, + ], + 'assertion' => [ + \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], + ], + ] + ]; + } + + /** + * @dataProvider dataHooks + */ + public function testAddonLoader(array $structure, array $enabledAddons, array $files, array $assertion) + { + vfsStream::create($structure)->at($this->root); + + foreach ($files as $file => $content) { + vfsStream::newFile($file) + ->withContent($content) + ->at($this->root); + } + + $configArray = []; + foreach ($enabledAddons as $enabledAddon) { + $configArray[$enabledAddon] = ['test' => []]; + } + + $config = \Mockery::mock(IManageConfigValues::class); + $config->shouldReceive('get')->with('addons')->andReturn($configArray)->once(); + + $addonLoader = new AddonLoader($this->root->url(), $config); + + self::assertEquals($assertion, $addonLoader->getActiveAddonConfig('hooks')); + } + + /** + * Test the exception in case of a wrong addon content + */ + public function testWrongContent() + { + $filename = 'addon/testaddon1/static/hooks.config.php'; + $wrongContent = "structure)->at($this->root); + + vfsStream::newFile($filename) + ->withContent($wrongContent) + ->at($this->root); + + $configArray = []; + foreach ($this->addons as $enabledAddon) { + $configArray[$enabledAddon] = ['test' => []]; + } + + $config = \Mockery::mock(IManageConfigValues::class); + $config->shouldReceive('get')->with('addons')->andReturn($configArray)->once(); + + $addonLoader = new AddonLoader($this->root->url(), $config); + + self::expectException(AddonInvalidConfigFileException::class); + self::expectExceptionMessage(sprintf('Error loading config file %s', $this->root->getChild($filename)->url())); + + $addonLoader->getActiveAddonConfig('hooks'); + } + + /** + * Test that nothing happens in case there are wrong addons files, but they're not used + */ + public function testNoHooksConfig() + { + $filename = 'addon/testaddon1/static/hooks.config.php'; + $wrongContent = "structure)->at($this->root); + + vfsStream::newFile($filename) + ->withContent($wrongContent) + ->at($this->root); + + $configArray = []; + foreach ($this->addons as $enabledAddon) { + $configArray[$enabledAddon] = ['test' => []]; + } + + $config = \Mockery::mock(IManageConfigValues::class); + $config->shouldReceive('get')->with('addons')->andReturn($configArray)->once(); + + $addonLoader = new AddonLoader($this->root->url(), $config); + self::assertEmpty($addonLoader->getActiveAddonConfig('anythingElse')); + } +} From 527622df4ace40cb52fb48f03fe16881f9e9962e Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 16 Jul 2023 22:41:56 +0200 Subject: [PATCH 13/24] Add tests for HookFileManager --- src/Core/Hooks/Util/HookFileManager.php | 4 +- .../Core/Hooks/Util/HookFileManagerTest.php | 243 ++++++++++++++++++ 2 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 tests/src/Core/Hooks/Util/HookFileManagerTest.php diff --git a/src/Core/Hooks/Util/HookFileManager.php b/src/Core/Hooks/Util/HookFileManager.php index eda7e9388..e25641e36 100644 --- a/src/Core/Hooks/Util/HookFileManager.php +++ b/src/Core/Hooks/Util/HookFileManager.php @@ -104,13 +104,13 @@ class HookFileManager $configFile = $this->basePath . '/' . static::STATIC_DIR . '/' . static::CONFIG_NAME . '.config.php'; if (!file_exists($configFile)) { - throw new HookConfigException(sprintf('config file %s does not exit.', $configFile)); + throw new HookConfigException(sprintf('config file %s does not exist.', $configFile)); } $config = include $configFile; if (!is_array($config)) { - throw new HookConfigException('Error loading config file ' . $configFile); + throw new HookConfigException(sprintf('Error loading config file %s.', $configFile)); } $this->hookConfig = array_merge_recursive($config, $this->addonLoader->getActiveAddonConfig(static::CONFIG_NAME)); diff --git a/tests/src/Core/Hooks/Util/HookFileManagerTest.php b/tests/src/Core/Hooks/Util/HookFileManagerTest.php new file mode 100644 index 000000000..cbd9a63b1 --- /dev/null +++ b/tests/src/Core/Hooks/Util/HookFileManagerTest.php @@ -0,0 +1,243 @@ +setUpVfsDir(); + } + + public function dataHooks(): array + { + return [ + 'normal' => [ + 'content' => << [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], + \Friendica\Core\Hooks\Capabilities\HookType::DECORATOR => [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class, + ], + ], +]; +EOF, + 'addonsArray' => [], + 'assertStrategies' => [ + [LoggerInterface::class, NullLogger::class, ''], + ], + 'assertDecorators' => [ + [LoggerInterface::class, NullLogger::class], + ], + ], + 'normalWithString' => [ + 'content' => << [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => '', + ], + ], + \Friendica\Core\Hooks\Capabilities\HookType::DECORATOR => [ + \Psr\Log\LoggerInterface::class => \Psr\Log\NullLogger::class, + ], +]; +EOF, + 'addonsArray' => [], + 'assertStrategies' => [ + [LoggerInterface::class, NullLogger::class, ''], + ], + 'assertDecorators' => [ + [LoggerInterface::class, NullLogger::class], + ], + ], + 'withAddons' => [ + 'content' => << [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], +]; +EOF, + 'addonsArray' => [ + \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => ['null'], + ], + ], + ], + 'assertStrategies' => [ + [LoggerInterface::class, NullLogger::class, ''], + [LoggerInterface::class, NullLogger::class, 'null'], + ], + 'assertDecorators' => [], + ], + 'withAddonsWithString' => [ + 'content' => << [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], +]; +EOF, + 'addonsArray' => [ + \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => 'null', + ], + ], + ], + 'assertStrategies' => [ + [LoggerInterface::class, NullLogger::class, ''], + [LoggerInterface::class, NullLogger::class, 'null'], + ], + 'assertDecorators' => [], + ], + // This should work because unique name convention is part of the instance manager logic, not of the file-infrastructure layer + 'withAddonsDoubleNamed' => [ + 'content' => << [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], +]; +EOF, + 'addonsArray' => [ + \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], + ], + 'assertStrategies' => [ + [LoggerInterface::class, NullLogger::class, ''], + [LoggerInterface::class, NullLogger::class, ''], + ], + 'assertDecorators' => [], + ], + 'withWrongContentButAddons' => [ + 'content' => << [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], +]; +EOF, + 'addonsArray' => [ + \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], + ], + ], + 'assertStrategies' => [ + [LoggerInterface::class, NullLogger::class, ''], + ], + 'assertDecorators' => [], + ], + ]; + } + + /** + * @dataProvider dataHooks + */ + public function testSetupHooks(string $content, array $addonsArray, array $assertStrategies, array $assertDecorators) + { + vfsStream::newFile('static/hooks.config.php') + ->withContent($content) + ->at($this->root); + + $addonLoader = \Mockery::mock(ICanLoadAddons::class); + $addonLoader->shouldReceive('getActiveAddonConfig')->andReturn($addonsArray)->once(); + + $hookFileManager = new HookFileManager($this->root->url(), $addonLoader); + + $instanceManager = \Mockery::mock(ICanRegisterInstances::class); + foreach ($assertStrategies as $assertStrategy) { + $instanceManager->shouldReceive('registerStrategy')->withArgs($assertStrategy)->once(); + } + + foreach ($assertDecorators as $assertDecorator) { + $instanceManager->shouldReceive('registerDecorator')->withArgs($assertDecorator)->once(); + } + + $hookFileManager->setupHooks($instanceManager); + + self::expectNotToPerformAssertions(); + } + + /** + * Test the exception in case the hooks.config.php file is missing + */ + public function testMissingHooksFile() + { + $addonLoader = \Mockery::mock(ICanLoadAddons::class); + $instanceManager = \Mockery::mock(ICanRegisterInstances::class); + $hookFileManager = new HookFileManager($this->root->url(), $addonLoader); + + self::expectException(HookConfigException::class); + self::expectExceptionMessage(sprintf('config file %s does not exist.', + $this->root->url() . '/' . HookFileManager::STATIC_DIR . '/' . HookFileManager::CONFIG_NAME . '.config.php')); + + $hookFileManager->setupHooks($instanceManager); + } + + /** + * Test the exception in case the hooks.config.php file is wrong + */ + public function testWrongHooksFile() + { + $addonLoader = \Mockery::mock(ICanLoadAddons::class); + $instanceManager = \Mockery::mock(ICanRegisterInstances::class); + $hookFileManager = new HookFileManager($this->root->url(), $addonLoader); + + vfsStream::newFile('static/hooks.config.php') + ->withContent("at($this->root); + + self::expectException(HookConfigException::class); + self::expectExceptionMessage(sprintf('Error loading config file %s.', + $this->root->url() . '/' . HookFileManager::STATIC_DIR . '/' . HookFileManager::CONFIG_NAME . '.config.php')); + + $hookFileManager->setupHooks($instanceManager); + } +} From 93af6f0564b28cd8cd30ec28dcced1d2e6e72881 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Jul 2023 00:10:15 +0200 Subject: [PATCH 14/24] Add tests for InstanceManager and remove Decorator hook logic (avoid complex Dice logic) --- doc/AddonsStrategyDecorator.md | 159 --------------- doc/StrategyHooks.md | 89 +++++++++ .../{HookType.php => BehavioralHookType.php} | 14 +- .../Capabilities/ICanCreateInstances.php | 20 +- .../Capabilities/ICanRegisterInstances.php | 19 +- src/Core/Hooks/Model/DiceInstanceManager.php | 45 +---- src/Core/Hooks/Util/HookFileManager.php | 15 +- src/Core/Logger/Factory/Logger.php | 12 +- static/hooks.config.php | 7 +- .../Util/Hooks/InstanceMocks/FakeInstance.php | 4 +- .../InstanceMocks/FakeInstanceDecorator.php | 12 +- .../src/Core/Addon/Model/AddonLoaderTest.php | 8 +- .../Core/Hooks/Model/InstanceManagerTest.php | 182 ++++++++---------- .../Core/Hooks/Util/HookFileManagerTest.php | 22 +-- 14 files changed, 218 insertions(+), 390 deletions(-) delete mode 100644 doc/AddonsStrategyDecorator.md create mode 100644 doc/StrategyHooks.md rename src/Core/Hooks/Capabilities/{HookType.php => BehavioralHookType.php} (82%) diff --git a/doc/AddonsStrategyDecorator.md b/doc/AddonsStrategyDecorator.md deleted file mode 100644 index 51456da6c..000000000 --- a/doc/AddonsStrategyDecorator.md +++ /dev/null @@ -1,159 +0,0 @@ -Friendica strategy and decorator Hooks -=========================================== - -* [Home](help) - -## Strategy hooks - -This type of hook is based on the [Strategy Design Pattern](https://refactoring.guru/design-patterns/strategy). - -A strategy class defines a possible implementation of a given interface based on a unique name. -Every name is possible as long as it's unique and not `null`. -Using an empty name (`''`) is possible as well and should be used as the "default" implementation. -To register a strategy, use the [`ICanRegisterInstance`](../src/Core/Hooks/Capabilities/ICanRegisterInstances.php) interface. - -After registration, a caller can automatically create this instance with the [`ICanCreateInstances`](../src/Core/Hooks/Capabilities/ICanCreateInstances.php) interface and the chosen name. - -This is useful in case there are different, possible implementations for the same purpose, like for logging, locking, caching, ... - -Normally, a config entry is used to choose the right implementation at runtime. -And if no config entry is set, the "default" implementation should be used. - -### Example - -```php -interface ExampleInterface -{ - public function testMethod(); -} - -public class ConcreteClassA implements ExampleInterface -{ - public function testMethod() - { - echo "concrete class A"; - } -} - -public class ConcreteClassB implements ExampleInterface -{ - public function testMethod() - { - echo "concrete class B"; - } -} - -/** @var \Friendica\Core\Hooks\Capabilities\ICanRegisterInstances $instanceRegister */ -$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassA::class, 'A'); -$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassB::class, 'B'); - -/** @var \Friendica\Core\Hooks\Capabilities\ICanCreateInstances $instanceManager */ -/** @var ConcreteClassA $concreteClass */ -$concreteClass = $instanceManager->createWithName(ExampleInterface::class, 'A'); - -$concreteClass->testMethod(); -// output: -// "concrete class A"; -``` - -## Decorator hooks - -This type of hook is based on the [Decorator Design Pattern](https://refactoring.guru/design-patterns/decorator). - -A decorator class extends a given strategy instance (see [Strategy hooks](#strategy-hooks)]). -To register a decorator, use the [`ICanRegisterInstance`](../src/Core/Hooks/Capabilities/ICanRegisterInstances.php) interface. - -After registration, a caller can automatically create an instance with the [`ICanCreateInstances`](../src/Core/Hooks/Capabilities/ICanCreateInstances.php) interface and the decorator will wrap its logic around the call. - -This is useful in case you want to extend a given class but the given class isn't responsible for these business logic. Or you want to extend an interface without knowing the concrete implementation. -For example profiling logger calls, Friendica is using a [`ProfilerLogger`](../src/Core/Logger/Type/ProfilerLogger.php), which wraps all other logging implementations and traces each log call. - -Normally, a config entry is used to enable/disable decorator. - -### Example - -```php -interface ExampleInterface -{ - public function testMethod(); -} - -public class ConcreteClassA implements ExampleInterface -{ - public function testMethod() - { - echo "concrete class A"; - } -} - -public class DecoratorClassA implements ExampleInterface -{ - /** @var ExampleInterface */ - protected $example; - - public function __construct(ExampleInterface $example) - { - $this->example = $example; - } - - public function testMethod() - { - echo "decorated!\n"; - $this->example->testMethod(); - } -} - -/** @var \Friendica\Core\Hooks\Capabilities\ICanRegisterInstances $instanceRegister */ -$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassA::class, 'A'); -$instanceRegister->registerDecorator(ExampleInterface::class, DecoratorClassA::class); - -/** @var \Friendica\Core\Hooks\Capabilities\ICanCreateInstances $instanceManager */ -/** @var ConcreteClassA $concreteClass */ -$concreteClass = $instanceManager->createWithName(ExampleInterface::class, 'A'); - -$concreteClass->testMethod(); -// output: -// "decorated!" -// "concrete class A"; -``` - -## hooks.config.php - -To avoid registering all strategies and decorators manually inside the code, Friendica introduced the [`hooks.config.php`](../static/hooks.config.php) file. - -There, you can register all kind of strategies and decorators in one file. - -### [`HookType::STRATEGY`](../src/Core/Hooks/Capabilities/HookType.php) - -For each given interface, a list of key-value pairs can be set, where the key is the concrete implementation class and the value is an array of unique names. - -### [`HookType::DECORATOR`](../src/Core/Hooks/Capabilities/HookType.php) - -For each given interface, a list of concrete decorator classes can be set. - -### Example - -```php -use Friendica\Core\Hooks\Capabilities\HookType as H; - -return [ - H::STRATEGY => [ - ExampleInterface::class => [ - ConcreteClassA::class => ['A'], - ConcreteClassB::class => ['B'], - ], - ], - H::DECORATOR => [ - ExampleInterface::class => [ - DecoratorClassA::class, - ], - ], -]; -``` - -## Addons - -The hook logic is useful for decoupling the Friendica core logic, but its primary goal is to modularize Friendica in creating addons. - -Therefor you can either use the interfaces directly as shown above, or you can place your own `hooks.config.php` file inside a `static` directory directly under your addon core directory. -Friendica will automatically search these config files for each **activated** addon and register the given hooks. diff --git a/doc/StrategyHooks.md b/doc/StrategyHooks.md new file mode 100644 index 000000000..cb355f93f --- /dev/null +++ b/doc/StrategyHooks.md @@ -0,0 +1,89 @@ +Friendica strategy Hooks +=========================================== + +* [Home](help) + +## Strategy hooks + +This type of hook is based on the [Strategy Design Pattern](https://refactoring.guru/design-patterns/strategy). + +A strategy class defines a possible implementation of a given interface based on a unique name. +Every name is possible as long as it's unique and not `null`. +Using an empty name (`''`) is possible as well and should be used as the "default" implementation. +To register a strategy, use the [`ICanRegisterInstance`](../src/Core/Hooks/Capabilities/ICanRegisterInstances.php) interface. + +After registration, a caller can automatically create this instance with the [`ICanCreateInstances`](../src/Core/Hooks/Capabilities/ICanCreateInstances.php) interface and the chosen name. + +This is useful in case there are different, possible implementations for the same purpose, like for logging, locking, caching, ... + +Normally, a config entry is used to choose the right implementation at runtime. +And if no config entry is set, the "default" implementation should be used. + +### Example + +```php +interface ExampleInterface +{ + public function testMethod(); +} + +public class ConcreteClassA implements ExampleInterface +{ + public function testMethod() + { + echo "concrete class A"; + } +} + +public class ConcreteClassB implements ExampleInterface +{ + public function testMethod() + { + echo "concrete class B"; + } +} + +/** @var \Friendica\Core\Hooks\Capabilities\ICanRegisterInstances $instanceRegister */ +$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassA::class, 'A'); +$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassB::class, 'B'); + +/** @var \Friendica\Core\Hooks\Capabilities\ICanCreateInstances $instanceManager */ +/** @var ConcreteClassA $concreteClass */ +$concreteClass = $instanceManager->create(ExampleInterface::class, 'A'); + +$concreteClass->testMethod(); +// output: +// "concrete class A"; +``` + +## hooks.config.php + +To avoid registering all strategies manually inside the code, Friendica introduced the [`hooks.config.php`](../static/hooks.config.php) file. + +There, you can register all kind of strategies in one file. + +### [`HookType::STRATEGY`](../src/Core/Hooks/Capabilities/HookType.php) + +For each given interface, a list of key-value pairs can be set, where the key is the concrete implementation class and the value is an array of unique names. + +### Example + +```php +use Friendica\Core\Hooks\Capabilities\BehavioralHookType as H; + +return [ + H::STRATEGY => [ + ExampleInterface::class => [ + ConcreteClassA::class => ['A'], + ConcreteClassB::class => ['B'], + ], + ], +]; +``` + +## Addons + +The hook logic is useful for decoupling the Friendica core logic, but its primary goal is to modularize Friendica in creating addons. + +Therefor you can either use the interfaces directly as shown above, or you can place your own `hooks.config.php` file inside a `static` directory directly under your addon core directory. +Friendica will automatically search these config files for each **activated** addon and register the given hooks. diff --git a/src/Core/Hooks/Capabilities/HookType.php b/src/Core/Hooks/Capabilities/BehavioralHookType.php similarity index 82% rename from src/Core/Hooks/Capabilities/HookType.php rename to src/Core/Hooks/Capabilities/BehavioralHookType.php index 6fea41e17..6336406e6 100644 --- a/src/Core/Hooks/Capabilities/HookType.php +++ b/src/Core/Hooks/Capabilities/BehavioralHookType.php @@ -21,7 +21,11 @@ namespace Friendica\Core\Hooks\Capabilities; -interface HookType +/** + * An enum of hook types, based on behavioral design patterns + * @see https://refactoring.guru/design-patterns/behavioral-patterns + */ +interface BehavioralHookType { /** * Defines the key for the list of strategy-hooks. @@ -29,11 +33,5 @@ interface HookType * @see https://refactoring.guru/design-patterns/strategy */ const STRATEGY = 'strategy'; - /** - * Defines the key for the list of decorator-hooks. - * - * @see https://refactoring.guru/design-patterns/decorator - */ - const DECORATOR = 'decorator'; - const EVENT = 'event'; + const EVENT = 'event'; } diff --git a/src/Core/Hooks/Capabilities/ICanCreateInstances.php b/src/Core/Hooks/Capabilities/ICanCreateInstances.php index 971430474..f2e4b8b0a 100644 --- a/src/Core/Hooks/Capabilities/ICanCreateInstances.php +++ b/src/Core/Hooks/Capabilities/ICanCreateInstances.php @@ -22,7 +22,7 @@ namespace Friendica\Core\Hooks\Capabilities; /** - * creates special instance and decorator treatments for given classes + * creates special instances for given classes */ interface ICanCreateInstances { @@ -31,27 +31,11 @@ interface ICanCreateInstances * * The instance will be build based on the registered strategy and the (unique) name * - * In case, there are registered decorators for this class as well, all decorators of the list will be wrapped - * around the instance before returning it - * * @param string $class The fully-qualified name of the given class or interface which will get returned * @param string $name An arbitrary identifier to find a concrete instance strategy. * @param array $arguments Additional arguments, which can be passed to the constructor of "$class" at runtime * * @return object The concrete instance of the type "$class" */ - public function createWithName(string $class, string $name, array $arguments = []): object; - - /** - * Returns a new instance of a given class - * - * In case, there are registered decorators for this class as well, all decorators of the list will be wrapped - * around the instance before returning it - * - * @param string $class The fully-qualified name of the given class or interface which will get returned - * @param array $arguments Additional arguments, which can be passed to the constructor of "$class" at runtime - * - * @return object The concrete instance of the type "$class" - */ - public function create(string $class, array $arguments = []): object; + public function create(string $class, string $name, array $arguments = []): object; } diff --git a/src/Core/Hooks/Capabilities/ICanRegisterInstances.php b/src/Core/Hooks/Capabilities/ICanRegisterInstances.php index 23a77ab35..f7689bbee 100644 --- a/src/Core/Hooks/Capabilities/ICanRegisterInstances.php +++ b/src/Core/Hooks/Capabilities/ICanRegisterInstances.php @@ -24,7 +24,7 @@ namespace Friendica\Core\Hooks\Capabilities; use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException; /** - * Register strategies and decorator/treatment handling for given classes + * Register strategies for given classes */ interface ICanRegisterInstances { @@ -43,21 +43,4 @@ interface ICanRegisterInstances * @throws HookRegisterArgumentException in case the given class for the interface isn't valid or already set */ public function registerStrategy(string $interface, string $class, ?string $name = null): self; - - /** - * Register a new decorator for a given class or interface - * - * @see https://refactoring.guru/design-patterns/decorator - * - * @note Decorator attach new behaviors to classes without changing them or without letting them know about it. - * - * @param string $class The fully-qualified class or interface name, which gets decorated by a class - * @param string $decoratorClass The fully-qualified name of the class which mimics the given class or interface and adds new functionality - * A placeholder for dependencies is possible as well - * - * @return $this This interface for chain-calls - * - * @throws HookRegisterArgumentException in case the given class for the class or interface isn't valid - */ - public function registerDecorator(string $class, string $decoratorClass): self; } diff --git a/src/Core/Hooks/Model/DiceInstanceManager.php b/src/Core/Hooks/Model/DiceInstanceManager.php index efbee444a..7c35dfe4d 100644 --- a/src/Core/Hooks/Model/DiceInstanceManager.php +++ b/src/Core/Hooks/Model/DiceInstanceManager.php @@ -36,7 +36,6 @@ use Friendica\Core\Hooks\Util\HookFileManager; class DiceInstanceManager implements ICanCreateInstances, ICanRegisterInstances { protected $instance = []; - protected $decorator = []; /** @var Dice */ protected $dice; @@ -60,52 +59,12 @@ class DiceInstanceManager implements ICanCreateInstances, ICanRegisterInstances } /** {@inheritDoc} */ - public function registerDecorator(string $class, string $decoratorClass): ICanRegisterInstances - { - $this->decorator[$class][] = $decoratorClass; - - return $this; - } - - /** {@inheritDoc} */ - public function create(string $class, array $arguments = []): object - { - $instanceClassName = $class; - $instanceRule = $this->dice->getRule($instanceClassName) ?? []; - - $instanceRule = array_replace_recursive($instanceRule, [ - 'constructParams' => $arguments, - ]); - - $this->dice = $this->dice->addRule($instanceClassName, $instanceRule); - - foreach ($this->decorator[$class] ?? [] as $decorator) { - $dependencyRule = $this->dice->getRule($decorator); - for ($i = 0; $i < count($dependencyRule['call'] ?? []); $i++) { - $dependencyRule['call'][$i][1] = [[Dice::INSTANCE => $instanceClassName]]; - } - $dependencyRule['constructParams'] = $arguments; - $dependencyRule['substitutions'] = [ - $class => $instanceClassName, - ]; - - $this->dice = $this->dice->addRule($decorator, $dependencyRule); - - $instanceClassName = $decorator; - } - - return $this->dice->create($instanceClassName); - } - - /** {@inheritDoc} */ - public function createWithName(string $class, string $name, array $arguments = []): object + public function create(string $class, string $name, array $arguments = []): object { if (empty($this->instance[$class][$name])) { throw new HookInstanceException(sprintf('The class with the name %s isn\'t registered for the class or interface %s', $name, $class)); } - $instanceClassName = $this->instance[$class][$name]; - - return $this->create($instanceClassName, $arguments); + return $this->dice->create($this->instance[$class][$name], $arguments); } } diff --git a/src/Core/Hooks/Util/HookFileManager.php b/src/Core/Hooks/Util/HookFileManager.php index e25641e36..b83f2b49c 100644 --- a/src/Core/Hooks/Util/HookFileManager.php +++ b/src/Core/Hooks/Util/HookFileManager.php @@ -22,7 +22,7 @@ namespace Friendica\Core\Hooks\Util; use Friendica\Core\Addon\Capabilities\ICanLoadAddons; -use Friendica\Core\Hooks\Capabilities\HookType; +use Friendica\Core\Hooks\Capabilities\BehavioralHookType; use Friendica\Core\Hooks\Capabilities\ICanRegisterInstances; use Friendica\Core\Hooks\Exceptions\HookConfigException; @@ -63,7 +63,7 @@ class HookFileManager foreach ($this->hookConfig as $hookType => $classList) { switch ($hookType) { - case HookType::STRATEGY: + case BehavioralHookType::STRATEGY: foreach ($classList as $interface => $strategy) { foreach ($strategy as $dependencyName => $names) { if (is_array($names)) { @@ -76,17 +76,6 @@ class HookFileManager } } break; - case HookType::DECORATOR: - foreach ($classList as $interface => $decorators) { - if (is_array($decorators)) { - foreach ($decorators as $decorator) { - $instanceRegister->registerDecorator($interface, $decorator); - } - } else { - $instanceRegister->registerDecorator($interface, $decorators); - } - } - break; } } } diff --git a/src/Core/Logger/Factory/Logger.php b/src/Core/Logger/Factory/Logger.php index 3fde878a3..81f8887ef 100644 --- a/src/Core/Logger/Factory/Logger.php +++ b/src/Core/Logger/Factory/Logger.php @@ -24,6 +24,8 @@ namespace Friendica\Core\Logger\Factory; use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Hooks\Capabilities\ICanCreateInstances; use Friendica\Core\Logger\Capabilities\LogChannel; +use Friendica\Core\Logger\Type\ProfilerLogger as ProfilerLoggerClass; +use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Throwable; @@ -41,7 +43,7 @@ class Logger $this->channel = $channel; } - public function create(ICanCreateInstances $createInstances, IManageConfigValues $config): LoggerInterface + public function create(ICanCreateInstances $createInstances, IManageConfigValues $config, Profiler $profiler): LoggerInterface { if (empty($config->get('system', 'debugging') ?? false)) { return new NullLogger(); @@ -50,7 +52,13 @@ class Logger $name = $config->get('system', 'logger_config') ?? ''; try { - return $createInstances->createWithName(LoggerInterface::class, $name, [$this->channel]); + /** @var LoggerInterface $logger */ + $logger = $createInstances->create(LoggerInterface::class, $name, [$this->channel]); + if ($config->get('system', 'profiling') ?? false) { + return new ProfilerLoggerClass($logger, $profiler); + } else { + return $logger; + } } catch (Throwable $e) { // No logger ... return new NullLogger(); diff --git a/static/hooks.config.php b/static/hooks.config.php index bc235913c..d46762f87 100644 --- a/static/hooks.config.php +++ b/static/hooks.config.php @@ -19,7 +19,7 @@ * */ -use Friendica\Core\Hooks\Capabilities\HookType as H; +use Friendica\Core\Hooks\Capabilities\BehavioralHookType as H; use Friendica\Core\Logger\Type; use Psr\Log; @@ -31,9 +31,4 @@ return [ Type\StreamLogger::class => ['stream'], ], ], - H::DECORATOR => [ - Log\LoggerInterface::class => [ - Type\ProfilerLogger::class, - ], - ], ]; diff --git a/tests/Util/Hooks/InstanceMocks/FakeInstance.php b/tests/Util/Hooks/InstanceMocks/FakeInstance.php index 9a0e9dd6f..64fe2c5ad 100644 --- a/tests/Util/Hooks/InstanceMocks/FakeInstance.php +++ b/tests/Util/Hooks/InstanceMocks/FakeInstance.php @@ -21,7 +21,7 @@ namespace Friendica\Test\Util\Hooks\InstanceMocks; -class FakeInstance +class FakeInstance implements IAmADecoratedInterface { protected $aText = null; protected $cBool = null; @@ -39,6 +39,8 @@ class FakeInstance $this->aText = $aText; $this->cBool = $cBool; $this->bText = $bText; + + return ''; } public function getAText(): ?string diff --git a/tests/Util/Hooks/InstanceMocks/FakeInstanceDecorator.php b/tests/Util/Hooks/InstanceMocks/FakeInstanceDecorator.php index af4db96c4..40ab78a25 100644 --- a/tests/Util/Hooks/InstanceMocks/FakeInstanceDecorator.php +++ b/tests/Util/Hooks/InstanceMocks/FakeInstanceDecorator.php @@ -25,14 +25,14 @@ class FakeInstanceDecorator implements IAmADecoratedInterface { public static $countInstance = 0; + const PREFIX = 'prefix1'; + /** @var IAmADecoratedInterface */ protected $orig; - protected $prefix = ''; - public function __construct(IAmADecoratedInterface $orig, string $prefix = '') + public function __construct(IAmADecoratedInterface $orig) { $this->orig = $orig; - $this->prefix = $prefix; self::$countInstance++; } @@ -44,16 +44,16 @@ class FakeInstanceDecorator implements IAmADecoratedInterface public function getAText(): ?string { - return $this->prefix . $this->orig->getAText(); + return static::PREFIX . $this->orig->getAText(); } public function getBText(): ?string { - return $this->prefix . $this->orig->getBText(); + return static::PREFIX . $this->orig->getBText(); } public function getCBool(): ?bool { - return $this->prefix . $this->orig->getCBool(); + return static::PREFIX . $this->orig->getCBool(); } } diff --git a/tests/src/Core/Addon/Model/AddonLoaderTest.php b/tests/src/Core/Addon/Model/AddonLoaderTest.php index 4496cd42f..be125535f 100644 --- a/tests/src/Core/Addon/Model/AddonLoaderTest.php +++ b/tests/src/Core/Addon/Model/AddonLoaderTest.php @@ -54,7 +54,7 @@ class AddonLoaderTest extends MockedTest [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], @@ -79,7 +79,7 @@ EOF; 'addon/testaddon1/static/hooks.config.php' => $this->content, ], 'assertion' => [ - \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], @@ -94,7 +94,7 @@ EOF; 'addon/testaddon2/static/hooks.config.php' => $this->content, ], 'assertion' => [ - \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => ['', ''], ], @@ -118,7 +118,7 @@ EOF; 'addon/testaddon2/static/hooks.config.php' => $this->content, ], 'assertion' => [ - \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], diff --git a/tests/src/Core/Hooks/Model/InstanceManagerTest.php b/tests/src/Core/Hooks/Model/InstanceManagerTest.php index 200500d63..de2bdc6b8 100644 --- a/tests/src/Core/Hooks/Model/InstanceManagerTest.php +++ b/tests/src/Core/Hooks/Model/InstanceManagerTest.php @@ -22,25 +22,27 @@ namespace Friendica\Test\src\Core\Hooks\Model; use Dice\Dice; +use Friendica\Core\Hooks\Exceptions\HookInstanceException; +use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException; use Friendica\Core\Hooks\Model\DiceInstanceManager; +use Friendica\Core\Hooks\Util\HookFileManager; use Friendica\Test\MockedTest; use Friendica\Test\Util\Hooks\InstanceMocks\FakeInstance; use Friendica\Test\Util\Hooks\InstanceMocks\FakeInstanceDecorator; use Friendica\Test\Util\Hooks\InstanceMocks\IAmADecoratedInterface; +use Mockery\MockInterface; class InstanceManagerTest extends MockedTest { - public function testEqualButNotSameInstance() + /** @var HookFileManager|MockInterface */ + protected $hookFileManager; + + protected function setUp(): void { - $instance = new DiceInstanceManager(new Dice()); + parent::setUp(); - $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class); - - $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake'); - $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake'); - - self::assertEquals($getInstanceA, $getInstanceB); - self::assertNotSame($getInstanceA, $getInstanceB); + $this->hookFileManager = \Mockery::mock(HookFileManager::class); + $this->hookFileManager->shouldReceive('setupHooks')->withAnyArgs(); } protected function tearDown(): void @@ -50,6 +52,19 @@ class InstanceManagerTest extends MockedTest parent::tearDown(); } + public function testEqualButNotSameInstance() + { + $instance = new DiceInstanceManager(new Dice(), $this->hookFileManager); + + $instance->registerStrategy(IAmADecoratedInterface::class, FakeInstance::class, 'fake'); + + $getInstanceA = $instance->create(IAmADecoratedInterface::class, 'fake'); + $getInstanceB = $instance->create(IAmADecoratedInterface::class, 'fake'); + + self::assertEquals($getInstanceA, $getInstanceB); + self::assertNotSame($getInstanceA, $getInstanceB); + } + public function dataTests(): array { return [ @@ -79,9 +94,9 @@ class InstanceManagerTest extends MockedTest /** * @dataProvider dataTests */ - public function testInstanceWithConstructorAnonymArgs(string $aString = null, bool $cBool = null, string $bString = null) + public function testInstanceWithArgs(string $aString = null, bool $cBool = null, string $bString = null) { - $instance = new DiceInstanceManager(new Dice()); + $instance = new DiceInstanceManager(new Dice(), $this->hookFileManager); $args = []; @@ -95,45 +110,12 @@ class InstanceManagerTest extends MockedTest $args[] = $cBool; } - $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args); + $instance->registerStrategy(IAmADecoratedInterface::class, FakeInstance::class, 'fake'); /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake'); + $getInstanceA = $instance->create(IAmADecoratedInterface::class, 'fake', $args); /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake'); - - self::assertEquals($getInstanceA, $getInstanceB); - self::assertNotSame($getInstanceA, $getInstanceB); - self::assertEquals($aString, $getInstanceA->getAText()); - self::assertEquals($aString, $getInstanceB->getAText()); - self::assertEquals($bString, $getInstanceA->getBText()); - self::assertEquals($bString, $getInstanceB->getBText()); - self::assertEquals($cBool, $getInstanceA->getCBool()); - self::assertEquals($cBool, $getInstanceB->getCBool()); - } - - /** - * @dataProvider dataTests - */ - public function testInstanceConstructorAndGetInstanceWithNamedArgs(string $aString = null, bool $cBool = null, string $bString = null) - { - $instance = new DiceInstanceManager(new Dice()); - - $args = []; - - if (isset($aString)) { - $args[] = $aString; - } - if (isset($cBool)) { - $args[] = $cBool; - } - - $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args); - - /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); - /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); + $getInstanceB = $instance->create(IAmADecoratedInterface::class, 'fake', $args); self::assertEquals($getInstanceA, $getInstanceB); self::assertNotSame($getInstanceA, $getInstanceB); @@ -150,24 +132,27 @@ class InstanceManagerTest extends MockedTest */ public function testInstanceWithTwoStrategies(string $aString = null, bool $cBool = null, string $bString = null) { - $instance = new DiceInstanceManager(new Dice()); + $instance = new DiceInstanceManager(new Dice(), $this->hookFileManager); $args = []; if (isset($aString)) { $args[] = $aString; } + if (isset($bString)) { + $args[] = $bString; + } if (isset($cBool)) { $args[] = $cBool; } - $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args); - $instance->registerStrategy(IAmADecoratedInterface::class, 'fake23', FakeInstance::class, $args); + $instance->registerStrategy(IAmADecoratedInterface::class, FakeInstance::class, 'fake'); + $instance->registerStrategy(IAmADecoratedInterface::class, FakeInstance::class, 'fake23'); /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); + $getInstanceA = $instance->create(IAmADecoratedInterface::class, 'fake', $args); /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake23', [$bString]); + $getInstanceB = $instance->create(IAmADecoratedInterface::class, 'fake23', $args); self::assertEquals($getInstanceA, $getInstanceB); self::assertNotSame($getInstanceA, $getInstanceB); @@ -180,79 +165,74 @@ class InstanceManagerTest extends MockedTest } /** - * @dataProvider dataTests + * Test the exception in case the interface was already registered */ - public function testDecorator(string $aString = null, bool $cBool = null, string $bString = null) + public function testDoubleRegister() { - $instance = new DiceInstanceManager(new Dice()); + self::expectException(HookRegisterArgumentException::class); + self::expectExceptionMessage(sprintf('A class with the name %s is already set for the interface %s', 'fake', IAmADecoratedInterface::class)); - $args = []; - - if (isset($aString)) { - $args[] = $aString; - } - if (isset($cBool)) { - $args[] = $cBool; - } - - $prefix = 'prefix1'; - - $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args); - $instance->registerStrategy(IAmADecoratedInterface::class, 'fake23', FakeInstance::class, $args); - $instance->registerDecorator(IAmADecoratedInterface::class, FakeInstanceDecorator::class, [$prefix]); - - /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); - /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake23', [$bString]); - - self::assertEquals(2, FakeInstanceDecorator::$countInstance); - self::assertEquals($getInstanceA, $getInstanceB); - self::assertNotSame($getInstanceA, $getInstanceB); - self::assertEquals($prefix . $aString, $getInstanceA->getAText()); - self::assertEquals($prefix . $aString, $getInstanceB->getAText()); - self::assertEquals($prefix . $bString, $getInstanceA->getBText()); - self::assertEquals($prefix . $bString, $getInstanceB->getBText()); - self::assertEquals($prefix . $cBool, $getInstanceA->getCBool()); - self::assertEquals($prefix . $cBool, $getInstanceB->getCBool()); + $instance = new DiceInstanceManager(new Dice(), $this->hookFileManager); + $instance->registerStrategy(IAmADecoratedInterface::class, FakeInstance::class, 'fake'); + $instance->registerStrategy(IAmADecoratedInterface::class, FakeInstance::class, 'fake'); } /** + * Test the exception in case the name of the instance isn't registered + */ + public function testWrongInstanceName() + { + self::expectException(HookInstanceException::class ); + self::expectExceptionMessage(sprintf('The class with the name %s isn\'t registered for the class or interface %s', 'fake', IAmADecoratedInterface::class)); + + $instance = new DiceInstanceManager(new Dice(), $this->hookFileManager); + $instance->create(IAmADecoratedInterface::class, 'fake'); + } + + /** + * Test in case there are already some rules + * * @dataProvider dataTests */ - public function testTwoDecoratorWithPrefix(string $aString = null, bool $cBool = null, string $bString = null) + public function testWithGivenRules(string $aString = null, bool $cBool = null, string $bString = null) { - $instance = new DiceInstanceManager(new Dice()); - $args = []; if (isset($aString)) { $args[] = $aString; } + if (isset($bString)) { + $args[] = $bString; + } + + $dice = (new Dice())->addRules([ + FakeInstance::class => [ + 'constructParams' => $args, + ], + ]); + + $args = []; + if (isset($cBool)) { $args[] = $cBool; } - $prefix = 'prefix1'; + $instance = new DiceInstanceManager($dice, $this->hookFileManager); - $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args); - $instance->registerStrategy(IAmADecoratedInterface::class, 'fake23', FakeInstance::class, $args); - $instance->registerDecorator(IAmADecoratedInterface::class, FakeInstanceDecorator::class, [$prefix]); - $instance->registerDecorator(IAmADecoratedInterface::class, FakeInstanceDecorator::class); + $instance->registerStrategy(IAmADecoratedInterface::class, FakeInstance::class, 'fake'); /** @var IAmADecoratedInterface $getInstanceA */ - $getInstanceA = $instance->createWithName(IAmADecoratedInterface::class, 'fake', [$bString]); + $getInstanceA = $instance->create(IAmADecoratedInterface::class, 'fake', $args); /** @var IAmADecoratedInterface $getInstanceB */ - $getInstanceB = $instance->createWithName(IAmADecoratedInterface::class, 'fake23', [$bString]); + $getInstanceB = $instance->create(IAmADecoratedInterface::class, 'fake', $args); - self::assertEquals(4, FakeInstanceDecorator::$countInstance); self::assertEquals($getInstanceA, $getInstanceB); self::assertNotSame($getInstanceA, $getInstanceB); - self::assertEquals($prefix . $aString, $getInstanceA->getAText()); - self::assertEquals($prefix . $aString, $getInstanceB->getAText()); - self::assertEquals($prefix . $bString, $getInstanceA->getBText()); - self::assertEquals($prefix . $bString, $getInstanceB->getBText()); - self::assertEquals($prefix . $cBool, $getInstanceA->getCBool()); - self::assertEquals($prefix . $cBool, $getInstanceB->getCBool()); + self::assertEquals($aString, $getInstanceA->getAText()); + self::assertEquals($aString, $getInstanceB->getAText()); + self::assertEquals($bString, $getInstanceA->getBText()); + self::assertEquals($bString, $getInstanceB->getBText()); + self::assertEquals($cBool, $getInstanceA->getCBool()); + self::assertEquals($cBool, $getInstanceB->getCBool()); } } diff --git a/tests/src/Core/Hooks/Util/HookFileManagerTest.php b/tests/src/Core/Hooks/Util/HookFileManagerTest.php index cbd9a63b1..746bfa7ca 100644 --- a/tests/src/Core/Hooks/Util/HookFileManagerTest.php +++ b/tests/src/Core/Hooks/Util/HookFileManagerTest.php @@ -31,12 +31,12 @@ class HookFileManagerTest extends MockedTest [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], ], - \Friendica\Core\Hooks\Capabilities\HookType::DECORATOR => [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::DECORATOR => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class, ], @@ -56,12 +56,12 @@ EOF, [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => '', ], ], - \Friendica\Core\Hooks\Capabilities\HookType::DECORATOR => [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::DECORATOR => [ \Psr\Log\LoggerInterface::class => \Psr\Log\NullLogger::class, ], ]; @@ -79,7 +79,7 @@ EOF, [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], @@ -87,7 +87,7 @@ return [ ]; EOF, 'addonsArray' => [ - \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => ['null'], ], @@ -104,7 +104,7 @@ EOF, [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], @@ -112,7 +112,7 @@ return [ ]; EOF, 'addonsArray' => [ - \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => 'null', ], @@ -130,7 +130,7 @@ EOF, [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], @@ -138,7 +138,7 @@ return [ ]; EOF, 'addonsArray' => [ - \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], @@ -163,7 +163,7 @@ return [ ]; EOF, 'addonsArray' => [ - \Friendica\Core\Hooks\Capabilities\HookType::STRATEGY => [ + \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], From 5c57d8de20d489333b40079c40050157248a873e Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Jul 2023 00:13:03 +0200 Subject: [PATCH 15/24] Add license --- .../Core/Hooks/Util/HookFileManagerTest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/src/Core/Hooks/Util/HookFileManagerTest.php b/tests/src/Core/Hooks/Util/HookFileManagerTest.php index 746bfa7ca..1cc4b5c90 100644 --- a/tests/src/Core/Hooks/Util/HookFileManagerTest.php +++ b/tests/src/Core/Hooks/Util/HookFileManagerTest.php @@ -1,4 +1,23 @@ . + * + */ namespace Friendica\Test\src\Core\Hooks\Util; From 903ecc2a76525ee9cea754e25ea3525f60ca412b Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Jul 2023 00:15:16 +0200 Subject: [PATCH 16/24] Make PHP-CS happy --- src/Core/Hooks/Model/DiceInstanceManager.php | 2 +- tests/src/Core/Addon/Model/AddonLoaderTest.php | 6 +++--- tests/src/Core/Hooks/Util/HookFileManagerTest.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Core/Hooks/Model/DiceInstanceManager.php b/src/Core/Hooks/Model/DiceInstanceManager.php index 7c35dfe4d..77b9c4d49 100644 --- a/src/Core/Hooks/Model/DiceInstanceManager.php +++ b/src/Core/Hooks/Model/DiceInstanceManager.php @@ -35,7 +35,7 @@ use Friendica\Core\Hooks\Util\HookFileManager; */ class DiceInstanceManager implements ICanCreateInstances, ICanRegisterInstances { - protected $instance = []; + protected $instance = []; /** @var Dice */ protected $dice; diff --git a/tests/src/Core/Addon/Model/AddonLoaderTest.php b/tests/src/Core/Addon/Model/AddonLoaderTest.php index be125535f..3e9a325b1 100644 --- a/tests/src/Core/Addon/Model/AddonLoaderTest.php +++ b/tests/src/Core/Addon/Model/AddonLoaderTest.php @@ -72,7 +72,7 @@ EOF; public function dataHooks(): array { return [ - 'normal' => [ + 'normal' => [ 'structure' => $this->structure, 'enabled' => $this->addons, 'files' => [ @@ -86,7 +86,7 @@ EOF; ], ], ], - 'double' => [ + 'double' => [ 'structure' => $this->structure, 'enabled' => $this->addons, 'files' => [ @@ -101,7 +101,7 @@ EOF; ], ], ], - 'wrongName' => [ + 'wrongName' => [ 'structure' => $this->structure, 'enabled' => $this->addons, 'files' => [ diff --git a/tests/src/Core/Hooks/Util/HookFileManagerTest.php b/tests/src/Core/Hooks/Util/HookFileManagerTest.php index 1cc4b5c90..216a9832f 100644 --- a/tests/src/Core/Hooks/Util/HookFileManagerTest.php +++ b/tests/src/Core/Hooks/Util/HookFileManagerTest.php @@ -62,7 +62,7 @@ return [ ], ]; EOF, - 'addonsArray' => [], + 'addonsArray' => [], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''], ], @@ -85,7 +85,7 @@ return [ ], ]; EOF, - 'addonsArray' => [], + 'addonsArray' => [], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''], ], From bca6abf4abaa35e290d518de3aea0a73caedffb1 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Jul 2023 01:06:11 +0200 Subject: [PATCH 17/24] Fix logger classes and tests --- src/Core/Logger/Factory/StreamLogger.php | 6 +-- src/Core/Logger/Util/FileSystem.php | 10 ++-- .../src/Core/Addon/Model/AddonLoaderTest.php | 6 +-- .../Core/Hooks/Util/HookFileManagerTest.php | 24 +-------- tests/src/Core/Logger/StreamLoggerTest.php | 54 +++++-------------- 5 files changed, 28 insertions(+), 72 deletions(-) diff --git a/src/Core/Logger/Factory/StreamLogger.php b/src/Core/Logger/Factory/StreamLogger.php index 6da4b8cd8..caad78e3b 100644 --- a/src/Core/Logger/Factory/StreamLogger.php +++ b/src/Core/Logger/Factory/StreamLogger.php @@ -25,10 +25,10 @@ use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Logger\Capabilities\LogChannel; use Friendica\Core\Logger\Exception\LoggerArgumentException; use Friendica\Core\Logger\Exception\LoggerException; +use Friendica\Core\Logger\Exception\LogLevelException; use Friendica\Core\Logger\Type\StreamLogger as StreamLoggerClass; use Friendica\Core\Logger\Util\FileSystem; use Psr\Log\LoggerInterface; -use Psr\Log\LogLevel; use Psr\Log\NullLogger; /** @@ -55,7 +55,7 @@ class StreamLogger extends AbstractLoggerTypeFactory $fileSystem = new FileSystem(); $logfile = $logfile ?? $config->get('system', 'logfile'); - if ((@file_exists($logfile) && !@is_writable($logfile)) && !@is_writable(basename($logfile))) { + if (!@file_exists($logfile) || !@is_writable($logfile)) { throw new LoggerArgumentException(sprintf('%s is not a valid logfile', $logfile)); } @@ -64,7 +64,7 @@ class StreamLogger extends AbstractLoggerTypeFactory if (array_key_exists($loglevel, StreamLoggerClass::levelToInt)) { $loglevel = StreamLoggerClass::levelToInt[$loglevel]; } else { - $loglevel = StreamLoggerClass::levelToInt[LogLevel::NOTICE]; + throw new LogLevelException(sprintf('The level "%s" is not valid.', $loglevel)); } $stream = $fileSystem->createStream($logfile); diff --git a/src/Core/Logger/Util/FileSystem.php b/src/Core/Logger/Util/FileSystem.php index 3793ac4a9..41bb423b4 100644 --- a/src/Core/Logger/Util/FileSystem.php +++ b/src/Core/Logger/Util/FileSystem.php @@ -21,6 +21,8 @@ namespace Friendica\Core\Logger\Util; +use Friendica\Core\Logger\Exception\LoggerUnusableException; + /** * Util class for filesystem manipulation for Logger classes */ @@ -37,6 +39,8 @@ class FileSystem * @param string $file The file * * @return string The directory name (empty if no directory is found, like urls) + * + * @throws LoggerUnusableException */ public function createDir(string $file): string { @@ -57,7 +61,7 @@ class FileSystem restore_error_handler(); if (!$status && !is_dir($dirname)) { - throw new \UnexpectedValueException(sprintf('Directory "%s" cannot get created: ' . $this->errorMessage, $dirname)); + throw new LoggerUnusableException(sprintf('Directory "%s" cannot get created: ' . $this->errorMessage, $dirname)); } return $dirname; @@ -75,7 +79,7 @@ class FileSystem * * @return resource the open stream resource * - * @throws \UnexpectedValueException + * @throws LoggerUnusableException */ public function createStream(string $url) { @@ -89,7 +93,7 @@ class FileSystem restore_error_handler(); if (!is_resource($stream)) { - throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: ' . $this->errorMessage, $url)); + throw new LoggerUnusableException(sprintf('The stream or file "%s" could not be opened: ' . $this->errorMessage, $url)); } return $stream; diff --git a/tests/src/Core/Addon/Model/AddonLoaderTest.php b/tests/src/Core/Addon/Model/AddonLoaderTest.php index 3e9a325b1..189110fde 100644 --- a/tests/src/Core/Addon/Model/AddonLoaderTest.php +++ b/tests/src/Core/Addon/Model/AddonLoaderTest.php @@ -72,7 +72,7 @@ EOF; public function dataHooks(): array { return [ - 'normal' => [ + 'normal' => [ 'structure' => $this->structure, 'enabled' => $this->addons, 'files' => [ @@ -86,7 +86,7 @@ EOF; ], ], ], - 'double' => [ + 'double' => [ 'structure' => $this->structure, 'enabled' => $this->addons, 'files' => [ @@ -101,7 +101,7 @@ EOF; ], ], ], - 'wrongName' => [ + 'wrongName' => [ 'structure' => $this->structure, 'enabled' => $this->addons, 'files' => [ diff --git a/tests/src/Core/Hooks/Util/HookFileManagerTest.php b/tests/src/Core/Hooks/Util/HookFileManagerTest.php index 216a9832f..f718e8a92 100644 --- a/tests/src/Core/Hooks/Util/HookFileManagerTest.php +++ b/tests/src/Core/Hooks/Util/HookFileManagerTest.php @@ -55,20 +55,12 @@ return [ \Psr\Log\NullLogger::class => [''], ], ], - \Friendica\Core\Hooks\Capabilities\BehavioralHookType::DECORATOR => [ - \Psr\Log\LoggerInterface::class => [ - \Psr\Log\NullLogger::class, - ], - ], ]; EOF, 'addonsArray' => [], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''], ], - 'assertDecorators' => [ - [LoggerInterface::class, NullLogger::class], - ], ], 'normalWithString' => [ 'content' => << '', ], ], - \Friendica\Core\Hooks\Capabilities\BehavioralHookType::DECORATOR => [ - \Psr\Log\LoggerInterface::class => \Psr\Log\NullLogger::class, - ], ]; EOF, 'addonsArray' => [], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''], ], - 'assertDecorators' => [ - [LoggerInterface::class, NullLogger::class], - ], ], 'withAddons' => [ 'content' => << [], ], 'withAddonsWithString' => [ 'content' => << [], ], // This should work because unique name convention is part of the instance manager logic, not of the file-infrastructure layer 'withAddonsDoubleNamed' => [ @@ -167,7 +151,6 @@ EOF, [LoggerInterface::class, NullLogger::class, ''], [LoggerInterface::class, NullLogger::class, ''], ], - 'assertDecorators' => [], ], 'withWrongContentButAddons' => [ 'content' => << [ [LoggerInterface::class, NullLogger::class, ''], ], - 'assertDecorators' => [], ], ]; } @@ -199,7 +181,7 @@ EOF, /** * @dataProvider dataHooks */ - public function testSetupHooks(string $content, array $addonsArray, array $assertStrategies, array $assertDecorators) + public function testSetupHooks(string $content, array $addonsArray, array $assertStrategies) { vfsStream::newFile('static/hooks.config.php') ->withContent($content) @@ -215,10 +197,6 @@ EOF, $instanceManager->shouldReceive('registerStrategy')->withArgs($assertStrategy)->once(); } - foreach ($assertDecorators as $assertDecorator) { - $instanceManager->shouldReceive('registerDecorator')->withArgs($assertDecorator)->once(); - } - $hookFileManager->setupHooks($instanceManager); self::expectNotToPerformAssertions(); diff --git a/tests/src/Core/Logger/StreamLoggerTest.php b/tests/src/Core/Logger/StreamLoggerTest.php index c8fac4939..a1e0af5bb 100644 --- a/tests/src/Core/Logger/StreamLoggerTest.php +++ b/tests/src/Core/Logger/StreamLoggerTest.php @@ -22,9 +22,7 @@ namespace Friendica\Test\src\Core\Logger; use Friendica\Core\Logger\Exception\LoggerArgumentException; -use Friendica\Core\Logger\Exception\LoggerException; use Friendica\Core\Logger\Exception\LogLevelException; -use Friendica\Core\Logger\Util\FileSystem; use Friendica\Test\Util\VFSTrait; use Friendica\Core\Logger\Type\StreamLogger; use org\bovigo\vfs\vfsStream; @@ -40,33 +38,26 @@ class StreamLoggerTest extends AbstractLoggerTest */ private $logfile; - /** - * @var \Friendica\Core\Logger\Util\Filesystem - */ - private $fileSystem; - protected function setUp(): void { parent::setUp(); $this->setUpVfsDir(); - - $this->fileSystem = new FileSystem(); } /** * {@@inheritdoc} */ - protected function getInstance($level = LogLevel::DEBUG) + protected function getInstance($level = LogLevel::DEBUG, $logfile = 'friendica.log') { - $this->logfile = vfsStream::newFile('friendica.log') + $this->logfile = vfsStream::newFile($logfile) ->at($this->root); $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($this->logfile->url())->once(); + $this->config->shouldReceive('get')->with('system', 'loglevel')->andReturn($level)->once(); - $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem, $level); - - return $logger; + $loggerFactory = new \Friendica\Core\Logger\Factory\StreamLogger($this->introspection, 'test'); + return $loggerFactory->create($this->config); } /** @@ -83,11 +74,12 @@ class StreamLoggerTest extends AbstractLoggerTest public function testNoUrl() { $this->expectException(LoggerArgumentException::class); - $this->expectExceptionMessage("Missing stream URL."); + $this->expectExceptionMessage(' is not a valid logfile'); $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn('')->once(); - $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem); + $loggerFactory = new \Friendica\Core\Logger\Factory\StreamLogger($this->introspection, 'test'); + $logger = $loggerFactory->create($this->config); $logger->emergency('not working'); } @@ -104,7 +96,8 @@ class StreamLoggerTest extends AbstractLoggerTest $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($logfile->url())->once(); - $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem); + $loggerFactory = new \Friendica\Core\Logger\Factory\StreamLogger($this->introspection, 'test'); + $logger = $loggerFactory->create($this->config); $logger->emergency('not working'); } @@ -119,7 +112,8 @@ class StreamLoggerTest extends AbstractLoggerTest static::markTestIncomplete('We need a platform independent way to set directory to readonly'); - $logger = new StreamLogger('test', '/$%/wrong/directory/file.txt', $this->introspection, $this->fileSystem); + $loggerFactory = new \Friendica\Core\Logger\Factory\StreamLogger($this->introspection, 'test'); + $logger = $loggerFactory->create($this->config); $logger->emergency('not working'); } @@ -132,9 +126,7 @@ class StreamLoggerTest extends AbstractLoggerTest $this->expectException(LogLevelException::class); $this->expectExceptionMessageMatches("/The level \".*\" is not valid./"); - $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn('file.text')->once(); - - $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem, 'NOPE'); + $logger = $this->getInstance('NOPE'); } /** @@ -145,29 +137,11 @@ class StreamLoggerTest extends AbstractLoggerTest $this->expectException(LogLevelException::class); $this->expectExceptionMessageMatches("/The level \".*\" is not valid./"); - $logfile = vfsStream::newFile('friendica.log') - ->at($this->root); - - $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($logfile->url())->once(); - - $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem); + $logger = $this->getInstance('NOPE'); $logger->log('NOPE', 'a test'); } - /** - * Test when the file is null - */ - public function testWrongFile() - { - $this->expectException(LoggerArgumentException::class); - $this->expectExceptionMessage("A stream must either be a resource or a string."); - - $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn(null)->once(); - - $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem); - } - /** * Test a relative path * @doesNotPerformAssertions From 8dbbf882a8b21c8e7c7398db7e0cad8d81f2a6b8 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Jul 2023 01:16:29 +0200 Subject: [PATCH 18/24] Fix SyslogLogger and tests --- src/Core/Logger/Factory/SyslogLogger.php | 6 +-- tests/functional/DependencyCheckTest.php | 2 +- .../Logger/SyslogLoggerFactoryWrapper.php | 47 +++++++++++++++++++ tests/src/Core/Logger/SyslogLoggerTest.php | 15 +++--- tests/src/Core/Logger/SyslogLoggerWrapper.php | 8 ++-- 5 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 tests/src/Core/Logger/SyslogLoggerFactoryWrapper.php diff --git a/src/Core/Logger/Factory/SyslogLogger.php b/src/Core/Logger/Factory/SyslogLogger.php index 385625862..7b1712344 100644 --- a/src/Core/Logger/Factory/SyslogLogger.php +++ b/src/Core/Logger/Factory/SyslogLogger.php @@ -23,9 +23,9 @@ namespace Friendica\Core\Logger\Factory; use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Logger\Exception\LoggerException; +use Friendica\Core\Logger\Exception\LogLevelException; use Friendica\Core\Logger\Type\SyslogLogger as SyslogLoggerClass; use Psr\Log\LoggerInterface; -use Psr\Log\LogLevel; /** * The logger factory for the SyslogLogger instance @@ -49,10 +49,10 @@ class SyslogLogger extends AbstractLoggerTypeFactory $logFacility = $config->get('system', 'syslog_facility') ?? SyslogLoggerClass::DEFAULT_FACILITY; $loglevel = SyslogLogger::mapLegacyConfigDebugLevel($config->get('system', 'loglevel')); - if (!array_key_exists($loglevel, SyslogLoggerClass::logLevels)) { + if (array_key_exists($loglevel, SyslogLoggerClass::logLevels)) { $loglevel = SyslogLoggerClass::logLevels[$loglevel]; } else { - $loglevel = SyslogLoggerClass::logLevels[LogLevel::NOTICE]; + throw new LogLevelException(sprintf('The level "%s" is not valid.', $loglevel)); } return new SyslogLoggerClass($this->channel, $this->introspection, $loglevel, $logOpts, $logFacility); diff --git a/tests/functional/DependencyCheckTest.php b/tests/functional/DependencyCheckTest.php index 89b8f79a5..4488fe586 100644 --- a/tests/functional/DependencyCheckTest.php +++ b/tests/functional/DependencyCheckTest.php @@ -145,7 +145,7 @@ class DependencyCheckTest extends FixtureTest $config->set('system', 'dlogfile', $this->root->url() . '/friendica.log'); /** @var LoggerInterface $logger */ - $logger = $this->dice->create('$devLogger', [['$channel' => 'dev']]); + $logger = $this->dice->create('$devLogger', ['dev']); self::assertInstanceOf(LoggerInterface::class, $logger); } diff --git a/tests/src/Core/Logger/SyslogLoggerFactoryWrapper.php b/tests/src/Core/Logger/SyslogLoggerFactoryWrapper.php new file mode 100644 index 000000000..30aaa4477 --- /dev/null +++ b/tests/src/Core/Logger/SyslogLoggerFactoryWrapper.php @@ -0,0 +1,47 @@ +. + * + */ + +namespace Friendica\Test\src\Core\Logger; + +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Logger\Exception\LogLevelException; +use Friendica\Core\Logger\Factory\SyslogLogger; +use Friendica\Core\Logger\Type\SyslogLogger as SyslogLoggerClass; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +class SyslogLoggerFactoryWrapper extends SyslogLogger +{ + public function create(IManageConfigValues $config): LoggerInterface + { + $logOpts = $config->get('system', 'syslog_flags') ?? SyslogLoggerClass::DEFAULT_FLAGS; + $logFacility = $config->get('system', 'syslog_facility') ?? SyslogLoggerClass::DEFAULT_FACILITY; + $loglevel = SyslogLogger::mapLegacyConfigDebugLevel($config->get('system', 'loglevel')); + + if (array_key_exists($loglevel, SyslogLoggerClass::logLevels)) { + $loglevel = SyslogLoggerClass::logLevels[$loglevel]; + } else { + throw new LogLevelException(sprintf('The level "%s" is not valid.', $loglevel)); + } + + return new SyslogLoggerWrapper($this->channel, $this->introspection, $loglevel, $logOpts, $logFacility); + } +} diff --git a/tests/src/Core/Logger/SyslogLoggerTest.php b/tests/src/Core/Logger/SyslogLoggerTest.php index 53ace79c0..c22aecc10 100644 --- a/tests/src/Core/Logger/SyslogLoggerTest.php +++ b/tests/src/Core/Logger/SyslogLoggerTest.php @@ -21,8 +21,6 @@ namespace Friendica\Test\src\Core\Logger; -use Friendica\Core\Logger\Exception\LoggerArgumentException; -use Friendica\Core\Logger\Exception\LoggerException; use Friendica\Core\Logger\Exception\LogLevelException; use Friendica\Core\Logger\Type\SyslogLogger; use Psr\Log\LogLevel; @@ -58,7 +56,10 @@ class SyslogLoggerTest extends AbstractLoggerTest */ protected function getInstance($level = LogLevel::DEBUG) { - $this->logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection, $level); + $this->config->shouldReceive('get')->with('system', 'loglevel')->andReturn($level); + + $loggerFactory = new SyslogLoggerFactoryWrapper($this->introspection, 'test'); + $this->logger = $loggerFactory->create($this->config); return $this->logger; } @@ -71,8 +72,8 @@ class SyslogLoggerTest extends AbstractLoggerTest { $this->expectException(LogLevelException::class); $this->expectExceptionMessageMatches("/The level \".*\" is not valid./"); - - $logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection, 'NOPE'); + + $logger = $this->getInstance('NOPE'); } /** @@ -83,7 +84,7 @@ class SyslogLoggerTest extends AbstractLoggerTest $this->expectException(LogLevelException::class); $this->expectExceptionMessageMatches("/The level \".*\" is not valid./"); - $logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection); + $logger = $this->getInstance(); $logger->log('NOPE', 'a test'); } @@ -94,7 +95,7 @@ class SyslogLoggerTest extends AbstractLoggerTest */ public function testClose() { - $logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection); + $logger = $this->getInstance(); $logger->emergency('test'); $logger->close(); // Reopened itself diff --git a/tests/src/Core/Logger/SyslogLoggerWrapper.php b/tests/src/Core/Logger/SyslogLoggerWrapper.php index 9fd16706a..1e0c4535f 100644 --- a/tests/src/Core/Logger/SyslogLoggerWrapper.php +++ b/tests/src/Core/Logger/SyslogLoggerWrapper.php @@ -21,10 +21,8 @@ namespace Friendica\Test\src\Core\Logger; -use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections; use Friendica\Core\Logger\Type\SyslogLogger; -use Friendica\Core\Logger\Util\Introspection; -use Psr\Log\LogLevel; /** * Wraps the SyslogLogger for replacing the syslog call with a string field. @@ -33,9 +31,9 @@ class SyslogLoggerWrapper extends SyslogLogger { private $content; - public function __construct($channel, IManageConfigValues $config, Introspection $introspection, $level = LogLevel::NOTICE) + public function __construct(string $channel, IHaveCallIntrospections $introspection, string $logLevel, string $logOptions, string $logFacility) { - parent::__construct($channel, $config, $introspection, $level); + parent::__construct($channel, $introspection, $logLevel, $logOptions, $logFacility); $this->content = ''; } From 447f9daa9f30a020902656f90a058cf19ec03562 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Jul 2023 01:19:37 +0200 Subject: [PATCH 19/24] Fix PHP-CS --- tests/src/Core/Logger/SyslogLoggerFactoryWrapper.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/src/Core/Logger/SyslogLoggerFactoryWrapper.php b/tests/src/Core/Logger/SyslogLoggerFactoryWrapper.php index 30aaa4477..63209cad3 100644 --- a/tests/src/Core/Logger/SyslogLoggerFactoryWrapper.php +++ b/tests/src/Core/Logger/SyslogLoggerFactoryWrapper.php @@ -26,7 +26,6 @@ use Friendica\Core\Logger\Exception\LogLevelException; use Friendica\Core\Logger\Factory\SyslogLogger; use Friendica\Core\Logger\Type\SyslogLogger as SyslogLoggerClass; use Psr\Log\LoggerInterface; -use Psr\Log\LogLevel; class SyslogLoggerFactoryWrapper extends SyslogLogger { From 3a0c18713e5c83824a410547c6c7fd804220f1d1 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Jul 2023 01:41:02 +0200 Subject: [PATCH 20/24] Remove unused ProfilerLogger factory --- src/Core/Logger/Factory/ProfilerLogger.php | 53 ---------------------- static/dependencies.config.php | 6 --- 2 files changed, 59 deletions(-) delete mode 100644 src/Core/Logger/Factory/ProfilerLogger.php diff --git a/src/Core/Logger/Factory/ProfilerLogger.php b/src/Core/Logger/Factory/ProfilerLogger.php deleted file mode 100644 index 8c07b51b6..000000000 --- a/src/Core/Logger/Factory/ProfilerLogger.php +++ /dev/null @@ -1,53 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Logger\Factory; - -use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Logger\Type\ProfilerLogger as ProfilerLoggerClass; -use Friendica\Util\Profiler; -use Psr\Log\LoggerInterface; - -/** - * The logger factory for the ProfilerLogger - * - * @see ProfilerLoggerClass - */ -class ProfilerLogger extends AbstractLoggerTypeFactory -{ - /** - * Wraps a given Logger with profiling information in case profiling is enabled - * - * @param IManageConfigValues $config The system configuration - * @param LoggerInterface $logger The given logger class, which should get wrapped - * @param Profiler $profiler The profiler utility - * - * @return LoggerInterface The PSR-3 compliant logger instance - */ - public function create(IManageConfigValues $config, LoggerInterface $logger, Profiler $profiler): LoggerInterface - { - if ($config->get('system', 'profiling') ?? false) { - return $logger; - } else { - return new ProfilerLoggerClass($logger, $profiler); - } - } -} diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 2b246f845..ce8a5bd9d 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -198,12 +198,6 @@ return [ ['create', [], Dice::CHAIN_CALL], ], ], - \Friendica\Core\Logger\Type\ProfilerLogger::class => [ - 'instanceOf' => \Friendica\Core\Logger\Factory\ProfilerLogger::class, - 'call' => [ - ['create', [], Dice::CHAIN_CALL], - ], - ], \Friendica\Core\Logger\Capabilities\IHaveCallIntrospections::class => [ 'instanceOf' => \Friendica\Core\Logger\Util\Introspection::class, 'constructParams' => [ From d440b9a63f2cc2ddfb567a1332aa928a19212e18 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Jul 2023 01:56:13 +0200 Subject: [PATCH 21/24] Restore LoggerInterface import --- bin/daemon.php | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/daemon.php b/bin/daemon.php index 1f0bb7079..317798a1d 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -38,6 +38,7 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Util\DateTimeFormat; +use Psr\Log\LoggerInterface; // Get options $shortopts = 'f'; From e659a0314086dd700dbe5e754e383ab758725805 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 17 Jul 2023 20:20:21 +0200 Subject: [PATCH 22/24] Apply suggestions from code review Co-authored-by: Hypolite Petovan --- bin/daemon.php | 2 +- bin/worker.php | 2 +- src/Core/Addon/Exception/AddonInvalidConfigFileException.php | 2 +- src/Core/Hooks/Exceptions/HookConfigException.php | 2 +- src/Core/Logger/Exception/LogLevelException.php | 2 +- src/Core/Logger/Exception/LoggerArgumentException.php | 2 +- src/Core/Logger/Exception/LoggerException.php | 2 +- src/Core/Logger/Exception/LoggerUnusableException.php | 2 +- src/Core/Logger/Factory/AbstractLoggerTypeFactory.php | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bin/daemon.php b/bin/daemon.php index 317798a1d..11bad2b11 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -63,7 +63,7 @@ $dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config /** @var \Friendica\Core\Addon\Capabilities\ICanLoadAddons $addonLoader */ $addonLoader = $dice->create(\Friendica\Core\Addon\Capabilities\ICanLoadAddons::class); $dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies')); -$dice = $dice->addRule(LoggerInterface::class,['constructParams' => [Logger\Capabilities\LogChannel::DAEMON]]); +$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [Logger\Capabilities\LogChannel::DAEMON]]); DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); diff --git a/bin/worker.php b/bin/worker.php index 18a41e064..42a8f533d 100755 --- a/bin/worker.php +++ b/bin/worker.php @@ -58,7 +58,7 @@ $dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config /** @var \Friendica\Core\Addon\Capabilities\ICanLoadAddons $addonLoader */ $addonLoader = $dice->create(\Friendica\Core\Addon\Capabilities\ICanLoadAddons::class); $dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies')); -$dice = $dice->addRule(LoggerInterface::class,['constructParams' => [LogChannel::WORKER]]); +$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [LogChannel::WORKER]]); DI::init($dice); \Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class)); diff --git a/src/Core/Addon/Exception/AddonInvalidConfigFileException.php b/src/Core/Addon/Exception/AddonInvalidConfigFileException.php index 3bb41be53..bf173d0a4 100644 --- a/src/Core/Addon/Exception/AddonInvalidConfigFileException.php +++ b/src/Core/Addon/Exception/AddonInvalidConfigFileException.php @@ -28,7 +28,7 @@ use Throwable; */ class AddonInvalidConfigFileException extends \RuntimeException { - public function __construct($message = "", $code = 0, Throwable $previous = null) + public function __construct($message = '', $code = 0, Throwable $previous = null) { parent::__construct($message, 500, $previous); } diff --git a/src/Core/Hooks/Exceptions/HookConfigException.php b/src/Core/Hooks/Exceptions/HookConfigException.php index ceca721b1..6588787eb 100644 --- a/src/Core/Hooks/Exceptions/HookConfigException.php +++ b/src/Core/Hooks/Exceptions/HookConfigException.php @@ -23,7 +23,7 @@ namespace Friendica\Core\Hooks\Exceptions; class HookConfigException extends \RuntimeException { - public function __construct($message = "", \Throwable $previous = null) + public function __construct($message = '', \Throwable $previous = null) { parent::__construct($message, 500, $previous); } diff --git a/src/Core/Logger/Exception/LogLevelException.php b/src/Core/Logger/Exception/LogLevelException.php index a79db80f5..9cf649a86 100644 --- a/src/Core/Logger/Exception/LogLevelException.php +++ b/src/Core/Logger/Exception/LogLevelException.php @@ -28,7 +28,7 @@ use Throwable; */ class LogLevelException extends \InvalidArgumentException { - public function __construct($message = "", Throwable $previous = null) + public function __construct($message = '', Throwable $previous = null) { parent::__construct($message, 500, $previous); } diff --git a/src/Core/Logger/Exception/LoggerArgumentException.php b/src/Core/Logger/Exception/LoggerArgumentException.php index bbc1fc066..6925be4c7 100644 --- a/src/Core/Logger/Exception/LoggerArgumentException.php +++ b/src/Core/Logger/Exception/LoggerArgumentException.php @@ -28,7 +28,7 @@ use Throwable; */ class LoggerArgumentException extends \InvalidArgumentException { - public function __construct($message = "", Throwable $previous = null) + public function __construct($message = '', Throwable $previous = null) { parent::__construct($message, 500, $previous); } diff --git a/src/Core/Logger/Exception/LoggerException.php b/src/Core/Logger/Exception/LoggerException.php index 742557678..7529f6e78 100644 --- a/src/Core/Logger/Exception/LoggerException.php +++ b/src/Core/Logger/Exception/LoggerException.php @@ -28,7 +28,7 @@ use Throwable; */ class LoggerException extends \Exception { - public function __construct($message = "", Throwable $previous = null) + public function __construct($message = '', Throwable $previous = null) { parent::__construct($message, 500, $previous); } diff --git a/src/Core/Logger/Exception/LoggerUnusableException.php b/src/Core/Logger/Exception/LoggerUnusableException.php index 46e49fd41..401a63482 100644 --- a/src/Core/Logger/Exception/LoggerUnusableException.php +++ b/src/Core/Logger/Exception/LoggerUnusableException.php @@ -28,7 +28,7 @@ use Throwable; */ class LoggerUnusableException extends \RuntimeException { - public function __construct($message = "", Throwable $previous = null) + public function __construct($message = '', Throwable $previous = null) { parent::__construct($message, 500, $previous); } diff --git a/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php b/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php index a6ff5eebb..402176d8e 100644 --- a/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php +++ b/src/Core/Logger/Factory/AbstractLoggerTypeFactory.php @@ -69,7 +69,7 @@ abstract class AbstractLoggerTypeFactory return LogLevel::INFO; // legacy DATA case "4": - // legacy ALL + // legacy ALL case "5": return LogLevel::DEBUG; // default if nothing set From cba656383eb3af306fcd62752620c241a76960f1 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 21 Jul 2023 22:41:36 +0200 Subject: [PATCH 23/24] Adhere feedback - rename hooks.config.php to strategies.config.php - change all corresponding classes and tests --- doc/StrategyHooks.md | 2 +- .../Capabilities/ICanCreateInstances.php | 4 +- ...stances.php => ICanRegisterStrategies.php} | 4 +- src/Core/Hooks/Model/DiceInstanceManager.php | 20 ++--- ...eManager.php => StrategiesFileManager.php} | 46 ++++------- src/Core/Logger/Factory/Logger.php | 4 +- src/DI.php | 2 +- src/Module/Admin/Summary.php | 4 +- static/dependencies.config.php | 9 ++- ...hooks.config.php => strategies.config.php} | 10 +-- .../Core/Hooks/Model/InstanceManagerTest.php | 8 +- ...Test.php => StrategiesFileManagerTest.php} | 80 +++++-------------- 12 files changed, 72 insertions(+), 121 deletions(-) rename src/Core/Hooks/Capabilities/{ICanRegisterInstances.php => ICanRegisterStrategies.php} (93%) rename src/Core/Hooks/Util/{HookFileManager.php => StrategiesFileManager.php} (62%) rename static/{hooks.config.php => strategies.config.php} (83%) rename tests/src/Core/Hooks/Util/{HookFileManagerTest.php => StrategiesFileManagerTest.php} (64%) diff --git a/doc/StrategyHooks.md b/doc/StrategyHooks.md index cb355f93f..39fc1bd8d 100644 --- a/doc/StrategyHooks.md +++ b/doc/StrategyHooks.md @@ -43,7 +43,7 @@ public class ConcreteClassB implements ExampleInterface } } -/** @var \Friendica\Core\Hooks\Capabilities\ICanRegisterInstances $instanceRegister */ +/** @var \Friendica\Core\Hooks\Capabilities\ICanRegisterStrategies $instanceRegister */ $instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassA::class, 'A'); $instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassB::class, 'B'); diff --git a/src/Core/Hooks/Capabilities/ICanCreateInstances.php b/src/Core/Hooks/Capabilities/ICanCreateInstances.php index f2e4b8b0a..77d4c4b36 100644 --- a/src/Core/Hooks/Capabilities/ICanCreateInstances.php +++ b/src/Core/Hooks/Capabilities/ICanCreateInstances.php @@ -32,10 +32,10 @@ interface ICanCreateInstances * The instance will be build based on the registered strategy and the (unique) name * * @param string $class The fully-qualified name of the given class or interface which will get returned - * @param string $name An arbitrary identifier to find a concrete instance strategy. + * @param string $strategy An arbitrary identifier to find a concrete instance strategy. * @param array $arguments Additional arguments, which can be passed to the constructor of "$class" at runtime * * @return object The concrete instance of the type "$class" */ - public function create(string $class, string $name, array $arguments = []): object; + public function create(string $class, string $strategy, array $arguments = []): object; } diff --git a/src/Core/Hooks/Capabilities/ICanRegisterInstances.php b/src/Core/Hooks/Capabilities/ICanRegisterStrategies.php similarity index 93% rename from src/Core/Hooks/Capabilities/ICanRegisterInstances.php rename to src/Core/Hooks/Capabilities/ICanRegisterStrategies.php index f7689bbee..911eb3499 100644 --- a/src/Core/Hooks/Capabilities/ICanRegisterInstances.php +++ b/src/Core/Hooks/Capabilities/ICanRegisterStrategies.php @@ -26,7 +26,7 @@ use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException; /** * Register strategies for given classes */ -interface ICanRegisterInstances +interface ICanRegisterStrategies { /** * Register a class(strategy) for a given interface with a unique name. @@ -36,7 +36,7 @@ interface ICanRegisterInstances * @param string $interface The interface, which the given class implements * @param string $class The fully-qualified given class name * A placeholder for dependencies is possible as well - * @param ?string $name An arbitrary identifier for the given class, which will be used for factories, dependency injections etc. + * @param ?string $name An arbitrary identifier for the given strategy, which will be used for factories, dependency injections etc. * * @return $this This interface for chain-calls * diff --git a/src/Core/Hooks/Model/DiceInstanceManager.php b/src/Core/Hooks/Model/DiceInstanceManager.php index 77b9c4d49..a8b0b540c 100644 --- a/src/Core/Hooks/Model/DiceInstanceManager.php +++ b/src/Core/Hooks/Model/DiceInstanceManager.php @@ -23,31 +23,31 @@ namespace Friendica\Core\Hooks\Model; use Dice\Dice; use Friendica\Core\Hooks\Capabilities\ICanCreateInstances; -use Friendica\Core\Hooks\Capabilities\ICanRegisterInstances; +use Friendica\Core\Hooks\Capabilities\ICanRegisterStrategies; use Friendica\Core\Hooks\Exceptions\HookInstanceException; use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException; -use Friendica\Core\Hooks\Util\HookFileManager; +use Friendica\Core\Hooks\Util\StrategiesFileManager; /** * This class represents an instance register, which uses Dice for creation * * @see Dice */ -class DiceInstanceManager implements ICanCreateInstances, ICanRegisterInstances +class DiceInstanceManager implements ICanCreateInstances, ICanRegisterStrategies { protected $instance = []; /** @var Dice */ protected $dice; - public function __construct(Dice $dice, HookFileManager $hookFileManager) + public function __construct(Dice $dice, StrategiesFileManager $strategiesFileManager) { $this->dice = $dice; - $hookFileManager->setupHooks($this); + $strategiesFileManager->setupStrategies($this); } /** {@inheritDoc} */ - public function registerStrategy(string $interface, string $class, ?string $name = null): ICanRegisterInstances + public function registerStrategy(string $interface, string $class, ?string $name = null): ICanRegisterStrategies { if (!empty($this->instance[$interface][$name])) { throw new HookRegisterArgumentException(sprintf('A class with the name %s is already set for the interface %s', $name, $interface)); @@ -59,12 +59,12 @@ class DiceInstanceManager implements ICanCreateInstances, ICanRegisterInstances } /** {@inheritDoc} */ - public function create(string $class, string $name, array $arguments = []): object + public function create(string $class, string $strategy, array $arguments = []): object { - if (empty($this->instance[$class][$name])) { - throw new HookInstanceException(sprintf('The class with the name %s isn\'t registered for the class or interface %s', $name, $class)); + if (empty($this->instance[$class][$strategy])) { + throw new HookInstanceException(sprintf('The class with the name %s isn\'t registered for the class or interface %s', $strategy, $class)); } - return $this->dice->create($this->instance[$class][$name], $arguments); + return $this->dice->create($this->instance[$class][$strategy], $arguments); } } diff --git a/src/Core/Hooks/Util/HookFileManager.php b/src/Core/Hooks/Util/StrategiesFileManager.php similarity index 62% rename from src/Core/Hooks/Util/HookFileManager.php rename to src/Core/Hooks/Util/StrategiesFileManager.php index b83f2b49c..700c401f2 100644 --- a/src/Core/Hooks/Util/HookFileManager.php +++ b/src/Core/Hooks/Util/StrategiesFileManager.php @@ -22,22 +22,21 @@ namespace Friendica\Core\Hooks\Util; use Friendica\Core\Addon\Capabilities\ICanLoadAddons; -use Friendica\Core\Hooks\Capabilities\BehavioralHookType; -use Friendica\Core\Hooks\Capabilities\ICanRegisterInstances; +use Friendica\Core\Hooks\Capabilities\ICanRegisterStrategies; use Friendica\Core\Hooks\Exceptions\HookConfigException; /** - * Manage all hooks.config.php files + * Manage all strategies.config.php files */ -class HookFileManager +class StrategiesFileManager { const STATIC_DIR = 'static'; - const CONFIG_NAME = 'hooks'; + const CONFIG_NAME = 'strategies'; /** @var ICanLoadAddons */ protected $addonLoader; /** @var array */ - protected $hookConfig = []; + protected $config = []; /** @var string */ protected $basePath; @@ -50,32 +49,21 @@ class HookFileManager /** * Loads all kinds of hooks and registers the corresponding instances * - * @param ICanRegisterInstances $instanceRegister The instance register + * @param ICanRegisterStrategies $instanceRegister The instance register * * @return void */ - public function setupHooks(ICanRegisterInstances $instanceRegister) + public function setupStrategies(ICanRegisterStrategies $instanceRegister) { - // In case it wasn't used before, reload the whole hook config - if (empty($this->hookConfig)) { - $this->reloadHookConfig(); - } - - foreach ($this->hookConfig as $hookType => $classList) { - switch ($hookType) { - case BehavioralHookType::STRATEGY: - foreach ($classList as $interface => $strategy) { - foreach ($strategy as $dependencyName => $names) { - if (is_array($names)) { - foreach ($names as $name) { - $instanceRegister->registerStrategy($interface, $dependencyName, $name); - } - } else { - $instanceRegister->registerStrategy($interface, $dependencyName, $names); - } - } + foreach ($this->config as $interface => $strategy) { + foreach ($strategy as $dependencyName => $names) { + if (is_array($names)) { + foreach ($names as $name) { + $instanceRegister->registerStrategy($interface, $dependencyName, $name); } - break; + } else { + $instanceRegister->registerStrategy($interface, $dependencyName, $names); + } } } } @@ -87,7 +75,7 @@ class HookFileManager * * @return void */ - protected function reloadHookConfig() + public function loadConfig() { // load core hook config $configFile = $this->basePath . '/' . static::STATIC_DIR . '/' . static::CONFIG_NAME . '.config.php'; @@ -102,6 +90,6 @@ class HookFileManager throw new HookConfigException(sprintf('Error loading config file %s.', $configFile)); } - $this->hookConfig = array_merge_recursive($config, $this->addonLoader->getActiveAddonConfig(static::CONFIG_NAME)); + $this->config = array_merge_recursive($config, $this->addonLoader->getActiveAddonConfig(static::CONFIG_NAME)); } } diff --git a/src/Core/Logger/Factory/Logger.php b/src/Core/Logger/Factory/Logger.php index 81f8887ef..c370cc4dd 100644 --- a/src/Core/Logger/Factory/Logger.php +++ b/src/Core/Logger/Factory/Logger.php @@ -43,7 +43,7 @@ class Logger $this->channel = $channel; } - public function create(ICanCreateInstances $createInstances, IManageConfigValues $config, Profiler $profiler): LoggerInterface + public function create(ICanCreateInstances $instanceCreator, IManageConfigValues $config, Profiler $profiler): LoggerInterface { if (empty($config->get('system', 'debugging') ?? false)) { return new NullLogger(); @@ -53,7 +53,7 @@ class Logger try { /** @var LoggerInterface $logger */ - $logger = $createInstances->create(LoggerInterface::class, $name, [$this->channel]); + $logger = $instanceCreator->create(LoggerInterface::class, $name, [$this->channel]); if ($config->get('system', 'profiling') ?? false) { return new ProfilerLoggerClass($logger, $profiler); } else { diff --git a/src/DI.php b/src/DI.php index c62b5e8d7..1917f710d 100644 --- a/src/DI.php +++ b/src/DI.php @@ -297,7 +297,7 @@ abstract class DI static::init($flushDice); } - public static function loggCheck(): ICheckLoggerSettings + public static function logCheck(): ICheckLoggerSettings { return self::$dice->create(LoggerSettingsCheck::class); } diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index 80437305d..99d0e3325 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -126,10 +126,10 @@ class Summary extends BaseAdmin } // Check logfile permission - if (($return = DI::loggCheck()->checkLogfile()) !== null) { + if (($return = DI::logCheck()->checkLogfile()) !== null) { $warningtext[] = $return; } - if (($return = DI::loggCheck()->checkDebugLogfile()) !== null) { + if (($return = DI::logCheck()->checkDebugLogfile()) !== null) { $warningtext[] = $return; } diff --git a/static/dependencies.config.php b/static/dependencies.config.php index ce8a5bd9d..98a9f3077 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -38,7 +38,7 @@ use Friendica\App; use Friendica\Core\Cache; use Friendica\Core\Config; use Friendica\Core\Hooks\Capabilities\ICanCreateInstances; -use Friendica\Core\Hooks\Capabilities\ICanRegisterInstances; +use Friendica\Core\Hooks\Capabilities\ICanRegisterStrategies; use Friendica\Core\Hooks\Model\DiceInstanceManager; use Friendica\Core\PConfig; use Friendica\Core\L10n; @@ -91,12 +91,15 @@ return [ [Dice::INSTANCE => Dice::SELF], ] ], - \Friendica\Core\Hooks\Util\HookFileManager::class => [ + \Friendica\Core\Hooks\Util\StrategiesFileManager::class => [ 'constructParams' => [ [Dice::INSTANCE => '$basepath'], ], + 'call' => [ + ['loadConfig'], + ], ], - ICanRegisterInstances::class => [ + ICanRegisterStrategies::class => [ 'instanceOf' => DiceInstanceManager::class, 'constructParams' => [ [Dice::INSTANCE => Dice::SELF], diff --git a/static/hooks.config.php b/static/strategies.config.php similarity index 83% rename from static/hooks.config.php rename to static/strategies.config.php index d46762f87..bb3598c36 100644 --- a/static/hooks.config.php +++ b/static/strategies.config.php @@ -24,11 +24,9 @@ use Friendica\Core\Logger\Type; use Psr\Log; return [ - H::STRATEGY => [ - Log\LoggerInterface::class => [ - Log\NullLogger::class => [''], - Type\SyslogLogger::class => ['syslog'], - Type\StreamLogger::class => ['stream'], - ], + Log\LoggerInterface::class => [ + Log\NullLogger::class => [''], + Type\SyslogLogger::class => ['syslog'], + Type\StreamLogger::class => ['stream'], ], ]; diff --git a/tests/src/Core/Hooks/Model/InstanceManagerTest.php b/tests/src/Core/Hooks/Model/InstanceManagerTest.php index de2bdc6b8..44459eb2e 100644 --- a/tests/src/Core/Hooks/Model/InstanceManagerTest.php +++ b/tests/src/Core/Hooks/Model/InstanceManagerTest.php @@ -25,7 +25,7 @@ use Dice\Dice; use Friendica\Core\Hooks\Exceptions\HookInstanceException; use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException; use Friendica\Core\Hooks\Model\DiceInstanceManager; -use Friendica\Core\Hooks\Util\HookFileManager; +use Friendica\Core\Hooks\Util\StrategiesFileManager; use Friendica\Test\MockedTest; use Friendica\Test\Util\Hooks\InstanceMocks\FakeInstance; use Friendica\Test\Util\Hooks\InstanceMocks\FakeInstanceDecorator; @@ -34,15 +34,15 @@ use Mockery\MockInterface; class InstanceManagerTest extends MockedTest { - /** @var HookFileManager|MockInterface */ + /** @var StrategiesFileManager|MockInterface */ protected $hookFileManager; protected function setUp(): void { parent::setUp(); - $this->hookFileManager = \Mockery::mock(HookFileManager::class); - $this->hookFileManager->shouldReceive('setupHooks')->withAnyArgs(); + $this->hookFileManager = \Mockery::mock(StrategiesFileManager::class); + $this->hookFileManager->shouldReceive('setupStrategies')->withAnyArgs(); } protected function tearDown(): void diff --git a/tests/src/Core/Hooks/Util/HookFileManagerTest.php b/tests/src/Core/Hooks/Util/StrategiesFileManagerTest.php similarity index 64% rename from tests/src/Core/Hooks/Util/HookFileManagerTest.php rename to tests/src/Core/Hooks/Util/StrategiesFileManagerTest.php index f718e8a92..9e1855da1 100644 --- a/tests/src/Core/Hooks/Util/HookFileManagerTest.php +++ b/tests/src/Core/Hooks/Util/StrategiesFileManagerTest.php @@ -22,16 +22,16 @@ namespace Friendica\Test\src\Core\Hooks\Util; use Friendica\Core\Addon\Capabilities\ICanLoadAddons; -use Friendica\Core\Hooks\Capabilities\ICanRegisterInstances; +use Friendica\Core\Hooks\Capabilities\ICanRegisterStrategies; use Friendica\Core\Hooks\Exceptions\HookConfigException; -use Friendica\Core\Hooks\Util\HookFileManager; +use Friendica\Core\Hooks\Util\StrategiesFileManager; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; use org\bovigo\vfs\vfsStream; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; -class HookFileManagerTest extends MockedTest +class StrategiesFileManagerTest extends MockedTest { use VFSTrait; @@ -50,11 +50,9 @@ class HookFileManagerTest extends MockedTest [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], - ], ]; EOF, 'addonsArray' => [], @@ -67,11 +65,9 @@ EOF, [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => '', ], - ], ]; EOF, 'addonsArray' => [], @@ -84,19 +80,15 @@ EOF, [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], - ], ]; EOF, 'addonsArray' => [ - \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => ['null'], ], - ], ], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''], @@ -108,19 +100,15 @@ EOF, [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], - ], ]; EOF, 'addonsArray' => [ - \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => 'null', ], - ], ], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''], @@ -133,48 +121,21 @@ EOF, [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], - ], ]; EOF, 'addonsArray' => [ - \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ \Psr\Log\LoggerInterface::class => [ \Psr\Log\NullLogger::class => [''], ], - ], ], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''], [LoggerInterface::class, NullLogger::class, ''], ], ], - 'withWrongContentButAddons' => [ - 'content' => << [ - \Psr\Log\LoggerInterface::class => [ - \Psr\Log\NullLogger::class => [''], - ], - ], -]; -EOF, - 'addonsArray' => [ - \Friendica\Core\Hooks\Capabilities\BehavioralHookType::STRATEGY => [ - \Psr\Log\LoggerInterface::class => [ - \Psr\Log\NullLogger::class => [''], - ], - ], - ], - 'assertStrategies' => [ - [LoggerInterface::class, NullLogger::class, ''], - ], - ], ]; } @@ -183,58 +144,59 @@ EOF, */ public function testSetupHooks(string $content, array $addonsArray, array $assertStrategies) { - vfsStream::newFile('static/hooks.config.php') + vfsStream::newFile(StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php') ->withContent($content) ->at($this->root); $addonLoader = \Mockery::mock(ICanLoadAddons::class); $addonLoader->shouldReceive('getActiveAddonConfig')->andReturn($addonsArray)->once(); - $hookFileManager = new HookFileManager($this->root->url(), $addonLoader); + $hookFileManager = new StrategiesFileManager($this->root->url(), $addonLoader); - $instanceManager = \Mockery::mock(ICanRegisterInstances::class); + $instanceManager = \Mockery::mock(ICanRegisterStrategies::class); foreach ($assertStrategies as $assertStrategy) { $instanceManager->shouldReceive('registerStrategy')->withArgs($assertStrategy)->once(); } - $hookFileManager->setupHooks($instanceManager); + $hookFileManager->loadConfig(); + $hookFileManager->setupStrategies($instanceManager); self::expectNotToPerformAssertions(); } /** - * Test the exception in case the hooks.config.php file is missing + * Test the exception in case the strategies.config.php file is missing */ - public function testMissingHooksFile() + public function testMissingStrategiesFile() { $addonLoader = \Mockery::mock(ICanLoadAddons::class); - $instanceManager = \Mockery::mock(ICanRegisterInstances::class); - $hookFileManager = new HookFileManager($this->root->url(), $addonLoader); + $instanceManager = \Mockery::mock(ICanRegisterStrategies::class); + $hookFileManager = new StrategiesFileManager($this->root->url(), $addonLoader); self::expectException(HookConfigException::class); self::expectExceptionMessage(sprintf('config file %s does not exist.', - $this->root->url() . '/' . HookFileManager::STATIC_DIR . '/' . HookFileManager::CONFIG_NAME . '.config.php')); + $this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php')); - $hookFileManager->setupHooks($instanceManager); + $hookFileManager->loadConfig(); } /** - * Test the exception in case the hooks.config.php file is wrong + * Test the exception in case the strategies.config.php file is wrong */ - public function testWrongHooksFile() + public function testWrongStrategiesFile() { $addonLoader = \Mockery::mock(ICanLoadAddons::class); - $instanceManager = \Mockery::mock(ICanRegisterInstances::class); - $hookFileManager = new HookFileManager($this->root->url(), $addonLoader); + $instanceManager = \Mockery::mock(ICanRegisterStrategies::class); + $hookFileManager = new StrategiesFileManager($this->root->url(), $addonLoader); - vfsStream::newFile('static/hooks.config.php') + vfsStream::newFile(StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php') ->withContent("at($this->root); self::expectException(HookConfigException::class); self::expectExceptionMessage(sprintf('Error loading config file %s.', - $this->root->url() . '/' . HookFileManager::STATIC_DIR . '/' . HookFileManager::CONFIG_NAME . '.config.php')); + $this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php')); - $hookFileManager->setupHooks($instanceManager); + $hookFileManager->loadConfig(); } } From ede8896721f64d49a733c23eeb9397e4719650e8 Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 21 Jul 2023 22:47:09 +0200 Subject: [PATCH 24/24] Make PHP-CS happy --- .../Hooks/Util/StrategiesFileManagerTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/src/Core/Hooks/Util/StrategiesFileManagerTest.php b/tests/src/Core/Hooks/Util/StrategiesFileManagerTest.php index 9e1855da1..592f6f1e0 100644 --- a/tests/src/Core/Hooks/Util/StrategiesFileManagerTest.php +++ b/tests/src/Core/Hooks/Util/StrategiesFileManagerTest.php @@ -86,9 +86,9 @@ return [ ]; EOF, 'addonsArray' => [ - \Psr\Log\LoggerInterface::class => [ - \Psr\Log\NullLogger::class => ['null'], - ], + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => ['null'], + ], ], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''], @@ -106,9 +106,9 @@ return [ ]; EOF, 'addonsArray' => [ - \Psr\Log\LoggerInterface::class => [ - \Psr\Log\NullLogger::class => 'null', - ], + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => 'null', + ], ], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''], @@ -127,9 +127,9 @@ return [ ]; EOF, 'addonsArray' => [ - \Psr\Log\LoggerInterface::class => [ - \Psr\Log\NullLogger::class => [''], - ], + \Psr\Log\LoggerInterface::class => [ + \Psr\Log\NullLogger::class => [''], + ], ], 'assertStrategies' => [ [LoggerInterface::class, NullLogger::class, ''],