diff --git a/Justfile b/Justfile index d5c11db1..328d47a4 100644 --- a/Justfile +++ b/Justfile @@ -4,7 +4,7 @@ _default: just --list --unsorted # Install all dependencies necesesary to run Annotated Container tools -install: _install_labrador_cs _install_phpunit _install_psalm _install_ac +install: _install_labrador_cs _install_phpunit _install_psalm _install_phploc _install_ac _install_ac: composer install @@ -18,6 +18,9 @@ _install_phpunit: _install_psalm: cd tools/psalm && composer install +_install_phploc: + cd tools/phploc && composer install + # Run unit tests test *FLAGS: @XDEBUG_MODE=coverage ./tools/phpunit/vendor/bin/phpunit {{FLAGS}} @@ -47,6 +50,9 @@ code-lint: code-lint-fix: @./tools/labrador-cs/vendor/bin/phpcbf -p --standard=./tools/labrador-cs/vendor/cspray/labrador-coding-standard/ruleset.xml --exclude=Generic.Files.LineLength src test +loc: + @./tools/phploc/vendor/bin/phploc src + # Run all CI checks. ALL checks will run, regardless of failures ci-check: -@just test diff --git a/annotated-container.xsd b/annotated-container.xsd index 38949d9b..fb71fdf1 100644 --- a/annotated-container.xsd +++ b/annotated-container.xsd @@ -32,6 +32,7 @@ + @@ -86,5 +87,10 @@ + + + + + \ No newline at end of file diff --git a/docs/how-to/02-bootstrap-your-container.md b/docs/how-to/02-bootstrap-your-container.md index 45e29a57..a6fd2892 100644 --- a/docs/how-to/02-bootstrap-your-container.md +++ b/docs/how-to/02-bootstrap-your-container.md @@ -224,7 +224,7 @@ By default, boostrapping expects all the path fragments in your configuration to namespace Acme\Demo; -use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;use Cspray\AnnotatedContainer\Bootstrap\DefaultDefinitionProviderFactory;use Cspray\AnnotatedContainer\Bootstrap\DefaultParameterStoreFactory;use Cspray\AnnotatedContainer\ContainerFactory\PhpDiContainerFactory;use Cspray\AnnotatedContainer\Event\Emitter; +use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultDefinitionProviderFactory;use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultParameterStoreFactory;use Cspray\AnnotatedContainer\ContainerFactory\PhpDiContainerFactory;use Cspray\AnnotatedContainer\Event\Emitter; // This method is an implementation provided by the reader $directoryResolver = MyDirectoryResolver::create(); @@ -252,12 +252,7 @@ Setting up caching is something that you must explicitly opt into during your bo namespace Acme\Demo; -use Cspray\AnnotatedContainer\Bootstrap\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\CacheAwareBootstrappingConfigurationProvider; -use Cspray\AnnotatedContainer\Bootstrap\DefaultDefinitionProviderFactory; -use Cspray\AnnotatedContainer\Bootstrap\DefaultParameterStoreFactory; -use Cspray\AnnotatedContainer\Bootstrap\XmlBootstrappingConfigurationProvider;use Cspray\AnnotatedContainer\ContainerFactory\PhpDiContainerFactory; -use Cspray\AnnotatedContainer\Definition\Cache\FileBackedContainerDefinitionCache;use Cspray\AnnotatedContainer\Definition\Serializer\XmlContainerDefinitionSerializer;use Cspray\AnnotatedContainer\Event\Emitter; +use Cspray\AnnotatedContainer\Bootstrap\Bootstrap;use Cspray\AnnotatedContainer\Bootstrap\CacheAwareBootstrappingConfigurationProvider;use Cspray\AnnotatedContainer\Bootstrap\XmlBootstrappingConfigurationProvider;use Cspray\AnnotatedContainer\Definition\Cache\FileBackedContainerDefinitionCache;use Cspray\AnnotatedContainer\Definition\Serializer\XmlContainerDefinitionSerializer;use Cspray\AnnotatedContainer\Event\Emitter; $container = Bootstrap::from(new Emitter()) ->bootstrapContainer( diff --git a/known-issues.xml b/known-issues.xml index f92060db..46c2a7c3 100644 --- a/known-issues.xml +++ b/known-issues.xml @@ -1,6 +1,11 @@ - + + + + + + @@ -66,7 +71,7 @@ [$serviceDelegateDefinition->classMethod()->class()->name(), $serviceDelegateDefinition->classMethod()->methodName()], $this->parametersForServiceDelegateToArray($injector, $state, $serviceDelegateDefinition), )]]> - + injector->make($id)]]> @@ -234,11 +239,8 @@ - + directories]]> - - - - + diff --git a/psalm.xml b/psalm.xml index e83fceb2..055fd83d 100644 --- a/psalm.xml +++ b/psalm.xml @@ -36,7 +36,7 @@ - + diff --git a/src/Bootstrap/Bootstrap.php b/src/Bootstrap/Bootstrap.php index beffab3b..fa2f0ec6 100644 --- a/src/Bootstrap/Bootstrap.php +++ b/src/Bootstrap/Bootstrap.php @@ -3,6 +3,17 @@ namespace Cspray\AnnotatedContainer\Bootstrap; use Cspray\AnnotatedContainer\AnnotatedContainer; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultDefinitionProviderFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultListenerFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultParameterStoreFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefinitionProviderFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\ListenerFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\ParameterStoreFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\XmlBootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\BootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\VendorPresenceBasedBootstrappingDirectoryResolver; use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactory; use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactoryOptionsBuilder; use Cspray\AnnotatedContainer\Definition\ContainerDefinition; @@ -33,6 +44,7 @@ public static function fromAnnotatedContainerConventions( ContainerFactory $containerFactory, Emitter $emitter, ParameterStoreFactory $parameterStoreFactory = new DefaultParameterStoreFactory(), + ListenerFactory $listenerFactory = new DefaultListenerFactory(), DefinitionProviderFactory $definitionProviderFactory = new DefaultDefinitionProviderFactory(), BootstrappingDirectoryResolver $directoryResolver = new VendorPresenceBasedBootstrappingDirectoryResolver(), Filesystem $filesystem = new PhpFunctionsFilesystem() @@ -41,7 +53,8 @@ public static function fromAnnotatedContainerConventions( $filesystem, $directoryResolver->configurationPath('annotated-container.xml'), $parameterStoreFactory, - $definitionProviderFactory + $definitionProviderFactory, + $listenerFactory ); return self::fromCompleteSetup( @@ -75,6 +88,10 @@ public function bootstrapContainer( $this->stopwatch->start(); + foreach ($this->bootstrappingConfiguration->listeners() as $listener) { + $this->emitter->addListener($listener); + } + $analysisOptions = $this->analysisOptions($this->bootstrappingConfiguration); $this->emitter->emitBeforeBootstrap($this->bootstrappingConfiguration); diff --git a/src/Bootstrap/BootstrappingConfiguration.php b/src/Bootstrap/Configuration/BootstrappingConfiguration.php similarity index 79% rename from src/Bootstrap/BootstrappingConfiguration.php rename to src/Bootstrap/Configuration/BootstrappingConfiguration.php index d2bc6830..d79bd7fd 100644 --- a/src/Bootstrap/BootstrappingConfiguration.php +++ b/src/Bootstrap/Configuration/BootstrappingConfiguration.php @@ -1,11 +1,12 @@ */ public function parameterStores() : array; + + /** + * @return list + */ + public function listeners() : array; } diff --git a/src/Bootstrap/CacheAwareBootstrappingConfiguration.php b/src/Bootstrap/Configuration/CacheAwareBootstrappingConfiguration.php similarity index 86% rename from src/Bootstrap/CacheAwareBootstrappingConfiguration.php rename to src/Bootstrap/Configuration/CacheAwareBootstrappingConfiguration.php index d1ba5a59..8a239c0d 100644 --- a/src/Bootstrap/CacheAwareBootstrappingConfiguration.php +++ b/src/Bootstrap/Configuration/CacheAwareBootstrappingConfiguration.php @@ -1,6 +1,6 @@ configuration->parameterStores(); } + + public function listeners() : array { + return $this->configuration->listeners(); + } } diff --git a/src/Bootstrap/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php b/src/Bootstrap/Configuration/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php similarity index 88% rename from src/Bootstrap/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php rename to src/Bootstrap/Configuration/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php index ddb7d121..f7b7af79 100644 --- a/src/Bootstrap/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php +++ b/src/Bootstrap/Configuration/ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration.php @@ -1,7 +1,8 @@ $identifier + * @return Listener + */ + public function createListener(string $identifier) : Listener { + assert(is_a($identifier, Listener::class, true)); + return new $identifier(); + } +} diff --git a/src/Bootstrap/DefaultParameterStoreFactory.php b/src/Bootstrap/Configuration/DefaultParameterStoreFactory.php similarity index 92% rename from src/Bootstrap/DefaultParameterStoreFactory.php rename to src/Bootstrap/Configuration/DefaultParameterStoreFactory.php index 76eaf395..2fa724e4 100644 --- a/src/Bootstrap/DefaultParameterStoreFactory.php +++ b/src/Bootstrap/Configuration/DefaultParameterStoreFactory.php @@ -1,6 +1,6 @@ $identifier + * @return Listener + */ + public function createListener(string $identifier) : Listener; +} diff --git a/src/Bootstrap/ParameterStoreFactory.php b/src/Bootstrap/Configuration/ParameterStoreFactory.php similarity index 84% rename from src/Bootstrap/ParameterStoreFactory.php rename to src/Bootstrap/Configuration/ParameterStoreFactory.php index 56eba195..ffa1cde7 100644 --- a/src/Bootstrap/ParameterStoreFactory.php +++ b/src/Bootstrap/Configuration/ParameterStoreFactory.php @@ -1,6 +1,6 @@ + */ + private readonly array $listeners; + public function __construct( private readonly Filesystem $filesystem, private readonly string $xmlFile, private readonly ParameterStoreFactory $parameterStoreFactory, - private readonly DefinitionProviderFactory $definitionProviderFactory + private readonly DefinitionProviderFactory $definitionProviderFactory, + private readonly ListenerFactory $listenerFactory, ) { if (!$this->filesystem->isFile($this->xmlFile)) { throw InvalidBootstrapConfiguration::fromFileMissing($this->xmlFile); } try { - $schemaFile = dirname(__DIR__, 2) . '/annotated-container.xsd'; + $schemaFile = dirname(__DIR__, 3) . '/annotated-container.xsd'; $dom = new DOMDocument(); $dom->loadXML($this->filesystem->read($this->xmlFile)); libxml_use_internal_errors(true); @@ -117,9 +123,21 @@ public function __construct( } } + $listeners = []; + $listenerNodes = $xpath->query('/ac:annotatedContainer/ac:listeners/ac:listener/text()'); + if ($listenerNodes instanceof DOMNodeList) { + foreach ($listenerNodes as $listenerNode) { + assert(isset($listenerNode->nodeValue)); + $listenerType = trim($listenerNode->nodeValue); + $listener = $this->listenerFactory->createListener($listenerType); + $listeners[] = $listener; + } + } + $this->directories = $scanDirectories; $this->definitionProvider = $definitionProvider; $this->parameterStores = $parameterStores; + $this->listeners = $listeners; } finally { libxml_clear_errors(); libxml_use_internal_errors(false); @@ -142,6 +160,13 @@ public function parameterStores() : array { return $this->parameterStores; } + /** + * @return list + */ + public function listeners() : array { + return $this->listeners; + } + public function cache() : ?ContainerDefinitionCache { return null; } diff --git a/src/Bootstrap/BootstrappingDirectoryResolver.php b/src/Bootstrap/DirectoryResolver/BootstrappingDirectoryResolver.php similarity index 76% rename from src/Bootstrap/BootstrappingDirectoryResolver.php rename to src/Bootstrap/DirectoryResolver/BootstrappingDirectoryResolver.php index 9703bcc9..5edefce7 100644 --- a/src/Bootstrap/BootstrappingDirectoryResolver.php +++ b/src/Bootstrap/DirectoryResolver/BootstrappingDirectoryResolver.php @@ -1,6 +1,6 @@ > + */ + abstract public function listeners() : array; + + /** + * @return ?class-string + */ abstract public function definitionProviderClass() : ?string; } diff --git a/src/Bootstrap/ThirdPartyInitializerProvider.php b/src/Bootstrap/Initializer/ThirdPartyInitializerProvider.php similarity index 76% rename from src/Bootstrap/ThirdPartyInitializerProvider.php rename to src/Bootstrap/Initializer/ThirdPartyInitializerProvider.php index b8ba4ed6..d21b1b14 100644 --- a/src/Bootstrap/ThirdPartyInitializerProvider.php +++ b/src/Bootstrap/Initializer/ThirdPartyInitializerProvider.php @@ -1,6 +1,6 @@ $type - * @return list> + * @return list */ public function servicesForType(string $type) : array; diff --git a/src/Bootstrap/ServiceWiringListener.php b/src/Bootstrap/Listener/ServiceWiringListener.php similarity index 95% rename from src/Bootstrap/ServiceWiringListener.php rename to src/Bootstrap/Listener/ServiceWiringListener.php index e4bc88e7..58e5bc6b 100644 --- a/src/Bootstrap/ServiceWiringListener.php +++ b/src/Bootstrap/Listener/ServiceWiringListener.php @@ -1,6 +1,6 @@ $type - * @return list> + * @return list */ public function servicesForType(string $type) : array { - /** @var list> $services */ + /** @var list $services */ $services = []; foreach ($this->containerDefinition->serviceDefinitions() as $serviceDefinition) { if ($serviceDefinition->isAbstract()) { diff --git a/src/Cli/AnnotatedContainerCliRunner.php b/src/Cli/AnnotatedContainerCliRunner.php index e59f5dae..9a864245 100644 --- a/src/Cli/AnnotatedContainerCliRunner.php +++ b/src/Cli/AnnotatedContainerCliRunner.php @@ -2,14 +2,14 @@ namespace Cspray\AnnotatedContainer\Cli; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingDirectoryResolver; -use Cspray\AnnotatedContainer\Bootstrap\ComposerJsonScanningThirdPartyInitializerProvider; -use Cspray\AnnotatedContainer\Bootstrap\ComposerRuntimePackagesComposerJsonPathProvider; -use Cspray\AnnotatedContainer\Bootstrap\ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration; -use Cspray\AnnotatedContainer\Bootstrap\PackagesComposerJsonPathProvider; -use Cspray\AnnotatedContainer\Bootstrap\ThirdPartyInitializerProvider; -use Cspray\AnnotatedContainer\Bootstrap\VendorPresenceBasedBootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\BootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\VendorPresenceBasedBootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ComposerJsonScanningThirdPartyInitializerProvider; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ComposerRuntimePackagesComposerJsonPathProvider; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\PackagesComposerJsonPathProvider; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ThirdPartyInitializerProvider; use Cspray\AnnotatedContainer\Cli\Command\BuildCommand; use Cspray\AnnotatedContainer\Cli\Command\CacheClearCommand; use Cspray\AnnotatedContainer\Cli\Command\Command; @@ -85,7 +85,7 @@ private static function disabledConfigRequiredCommand(string $commandName) : Dis $howToEnable = wordwrap(sprintf( 'A %s object must be provided. This can be accomplished by running the "init" command.', BootstrappingConfiguration::class - )); + ), 80); assert($howToEnable !== ''); return new DisabledCommand($commandName, $howToEnable); } @@ -97,7 +97,7 @@ private static function disabledCacheRequiredCommand(string $commandName) : Disa $configClass = BootstrappingConfiguration::class; $howToEnable = "A $configClass object with a cache() method that returns a non-null value. For more information, " . "read /docs/how-to/03-caching-container-definition.md."; - $howToEnable = wordwrap($howToEnable); + $howToEnable = wordwrap($howToEnable, 80); assert($howToEnable !== ''); return new DisabledCommand($commandName, $howToEnable); } diff --git a/src/Cli/Command/InitCommand.php b/src/Cli/Command/InitCommand.php index 7f0104f2..39d6f187 100644 --- a/src/Cli/Command/InitCommand.php +++ b/src/Cli/Command/InitCommand.php @@ -3,8 +3,8 @@ namespace Cspray\AnnotatedContainer\Cli\Command; use Cspray\AnnotatedContainer\AnnotatedContainerVersion; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingDirectoryResolver; -use Cspray\AnnotatedContainer\Bootstrap\ThirdPartyInitializerProvider; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\BootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ThirdPartyInitializerProvider; use Cspray\AnnotatedContainer\Cli\Exception\ComposerConfigurationNotFound; use Cspray\AnnotatedContainer\Cli\Exception\InvalidOptionType; use Cspray\AnnotatedContainer\Cli\Exception\PotentialConfigurationOverwrite; @@ -103,6 +103,13 @@ public function help() : string { will be defined unless options are passed. If you use this configuration option please review Defining Class Configurations detailed below. + 5. Setup configuration to register Listener implementations in the + Emitter. You can provide multiple --listener options when executing this + command to define configured values. The value passed to this option + MUST be a fully-qualified class name. By default, no listeners will be + defined unless options are passed. If you use this configuration option + please review Defining Class Configurations detailed below. + Resolving File Paths ============================================================================ @@ -138,7 +145,14 @@ class name with an empty constructor. If you require constructor dependencies, Add a ParameterStore to the ContainerFactory. This can be used to allow injecting custom values with the Inject Attribute. Please be sure to review Defining Class Configurations if you use this value. + + --listener="Fully\Qualified\Class\Name" + Add a Listener to the Emitter. This can allow custom functionality to + respond when certain events or actions occur during Annotated Container's + lifecycle. Please be sure to review Defining Class Configurations if you + use this value. + SHELL; } @@ -201,6 +215,11 @@ private function validateInput(Input $input) : void { if (is_bool($parameterStore)) { throw InvalidOptionType::fromBooleanOption('parameter-store'); } + + $listener = $input->option('listener'); + if (is_bool($listener)) { + throw InvalidOptionType::fromBooleanOption('listener'); + } } /** @@ -288,6 +307,14 @@ private function generateAndSaveConfiguration(Input $input, array $composer, str $vendor = $scanDirectories->appendChild( $dom->createElementNS(self::XML_SCHEMA, 'vendor') ); + + $listeners = []; + $listenerInput = $input->option('listener'); + if (is_string($listenerInput)) { + $listeners[] = $listenerInput; + } elseif (is_array($listenerInput)) { + $listeners = $listenerInput; + } foreach ($this->initializerProvider->thirdPartyInitializers() as $thirdPartyInitializer) { $packageRelativeScanDirectories = $thirdPartyInitializer->relativeScanDirectories(); if (count($packageRelativeScanDirectories) > 0) { @@ -314,6 +341,22 @@ private function generateAndSaveConfiguration(Input $input, array $composer, str $dom->createElementNS(self::XML_SCHEMA, 'definitionProvider', $providerClass) ); } + + $listeners = array_merge($listeners, $thirdPartyInitializer->listeners()); + } + + if (count($listeners) > 0) { + $listenersNode = $dom->createElementNS( + self::XML_SCHEMA, + 'listeners' + ); + foreach ($listeners as $listener) { + assert(is_string($listener)); + $listenersNode->appendChild( + $dom->createElementNS(self::XML_SCHEMA, 'listener', $listener) + ); + } + $root->appendChild($listenersNode); } $schemaPath = dirname(__DIR__, 3) . '/annotated-container.xsd'; diff --git a/src/Cli/Command/ValidateCommand.php b/src/Cli/Command/ValidateCommand.php index 97261ea9..afc93cf2 100644 --- a/src/Cli/Command/ValidateCommand.php +++ b/src/Cli/Command/ValidateCommand.php @@ -2,9 +2,9 @@ namespace Cspray\AnnotatedContainer\Cli\Command; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingDirectoryResolver; -use Cspray\AnnotatedContainer\Bootstrap\ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\ContainerDefinitionAnalysisOptionsFromBootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\BootstrappingDirectoryResolver; use Cspray\AnnotatedContainer\Cli\Exception\ProfileNotString; use Cspray\AnnotatedContainer\Cli\Input\Input; use Cspray\AnnotatedContainer\Cli\Output\TerminalOutput; diff --git a/src/ContainerFactory/AurynContainerFactory.php b/src/ContainerFactory/AurynContainerFactory.php index 84d61042..d2cf46a3 100644 --- a/src/ContainerFactory/AurynContainerFactory.php +++ b/src/ContainerFactory/AurynContainerFactory.php @@ -52,11 +52,6 @@ public function __construct( $this->injector->delegate(Profiles::class, fn() => $this->state->activeProfiles()); } - /** - * @template T - * @param class-string|non-empty-string $id - * @return ($id is class-string ? T : mixed) - */ public function get(string $id) { try { if (!$this->has($id)) { @@ -70,9 +65,7 @@ public function get(string $id) { $id = $namedType->name(); } - /** @var T|mixed $value */ - $value = $this->injector->make($id); - return $value; + return $this->injector->make($id); } catch (InjectionException $injectionException) { throw ContainerException::fromCaughtThrowable($injectionException); } diff --git a/src/Event/BootstrapEmitter.php b/src/Event/BootstrapEmitter.php index f6b6e38e..4ad9c689 100644 --- a/src/Event/BootstrapEmitter.php +++ b/src/Event/BootstrapEmitter.php @@ -3,7 +3,7 @@ namespace Cspray\AnnotatedContainer\Event; use Cspray\AnnotatedContainer\AnnotatedContainer; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; use Cspray\AnnotatedContainer\Bootstrap\ContainerAnalytics; use Cspray\AnnotatedContainer\Definition\ContainerDefinition; diff --git a/src/Event/Emitter.php b/src/Event/Emitter.php index 966b880a..74a66272 100644 --- a/src/Event/Emitter.php +++ b/src/Event/Emitter.php @@ -3,7 +3,7 @@ namespace Cspray\AnnotatedContainer\Event; use Cspray\AnnotatedContainer\AnnotatedContainer; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; use Cspray\AnnotatedContainer\Bootstrap\ContainerAnalytics; use Cspray\AnnotatedContainer\ContainerFactory\AliasResolution\AliasResolutionReason; use Cspray\AnnotatedContainer\Definition\AliasDefinition; diff --git a/src/Event/Listener/Bootstrap/AfterBootstrap.php b/src/Event/Listener/Bootstrap/AfterBootstrap.php index 962cc0bf..95dabfc0 100644 --- a/src/Event/Listener/Bootstrap/AfterBootstrap.php +++ b/src/Event/Listener/Bootstrap/AfterBootstrap.php @@ -3,7 +3,7 @@ namespace Cspray\AnnotatedContainer\Event\Listener\Bootstrap; use Cspray\AnnotatedContainer\AnnotatedContainer; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; use Cspray\AnnotatedContainer\Bootstrap\ContainerAnalytics; use Cspray\AnnotatedContainer\Definition\ContainerDefinition; use Cspray\AnnotatedContainer\Event\Listener; diff --git a/src/Event/Listener/Bootstrap/BeforeBootstrap.php b/src/Event/Listener/Bootstrap/BeforeBootstrap.php index 6cb1058e..129db1c0 100644 --- a/src/Event/Listener/Bootstrap/BeforeBootstrap.php +++ b/src/Event/Listener/Bootstrap/BeforeBootstrap.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Event\Listener\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; use Cspray\AnnotatedContainer\Event\Listener; interface BeforeBootstrap extends Listener { diff --git a/src/Exception/InvalidAnnotatedTarget.php b/src/Exception/InvalidAnnotatedTarget.php deleted file mode 100644 index be5ed274..00000000 --- a/src/Exception/InvalidAnnotatedTarget.php +++ /dev/null @@ -1,13 +0,0 @@ - */ + /** @var list */ private array $directories = []; private ?DefinitionProvider $consumer = null; @@ -20,10 +20,10 @@ private function __construct() { /** * Specify the directories that should be parsed when generating the ContainerDefinition * - * @param string ...$directories + * @param non-empty-string ...$directories * @return static */ - public static function scanDirectories(string...$directories) : self { + public static function scanDirectories(string ...$directories) : self { $instance = new self(); $instance->directories = array_values($directories); return $instance; @@ -48,7 +48,7 @@ public function build() : ContainerDefinitionAnalysisOptions { $this->consumer, ) implements ContainerDefinitionAnalysisOptions { /** - * @param list $directories + * @param non-empty-list $directories * @param DefinitionProvider|null $consumer */ public function __construct( diff --git a/test/Fixture/VendorScanningInitializers/vendor/cspray/package/other_src/ThirdInitializer.php b/test/Fixture/VendorScanningInitializers/vendor/cspray/package/other_src/ThirdInitializer.php index 49029ecc..70dd386d 100644 --- a/test/Fixture/VendorScanningInitializers/vendor/cspray/package/other_src/ThirdInitializer.php +++ b/test/Fixture/VendorScanningInitializers/vendor/cspray/package/other_src/ThirdInitializer.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Fixture\VendorScanningInitializers\Vendor\Package; -use Cspray\AnnotatedContainer\Bootstrap\ThirdPartyInitializer; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ThirdPartyInitializer; class ThirdInitializer extends ThirdPartyInitializer { @@ -17,4 +17,8 @@ public function definitionProviderClass() : ?string { public function packageName() : string { return 'cspray/package'; } + + public function listeners() : array { + return []; + } } diff --git a/test/Fixture/VendorScanningInitializers/vendor/cspray/package/src/FirstInitializer.php b/test/Fixture/VendorScanningInitializers/vendor/cspray/package/src/FirstInitializer.php index 319a3e6f..a1f3959f 100644 --- a/test/Fixture/VendorScanningInitializers/vendor/cspray/package/src/FirstInitializer.php +++ b/test/Fixture/VendorScanningInitializers/vendor/cspray/package/src/FirstInitializer.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Fixture\VendorScanningInitializers\Vendor\Package; -use Cspray\AnnotatedContainer\Bootstrap\ThirdPartyInitializer; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ThirdPartyInitializer; // Combined with the #[Service] attribute on SomeService ensures this package // source directory is scanned @@ -22,4 +22,8 @@ public function definitionProviderClass() : ?string { public function packageName() : string { return 'cspray/package'; } + + public function listeners() : array { + return []; + } } diff --git a/test/Fixture/VendorScanningInitializers/vendor/cspray/package/src/SecondInitializer.php b/test/Fixture/VendorScanningInitializers/vendor/cspray/package/src/SecondInitializer.php index 67a6fe60..85bbc54e 100644 --- a/test/Fixture/VendorScanningInitializers/vendor/cspray/package/src/SecondInitializer.php +++ b/test/Fixture/VendorScanningInitializers/vendor/cspray/package/src/SecondInitializer.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Fixture\VendorScanningInitializers\Vendor\Package; -use Cspray\AnnotatedContainer\Bootstrap\ThirdPartyInitializer; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ThirdPartyInitializer; use Cspray\AnnotatedContainer\Fixture\VendorScanningInitializers\DependencyDefinitionProvider; // Ensures that the ThirdPartyDependency is provided, through the DependencyDefinitionProvider @@ -20,4 +20,8 @@ public function definitionProviderClass() : string { public function packageName() : string { return 'cspray/package'; } + + public function listeners() : array { + return []; + } } diff --git a/test/Unit/AnnotatedContainerVersionTest.php b/test/Unit/AnnotatedContainerVersionTest.php index 1ba9bcea..b0afc503 100644 --- a/test/Unit/AnnotatedContainerVersionTest.php +++ b/test/Unit/AnnotatedContainerVersionTest.php @@ -6,7 +6,7 @@ use Cspray\AnnotatedContainer\AnnotatedContainerVersion; use PHPUnit\Framework\TestCase; -class AnnotatedContainerVersionTest extends TestCase { +final class AnnotatedContainerVersionTest extends TestCase { public function testGetApiVersionReturnsVersionFileContents() : void { self::assertSame( diff --git a/test/Unit/Bootstrap/BootstrapTest.php b/test/Unit/Bootstrap/BootstrapTest.php index b267d3fc..c42a8ce5 100644 --- a/test/Unit/Bootstrap/BootstrapTest.php +++ b/test/Unit/Bootstrap/BootstrapTest.php @@ -4,13 +4,17 @@ use Cspray\AnnotatedContainer\AnnotatedContainer; use Cspray\AnnotatedContainer\Bootstrap\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingDirectoryResolver; -use Cspray\AnnotatedContainer\Bootstrap\CacheAwareBootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\CacheAwareBootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultDefinitionProviderFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultListenerFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultParameterStoreFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\XmlBootstrappingConfiguration; use Cspray\AnnotatedContainer\Bootstrap\ContainerAnalytics; -use Cspray\AnnotatedContainer\Bootstrap\ServiceFromServiceDefinition; -use Cspray\AnnotatedContainer\Bootstrap\ServiceGatherer; -use Cspray\AnnotatedContainer\Bootstrap\ServiceWiringListener; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\BootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\Listener\ServiceFromServiceDefinition; +use Cspray\AnnotatedContainer\Bootstrap\Listener\ServiceGatherer; +use Cspray\AnnotatedContainer\Bootstrap\Listener\ServiceWiringListener; use Cspray\AnnotatedContainer\ContainerFactory\AurynContainerFactory; use Cspray\AnnotatedContainer\ContainerFactory\ContainerFactory; use Cspray\AnnotatedContainer\ContainerFactory\ParameterStore; @@ -22,20 +26,23 @@ use Cspray\AnnotatedContainer\Event\Listener\Bootstrap\AfterBootstrap; use Cspray\AnnotatedContainer\Exception\InvalidBootstrapConfiguration; use Cspray\AnnotatedContainer\Filesystem\Filesystem; +use Cspray\AnnotatedContainer\Filesystem\PhpFunctionsFilesystem; +use Cspray\AnnotatedContainer\Fixture\CustomServiceAttribute\Repository; +use Cspray\AnnotatedContainer\Fixture\Fixtures; use Cspray\AnnotatedContainer\Profiles; use Cspray\AnnotatedContainer\StaticAnalysis\ContainerDefinitionAnalysisOptionsBuilder; use Cspray\AnnotatedContainer\StaticAnalysis\DefinitionProvider; use Cspray\AnnotatedContainer\Unit\Helper\FixtureBootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Unit\Helper\StubAnalysisListener; use Cspray\AnnotatedContainer\Unit\Helper\StubBootstrapListener; +use Cspray\AnnotatedContainer\Unit\Helper\StubContainerFactoryListener; use Cspray\AnnotatedContainer\Unit\Helper\StubDefinitionProvider; use Cspray\AnnotatedContainer\Unit\Helper\StubParameterStore; -use Cspray\AnnotatedContainer\Fixture\CustomServiceAttribute\Repository; -use Cspray\AnnotatedContainer\Fixture\Fixtures; use Cspray\PrecisionStopwatch\KnownIncrementingPreciseTime; use Cspray\PrecisionStopwatch\Stopwatch; -use PHPUnit\Framework\TestCase; use org\bovigo\vfs\vfsStream as VirtualFilesystem; use org\bovigo\vfs\vfsStreamDirectory as VirtualDirectory; +use PHPUnit\Framework\TestCase; final class BootstrapTest extends TestCase { @@ -485,4 +492,55 @@ public function testBootstrapFromAnnotatedContainerConventionsWithFilePresentRet $service ); } + + public function testBootstrapWithConfiguredListenersAutomaticallyAddsThemToEmitter() : void { + $emitter = new Emitter(); + $containerFactory = new PhpDiContainerFactory($emitter); + $directoryResolver = new FixtureBootstrappingDirectoryResolver(); + $xml = << + + + + SingleConcreteService + + + + Cspray\AnnotatedContainer\Unit\Helper\StubAnalysisListener + Cspray\AnnotatedContainer\Unit\Helper\StubBootstrapListener + Cspray\AnnotatedContainer\Unit\Helper\StubContainerFactoryListener + + +XML; + VirtualFilesystem::newFile('annotated-container.xml')->at($this->vfs)->setContent($xml); + + $bootstrap = Bootstrap::fromCompleteSetup( + $configuration = new XmlBootstrappingConfiguration( + new PhpFunctionsFilesystem(), + $directoryResolver->configurationPath('annotated-container.xml'), + new DefaultParameterStoreFactory(), + new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() + ), + $containerFactory, + $emitter, + $directoryResolver, + ); + + $bootstrap->bootstrapContainer(); + + self::assertCount(3, $configuration->listeners()); + + $stubAnalysis = $configuration->listeners()[0]; + self::assertInstanceOf(StubAnalysisListener::class, $stubAnalysis); + self::assertCount(3, $stubAnalysis->getTriggeredEvents()); + + $stubBootstrap = $configuration->listeners()[1]; + self::assertInstanceOf(StubBootstrapListener::class, $stubBootstrap); + self::assertCount(2, $stubBootstrap->getTriggeredEvents()); + + $stubFactory = $configuration->listeners()[2]; + self::assertInstanceOf(StubContainerFactoryListener::class, $stubFactory); + self::assertCount(2, $stubFactory->getTriggeredEvents()); + } } diff --git a/test/Unit/Bootstrap/CacheAwareBootstrappingConfigurationTest.php b/test/Unit/Bootstrap/CacheAwareBootstrappingConfigurationTest.php index ae22e123..3d7f31cb 100644 --- a/test/Unit/Bootstrap/CacheAwareBootstrappingConfigurationTest.php +++ b/test/Unit/Bootstrap/CacheAwareBootstrappingConfigurationTest.php @@ -2,9 +2,10 @@ namespace Cspray\AnnotatedContainer\Unit\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; -use Cspray\AnnotatedContainer\Bootstrap\CacheAwareBootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\CacheAwareBootstrappingConfiguration; use Cspray\AnnotatedContainer\Definition\Cache\ContainerDefinitionCache; +use Cspray\AnnotatedContainer\Unit\Helper\StubAnalysisListener; use Cspray\AnnotatedContainer\Unit\Helper\StubDefinitionProvider; use Cspray\AnnotatedContainer\Unit\Helper\StubParameterStore; use PHPUnit\Framework\Attributes\DataProvider; @@ -16,7 +17,8 @@ public static function nonCacheMethodsProvider() : array { return [ 'scanDirectories' => ['scanDirectories', ['dir']], 'containerDefinitionProvider' => ['containerDefinitionProvider', new StubDefinitionProvider()], - 'parameterStores' => ['parameterStores', [new StubParameterStore()]] + 'parameterStores' => ['parameterStores', [new StubParameterStore()]], + 'listeners' => ['listeners', [new StubAnalysisListener()]] ]; } diff --git a/test/Unit/Bootstrap/ComposerJsonScanningThirdPartyInitializerProviderTest.php b/test/Unit/Bootstrap/ComposerJsonScanningThirdPartyInitializerProviderTest.php index a8b1d630..701ac00c 100644 --- a/test/Unit/Bootstrap/ComposerJsonScanningThirdPartyInitializerProviderTest.php +++ b/test/Unit/Bootstrap/ComposerJsonScanningThirdPartyInitializerProviderTest.php @@ -2,12 +2,11 @@ namespace Cspray\AnnotatedContainer\Unit\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\ComposerJsonScanningThirdPartyInitializerProvider; -use Cspray\AnnotatedContainer\Bootstrap\PackagesComposerJsonPathProvider; -use Cspray\AnnotatedContainer\Bootstrap\ThirdPartyInitializer; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ComposerJsonScanningThirdPartyInitializerProvider; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\PackagesComposerJsonPathProvider; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ThirdPartyInitializer; use Cspray\AnnotatedContainer\Exception\InvalidThirdPartyInitializer; use Cspray\AnnotatedContainer\Filesystem\Filesystem; -use Cspray\AnnotatedContainer\Fixture\Fixtures; use Cspray\AnnotatedContainer\Fixture\VendorScanningInitializers\Vendor\Package\FirstInitializer; use Cspray\AnnotatedContainer\Fixture\VendorScanningInitializers\Vendor\Package\SecondInitializer; use Cspray\AnnotatedContainer\Fixture\VendorScanningInitializers\Vendor\Package\ThirdInitializer; diff --git a/test/Unit/Bootstrap/ComposerRuntimePackagesComposerJsonPathProviderTest.php b/test/Unit/Bootstrap/ComposerRuntimePackagesComposerJsonPathProviderTest.php index d44b59b3..9aed8c11 100644 --- a/test/Unit/Bootstrap/ComposerRuntimePackagesComposerJsonPathProviderTest.php +++ b/test/Unit/Bootstrap/ComposerRuntimePackagesComposerJsonPathProviderTest.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Unit\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\ComposerRuntimePackagesComposerJsonPathProvider; +use Cspray\AnnotatedContainer\Bootstrap\Initializer\ComposerRuntimePackagesComposerJsonPathProvider; use PHPUnit\Framework\TestCase; class ComposerRuntimePackagesComposerJsonPathProviderTest extends TestCase { diff --git a/test/Unit/Bootstrap/DefaultDefinitionProviderFactoryTest.php b/test/Unit/Bootstrap/DefaultDefinitionProviderFactoryTest.php index 332215b6..49eac289 100644 --- a/test/Unit/Bootstrap/DefaultDefinitionProviderFactoryTest.php +++ b/test/Unit/Bootstrap/DefaultDefinitionProviderFactoryTest.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Unit\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\DefaultDefinitionProviderFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultDefinitionProviderFactory; use Cspray\AnnotatedContainer\Exception\InvalidDefinitionProvider; use Cspray\AnnotatedContainer\StaticAnalysis\DefinitionProvider; use Cspray\AnnotatedContainer\Unit\Helper\StubDefinitionProvider; diff --git a/test/Unit/Bootstrap/DefaultParameterStoreFactoryTest.php b/test/Unit/Bootstrap/DefaultParameterStoreFactoryTest.php index 5cbff2f8..d48278cc 100644 --- a/test/Unit/Bootstrap/DefaultParameterStoreFactoryTest.php +++ b/test/Unit/Bootstrap/DefaultParameterStoreFactoryTest.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Unit\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\DefaultParameterStoreFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultParameterStoreFactory; use Cspray\AnnotatedContainer\ContainerFactory\ParameterStore; use Cspray\AnnotatedContainer\Exception\InvalidParameterStore; use Cspray\AnnotatedContainer\Unit\Helper\StubParameterStore; diff --git a/test/Unit/Bootstrap/DelegatedParameterStoreFactoryTest.php b/test/Unit/Bootstrap/DelegatedParameterStoreFactoryTest.php index 0135a0df..2b031ce5 100644 --- a/test/Unit/Bootstrap/DelegatedParameterStoreFactoryTest.php +++ b/test/Unit/Bootstrap/DelegatedParameterStoreFactoryTest.php @@ -2,8 +2,8 @@ namespace Cspray\AnnotatedContainer\Unit\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\DelegatedParameterStoreFactory; -use Cspray\AnnotatedContainer\Bootstrap\ParameterStoreFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DelegatedParameterStoreFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\ParameterStoreFactory; use Cspray\AnnotatedContainer\Unit\Helper\StubParameterStore; use PHPUnit\Framework\TestCase; diff --git a/test/Unit/Bootstrap/RootDirectoryBootstrappingDirectoryResolverTest.php b/test/Unit/Bootstrap/RootDirectoryBootstrappingDirectoryResolverTest.php index a5f780b5..bbbffd58 100644 --- a/test/Unit/Bootstrap/RootDirectoryBootstrappingDirectoryResolverTest.php +++ b/test/Unit/Bootstrap/RootDirectoryBootstrappingDirectoryResolverTest.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Unit\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\RootDirectoryBootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\RootDirectoryBootstrappingDirectoryResolver; use PHPUnit\Framework\TestCase; final class RootDirectoryBootstrappingDirectoryResolverTest extends TestCase { diff --git a/test/Unit/Bootstrap/VendorPresenceBasedBootstrappingDirectoryResolverTest.php b/test/Unit/Bootstrap/VendorPresenceBasedBootstrappingDirectoryResolverTest.php index 2d5485b9..a5b69499 100644 --- a/test/Unit/Bootstrap/VendorPresenceBasedBootstrappingDirectoryResolverTest.php +++ b/test/Unit/Bootstrap/VendorPresenceBasedBootstrappingDirectoryResolverTest.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Unit\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\VendorPresenceBasedBootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\VendorPresenceBasedBootstrappingDirectoryResolver; use Cspray\AnnotatedContainer\Filesystem\Filesystem; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; diff --git a/test/Unit/Bootstrap/XmlBootstrappingConfigurationTest.php b/test/Unit/Bootstrap/XmlBootstrappingConfigurationTest.php index 965d62be..d0270139 100644 --- a/test/Unit/Bootstrap/XmlBootstrappingConfigurationTest.php +++ b/test/Unit/Bootstrap/XmlBootstrappingConfigurationTest.php @@ -2,22 +2,26 @@ namespace Cspray\AnnotatedContainer\Unit\Bootstrap; -use Cspray\AnnotatedContainer\Bootstrap\DefaultDefinitionProviderFactory; -use Cspray\AnnotatedContainer\Bootstrap\DefaultParameterStoreFactory; -use Cspray\AnnotatedContainer\Bootstrap\DefinitionProviderFactory; -use Cspray\AnnotatedContainer\Bootstrap\ParameterStoreFactory; -use Cspray\AnnotatedContainer\Bootstrap\XmlBootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultDefinitionProviderFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultListenerFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefaultParameterStoreFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\DefinitionProviderFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\ListenerFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\ParameterStoreFactory; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\XmlBootstrappingConfiguration; use Cspray\AnnotatedContainer\ContainerFactory\ParameterStore; +use Cspray\AnnotatedContainer\Event\Listener; use Cspray\AnnotatedContainer\Exception\InvalidBootstrapConfiguration; use Cspray\AnnotatedContainer\Filesystem\Filesystem; +use Cspray\AnnotatedContainer\Fixture\Fixtures; use Cspray\AnnotatedContainer\StaticAnalysis\CompositeDefinitionProvider; use Cspray\AnnotatedContainer\StaticAnalysis\DefinitionProvider; +use Cspray\AnnotatedContainer\Unit\Helper\StubAnalysisListener; +use Cspray\AnnotatedContainer\Unit\Helper\StubBootstrapListener; +use Cspray\AnnotatedContainer\Unit\Helper\StubContainerFactoryListener; use Cspray\AnnotatedContainer\Unit\Helper\StubDefinitionProvider; use Cspray\AnnotatedContainer\Unit\Helper\StubDefinitionProviderWithDependencies; use Cspray\AnnotatedContainer\Unit\Helper\StubParameterStore; -use Cspray\AnnotatedContainer\Fixture\Fixtures; -use org\bovigo\vfs\vfsStream as VirtualFilesystem; -use org\bovigo\vfs\vfsStreamDirectory as VirtualDirectory; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use function Cspray\AnnotatedContainer\Reflection\types; @@ -64,6 +68,7 @@ public function testXmlDoesNotValidateSchemaThrowsError() : void { '/my/path/to/annotated-container.xml', new DefaultParameterStoreFactory(), new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); } @@ -91,6 +96,7 @@ public function testValidXmlReturnsScanDirectories() : void { '/path/to/annotated-container.xml', new DefaultParameterStoreFactory(), new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); self::assertSame( @@ -124,6 +130,7 @@ public function testValidXmlReturnsDefinitionProvider() : void { '/path/annotated-container.xml', new DefaultParameterStoreFactory(), new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); $provider = $configuration->containerDefinitionProvider(); self::assertInstanceOf( @@ -158,6 +165,7 @@ public function testDefinitionProviderEmptyIfNoneDefined() : void { 'annotated-container.xml', new DefaultParameterStoreFactory(), new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); self::assertNull($config->containerDefinitionProvider()); @@ -182,6 +190,7 @@ public function testCacheDirNotSpecifiedReturnsNull() : void { 'annotated-container.xml', new DefaultParameterStoreFactory(), new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); self::assertNull($config->cache()); } @@ -205,6 +214,7 @@ public function testCacheIsAlwaysNull() : void { 'annotated-container.xml', new DefaultParameterStoreFactory(), new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); self::assertNull($config->cache()); @@ -232,6 +242,7 @@ public function testParameterStoresReturned() : void { 'annotated-container.xml', new DefaultParameterStoreFactory(), new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); self::assertCount(1, $config->parameterStores()); @@ -271,6 +282,7 @@ public function createParameterStore(string $identifier) : ParameterStore { 'annotated-container.xml', $parameterStoreFactory, new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); self::assertCount(1, $config->parameterStores()); @@ -304,7 +316,8 @@ public function createProvider(string $identifier) : DefinitionProvider { $this->filesystem, 'annotated-container.xml', new DefaultParameterStoreFactory(), - $consumerFactory + $consumerFactory, + new DefaultListenerFactory() ); $provider = $config->containerDefinitionProvider(); @@ -347,7 +360,8 @@ public function testVendorScanDirectoriesIncludedInList() : void { $this->filesystem, 'annotated-container.xml', new DefaultParameterStoreFactory(), - new DefaultDefinitionProviderFactory() + new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); self::assertSame( @@ -369,7 +383,79 @@ public function testConfigurationFileNotPresentThrowsException() : void { $this->filesystem, 'annotated-container.xml', new DefaultParameterStoreFactory(), - new DefaultDefinitionProviderFactory() + new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() + ); + } + + public function testConfigurationFileWithListenersPresentReturnsCorrectInstance() : void { + $goodXml = << + + + + src + + + + Cspray\AnnotatedContainer\Unit\Helper\StubAnalysisListener + Cspray\AnnotatedContainer\Unit\Helper\StubBootstrapListener + Cspray\AnnotatedContainer\Unit\Helper\StubContainerFactoryListener + + +XML; + + $this->mockFilePresentFilesystemInteractions('annotated-container.xml', $goodXml); + + $configuration = new XmlBootstrappingConfiguration( + $this->filesystem, + 'annotated-container.xml', + new DefaultParameterStoreFactory(), + new DefaultDefinitionProviderFactory(), + new DefaultListenerFactory() ); + + self::assertCount(3, $configuration->listeners()); + self::assertContainsOnlyInstancesOf(Listener::class, $configuration->listeners()); + self::assertInstanceOf(StubAnalysisListener::class, $configuration->listeners()[0]); + self::assertInstanceOf(StubBootstrapListener::class, $configuration->listeners()[1]); + self::assertInstanceOf(StubContainerFactoryListener::class, $configuration->listeners()[2]); + } + + public function testConfigurationFileWithListenersUsesListenerFactoryForCreation() : void { + $goodXml = << + + + + src + + + + Cspray\AnnotatedContainer\Unit\Helper\StubAnalysisListener + + +XML; + + $this->mockFilePresentFilesystemInteractions('annotated-container.xml', $goodXml); + + $listener = $this->createMock(Listener::class); + $listenerFactory = $this->createMock(ListenerFactory::class); + $listenerFactory->expects($this->once()) + ->method('createListener') + ->with(StubAnalysisListener::class) + ->willReturn($listener); + + $configuration = new XmlBootstrappingConfiguration( + $this->filesystem, + 'annotated-container.xml', + new DefaultParameterStoreFactory(), + new DefaultDefinitionProviderFactory(), + $listenerFactory + ); + + self::assertCount(1, $configuration->listeners()); + self::assertContainsOnlyInstancesOf(Listener::class, $configuration->listeners()); + self::assertSame($listener, $configuration->listeners()[0]); } } diff --git a/test/Unit/Cli/AnnotatedContainerCliRunnerTest.php b/test/Unit/Cli/AnnotatedContainerCliRunnerTest.php index bcddc191..cccaa93d 100644 --- a/test/Unit/Cli/AnnotatedContainerCliRunnerTest.php +++ b/test/Unit/Cli/AnnotatedContainerCliRunnerTest.php @@ -3,7 +3,7 @@ namespace Cspray\AnnotatedContainer\Unit\Cli; use Cspray\AnnotatedContainer\AnnotatedContainerVersion; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; use Cspray\AnnotatedContainer\Cli\AnnotatedContainerCliRunner; use Cspray\AnnotatedContainer\Cli\Command\BuildCommand; use Cspray\AnnotatedContainer\Cli\Command\CacheClearCommand; @@ -66,9 +66,9 @@ public function testSetupWithNullConfigurationHasCorrectBuildCommandHelp() : voi $expected = <<subject->help()); @@ -343,20 +360,35 @@ public function testThirdPartyInitializerProvidersAreAddedToConfiguration() : vo ] ], JSON_THROW_ON_ERROR)); - $thirdPartyInitializer = $this->createMock(ThirdPartyInitializer::class); - $thirdPartyInitializer->expects($this->once()) + $firstInitializer = $this->createMock(ThirdPartyInitializer::class); + $firstInitializer->expects($this->once()) ->method('packageName') ->willReturn('cspray/package-name'); - $thirdPartyInitializer->expects($this->once()) + $firstInitializer->expects($this->once()) ->method('relativeScanDirectories') ->willReturn(['src', 'lib']); - $thirdPartyInitializer->expects($this->once()) + $firstInitializer->expects($this->once()) ->method('definitionProviderClass') ->willReturn(StubDefinitionProvider::class); + $firstInitializer->expects($this->once()) + ->method('listeners') + ->willReturn([StubAnalysisListener::class]); + + $secondInitializer = $this->createMock(ThirdPartyInitializer::class); + $secondInitializer->expects($this->once()) + ->method('relativeScanDirectories') + ->willReturn([]); + $secondInitializer->expects($this->never())->method('packageName'); + $secondInitializer->expects($this->once()) + ->method('definitionProviderClass') + ->willReturn(null); + $secondInitializer->expects($this->once()) + ->method('listeners') + ->willReturn([StubBootstrapListener::class, StubContainerFactoryListener::class]); $this->thirdPartyInitializerProvider->expects($this->once()) ->method('thirdPartyInitializers') - ->willReturn([$thirdPartyInitializer]); + ->willReturn([$firstInitializer, $secondInitializer]); $version = AnnotatedContainerVersion::version(); $definitionProvider = StubDefinitionProvider::class; @@ -381,6 +413,11 @@ public function testThirdPartyInitializerProvidersAreAddedToConfiguration() : vo $definitionProvider + + Cspray\AnnotatedContainer\Unit\Helper\StubAnalysisListener + Cspray\AnnotatedContainer\Unit\Helper\StubBootstrapListener + Cspray\AnnotatedContainer\Unit\Helper\StubContainerFactoryListener + XML; @@ -508,6 +545,63 @@ public function testSingleParameterStoreRespected() : void { self::assertSame(0, $exitCode); } + public function testSingleListenerRespected() : void { + $this->directoryResolver->expects($this->once()) + ->method('configurationPath') + ->with('annotated-container.xml') + ->willReturn('/config/dir/annotated-container.xml'); + + $this->directoryResolver->expects($this->once()) + ->method('rootPath') + ->with('composer.json') + ->willReturn('/root/composer.json'); + + $this->filesystem->expects($this->exactly(2)) + ->method('exists') + ->willReturnMap([ + ['/config/dir/annotated-container.xml', false], + ['/root/composer.json', true] + ]); + + $this->filesystem->expects($this->once()) + ->method('read') + ->with('/root/composer.json') + ->willReturn(json_encode([ + 'autoload' => [ + 'psr-4' => [ + 'Another\\Namespace\\' => ['src'] + ] + ], + ], JSON_THROW_ON_ERROR)); + + $version = AnnotatedContainerVersion::version(); + $expected = << + + + + src + + + + + + MyListenerClass + + + +XML; + + $this->filesystem->expects($this->once()) + ->method('write') + ->with('/config/dir/annotated-container.xml', $expected); + + $input = new StubInput(['listener' => 'MyListenerClass'], ['init']); + $exitCode = $this->subject->handle($input, $this->output); + + self::assertSame(0, $exitCode); + } + public function testMultipleParameterStoresRespected() : void { $this->directoryResolver->expects($this->once()) ->method('configurationPath') @@ -566,6 +660,64 @@ public function testMultipleParameterStoresRespected() : void { self::assertSame(0, $exitCode); } + public function testMultipleListenersRespected() : void { + $this->directoryResolver->expects($this->once()) + ->method('configurationPath') + ->with('annotated-container.xml') + ->willReturn('/config/dir/annotated-container.xml'); + + $this->directoryResolver->expects($this->once()) + ->method('rootPath') + ->with('composer.json') + ->willReturn('/root/composer.json'); + + $this->filesystem->expects($this->exactly(2)) + ->method('exists') + ->willReturnMap([ + ['/config/dir/annotated-container.xml', false], + ['/root/composer.json', true] + ]); + + $this->filesystem->expects($this->once()) + ->method('read') + ->with('/root/composer.json') + ->willReturn(json_encode([ + 'autoload' => [ + 'psr-4' => [ + 'Another\\Namespace\\' => ['src'] + ] + ], + ], JSON_THROW_ON_ERROR)); + + $version = AnnotatedContainerVersion::version(); + $expected = << + + + + src + + + + + + MyListenerClassOne + MyListenerClassTwo + + + +XML; + + $this->filesystem->expects($this->once()) + ->method('write') + ->with('/config/dir/annotated-container.xml', $expected); + + $input = new StubInput(['listener' => ['MyListenerClassOne', 'MyListenerClassTwo']], ['init']); + $exitCode = $this->subject->handle($input, $this->output); + + self::assertSame(0, $exitCode); + } + public function testDefinitionProviderBooleanThrowsException() : void { $this->expectException(InvalidOptionType::class); $this->expectExceptionMessage('The option "definition-provider" MUST NOT be a flag-only option.'); @@ -590,6 +742,14 @@ public function testParameterStoreBooleanThrowsException() : void { $this->subject->handle($input, $this->output); } + public function testListenerBooleanThrowsException() : void { + $this->expectException(InvalidOptionType::class); + $this->expectExceptionMessage('The option "listener" MUST NOT be a flag-only option.'); + + $input = new StubInput(['listener' => true], ['init']); + $this->subject->handle($input, $this->output); + } + public function testSuccessfulRunHasCorrectOutput() : void { $this->directoryResolver->expects($this->once()) ->method('configurationPath') diff --git a/test/Unit/Cli/Command/ValidateCommandTest.php b/test/Unit/Cli/Command/ValidateCommandTest.php index 47700841..933a76b7 100644 --- a/test/Unit/Cli/Command/ValidateCommandTest.php +++ b/test/Unit/Cli/Command/ValidateCommandTest.php @@ -3,10 +3,12 @@ namespace Cspray\AnnotatedContainer\Unit\Cli\Command; use Cspray\AnnotatedContainer\Attribute\Service; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; use Cspray\AnnotatedContainer\Cli\Command\ValidateCommand; use Cspray\AnnotatedContainer\Cli\Exception\ProfileNotString; use Cspray\AnnotatedContainer\Cli\Output\TerminalOutput; +use Cspray\AnnotatedContainer\Fixture\LogicalConstraints\DuplicateServiceType\DummyService; +use Cspray\AnnotatedContainer\Fixture\LogicalConstraints\LogicalConstraintFixtures; use Cspray\AnnotatedContainer\LogicalConstraint\Check\DuplicateServiceDelegate; use Cspray\AnnotatedContainer\LogicalConstraint\Check\DuplicateServiceName; use Cspray\AnnotatedContainer\LogicalConstraint\Check\DuplicateServicePrepare; @@ -17,8 +19,6 @@ use Cspray\AnnotatedContainer\Unit\Helper\FixtureBootstrappingDirectoryResolver; use Cspray\AnnotatedContainer\Unit\Helper\InMemoryOutput; use Cspray\AnnotatedContainer\Unit\Helper\StubInput; -use Cspray\AnnotatedContainer\Fixture\LogicalConstraints\DuplicateServiceType\DummyService; -use Cspray\AnnotatedContainer\Fixture\LogicalConstraints\LogicalConstraintFixtures; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; diff --git a/test/Unit/Event/EmitterTest.php b/test/Unit/Event/EmitterTest.php index 733372a5..639d2d62 100644 --- a/test/Unit/Event/EmitterTest.php +++ b/test/Unit/Event/EmitterTest.php @@ -4,7 +4,7 @@ use Closure; use Cspray\AnnotatedContainer\AnnotatedContainer; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; use Cspray\AnnotatedContainer\Bootstrap\ContainerAnalytics; use Cspray\AnnotatedContainer\ContainerFactory\AliasResolution\AliasResolutionReason; use Cspray\AnnotatedContainer\Definition\AliasDefinition; diff --git a/test/Unit/Helper/FixtureBootstrappingDirectoryResolver.php b/test/Unit/Helper/FixtureBootstrappingDirectoryResolver.php index 178f5559..ddb3bb9a 100644 --- a/test/Unit/Helper/FixtureBootstrappingDirectoryResolver.php +++ b/test/Unit/Helper/FixtureBootstrappingDirectoryResolver.php @@ -2,7 +2,7 @@ namespace Cspray\AnnotatedContainer\Unit\Helper; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingDirectoryResolver; +use Cspray\AnnotatedContainer\Bootstrap\DirectoryResolver\BootstrappingDirectoryResolver; use Cspray\AnnotatedContainer\Fixture\Fixtures; final class FixtureBootstrappingDirectoryResolver implements BootstrappingDirectoryResolver { diff --git a/test/Unit/Helper/StubBootstrapListener.php b/test/Unit/Helper/StubBootstrapListener.php index 560a615c..df18f2c6 100644 --- a/test/Unit/Helper/StubBootstrapListener.php +++ b/test/Unit/Helper/StubBootstrapListener.php @@ -3,7 +3,7 @@ namespace Cspray\AnnotatedContainer\Unit\Helper; use Cspray\AnnotatedContainer\AnnotatedContainer; -use Cspray\AnnotatedContainer\Bootstrap\BootstrappingConfiguration; +use Cspray\AnnotatedContainer\Bootstrap\Configuration\BootstrappingConfiguration; use Cspray\AnnotatedContainer\Bootstrap\ContainerAnalytics; use Cspray\AnnotatedContainer\Definition\ContainerDefinition; use Cspray\AnnotatedContainer\Event\Listener\Bootstrap\AfterBootstrap; diff --git a/test/Unit/ProfilesTest.php b/test/Unit/ProfilesTest.php index f2519c77..bdf4fd6a 100644 --- a/test/Unit/ProfilesTest.php +++ b/test/Unit/ProfilesTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase; use Closure; -class ProfilesTest extends TestCase { +final class ProfilesTest extends TestCase { public function testProfilesFromListReturnsCorrectArray() : void { $subject = Profiles::fromList(['default', 'dev', 'prod']); diff --git a/test/Unit/ThirdPartyFunctionsTest.php b/test/Unit/ThirdPartyFunctionsTest.php index 0fa44c98..f45f65b2 100644 --- a/test/Unit/ThirdPartyFunctionsTest.php +++ b/test/Unit/ThirdPartyFunctionsTest.php @@ -10,7 +10,7 @@ use function Cspray\AnnotatedContainer\Definition\service; use function Cspray\AnnotatedContainer\Reflection\types; -class ThirdPartyFunctionsTest extends TestCase { +final class ThirdPartyFunctionsTest extends TestCase { use ContainerDefinitionAssertionsTrait; diff --git a/tools/phploc/composer.json b/tools/phploc/composer.json new file mode 100644 index 00000000..5e14c258 --- /dev/null +++ b/tools/phploc/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "phploc/phploc": "^7.0" + } +} \ No newline at end of file