Skip to content

Commit 2a567bf

Browse files
committed
v2.0 Create proxy container
1 parent 7c5ceea commit 2a567bf

24 files changed

+835
-52
lines changed

.github/workflows/ci.yaml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,11 @@ jobs:
1313
fail-fast: false
1414
matrix:
1515
# normal, highest, non-dev installs
16-
php-version: [ '8.2' ]
16+
php-version: [ '8.4' ]
1717
dependency-versions: [ 'highest' ]
1818
include:
19-
# testing lowest PHP version with the lowest dependencies
20-
- php-version: '8.2'
21-
dependency-versions: 'lowest'
22-
2319
# testing dev versions with the highest PHP
24-
- php-version: '8.2'
20+
- php-version: '8.4'
2521
dependency-versions: 'highest'
2622

2723
steps:
@@ -31,7 +27,7 @@ jobs:
3127
- name: "Install PHP"
3228
uses: "shivammathur/setup-php@v2"
3329
with:
34-
coverage: "none"
30+
coverage: "xdebug"
3531
php-version: "${{ matrix.php-version }}"
3632

3733
- name: "Composer install"
@@ -40,5 +36,5 @@ jobs:
4036
dependency-versions: "${{ matrix.dependency-versions }}"
4137
composer-options: "--prefer-dist --no-progress"
4238

43-
- name: "RUn tests"
39+
- name: "Run tests"
4440
run: "composer coverage-html"

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
vendor
33
composer.lock
44
.phpunit.result.cache
5+
.phpunit.cache
56
.php-cs-fixer.cache
67
test-coverage-report
78
phpunit.xml
89
.php-cs-fixer.php
9-
phpstan.neon
10+
phpstan.neon
11+
tests/Unit/var

.php-cs-fixer.dist.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
For the full copyright and license information, please view the LICENSE
2525
file that was distributed with this source code.
2626
EOF
27-
]
27+
],
28+
'phpdoc_to_comment' => false, // отключаем
2829
))
2930
->setRiskyAllowed(true)
3031
->setFinder($finder);

