diff --git a/src/Addon/Event/AddonStartEvent.php b/src/Addon/Event/AddonStartEvent.php index 48836ade86..921eccfcac 100644 --- a/src/Addon/Event/AddonStartEvent.php +++ b/src/Addon/Event/AddonStartEvent.php @@ -9,20 +9,22 @@ declare(strict_types=1); namespace Friendica\Addon\Event; +use Psr\Container\ContainerInterface; + /** * Start an addon. */ final class AddonStartEvent { - private array $dependencies; + private ContainerInterface $container; - public function __construct(array $dependencies) + public function __construct(ContainerInterface $container) { - $this->dependencies = $dependencies; + $this->container = $container; } - public function getDependencies(): array + public function getContainer(): ContainerInterface { - return $this->dependencies; + return $this->container; } } diff --git a/src/App.php b/src/App.php index 61991e1d92..e085081b12 100644 --- a/src/App.php +++ b/src/App.php @@ -38,6 +38,7 @@ use Friendica\Protocol\ATProtocol\DID; use Friendica\Security\Authentication; use Friendica\Security\ExAuth; use Friendica\Security\OpenWebAuth; +use Friendica\Service\Addon\AddonContainer; use Friendica\Service\Addon\AddonManager; use Friendica\Util\BasePath; use Friendica\Util\DateTimeFormat; @@ -208,17 +209,18 @@ class App // At this place we should be careful because addons can change the container // Maybe we should create a new container especially for the addons - foreach ($$this->addonManager->getProvidedDependencyRules() as $name => $rule) { + foreach ($this->addonManager->getProvidedDependencyRules() as $name => $rule) { $this->container->addRule($name, $rule); } - $dependencies = []; + $containers = []; - foreach ($this->addonManager->getAllRequiredDependencies() as $dependency) { - $dependencies[$dependency] = $this->container->create($dependency); + foreach ($this->addonManager->getRequiredDependencies() as $addonId => $dependencies) { + // @TODO At this point we can filter or restrict the dependencies of addons + $containers[$addonId] = AddonContainer::fromContainer($this->container, $dependencies); } - $this->addonManager->initAddons($dependencies); + $this->addonManager->initAddons($containers); } private function registerEventDispatcher(): void diff --git a/src/Service/Addon/Addon.php b/src/Service/Addon/Addon.php index 41ed3cd791..5dd3b2be52 100644 --- a/src/Service/Addon/Addon.php +++ b/src/Service/Addon/Addon.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace Friendica\Service\Addon; +use Psr\Container\ContainerInterface; + /** * Interface to communicate with an addon. */ @@ -22,7 +24,7 @@ interface Addon public function getProvidedDependencyRules(): array; - public function initAddon(array $dependencies): void; + public function initAddon(ContainerInterface $container): void; public function installAddon(): void; diff --git a/src/Service/Addon/AddonManager.php b/src/Service/Addon/AddonManager.php index 17e7c82768..03fbc421ec 100644 --- a/src/Service/Addon/AddonManager.php +++ b/src/Service/Addon/AddonManager.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace Friendica\Service\Addon; +use Psr\Container\ContainerInterface; + /** * Manager for all addons. */ @@ -29,18 +31,6 @@ final class AddonManager $this->addons = $this->addonFactory->getAddons($addonNames); } - public function getAllRequiredDependencies(): array - { - $dependencies = []; - - foreach ($this->addons as $addon) { - // @TODO Here we can filter or deny dependencies from addons - $dependencies = array_merge($dependencies, $addon->getRequiredDependencies()); - } - - return array_unique($dependencies); - } - public function getRequiredDependencies(): array { $dependencies = []; @@ -76,21 +66,19 @@ final class AddonManager return $events; } - public function initAddons(array $dependencies): void + /** + * @param ContainerInterface[] $containers + */ + public function initAddons(array $containers): void { foreach ($this->addons as $addon) { - $required = $addon->getRequiredDependencies(); - $addonDependencies = []; + $container = $containers[$addon->getId()] ?? null; - foreach ($required as $dependency) { - if (!array_key_exists($dependency, $dependencies)) { - throw new \RuntimeException(sprintf('Dependency "%s" required by addon "%s" not found.', $dependency, $addon)); - } - - $addonDependencies[$dependency] = $dependencies[$dependency]; + if ($container === null) { + throw new \RuntimeException(sprintf('Container for addon "%s" is missing.', $addon->getId())); } - $addon->initAddon($addonDependencies); + $addon->initAddon($container); } } } diff --git a/src/Service/Addon/AddonProxy.php b/src/Service/Addon/AddonProxy.php index 8db404c035..895afeca19 100644 --- a/src/Service/Addon/AddonProxy.php +++ b/src/Service/Addon/AddonProxy.php @@ -13,6 +13,7 @@ use Friendica\Addon\AddonBootstrap; use Friendica\Addon\DependencyProvider; use Friendica\Addon\Event\AddonStartEvent; use Friendica\Addon\InstallableAddon; +use Psr\Container\ContainerInterface; /** * Proxy object for an addon. @@ -61,7 +62,7 @@ final class AddonProxy implements Addon return []; } - public function initAddon(array $dependencies): void + public function initAddon(ContainerInterface $container): void { if ($this->isInit) { return; @@ -69,7 +70,7 @@ final class AddonProxy implements Addon $this->isInit = true; - $event = new AddonStartEvent($dependencies); + $event = new AddonStartEvent($container); $this->bootstrap->initAddon($event); } diff --git a/src/Service/Addon/LegacyAddonProxy.php b/src/Service/Addon/LegacyAddonProxy.php index 5ab98b2fa8..fff6e5670f 100644 --- a/src/Service/Addon/LegacyAddonProxy.php +++ b/src/Service/Addon/LegacyAddonProxy.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace Friendica\Service\Addon; +use Psr\Container\ContainerInterface; + /** * Proxy object for a legacy addon. */ @@ -52,7 +54,7 @@ final class LegacyAddonProxy implements Addon return []; } - public function initAddon(array $dependencies): void + public function initAddon(ContainerInterface $container): void { if ($this->isInit) { return; diff --git a/tests/Unit/Service/Addon/AddonProxyTest.php b/tests/Unit/Service/Addon/AddonProxyTest.php index 3c0864483a..cac13aa291 100644 --- a/tests/Unit/Service/Addon/AddonProxyTest.php +++ b/tests/Unit/Service/Addon/AddonProxyTest.php @@ -16,6 +16,7 @@ use Friendica\Addon\InstallableAddon; use Friendica\Service\Addon\Addon; use Friendica\Service\Addon\AddonProxy; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; /** @@ -97,7 +98,7 @@ class AddonProxyTest extends TestCase $addon = new AddonProxy('id', $bootstrap); - $addon->initAddon([]); + $addon->initAddon($this->createStub(ContainerInterface::class)); } public function testInitAddonMultipleTimesWillCallBootstrapOnce(): void @@ -109,26 +110,22 @@ class AddonProxyTest extends TestCase $addon = new AddonProxy('id', $bootstrap); - $addon->initAddon([]); - $addon->initAddon([]); + $addon->initAddon($this->createStub(ContainerInterface::class)); + $addon->initAddon($this->createStub(ContainerInterface::class)); } public function testInitAddonCallsBootstrapWithDependencies(): void { + $container = $this->createStub(ContainerInterface::class); + $bootstrap = $this->createMock(AddonBootstrap::class); - - $bootstrap->expects($this->once())->method('initAddon')->willReturnCallback(function (AddonStartEvent $event) { - $dependencies = $event->getDependencies(); - - $this->assertArrayHasKey(LoggerInterface::class, $dependencies); - $this->assertInstanceOf(LoggerInterface::class, $dependencies[LoggerInterface::class]); + $bootstrap->expects($this->once())->method('initAddon')->willReturnCallback(function (AddonStartEvent $event) use ($container) { + $this->assertSame($container, $event->getContainer()); }); $addon = new AddonProxy('id', $bootstrap); - $addon->initAddon( - [LoggerInterface::class => $this->createStub(LoggerInterface::class)] - ); + $addon->initAddon($container); } public function testInstallAddonCallsBootstrap(): void @@ -138,7 +135,7 @@ class AddonProxyTest extends TestCase $addon = new AddonProxy('id', $bootstrap); - $addon->initAddon([]); + $addon->initAddon($this->createStub(ContainerInterface::class)); $addon->installAddon(); } @@ -149,7 +146,7 @@ class AddonProxyTest extends TestCase $addon = new AddonProxy('id', $bootstrap); - $addon->initAddon([]); + $addon->initAddon($this->createStub(ContainerInterface::class)); $addon->uninstallAddon(); } } diff --git a/tests/Unit/Service/Addon/LegacyAddonProxyTest.php b/tests/Unit/Service/Addon/LegacyAddonProxyTest.php index c3b54326fe..8604022c56 100644 --- a/tests/Unit/Service/Addon/LegacyAddonProxyTest.php +++ b/tests/Unit/Service/Addon/LegacyAddonProxyTest.php @@ -13,6 +13,7 @@ use Friendica\Service\Addon\Addon; use Friendica\Service\Addon\LegacyAddonProxy; use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; class LegacyAddonProxyTest extends TestCase { @@ -85,7 +86,7 @@ class LegacyAddonProxyTest extends TestCase $addon = new LegacyAddonProxy('helloaddon', $root->url()); try { - $addon->initAddon([]); + $addon->initAddon($this->createStub(ContainerInterface::class)); } catch (\Throwable $th) { $this->assertSame( 'Addon loaded', @@ -105,7 +106,7 @@ class LegacyAddonProxyTest extends TestCase $addon = new LegacyAddonProxy('helloaddon', $root->url()); try { - $addon->initAddon([]); + $addon->initAddon($this->createStub(ContainerInterface::class)); } catch (\Exception $th) { $this->assertSame( 'Addon loaded', @@ -113,8 +114,8 @@ class LegacyAddonProxyTest extends TestCase ); } - $addon->initAddon([]); - $addon->initAddon([]); + $addon->initAddon($this->createStub(ContainerInterface::class)); + $addon->initAddon($this->createStub(ContainerInterface::class)); } public function testInstallAddonWillCallInstallFunction(): void @@ -127,7 +128,7 @@ class LegacyAddonProxyTest extends TestCase $addon = new LegacyAddonProxy('helloaddon', $root->url()); - $addon->initAddon([]); + $addon->initAddon($this->createStub(ContainerInterface::class)); try { $addon->installAddon(); } catch (\Exception $th) { @@ -148,7 +149,7 @@ class LegacyAddonProxyTest extends TestCase $addon = new LegacyAddonProxy('helloaddon', $root->url()); - $addon->initAddon([]); + $addon->initAddon($this->createStub(ContainerInterface::class)); try { $addon->uninstallAddon(); } catch (\Exception $th) { diff --git a/tests/Util/helloaddon/src/HelloAddon.php b/tests/Util/helloaddon/src/HelloAddon.php index 63fb6f1d77..0f7c1c1bb9 100644 --- a/tests/Util/helloaddon/src/HelloAddon.php +++ b/tests/Util/helloaddon/src/HelloAddon.php @@ -32,7 +32,7 @@ class HelloAddon implements AddonBootstrap, DependencyProvider, InstallableAddon * * The array should contain FQCN of the required services. * - * The dependencies will be passed to the initAddon() method via AddonStartEvent::getDependencies(). + * The dependencies will be passed as a PSR-11 Container to the initAddon() method via AddonStartEvent::getContainer(). */ public function getRequiredDependencies(): array { @@ -87,12 +87,9 @@ class HelloAddon implements AddonBootstrap, DependencyProvider, InstallableAddon public function initAddon(AddonStartEvent $event): void { - // $dependencies containts an array of services defined in getRequiredDependencies(). - // The keys are the FQCN of the services. - // The values are the instances of the services. - $dependencies = $event->getDependencies(); + $container = $event->getContainer(); - $this->logger = $dependencies[LoggerInterface::class]; + $this->logger = $container->get(LoggerInterface::class); $this->logger->info('Hello from HelloAddon'); }