diff --git a/docs/book/index.md b/docs/book/index.md new file mode 100644 index 00000000..86a84e30 --- /dev/null +++ b/docs/book/index.md @@ -0,0 +1,3 @@ +# This Is Only a Placeholder + +The content of this page is automatically generated. diff --git a/docs/book/v3/cookbook/factories-vs-abstract-factories.md b/docs/book/v3/cookbook/factories-vs-abstract-factories.md index 986747d5..49558816 100644 --- a/docs/book/v3/cookbook/factories-vs-abstract-factories.md +++ b/docs/book/v3/cookbook/factories-vs-abstract-factories.md @@ -49,14 +49,14 @@ The service manager is optimized to locate *factories*, as it can do an immediate hash table lookup; abstract factories involve: - Looping through each abstract factory - - invoking its method for service location - - if the service is located, using the factory + - invoking its method for service location + - if the service is located, using the factory This means, internally: - a hash table lookup (for the abstract factory) - invocation of 1:N methods for discovery - - which may contain additional lookups and/or retrievals in the container + - which may contain additional lookups and/or retrievals in the container - invocation of a factory method (assuming successful lookup) As such, having an explicit map can aid performance dramatically. diff --git a/docs/book/v3/index.html b/docs/book/v3/index.html deleted file mode 100644 index 5c00f74d..00000000 --- a/docs/book/v3/index.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
-

laminas-servicemanager

- -

Factory-Driven Dependency Injection Container

