Skip to content
This repository was archived by the owner on Feb 6, 2020. It is now read-only.

Commit 1fe896e

Browse files
committed
Merge branch 'weierophinney-feature/mutability' into develop
2 parents 5ae2199 + b253d70 commit 1fe896e

10 files changed

+573
-158
lines changed

CHANGELOG.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ All notable changes to this project will be documented in this file, in reverse
6666
Of course, you can still override the `validate` method if your logic is more
6767
complex.
6868

69+
- A new method, `configure()`, was added, allowing full configuration of the
70+
`ServiceManager` instance at once. Each of the various configuration methods —
71+
`setAlias()`, `setInvokableClass()`, etc. — now proxy to this method.
72+
73+
- A new method, `mapLazyService($name, $class = null)`, was added, to allow
74+
mapping a lazy service, and as an analog to the other various service
75+
definition methods.
76+
6977
### Deprecated
7078

7179
- Nothing
@@ -114,20 +122,12 @@ changes, outlined in this section.
114122
`Config` and `ConfigInterface` still exist, however, but primarily for the
115123
purposes of codifying and aggregating configuration to use.
116124

117-
- The ServiceManager is now immutable. Once configured, it cannot be altered.
118-
You need to create a new service manager if you need to change the
119-
configuration. This ensures safer and more aggressive caching. A new method,
120-
`withConfig()`, allows you to create a new instance that merges the provided
121-
configuration.
122-
123125
- `ConfigInterface` has two important changes:
124-
- `configureServiceManager()` now **must** return a service manager instance.
125-
Since the ServiceManager is now immutable, and the various methods for
126-
injecting services are gone, the expectation is that this method will pass
127-
configuration to `ServiceManager::withConfig()` and return the new instance.
126+
- `configureServiceManager()` now **must** return the updated service manager
127+
instance.
128128
- A new method, `toArray()`, was added, to allow pulling the configuration in
129129
order to pass to a ServiceManager or plugin manager's constructor or
130-
`withConfig()` method.
130+
`configure()` method.
131131

132132
- Interfaces for `FactoryInterface`, `DelegatorFactoryInterface` and
133133
`AbstractFactoryInterface` have changed. All are now directly invokable. This

doc/book/configuring-the-service-manager.md

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -484,14 +484,31 @@ This works because the `InvokableFactory` will automatically pass the options
484484

485485
## Altering a service manager's config
486486

487-
The service manager is immutable. This means that once you have created a
488-
service manager instance, you cannot alter its configuration. This is done for
489-
performance reasons, as it allows us to more aggressively cache.
490-
491-
However, you may need to alter its configuration at runtime in order to add new
492-
factories. To do that, you can use the `withConfig()` method. This method creates a
493-
*new* service manager instance, which merges its own configuration with the new
494-
configuration you provide to it:
487+
Assuming that you have not called `$container->setAllowOverride(false)`, you can,
488+
at any time, configure the service manager with new services using any of the
489+
following methods:
490+
491+
- `configure()`, which accepts the same configuration array as the constructor.
492+
- `setAlias($alias, $target)`
493+
- `setInvokableClass($name, $class = null)`; if no `$class` is passed, the
494+
assumption is that `$name` is the class name.
495+
- `setFactory($name, $factory)`, where `$factory` can be either a callable
496+
factory or the name of a factory class to use.
497+
- `mapLazyService($name, $class = null)`, to map the service name `$name` to
498+
`$class`; if the latter is not provided, `$name` is used for both sides of
499+
the map.
500+
- `addAbstractFactory($factory)`, where `$factory` can be either a
501+
`Zend\ServiceManager\Factory\AbstractFactoryInterface` instance or the name
502+
of a class implementing the interface.
503+
- `addDelegator($name, $factory)`, where `$factory` can be either a callable
504+
delegator factory, or the name of a delegator factory class to use.
505+
- `addInitializer($initializer)`, where `$initializer` can be either a callable
506+
initializer, or the name of an initializer class to use.
507+
- `setService($name, $instance)`
508+
- `setShared($name, $shared)`, where `$shared` is a boolean flag indicating
509+
whether or not the named service should be shared.
510+
511+
As examples:
495512

