refactor: introduce DI container (#4238)

* refactor: introduce DI container

* add bin/test
This commit is contained in:
Dag 2024-08-29 22:48:59 +02:00 committed by GitHub
parent e010fd4d52
commit 58544cd61a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 231 additions and 89 deletions

View file

@ -2,10 +2,11 @@
final class BridgeCard
{
public static function render(string $bridgeClassName, ?string $token): string
{
$bridgeFactory = new BridgeFactory();
public static function render(
BridgeFactory $bridgeFactory,
string $bridgeClassName,
?string $token
): string {
$bridge = $bridgeFactory->create($bridgeClassName);
$uri = $bridge->getURI();

View file

@ -8,10 +8,12 @@ final class BridgeFactory
private array $enabledBridges = [];
private array $missingEnabledBridges = [];
public function __construct()
{
$this->cache = RssBridge::getCache();
$this->logger = RssBridge::getLogger();
public function __construct(
CacheInterface $cache,
Logger $logger
) {
$this->cache = $cache;
$this->logger = $logger;
// Create all possible bridge class names from fs
foreach (scandir(__DIR__ . '/../bridges/') as $file) {

View file

@ -14,10 +14,6 @@ class CacheFactory
public function create(string $name = null): CacheInterface
{
$name ??= Configuration::getConfig('cache', 'type');
if (!$name) {
throw new \Exception('No cache type configured');
}
$cacheNames = [];
foreach (scandir(PATH_LIB_CACHES) as $file) {
if (preg_match('/^([^.]+)Cache\.php$/U', $file, $m)) {

33
lib/Container.php Normal file
View file

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
class Container implements \ArrayAccess
{
private array $values = [];
private array $resolved = [];
public function offsetSet($offset, $value): void
{
$this->values[$offset] = $value;
}
#[ReturnTypeWillChange] public function offsetGet($offset)
{
if (!isset($this->values[$offset])) {
throw new \Exception(sprintf('Unknown container key: "%s"', $offset));
}
if (!isset($this->resolved[$offset])) {
$this->resolved[$offset] = $this->values[$offset]($this);
}
return $this->resolved[$offset];
}
#[ReturnTypeWillChange] public function offsetExists($offset)
{
}
public function offsetUnset($offset): void
{
}
}

View file

@ -2,18 +2,12 @@
final class RssBridge
{
private static Logger $logger;
private static CacheInterface $cache;
private static HttpClient $httpClient;
private static Container $container;
public function __construct(
Logger $logger,
CacheInterface $cache,
HttpClient $httpClient
Container $container
) {
self::$logger = $logger;
self::$cache = $cache;
self::$httpClient = $httpClient;
self::$container = $container;
}
public function main(Request $request): Response
@ -83,10 +77,9 @@ final class RssBridge
return new Response(render(__DIR__ . '/../templates/error.html.php', ['message' => 'Invalid action']), 400);
}
$className = '\\' . $actionName;
$actionObject = new $className();
$controller = self::$container[$actionName];
$response = $actionObject($request);
$response = $controller($request);
return $response;
}
@ -94,16 +87,16 @@ final class RssBridge
public static function getLogger(): Logger
{
// null logger is only for the tests not to fail
return self::$logger ?? new NullLogger();
return self::$container['logger'] ?? new NullLogger();
}
public static function getCache(): CacheInterface
{
return self::$cache;
return self::$container['cache'];
}
public static function getHttpClient(): HttpClient
{
return self::$httpClient;
return self::$container['http_client'];
}
}

78
lib/dependencies.php Normal file
View file

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
$container = new Container();
$container[ConnectivityAction::class] = function ($c) {
return new ConnectivityAction($c['bridge_factory']);
};
$container[DetectAction::class] = function ($c) {
return new DetectAction($c['bridge_factory']);
};
$container[DisplayAction::class] = function ($c) {
return new DisplayAction($c['cache'], $c['logger'], $c['bridge_factory']);
};
$container[FindfeedAction::class] = function ($c) {
return new FindfeedAction($c['bridge_factory']);
};
$container[FrontpageAction::class] = function ($c) {
return new FrontpageAction($c['bridge_factory']);
};
$container[HealthAction::class] = function () {
return new HealthAction();
};
$container[ListAction::class] = function ($c) {
return new ListAction($c['bridge_factory']);
};
$container['bridge_factory'] = function ($c) {
return new BridgeFactory($c['cache'], $c['logger']);
};
$container['http_client'] = function () {
return new CurlHttpClient();
};
$container['cache_factory'] = function ($c) {
return new CacheFactory($c['logger']);
};
$container['logger'] = function () {
$logger = new SimpleLogger('rssbridge');
if (Debug::isEnabled()) {
$logger->addHandler(new ErrorLogHandler(Logger::DEBUG));
} else {
$logger->addHandler(new ErrorLogHandler(Logger::INFO));
}
// Uncomment this for info logging to fs
// $logger->addHandler(new StreamHandler('/tmp/rss-bridge.txt', Logger::INFO));
// Uncomment this for debug logging to fs
//$logger->addHandler(new StreamHandler('/tmp/rss-bridge-debug.txt', Logger::DEBUG));
return $logger;
};
$container['cache'] = function ($c) {
/** @var CacheFactory $cacheFactory */
$cacheFactory = $c['cache_factory'];
$type = Configuration::getConfig('cache', 'type');
if (!$type) {
throw new \Exception('No cache type configured');
}
if (Debug::isEnabled()) {
$cache = $cacheFactory->create('array');
} else {
$cache = $cacheFactory->create($type);
}
return $cache;
};
return $container;

View file

@ -175,8 +175,9 @@ final class ErrorLogHandler
$context = Json::encode($record['context']);
}
}
// Intentionally omitting newline
$text = sprintf(
"[%s] %s.%s %s %s\n",
'[%s] %s.%s %s %s',
$record['created_at']->format('Y-m-d H:i:s'),
$record['name'],
$record['level_name'],