composer.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
],
1212
"require": {
1313
"php": "^8.2",
14-
"psr/container": "^2.0"
14+
"psr/container": "^2"
1515
},
1616
"require-dev": {
17-
"ergebnis/composer-normalize": "^2.29",
18-
"friendsofphp/php-cs-fixer": "^3.13",
19-
"phpstan/phpstan": "^1.9",
20-
"phpunit/php-code-coverage": "^9.2",
21-
"phpunit/phpunit": "^9.5",
22-
"vimeo/psalm": "^5.2"
17+
"ergebnis/composer-normalize": "^2",
18+
"friendsofphp/php-cs-fixer": "^3",
19+
"phpstan/phpstan": "^2",
20+
"phpunit/php-code-coverage": "^10 || ^11 || ^12",
21+
"phpunit/phpunit": "^10 || ^11 || ^12",
22+
"vimeo/psalm": "^6"
2323
},
2424
"suggest": {
2525
"micro/autowire": "Autowire helper for dependency injection"

phpunit.xml.dist

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3-
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/12.3/phpunit.xsd"
43
bootstrap="vendor/autoload.php"
54
backupGlobals="false"
6-
backupStaticAttributes="false"
75
colors="true"
8-
convertErrorsToExceptions="true"
9-
convertNoticesToExceptions="true"
10-
convertWarningsToExceptions="true"
116
processIsolation="false"
12-
stopOnFailure="false">
13-
<coverage>
14-
<include>
15-
<directory suffix=".php">src/</directory>
16-
</include>
17-
</coverage>
7+
stopOnFailure="false"
8+
cacheDirectory=".phpunit.cache"
9+
backupStaticProperties="false">
1810
<testsuites>
1911
<testsuite name="Micro Component: Dependency Injection Unit Test Suite">
2012
<directory>tests/Unit</directory>
@@ -23,4 +15,9 @@
2315
<php>
2416
<env name="APP_ENV" value="dev-test"/>
2517
</php>
18+
<source>
19+
<include>
20+
<directory suffix=".php">src/</directory>
21+
</include>
22+
</source>
2623
</phpunit>

src/Container.php

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
use Micro\Component\DependencyInjection\Exception\ServiceNotRegisteredException;
1515
use Micro\Component\DependencyInjection\Exception\ServiceRegistrationException;
16-
use Psr\Container\ContainerInterface;
1716

1817
/**
1918
* @author Stanislau Komar <head.trackingsoft@gmail.com>
@@ -26,12 +25,12 @@ class Container implements ContainerInterface, ContainerRegistryInterface, Conta
2625
private array $services = [];
2726

2827
/**
29-
* @var array<class-string, callable(Container): object>
28+
* @var array<class-string, callable>
3029
*/
3130
private array $servicesRaw = [];
3231

3332
/**
34-
* @var array<class-string, array<int, array<callable(object, Container): object>>>
33+
* @var array<class-string, array<int, array<int, callable>>>
3534
*/
3635
private array $decorators = [];
3736

@@ -45,6 +44,7 @@ class Container implements ContainerInterface, ContainerRegistryInterface, Conta
4544
*
4645
* @psalm-return T
4746
*/
47+
#[\Override]
4848
public function get(string $id): object
4949
{
5050
if (!empty($this->services[$id])) {
@@ -61,15 +61,18 @@ public function get(string $id): object
6161
*
6262
* @psalm-suppress MoreSpecificImplementedParamType
6363
*/
64+
#[\Override]
6465
public function has(string $id): bool
6566
{
66-
return !empty($this->servicesRaw[$id]) || !empty($this->services[$id]);
67+
return \array_key_exists($id, $this->servicesRaw)
68+
|| \array_key_exists($id, $this->services);
6769
}
6870

71+
#[\Override]
6972
public function register(string $id, callable $service, bool $force = false): void
7073
{
7174
if ($this->has($id) && !$force) {
72-
throw new ServiceRegistrationException(sprintf('Service "%s" already registered', $id));
75+
throw new ServiceRegistrationException(\sprintf('Service "%s" already registered', $id));
7376
}
7477

7578
$this->servicesRaw[$id] = $service;
@@ -78,11 +81,23 @@ public function register(string $id, callable $service, bool $force = false): vo
7881
/**
7982
* @psalm-suppress InvalidPropertyAssignmentValue
8083
*/
84+
#[\Override]
8185
public function decorate(string $id, callable $service, int $priority = 0): void
8286
{
8387
if (!\array_key_exists($id, $this->decorators)) {
88+
/**
89+
* @psalm-suppress PropertyTypeCoercion
90+
*
91+
* @phpstan-ignore-next-line
92+
*/
8493
$this->decorators[$id] = [];
8594
}
95+
96+
/**
97+
* @psalm-suppress PropertyTypeCoercion
98+
*
99+
* @phpstan-ignore-next-line
100+
*/
86101
$this->decorators[$id][$priority][] = $service;
87102
}
88103

@@ -93,7 +108,7 @@ public function decorate(string $id, callable $service, int $priority = 0): void
93108
*/
94109
protected function initializeService(string $serviceId): void
95110
{
96-
if (empty($this->servicesRaw[$serviceId])) {
111+
if (!isset($this->servicesRaw[$serviceId])) {
97112
throw new ServiceNotRegisteredException($serviceId);
98113
}
99114

src/ContainerCompiled.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Micro framework package.
7+
*
8+
* (c) Stanislau Komar <kost@micro-php.net>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Micro\Component\DependencyInjection;
15+
16+
use Micro\Component\DependencyInjection\Exception\ServiceRegistrationException;
17+
use Micro\Component\DependencyInjection\Proxy\ProxyBuilderInterface;
18+
use Micro\Component\DependencyInjection\Proxy\ProxyClassNameGeneratorInterface;
19+
use Psr\Container\ContainerInterface as PsrContainerInterface;
20+
21+
/** @psalm-suppress UnusedClass */
22+
final class ContainerCompiled extends Container implements ContainerRegistryCompiledInterface
23+
{
24+
private bool $isCompiled;
25+
26+
private PsrContainerInterface $decorated;
27+
28+
public function __construct(
29+
private readonly ProxyClassNameGeneratorInterface $classNameGenerator,
30+
private readonly ProxyBuilderInterface $proxyBuilder,
31+
?PsrContainerInterface $decorated = null,
32+
) {
33+
$this->isCompiled = false;
34+
$this->decorated = $decorated ?? new Container();
35+
}
36+
37+
/**
38+
* @template T of object
39+
*
40+
* @param class-string<T> $id
41+
*
42+
* @psalm-suppress MoreSpecificImplementedParamType
43+
*
44+
* @return T
45+
*/
46+
#[\Override]
47+
public function get(string $id): object
48+
{
49+
$proxyClass = $this->classNameGenerator->createProxyClassName($id);
50+
if (!class_exists($proxyClass)) {
51+
/* @psalm-suppress MixedReturnStatement */
52+
return $this->decorated->get($id);
53+
}
54+
55+
/**
56+
* @var T $instance
57+
*
58+
* @psalm-suppress MixedMethodCall
59+
*/
60+
$instance = new $proxyClass($this);
61+
62+
return $instance;
63+
}
64+
65+
#[\Override]
66+
public function has(string $id): bool
67+
{
68+
return $this->decorated->has($id);
69+
}
70+
71+
#[\Override]
72+
public function register(string $id, callable $service, bool $force = false): void
73+
{
74+
if (!$this->decorated instanceof ContainerRegistryInterface) {
75+
throw new ServiceRegistrationException('Container can not decorate service because container does not implement '.ContainerRegistryInterface::class);
76+
}
77+
78+
if ($this->isCompiled) {
79+
throw new ServiceRegistrationException('Can not register service because container already compiled.');
80+
}
81+
82+
if (interface_exists($id) || class_exists($id)) {
83+
$this->proxyBuilder->add($id);
84+
}
85+
86+
/** @psalm-suppress ArgumentTypeCoercion */
87+
$this->decorated->register($id, $service, $force);
88+
}
89+
90+
/**
91+
* @psalm-suppress InvalidPropertyAssignmentValue
92+
*/
93+
#[\Override]
94+
public function decorate(string $id, callable $service, int $priority = 0): void
95+
{
96+
if (!$this->decorated instanceof ContainerDecoratorInterface) {
97+
throw new ServiceRegistrationException('Container can not decorate service because container does not implement '.ContainerDecoratorInterface::class);
98+
}
99+
100+
if ($this->isCompiled) {
101+
throw new ServiceRegistrationException('Can not register service because container already compiled.');
102+
}
103+
104+
if (interface_exists($id) || class_exists($id)) {
105+
$this->proxyBuilder->add($id);
106+
}
107+
108+
/** @psalm-suppress ArgumentTypeCoercion */
109+
$this->decorated->decorate($id, $service, $priority);
110+
}
111+
112+
#[\Override]
113+
public function compile(): void
114+
{
115+
$this->proxyBuilder->build();
116+
117+
$this->isCompiled = true;
118+
}
119+
}

src/ContainerDecoratorInterface.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ interface ContainerDecoratorInterface
1616
/**
1717
* @template T of object
1818
*
19-
* @param class-string<T> $id
19+
* @param class-string<T>|non-empty-string $id
20+
* @param callable(T, ContainerInterface): T $service
2021
*/
2122
public function decorate(string $id, callable $service, int $priority = 0): void;
2223
}

src/ContainerInterface.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Micro framework package.
7+
*
8+
* (c) Stanislau Komar <kost@micro-php.net>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Micro\Component\DependencyInjection;
15+
16+
interface ContainerInterface extends \Psr\Container\ContainerInterface
17+
{
18+
/**
19+
* @param class-string $id
20+
*
21+
* @psalm-suppress MoreSpecificImplementedParamType
22+
*/
23+
#[\Override]
24+
public function has(string $id): bool;
25+
26+
/**
27+
* @template T of object
28+
*
29+
* @param class-string<T> $id
30+
*
31+
* @psalm-return T
32+
*
33+
* @psalm-suppress MoreSpecificImplementedParamType
34+
*/
35+
#[\Override]
36+
public function get(string $id): mixed;
37+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Micro framework package.
7+
*
8+
* (c) Stanislau Komar <kost@micro-php.net>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Micro\Component\DependencyInjection;
15+
16+
interface ContainerRegistryCompiledInterface extends ContainerRegistryInterface
17+
{
18+
/** @psalm-suppress PossiblyUnusedMethod */
19+
public function compile(): void;
20+
}

0 commit comments

Comments
 (0)