From 02408ed76227f85d2fd1da5052d948cbf7743669 Mon Sep 17 00:00:00 2001 From: Charles Sprayberry Date: Sun, 3 Mar 2024 23:42:43 -0500 Subject: [PATCH] Remove observers, add events to container factory (#357) * Remove observers, add events to container factory * Resolve static analysis issues --- annotated-container.xsd | 7 - composer.json | 1 - .../package/other_src/ThirdInitializer.php | 6 - .../cspray/package/src/DependencyObserver.php | 15 - .../cspray/package/src/FirstInitializer.php | 4 - .../cspray/package/src/SecondInitializer.php | 4 - src/Bootstrap/Bootstrap.php | 110 +----- src/Bootstrap/BootstrappingConfiguration.php | 6 - src/Bootstrap/ContainerAnalyticsObserver.php | 11 - src/Bootstrap/ContainerCreatedObserver.php | 15 - src/Bootstrap/ObserverFactory.php | 11 - src/Bootstrap/PostAnalysisObserver.php | 14 - src/Bootstrap/PreAnalysisObserver.php | 13 - ...Observer.php => ServiceWiringListener.php} | 17 +- src/Bootstrap/ThirdPartyInitializer.php | 6 - .../XmlBootstrappingConfiguration.php | 49 --- src/Cli/Command/InitCommand.php | 17 - src/Cli/Command/ValidateCommand.php | 30 +- .../AbstractContainerFactory.php | 15 +- .../IlluminateContainerFactory.php | 7 +- src/Exception/BackingContainerNotFound.php | 10 - .../InvalidBootstrapConfiguration.php | 10 - src/Exception/InvalidLogFile.php | 11 - src/Exception/UnsupportedOperation.php | 10 - test/Unit/BootstrapTest.php | 340 ++++++------------ test/Unit/Cli/Command/InitCommandTest.php | 83 ----- test/Unit/ContainerFactoryTestCase.php | 25 +- .../AurynContainerFactoryTest.php | 5 +- .../IlluminateContainerFactoryTest.php | 5 +- .../PhpDiContainerFactoryTest.php | 5 +- .../Helper/StubContainerFactoryListener.php | 26 ++ .../XmlBootstrappingConfigurationTest.php | 98 ----- 32 files changed, 210 insertions(+), 776 deletions(-) delete mode 100644 fixture_src/VendorScanningInitializers/vendor/cspray/package/src/DependencyObserver.php delete mode 100644 src/Bootstrap/ContainerAnalyticsObserver.php delete mode 100644 src/Bootstrap/ContainerCreatedObserver.php delete mode 100644 src/Bootstrap/ObserverFactory.php delete mode 100644 src/Bootstrap/PostAnalysisObserver.php delete mode 100644 src/Bootstrap/PreAnalysisObserver.php rename src/Bootstrap/{ServiceWiringObserver.php => ServiceWiringListener.php} (87%) delete mode 100644 src/Exception/BackingContainerNotFound.php delete mode 100644 src/Exception/InvalidLogFile.php delete mode 100644 src/Exception/UnsupportedOperation.php create mode 100644 test/Unit/Helper/StubContainerFactoryListener.php diff --git a/annotated-container.xsd b/annotated-container.xsd index 1d8e9e87..7f65b3e4 100644 --- a/annotated-container.xsd +++ b/annotated-container.xsd @@ -33,7 +33,6 @@ - @@ -75,12 +74,6 @@ - - - - - - diff --git a/composer.json b/composer.json index 9a05e38e..8c740560 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,6 @@ }, "files": [ "fixture_src/VendorScanningInitializers/vendor/cspray/package/other_src/DependencyDefinitionProvider.php", - "fixture_src/VendorScanningInitializers/vendor/cspray/package/src/DependencyObserver.php", "fixture_src/VendorScanningInitializers/vendor/cspray/package/src/FirstInitializer.php", "fixture_src/VendorScanningInitializers/vendor/cspray/package/src/SecondInitializer.php", "fixture_src/VendorScanningInitializers/vendor/cspray/package/src/SomeService.php", diff --git a/fixture_src/VendorScanningInitializers/vendor/cspray/package/other_src/ThirdInitializer.php b/fixture_src/VendorScanningInitializers/vendor/cspray/package/other_src/ThirdInitializer.php index 7c779c84..ef006b65 100644 --- a/fixture_src/VendorScanningInitializers/vendor/cspray/package/other_src/ThirdInitializer.php +++ b/fixture_src/VendorScanningInitializers/vendor/cspray/package/other_src/ThirdInitializer.php @@ -10,12 +10,6 @@ public function getRelativeScanDirectories() : array { return []; } - public function getObserverClasses() : array { - return [ - DependencyObserver::class - ]; - } - public function getDefinitionProviderClass() : ?string { return null; } diff --git a/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/DependencyObserver.php b/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/DependencyObserver.php deleted file mode 100644 index 76451b48..00000000 --- a/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/DependencyObserver.php +++ /dev/null @@ -1,15 +0,0 @@ -get(SomeService::class)->setSomething('called from observer'); - } -} \ No newline at end of file diff --git a/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/FirstInitializer.php b/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/FirstInitializer.php index 0e4eb0b9..141ccae2 100644 --- a/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/FirstInitializer.php +++ b/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/FirstInitializer.php @@ -15,10 +15,6 @@ public function getRelativeScanDirectories() : array { ]; } - public function getObserverClasses() : array { - return []; - } - public function getDefinitionProviderClass() : ?string { return null; } diff --git a/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/SecondInitializer.php b/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/SecondInitializer.php index 1d875aa1..2fa70fde 100644 --- a/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/SecondInitializer.php +++ b/fixture_src/VendorScanningInitializers/vendor/cspray/package/src/SecondInitializer.php @@ -12,10 +12,6 @@ public function getRelativeScanDirectories() : array { return []; } - public function getObserverClasses() : array { - return []; - } - public function getDefinitionProviderClass() : string { return DependencyDefinitionProvider::class; } diff --git a/src/Bootstrap/Bootstrap.php b/src/Bootstrap/Bootstrap.php index 4a9ccb06..e0e89787 100644 --- a/src/Bootstrap/Bootstrap.php +++ b/src/Bootstrap/Bootstrap.php @@ -2,7 +2,6 @@ namespace Cspray\AnnotatedContainer\Bootstrap; -use Auryn\Injector; use Cspray\AnnotatedContainer\AnnotatedContainer; use Cspray\AnnotatedContainer\Definition\ContainerDefinition; use Cspray\AnnotatedContainer\Event\BootstrapEmitter; @@ -13,52 +12,27 @@ use Cspray\AnnotatedContainer\StaticAnalysis\ContainerDefinitionAnalysisOptionsBuilder; use Cspray\AnnotatedContainer\StaticAnalysis\ContainerDefinitionAnalyzer; use Cspray\AnnotatedContainer\StaticAnalysis\AnnotatedTargetDefinitionConverter; -use Cspray\AnnotatedContainer\ContainerFactory\AurynContainerFactory; use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactory; use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactoryOptionsBuilder; -use Cspray\AnnotatedContainer\ContainerFactory\PhpDiContainerFactory; -use Cspray\AnnotatedContainer\Exception\BackingContainerNotFound; use Cspray\AnnotatedContainer\Serializer\ContainerDefinitionSerializer; use Cspray\AnnotatedTarget\PhpParserAnnotatedTargetParser; use Cspray\PrecisionStopwatch\Marker; use Cspray\PrecisionStopwatch\Metrics; use Cspray\PrecisionStopwatch\Stopwatch; -use DI\Container; final class Bootstrap { - private readonly ?BootstrapEmitter $emitter; - private readonly BootstrappingDirectoryResolver $directoryResolver; - private readonly ParameterStoreFactory $parameterStoreFactory; - - private readonly Stopwatch $stopwatch; - - /** - * @var list - */ - private array $observers = []; public function __construct( - BootstrapEmitter $emitter = null, + private readonly ContainerFactory $containerFactory, + private readonly ?BootstrapEmitter $emitter = null, BootstrappingDirectoryResolver $directoryResolver = null, - ParameterStoreFactory $parameterStoreFactory = null, + private readonly ParameterStoreFactory $parameterStoreFactory = new DefaultParameterStoreFactory(), private readonly ?DefinitionProviderFactory $definitionProviderFactory = null, - private readonly ?ObserverFactory $observerFactory = null, - Stopwatch $stopwatch = null, - private readonly ?ContainerFactory $containerFactory = null + private readonly Stopwatch $stopwatch = new Stopwatch(), ) { - $this->emitter = $emitter; $this->directoryResolver = $directoryResolver ?? $this->defaultDirectoryResolver(); - $this->parameterStoreFactory = $parameterStoreFactory ?? new DefaultParameterStoreFactory(); - $this->stopwatch = $stopwatch ?? new Stopwatch(); - - if ($this->observerFactory !== null) { - trigger_error( - 'The Observer system is being replaced in 3.0 with an Event system. There will no longer be a ' . ObserverFactory::class . ' and will be removed as a dependency to ' . Bootstrap::class, - E_USER_DEPRECATED - ); - } } private function defaultDirectoryResolver() : BootstrappingDirectoryResolver { @@ -70,14 +44,6 @@ private function defaultDirectoryResolver() : BootstrappingDirectoryResolver { return new RootDirectoryBootstrappingDirectoryResolver($rootDir); } - public function addObserver(PreAnalysisObserver|PostAnalysisObserver|ContainerCreatedObserver|ContainerAnalyticsObserver $observer) : void { - trigger_error( - 'The Observer system is being removed from ' . Bootstrap::class . ' in 3.0.', - E_USER_DEPRECATED - ); - $this->observers[] = $observer; - } - public function bootstrapContainer( Profiles $profiles, string $configurationFile = 'annotated-container.xml' @@ -90,16 +56,9 @@ public function bootstrapContainer( $this->emitter?->emitBeforeBootstrap($configuration); - foreach ($configuration->getObservers() as $observer) { - $this->addObserver($observer); - } - - $this->notifyPreAnalysis($profiles); - $analysisPrepped = $this->stopwatch->mark(); $containerDefinition = $this->runStaticAnalysis($configuration, $analysisOptions); - $this->notifyPostAnalysis($profiles, $containerDefinition); $analysisCompleted = $this->stopwatch->mark(); @@ -109,11 +68,8 @@ public function bootstrapContainer( $containerDefinition, ); - $this->notifyContainerCreated($profiles, $containerDefinition, $container); - $metrics = $this->stopwatch->stop(); $analytics = $this->createAnalytics($metrics, $analysisPrepped, $analysisCompleted); - $this->notifyAnalytics($analytics); $this->emitter?->emitAfterBootstrap( $configuration, @@ -130,7 +86,6 @@ private function bootstrappingConfiguration(string $configurationFile) : Bootstr return new XmlBootstrappingConfiguration( $configFile, parameterStoreFactory: $this->parameterStoreFactory, - observerFactory: $this->observerFactory, definitionProviderFactory: $this->definitionProviderFactory ); } @@ -150,14 +105,6 @@ private function analysisOptions(BootstrappingConfiguration $configuration) : Co return $analysisOptions->build(); } - private function notifyPreAnalysis(Profiles $activeProfiles) : void { - foreach ($this->observers as $observer) { - if ($observer instanceof PreAnalysisObserver) { - $observer->notifyPreAnalysis($activeProfiles); - } - } - } - private function runStaticAnalysis( BootstrappingConfiguration $configuration, ContainerDefinitionAnalysisOptions $analysisOptions @@ -182,56 +129,18 @@ private function containerDefinitionAnalyzer(?string $cacheDir) : ContainerDefin return $compiler; } - private function notifyPostAnalysis(Profiles $activeProfiles, ContainerDefinition $containerDefinition) : void { - foreach ($this->observers as $observer) { - if ($observer instanceof PostAnalysisObserver) { - $observer->notifyPostAnalysis($activeProfiles, $containerDefinition); - } - } - } - private function createContainer( BootstrappingConfiguration $configuration, Profiles $activeProfiles, ContainerDefinition $containerDefinition, ) : AnnotatedContainer { - $containerFactory = $this->containerFactory(); - foreach ($configuration->getParameterStores() as $parameterStore) { - $containerFactory->addParameterStore($parameterStore); + $this->containerFactory->addParameterStore($parameterStore); } $factoryOptions = ContainerFactoryOptionsBuilder::forProfiles($activeProfiles); - return $containerFactory->createContainer($containerDefinition, $factoryOptions->build()); - } - - private function containerFactory() : ContainerFactory { - if ($this->containerFactory !== null) { - return $this->containerFactory; - } - - if (class_exists(Injector::class)) { - return new AurynContainerFactory(); - } - - if (class_exists(Container::class)) { - return new PhpDiContainerFactory(); - } - - throw BackingContainerNotFound::fromMissingImplementation(); - } - - private function notifyContainerCreated( - Profiles $activeProfiles, - ContainerDefinition $containerDefinition, - AnnotatedContainer $container - ) : void { - foreach ($this->observers as $observer) { - if ($observer instanceof ContainerCreatedObserver) { - $observer->notifyContainerCreated($activeProfiles, $containerDefinition, $container); - } - } + return $this->containerFactory->createContainer($containerDefinition, $factoryOptions->build()); } private function createAnalytics( @@ -247,11 +156,4 @@ private function createAnalytics( ); } - private function notifyAnalytics(ContainerAnalytics $analytics) : void { - foreach ($this->observers as $observer) { - if ($observer instanceof ContainerAnalyticsObserver) { - $observer->notifyAnalytics($analytics); - } - } - } } diff --git a/src/Bootstrap/BootstrappingConfiguration.php b/src/Bootstrap/BootstrappingConfiguration.php index b816a14b..35b793bf 100644 --- a/src/Bootstrap/BootstrappingConfiguration.php +++ b/src/Bootstrap/BootstrappingConfiguration.php @@ -23,10 +23,4 @@ public function getContainerDefinitionProvider() : ?DefinitionProvider; */ public function getParameterStores() : array; - /** - * @return list - * @deprecated - */ - public function getObservers() : array; - } diff --git a/src/Bootstrap/ContainerAnalyticsObserver.php b/src/Bootstrap/ContainerAnalyticsObserver.php deleted file mode 100644 index 6c7cbdd9..00000000 --- a/src/Bootstrap/ContainerAnalyticsObserver.php +++ /dev/null @@ -1,11 +0,0 @@ -containerDefinition = new ProfilesAwareContainerDefinition($containerDefinition, $activeProfiles); } + /** + * @param class-string $type + * @return list + */ public function getServicesForType(string $type) : array { - /** @var array $services */ + /** @var list $services */ $services = []; foreach ($this->containerDefinition->getServiceDefinitions() as $serviceDefinition) { if ($serviceDefinition->isAbstract()) { @@ -86,5 +90,4 @@ public function getDefinition() : ServiceDefinition { $this->wireServices($container, $serviceGatherer); } - abstract protected function wireServices(AnnotatedContainer $container, ServiceGatherer $gatherer) : void; } diff --git a/src/Bootstrap/ThirdPartyInitializer.php b/src/Bootstrap/ThirdPartyInitializer.php index 95fe0f82..2ef1b895 100644 --- a/src/Bootstrap/ThirdPartyInitializer.php +++ b/src/Bootstrap/ThirdPartyInitializer.php @@ -14,11 +14,5 @@ abstract public function getPackageName() : string; */ abstract public function getRelativeScanDirectories() : array; - /** - * @return list - * @deprecated - */ - abstract public function getObserverClasses() : array; - abstract public function getDefinitionProviderClass() : ?string; } diff --git a/src/Bootstrap/XmlBootstrappingConfiguration.php b/src/Bootstrap/XmlBootstrappingConfiguration.php index bf30f729..4e1f42a0 100644 --- a/src/Bootstrap/XmlBootstrappingConfiguration.php +++ b/src/Bootstrap/XmlBootstrappingConfiguration.php @@ -28,15 +28,9 @@ final class XmlBootstrappingConfiguration implements BootstrappingConfiguration */ private readonly array $parameterStores; - /** - * @var list - */ - private readonly array $observers; - public function __construct( private readonly string $xmlFile, private readonly ?ParameterStoreFactory $parameterStoreFactory = null, - private readonly ?ObserverFactory $observerFactory = null, private readonly ?DefinitionProviderFactory $definitionProviderFactory = null ) { if (!file_exists($this->xmlFile)) { @@ -133,30 +127,6 @@ public function __construct( } } - $observers = []; - $observerNodes = $xpath->query('/ac:annotatedContainer/ac:observers/ac:observer/text()'); - if ($observerNodes instanceof DOMNodeList) { - foreach ($observerNodes as $observerNode) { - $observerClass = (string) $observerNode->nodeValue; - if (isset($this->observerFactory)) { - $observer = $this->observerFactory->createObserver($observerClass); - } else { - if (!$this->isObserverType($observerClass)) { - throw InvalidBootstrapConfiguration::fromConfiguredObserverWrongType($observerClass); - } - $observer = new $observerClass(); - } - $observers[] = $observer; - } - } - - if ($observers !== []) { - trigger_error( - 'There are Observer implementations found in your configuration. This feature will be removed in 3.0 and replaced with an Event system. Your configuration will need to be changed when upgrading to Annotated Container 3+.', - E_USER_DEPRECATED - ); - } - /** @var DOMNodeList $cacheDirNodes */ $cacheDirNodes = $xpath->query('/ac:annotatedContainer/ac:cacheDir'); $cache = null; @@ -168,24 +138,12 @@ public function __construct( $this->definitionProvider = $definitionProvider; $this->cacheDir = $cache; $this->parameterStores = $parameterStores; - $this->observers = $observers; } finally { libxml_clear_errors(); libxml_use_internal_errors(false); } } - private function isObserverType(string $observerClass) : bool { - if (!class_exists($observerClass)) { - return false; - } - - return is_subclass_of($observerClass, PreAnalysisObserver::class) || - is_subclass_of($observerClass, PostAnalysisObserver::class) || - is_subclass_of($observerClass, ContainerCreatedObserver::class) || - is_subclass_of($observerClass, ContainerAnalyticsObserver::class); - } - public function getScanDirectories() : array { return $this->directories; } @@ -206,11 +164,4 @@ public function getCacheDirectory() : ?string { return $this->cacheDir; } - /** - * @return list - * @deprecated - */ - public function getObservers() : array { - return $this->observers; - } } diff --git a/src/Cli/Command/InitCommand.php b/src/Cli/Command/InitCommand.php index b3cd079d..d9d06c4a 100644 --- a/src/Cli/Command/InitCommand.php +++ b/src/Cli/Command/InitCommand.php @@ -341,17 +341,6 @@ private function generateAndSaveConfiguration(Input $input, array $composer, str } } - /** @var string|array|null $observers */ - $observers = $input->getOption('observer'); - $observersNode = $root->appendChild($dom->createElementNS(self::XML_SCHEMA, 'observers')); - if ($observers !== null) { - $observers = is_string($observers) ? [$observers] : $observers; - /** @var string $observer */ - foreach ($observers as $observer) { - $observersNode->appendChild($dom->createElementNS(self::XML_SCHEMA, 'observer', $observer)); - } - } - $vendor = $scanDirectories->appendChild( $dom->createElementNS(self::XML_SCHEMA, 'vendor') ); @@ -382,12 +371,6 @@ private function generateAndSaveConfiguration(Input $input, array $composer, str $dom->createElementNS(self::XML_SCHEMA, 'definitionProvider', $providerClass) ); } - - foreach ($thirdPartyInitializer->getObserverClasses() as $observerClass) { - $observersNode->appendChild( - $dom->createElementNS(self::XML_SCHEMA, 'observer', $observerClass) - ); - } } /** @var string|null $cacheDirOpt */ diff --git a/src/Cli/Command/ValidateCommand.php b/src/Cli/Command/ValidateCommand.php index 2a761070..52ab2176 100644 --- a/src/Cli/Command/ValidateCommand.php +++ b/src/Cli/Command/ValidateCommand.php @@ -6,7 +6,9 @@ use Cspray\AnnotatedContainer\AnnotatedContainer; use Cspray\AnnotatedContainer\Autowire\AutowireableParameterSet; use Cspray\AnnotatedContainer\Bootstrap\Bootstrap; +use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; use Cspray\AnnotatedContainer\Bootstrap\BootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\ContainerAnalytics; use Cspray\AnnotatedContainer\Bootstrap\PostAnalysisObserver; use Cspray\AnnotatedContainer\Cli\Command; use Cspray\AnnotatedContainer\Cli\Exception\ConfigurationNotFound; @@ -16,6 +18,8 @@ use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactoryOptions; use Cspray\AnnotatedContainer\ContainerFactory\ParameterStore; use Cspray\AnnotatedContainer\Definition\ContainerDefinition; +use Cspray\AnnotatedContainer\Event\Emitter; +use Cspray\AnnotatedContainer\Event\Listener\Bootstrap\AfterBootstrap; use Cspray\AnnotatedContainer\Exception\UnsupportedOperation; use Cspray\AnnotatedContainer\LogicalConstraint\Check\DuplicateServiceDelegate; use Cspray\AnnotatedContainer\LogicalConstraint\Check\DuplicateServiceName; @@ -122,17 +126,21 @@ public function handle(Input $input, TerminalOutput $output) : int { throw ConfigurationNotFound::fromMissingFile($configFile); } + $emitter = new Emitter(); + $bootstrap = new Bootstrap( + emitter: $emitter, directoryResolver: $this->directoryResolver, containerFactory: $this->noOpContainerFactory() ); $containerDefinition = null; - $infoCapturingObserver = $this->infoCapturingObserver(static function(ContainerDefinition $definition) use(&$containerDefinition) { + $infoCapturingListener = $this->infoCapturingListener(static function(ContainerDefinition $definition) use(&$containerDefinition) { $containerDefinition = $definition; }); - $bootstrap->addObserver($infoCapturingObserver); + + $emitter->addAfterBootstrapListener($infoCapturingListener); $inputProfiles = $input->getOption('profile') ?? ['default']; if (is_string($inputProfiles)) { @@ -191,23 +199,23 @@ public function createContainer(ContainerDefinition $containerDefinition, Contai return new class implements AnnotatedContainer { public function getBackingContainer() : object { - throw UnsupportedOperation::fromMethodNotSupported(__METHOD__); + throw new \RuntimeException(__METHOD__); } public function make(string $classType, AutowireableParameterSet $parameters = null) : object { - throw UnsupportedOperation::fromMethodNotSupported(__METHOD__); + throw new \RuntimeException(__METHOD__); } public function invoke(callable $callable, AutowireableParameterSet $parameters = null) : mixed { - throw UnsupportedOperation::fromMethodNotSupported(__METHOD__); + throw new \RuntimeException(__METHOD__); } public function get(string $id) { - throw UnsupportedOperation::fromMethodNotSupported(__METHOD__); + throw new \RuntimeException(__METHOD__); } public function has(string $id) : bool { - throw UnsupportedOperation::fromMethodNotSupported(__METHOD__); + throw new \RuntimeException(__METHOD__); } }; } @@ -219,10 +227,10 @@ public function addParameterStore(ParameterStore $parameterStore) : void { /** * @param Closure(ContainerDefinition):void $closure - * @return PostAnalysisObserver + * @return AfterBootstrap */ - private function infoCapturingObserver(Closure $closure) : PostAnalysisObserver { - return new class($closure) implements PostAnalysisObserver { + private function infoCapturingListener(Closure $closure) : AfterBootstrap { + return new class($closure) implements AfterBootstrap { /** * @param Closure(ContainerDefinition):void $closure */ @@ -231,7 +239,7 @@ public function __construct( ) { } - public function notifyPostAnalysis(Profiles $activeProfiles, ContainerDefinition $containerDefinition) : void { + public function handleAfterBootstrap(BootstrappingConfiguration $bootstrappingConfiguration, ContainerDefinition $containerDefinition, AnnotatedContainer $container, ContainerAnalytics $containerAnalytics) : void { ($this->closure)($containerDefinition); } }; diff --git a/src/ContainerFactory/AbstractContainerFactory.php b/src/ContainerFactory/AbstractContainerFactory.php index 1ac21124..8ea1a8a9 100644 --- a/src/ContainerFactory/AbstractContainerFactory.php +++ b/src/ContainerFactory/AbstractContainerFactory.php @@ -12,6 +12,7 @@ use Cspray\AnnotatedContainer\Definition\ServiceDefinition; use Cspray\AnnotatedContainer\Definition\ServiceDelegateDefinition; use Cspray\AnnotatedContainer\Definition\ServicePrepareDefinition; +use Cspray\AnnotatedContainer\Event\ContainerFactoryEmitter; use Cspray\AnnotatedContainer\Exception\ParameterStoreNotFound; use Cspray\AnnotatedContainer\Profiles; use Cspray\Typiphy\ObjectType; @@ -21,6 +22,7 @@ abstract class AbstractContainerFactory implements ContainerFactory { protected readonly AliasDefinitionResolver $aliasDefinitionResolver; + private readonly ?ContainerFactoryEmitter $emitter; /** * @var ParameterStore[] @@ -28,21 +30,28 @@ abstract class AbstractContainerFactory implements ContainerFactory { private array $parameterStores = []; public function __construct( - AliasDefinitionResolver $aliasDefinitionResolver = null + ContainerFactoryEmitter $emitter = null, + AliasDefinitionResolver $aliasDefinitionResolver = null, ) { // Injecting environment variables is something we have supported since early versions. // We don't require adding this parameter store explicitly to continue providing this functionality // without the end-user having to change how they construct their ContainerFactory. $this->addParameterStore(new EnvironmentParameterStore()); $this->aliasDefinitionResolver = $aliasDefinitionResolver ?? new StandardAliasDefinitionResolver(); + $this->emitter = $emitter; } final public function createContainer(ContainerDefinition $containerDefinition, ContainerFactoryOptions $containerFactoryOptions = null) : AnnotatedContainer { $activeProfiles = $containerFactoryOptions?->getProfiles() ?? Profiles::fromList(['default']); - $state = $this->createContainerState($containerDefinition, $activeProfiles); + $this->emitter?->emitBeforeContainerCreation($activeProfiles, $containerDefinition); - $container = $this->createAnnotatedContainer($state, $activeProfiles); + $container = $this->createAnnotatedContainer( + $this->createContainerState($containerDefinition, $activeProfiles), + $activeProfiles + ); + + $this->emitter?->emitAfterContainerCreation($activeProfiles, $containerDefinition, $container); return $container; } diff --git a/src/ContainerFactory/IlluminateContainerFactory.php b/src/ContainerFactory/IlluminateContainerFactory.php index b23e5c4d..1e324d40 100644 --- a/src/ContainerFactory/IlluminateContainerFactory.php +++ b/src/ContainerFactory/IlluminateContainerFactory.php @@ -14,6 +14,7 @@ use Cspray\AnnotatedContainer\Definition\ServiceDefinition; use Cspray\AnnotatedContainer\Definition\ServiceDelegateDefinition; use Cspray\AnnotatedContainer\Definition\ServicePrepareDefinition; +use Cspray\AnnotatedContainer\Event\ContainerFactoryEmitter; use Cspray\AnnotatedContainer\Exception\ServiceNotFound; use Cspray\AnnotatedContainer\Profiles; use Cspray\Typiphy\ObjectType; @@ -25,9 +26,11 @@ final class IlluminateContainerFactory extends AbstractContainerFactory { public function __construct( private readonly Container $container = new \Illuminate\Container\Container(), - AliasDefinitionResolver $aliasDefinitionResolver = null + ContainerFactoryEmitter $emitter = null, + AliasDefinitionResolver $aliasDefinitionResolver = null, + ) { - parent::__construct($aliasDefinitionResolver); + parent::__construct($emitter, $aliasDefinitionResolver); } protected function getBackingContainerType() : ObjectType { diff --git a/src/Exception/BackingContainerNotFound.php b/src/Exception/BackingContainerNotFound.php deleted file mode 100644 index b4e7aa6d..00000000 --- a/src/Exception/BackingContainerNotFound.php +++ /dev/null @@ -1,10 +0,0 @@ -withContent($goodXml) ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $bootstrap = new Bootstrap( + new AurynContainerFactory(), directoryResolver: $directoryResolver + ); $container = $bootstrap->bootstrapContainer(Profiles::fromList(['default'])); $service = $container->get(Fixtures::singleConcreteService()->fooImplementation()->getName()); @@ -95,7 +100,10 @@ public function testBootstrapSingleConcreteServiceWithCache() : void { VirtualFilesystem::newDirectory('.annotated-container-cache') ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $bootstrap = new Bootstrap( + new AurynContainerFactory(), + directoryResolver: $directoryResolver + ); $bootstrap->bootstrapContainer(Profiles::fromList(['default'])); $expected = md5(Fixtures::singleConcreteService()->getPath()); @@ -123,7 +131,10 @@ public function testBootstrapWithValidDefinitionProvider() : void { ->withContent($goodXml) ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $bootstrap = new Bootstrap( + new AurynContainerFactory(), + directoryResolver: $directoryResolver + ); $container = $bootstrap->bootstrapContainer(Profiles::fromList(['default'])); $service = $container->get(Fixtures::thirdPartyServices()->fooInterface()->getName()); @@ -151,7 +162,10 @@ public function testBootstrapWithParameterStores() : void { ->withContent($goodXml) ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $bootstrap = new Bootstrap( + new AurynContainerFactory(), + directoryResolver: $directoryResolver + ); $container = $bootstrap->bootstrapContainer(Profiles::fromList(['default'])); $service = $container->get(Fixtures::injectCustomStoreServices()->scalarInjector()->getName()); @@ -177,7 +191,10 @@ public function testBootstrapResolvesProfileServices() : void { ->withContent($goodXml) ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $bootstrap = new Bootstrap( + new AurynContainerFactory(), + directoryResolver: $directoryResolver + ); $container = $bootstrap->bootstrapContainer(profiles: Profiles::fromList(['default', 'dev'])); $service = $container->get(Fixtures::profileResolvedServices()->fooInterface()->getName()); self::assertInstanceOf(Fixtures::profileResolvedServices()->devImplementation()->getName(), $service); @@ -201,7 +218,10 @@ public function testBootstrapSingleConcreteServiceUsesCustomFileName() : void { ->withContent($goodXml) ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $bootstrap = new Bootstrap( + new AurynContainerFactory(), + directoryResolver: $directoryResolver + ); $container = $bootstrap->bootstrapContainer(profiles: Profiles::fromList(['default']), configurationFile: 'my-container.xml.dist'); $service = $container->get(Fixtures::singleConcreteService()->fooImplementation()->getName()); @@ -244,7 +264,11 @@ public function createProvider(string $identifier) : DefinitionProvider { } }; - $container = (new Bootstrap(directoryResolver: $directoryResolver, definitionProviderFactory: $factory))->bootstrapContainer(Profiles::fromList(['default'])); + $container = (new Bootstrap( + new AurynContainerFactory(), + directoryResolver: $directoryResolver, + definitionProviderFactory: $factory + ))->bootstrapContainer(Profiles::fromList(['default'])); $service = $container->get(Fixtures::thirdPartyServices()->fooInterface()->getName()); @@ -283,107 +307,17 @@ public function createParameterStore(string $identifier) : ParameterStore { } }; - $container = (new Bootstrap(directoryResolver: $directoryResolver, parameterStoreFactory: $factory))->bootstrapContainer(Profiles::fromList(['default'])); + $container = (new Bootstrap( + new AurynContainerFactory(), + directoryResolver: $directoryResolver, + parameterStoreFactory: $factory + ))->bootstrapContainer(Profiles::fromList(['default'])); $service = $container->get(Fixtures::injectCustomStoreServices()->scalarInjector()->getName()); self::assertSame('ac-ackey', $service->key); } - public function testBootstrapObserverInvokedCorrectOrder() : void { - $directoryResolver = new FixtureBootstrappingDirectoryResolver(); - - $goodXml = << - - - - SingleConcreteService - - - -XML; - - VirtualFilesystem::newFile('annotated-container.xml') - ->withContent($goodXml) - ->at($this->vfs); - - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); - - $bootstrap->addObserver($subject = new StubBootstrapObserver()); - - $bootstrap->bootstrapContainer(Profiles::fromList(['default'])); - - self::assertCount(3, $subject->getInvokedMethods()); - self::assertSame([ - [sprintf('%s::%s', StubBootstrapObserver::class, 'notifyPreAnalysis')], - [sprintf('%s::%s', StubBootstrapObserver::class, 'notifyPostAnalysis')], - [sprintf('%s::%s', StubBootstrapObserver::class, 'notifyContainerCreated')] - ], $subject->getInvokedMethods()); - } - - public function testBootstrapMultipleObservers() : void { - $directoryResolver = new FixtureBootstrappingDirectoryResolver(); - - $goodXml = << - - - - SingleConcreteService - - - -XML; - - VirtualFilesystem::newFile('annotated-container.xml') - ->withContent($goodXml) - ->at($this->vfs); - - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); - - $bootstrap->addObserver($one = new StubBootstrapObserver()); - $bootstrap->addObserver($two = new StubBootstrapObserver()); - $bootstrap->addObserver($three = new StubBootstrapObserver()); - - $bootstrap->bootstrapContainer(Profiles::fromList(['default'])); - - self::assertCount(3, $one->getInvokedMethods()); - self::assertCount(3, $two->getInvokedMethods()); - self::assertCount(3, $three->getInvokedMethods()); - } - - public function testObserversAddedFromConfiguration() : void { - $directoryResolver = new FixtureBootstrappingDirectoryResolver(); - - $goodXml = << - - - - SingleConcreteService - - - - Cspray\AnnotatedContainer\Unit\Helper\StubBootstrapObserver - - -XML; - - VirtualFilesystem::newFile('annotated-container.xml') - ->withContent($goodXml) - ->at($this->vfs); - - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); - $bootstrap->bootstrapContainer(Profiles::fromList(['default'])); - - $observers = (new \ReflectionObject($bootstrap))->getProperty('observers')->getValue($bootstrap); - - self::assertCount(1, $observers); - self::assertInstanceOf(StubBootstrapObserver::class, $observers[0]); - self::assertCount(3, $observers[0]->getInvokedMethods()); - } - public function testServiceWiringObserver() : void { $directoryResolver = new FixtureBootstrappingDirectoryResolver(); @@ -402,9 +336,15 @@ public function testServiceWiringObserver() : void { ->withContent($goodXml) ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $emitter = new Emitter(); + + $bootstrap = new Bootstrap( + new AurynContainerFactory($emitter), + emitter: $emitter, + directoryResolver: $directoryResolver + ); - $observer = new class extends ServiceWiringObserver { + $listener = new class extends ServiceWiringListener { private ?AnnotatedContainer $container = null; private array $services = []; @@ -422,17 +362,18 @@ protected function wireServices(AnnotatedContainer $container, ServiceGatherer $ $this->services = $gatherer->getServicesForType(Fixtures::ambiguousAliasedServices()->fooInterface()->getName()); } }; - $bootstrap->addObserver($observer); + + $emitter->addAfterContainerCreationListener($listener); $container = $bootstrap->bootstrapContainer(Profiles::fromList(['default'])); - $actual = $observer->getServices(); + $actual = $listener->getServices(); $actualServices = array_map(fn(ServiceFromServiceDefinition $fromServiceDefinition) => $fromServiceDefinition->getService(), $actual); usort($actualServices, fn($a, $b) => $a::class <=> $b::class); - self::assertSame($container, $observer->getAnnotatedContainer()); + self::assertSame($container, $listener->getAnnotatedContainer()); self::assertSame([ $container->get(Fixtures::ambiguousAliasedServices()->barImplementation()->getName()), $container->get(Fixtures::ambiguousAliasedServices()->bazImplementation()->getName()), @@ -458,9 +399,15 @@ public function testServiceWiringObserverByAttributes() : void { ->withContent($goodXml) ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $emitter = new Emitter(); + + $bootstrap = new Bootstrap( + new AurynContainerFactory($emitter), + emitter: $emitter, + directoryResolver: $directoryResolver + ); - $observer = new class extends ServiceWiringObserver { + $listener = new class extends ServiceWiringListener { private ?AnnotatedContainer $container = null; private array $services = []; @@ -478,14 +425,15 @@ protected function wireServices(AnnotatedContainer $container, ServiceGatherer $ $this->services = $gatherer->getServicesWithAttribute(Repository::class); } }; - $bootstrap->addObserver($observer); + + $emitter->addAfterContainerCreationListener($listener); $container = $bootstrap->bootstrapContainer(Profiles::fromList(['default', 'test'])); - $actual = $observer->getServices(); + $actual = $listener->getServices(); $actualServices = array_map(fn(ServiceFromServiceDefinition $fromServiceDefinition) => $fromServiceDefinition->getService(), $actual); - self::assertSame($container, $observer->getAnnotatedContainer()); + self::assertSame($container, $listener->getAnnotatedContainer()); self::assertSame([ $container->get(Fixtures::customServiceAttribute()->myRepo()->getName()), ], $actualServices); @@ -509,9 +457,15 @@ public function testServiceWiringObserverByTypeProfileAware() : void { ->withContent($goodXml) ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $emitter = new Emitter(); - $observer = new class extends ServiceWiringObserver { + $bootstrap = new Bootstrap( + new AurynContainerFactory($emitter), + emitter: $emitter, + directoryResolver: $directoryResolver + ); + + $listener = new class extends ServiceWiringListener { private ?AnnotatedContainer $container = null; private array $services = []; @@ -529,17 +483,18 @@ protected function wireServices(AnnotatedContainer $container, ServiceGatherer $ $this->services = $gatherer->getServicesForType(Fixtures::profileResolvedServices()->fooInterface()->getName()); } }; - $bootstrap->addObserver($observer); + + $emitter->addAfterContainerCreationListener($listener); $container = $bootstrap->bootstrapContainer(Profiles::fromList(['default', 'prod'])); - $actual = $observer->getServices(); + $actual = $listener->getServices(); $actualServices = array_map(fn(ServiceFromServiceDefinition $fromServiceDefinition) => $fromServiceDefinition->getService(), $actual); usort($actualServices, fn($a, $b) => $a::class <=> $b::class); - self::assertSame($container, $observer->getAnnotatedContainer()); + self::assertSame($container, $listener->getAnnotatedContainer()); self::assertSame([ $container->get(Fixtures::profileResolvedServices()->prodImplementation()->getName()), ], $actualServices); @@ -563,9 +518,15 @@ public function testServiceWiringObserverByAttributesProfileAware() : void { ->withContent($goodXml) ->at($this->vfs); - $bootstrap = new Bootstrap(directoryResolver: $directoryResolver); + $emitter = new Emitter(); + + $bootstrap = new Bootstrap( + new AurynContainerFactory($emitter), + emitter: $emitter, + directoryResolver: $directoryResolver + ); - $observer = new class extends ServiceWiringObserver { + $listener = new class extends ServiceWiringListener { private ?AnnotatedContainer $container = null; private array $services = []; @@ -583,88 +544,14 @@ protected function wireServices(AnnotatedContainer $container, ServiceGatherer $ $this->services = $gatherer->getServicesWithAttribute(Repository::class); } }; - $bootstrap->addObserver($observer); + + $emitter->addAfterContainerCreationListener($listener); // The Repository is only active under 'test' profile and should not be included $container = $bootstrap->bootstrapContainer(Profiles::fromList(['default', 'dev'])); - self::assertSame($container, $observer->getAnnotatedContainer()); - self::assertEmpty($observer->getServices()); - } - - public function testObserverFactoryRespected() : void { - $directoryResolver = new FixtureBootstrappingDirectoryResolver(); - - $goodXml = << - - - - SingleConcreteService - - - - Passed to ObserverFactory - - -XML; - - VirtualFilesystem::newFile('annotated-container.xml') - ->withContent($goodXml) - ->at($this->vfs); - - $observerFactory = $this->getMockBuilder(ObserverFactory::class)->getMock(); - $observerFactory->expects($this->once()) - ->method('createObserver') - ->with('Passed to ObserverFactory') - ->willReturn($this->getMockBuilder(PreAnalysisObserver::class)->getMock()); - - (new Bootstrap( - directoryResolver: $directoryResolver, - observerFactory: $observerFactory - ))->bootstrapContainer(Profiles::fromList(['default'])); - } - - public function testContainerAnalyticsObserverNotifiedAfterContainerCreated() : void { - $directoryResolver = new FixtureBootstrappingDirectoryResolver(); - - $goodXml = << - - - - SingleConcreteService - - - -XML; - - VirtualFilesystem::newFile('annotated-container.xml') - ->withContent($goodXml) - ->at($this->vfs); - - $observer = new class implements ContainerCreatedObserver, ContainerAnalyticsObserver { - private array $calls = []; - - public function notifyAnalytics(ContainerAnalytics $analytics) : void { - $this->calls[] = __FUNCTION__; - } - - public function notifyContainerCreated(Profiles $activeProfiles, ContainerDefinition $containerDefinition, AnnotatedContainer $container) : void { - $this->calls[] = __FUNCTION__; - } - - public function getCalls() : array { - return $this->calls; - } - }; - - $subject = new Bootstrap(directoryResolver: $directoryResolver); - $subject->addObserver($observer); - - $subject->bootstrapContainer(Profiles::fromList(['default'])); - - self::assertSame(['notifyContainerCreated', 'notifyAnalytics'], $observer->getCalls()); + self::assertSame($container, $listener->getAnnotatedContainer()); + self::assertEmpty($listener->getServices()); } public function testContainerAnalyticsHasExpectedTotalDuration() : void { @@ -685,27 +572,32 @@ public function testContainerAnalyticsHasExpectedTotalDuration() : void { ->withContent($goodXml) ->at($this->vfs); - $observer = new class implements ContainerAnalyticsObserver { - private ?ContainerAnalytics $analytics = null; + $emitter = new Emitter(); - public function notifyAnalytics(ContainerAnalytics $analytics) : void { - $this->analytics = $analytics; - } + $listener = new class implements AfterBootstrap { + private ?ContainerAnalytics $analytics = null; public function getAnalytics() : ?ContainerAnalytics { return $this->analytics; } + + public function handleAfterBootstrap(BootstrappingConfiguration $bootstrappingConfiguration, ContainerDefinition $containerDefinition, AnnotatedContainer $container, ContainerAnalytics $containerAnalytics,) : void { + $this->analytics = $containerAnalytics; + } }; $subject = new Bootstrap( + new AurynContainerFactory(), + emitter: $emitter, directoryResolver: $directoryResolver, stopwatch: new Stopwatch(new KnownIncrementingPreciseTime()) ); - $subject->addObserver($observer); + + $emitter->addAfterBootstrapListener($listener); $subject->bootstrapContainer(Profiles::fromList(['default'])); - $analytics = $observer->getAnalytics(); + $analytics = $listener->getAnalytics(); self::assertNotNull($analytics); self::assertSame(3, $analytics->totalTime->timeTakenInNanoseconds()); @@ -714,38 +606,6 @@ public function getAnalytics() : ?ContainerAnalytics { self::assertSame(1, $analytics->timeTakenCreatingContainer->timeTakenInNanoseconds()); } - public function testContainerAnalyticsObserverInConfigurationRespected() : void { - $directoryResolver = new FixtureBootstrappingDirectoryResolver(); - - $goodXml = << - - - - SingleConcreteService - - - - Cspray\AnnotatedContainer\Unit\Helper\StubAnalyticsObserver - - -XML; - - VirtualFilesystem::newFile('annotated-container.xml') - ->withContent($goodXml) - ->at($this->vfs); - - $subject = new Bootstrap( - directoryResolver: $directoryResolver, - ); - - StubAnalyticsObserver::$analytics = null; - - $subject->bootstrapContainer(Profiles::fromList(['default'])); - - self::assertNotNull(StubAnalyticsObserver::$analytics); - } - public function testContainerFactoryPassedToConstructorTakesPriority() : void { $directoryResolver = new FixtureBootstrappingDirectoryResolver(); @@ -802,7 +662,11 @@ public function testBootstrapEventsTriggeredInCorrectOrder() : void { $emitter->addBeforeBootstrapListener($listener); $emitter->addAfterBootstrapListener($listener); - $bootstrap = new Bootstrap(emitter: $emitter, directoryResolver: $directoryResolver); + $bootstrap = new Bootstrap( + new AurynContainerFactory(), + emitter: $emitter, + directoryResolver: $directoryResolver + ); $bootstrap->bootstrapContainer(Profiles::fromList(['default'])); self::assertSame( diff --git a/test/Unit/Cli/Command/InitCommandTest.php b/test/Unit/Cli/Command/InitCommandTest.php index 27cbf7a1..db62ef70 100644 --- a/test/Unit/Cli/Command/InitCommandTest.php +++ b/test/Unit/Cli/Command/InitCommandTest.php @@ -293,9 +293,6 @@ public function testInitDefaultFileComposerJsonPresentCreatesConfigurationFile() Cspray\AnnotatedContainerFixture\VendorScanningInitializers\DependencyDefinitionProvider - - Cspray\AnnotatedContainerFixture\VendorScanningInitializers\DependencyObserver - .annotated-container-cache @@ -353,7 +350,6 @@ public function testDefaultInitTakesConfigurationNameFromOption() : void { - .annotated-container-cache @@ -397,7 +393,6 @@ public function testConfigFileFromComposerRespected() : void { - .annotated-container-cache @@ -459,7 +454,6 @@ public function testHandlesCacheDirAlreadyPresent() : void { - .annotated-container-cache @@ -500,7 +494,6 @@ public function testRespectCacheDirProvidedAsOption() : void { - my-cache-dir @@ -542,7 +535,6 @@ public function testRespectsNestedCacheDir() : void { - path/cache/my-cache-dir @@ -578,7 +570,6 @@ public function testSingleDefinitionProviderRespected() : void { ConsumerClass - .annotated-container-cache @@ -614,7 +605,6 @@ public function testSingleParameterStoreRespected() : void { MyParameterStoreClass - .annotated-container-cache @@ -651,7 +641,6 @@ public function testMultipleParameterStoresRespected() : void { MyParameterStoreClassOne MyParameterStoreClassTwo - .annotated-container-cache @@ -659,78 +648,6 @@ public function testMultipleParameterStoresRespected() : void { self::assertStringEqualsFile('vfs://root/annotated-container.xml', $expected); } - public function testSingleObserverRespected() : void { - VirtualFilesystem::newFile('composer.json') - ->withContent(json_encode([ - 'autoload' => [ - 'psr-0' => [ - 'Namespace\\' => 'src' - ] - ], - ], JSON_THROW_ON_ERROR))->at($this->vfs); - - $input = new StubInput(['observer' => 'MyObserverClass'], ['init']); - $exitCode = $this->subject->handle($input, $this->output); - - self::assertSame(0, $exitCode); - - $expected = << - - - - src - - - - - - MyObserverClass - - .annotated-container-cache - - -XML; - self::assertStringEqualsFile('vfs://root/annotated-container.xml', $expected); - } - - public function testMultipleObserversRespected() : void { - VirtualFilesystem::newFile('composer.json') - ->withContent(json_encode([ - 'autoload' => [ - 'psr-0' => [ - 'Namespace\\' => 'src' - ] - ], - ], JSON_THROW_ON_ERROR))->at($this->vfs); - - $input = new StubInput(['observer' => ['MyObserverClassOne', 'MyObserverClassTwo']], ['init']); - $exitCode = $this->subject->handle($input, $this->output); - - self::assertSame(0, $exitCode); - - $expected = << - - - - src - - - - - - MyObserverClassOne - MyObserverClassTwo - - .annotated-container-cache - - -XML; - self::assertStringEqualsFile('vfs://root/annotated-container.xml', $expected); - } - - public function testConfigFileBooleanThrowsException() : void { $this->expectException(InvalidOptionType::class); $this->expectExceptionMessage('The option "config-file" MUST NOT be a flag-only option.'); diff --git a/test/Unit/ContainerFactoryTestCase.php b/test/Unit/ContainerFactoryTestCase.php index 78184008..6ca52d74 100644 --- a/test/Unit/ContainerFactoryTestCase.php +++ b/test/Unit/ContainerFactoryTestCase.php @@ -4,6 +4,7 @@ use Cspray\AnnotatedContainer\Autowire\AutowireableFactory; use Cspray\AnnotatedContainer\Autowire\AutowireableInvoker; +use Cspray\AnnotatedContainer\Event\Emitter; use Cspray\AnnotatedContainer\Exception\InvalidAlias; use Cspray\AnnotatedContainer\Profiles; use Cspray\AnnotatedContainer\StaticAnalysis\AnnotatedTargetContainerDefinitionAnalyzer; @@ -21,16 +22,15 @@ use Cspray\AnnotatedContainer\Serializer\ContainerDefinitionSerializer; use Cspray\AnnotatedContainer\AnnotatedContainer; use Cspray\AnnotatedContainer\Unit\Helper\StubParameterStore; +use Cspray\AnnotatedContainer\Unit\Helper\StubContainerFactoryListener; use Cspray\AnnotatedContainerFixture; use Cspray\AnnotatedContainerFixture\Fixture; use Cspray\AnnotatedContainerFixture\Fixtures; use Cspray\AnnotatedTarget\PhpParserAnnotatedTargetParser; -use Cspray\Typiphy\Internal\NamedType; use Cspray\Typiphy\ObjectType; use Cspray\Typiphy\Type; use Cspray\Typiphy\TypeIntersect; use Cspray\Typiphy\TypeUnion; -use Illuminate\Contracts\Container\Container; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerExceptionInterface; @@ -44,7 +44,7 @@ abstract class ContainerFactoryTestCase extends TestCase { private Profiles $activeProfiles; - abstract protected function getContainerFactory() : ContainerFactory; + abstract protected function getContainerFactory(Emitter $emitter = new Emitter()) : ContainerFactory; abstract protected function getBackingContainerInstanceOf() : ObjectType; @@ -63,13 +63,14 @@ private function getContainer( string $dir, Profiles $profiles = null, ParameterStore $parameterStore = null, + Emitter $emitter = new Emitter() ) : AnnotatedContainer { $compiler = $this->getContainerDefinitionCompiler(); $optionsBuilder = ContainerDefinitionAnalysisOptionsBuilder::scanDirectories($dir); $containerDefinition = $compiler->analyze($optionsBuilder->build()); $containerOptions = ContainerFactoryOptionsBuilder::forProfiles($profiles ?? Profiles::fromList(['default'])); - $factory = $this->getContainerFactory(); + $factory = $this->getContainerFactory($emitter); if ($parameterStore !== null) { $factory->addParameterStore($parameterStore); } @@ -455,4 +456,20 @@ public function testDeserializingContainerWithInjectAllowsServiceCreation(Fixtur $assertions($containerFactory, $deserialize); } + public function testContainerCreationEventsEmitted() : void { + $emitter = new Emitter(); + + $listener = new StubContainerFactoryListener(); + $emitter->addBeforeContainerCreationListener($listener); + $emitter->addAfterContainerCreationListener($listener); + + $this->getContainer( + Fixtures::singleConcreteService()->getPath(), + Profiles::fromList(['default']), + emitter: $emitter + ); + + self::assertSame(['BeforeContainerCreation', 'AfterContainerCreation'], $listener->getTriggeredEvents()); + } + } diff --git a/test/Unit/ContainerFactoryTests/AurynContainerFactoryTest.php b/test/Unit/ContainerFactoryTests/AurynContainerFactoryTest.php index 21e71bd7..043af1bc 100644 --- a/test/Unit/ContainerFactoryTests/AurynContainerFactoryTest.php +++ b/test/Unit/ContainerFactoryTests/AurynContainerFactoryTest.php @@ -5,14 +5,15 @@ use Auryn\Injector; use Cspray\AnnotatedContainer\ContainerFactory\AurynContainerFactory; use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactory; +use Cspray\AnnotatedContainer\Event\Emitter; use Cspray\AnnotatedContainer\Unit\ContainerFactoryTestCase; use Cspray\Typiphy\ObjectType; use function Cspray\Typiphy\objectType; class AurynContainerFactoryTest extends ContainerFactoryTestCase { - protected function getContainerFactory() : ContainerFactory { - return new AurynContainerFactory(); + protected function getContainerFactory(Emitter $emitter = new Emitter()) : ContainerFactory { + return new AurynContainerFactory(emitter: $emitter); } protected function getBackingContainerInstanceOf() : ObjectType { diff --git a/test/Unit/ContainerFactoryTests/IlluminateContainerFactoryTest.php b/test/Unit/ContainerFactoryTests/IlluminateContainerFactoryTest.php index 24434f1d..86ad8808 100644 --- a/test/Unit/ContainerFactoryTests/IlluminateContainerFactoryTest.php +++ b/test/Unit/ContainerFactoryTests/IlluminateContainerFactoryTest.php @@ -4,6 +4,7 @@ use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactory; use Cspray\AnnotatedContainer\ContainerFactory\IlluminateContainerFactory; +use Cspray\AnnotatedContainer\Event\Emitter; use Cspray\AnnotatedContainer\Unit\ContainerFactoryTestCase; use Cspray\Typiphy\ObjectType; use Illuminate\Contracts\Container\Container; @@ -11,8 +12,8 @@ class IlluminateContainerFactoryTest extends ContainerFactoryTestCase { - protected function getContainerFactory() : ContainerFactory { - return new IlluminateContainerFactory(); + protected function getContainerFactory(Emitter $emitter = new Emitter()) : ContainerFactory { + return new IlluminateContainerFactory(emitter: $emitter); } protected function getBackingContainerInstanceOf() : ObjectType { diff --git a/test/Unit/ContainerFactoryTests/PhpDiContainerFactoryTest.php b/test/Unit/ContainerFactoryTests/PhpDiContainerFactoryTest.php index d18dee8d..d5cf006a 100644 --- a/test/Unit/ContainerFactoryTests/PhpDiContainerFactoryTest.php +++ b/test/Unit/ContainerFactoryTests/PhpDiContainerFactoryTest.php @@ -4,6 +4,7 @@ use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactory; use Cspray\AnnotatedContainer\ContainerFactory\PhpDiContainerFactory; +use Cspray\AnnotatedContainer\Event\Emitter; use Cspray\AnnotatedContainer\Unit\ContainerFactoryTestCase; use Cspray\Typiphy\ObjectType; use DI\Container; @@ -11,8 +12,8 @@ class PhpDiContainerFactoryTest extends ContainerFactoryTestCase { - protected function getContainerFactory() : ContainerFactory { - return new PhpDiContainerFactory(); + protected function getContainerFactory(Emitter $emitter = new Emitter()) : ContainerFactory { + return new PhpDiContainerFactory(emitter: $emitter); } protected function getBackingContainerInstanceOf() : ObjectType { diff --git a/test/Unit/Helper/StubContainerFactoryListener.php b/test/Unit/Helper/StubContainerFactoryListener.php new file mode 100644 index 00000000..f34c7840 --- /dev/null +++ b/test/Unit/Helper/StubContainerFactoryListener.php @@ -0,0 +1,26 @@ +triggeredEvents[] = 'AfterContainerCreation'; + } + + public function handleBeforeContainerCreation(Profiles $profiles, ContainerDefinition $containerDefinition) : void { + $this->triggeredEvents[] = 'BeforeContainerCreation'; + } + + public function getTriggeredEvents() : array { + return $this->triggeredEvents; + } +} \ No newline at end of file diff --git a/test/Unit/XmlBootstrappingConfigurationTest.php b/test/Unit/XmlBootstrappingConfigurationTest.php index 6f608890..f2891eb3 100644 --- a/test/Unit/XmlBootstrappingConfigurationTest.php +++ b/test/Unit/XmlBootstrappingConfigurationTest.php @@ -392,104 +392,6 @@ public function createProvider(string $identifier) : DefinitionProvider { self::assertContainsOnlyInstancesOf(StubDefinitionProviderWithDependencies::class, $provider->getDefinitionProviders()); } - public function testObserversContainsNonClassThrowsException() : void { - $badXml = << - - - - src - - - - something not a class - - -XML; - - VirtualFilesystem::newFile('annotated-container.xml') - ->withContent($badXml) - ->at($this->vfs); - - $this->expectException(InvalidBootstrapConfiguration::class); - $this->expectExceptionMessage(sprintf( - 'The entry something not a class in observers does not implement one of the following interfaces %s, %s, %s or %s', - PreAnalysisObserver::class, - PostAnalysisObserver::class, - ContainerCreatedObserver::class, - ContainerAnalyticsObserver::class - )); - new XmlBootstrappingConfiguration( - 'vfs://root/annotated-container.xml', - ); - } - - public function testObserversContainsNotObserverThrowsException() : void { - $badXml = << - - - - src - - - - Cspray\AnnotatedContainer\Helper\StubDefinitionProvider - - -XML; - - VirtualFilesystem::newFile('annotated-container.xml') - ->withContent($badXml) - ->at($this->vfs); - - $this->expectException(InvalidBootstrapConfiguration::class); - $this->expectExceptionMessage(sprintf( - 'The entry Cspray\AnnotatedContainer\Helper\StubDefinitionProvider in observers does not implement one of the following interfaces %s, %s, %s or %s', - PreAnalysisObserver::class, - PostAnalysisObserver::class, - ContainerCreatedObserver::class, - ContainerAnalyticsObserver::class - )); - new XmlBootstrappingConfiguration( - 'vfs://root/annotated-container.xml', - ); - } - - public function testObserverFactoryPresentRespected() : void { - $goodXml = << - - - - src - - - - Cspray\AnnotatedContainer\Unit\Helper\StubBootstrapObserverWithDependencies - - -XML; - - $observerFactory = new class implements ObserverFactory { - public function createObserver(string $observer) : PreAnalysisObserver|PostAnalysisObserver|ContainerCreatedObserver { - return new $observer('from observer factory'); - } - }; - - VirtualFilesystem::newFile('annotated-container.xml') - ->withContent($goodXml) - ->at($this->vfs); - - $config = new XmlBootstrappingConfiguration( - 'vfs://root/annotated-container.xml', - observerFactory: $observerFactory - ); - - self::assertCount(1, $config->getObservers()); - self::assertInstanceOf(StubBootstrapObserverWithDependencies::class, $config->getObservers()[0]); - } - public function testVendorScanDirectoriesIncludedInList() : void { $goodXml = <<