-
-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #232 from laminas/4.0.x-merge-up-into-4.1.x_ejG6mt8F
Merge release 4.0.1 into 4.1.x
- Loading branch information
Showing
24 changed files
with
577 additions
and
225 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# This Is Only a Placeholder | ||
|
||
The content of this page is automatically generated. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
> // ... | ||
> } | ||
> ``` |
Oops, something went wrong.