From: Philipp Date: Sun, 15 Jan 2023 21:31:19 +0000 (+0100) Subject: Introduce new Hook logic X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=f609e38600f54fe8465d1194008d823103f41baa;p=friendica.git Introduce new Hook logic - InstanceManager for computing strategies and to allow decorators - Adapting Core\Logger to use it --- diff --git a/src/Core/Hooks/Capabilities/IAmAStrategy.php b/src/Core/Hooks/Capabilities/IAmAStrategy.php new file mode 100644 index 0000000000..a830d912c0 --- /dev/null +++ b/src/Core/Hooks/Capabilities/IAmAStrategy.php @@ -0,0 +1,29 @@ +. + * + */ + +namespace Friendica\Core\Hooks\Capabilities; + +/** + * All interfaces, marking this interface are valid Strategies for Hook calls + */ +interface IAmAStrategy +{ +} diff --git a/src/Core/Hooks/Capabilities/ICanManageInstances.php b/src/Core/Hooks/Capabilities/ICanManageInstances.php new file mode 100644 index 0000000000..08802a08e6 --- /dev/null +++ b/src/Core/Hooks/Capabilities/ICanManageInstances.php @@ -0,0 +1,81 @@ +. + * + */ + +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 + */ +interface ICanManageInstances +{ + /** + * 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 The name of the given class, which will be used for factories, dependency injections etc. + * @param string $class The class of the given class + * @param ?array $arguments Additional arguments, which can be passed to the constructor + * + * @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; + + /** + * 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 class or interface, which gets decorated by a class + * @param string $decoratorClass 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" + * + * @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 A given class or interface, which will get returned + * @param string $name The name of the concrete class, wich + * @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; +} diff --git a/src/Core/Hooks/Exceptions/HookInstanceException.php b/src/Core/Hooks/Exceptions/HookInstanceException.php new file mode 100644 index 0000000000..bda8e7c8fb --- /dev/null +++ b/src/Core/Hooks/Exceptions/HookInstanceException.php @@ -0,0 +1,30 @@ +. + * + */ + +namespace Friendica\Core\Hooks\Exceptions; + +class HookInstanceException extends \RuntimeException +{ + public function __construct($message = "", \Throwable $previous = null) + { + parent::__construct($message, 500, $previous); + } +} diff --git a/src/Core/Hooks/Exceptions/HookRegisterArgumentException.php b/src/Core/Hooks/Exceptions/HookRegisterArgumentException.php new file mode 100644 index 0000000000..799c4294be --- /dev/null +++ b/src/Core/Hooks/Exceptions/HookRegisterArgumentException.php @@ -0,0 +1,30 @@ +. + * + */ + +namespace Friendica\Core\Hooks\Exceptions; + +class HookRegisterArgumentException extends \RuntimeException +{ + public function __construct($message = "", \Throwable $previous = null) + { + parent::__construct($message, 500, $previous); + } +} diff --git a/src/Core/Hooks/Model/InstanceManager.php b/src/Core/Hooks/Model/InstanceManager.php new file mode 100644 index 0000000000..4b8a0e6de8 --- /dev/null +++ b/src/Core/Hooks/Model/InstanceManager.php @@ -0,0 +1,104 @@ +. + * + */ + +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 subsituation 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/Logger/Exception/LoggerInvalidException.php b/src/Core/Logger/Exception/LoggerInvalidException.php new file mode 100644 index 0000000000..db6ecb78c5 --- /dev/null +++ b/src/Core/Logger/Exception/LoggerInvalidException.php @@ -0,0 +1,32 @@ +. + * + */ + +namespace Friendica\Core\Logger\Exception; + +use Throwable; + +class LoggerInvalidException extends \RuntimeException +{ + public function __construct($message = "", Throwable $previous = null) + { + parent::__construct($message, 500, $previous); + } +} diff --git a/src/Core/Logger/Factory/Logger.php b/src/Core/Logger/Factory/Logger.php index 3dd2a81dc2..2821a813c2 100644 --- a/src/Core/Logger/Factory/Logger.php +++ b/src/Core/Logger/Factory/Logger.php @@ -22,16 +22,11 @@ namespace Friendica\Core\Logger\Factory; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core; -use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections; +use Friendica\Core\Hooks\Capabilities\ICanManageInstances; use Friendica\Core\Logger\Exception\LogLevelException; -use Friendica\Database\Database; -use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Util\FileSystem; use Friendica\Core\Logger\Type\ProfilerLogger; use Friendica\Core\Logger\Type\StreamLogger; use Friendica\Core\Logger\Type\SyslogLogger; -use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use Psr\Log\NullLogger; @@ -44,115 +39,55 @@ class Logger const DEV_CHANNEL = 'dev'; /** @var string The log-channel (app, worker, ...) */ - private $channel; + protected $channel; + /** @var ICanManageInstances */ + protected $instanceManager; + /** @var IManageConfigValues */ + protected $config; - public function __construct(string $channel, bool $includeAddon = true) + public function __construct(string $channel, ICanManageInstances $instanceManager, IManageConfigValues $config, string $logfile = null) { - $this->channel = $channel; + $this->channel = $channel; + $this->instanceManager = $instanceManager; + $this->config = $config; - /// @fixme clean solution = Making Addon & Hook dynamic and load them inside the constructor, so there's no custom load logic necessary anymore - if ($includeAddon) { - Core\Addon::loadAddons(); - Core\Hook::loadHooks(); + $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); } } /** * Creates a new PSR-3 compliant logger instances * - * @param Database $database The Friendica Database instance - * @param IManageConfigValues $config The config - * @param Profiler $profiler The profiler of the app - * @param FileSystem $fileSystem FileSystem utils - * @param string|null $minLevel (optional) Override minimum Loglevel to log + * @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(Database $database, IManageConfigValues $config, Profiler $profiler, FileSystem $fileSystem, IHaveCallIntrospections $introspection, ?string $minLevel = null): LoggerInterface + public function create(string $loglevel = null): LoggerInterface { - if (empty($config->get('system', 'debugging', false))) { - $logger = new NullLogger(); - $database->setLogger($logger); - return $logger; - } - - $minLevel = $minLevel ?? $config->get('system', 'loglevel'); - $loglevel = self::mapLegacyConfigDebugLevel((string)$minLevel); - - $name = $config->get('system', 'logger_config', 'stream'); - - switch ($name) { - case 'syslog': - try { - $logger = new SyslogLogger($this->channel, $introspection, $loglevel, $config->get('system', 'syslog_flags', SyslogLogger::DEFAULT_FLAGS), $config->get('system', 'syslog_facility', SyslogLogger::DEFAULT_FACILITY)); - } catch (LogLevelException $exception) { - // If there's a wrong config value for loglevel, try again with standard - $logger = $this->create($database, $config, $profiler, $fileSystem, $introspection, LogLevel::NOTICE); - $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]); - } catch (\Throwable $e) { - // No logger ... - $logger = new NullLogger(); - } - break; - - case 'stream': - default: - $data = [ - 'name' => $name, - 'channel' => $this->channel, - 'introspection' => $introspection, - 'loglevel' => $loglevel, - 'logger' => null, - ]; - try { - Core\Hook::callAll('logger_instance', $data); - } catch (InternalServerErrorException $exception) { - $data['logger'] = null; - } - - if (($data['logger'] ?? null) instanceof LoggerInterface) { - $logger = $data['logger']; - } - - if (empty($logger)) { - $stream = $config->get('system', 'logfile'); - // just add a stream in case it's either writable or not file - if (!is_file($stream) || is_writable($stream)) { - try { - $logger = new StreamLogger($this->channel, $stream, $introspection, $fileSystem, $loglevel); - } catch (LogLevelException $exception) { - // If there's a wrong config value for loglevel, try again with standard - $logger = $this->create($database, $config, $profiler, $fileSystem, $introspection, LogLevel::NOTICE); - $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]); - } catch (\Throwable $t) { - // No logger ... - $logger = new NullLogger(); - } - } else { - try { - $logger = new SyslogLogger($this->channel, $introspection, $loglevel); - } catch (LogLevelException $exception) { - // If there's a wrong config value for loglevel, try again with standard - $logger = $this->create($database, $config, $profiler, $fileSystem, $introspection, LogLevel::NOTICE); - $logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]); - } catch (\Throwable $e) { - // No logger ... - $logger = new NullLogger(); - } - } - } - break; + if (empty($this->config->get('system', 'debugging') ?? false)) { + return new NullLogger(); } - $profiling = $config->get('system', 'profiling', false); + $loglevel = $loglevel ?? static::mapLegacyConfigDebugLevel($this->config->get('system', 'loglevel')); + $name = $this->config->get('system', 'logger_config') ?? 'stream'; - // In case profiling is enabled, wrap the ProfilerLogger around the current logger - if (isset($profiling) && $profiling !== false) { - $logger = new ProfilerLogger($logger, $profiler); + 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) { + // No logger ... + return new NullLogger(); } - - $database->setLogger($logger); - return $logger; } /** @@ -163,63 +98,24 @@ class Logger * * It should never get filled during normal usage of Friendica * - * @param IManageConfigValues $config The config - * @param Profiler $profiler The profiler of the app - * @param FileSystem $fileSystem FileSystem utils - * * @return LoggerInterface The PSR-3 compliant logger instance * @throws \Exception */ - public static function createDev(IManageConfigValues $config, Profiler $profiler, FileSystem $fileSystem, IHaveCallIntrospections $introspection) + public function createDev() { - $debugging = $config->get('system', 'debugging'); - $stream = $config->get('system', 'dlogfile'); - $developerIp = $config->get('system', 'dlogip'); + $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 = $config->get('system', 'logger_config', 'stream'); - - switch ($name) { - - case 'syslog': - $logger = new SyslogLogger(self::DEV_CHANNEL, $introspection, LogLevel::DEBUG); - break; - - case 'stream': - default: - $data = [ - 'name' => $name, - 'channel' => self::DEV_CHANNEL, - 'introspection' => $introspection, - 'loglevel' => LogLevel::DEBUG, - 'logger' => null, - ]; - try { - Core\Hook::callAll('logger_instance', $data); - } catch (InternalServerErrorException $exception) { - $data['logger'] = null; - } - - if (($data['logger'] ?? null) instanceof LoggerInterface) { - return $data['logger']; - } - - $logger = new StreamLogger(self::DEV_CHANNEL, $stream, $introspection, $fileSystem, LogLevel::DEBUG); - break; - } - - $profiling = $config->get('system', 'profiling', false); - - // In case profiling is enabled, wrap the ProfilerLogger around the current logger - if (isset($profiling) && $profiling !== false) { - $logger = new ProfilerLogger($logger, $profiler); - } + $name = $this->config->get('system', 'logger_config') ?? 'stream'; - return $logger; + /** @var LoggerInterface */ + return $this->instanceManager->getInstance(LoggerInterface::class, $name, [self::DEV_CHANNEL, LogLevel::DEBUG, $stream]); } /** diff --git a/src/Core/Logger/Type/StreamLogger.php b/src/Core/Logger/Type/StreamLogger.php index a444faaaba..2568fb4a2e 100644 --- a/src/Core/Logger/Type/StreamLogger.php +++ b/src/Core/Logger/Type/StreamLogger.php @@ -21,6 +21,8 @@ namespace Friendica\Core\Logger\Type; +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Hooks\Capabilities\IAmAStrategy; use Friendica\Core\Logger\Exception\LoggerArgumentException; use Friendica\Core\Logger\Exception\LoggerException; use Friendica\Core\Logger\Exception\LogLevelException; @@ -32,7 +34,7 @@ use Psr\Log\LogLevel; /** * A Logger instance for logging into a stream (file, stdout, stderr) */ -class StreamLogger extends AbstractLogger +class StreamLogger extends AbstractLogger implements IAmAStrategy { /** * The minimum loglevel at which this logger will be triggered @@ -80,16 +82,20 @@ class StreamLogger extends AbstractLogger /** * {@inheritdoc} - * @param string|resource $stream The stream to write with this logger (either a file or a stream, i.e. stdout) * @param string $level The minimum loglevel at which this logger will be triggered * * @throws LoggerArgumentException * @throws LogLevelException */ - public function __construct($channel, $stream, Introspection $introspection, FileSystem $fileSystem, string $level = LogLevel::DEBUG) + public function __construct(string $channel, IManageConfigValues $config, Introspection $introspection, FileSystem $fileSystem, string $level = LogLevel::DEBUG) { $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)) { diff --git a/src/Core/Logger/Type/SyslogLogger.php b/src/Core/Logger/Type/SyslogLogger.php index b38cb01855..2e4bc94077 100644 --- a/src/Core/Logger/Type/SyslogLogger.php +++ b/src/Core/Logger/Type/SyslogLogger.php @@ -21,6 +21,8 @@ namespace Friendica\Core\Logger\Type; +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Hooks\Capabilities\IAmAStrategy; use Friendica\Core\Logger\Exception\LoggerException; use Friendica\Core\Logger\Exception\LogLevelException; use Friendica\Core\Logger\Util\Introspection; @@ -30,7 +32,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 +class SyslogLogger extends AbstractLogger implements IAmAStrategy { const IDENT = 'Friendica'; @@ -100,17 +102,16 @@ class SyslogLogger extends AbstractLogger /** * {@inheritdoc} * @param string $level The minimum loglevel at which this logger will be triggered - * @param int $logOpts Indicates what logging options will be used when generating a log message - * @param int $logFacility Used to specify what type of program is logging the message * * @throws LogLevelException * @throws LoggerException */ - public function __construct($channel, Introspection $introspection, string $level = LogLevel::NOTICE, int $logOpts = self::DEFAULT_FLAGS, int $logFacility = self::DEFAULT_FACILITY ) + public function __construct(string $channel, IManageConfigValues $config, Introspection $introspection, string $level = LogLevel::NOTICE) { parent::__construct($channel, $introspection); - $this->logOpts = $logOpts; - $this->logFacility = $logFacility; + + $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]); } diff --git a/src/Database/Database.php b/src/Database/Database.php index 032a282030..a5fe7f978a 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -36,7 +36,6 @@ use PDO; use PDOException; use PDOStatement; use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; /** * This class is for the low level database stuff that does driver specific things. @@ -81,16 +80,14 @@ class Database /** @var ViewDefinition */ protected $viewDefinition; - public function __construct(IManageConfigValues $config, Profiler $profiler, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition) + public function __construct(IManageConfigValues $config, Profiler $profiler, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition, LoggerInterface $logger) { // We are storing these values for being able to perform a reconnect - $this->config = $config; - $this->profiler = $profiler; + $this->config = $config; + $this->profiler = $profiler; $this->dbaDefinition = $dbaDefinition; $this->viewDefinition = $viewDefinition; - - // Temporary NullLogger until we can fetch the logger class from the config - $this->logger = new NullLogger(); + $this->logger = $logger; $this->connect(); } @@ -196,21 +193,6 @@ class Database $this->testmode = $test; } - /** - * Sets the logger for DBA - * - * @note this is necessary because if we want to load the logger configuration - * from the DB, but there's an error, we would print out an exception. - * So the logger gets updated after the logger configuration can be retrieved - * from the database - * - * @param LoggerInterface $logger - */ - public function setLogger(LoggerInterface $logger) - { - $this->logger = $logger; - } - /** * Sets the profiler for DBA * diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 1836a44dd5..5b7a345685 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -37,6 +37,8 @@ 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\PConfig; use Friendica\Core\L10n; use Friendica\Core\Lock; @@ -76,6 +78,12 @@ return [ $_SERVER ] ], + ICanManageInstances::class => [ + 'instanceOf' => InstanceManager::class, + 'constructParams' => [ + [Dice::INSTANCE => Dice::SELF], + ], + ], Config\Util\ConfigFileManager::class => [ 'instanceOf' => Config\Factory\Config::class, 'call' => [ diff --git a/tests/Util/CreateDatabaseTrait.php b/tests/Util/CreateDatabaseTrait.php index 51f74c7a52..127d8da2f1 100644 --- a/tests/Util/CreateDatabaseTrait.php +++ b/tests/Util/CreateDatabaseTrait.php @@ -30,6 +30,7 @@ use Friendica\Database\Definition\ViewDefinition; use Friendica\Test\DatabaseTestTrait; use Friendica\Test\Util\Database\StaticDatabase; use Friendica\Util\Profiler; +use Psr\Log\NullLogger; trait CreateDatabaseTrait { @@ -45,7 +46,7 @@ trait CreateDatabaseTrait ], ])); - $database = new StaticDatabase($config, new Profiler($config), (new DbaDefinition($this->root->url()))->load(), (new ViewDefinition($this->root->url()))->load()); + $database = new StaticDatabase($config, new Profiler($config), (new DbaDefinition($this->root->url()))->load(), (new ViewDefinition($this->root->url()))->load(), new NullLogger()); $database->setTestmode(true); return $database; diff --git a/tests/Util/Hooks/InstanceMocks/FakeInstance.php b/tests/Util/Hooks/InstanceMocks/FakeInstance.php new file mode 100644 index 0000000000..70fdbcd4fe --- /dev/null +++ b/tests/Util/Hooks/InstanceMocks/FakeInstance.php @@ -0,0 +1,60 @@ +. + * + */ + +namespace Friendica\Test\Util\Hooks\InstanceMocks; + +use Friendica\Core\Hooks\Capabilities\IAmAStrategy; + +class FakeInstance implements IAmADecoratedInterface, IAmAStrategy +{ + protected $aText = null; + protected $cBool = null; + protected $bText = null; + + public function __construct(string $aText = null, bool $cBool = null, string $bText = null) + { + $this->aText = $aText; + $this->cBool = $cBool; + $this->bText = $bText; + } + + public function createSomething(string $aText, bool $cBool, string $bText): string + { + $this->aText = $aText; + $this->cBool = $cBool; + $this->bText = $bText; + } + + public function getAText(): ?string + { + return $this->aText; + } + + public function getBText(): ?string + { + return $this->bText; + } + + public function getCBool(): ?bool + { + return $this->cBool; + } +} diff --git a/tests/Util/Hooks/InstanceMocks/FakeInstanceDecorator.php b/tests/Util/Hooks/InstanceMocks/FakeInstanceDecorator.php new file mode 100644 index 0000000000..af4db96c44 --- /dev/null +++ b/tests/Util/Hooks/InstanceMocks/FakeInstanceDecorator.php @@ -0,0 +1,59 @@ +. + * + */ + +namespace Friendica\Test\Util\Hooks\InstanceMocks; + +class FakeInstanceDecorator implements IAmADecoratedInterface +{ + public static $countInstance = 0; + + /** @var IAmADecoratedInterface */ + protected $orig; + protected $prefix = ''; + + public function __construct(IAmADecoratedInterface $orig, string $prefix = '') + { + $this->orig = $orig; + $this->prefix = $prefix; + + self::$countInstance++; + } + + public function createSomething(string $aText, bool $cBool, string $bText): string + { + return $this->orig->createSomething($aText, $cBool, $bText); + } + + public function getAText(): ?string + { + return $this->prefix . $this->orig->getAText(); + } + + public function getBText(): ?string + { + return $this->prefix . $this->orig->getBText(); + } + + public function getCBool(): ?bool + { + return $this->prefix . $this->orig->getCBool(); + } +} diff --git a/tests/Util/Hooks/InstanceMocks/IAmADecoratedInterface.php b/tests/Util/Hooks/InstanceMocks/IAmADecoratedInterface.php new file mode 100644 index 0000000000..fe93aa998d --- /dev/null +++ b/tests/Util/Hooks/InstanceMocks/IAmADecoratedInterface.php @@ -0,0 +1,33 @@ +. + * + */ + +namespace Friendica\Test\Util\Hooks\InstanceMocks; + +interface IAmADecoratedInterface +{ + public function createSomething(string $aText, bool $cBool, string $bText): string; + + public function getAText(): ?string; + + public function getBText(): ?string; + + public function getCBool(): ?bool; +} diff --git a/tests/functional/DependencyCheckTest.php b/tests/functional/DependencyCheckTest.php index 86724bf437..27e693295e 100644 --- a/tests/functional/DependencyCheckTest.php +++ b/tests/functional/DependencyCheckTest.php @@ -25,7 +25,6 @@ use Dice\Dice; use Friendica\App; use Friendica\Core\Cache\Capability\ICanCache; use Friendica\Core\Cache\Capability\ICanCacheInMemory; -use Friendica\Core\Config\Model\Config; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Lock\Capability\ICanLock; @@ -33,7 +32,6 @@ use Friendica\Database\Database; use Friendica\Test\Util\VFSTrait; use Friendica\Util\BasePath; use Friendica\Core\Config\Util\ConfigFileManager; -use Friendica\Util\Profiler; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -53,7 +51,18 @@ class DependencyCheckTest extends TestCase $this->setUpVfsDir(); $this->dice = (new Dice()) - ->addRules(include __DIR__ . '/../../static/dependencies.config.php'); + ->addRules(include __DIR__ . '/../../static/dependencies.config.php') + ->addRule(BasePath::class, [ + 'constructParams' => [ + $this->root->url(), + [], + ], + ]) + ->addRule(LoggerInterface::class, ['constructParams' => ['test']]); + + /** @var IManageConfigValues $config */ + $config = $this->dice->create(IManageConfigValues::class); + $config->set('system', 'logfile', $this->root->url() . '/logs/friendica.log'); } /** @@ -142,7 +151,7 @@ class DependencyCheckTest extends TestCase public function testLogger() { /** @var LoggerInterface $logger */ - $logger = $this->dice->create(LoggerInterface::class, ['test']); + $logger = $this->dice->create(LoggerInterface::class, [['$channel' => 'test']]); self::assertInstanceOf(LoggerInterface::class, $logger); } @@ -154,7 +163,7 @@ class DependencyCheckTest extends TestCase $config->set('system', 'dlogfile', $this->root->url() . '/friendica.log'); /** @var LoggerInterface $logger */ - $logger = $this->dice->create('$devLogger', ['dev']); + $logger = $this->dice->create('$devLogger', [['$channel' => 'dev']]); self::assertInstanceOf(LoggerInterface::class, $logger); } @@ -164,6 +173,7 @@ class DependencyCheckTest extends TestCase /** @var ICanCache $cache */ $cache = $this->dice->create(ICanCache::class); + self::assertInstanceOf(ICanCache::class, $cache); } diff --git a/tests/src/Core/Cache/DatabaseCacheTest.php b/tests/src/Core/Cache/DatabaseCacheTest.php index e5f3359580..81804a806e 100644 --- a/tests/src/Core/Cache/DatabaseCacheTest.php +++ b/tests/src/Core/Cache/DatabaseCacheTest.php @@ -62,7 +62,7 @@ class DatabaseCacheTest extends CacheTest $dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load(); $viewDefinition = (new ViewDefinition($configCache->get('system', 'basepath')))->load(); - $dba = new StaticDatabase($config, $profiler, $dbaDefinition, $viewDefinition); + $dba = new StaticDatabase($config, $profiler, $dbaDefinition, $viewDefinition, new NullLogger()); $this->cache = new Cache\Type\DatabaseCache('database', $dba); return $this->cache; diff --git a/tests/src/Core/Hooks/Model/InstanceManagerTest.php b/tests/src/Core/Hooks/Model/InstanceManagerTest.php new file mode 100644 index 0000000000..94340b012f --- /dev/null +++ b/tests/src/Core/Hooks/Model/InstanceManagerTest.php @@ -0,0 +1,258 @@ +. + * + */ + +namespace Friendica\Test\src\Core\Hooks\Model; + +use Dice\Dice; +use Friendica\Core\Hooks\Model\InstanceManager; +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; + +class InstanceManagerTest extends MockedTest +{ + public function testEqualButNotSameInstance() + { + $instance = new InstanceManager(new Dice()); + + $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class); + + $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake'); + $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake'); + + self::assertEquals($getInstanceA, $getInstanceB); + self::assertNotSame($getInstanceA, $getInstanceB); + } + + protected function tearDown(): void + { + FakeInstanceDecorator::$countInstance = 0; + + parent::tearDown(); + } + + public function dataTests(): array + { + return [ + 'only_a' => [ + 'aString' => 'test', + ], + 'a_b' => [ + 'aString' => 'test', + 'cBool' => false, + 'bString' => 'test23', + + ], + 'a_c' => [ + 'aString' => 'test', + 'cBool' => false, + 'bString' => null, + ], + 'a_b_c' => [ + 'aString' => 'test', + 'cBool' => false, + 'bString' => 'test23', + ], + 'null' => [], + ]; + } + + /** + * @dataProvider dataTests + */ + public function testInstanceWithConstructorAnonymArgs(string $aString = null, bool $cBool = null, string $bString = null) + { + $instance = new InstanceManager(new Dice()); + + $args = []; + + if (isset($aString)) { + $args[] = $aString; + } + if (isset($bString)) { + $args[] = $bString; + } + if (isset($cBool)) { + $args[] = $cBool; + } + + $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args); + + /** @var IAmADecoratedInterface $getInstanceA */ + $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake'); + /** @var IAmADecoratedInterface $getInstanceB */ + $getInstanceB = $instance->getInstance(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 InstanceManager(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->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + /** @var IAmADecoratedInterface $getInstanceB */ + $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + + 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 testInstanceWithTwoStrategies(string $aString = null, bool $cBool = null, string $bString = null) + { + $instance = new InstanceManager(new Dice()); + + $args = []; + + if (isset($aString)) { + $args[] = $aString; + } + if (isset($cBool)) { + $args[] = $cBool; + } + + $instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args); + $instance->registerStrategy(IAmADecoratedInterface::class, 'fake23', FakeInstance::class, $args); + + /** @var IAmADecoratedInterface $getInstanceA */ + $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + /** @var IAmADecoratedInterface $getInstanceB */ + $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake23', [$bString]); + + 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 testDecorator(string $aString = null, bool $cBool = null, string $bString = null) + { + $instance = new InstanceManager(new Dice()); + + $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->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + /** @var IAmADecoratedInterface $getInstanceB */ + $getInstanceB = $instance->getInstance(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()); + } + + /** + * @dataProvider dataTests + */ + public function testTwoDecoratorWithPrefix(string $aString = null, bool $cBool = null, string $bString = null) + { + $instance = new InstanceManager(new Dice()); + + $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]); + $instance->registerDecorator(IAmADecoratedInterface::class, FakeInstanceDecorator::class); + + /** @var IAmADecoratedInterface $getInstanceA */ + $getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]); + /** @var IAmADecoratedInterface $getInstanceB */ + $getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake23', [$bString]); + + 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()); + } +} diff --git a/tests/src/Core/Logger/AbstractLoggerTest.php b/tests/src/Core/Logger/AbstractLoggerTest.php index 3a5e0ecd11..4605f6f75a 100644 --- a/tests/src/Core/Logger/AbstractLoggerTest.php +++ b/tests/src/Core/Logger/AbstractLoggerTest.php @@ -21,6 +21,7 @@ namespace Friendica\Test\src\Core\Logger; +use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Test\MockedTest; use Friendica\Core\Logger\Util\Introspection; use Mockery\MockInterface; @@ -41,6 +42,10 @@ abstract class AbstractLoggerTest extends MockedTest * @var Introspection|MockInterface */ protected $introspection; + /** + * @var IManageConfigValues|MockInterface + */ + protected $config; /** * Returns the content of the current logger instance @@ -68,6 +73,8 @@ abstract class AbstractLoggerTest extends MockedTest 'line' => self::LINE, 'function' => self::FUNC ]); + + $this->config = \Mockery::mock(IManageConfigValues::class); } public function assertLogline($string) diff --git a/tests/src/Core/Logger/StreamLoggerTest.php b/tests/src/Core/Logger/StreamLoggerTest.php index a83a5c2e4e..1ddddf4c1b 100644 --- a/tests/src/Core/Logger/StreamLoggerTest.php +++ b/tests/src/Core/Logger/StreamLoggerTest.php @@ -62,7 +62,9 @@ class StreamLoggerTest extends AbstractLoggerTest $this->logfile = vfsStream::newFile('friendica.log') ->at($this->root); - $logger = new StreamLogger('test', $this->logfile->url(), $this->introspection, $this->fileSystem, $level); + $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($this->logfile->url())->once(); + + $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem, $level); return $logger; } @@ -75,43 +77,6 @@ class StreamLoggerTest extends AbstractLoggerTest return $this->logfile->getContent(); } - /** - * Test if a stream is working - */ - public function testStream() - { - $logfile = vfsStream::newFile('friendica.log') - ->at($this->root); - - $filehandler = fopen($logfile->url(), 'ab'); - - $logger = new \Friendica\Core\Logger\Type\StreamLogger('test', $filehandler, $this->introspection, $this->fileSystem); - $logger->emergency('working'); - - $text = $logfile->getContent(); - - self::assertLogline($text); - } - - /** - * Test if the close statement is working - */ - public function testClose() - { - $logfile = vfsStream::newFile('friendica.log') - ->at($this->root); - - $logger = new StreamLogger('test', $logfile->url(), $this->introspection, $this->fileSystem); - $logger->emergency('working'); - $logger->close(); - // close doesn't affect - $logger->emergency('working too'); - - $text = $logfile->getContent(); - - self::assertLoglineNums(2, $text); - } - /** * Test when a file isn't set */ @@ -120,7 +85,9 @@ class StreamLoggerTest extends AbstractLoggerTest $this->expectException(LoggerArgumentException::class); $this->expectExceptionMessage("Missing stream URL."); - $logger = new StreamLogger('test', '', $this->introspection, $this->fileSystem); + $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn('')->once(); + + $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem); $logger->emergency('not working'); } @@ -130,13 +97,14 @@ class StreamLoggerTest extends AbstractLoggerTest */ public function testWrongUrl() { - $this->expectException(LoggerException::class); - $this->expectExceptionMessage("Cannot create stream."); + $this->expectException(LoggerArgumentException::class); $logfile = vfsStream::newFile('friendica.log') ->at($this->root)->chmod(0); - $logger = new StreamLogger('test', $logfile->url(), $this->introspection, $this->fileSystem); + $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($logfile->url())->once(); + + $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem); $logger->emergency('not working'); } @@ -164,7 +132,9 @@ class StreamLoggerTest extends AbstractLoggerTest $this->expectException(LogLevelException::class); $this->expectExceptionMessageMatches("/The level \".*\" is not valid./"); - $logger = new StreamLogger('test', 'file.text', $this->introspection, $this->fileSystem, 'NOPE'); + $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn('file.text')->once(); + + $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem, 'NOPE'); } /** @@ -178,7 +148,9 @@ class StreamLoggerTest extends AbstractLoggerTest $logfile = vfsStream::newFile('friendica.log') ->at($this->root); - $logger = new StreamLogger('test', $logfile->url(), $this->introspection, $this->fileSystem); + $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($logfile->url())->once(); + + $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem); $logger->log('NOPE', 'a test'); } @@ -191,7 +163,9 @@ class StreamLoggerTest extends AbstractLoggerTest $this->expectException(LoggerArgumentException::class); $this->expectExceptionMessage("A stream must either be a resource or a string."); - $logger = new StreamLogger('test', null, $this->introspection, $this->fileSystem); + $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn(null)->once(); + + $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem); } /** @@ -207,7 +181,9 @@ class StreamLoggerTest extends AbstractLoggerTest chdir($this->root->getChild('logs')->url()); - $logger = new StreamLogger('test', '../friendica.log' , $this->introspection, $this->fileSystem); + $this->config->shouldReceive('get')->with('system', 'logfile')->andReturn('../friendica.log')->once(); + + $logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem); $logger->info('Test'); } diff --git a/tests/src/Core/Logger/SyslogLoggerTest.php b/tests/src/Core/Logger/SyslogLoggerTest.php index 89f9d82e6c..53ace79c08 100644 --- a/tests/src/Core/Logger/SyslogLoggerTest.php +++ b/tests/src/Core/Logger/SyslogLoggerTest.php @@ -39,6 +39,10 @@ class SyslogLoggerTest extends AbstractLoggerTest parent::setUp(); $this->introspection->shouldReceive('addClasses')->with([SyslogLogger::class]); + $this->config->shouldReceive('get')->with('system', 'syslog_flags')->andReturn(SyslogLogger::DEFAULT_FLAGS) + ->once(); + $this->config->shouldReceive('get')->with('system', 'syslog_facility') + ->andReturn(SyslogLogger::DEFAULT_FACILITY)->once(); } /** @@ -54,7 +58,7 @@ class SyslogLoggerTest extends AbstractLoggerTest */ protected function getInstance($level = LogLevel::DEBUG) { - $this->logger = new SyslogLoggerWrapper('test', $this->introspection, $level); + $this->logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection, $level); return $this->logger; } @@ -68,7 +72,7 @@ class SyslogLoggerTest extends AbstractLoggerTest $this->expectException(LogLevelException::class); $this->expectExceptionMessageMatches("/The level \".*\" is not valid./"); - $logger = new SyslogLoggerWrapper('test', $this->introspection, 'NOPE'); + $logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection, 'NOPE'); } /** @@ -79,7 +83,7 @@ class SyslogLoggerTest extends AbstractLoggerTest $this->expectException(LogLevelException::class); $this->expectExceptionMessageMatches("/The level \".*\" is not valid./"); - $logger = new SyslogLoggerWrapper('test', $this->introspection); + $logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection); $logger->log('NOPE', 'a test'); } @@ -90,7 +94,7 @@ class SyslogLoggerTest extends AbstractLoggerTest */ public function testClose() { - $logger = new SyslogLoggerWrapper('test', $this->introspection); + $logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection); $logger->emergency('test'); $logger->close(); // Reopened itself diff --git a/tests/src/Core/Logger/SyslogLoggerWrapper.php b/tests/src/Core/Logger/SyslogLoggerWrapper.php index 634edc1195..9fd16706a8 100644 --- a/tests/src/Core/Logger/SyslogLoggerWrapper.php +++ b/tests/src/Core/Logger/SyslogLoggerWrapper.php @@ -21,6 +21,7 @@ namespace Friendica\Test\src\Core\Logger; +use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Logger\Type\SyslogLogger; use Friendica\Core\Logger\Util\Introspection; use Psr\Log\LogLevel; @@ -32,9 +33,9 @@ class SyslogLoggerWrapper extends SyslogLogger { private $content; - public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER) + public function __construct($channel, IManageConfigValues $config, Introspection $introspection, $level = LogLevel::NOTICE) { - parent::__construct($channel, $introspection, $level, $logOpts, $logFacility); + parent::__construct($channel, $config, $introspection, $level); $this->content = ''; }