Skip to content

Commit 80c2af6

Browse files
committed
getFactory added, it's like raw in pimple container, so there is feature parity
1 parent e5d3e3b commit 80c2af6

12 files changed

+359
-9
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ There is a laminas service manager adapter at [chubbyphp/chubbyphp-laminas-confi
3636
Through [Composer](http://getcomposer.org) as [chubbyphp/chubbyphp-container][1].
3737

3838
```sh
39-
composer require chubbyphp/chubbyphp-container "^2.4"
39+
composer require chubbyphp/chubbyphp-container "^2.5"
4040
```
4141

4242
## Usage
@@ -112,6 +112,21 @@ $container = new MinimalContainer();
112112
$container->factory('key', new Parameter('value'));
113113
```
114114

115+
#### Get factory
116+
117+
```php
118+
<?php
119+
120+
use App\Service\MyService;
121+
use Chubbyphp\Container\MinimalContainer;
122+
123+
$container = new MinimalContainer();
124+
125+
$myServiceFactory = $container->getFactory(MyService::class);
126+
127+
$myService = $myServiceFactory($container);
128+
```
129+
115130
#### Get
116131

117132
```php

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"php-coveralls/php-coveralls": "^2.9.1",
2525
"phpstan/extension-installer": "^1.4.3",
2626
"phpstan/phpstan": "^2.1.33",
27-
"phpunit/phpunit": "^12.5.6"
27+
"phpunit/phpunit": "^12.5.6",
28+
"pimple/pimple": "^3.6"
2829
},
2930
"autoload": {
3031
"psr-4": { "Chubbyphp\\Container\\": "src/" }
@@ -41,7 +42,7 @@
4142
},
4243
"extra": {
4344
"branch-alias": {
44-
"dev-master": "2.4-dev"
45+
"dev-master": "2.5-dev"
4546
}
4647
},
4748
"scripts": {

doc/MigrateFromPimple.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,7 @@ $container->factory('cookie_name', new Parameter('SESSION_ID'));
8282
$container->factory('session_storage_class', new Parameter('SessionStorage'));
8383

8484
$container->factory('session_storage', static function (ContainerInterface $container): SessionStorage {
85-
$sessionStorageClass = $container->get('session_storage_class');
86-
87-
return new $sessionStorageClass($container->get('cookie_name'));
85+
return new ($container->get('session_storage_class'))($container->get('cookie_name'));
8886
});
8987
```
9088

@@ -192,4 +190,10 @@ $sessionFunction = $container->raw('session');
192190

193191
### Chubbyphp
194192

195-
There is no equivalent to this functionality.
193+
```php
194+
$container->factory('session', static function (ContainerInterface $container): Session {
195+
return new Session($container->get('session_storage'));
196+
});
197+
198+
$sessionFunction = $container->getFactory('session');
199+
```

src/Container.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ public function prototypeFactory(string $id, callable $factory): ContainerInterf
8989
return $this;
9090
}
9191

92+
public function getFactory(string $id): callable
93+
{
94+
if (isset($this->prototypeFactories[$id])) {
95+
return $this->prototypeFactories[$id];
96+
}
97+
98+
if (isset($this->factories[$id])) {
99+
return $this->factories[$id];
100+
}
101+
102+
throw NotFoundException::create($id);
103+
}
104+
92105
public function get(string $id): mixed
93106
{
94107
if (isset($this->prototypeFactories[$id])) {

src/MinimalContainer.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ public function factory(string $id, callable $factory): ContainerInterface
5252
return $this;
5353
}
5454

55+
public function getFactory(string $id): callable
56+
{
57+
if (isset($this->factories[$id])) {
58+
return $this->factories[$id];
59+
}
60+
61+
throw NotFoundException::create($id);
62+
}
63+
5564
public function get(string $id): mixed
5665
{
5766
return $this->services[$id] ?? $this->services[$id] = $this->createFromFactory($id);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Chubbyphp\Tests\Container\Integration;
6+
7+
use Chubbyphp\Container\Container as ChubbyphpContainer;
8+
9+
final class ChubbyphpFooProvider
10+
{
11+
public function __invoke(): array
12+
{
13+
return [
14+
'session_storage' => static fn (ChubbyphpContainer $container): object => new ($container->get('session_storage_class'))($container->get('cookie_name')),
15+
];
16+
}
17+
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Chubbyphp\Tests\Container\Integration;
6+
7+
use Chubbyphp\Container\Container as ChubbyphpContainer;
8+
use Chubbyphp\Container\Parameter;
9+
use PHPUnit\Framework\TestCase;
10+
use Pimple\Container as PimpleContainer;
11+
12+
/**
13+
* @internal
14+
*
15+
* @coversNothing
16+
*/
17+
final class MigrateFromPimpleTest extends TestCase
18+
{
19+
/**
20+
* Tests all Pimple container features:
21+
* - Defining services (singleton by default)
22+
* - Defining factory services (non-singleton via factory())
23+
* - Defining parameters
24+
* - Protecting parameters (storing callables as values)
25+
* - Modifying services after definition (extend)
26+
* - Extending container with providers
27+
* - Fetching raw service creation function.
28+
*/
29+
public function testPimple(): void
30+
{
31+
$container = new PimpleContainer();
32+
33+
// Define parameters
34+
$container['cookie_name'] = 'SESSION_ID';
35+
$container['session_storage_class'] = SessionStorage::class;
36+
37+
// Protect a callable (store it as a value, not as a service factory)
38+
$container['random_func'] = $container->protect(static fn (): int => random_int(0, getrandmax()));
39+
40+
// Extend container with a provider (defines session_storage)
41+
$container->register(new PimpleFooProvider());
42+
43+
// Modify service after definition (extend)
44+
$container->extend('session_storage', static function (SessionStorage $storage, PimpleContainer $c): SessionStorage {
45+
$storage->setData(['key' => 'value']);
46+
47+
return $storage;
48+
});
49+
50+
// Define another singleton service depending on session_storage
51+
$container['session'] = static fn (PimpleContainer $c): Session => new Session($c['session_storage']);
52+
53+
// Define a factory service (non-singleton, new instance each time)
54+
$container['session_prototype'] = $container->factory(static fn (PimpleContainer $c): Session => new Session($c['session_storage']));
55+
56+
// Assertions
57+
58+
// Parameters work
59+
self::assertSame('SESSION_ID', $container['cookie_name']);
60+
self::assertSame(SessionStorage::class, $container['session_storage_class']);
61+
62+
// Protected callable is returned as-is (not invoked)
63+
/** @var callable(): int $randomFunc */
64+
$randomFunc = $container['random_func'];
65+
self::assertIsCallable($randomFunc);
66+
self::assertIsInt($randomFunc());
67+
68+
// Singleton service returns same instance
69+
/** @var SessionStorage $sessionStorage1 */
70+
$sessionStorage1 = $container['session_storage'];
71+
72+
/** @var SessionStorage $sessionStorage2 */
73+
$sessionStorage2 = $container['session_storage'];
74+
self::assertInstanceOf(SessionStorage::class, $sessionStorage1);
75+
self::assertSame($sessionStorage1, $sessionStorage2);
76+
self::assertSame('SESSION_ID', $sessionStorage1->sessionId);
77+
78+
// Extended service has modifications applied
79+
self::assertSame(['key' => 'value'], $sessionStorage1->getData());
80+
81+
// Session service works with dependency injection
82+
/** @var Session $session */
83+
$session = $container['session'];
84+
self::assertInstanceOf(Session::class, $session);
85+
self::assertSame($sessionStorage1, $session->session);
86+
87+
// Factory service returns different instances each time
88+
self::assertNotSame($container['session_prototype'], $container['session_prototype']);
89+
90+
// Raw function can be fetched and invoked manually
91+
/** @var callable(PimpleContainer): Session $sessionFunction */
92+
$sessionFunction = $container->raw('session');
93+
self::assertIsCallable($sessionFunction);
94+
self::assertInstanceOf(Session::class, $sessionFunction($container));
95+
}
96+
97+
/**
98+
* Tests all Chubbyphp container features:
99+
* - Defining services (singleton via factory())
100+
* - Defining factory services (non-singleton via prototypeFactory())
101+
* - Defining parameters (via Parameter class)
102+
* - Protecting parameters (storing callables via Parameter class)
103+
* - Modifying services after definition (via factory() with previous callable)
104+
* - Extending container with providers
105+
* - Fetching raw service creation function.
106+
*/
107+
public function testChubbyphp(): void
108+
{
109+
$container = new ChubbyphpContainer();
110+
111+
// Define parameters using Parameter class
112+
$container->factory('cookie_name', new Parameter('SESSION_ID'));
113+
$container->factory('session_storage_class', new Parameter(SessionStorage::class));
114+
115+
// Protect a callable (store it as a value, not as a service factory)
116+
$container->factory('random_func', new Parameter(static fn (): int => random_int(0, getrandmax())));
117+
118+
// Extend container with a provider (defines session_storage)
119+
$container->factories((new ChubbyphpFooProvider())());
120+
121+
// Modify service after definition (via factory with previous callable)
122+
$container->factory('session_storage', static function (ChubbyphpContainer $c, callable $previous): SessionStorage {
123+
$storage = $previous($c);
124+
$storage->setData(['key' => 'value']);
125+
126+
return $storage;
127+
});
128+
129+
// Define another singleton service depending on session_storage
130+
$container->factory('session', static fn (ChubbyphpContainer $c): Session => new Session($c->get('session_storage')));
131+
132+
// Define a prototype factory service (non-singleton, new instance each time)
133+
$container->prototypeFactory('session_prototype', static fn (ChubbyphpContainer $c): Session => new Session($c->get('session_storage')));
134+
135+
// Assertions
136+
137+
// Parameters work
138+
self::assertSame('SESSION_ID', $container->get('cookie_name'));
139+
self::assertSame(SessionStorage::class, $container->get('session_storage_class'));
140+
141+
// Protected callable is returned as-is (not invoked)
142+
/** @var callable(): int $randomFunc */
143+
$randomFunc = $container->get('random_func');
144+
self::assertIsCallable($randomFunc);
145+
self::assertIsInt($randomFunc());
146+
147+
// Singleton service returns same instance
148+
/** @var SessionStorage $sessionStorage1 */
149+
$sessionStorage1 = $container->get('session_storage');
150+
151+
/** @var SessionStorage $sessionStorage2 */
152+
$sessionStorage2 = $container->get('session_storage');
153+
self::assertInstanceOf(SessionStorage::class, $sessionStorage1);
154+
self::assertSame($sessionStorage1, $sessionStorage2);
155+
self::assertSame('SESSION_ID', $sessionStorage1->sessionId);
156+
157+
// Extended service has modifications applied
158+
self::assertSame(['key' => 'value'], $sessionStorage1->getData());
159+
160+
// Session service works with dependency injection
161+
/** @var Session $session */
162+
$session = $container->get('session');
163+
self::assertInstanceOf(Session::class, $session);
164+
self::assertSame($sessionStorage1, $session->session);
165+
166+
// Prototype factory service returns different instances each time
167+
self::assertNotSame($container->get('session_prototype'), $container->get('session_prototype'));
168+
169+
// Raw factory function can be fetched and invoked manually
170+
/** @var callable(ChubbyphpContainer): Session $sessionFunction */
171+
$sessionFunction = $container->getFactory('session');
172+
self::assertIsCallable($sessionFunction);
173+
self::assertInstanceOf(Session::class, $sessionFunction($container));
174+
}
175+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Chubbyphp\Tests\Container\Integration;
6+
7+
use Pimple\Container as PimpleContainer;
8+
use Pimple\ServiceProviderInterface;
9+
10+
final class PimpleFooProvider implements ServiceProviderInterface
11+
{
12+
public function register(PimpleContainer $container): void
13+
{
14+
$container['session_storage'] = static fn (PimpleContainer $container): object => new ($container['session_storage_class'])($container['cookie_name']);
15+
}
16+
}

tests/Integration/Session.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Chubbyphp\Tests\Container\Integration;
6+
7+
final class Session
8+
{
9+
public function __construct(public readonly SessionStorage $session) {}
10+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Chubbyphp\Tests\Container\Integration;
6+
7+
final class SessionStorage
8+
{
9+
private array $data = [];
10+
11+
public function __construct(public readonly string $sessionId) {}
12+
13+
public function setData(array $data): void
14+
{
15+
$this->data = $data;
16+
}
17+
18+
public function getData(): array
19+
{
20+
return $this->data;
21+
}
22+
}

0 commit comments

Comments
 (0)