Skip to content

Commit b6ce8b0

Browse files
author
Robin de Graaf
committed
Time to release a 1.0.0
1 parent 66ca48b commit b6ce8b0

File tree

6 files changed

+103
-93
lines changed

6 files changed

+103
-93
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.DS_Store
22
.idea
3+
.phpunit.result.cache
34
composer.lock
45
vendor
56
coverage

Diff for: CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Parable PHP DI
22

3+
## 1.0.0
4+
5+
It's time! Finally a 1.0.0 release, locking the interface in place for at least a while.
6+
7+
_Changes_
8+
- Completed the `README`.
9+
- `clear()` no longer throws on a non-stored instance, since the expected result (that instance no longer being stored) is met.
10+
- `NotFoundException` renamed to `InstanceNotFoundException` for clarity.
11+
- Added `unmap(string $requested): void` to unmap _and_ clear any mapped instance.
12+
- Improved `clearExcept()` to use `clear()` so it also unsets any existing relationships.
13+
314
## 0.3.2
415

516
_Changes_

Diff for: README.md

+12-13
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ $container = new Container();
3737

3838
class App
3939
{
40-
public function __construct(ThatInterface $classWithInterface)
40+
public function __construct(ThatInterface $classWithThatInterface)
4141
{
4242
}
4343

@@ -47,15 +47,15 @@ class App
4747
}
4848
}
4949

50-
$container->map(ThatInterface::class, ClassWithInterface::class);
50+
$container->map(ThatInterface::class, ClassWithThatInterface::class);
5151

5252
$app = $container->get(App::class);
5353
$app->run();
5454
```
5555

56-
The above situation can also be solved by instantiating and then storing `ClassWithInterface` under `ThatInterface`.
56+
The above situation can also be solved by instantiating and then storing `ClassWithThatInterface` under `ThatInterface`.
5757

58-
Example usage of a class that needs the di itself:
58+
Example usage of a class that needs the di itself, in case you need to do dynamic DI:
5959

6060
```php
6161
use \Parable\Di\Container;
@@ -65,31 +65,30 @@ $container = new Container();
6565
class App
6666
{
6767
public $container;
68-
public function __construct(\Parable\Di\Container $container)
69-
{
70-
$this->container = $container;
71-
}
68+
public function __construct(
69+
public Container $container
70+
) {}
7271
}
7372

