Skip to content

Commit 9cea251

Browse files
authored
fix: Fix Symfony services (#886)
Closes #451.
1 parent 1512cb6 commit 9cea251

File tree

4 files changed

+192
-0
lines changed

4 files changed

+192
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ potentially very difficult to debug due to dissimilar or unsupported package ver
5858
- [Autoload aliases](docs/further-reading.md#autoload-aliases)
5959
- [Class aliases](docs/further-reading.md#class-aliases)
6060
- [Function aliases](docs/further-reading.md#function-aliases)
61+
- [Symfony support](docs/further-reading.md#symfony-support)
6162
- [Limitations](docs/limitations.md#limitations)
6263
- [Dynamic symbols](docs/limitations.md#dynamic-symbols)
6364
- [Date symbols](docs/limitations.md#date-symbols)

docs/further-reading.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- [Autoload aliases](#autoload-aliases)
55
- [Class aliases](#class-aliases)
66
- [Function aliases](#function-aliases)
7+
- [Symfony support](#symfony-support)
78

89

910
### How to deal with unknown third-party symbols
@@ -65,6 +66,51 @@ When [exposing a function] or when a globally declared [excluded function]
6566
declaration is found (see [#706]), an alias will be registered.
6667

6768

69+
### Symfony Support
70+
71+
When using [PHP configuration][symfony-php-config] files for your services, some elements may not be prefixed correctly
72+
due to being strings. For example (taken directly from the Symfony docs):
73+
74+
```php
75+
<?php // config/services.php
76+
77+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
78+
79+
return function(ContainerConfigurator $container): void {
80+
// default configuration for services in *this* file
81+
$services = $container->services()
82+
->defaults()
83+
->autowire() // Automatically injects dependencies in your services.
84+
->autoconfigure() // Automatically registers your services as commands, event subscribers, etc.
85+
;
86+
87+
// makes classes in src/ available to be used as services
88+
// this creates a service per class whose id is the fully-qualified class name
89+
$services->load('App\\', '../src/')
90+
->exclude('../src/{DependencyInjection,Entity,Kernel.php}');
91+
92+
// order is important in this file because service definitions
93+
// always *replace* previous ones; add your own service configuration below
94+
};
95+
```
96+
97+
The string `'App\\'` from `$services->load()` will not be made into `'Prefix\\App\\'`. To address this
98+
you need to use [patchers]. Alternatively, PHP-Scoper provides one which should should handle such cases:
99+
100+
```php
101+
<?php // scoper.inc.php
102+
103+
$symfonyPatcher = (require __DIR__.'/vendor/humbug/php-scoper/res/create-symfony-php-services-patcher.php')('config/services.php');
104+
105+
return [
106+
'patchers' => [$symfonyPatcher],
107+
// ...
108+
];
109+
```
110+
111+
Note that the path is the "regular path(s)" that can be passed to patchers.
112+
113+
68114
<br />
69115
<hr />
70116

@@ -76,3 +122,5 @@ declaration is found (see [#706]), an alias will be registered.
76122
[exposing a class]: configuration.md#exposing-classes
77123
[exposing a function]: configuration.md#exposing-functions
78124
[#706]: https://github.com/humbug/php-scoper/pull/706
125+
[patchers]: ./configuration.md#patchers
126+
[symfony-php-config]: https://symfony.com/doc/current/service_container.html#explicitly-configuring-services-and-arguments
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the humbug/php-scoper package.
7+
*
8+
* Copyright (c) 2017 Théo FIDRY <[email protected]>,
9+
* Pádraic Brady <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
/**
16+
* Creates a patcher able to fix the paths of the Symfony PHP configuration files.
17+
*
18+
* @param string|array<string> $filesPath
19+
*/
20+
return static function (array|string $fileOrFilesPath): Closure {
21+
$filesPath = (array) $fileOrFilesPath;
22+
23+
return static function (string $filePath, string $prefix, string $contents) use ($filesPath): string {
24+
if (!in_array($filePath, $filesPath, true)) {
25+
return $contents;
26+
}
27+
28+
return preg_replace(
29+
'/(.*->load\((?:\n\s+)?\')(.+?\\\\)(\',.*)/',
30+
'$1'.$prefix.'\\\\$2$3',
31+
$contents,
32+
);
33+
};
34+
};
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the humbug/php-scoper package.
7+
*
8+
* Copyright (c) 2017 Théo FIDRY <[email protected]>,
9+
* Pádraic Brady <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
namespace Humbug\PhpScoper\Patcher;
16+
17+
use Closure;
18+
use PHPUnit\Framework\TestCase;
19+
20+
/**
21+
* @internal
22+
*/
23+
final class SymfonyPhpServicesPatcherTest extends TestCase
24+
{
25+
private const FILE_PATH = 'path/to/config.php';
26+
27+
private Closure $patcher;
28+
29+
protected function setUp(): void
30+
{
31+
$this->patcher = (require __DIR__.'/../../res/create-symfony-php-services-patcher.php')([self::FILE_PATH]);
32+
}
33+
34+
/**
35+
* @dataProvider symfonyConfigFileProvider
36+
*/
37+
public function test_it_can_patch_a_symfony_service_file(
38+
string $prefix,
39+
string $contents,
40+
string $expected,
41+
): void {
42+
$actual = ($this->patcher)(self::FILE_PATH, $prefix, $contents);
43+
44+
self::assertSame($expected, $actual);
45+
}
46+
47+
public static function symfonyConfigFileProvider(): iterable
48+
{
49+
$prefix = 'Prefix';
50+
51+
yield 'load statement' => [
52+
$prefix,
53+
<<<'PHP'
54+
use SomeNamespace\SomeClass;
55+
56+
return static function (ContainerConfigurator $containerConfigurator) {
57+
$services = $containerConfigurator->services();
58+
59+
$services->load('SomeNamespace\ConsoleColorDiff\\', __DIR__ . '/../src');
60+
61+
$services->set(SomeClass::class);
62+
}
63+
PHP,
64+
<<<'PHP'
65+
use SomeNamespace\SomeClass;
66+
67+
return static function (ContainerConfigurator $containerConfigurator) {
68+
$services = $containerConfigurator->services();
69+
70+
$services->load('Prefix\SomeNamespace\ConsoleColorDiff\\', __DIR__ . '/../src');
71+
72+
$services->set(SomeClass::class);
73+
}
74+
PHP,
75+
];
76+
77+
yield 'multiline load statement' => [
78+
$prefix,
79+
<<<'PHP'
80+
use SomeNamespace\SomeClass;
81+
82+
return static function (ContainerConfigurator $containerConfigurator) {
83+
$services = $containerConfigurator->services();
84+
85+
$services->load(
86+
'SomeNamespace\ConsoleColorDiff\\',
87+
__DIR__ . '/../src',
88+
);
89+
90+
$services->set(SomeClass::class);
91+
}
92+
PHP,
93+
<<<'PHP'
94+
use SomeNamespace\SomeClass;
95+
96+
return static function (ContainerConfigurator $containerConfigurator) {
97+
$services = $containerConfigurator->services();
98+
99+
$services->load(
100+
'Prefix\SomeNamespace\ConsoleColorDiff\\',
101+
__DIR__ . '/../src',
102+
);
103+
104+
$services->set(SomeClass::class);
105+
}
106+
PHP,
107+
];
108+
}
109+
}

0 commit comments

Comments
 (0)