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

Commit 85488fb

Browse files
committed
Merge branch 'hotfix/93'
Close #93
2 parents c3fb148 + 569cf35 commit 85488fb

File tree

4 files changed

+230
-3
lines changed

4 files changed

+230
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ All notable changes to this project will be documented in this file, in reverse
1313
- [#95](https://github.com/zendframework/zend-servicemanager/pull/95) adds
1414
GitHub Pages publication automation, and moves the documentation to
1515
https://zendframework.github.io/zend-servicemanager/
16+
- [#93](https://github.com/zendframework/zend-servicemanager/pull/93) adds
17+
`Zend\ServiceManager\Test\CommonPluginManagerTrait`, which can be used to
18+
validate that a plugin manager instance is ready for version 3.
1619

1720
### Deprecated
1821

doc/book/migration.md

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,8 @@ class ObserverPluginManager extends AbstractPluginManager
11371137
'log' => LogObserver::class,
11381138
];
11391139

1140+
protected $shareByDefault = false;
1141+
11401142
public function validatePlugin($instance)
11411143
{
11421144
if (! $plugin instanceof ObserverInterface) {
@@ -1156,12 +1158,16 @@ To prepare this for version 3, we need to do the following:
11561158
`factories` and `aliases`.
11571159
- We need to implement a `validate()` method.
11581160
- We need to update the `validatePlugin()` method to proxy to `validate()`.
1161+
- We need to add a `$sharedByDefault` property (if `$shareByDefault` is present).
11591162

11601163
Doing so, we get the following result:
11611164

11621165
```php
1166+
namespace MyNamespace;
1167+
11631168
use RuntimeException;
11641169
use Zend\ServiceManager\AbstractPluginManager;
1170+
use Zend\ServiceManager\Exception\InvalidServiceException;
11651171
use Zend\ServiceManager\Factory\InvokableFactory;
11661172

11671173
class ObserverPluginManager extends AbstractPluginManager
@@ -1176,14 +1182,21 @@ class ObserverPluginManager extends AbstractPluginManager
11761182
];
11771183

11781184
protected $factories = [
1179-
MailObserver::class => InvokableFactory::class,
1185+
MailObserver::class => InvokableFactory::class,
11801186
LogObserver::class => InvokableFactory::class,
1187+
// Legacy (v2) due to alias resolution
1188+
'mynamespacemailobserver' => InvokableFactory::class,
1189+
'mynamespacelogobserver' => InvokableFactory::class,
11811190
];
11821191

1192+
protected $shareByDefault = false;
1193+
1194+
protected $sharedByDefault = false;
1195+
11831196
public function validate($instance)
11841197
{
11851198
if (! $plugin instanceof $this->instanceOf) {
1186-
throw new RuntimeException(sprintf(
1199+
throw new InvalidServiceException(sprintf(
11871200
'Invalid plugin "%s" created; not an instance of %s',
11881201
get_class($instance),
11891202
$this->instanceOf
@@ -1193,7 +1206,11 @@ class ObserverPluginManager extends AbstractPluginManager
11931206

11941207
public function validatePlugin($instance)
11951208
{
1196-
$this->validate($instance);
1209+
try {
1210+
$this->validate($instance);
1211+
} catch (InvalidServiceException $e) {
1212+
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
1213+
}
11971214
}
11981215
}
11991216
```
@@ -1209,16 +1226,68 @@ Things to note about the above:
12091226
- The aliases point to the fully qualified class name (FQCN) for the service
12101227
being generated, and these are mapped to `InvokableFactory` instances. This
12111228
means you can also fetch your plugins by their FQCN.
1229+
- There are also factory entries for the canonicalized FQCN of each factory,
1230+
which will be used in v2. (Canonicalization in v2 strips non-alphanumeric
1231+
characters, and casts to lowercase.)
1232+
- `validatePlugin()` continues to throw the old exception
12121233

12131234
The above will now work in both version 2 and version 3.
12141235

1236+
### Migration testing
1237+
1238+
To test your changes, create a new `MigrationTest` case that uses
1239+
`Zend\ServiceManager\Test\CommonPluginManagerTrait`. Override
1240+
`getPluginManager()` to return an instance of your plugin manager, and override
1241+
`getV2InvalidPluginException()` to return the classname of the exception your
1242+
`validatePlugin()` method throws:
1243+
1244+
```php
1245+
use MyNamespace\ObserverInterface;
1246+
use MyNamespace\ObserverPluginManager;
1247+
use MyNamespace\Exception\RuntimeException;
1248+
use PHPUnit_Framework_TestCase as TestCase;
1249+
use Zend\ServiceManager\ServiceManager;
1250+
use Zend\ServiceManager\Test\CommonPluginManagerTrait;
1251+
1252+
class MigrationTest extends TestCase
1253+
{
1254+
use CommonPluginManagerTrait;
1255+
1256+
protected function getPluginManager()
1257+
{
1258+
return new ObserverPluginManager(new ServiceManager());
1259+
}
1260+
1261+
protected function getV2InvalidPluginException()
1262+
{
1263+
return RuntimeException::class;
1264+
}
1265+
1266+
protected function getInstanceOf()
1267+
{
1268+
return ObserverInterface::class;
1269+
}
1270+
}
1271+
```
1272+
1273+
This will check that:
1274+
1275+
- You have set the `$instanceOf` property.
1276+
- `$shareByDefault` and `$sharedByDefault` match, if present.
1277+
- That requesting an invalid plugin throws the right exception.
1278+
- That all your aliases resolve.
1279+
1280+
1281+
### Post migration
1282+
12151283
After you migrate to version 3, you can clean up your plugin manager:
12161284

12171285
- Remove the `validatePlugin()` method.
12181286
- If your `validate()` routine is only checking that the instance is of a single
12191287
type, and has no other logic, you can remove that implementation as well, as
12201288
the `AbstractPluginManager` already takes care of that when `$instanceOf` is
12211289
defined!
1290+
- Remove the canonicalized FQCN entry for each factory
12221291

12231292
Performing these steps on the above, we get:
12241293

src/Test/CommonPluginManagerTrait.php

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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) 2016 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\Test;
11+
12+
use ReflectionClass;
13+
use ReflectionProperty;
14+
use Zend\ServiceManager\Exception\InvalidServiceException;
15+
16+
/**
17+
* Trait for testing plugin managers for v2-v3 compatibility
18+
*
19+
* To use this trait:
20+
* * implement the `getPluginManager()` method to return your plugin manager
21+
* * implement the `getV2InvalidPluginException()` method to return the class `validatePlugin()` throws under v2
22+
*/
23+
trait CommonPluginManagerTrait
24+
{
25+
public function testInstanceOfMatches()
26+
{
27+
$manager = $this->getPluginManager();
28+
$reflection = new ReflectionProperty($manager, 'instanceOf');
29+
$reflection->setAccessible(true);
30+
$this->assertEquals($this->getInstanceOf(), $reflection->getValue($manager), 'instanceOf does not match');
31+
}
32+
33+
public function testShareByDefaultAndSharedByDefault()
34+
{
35+
$manager = $this->getPluginManager();
36+
$reflection = new ReflectionClass($manager);
37+
$shareByDefault = $sharedByDefault = true;
38+
39+
foreach ($reflection->getProperties() as $prop) {
40+
if ($prop->getName() == 'shareByDefault') {
41+
$prop->setAccessible(true);
42+
$shareByDefault = $prop->getValue($manager);
43+
}
44+
if ($prop->getName() == 'sharedByDefault') {
45+
$prop->setAccessible(true);
46+
$sharedByDefault = $prop->getValue($manager);
47+
}
48+
}
49+
50+
$this->assertTrue(
51+
$shareByDefault == $sharedByDefault,
52+
'Values of shareByDefault and sharedByDefault do not match'
53+
);
54+
}
55+
56+
public function testRegisteringInvalidElementRaisesException()
57+
{
58+
$this->setExpectedException($this->getServiceNotFoundException());
59+
$this->getPluginManager()->setService('test', $this);
60+
}
61+
62+
public function testLoadingInvalidElementRaisesException()
63+
{
64+
$manager = $this->getPluginManager();
65+
$manager->setInvokableClass('test', get_class($this));
66+
$this->setExpectedException($this->getServiceNotFoundException());
67+
$manager->get('test');
68+
}
69+
70+
/**
71+
* @dataProvider aliasProvider
72+
*/
73+
public function testPluginAliasesResolve($alias, $expected)
74+
{
75+
$this->assertInstanceOf($expected, $this->getPluginManager()->get($alias), "Alias '$alias' does not resolve'");
76+
}
77+
78+
public function aliasProvider()
79+
{
80+
$manager = $this->getPluginManager();
81+
$reflection = new ReflectionProperty($manager, 'aliases');
82+
$reflection->setAccessible(true);
83+
$data = [];
84+
foreach ($reflection->getValue($manager) as $alias => $expected) {
85+
$data[] = [$alias, $expected];
86+
}
87+
return $data;
88+
}
89+
90+
protected function getServiceNotFoundException()
91+
{
92+
$manager = $this->getPluginManager();
93+
if (method_exists($manager, 'configure')) {
94+
return InvalidServiceException::class;
95+
}
96+
return $this->getV2InvalidPluginException();
97+
}
98+
99+
/**
100+
* Returns the plugin manager to test
101+
* @return \Zend\ServiceManager\AbstractPluginManager
102+
*/
103+
abstract protected function getPluginManager();
104+
105+
/**
106+
* Returns the FQCN of the exception thrown under v2 by `validatePlugin()`
107+
* @return mixed
108+
*/
109+
abstract protected function getV2InvalidPluginException();
110+
111+
/**
112+
* Returns the value the instanceOf property has been set to
113+
* @return string
114+
*/
115+
abstract protected function getInstanceOf();
116+
}

test/ExamplePluginManagerTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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) 2016 Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license http://framework.zend.com/license/new-bsd New BSD License
8+
*/
9+
10+
namespace ZendTest\ServiceManager;
11+
12+
use PHPUnit_Framework_TestCase as TestCase;
13+
use Zend\ServiceManager\ServiceManager;
14+
use Zend\ServiceManager\Test\CommonPluginManagerTrait;
15+
use ZendTest\ServiceManager\TestAsset\InvokableObject;
16+
use ZendTest\ServiceManager\TestAsset\V2v3PluginManager;
17+
18+
/**
19+
* Example test of using CommonPluginManagerTrait
20+
*/
21+
class ExamplePluginManagerTest extends TestCase
22+
{
23+
use CommonPluginManagerTrait;
24+
25+
protected function getPluginManager()
26+
{
27+
return new V2v3PluginManager(new ServiceManager());
28+
}
29+
30+
protected function getV2InvalidPluginException()
31+
{
32+
return \RuntimeException::class;
33+
}
34+
35+
protected function getInstanceOf()
36+
{
37+
return InvokableObject::class;
38+
}
39+
}

0 commit comments

Comments
 (0)