Skip to content

Commit

Permalink
fix: Fix Symfony services (#886)
Browse files Browse the repository at this point in the history
Closes #451.
  • Loading branch information
theofidry authored Nov 3, 2023
1 parent 1512cb6 commit 9cea251
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ potentially very difficult to debug due to dissimilar or unsupported package ver
- [Autoload aliases](docs/further-reading.md#autoload-aliases)
- [Class aliases](docs/further-reading.md#class-aliases)
- [Function aliases](docs/further-reading.md#function-aliases)
- [Symfony support](docs/further-reading.md#symfony-support)
- [Limitations](docs/limitations.md#limitations)
- [Dynamic symbols](docs/limitations.md#dynamic-symbols)
- [Date symbols](docs/limitations.md#date-symbols)
Expand Down
48 changes: 48 additions & 0 deletions docs/further-reading.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- [Autoload aliases](#autoload-aliases)
- [Class aliases](#class-aliases)
- [Function aliases](#function-aliases)
- [Symfony support](#symfony-support)


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


### Symfony Support

When using [PHP configuration][symfony-php-config] files for your services, some elements may not be prefixed correctly
due to being strings. For example (taken directly from the Symfony docs):

```php
<?php // config/services.php

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return function(ContainerConfigurator $container): void {
// default configuration for services in *this* file
$services = $container->services()
->defaults()
->autowire() // Automatically injects dependencies in your services.
->autoconfigure() // Automatically registers your services as commands, event subscribers, etc.
;

// makes classes in src/ available to be used as services
// this creates a service per class whose id is the fully-qualified class name
$services->load('App\\', '../src/')
->exclude('../src/{DependencyInjection,Entity,Kernel.php}');

// order is important in this file because service definitions
// always *replace* previous ones; add your own service configuration below
};
```

The string `'App\\'` from `$services->load()` will not be made into `'Prefix\\App\\'`. To address this
you need to use [patchers]. Alternatively, PHP-Scoper provides one which should should handle such cases:

```php
<?php // scoper.inc.php

$symfonyPatcher = (require __DIR__.'/vendor/humbug/php-scoper/res/create-symfony-php-services-patcher.php')('config/services.php');

return [
'patchers' => [$symfonyPatcher],
// ...
];
```

Note that the path is the "regular path(s)" that can be passed to patchers.


<br />
<hr />

Expand All @@ -76,3 +122,5 @@ declaration is found (see [#706]), an alias will be registered.
[exposing a class]: configuration.md#exposing-classes
[exposing a function]: configuration.md#exposing-functions
[#706]: https://github.com/humbug/php-scoper/pull/706
[patchers]: ./configuration.md#patchers
[symfony-php-config]: https://symfony.com/doc/current/service_container.html#explicitly-configuring-services-and-arguments
34 changes: 34 additions & 0 deletions res/create-symfony-php-services-patcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

/*
* This file is part of the humbug/php-scoper package.
*
* Copyright (c) 2017 Théo FIDRY <[email protected]>,
* Pádraic Brady <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/**
* Creates a patcher able to fix the paths of the Symfony PHP configuration files.
*
* @param string|array<string> $filesPath
*/
return static function (array|string $fileOrFilesPath): Closure {
$filesPath = (array) $fileOrFilesPath;

return static function (string $filePath, string $prefix, string $contents) use ($filesPath): string {
if (!in_array($filePath, $filesPath, true)) {
return $contents;
}

return preg_replace(
'/(.*->load\((?:\n\s+)?\')(.+?\\\\)(\',.*)/',
'$1'.$prefix.'\\\\$2$3',
$contents,
);
};
};
109 changes: 109 additions & 0 deletions tests/Patcher/SymfonyPhpServicesPatcherTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

declare(strict_types=1);

/*
* This file is part of the humbug/php-scoper package.
*
* Copyright (c) 2017 Théo FIDRY <[email protected]>,
* Pádraic Brady <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Humbug\PhpScoper\Patcher;

use Closure;
use PHPUnit\Framework\TestCase;

/**
* @internal
*/
final class SymfonyPhpServicesPatcherTest extends TestCase
{
private const FILE_PATH = 'path/to/config.php';

private Closure $patcher;

protected function setUp(): void
{
$this->patcher = (require __DIR__.'/../../res/create-symfony-php-services-patcher.php')([self::FILE_PATH]);
}

/**
* @dataProvider symfonyConfigFileProvider
*/
public function test_it_can_patch_a_symfony_service_file(
string $prefix,
string $contents,
string $expected,
): void {
$actual = ($this->patcher)(self::FILE_PATH, $prefix, $contents);

self::assertSame($expected, $actual);
}

public static function symfonyConfigFileProvider(): iterable
{
$prefix = 'Prefix';

yield 'load statement' => [
$prefix,
<<<'PHP'
use SomeNamespace\SomeClass;
return static function (ContainerConfigurator $containerConfigurator) {
$services = $containerConfigurator->services();
$services->load('SomeNamespace\ConsoleColorDiff\\', __DIR__ . '/../src');
$services->set(SomeClass::class);
}
PHP,
<<<'PHP'
use SomeNamespace\SomeClass;
return static function (ContainerConfigurator $containerConfigurator) {
$services = $containerConfigurator->services();
$services->load('Prefix\SomeNamespace\ConsoleColorDiff\\', __DIR__ . '/../src');
$services->set(SomeClass::class);
}
PHP,
];

yield 'multiline load statement' => [
$prefix,
<<<'PHP'
use SomeNamespace\SomeClass;
return static function (ContainerConfigurator $containerConfigurator) {
$services = $containerConfigurator->services();
$services->load(
'SomeNamespace\ConsoleColorDiff\\',
__DIR__ . '/../src',
);
$services->set(SomeClass::class);
}
PHP,
<<<'PHP'
use SomeNamespace\SomeClass;
return static function (ContainerConfigurator $containerConfigurator) {
$services = $containerConfigurator->services();
$services->load(
'Prefix\SomeNamespace\ConsoleColorDiff\\',
__DIR__ . '/../src',
);
$services->set(SomeClass::class);
}
PHP,
];
}
}

0 comments on commit 9cea251

Please sign in to comment.