- -
$ composer require laminas/laminas-servicemanager
-
-
- diff --git a/docs/book/v3/index.md b/docs/book/v3/index.md deleted file mode 120000 index 8a33348c..00000000 --- a/docs/book/v3/index.md +++ /dev/null @@ -1 +0,0 @@ -../../../README.md \ No newline at end of file diff --git a/docs/book/v3/lazy-services.md b/docs/book/v3/lazy-services.md new file mode 100644 index 00000000..3f24327c --- /dev/null +++ b/docs/book/v3/lazy-services.md @@ -0,0 +1,146 @@ +# Lazy Services + +`Laminas\ServiceManager` can use [delegator factories](delegators.md) to generate +"lazy" references to your services. + +Lazy services are [proxies](http://en.wikipedia.org/wiki/Proxy_pattern) that +get lazily instantiated, and keep a reference to the real instance of +the proxied service. + +## Use cases + +You may want to lazily initialize a service when it is instantiated very often, +but not always used. + +A typical example is a database connection: it is a dependency to many other +elements in your application, but that doesn't mean that every request will +execute queries through it. + +Additionally, instantiating a connection to the database may require some time +and eat up resources. + +Proxying the database connection would allow you to delay that overhead until the +object is really needed. + +## Setup + +`Laminas\ServiceManager\Proxy\LazyServiceFactory` is a [delegator factory](delegators.md) +capable of generating lazy loading proxies for your services. + +The lazy service facilities depend on [ProxyManager](https://github.com/FriendsOfPHP/proxy-manager-lts); +you will need to install that package before using the feature: + +```php +$ composer require friendsofphp/proxy-manager-lts +``` + +## Practical example + +To demonstrate how a lazy service works, you may use the following `Buzzer` +example class, which is designed to be slow at instantiation time for +demonstration purposes: + +```php +namespace MyApp; + +class Buzzer +{ + public function __construct() + { + // deliberately halting the application for 5 seconds + sleep(5); + } + + public function buzz() + { + return 'Buzz!'; + } +} +``` + +You can then proceed and configure the service manager to generate proxies +instead of real services: + +```php +use MyApp\Buzzer; +use Laminas\ServiceManager\Factory\InvokableFactory; +use Laminas\ServiceManager\Proxy\LazyServiceFactory; +use Laminas\ServiceManager\ServiceManager; + +$serviceManager = new \Laminas\ServiceManager\ServiceManager([ + 'factories' => [ + Buzzer::class => InvokableFactory::class, + ], + 'lazy_services' => [ + // Mapping services to their class names is required + // since the ServiceManager is not a declarative DIC. + 'class_map' => [ + Buzzer::class => Buzzer::class, + ], + ], + 'delegators' => [ + Buzzer::class => [ + LazyServiceFactory::class, + ], + ], +]); +``` + +This configuration tells the service manager to add the add +`LazyServiceFactory` as a delegator for `Buzzer`. + +You can now retrieve the buzzer: + +```php +use MyApp\Buzzer; + +$buzzer = $serviceManager->get(Buzzer::class); +echo $buzzer->buzz(); +``` + +To verify that the proxying occurred correctly, you can run the following code, +which should delay the 5 seconds wait time hardcoded in `Buzzer::__construct` +until `Buzzer::buzz` is invoked: + +```php +use MyApp\Buzzer; + +for ($i = 0; $i < 100; $i += 1) { + $buzzer = $serviceManager->get(Buzzer::class); + echo "created buzzer $i\n"; +} + +echo $buzzer->buzz(); +``` + +## Configuration + +This is the config structure expected by `Laminas\ServiceManager\Proxy\LazyServiceFactory`, +in the `lazy_services` key passed in the service manager configuration: + +```php +[ + // map of service names and their relative class names - this + // is required since the service manager cannot know the + // class name of defined services up front + 'class_map' => [ + // 'foo' => 'MyApplication\Foo', + ], + + // directory where proxy classes will be written - default to system_get_tmp_dir() + 'proxies_target_dir' => null, + + // namespace of the generated proxies, default to "ProxyManagerGeneratedProxy" + 'proxies_namespace' => null, + + // whether the generated proxy classes should be written to disk or generated on-the-fly + 'write_proxy_files' => false, +]; +``` + +After you have an instance, you can map lazy service/class pairs using +`mapLazyService()`: + +```php +$container->mapLazyService('foo', \MyApplication\Foo::class); +``` diff --git a/docs/book/v3/migration.md b/docs/book/v3/migration.md index 8fa1d899..4575a281 100644 --- a/docs/book/v3/migration.md +++ b/docs/book/v3/migration.md @@ -952,9 +952,9 @@ In addition, review the following changes. #### Constructor - The constructor now accepts the following arguments, in the following order: - - The parent container instance; this is usually the application-level + - The parent container instance; this is usually the application-level `ServiceManager` instance. - - Optionally, an array of configuration for the plugin manager instance; this + - Optionally, an array of configuration for the plugin manager instance; this should have the same format as for a `ServiceManager` instance. - `validatePlugin()` was renamed to `validate()` (now defined in `PluginManagerInterface`). The `AbstractPluginManager` provides diff --git a/docs/book/v3/plugin-managers.md b/docs/book/v3/plugin-managers.md new file mode 100644 index 00000000..5ded5d69 --- /dev/null +++ b/docs/book/v3/plugin-managers.md @@ -0,0 +1,92 @@ +# Plugin managers + +Plugin managers are *specialized* service managers, typically used to create +homogeneous objects of a specific type. + +Because a plugin manager extends a service manager, it works the same and can +be configured similarly. It provides a separation of concerns (it will be used +in specific contexts), and provides additional instance validation. + +Laminas components extensively use plugin managers to create services +that share common functionalities. For instance, all validator services are +specified inside a specialized `ValidatorPluginManager`. + +## Creating a plugin manager + +To create a plugin manager, you first need to create a new class that extends +`Laminas\ServiceManager\AbstractPluginManager`: + +```php +class ValidatorPluginManager extends AbstractPluginManager +{ + protected $instanceOf = ValidatorInterface::class; +} +``` + +The `$instanceOf` variable specifies a class/interface type that all instances +retrieved from the plugin manager must fulfill. If an instance created by the +plugin manager does not match, a `Laminas\ServiceManager\Exception\InvalidServiceException` +exception will be thrown. + +Most of the time, this shortcut is enough. However if you have more complex +validation rules, you can override the `validate()` method: + +```php +class ValidatorPluginManager extends AbstractPluginManager +{ + public function validate($instance) + { + if ($instance instanceof Foo || $instance instanceof Bar) { + return; + } + + throw new InvalidServiceException('This is not a valid service!'); + } +} +``` + +## Configuring a plugin manager + +A plugin manager requires that you pass a parent service manager (typically, +the application's service manager) as well as service configuration. Service +configuration follows the exact same pattern as for a normal service manager; +refer to the [configuring the service manager](configuring-the-service-manager.md) section for details. + +Because a plugin manager is often a service itself, we recommend you to +register the plugin manager as part of the general service manager, as shown +below: + +```php +$serviceManager = new ServiceManager([ + 'factories' => [ + ValidatorPluginManager::class => function(ContainerInterface $container, $requestedName) { + return new ValidatorPluginManager($container, [ + 'factories' => [ + StringLengthValidator::class => InvokableFactory::class, + ], + ]); + }, + ], +]); + +// Get the plugin manager: + +$pluginManager = $serviceManager->get(ValidatorPluginManager::class); + +// Use the plugin manager + +$validator = $pluginManager->get(StringLengthValidator::class); +``` + +> Unlike the version 2 implementation, when inside the context of the factory +> of a service created by a plugin manager, the passed container **will not +> be** the plugin manager, but the parent service manager instead. If you need +> access to other plugins of the same type, you will need to fetch the plugin +> manager from the container: +> +> ```php +> function ($container, $name, array $options = []) { +> $validators = $container->get(ValidatorPluginManager::class); +> // ... +> } +> ``` diff --git a/docs/book/v3/reflection-abstract-factory.md b/docs/book/v3/reflection-abstract-factory.md new file mode 100644 index 00000000..9e771b6d --- /dev/null +++ b/docs/book/v3/reflection-abstract-factory.md @@ -0,0 +1,166 @@ +# Reflection Factory + +- Since 3.2.0. + +Writing a factory class for each and every service that has dependencies +can be tedious, particularly in early development as you are still sorting +out dependencies. + +laminas-servicemanager ships with `Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory`, +which provides a reflection-based approach to instantiation, resolving +constructor dependencies to the relevant services. The factory may be used as +either an abstract factory, or mapped to specific service names as a factory: + +```php +use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory; + +return [ + /* ... */ + 'service_manager' => [ + 'abstract_factories' => [ + ReflectionBasedAbstractFactory::class, + ], + 'factories' => [ + 'MyModule\Model\FooModel' => ReflectionBasedAbstractFactory::class, + ], + ], + /* ... */ +]; +``` + +Mapping services to the factory is more explicit and performant. + +The factory operates with the following constraints/features: + +- A parameter named `$config` typehinted as an array will receive the + application "config" service (i.e., the merged configuration). +- Parameters typehinted against array, but not named `$config`, will + be injected with an empty array. +- Scalar parameters will result in the factory raising an exception, + unless a default value is present; if it is, that value will be used. +- If a service cannot be found for a given typehint, the factory will + raise an exception detailing this. + +`$options` passed to the factory are ignored in all cases, as we cannot +make assumptions about which argument(s) they might replace. + +Once your dependencies have stabilized, we recommend writing a dedicated +factory, as reflection can introduce performance overhead; you may use the +[generate-factory-for-class console tool](console-tools.md#generate-factory-for-class) +to do so. + +## Handling well-known services + +Some services provided by Laminas components do not have +entries based on their class name (for historical reasons). As examples: + +- `Laminas\Console\Adapter\AdapterInterface` maps to the service name `ConsoleAdapter`, +- `Laminas\Filter\FilterPluginManager` maps to the service name `FilterManager`, +- `Laminas\Hydrator\HydratorPluginManager` maps to the service name `HydratorManager`, +- `Laminas\InputFilter\InputFilterPluginManager` maps to the service name `InputFilterManager`, +- `Laminas\Log\FilterPluginManager` maps to the service name `LogFilterManager`, +- `Laminas\Log\FormatterPluginManager` maps to the service name `LogFormatterManager`, +- `Laminas\Log\ProcessorPluginManager` maps to the service name `LogProcessorManager`, +- `Laminas\Log\WriterPluginManager` maps to the service name `LogWriterManager`, +- `Laminas\Serializer\AdapterPluginManager` maps to the service name `SerializerAdapterManager`, +- `Laminas\Validator\ValidatorPluginManager` maps to the service name `ValidatorManager`, + +To allow the `ReflectionBasedAbstractFactory` to find these, you have two +options. + +The first is to pass an array of mappings via the constructor: + +```php +$reflectionFactory = new ReflectionBasedAbstractFactory([ + \Laminas\Console\Adapter\AdapterInterface::class => 'ConsoleAdapter', + \Laminas\Filter\FilterPluginManager::class => 'FilterManager', + \Laminas\Hydrator\HydratorPluginManager::class => 'HydratorManager', + \Laminas\InputFilter\InputFilterPluginManager::class => 'InputFilterManager', + \Laminas\Log\FilterPluginManager::class => 'LogFilterManager', + \Laminas\Log\FormatterPluginManager::class => 'LogFormatterManager', + \Laminas\Log\ProcessorPluginManager::class => 'LogProcessorManager', + \Laminas\Log\WriterPluginManager::class => 'LogWriterManager', + \Laminas\Serializer\AdapterPluginManager::class => 'SerializerAdapterManager', + \Laminas\Validator\ValidatorPluginManager::class => 'ValidatorManager', +]); +``` + +This can be done either in your configuration file (which could be problematic +when considering serialization for caching), or during an early phase of +application bootstrapping. + +For instance, with laminas-mvc, this might be in your `Application` module's +bootstrap listener: + +```php +namespace Application + +use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory; + +class Module +{ + public function onBootstrap($e) + { + $application = $e->getApplication(); + $container = $application->getServiceManager(); + + $container->addAbstractFactory(new ReflectionBasedAbstractFactory([ + /* ... */ + ])); + } +} +``` + +For Mezzio, it could be part of your `config/container.php` definition: + +```php +$container = new ServiceManager(); +(new Config($config['dependencies']))->configureServiceManager($container); +// Add the following: +$container->addAbstractFactory(new ReflectionBasedAbstractFactory([ + /* ... */ +])); +``` + +The second approach is to extend the class, and define the map in the +`$aliases` property: + +```php +namespace Application; + +use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory; + +class ReflectionAbstractFactory extends ReflectionBasedAbstractFactory +{ + protected $aliases = [ + \Laminas\Console\Adapter\AdapterInterface::class => 'ConsoleAdapter', + \Laminas\Filter\FilterPluginManager::class => 'FilterManager', + \Laminas\Hydrator\HydratorPluginManager::class => 'HydratorManager', + \Laminas\InputFilter\InputFilterPluginManager::class => 'InputFilterManager', + \Laminas\Log\FilterPluginManager::class => 'LogFilterManager', + \Laminas\Log\FormatterPluginManager::class => 'LogFormatterManager', + \Laminas\Log\ProcessorPluginManager::class => 'LogProcessorManager', + \Laminas\Log\WriterPluginManager::class => 'LogWriterManager', + \Laminas\Serializer\AdapterPluginManager::class => 'SerializerAdapterManager', + \Laminas\Validator\ValidatorPluginManager::class => 'ValidatorManager', + ]; +} +``` + +You could then register it via class name in your service configuration. + +## Alternatives + +You may also use the [Config Abstract Factory](config-abstract-factory.md), +which gives slightly more flexibility in terms of mapping dependencies: + +- If you wanted to map to a specific implementation, choose the + `ConfigAbstractFactory`. +- If you need to map to a service that will return a scalar or array (e.g., a + subset of the `'config'` service), choose the `ConfigAbstractFactory`. +- If you need a faster factory for production, choose the + `ConfigAbstractFactory` or create a custom factory. + +## References + +This feature was inspired by [a blog post by Alexandre Lemaire](http://circlical.com/blog/2016/3/9/preparing-for-zend-f). diff --git a/docs/book/v4/ahead-of-time-factories.md b/docs/book/v4/cli-commands/generate-ahead-of-time-factories.md similarity index 94% rename from docs/book/v4/ahead-of-time-factories.md rename to docs/book/v4/cli-commands/generate-ahead-of-time-factories.md index 24fc8ffb..00a228d7 100644 --- a/docs/book/v4/ahead-of-time-factories.md +++ b/docs/book/v4/cli-commands/generate-ahead-of-time-factories.md @@ -1,8 +1,6 @@ # Ahead of Time Factories -- Since 4.0.0 - -In addition to the already existing [Reflection Factory](reflection-abstract-factory.md), one can create factories for those services using `ReflectionBasedAbstractFactory` before deploying the project to production. For the initial project setup regarding CLI tooling, please refer to [this documentation](console-tools.md#requirements). +In addition to the already existing [Reflection Factory](../reflection-abstract-factory.md), one can create factories for those services using `ReflectionBasedAbstractFactory` before deploying the project to production. For the initial project setup regarding CLI tooling, please refer to [this documentation](introduction.md). ## Usage diff --git a/docs/book/v4/cli-commands/generate-dependencies-for-config-factory.md b/docs/book/v4/cli-commands/generate-dependencies-for-config-factory.md new file mode 100644 index 00000000..cc0f5cf8 --- /dev/null +++ b/docs/book/v4/cli-commands/generate-dependencies-for-config-factory.md @@ -0,0 +1,31 @@ +# Generate Dependencies for Config Factory + +```bash +$ ./vendor/bin/laminas servicemanager:generate-deps-for-config-factory -h +Description: + Reads the provided configuration file (creating it if it does not exist), and injects it with ConfigAbstractFactory dependency configuration for the provided class name, writing the changes back to the file. + +Usage: + servicemanager:generate-deps-for-config-factory [options] [--] + +Arguments: + configFile Path to a config file for which to generate configuration. If the file does not exist, it will be created. If it does exist, it must return an array, and the file will be updated with new configuration. + class Name of the class to reflect and for which to generate dependency configuration. + +Options: + -i, --ignore-unresolved Ignore classes with unresolved direct dependencies. + -q, --quiet Do not output any message +``` + +This utility will generate dependency configuration for the named class for use +with the [ConfigAbstractFactory](../config-abstract-factory.md). When doing so, it +will read the named configuration file (creating it if it does not exist), and +merge any configuration it generates with the return values of that file, +writing the changes back to the original file. + +The tool also supports the `-i` or `--ignore-unresolved` flag. +Use these flags when you have typehints to classes that cannot be resolved. +When you omit the flag, such classes will cause the tool to fail with an +exception message. By adding the flag, you can have it continue and produce +configuration. This option is particularly useful when typehints are on +interfaces or resolve to services served by other abstract factories. \ No newline at end of file diff --git a/docs/book/v4/cli-commands/generate-factory-for-class.md b/docs/book/v4/cli-commands/generate-factory-for-class.md new file mode 100644 index 00000000..399cbcce --- /dev/null +++ b/docs/book/v4/cli-commands/generate-factory-for-class.md @@ -0,0 +1,28 @@ +# Generate Factory for Class + +```bash +$ ./vendor/bin/laminas servicemanager:generate-factory-for-class -h +Description: + Generates to STDOUT a factory for creating the specified class; this may then be added to your application, and configured as a factory for the class. + +Usage: + servicemanager:generate-factory-for-class + +Arguments: + className Name of the class to reflect and for which to generate a factory. + +Options: + -q, --quiet Do not output any message +``` + +This utility generates a factory class for the given class, based on the +typehints in its constructor. The factory is emitted to STDOUT, and may be piped +to a file if desired: + +```bash +$ ./vendor/bin/laminas servicemanager:generate-factory-for-class \ +> "Application\\Model\\AlbumModel" > ./module/Application/src/Model/AlbumModelFactory.php +``` + +The class generated implements `Laminas\ServiceManager\Factory\FactoryInterface`, +and is generated within the same namespace as the originating class. \ No newline at end of file diff --git a/docs/book/v4/cli-commands/introduction.md b/docs/book/v4/cli-commands/introduction.md new file mode 100644 index 00000000..2b50f68a --- /dev/null +++ b/docs/book/v4/cli-commands/introduction.md @@ -0,0 +1,24 @@ +# Introduction + +INFO: +Starting in 4.0.0, `laminas-servicemanager` moved the CLI tooling to [`laminas-cli`](https://docs.laminas.dev/laminas-cli/) and provides several commands to be executed. + +> MISSING: **Installation Requirements** +> +> To run the console tools with `laminas-servicemanager`, the `laminas/laminas-cli` component needs to be added to the project dependencies. +> +> ```bash +> $ composer require laminas/laminas-cli +> ``` +> +> _In case laminas-cli is only required to consume these console tools, you might consider using the `--dev` flag._ + +## Available Commands + +- [Generate Dependencies for Config Factory](generate-dependencies-for-config-factory.md) +- [Generate Factory for Class](generate-factory-for-class.md) +- [Generate Ahead of Time Factories](generate-ahead-of-time-factories.md) + +## Learn More + +- [laminas-cli: Writing Custom Commands for laminas-mvc and Mezzio based Applications](https://docs.laminas.dev/laminas-cli/) \ No newline at end of file diff --git a/docs/book/v4/config-abstract-factory.md b/docs/book/v4/config-abstract-factory.md index fd9c2dc4..690815c6 100644 --- a/docs/book/v4/config-abstract-factory.md +++ b/docs/book/v4/config-abstract-factory.md @@ -1,7 +1,5 @@ # Config Abstract Factory -- Since 3.2.0 - You can simplify the process of creating factories by registering `Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory` with your service manager instance. This allows you to define services using a configuration map, @@ -39,7 +37,7 @@ return [ ]; ``` -Like all abstract factories starting in version 3, you may also use the config +Like all abstract factories starting, you may also use the config abstract factory as a mapped factory, registering it as a factory for a specific class: diff --git a/docs/book/v4/configuring-the-service-manager.md b/docs/book/v4/configuring-the-service-manager.md index 6e09e176..63347691 100644 --- a/docs/book/v4/configuring-the-service-manager.md +++ b/docs/book/v4/configuring-the-service-manager.md @@ -129,8 +129,7 @@ $serviceManager = new ServiceManager([ ### Mapping multiple service to the same factory -Unlike version 2 implementations of the component, in the version 3 -implementation, the `$requestedName` is guaranteed to be passed as the second +The `$requestedName` is guaranteed to be passed as the second parameter of a factory. This is useful when you need to create multiple services that are created exactly the same way, hence reducing the number of needed factories. @@ -176,7 +175,7 @@ This pattern can often replace abstract factories, and is more performant: service using the same factory. Using factories is recommended in most cases where abstract factories were used -in version 2. +in older versions of this component. This feature *can* be abused, however: for instance, if you have dozens of services that share the same creation, but which do not share any common diff --git a/docs/book/v4/console-tools.md b/docs/book/v4/console-tools.md deleted file mode 100644 index 8d3b9b85..00000000 --- a/docs/book/v4/console-tools.md +++ /dev/null @@ -1,103 +0,0 @@ -# Console Tools - -Starting in 4.0.0, `laminas-servicemanager` moved the CLI tooling to `laminas-cli` and provides several commands to be executed. - -## Requirements - -To run the console tools with `laminas-servicemanager` v4, the [`laminas/laminas-cli`](https://docs.laminas.dev/laminas-cli/) component needs to be added to the project dependencies. - -> ### Installation -> -> ```shell -> $ composer require laminas/laminas-cli -> ``` -> -> _In case laminas-cli is only required to consume these console tools, you might consider using the `--dev` flag._ - -## Available Commands - -- [Generate Dependencies for Config Factory](#generate-dependencies-for-config-factory) -- [Generate Factory for Class](#generate-factory-for-class) -- [Generate Ahead of Time Factories](#generate-ahead-of-time-factories) - -## Generate Dependencies for Config Factory - -```bash -$ ./vendor/bin/laminas servicemanager:generate-deps-for-config-factory -h -Description: - Reads the provided configuration file (creating it if it does not exist), and injects it with ConfigAbstractFactory dependency configuration for the provided class name, writing the changes back to the file. - -Usage: - servicemanager:generate-deps-for-config-factory [options] [--] - -Arguments: - configFile Path to a config file for which to generate configuration. If the file does not exist, it will be created. If it does exist, it must return an array, and the file will be updated with new configuration. - class Name of the class to reflect and for which to generate dependency configuration. - -Options: - -i, --ignore-unresolved Ignore classes with unresolved direct dependencies. - -q, --quiet Do not output any message -``` - -This utility will generate dependency configuration for the named class for use -with the [ConfigAbstractFactory](config-abstract-factory.md). When doing so, it -will read the named configuration file (creating it if it does not exist), and -merge any configuration it generates with the return values of that file, -writing the changes back to the original file. - -Since 3.2.1, the tool also supports the `-i` or `--ignore-unresolved` flag. -Use these flags when you have typehints to classes that cannot be resolved. -When you omit the flag, such classes will cause the tool to fail with an -exception message. By adding the flag, you can have it continue and produce -configuration. This option is particularly useful when typehints are on -interfaces or resolve to services served by other abstract factories. - -## Generate Factory for Class - -```bash -$ ./vendor/bin/laminas servicemanager:generate-factory-for-class -h -Description: - Generates to STDOUT a factory for creating the specified class; this may then be added to your application, and configured as a factory for the class. - -Usage: - servicemanager:generate-factory-for-class - -Arguments: - className Name of the class to reflect and for which to generate a factory. - -Options: - -q, --quiet Do not output any message -``` - -This utility generates a factory class for the given class, based on the -typehints in its constructor. The factory is emitted to STDOUT, and may be piped -to a file if desired: - -```bash -$ ./vendor/bin/laminas servicemanager:generate-factory-for-class \ -> "Application\\Model\\AlbumModel" > ./module/Application/src/Model/AlbumModelFactory.php -``` - -The class generated implements `Laminas\ServiceManager\Factory\FactoryInterface`, -and is generated within the same namespace as the originating class. - -## Generate Ahead of Time Factories - -```bash -$ vendor/bin/laminas servicemanager:generate-aot-factories -h -Description: - Creates factories which replace the runtime overhead for `ReflectionBasedAbstractFactory`. - -Usage: - servicemanager:generate-aot-factories [] - -Arguments: - localConfigFilename Should be a path targeting a filename which will be created so that the config autoloading will pick it up. Using a `.local.php` suffix should verify that the file is overriding existing configuration. [default: "config/autoload/generated-factories.local.php"] - -Options: - -q, --quiet Do not output any message -``` - -This utility will generate factories in the same way as [servicemanager:generate-factory-for-class](#generate-factory-for-class). The main difference is, that it will scan the whole project configuration for the usage of `ReflectionBasedAbstractFactory` within **any** ServiceManager look-a-like configuration (i.e. explicit usage within `factories`) and auto-generates factories for all of these services **plus** creates a configuration file which overrides **all** ServiceManager look-a-like configurations so that these consume the generated factories. - -For more details and how to set up a project so that all factories are properly replaced, refer to the [dedicated command documentation](ahead-of-time-factories.md). diff --git a/docs/book/v4/cookbook/factories-vs-abstract-factories.md b/docs/book/v4/cookbook/factories-vs-abstract-factories.md index 986747d5..42f284dd 100644 --- a/docs/book/v4/cookbook/factories-vs-abstract-factories.md +++ b/docs/book/v4/cookbook/factories-vs-abstract-factories.md @@ -1,6 +1,6 @@ # When To Use Factories vs Abstract Factories -Starting with version 3, `Laminas\ServiceManager\Factory\AbstractFactoryInterface` +`Laminas\ServiceManager\Factory\AbstractFactoryInterface` extends `Laminas\ServiceManager\Factory\FactoryInterface`, meaning they may be used as either an abstract factory, or mapped to a specific service name as its factory. @@ -19,20 +19,20 @@ Why would you choose one approach over the other? ## Comparisons -Approach | Pros | Cons ----------------- | -------------- | ---- -Abstract factory | One-time setup | Performance; discovery of code responsible for creating instance -Factory | Performance; explicit mapping to factory responsible | Additional (duplicate) setup +| Approach | Pros | Cons | +|------------------|------------------------------------------------------|------------------------------------------------------------------| +| Abstract factory | One-time setup | Performance; discovery of code responsible for creating instance | +| Factory | Performance; explicit mapping to factory responsible | Additional (duplicate) setup | Essentially, it comes down to *convenience* versus *explicitness* and/or *performance*. ## Convenience -Writing a factory per service is time consuming, and, particularly in early +Writing a factory per service is time-consuming, and, particularly in early stages of an application, can distract from the actual business of writing the classes and implementations; in addition, since requirements are often changing -regularly, this boiler-plate code can be a nuisance. +regularly, this boilerplate code can be a nuisance. In such situations, one or more abstract factories — such as the [ConfigAbstractFactory](../config-abstract-factory.md), the @@ -49,14 +49,14 @@ The service manager is optimized to locate *factories*, as it can do an immediate hash table lookup; abstract factories involve: - Looping through each abstract factory - - invoking its method for service location - - if the service is located, using the factory + - invoking its method for service location + - if the service is located, using the factory This means, internally: - a hash table lookup (for the abstract factory) - invocation of 1:N methods for discovery - - which may contain additional lookups and/or retrievals in the container + - which may contain additional lookups and/or retrievals in the container - invocation of a factory method (assuming successful lookup) As such, having an explicit map can aid performance dramatically. @@ -83,7 +83,7 @@ make an appropriate choice. ## Tooling -Starting with 3.2.0, we began offering a variety of [console tools](../console-tools.md) +We are offering a variety of [CLI commands](../cli-commands/introduction.md) to assist you in generating both dependency configuration and factories. Use these to help your code evolve. An expected workflow in your application development evolution is: diff --git a/docs/book/v4/index.html b/docs/book/v4/index.html deleted file mode 100644 index 5c00f74d..00000000 --- a/docs/book/v4/index.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
-

