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

Commit aa5a09f

Browse files
committed
Merge branch 'hotfix/205-creation-options' into release-2.7
Close #205
2 parents cbd26db + 8ec53ca commit aa5a09f

File tree

5 files changed

+149
-9
lines changed

5 files changed

+149
-9
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ before_install:
3535
- if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then composer require --dev --no-update satooshi/php-coveralls ; fi
3636

3737
install:
38-
- COMPOSER_ROOT_VERSION=2.7.99 travis_retry composer install --no-interaction --ignore-platform-reqs
38+
- COMPOSER_ROOT_VERSION=2.7.99 travis_retry composer install --no-interaction
3939

4040
script:
4141
- if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/phpunit --coverage-clover clover.xml ; fi

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
All notable changes to this project will be documented in this file, in reverse chronological order by release.
44

5-
## 2.7.9 - TBD
5+
## 2.7.9 - 2017-11-27
66

77
### Added
88

@@ -18,7 +18,12 @@ All notable changes to this project will be documented in this file, in reverse
1818

1919
### Fixed
2020

21-
- Nothing.
21+
- [#205](https://github.com/zendframework/zend-servicemanager/pull/205) fixes
22+
how the `AbstractPluginManager` handles repeated retrievals of the same
23+
service when instance options are provided and the service is marked as
24+
"shared". Previously, it incorrectly would return the first instance
25+
retrieved; with this release, no instance created with instance options is
26+
ever shared.
2227

2328
## 2.7.8 - 2016-12-19
2429

src/AbstractPluginManager.php

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ abstract public function validatePlugin($plugin);
147147
public function get($name, $options = [], $usePeeringServiceManagers = true)
148148
{
149149
$isAutoInvokable = false;
150+
$cName = null;
151+
$sharedInstance = null;
150152

151153
// Allow specifying a class name directly; registers as an invokable class
152154
if (!$this->has($name) && $this->autoAddInvokableClass && class_exists($name)) {
@@ -157,22 +159,58 @@ public function get($name, $options = [], $usePeeringServiceManagers = true)
157159

158160
$this->creationOptions = $options;
159161

162+
// If creation options were provided, we want to force creation of a
163+
// new instance.
164+
if (! empty($this->creationOptions)) {
165+
$cName = isset($this->canonicalNames[$name])
166+
? $this->canonicalNames[$name]
167+
: $this->canonicalizeName($name);
168+
169+
if (isset($this->instances[$cName])) {
170+
$sharedInstance = $this->instances[$cName];
171+
unset($this->instances[$cName]);
172+
}
173+
}
174+
160175
try {
161176
$instance = parent::get($name, $usePeeringServiceManagers);
162177
} catch (Exception\ServiceNotFoundException $exception) {
178+
if ($sharedInstance) {
179+
$this->instances[$cName] = $sharedInstance;
180+
}
181+
$this->creationOptions = null;
163182
$this->tryThrowingServiceLocatorUsageException($name, $isAutoInvokable, $exception);
164183
} catch (Exception\ServiceNotCreatedException $exception) {
184+
if ($sharedInstance) {
185+
$this->instances[$cName] = $sharedInstance;
186+
}
187+
$this->creationOptions = null;
165188
$this->tryThrowingServiceLocatorUsageException($name, $isAutoInvokable, $exception);
166189
}
167190

168191
$this->creationOptions = null;
169192

193+
// If we had a previously shared instance, restore it.
194+
if ($sharedInstance) {
195+
$this->instances[$cName] = $sharedInstance;
196+
}
197+
170198
try {
171199
$this->validatePlugin($instance);
172200
} catch (Exception\RuntimeException $exception) {
173201
$this->tryThrowingServiceLocatorUsageException($name, $isAutoInvokable, $exception);
174202
}
175203

204+
// If we created a new instance using creation options, and it was
205+
// marked to share, we remove the shared instance
206+
// (options === cannot share)
207+
if ($cName
208+
&& isset($this->instances[$cName])
209+
&& $this->instances[$cName] === $instance
210+
) {
211+
unset($this->instances[$cName]);
212+
}
213+
176214
return $instance;
177215
}
178216

@@ -321,10 +359,8 @@ protected function createServiceViaCallback($callable, $cName, $rName)
321359
// duck-type MutableCreationOptionsInterface for forward compatibility
322360
if (isset($factory)
323361
&& method_exists($factory, 'setCreationOptions')
324-
&& is_array($this->creationOptions)
325-
&& !empty($this->creationOptions)
326362
) {
327-
$factory->setCreationOptions($this->creationOptions);
363+
$factory->setCreationOptions(is_array($this->creationOptions) ? $this->creationOptions : []);
328364
} elseif ($factory instanceof Factory\InvokableFactory) {
329365
$factory->setCreationOptions(null);
330366
}

test/AbstractPluginManagerTest.php

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Zend\ServiceManager\Factory\InvokableFactory;
2020
use Zend\ServiceManager\ServiceManager;
2121
use ZendTest\ServiceManager\TestAsset\Baz;
22+
use ZendTest\ServiceManager\TestAsset\FactoryUsingCreationOptions;
2223
use ZendTest\ServiceManager\TestAsset\FooPluginManager;
2324
use ZendTest\ServiceManager\TestAsset\InvokableObject;
2425
use ZendTest\ServiceManager\TestAsset\MockSelfReturningDelegatorFactory;
@@ -110,8 +111,9 @@ public function testMutableMethodNeverCalledWithoutCreationOptions()
110111
{
111112
$mock = 'ZendTest\ServiceManager\TestAsset\CallableWithMutableCreationOptions';
112113
$callable = $this->getMock($mock, ['setCreationOptions']);
113-
$callable->expects($this->never())
114-
->method('setCreationOptions');
114+
$callable->expects($this->once())
115+
->method('setCreationOptions')
116+
->with([]);
115117

116118
$ref = new ReflectionObject($this->pluginManager);
117119

@@ -164,7 +166,70 @@ public function testInvokableFactoryNoOptionsResetsCreationOptions()
164166
$plugin2 = $pluginManager->get(Baz::class);
165167

166168
$this->assertSame($creationOptions, $plugin1->options);
167-
$this->assertNull($plugin2->options);
169+
$this->assertEmpty($plugin2->options);
170+
}
171+
172+
/**
173+
* @group 205
174+
*/
175+
public function testRetrievingServicesViaFactoryThatUsesCreationOptionsShouldNotRememberServiceOnSubsequentRetrievals()
176+
{
177+
/** @var $pluginManager AbstractPluginManager */
178+
$pluginManager = $this->getMockForAbstractClass('Zend\ServiceManager\AbstractPluginManager');
179+
$pluginManager->setFactory(Baz::class, FactoryUsingCreationOptions::class);
180+
$pluginManager->setShared(Baz::class, false);
181+
$creationOptions = ['key1' => 'value1'];
182+
$plugin1 = $pluginManager->get(Baz::class, $creationOptions);
183+
$plugin2 = $pluginManager->get(Baz::class);
184+
185+
$this->assertNotSame($plugin1, $plugin2);
186+
187+
$this->assertSame($creationOptions, $plugin1->options);
188+
$this->assertEmpty($plugin2->options);
189+
}
190+
191+
/**
192+
* @group 205
193+
*/
194+
public function testRetrievingServicesViaFactoryThatUsesCreationOptionsShouldReturnNewInstanceIfOptionsAreProvided()
195+
{
196+
/** @var $pluginManager AbstractPluginManager */
197+
$pluginManager = $this->getMockForAbstractClass('Zend\ServiceManager\AbstractPluginManager');
198+
$pluginManager->setFactory(Baz::class, FactoryUsingCreationOptions::class);
199+
$pluginManager->setShared(Baz::class, false);
200+
$creationOptions = ['key1' => 'value1'];
201+
$plugin1 = $pluginManager->get(Baz::class);
202+
$plugin2 = $pluginManager->get(Baz::class, $creationOptions);
203+
204+
$this->assertNotSame($plugin1, $plugin2);
205+
206+
$this->assertEmpty($plugin1->options);
207+
$this->assertSame($creationOptions, $plugin2->options);
208+
}
209+
210+
/**
211+
* @group 205
212+
*/
213+
// @codingStandardsIgnoreStart
214+
public function testRetrievingServicesViaFactoryThatUsesCreationOptionsShouldReturnNewInstanceEveryTimeOptionsAreProvided()
215+
{
216+
// @codingStandardsIgnoreEnd
217+
/** @var $pluginManager AbstractPluginManager */
218+
$pluginManager = $this->getMockForAbstractClass('Zend\ServiceManager\AbstractPluginManager');
219+
$pluginManager->setFactory(Baz::class, FactoryUsingCreationOptions::class);
220+
$pluginManager->setShared(Baz::class, false);
221+
$creationOptions = ['key1' => 'value1'];
222+
$plugin1 = $pluginManager->get(Baz::class, $creationOptions);
223+
$plugin2 = $pluginManager->get(Baz::class);
224+
$plugin3 = $pluginManager->get(Baz::class, $creationOptions);
225+
226+
$this->assertNotSame($plugin1, $plugin2);
227+
$this->assertNotSame($plugin1, $plugin3);
228+
$this->assertNotSame($plugin2, $plugin3);
229+
230+
$this->assertSame($creationOptions, $plugin1->options);
231+
$this->assertEmpty($plugin2->options);
232+
$this->assertSame($creationOptions, $plugin3->options);
168233
}
169234

170235
public function testValidatePluginIsCalledWithDelegatorFactoryIfItsAService()
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-servicemanager for the canonical source repository
4+
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-servicemanager/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
namespace ZendTest\ServiceManager\TestAsset;
9+
10+
use Zend\ServiceManager\FactoryInterface;
11+
use Zend\ServiceManager\ServiceLocatorInterface;
12+
13+
class FactoryUsingCreationOptions implements FactoryInterface
14+
{
15+
/**
16+
* @var array
17+
*/
18+
private $creationOptions;
19+
20+
public function createService(ServiceLocatorInterface $serviceLocator)
21+
{
22+
return new Baz($this->creationOptions);
23+
}
24+
25+
/**
26+
* @param array $creationOptions
27+
*
28+
* @return void
29+
*/
30+
public function setCreationOptions(array $creationOptions)
31+
{
32+
$this->creationOptions = $creationOptions;
33+
}
34+
}

0 commit comments

Comments
 (0)