496513
```php
497514
use Zend\ServiceManager\ServiceManager;
@@ -502,18 +519,47 @@ $serviceManager = new ServiceManager([
502519
]
503520
]);
504521

505-
$newServiceManager = $serviceManager->withConfig([
522+
$serviceManager->configure([
506523
'factories' => [
507524
DateTime::class => InvokableFactory::class
508525
]
509526
]);
510527

511-
var_dump($serviceManager->has(DateTime::class)); // prints false
512528
var_dump($newServiceManager->has(DateTime::class)); // prints true
513-
```
514529

515-
As you can see from this example, the old service manager has been untouched,
516-
and therefor does not contain any factory set for the `DateTime` service.
530+
// Create an alias from 'Date' to 'DateTime'
531+
$serviceManager->setAlias('Date', DateTime::class);
532+
533+
// Set a factory for the 'Time' service
534+
$serviceManager->setFactory('Time', function ($container) {
535+
return $container->get(DateTime::class);
536+
});
537+
538+
// Map a lazy service named 'localtime' to the class DateTime.
539+
$serviceManager->mapLazyService('localtime', DateTime::class);
540+
541+
// Add an abstract factory
542+
$serviceManager->addAbstractFactory(new CustomAbstractFactory());
517543

518-
> When creating a new service manager through the `withConfig` method, **no
519-
> services** created with the old service manager are cloned.
544+
// Add a delegator factory for the DateTime service
545+
$serviceManager->addDelegator(DateTime::class, function ($container, $name, $callback) {
546+
$dateTime = $callback();
547+
$dataTime->setTimezone(new DateTimezone('UTC'));
548+
return $dateTime;
549+
});
550+
551+
// Add an initializer
552+
// Note: don't do this. Use delegator factories instead.
553+
$serviceManager->addInitializer(function ($service, $instance) {
554+
if (! $instance instanceof DateTime) {
555+
return;
556+
}
557+
$instance->setTimezone(new DateTimezone('America/Chicago'));
558+
})
559+
560+
// Explicitly map a service name to an instance.
561+
$serviceManager->setService('foo', new stdClass);
562+
563+
// Mark the DateTime service as NOT being shared.
564+
$serviceManager->setShared(DateTime::class, false);
565+
```

doc/book/lazy-services.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ in the `lazy_services` key passed in the service manager configuration:
122122
[
123123
// map of service names and their relative class names - this
124124
// is required since the service manager cannot know the
125-
// class name of defined services upfront
125+
// class name of defined services up front
126126
'class_map' => [
127127
// 'foo' => 'MyApplication\Foo',
128128
],
@@ -137,3 +137,10 @@ in the `lazy_services` key passed in the service manager configuration:
137137
'write_proxy_files' => false,
138138
];
139139
```
140+
141+
After you have an instance, you can map lazy service/class pairs using
142+
`mapLazyService()`:
143+
144+
```php
145+
$container->mapLazyService('foo', \MyApplication\Foo::class);
146+
```

doc/book/migration.md