7473
$app = $container->get(App::class);
75-
var_dump($app->container->has(App::class));
76-
77-
// output: bool(true)
74+
var_dump($app->container->has(App::class)); // output: bool(true)
7875
```
7976

80-
For all other use cases, simply check the tests in `tests/DiTest.php`.
77+
For all other use cases, simply check the tests in `tests/ContainerTest.php`.
8178

8279
## API
8380

8481
- `get(string $name): object` - creates or gets instance
8582
- `has(string $name): bool` - check if instance is stored
83+
- `assertHas(string $name): void` - check and throw exception if instance is not stored
8684
- `build(string $name): object` - build instance with stored deps, don't store
8785
- `buildAll(string $name): object` - build instance with new deps, don't store
8886
- `map(string $requested, string $replacement): void` - allow pre-emptively defining a replacement class to be instantiated when the requested name is retrieved or built. Use for lazy-loading classes, i.e. for interface deps.
87+
- `unmap(string $requested): void` - removes the mapping _and_ clears any previously mapped instances
8988
- `getDependenciesFor(string $name, [int $storedDependencies]): array` - get dependencies for instance, with stored (default) or new deps
9089
- `store(object $instance, [string $name]): void` - store instance under name, or under instance name by default
9190
- `clear(string $name): void` - clear instance
92-
- `clearExcept(array $keep): void` - clear all except provided names
91+
- `clearExcept(array $keep): void` - clear all except provided names, throws if any of the instances to keep doesn't exist
9392
- `clearAll(): void` - clear all
9493

9594
Where `object` refers to any instance of any class.

Diff for: src/Container.php

+47-61
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace Parable\Di;
44

55
use Parable\Di\Exceptions\ContainerException;
6-
use Parable\Di\Exceptions\NotFoundException;
6+
use Parable\Di\Exceptions\InstanceNotFoundException;
77
use ReflectionClass;
88
use ReflectionNamedType;
99
use Throwable;
@@ -42,9 +42,6 @@ public function get(string $name): object
4242
return $this->instances[$name];
4343
}
4444

45-
/**
46-
* Returns whether an instance is currently stored or not.
47-
*/
4845
public function has(string $name): bool
4946
{
5047
$name = $this->getDefinitiveName($name);
@@ -55,7 +52,7 @@ public function has(string $name): bool
5552
public function assertHas(string $name): void
5653
{
5754
if (!$this->has($name)) {
58-
throw NotFoundException::fromName($name);
55+
throw InstanceNotFoundException::fromName($name);
5956
}
6057
}
6158

@@ -79,31 +76,6 @@ public function buildAll(string $name): object
7976
return $this->createInstance($name, self::USE_NEW_DEPENDENCIES);
8077
}
8178

82-
/**
83-
* Create an instance with either new or existing dependencies.
84-
*
85-
* @throws ContainerException
86-
*/
87-
protected function createInstance(string $name, int $useStoredDependencies): object
88-
{
89-
$name = $this->getDefinitiveName($name);
90-
91-
if (interface_exists($name)) {
92-
throw new ContainerException(sprintf(
93-
"Cannot create instance for interface `%s`.",
94-
$name
95-
));
96-
}
97-
98-
try {
99-
$dependencies = $this->getDependenciesFor($name, $useStoredDependencies);
100-
} catch (Throwable $t) {
101-
throw new ContainerException($t->getMessage(), (int)$t->getCode(), $t);
102-
}
103-
104-
return new $name(...$dependencies);
105-
}
106-
10779
/**
10880
* Map the requested name to the replacement name. When the requested
10981
* name is retrieved, the replacement name will be used to build the instance.
@@ -113,21 +85,12 @@ public function map(string $requested, string $replacement): void
11385
$this->maps[$this->normalize($requested)] = $this->normalize($replacement);
11486
}
11587

116-
/**
117-
* Return the mapping if it exists, otherwise just return the requested name.
118-
*/
119-
protected function getMapIfExists(string $requested): string
88+
public function unmap(string $requested): void
12089
{
121-
return $this->maps[$requested] ?? $requested;
90+
unset($this->maps[$requested]);
91+
$this->clear($requested);
12292
}
12393

124-
/**
125-
* Get the dependencies for an instance, based on the constructor.
126-
* Optionally use stored dependencies or always create new ones.
127-
*
128-
* @return mixed[]
129-
* @throws ContainerException
130-
*/
13194
public function getDependenciesFor(
13295
string $name,
13396
int $useStoredDependencies = self::USE_STORED_DEPENDENCIES
@@ -210,53 +173,43 @@ public function getDependenciesFor(
210173
return $dependencies;
211174
}
212175

213-
/**
214-
* Store the provided instance with the provided name, or the class name of the object.
215-
*/
216176
public function store(object $instance, string $name = null): void
217177
{
218178
$name = $this->getDefinitiveName($name ?? $instance::class);
219179

220180
$this->instances[$name] = $instance;
221181
}
222182

223-
/**
224-
* Clear the requested instance.
225-
*
226-
* @throws NotFoundException
227-
*/
228183
public function clear(string $name): void
229184
{
230185
$name = $this->getDefinitiveName($name);
231186

232-
$this->assertHas($name);
187+
if (!$this->has($name)) {
188+
return;
189+
}
233190

234191
unset($this->instances[$name]);
235192

236193
$this->clearRelationship($name);
237194
}
238195

239196
/**
240-
* Clear all instances except those provided.
241-
*
242197
* @param string[] $keep
243198
*
244199
* @throws ContainerException
245-
* @throws NotFoundException
200+
* @throws InstanceNotFoundException
246201
*/
247202
public function clearExcept(array $keep): void
248203
{
249-
$kept = [];
250-
251204
foreach ($keep as $name) {
252-
$name = $this->getDefinitiveName($name);
253-
254205
$this->assertHas($name);
255-
256-
$kept[$name] = $this->get($name);
257206
}
258207

259-
$this->instances = $kept;
208+
foreach ($this->instances as $name => $instance) {
209+
if (!in_array($name, $keep, true)) {
210+
$this->clear($name);
211+
}
212+
}
260213
}
261214

262215
/**
@@ -268,6 +221,39 @@ public function clearAll(): void
268221
$this->relationships = [];
269222
}
270223

224+
/**
225+
* Create an instance with either new or existing dependencies.
226+
*
227+
* @throws ContainerException
228+
*/
229+
protected function createInstance(string $name, int $useStoredDependencies): object
230+
{
231+
$name = $this->getDefinitiveName($name);
232+
233+
if (interface_exists($name)) {
234+
throw new ContainerException(sprintf(
235+
"Cannot create instance for interface `%s`.",
236+
$name
237+
));
238+
}
239+
240+
try {
241+
$dependencies = $this->getDependenciesFor($name, $useStoredDependencies);
242+
} catch (Throwable $t) {
243+
throw new ContainerException($t->getMessage(), (int)$t->getCode(), $t);
244+
}
245+
246+
return new $name(...$dependencies);
247+
}
248+
249+
/**
250+
* Return the mapping if it exists, otherwise just return the requested name.
251+
*/
252+
protected function getMapIfExists(string $requested): string
253+
{
254+
return $this->maps[$requested] ?? $requested;
255+
}
256+
271257
/**
272258
* Store the relationship between the two items.
273259
*

Diff for: src/Exceptions/NotFoundException.php renamed to src/Exceptions/InstanceNotFoundException.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Exception;
66

7-
class NotFoundException extends Exception
7+
class InstanceNotFoundException extends Exception
88
{
99
public static function fromName(string $id)
1010
{

Diff for: tests/DiTest.php renamed to tests/ContainerTest.php

+31-18
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Parable\Di\Container;
66
use Parable\Di\Exceptions\ContainerException;
7-
use Parable\Di\Exceptions\NotFoundException;
7+
use Parable\Di\Exceptions\InstanceNotFoundException;
88
use Parable\Di\Tests\Classes\ScalarDependency;
99
use Parable\Di\Tests\Classes\CyclicalDependencyFirst;
1010
use Parable\Di\Tests\Classes\CyclicalDependencySecond;
@@ -19,14 +19,14 @@
1919
use Parable\Di\Tests\Classes\ScalarDependencyWithDefaultAndNonScalarReverse;
2020
use PHPUnit\Framework\TestCase;
2121

22-
class DiTest extends TestCase
22+
class ContainerTest extends TestCase
2323
{
2424
protected Container $container;
2525

26-
public function setUp(): void
27-
{
28-
$this->container = new Container();
29-
}
26+
public function setUp(): void
27+
{
28+
$this->container = new Container();
29+
}
3030

3131
public function testGetStoresAndRetrievesInstance(): void
3232
{
@@ -134,6 +134,24 @@ public function testMappingWorksForInterface(): void
134134
self::assertTrue($this->container->has(FakeWithInterface::class));
135135
}
136136

137+
public function testUnmappingWorksForInterface(): void
138+
{
139+
// Since mapped, this part should work
140+
$this->container->map(FakeInterface::class, FakeWithInterface::class);
141+
$this->container->get(FakeInterface::class);
142+
143+
// But not anymore
144+
$this->container->unmap(FakeInterface::class);
145+
146+
try {
147+
$fakeWithInterface = $this->container->get(FakeInterface::class);
148+
149+
self::fail('Should not have gotten here due to interface instantiation.');
150+
} catch (ContainerException $e) {
151+
self::assertStringContainsString('Cannot create instance for interface', $e->getMessage());
152+
}
153+
}
154+
137155
public function testGetDependenciesForWorks(): void
138156
{
139157
$noDependencies = $this->container->get(NoDependencies::class);
@@ -243,14 +261,6 @@ public function testClearWorks(): void
243261
self::assertTrue($this->container->has(Dependencies::class));
244262
}
245263

246-
public function testClearThrowsForMissingInstance(): void
247-
{
248-
$this->expectException(NotFoundException::class);
249-
$this->expectExceptionMessage("No instance found stored for `Parable\Di\Tests\Classes\NoDependencies`.");
250-
251-
$this->container->clear(NoDependencies::class);
252-
}
253-
254264
public function testClearAllWorks(): void
255265
{
256266
$this->container->get(NoDependencies::class);
@@ -279,12 +289,15 @@ public function testClearExceptWorks(): void
279289
self::assertFalse($this->container->has(Dependencies::class));
280290
}
281291

282-
public function testClearExceptThrowsOnMissingInstance(): void
292+
public function testClearExceptThrowsWhenNonExistingClassIsProvided(): void
283293
{
284-
$this->expectException(NotFoundException::class);
285-
$this->expectExceptionMessage("No instance found stored for `Parable\Di\Tests\Classes\NoDependencies`.");
294+
$this->expectException(InstanceNotFoundException::class);
295+
$this->expectExceptionMessage(sprintf(
296+
"No instance found stored for `%s`.",
297+
ScalarDependency::class
298+
));
286299

287-
$this->container->clearExcept([NoDependencies::class]);
300+
$this->container->clearExcept([ScalarDependency::class]);
288301
}
289302

290303
public function testThrowsOnCyclicalDependency(): void

0 commit comments

Comments
 (0)