Skip to content

Commit 5e76cee

Browse files
committed
Initial
0 parents  commit 5e76cee

File tree

10 files changed

+659
-0
lines changed

10 files changed

+659
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
4+
composer.phar

.styleci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
enabled:
2+
3+
disabled:
4+
- align_double_arrow
5+
- concat_without_spaces
6+
- phpdoc_indent
7+
- phpdoc_params

.travis.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
language: php
2+
3+
sudo: false
4+
5+
cache:
6+
directories:
7+
- $HOME/.composer/cache
8+
9+
before_script:
10+
- composer install
11+
12+
script:
13+
- phpunit

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2016 Daniel Leech
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

README.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
PHPBench Service Container
2+
==========================
3+
4+
[![Build Status](https://travis-ci.org/phpbench/container.svg?branch=master)](https://travis-ci.org/phpbench/container)
5+
[![StyleCI](https://styleci.io/repos/<repo-id>/shield)](https://styleci.io/repos/<repo-id>)
6+
7+
This is a simple but powerful dependency injection container:
8+
9+
- Extendable (a.k.a service providers);
10+
- Configurable;
11+
- Extensions provide default configuration;
12+
- Service tagging;
13+
14+
Simple usage
15+
------------
16+
17+
```php
18+
$container = new Container();
19+
$container->register('foobar', function (Container $container) {
20+
return new \stdClass();
21+
});
22+
```
23+
24+
Extending
25+
---------
26+
27+
Extension classes should be passed as the first argument to the container (the
28+
user configuration is the second argumnet).
29+
30+
```php
31+
$container = new Container(
32+
[
33+
MyExtension::class
34+
],
35+
[
36+
'foo.bar' => 'my_new_value',
37+
]
38+
);
39+
$container->init(); // required if you use the build() method.
40+
```
41+
42+
```php
43+
class MyExtension implements ExtensionInterface
44+
{
45+
public function load(Container $container)
46+
{
47+
$container->register('my_service', function (Container $container) {
48+
return new MyService(
49+
$container->getParameter('foo_bar'),
50+
$container->get('some_other_service')
51+
);
52+
});
53+
54+
$container->register('tagged_service', function (Container $container) {
55+
return new MyService(
56+
$container->getParameter('foo_bar'),
57+
$container->get('some_other_service')
58+
);
59+
}, [ 'tag' => []);
60+
}
61+
62+
/**
63+
* Return the default parameters for the container.
64+
*
65+
* @return array
66+
*/
67+
public function getDefaultConfig()
68+
{
69+
return [
70+
'foo_bar' => 'this is foo'
71+
];
72+
}
73+
74+
/**
75+
* Build the container.
76+
*/
77+
public function build(Container $container)
78+
{
79+
foreach ($container->getServiceIdsForTag() as $serviceId) {
80+
$container->get('my_service')->add($container->get($serviceId));
81+
}
82+
}
83+
}
84+
```
85+
86+
Tagging
87+
-------
88+
89+
You can use tags (as in the above example) to collect services from extensions
90+
and do things with them, but be aware that this will cause all of the involved
91+
classes to be instantaited. Which is not very efficient.

composer.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
3+
"name": "phpbench/container",
4+
"description": "Simple, configurable, service container.",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "Daniel Leech",
9+
"email": "daniel@dantleech.com"
10+
}
11+
],
12+
"require": {
13+
},
14+
"require-dev": {
15+
},
16+
"autoload": {
17+
"psr-4": {
18+
"PhpBench\\DependencyInjection\\": "lib/"
19+
}
20+
},
21+
"autoload-dev": {
22+
"psr-4": {
23+
"PhpBench\\DependencyInjection\\Tests\\": "tests/"
24+
}
25+
},
26+
"extra": {
27+
"branch-alias": {
28+
"dev-master": "1.0-dev"
29+
}
30+
}
31+
}

lib/Container.php

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the PHPBench package
5+
*
6+
* (c) Daniel Leech <daniel@dantleech.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace PhpBench\DependencyInjection;
13+
14+
use Interop\Container\ContainerInterface;
15+
16+
/**
17+
* PHPBench Container.
18+
*
19+
* This is a simple, extendable, closure based dependency injection container.
20+
*/
21+
class Container implements ContainerInterface
22+
{
23+
private $instantiators = [];
24+
private $services = [];
25+
private $tags = [];
26+
private $config = [];
27+
private $userConfig = [];
28+
29+
private $extensionClasses = [];
30+
31+
public function __construct(array $extensionClasses = [], array $userConfig = [])
32+
{
33+
$this->extensionClasses = $extensionClasses;
34+
$this->userConfig = $userConfig;
35+
}
36+
37+
/**
38+
* Configure the container. This method will call the `configure()` method
39+
* on each extension. Extensions must use this opportunity to register their
40+
* services and define any default config.
41+
*
42+
* This method must be called before `build()`.
43+
*/
44+
public function init()
45+
{
46+
$extensions = [];
47+
48+
if (empty($this->extensionClasses) && empty($this->userConfig)) {
49+
return;
50+
}
51+
52+
foreach ($this->extensionClasses as $extensionClass) {
53+
if (!class_exists($extensionClass)) {
54+
throw new \InvalidArgumentException(sprintf(
55+
'Extension class "%s" does not exist',
56+
$extensionClass
57+
));
58+
}
59+
60+
$extension = new $extensionClass();
61+
62+
if (!$extension instanceof ExtensionInterface) {
63+
throw new \InvalidArgumentException(sprintf(
64+
// add any manually specified extensions
65+
'Extension "%s" must implement the PhpBench\\Extension interface',
66+
get_class($extension)
67+
));
68+
}
69+
70+
$extensions[] = $extension;
71+
72+
$this->config = array_merge(
73+
$this->config,
74+
$extension->getDefaultConfig()
75+
);
76+
}
77+
78+
$diff = array_diff(array_keys($this->userConfig), array_keys($this->config));
79+
80+
if ($diff) {
81+
throw new \InvalidArgumentException(sprintf(
82+
'Unknown configuration keys: "%s". Permitted keys: "%s"',
83+
implode('", "', $diff), implode('", "', array_keys($this->config))
84+
));
85+
}
86+
87+
$this->config = array_merge(
88+
$this->config,
89+
$this->userConfig
90+
);
91+
92+
foreach ($extensions as $extension) {
93+
$extension->load($this);
94+
}
95+
96+
foreach ($extensions as $extension) {
97+
$extension->build($this);
98+
}
99+
}
100+
101+
/**
102+
* Instantiate and return the service with the given ID.
103+
* Note that this method will return the same instance on subsequent calls.
104+
*
105+
* @param string $serviceId
106+
*
107+
* @return mixed
108+
*/
109+
public function get($serviceId)
110+
{
111+
if (isset($this->services[$serviceId])) {
112+
return $this->services[$serviceId];
113+
}
114+
115+
if (!isset($this->instantiators[$serviceId])) {
116+
throw new \InvalidArgumentException(sprintf(
117+
'No instantiator has been registered for requested service "%s"',
118+
$serviceId
119+
));
120+
}
121+
122+
$this->services[$serviceId] = $this->instantiators[$serviceId]($this);
123+
124+
return $this->services[$serviceId];
125+
}
126+
127+
public function has($serviceId)
128+
{
129+
return isset($this->instantiators[$serviceId]);
130+
}
131+
132+
/**
133+
* Set a service instance.
134+
*
135+
* @param string $serviceId
136+
* @param mixed $instance
137+
*/
138+
public function set($serviceId, $instance)
139+
{
140+
$this->services[$serviceId] = $instance;
141+
}
142+
143+
/**
144+
* Return services IDs for the given tag.
145+
*
146+
* @param string $tag
147+
*
148+
* @return string[]
149+
*/
150+
public function getServiceIdsForTag($tag)
151+
{
152+
$serviceIds = [];
153+
foreach ($this->tags as $serviceId => $tags) {
154+
if (isset($tags[$tag])) {
155+
$serviceIds[$serviceId] = $tags[$tag];
156+
}
157+
}
158+
159+
return $serviceIds;
160+
}
161+
162+
/**
163+
* Register a service with the given ID and instantiator.
164+
*
165+
* The instantiator is a closure which accepts an instance of this container and
166+
* returns a new instance of the service class.
167+
*
168+
* @param string $serviceId
169+
* @param \Closure $instantiator
170+
* @param string[] $tags
171+
*/
172+
public function register($serviceId, \Closure $instantiator, array $tags = [])
173+
{
174+
if (isset($this->instantiators[$serviceId])) {
175+
throw new \InvalidArgumentException(sprintf(
176+
'Service with ID "%s" has already been registered', $serviceId));
177+
}
178+
179+
$this->instantiators[$serviceId] = $instantiator;
180+
$this->tags[$serviceId] = $tags;
181+
}
182+
183+
/**
184+
* Set the value of the parameter with the given name.
185+
*
186+
* @param string $name
187+
* @param mixed $value
188+
*/
189+
public function setParameter($name, $value)
190+
{
191+
$this->config[$name] = $value;
192+
}
193+
194+
/**
195+
* Return the parameter with the given name.
196+
*
197+
* @param string $name
198+
*
199+
* @throws \InvalidArgumentException
200+
*
201+
* @return mixed
202+
*/
203+
public function getParameter($name)
204+
{
205+
if (!array_key_exists($name, $this->config)) {
206+
throw new \InvalidArgumentException(sprintf(
207+
'Parameter "%s" has not been registered',
208+
$name
209+
));
210+
}
211+
212+
return $this->config[$name];
213+
}
214+
215+
/**
216+
* Return true if the named parameter exists.
217+
*
218+
* @param string $name
219+
*
220+
* @return bool
221+
*/
222+
public function hasParameter($name)
223+
{
224+
return array_key_exists($name, $this->config);
225+
}
226+
}

0 commit comments

Comments
 (0)