Lines changed: 19 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,16 @@ return [
311311
];
312312
```
313313

314+
Additionally, assuming you have configured lazy services initially with the
315+
proxy namespace, target directory, etc., you can map lazy services using the new
316+
method `mapLazyService($name, $class)`:
317+
318+
```php
319+
$container->mapLazyService('MyClass', 'MyClass');
320+
// or, more simply:
321+
$container->mapLazyService('MyClass');
322+
```
323+
314324
## ServiceLocatorInterface Changes
315325

316326
The `ServiceLocatorInterface` now extends the
@@ -332,70 +342,32 @@ service, and to allow using the provided `$options` when creating the instance.
332342
`Zend\ServiceManager\ServiceManager` remains the primary interface with which
333343
developers will interact. It has the following changes in v3:
334344

335-
- It is now immutable.
336-
- It accepts all configuration at instantiation.
337-
- It removes all methods that would alter state.
338-
- It adds a new method, `withConfig()`, for generating a new instance that
339-
merges the provided configuration with previous definitions.
345+
- It adds a new method, `configure()`, which allows configuring all instance
346+
generation capabilities (aliases, factories, abstract factories, etc.) at
347+
once.
340348
- Peering capabilities were removed.
341349
- Exceptions are *always* thrown when service instance creation fails or
342350
produces an error; you can no longer disable this.
343-
- Configuration no longer requires a `Zend\ServiceManager\Config` instance; that
344-
class has been removed.
351+
- Configuration no longer requires a `Zend\ServiceManager\Config` instance.
352+
`Config` can be used, but is no needed.
345353
- It adds a new method, `build()`, for creating discrete service instances.
346354

347355
### Methods Removed
348356

349357
*The following methods are removed* in v3:
350358

351-
- `setAllowOverride()`/`getAllowOverride()`; since instances are now immutable,
352-
these no longer had any meaning.
353359
- `setShareByDefault()`/`shareByDefault()`; this can be passed during
354-
instantiation or via `withConfig()`.
360+
instantiation or via `configure()`.
355361
- `setThrowExceptionInCreate()`/`getThrowExceptionInCreate()`; exceptions are
356362
*always* thrown when errors are encountered during service instance creation.
357363
- `setRetrieveFromPeeringManagerFirst()`/`retrieveFromPeeringManagerFirst()`;
358364
peering is no longer supported.
359-
- `setInvokableClass()`; invokable classes are no longer supported separately,
360-
regardless.
361-
- `setFactory()`; pass factories during instantiation or via `withConfig()`.
362-
- `addAbstractFactory()`; provide abstract factories during instantiation or via
363-
`withConfig()`.
364-
- `addDelegator()`; provide delegator factories during instantiation or via
365-
`withConfig()`.
366-
- `addInitializer()`; pass initializers during instantiation or via
367-
`withConfig()`.
368-
- `setService()`; provide concrete service instances during instantiation or via
369-
`withConfig()`.
370-
- `setShared()`/`isShared()`; provide per-service sharing status at
371-
instantiation or via `withConfig()`.
372365

373366
### Constructor
374367

375368
The constructor now accepts an array of service configuration, not a
376369
`Zend\ServiceManager\Config` instance.
377370

378-
### Immutability
379-
380-
The Service Manager is now immutable. This allows us to perform aggressive
381-
caching, and prevents the need to check for state changes when new services are
382-
added.
383-
384-
*Typically, you should pass all service configuration at instantiation.*
385-
386-
If you need to change a Service Manager instance — for instance, to add more
387-
factories, delegators, etc. — the class provides a new method, `withConfig()`.
388-
This method will merge the configuration you provide with that found in the
389-
Service Manager instance in order to return a *new instance*:
390-
391-
```php
392-
$updated = $container->withConfig([
393-
'factories' => [
394-
'MyClass' => InvokableFactory::class,
395-
],
396-
]);
397-
```
398-
399371
### Use `build()` for discrete instances
400372

401373
The new method `build()` acts as a factory method for configured services, and
@@ -758,10 +730,8 @@ The following classes and interfaces have changes:
758730
- `Zend\ServiceManager\Proxy\LazyServiceFactory` is now marked `final`, and
759731
implements `Zend\ServiceManager\Proxy\DelegatorFactoryInterface`. Its
760732
dependencies and capabilities remain the same.
761-
- `Zend\ServiceManager\ConfigInterface` now is expected to *return* a
762-
`ServiceManager` instance. This is because instances cannot be configured
763-
in-situ; implementers will now need to call `withConfig()` and return the
764-
instance returned by that method.
733+
- `Zend\ServiceManager\ConfigInterface` now is expected to *return* the modified
734+
`ServiceManager` instance.
765735
- `Zend\ServiceManager\Config` was updated to follow the changes to
766-
`ConfigInterface` and `ServiceManager`, and now returns a new
736+
`ConfigInterface` and `ServiceManager`, and now returns the updated
767737
`ServiceManager` instance from `configureServiceManager()`.

src/Config.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ public function __construct(array $config = [])
6666
* Configure service manager
6767
*
6868
* @param ServiceManager $serviceManager
69-
* @return ServiceManager Returns a new instance with the merged configuration.
69+
* @return ServiceManager Returns the updated service manager instance.
7070
*/
7171
public function configureServiceManager(ServiceManager $serviceManager)
7272
{
73-
return $serviceManager->withConfig($this->config);
73+
return $serviceManager->configure($this->config);
7474
}
7575

7676
/**
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
/**
3+
* Zend Framework (http://framework.zend.com/)
4+
*
5+
* @link http://github.com/zendframework/zf2 for the canonical source repository
6+
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license http://framework.zend.com/license/new-bsd New BSD License
8+
*/
9+
10+
namespace Zend\ServiceManager\Exception;
11+
12+
use DomainException;
13+
14+
/**
15+
* @inheritDoc
16+
*/
17+
class ContainerModificationsNotAllowedException extends DomainException implements ExceptionInterface
18+
{
19+
}

0 commit comments

Comments
 (0)