laminas-servicemanager

- -

Factory-Driven Dependency Injection Container

- -
$ composer require laminas/laminas-servicemanager
-
-
- diff --git a/docs/book/v4/index.md b/docs/book/v4/index.md deleted file mode 120000 index 8a33348c..00000000 --- a/docs/book/v4/index.md +++ /dev/null @@ -1 +0,0 @@ -../../../README.md \ No newline at end of file diff --git a/docs/book/v4/migration.md b/docs/book/v4/migration.md deleted file mode 100644 index c3b0fb29..00000000 --- a/docs/book/v4/migration.md +++ /dev/null @@ -1,3 +0,0 @@ -# Migration Guide - -TBD diff --git a/docs/book/v4/migration/v3-to-v4.md b/docs/book/v4/migration/v3-to-v4.md new file mode 100644 index 00000000..1fbd9536 --- /dev/null +++ b/docs/book/v4/migration/v3-to-v4.md @@ -0,0 +1,5 @@ +# Migration from Version 3 to 4 + +INFO: **Work in progress** +We are currently working on the migration guide. +We will update this page as soon as possible! diff --git a/docs/book/v4/plugin-managers.md b/docs/book/v4/plugin-managers.md index 5ded5d69..e47379f9 100644 --- a/docs/book/v4/plugin-managers.md +++ b/docs/book/v4/plugin-managers.md @@ -78,7 +78,7 @@ $pluginManager = $serviceManager->get(ValidatorPluginManager::class); $validator = $pluginManager->get(StringLengthValidator::class); ``` -> Unlike the version 2 implementation, when inside the context of the factory +> When inside the context of the factory > of a service created by a plugin manager, the passed container **will not > be** the plugin manager, but the parent service manager instead. If you need > access to other plugins of the same type, you will need to fetch the plugin diff --git a/docs/book/v4/psr-11.md b/docs/book/v4/psr-11.md index c0bfe98f..5be00603 100644 --- a/docs/book/v4/psr-11.md +++ b/docs/book/v4/psr-11.md @@ -1,55 +1,10 @@ # PSR-11 Support -[container-interop/container-interop 1.2.0](https://github.com/container-interop/container-interop/releases/tag/1.2.0) -modifies its codebase to extend interfaces from [psr/container](https://github.com/php-fig/container) -(the official interfaces for [PSR-11](http://www.php-fig.org/psr/psr-11/)). If -you are on a pre-3.3.0 version of laminas-servicemanager, update your project, and -receive container-interop 1.2, then laminas-servicemanager can already act as a -PSR-11 provider! +## Standard Support -laminas-servicemanager 3.3.0 requires at least version 1.2 of container-interop, -and _also_ requires psr/container 1.0 to explicitly signal that it is a PSR-11 -provider, and to allow removal of the container-interop dependency later. +Version 4.0 of laminas-servicemanager supports version 1.1 and 2 of [PSR-11: Container interface](https://www.php-fig.org/psr/psr-11/), and has update the various factory interfaces and exception implementations to typehint against the PSR-11 interfaces. -Version 4.0 will require only psr/container, and will update the various factory -interfaces and exception implementations to typehint against the PSR-11 -interfaces, which will require changes to any implementations you have. In the -meantime, you can [duck-type](https://en.wikipedia.org/wiki/Duck_typing) the -following factory types: +## Migrating Code to laminas-servicemanager 4.x Compatibility -- `Laminas\ServiceManager\Factory\FactoryInterface`: use a callable with the - following signature: - ```php - function ( - \Psr\Container\ContainerInterface $container, - string $requestedName, - array $options = null - ) - ``` - -- `Laminas\ServiceManager\Factory\DelegatorFactoryInterface`: use a callable with - the following signature: - ```php - function ( - \Psr\Container\ContainerInterface $container, - string $name, - callable $callback, - array $options = null - ) - ``` - -- `Laminas\ServiceManager\Initializer\InitializerInterface`: use a callable with - the following signature: - ```php - function ( - \Psr\Container\ContainerInterface $container, - $instance - ) - ``` - -Abstract factories _can not_ be duck typed, due to the additional `canCreate()` -method. - -You can also leave your factories as-is for now, and update them once -laminas-servicemanager v4.0 is released, at which time we will be providing tooling -to help migrate your factories to PSR-11. +To migrate code to be compatible with laminas-servicemanager 4.x, [laminas-servicemanager-migration](https://docs.laminas.dev/laminas-servicemanager-migration/) can be used. +This package provides a set of rules based on Rector that can be used to migrate code. diff --git a/docs/book/v4/reflection-abstract-factory.md b/docs/book/v4/reflection-abstract-factory.md index cbd9beeb..20af5f5a 100644 --- a/docs/book/v4/reflection-abstract-factory.md +++ b/docs/book/v4/reflection-abstract-factory.md @@ -1,7 +1,5 @@ # Reflection Factory -- Since 3.2.0. - Writing a factory class for each and every service that has dependencies can be tedious, particularly in early development as you are still sorting out dependencies. @@ -28,7 +26,7 @@ return [ ]; ``` -Mapping services to the factory is more explicit and even more performant than in v3.0 due to the [ahead of time factory generation](ahead-of-time-factories.md). +Mapping services to the factory is more explicit and even more performant than in v3.0 due to the [ahead of time factory generation](cli-commands/generate-ahead-of-time-factories.md). The factory operates with the following constraints/features: @@ -49,8 +47,8 @@ factory, as reflection introduces a performance overhead. There are two ways to provide dedicated factories for services consuming `ReflectionBasedAbstractFactory`: -1. Usage of the [generate-factory-for-class console tool](console-tools.md#generate-factory-for-class) (this will also require to manually modify the configuration) -2. Usage of the [generate-aot-factories console tool](console-tools.md#generate-ahead-of-time-factories) which needs an initial project + deployment setup +1. Usage of the [generate-factory-for-class console tool](cli-commands/generate-factory-for-class.md) (this will also require to manually modify the configuration) +2. Usage of the [generate-aot-factories console tool](cli-commands/generate-ahead-of-time-factories.md) which needs an initial project + deployment setup ## Alternatives diff --git a/mkdocs.yml b/mkdocs.yml index 7e405119..44f7f0cb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,24 +1,61 @@ docs_dir: docs/book site_dir: docs/html nav: - - Home: index.md - - 'Quick Start': quick-start.md + - Home: index.md + - v4: + - "Quick Start": v4/quick-start.md + - "PSR-11 Support": v4/psr-11.md + - "Configuring the service manager": v4/configuring-the-service-manager.md + - Delegators: v4/delegators.md + - "Lazy services": v4/lazy-services.md + - "Plugin managers": v4/plugin-managers.md + - "Autowiring & Configuration-based Factories": + - "Reflection-based Factory": v4/reflection-abstract-factory.md + - "Configuration-based Factory": v4/config-abstract-factory.md + - "CLI Commands": + - Introduction: v4/cli-commands/introduction.md + - "Generate Dependencies for Config Factory": v4/cli-commands/generate-dependencies-for-config-factory.md + - "Generate Factory for Class": v4/cli-commands/generate-factory-for-class.md + - "Generate Ahead of Time Factories": v4/cli-commands/generate-ahead-of-time-factories.md + - Cookbook: + - "Factories vs Abstract Factories": v4/cookbook/factories-vs-abstract-factories.md + - "Migration Guide": + - "Migration from Version 3 to 4": v4/migration/v3-to-v4.md + - v3: + - "Quick Start": v3/quick-start.md - Reference: - - 'PSR-11 Support': psr-11.md - - 'Configuring the service manager': configuring-the-service-manager.md - - Delegators: delegators.md - - 'Lazy services': lazy-services.md - - 'Plugin managers': plugin-managers.md - - 'Configuration-based Abstract Factory': config-abstract-factory.md - - 'Reflection-based Abstract Factory': - - 'Usage': reflection-abstract-factory.md - - 'Ahead of Time Factories': ahead-of-time-factories.md - - 'Console Tools': console-tools.md + - "PSR-11 Support": v3/psr-11.md + - "Configuring the service manager": v3/configuring-the-service-manager.md + - Delegators: v3/delegators.md + - "Lazy services": v3/lazy-services.md + - "Plugin managers": v3/plugin-managers.md + - "Configuration-based Abstract Factory": v3/config-abstract-factory.md + - "Reflection-based Abstract Factory": v3/reflection-abstract-factory.md + - "Console Tools": v3/console-tools.md - Cookbook: - - 'Factories vs Abstract Factories': cookbook/factories-vs-abstract-factories.md - - 'Migration Guide': migration.md + - "Factories vs Abstract Factories": v3/cookbook/factories-vs-abstract-factories.md + - "Migration Guide": v3/migration.md site_name: laminas-servicemanager -site_description: 'laminas-servicemanager: factory-driven dependency injection container' -repo_url: 'https://github.com/laminas/laminas-servicemanager' +site_description: "laminas-servicemanager: factory-driven dependency injection container" +repo_url: "https://github.com/laminas/laminas-servicemanager" extra: project: Components + current_version: v4 + versions: + - v4 + - v3 +plugins: + - search + - redirects: + redirect_maps: + quick-start.md: v4/quick-start.md + psr-11.md: v4/psr-11.md + configuring-the-service-manager.md: v4/configuring-the-service-manager.md + delegators.md: v4/delegators.md + lazy-services.md: v4/lazy-services.md + plugin-managers.md: v4/plugin-managers.md + config-abstract-factory.md: v4/config-abstract-factory.md + reflection-abstract-factory.md: v4/reflection-abstract-factory.md + cookbook/factories-vs-abstract-factories.md: v4/cookbook/factories-vs-abstract-factories.md + migration.md: v4/migration/v3-to-v4.md + console-tools.md: v3/console-tools.md