Skip to content

Commit

Permalink
Merge pull request #360 from lcobucci/allow-customisation-of-containe…
Browse files Browse the repository at this point in the history
…r-builder

Allow users to customise Symfony's `ContainerBuilder`
  • Loading branch information
lcobucci authored Oct 18, 2021
2 parents 3ec7f5e + 9d997c1 commit 0fde70e
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 41 deletions.
41 changes: 29 additions & 12 deletions src/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder as SymfonyBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;

use function assert;
Expand Down Expand Up @@ -37,38 +38,54 @@ public static function default(string $configurationFile, string $namespace): se
return self::xml($configurationFile, $namespace);
}

public static function xml(string $configurationFile, string $namespace): self
{
/** @param class-string<SymfonyBuilder>|null $builderClass */
public static function xml(
string $configurationFile,
string $namespace,
?string $builderClass = null,
): self {
return new self(
new ContainerConfiguration($namespace),
new Generators\Xml($configurationFile),
new Generators\Xml($configurationFile, $builderClass),
new ParameterBag(),
);
}

public static function php(string $configurationFile, string $namespace): self
{
/** @param class-string<SymfonyBuilder>|null $builderClass */
public static function php(
string $configurationFile,
string $namespace,
?string $builderClass = null,
): self {
return new self(
new ContainerConfiguration($namespace),
new Generators\Php($configurationFile),
new Generators\Php($configurationFile, $builderClass),
new ParameterBag(),
);
}

public static function yaml(string $configurationFile, string $namespace): self
{
/** @param class-string<SymfonyBuilder>|null $builderClass */
public static function yaml(
string $configurationFile,
string $namespace,
?string $builderClass = null,
): self {
return new self(
new ContainerConfiguration($namespace),
new Generators\Yaml($configurationFile),
new Generators\Yaml($configurationFile, $builderClass),
new ParameterBag(),
);
}

public static function delegating(string $configurationFile, string $namespace): self
{
/** @param class-string<SymfonyBuilder>|null $builderClass */
public static function delegating(
string $configurationFile,
string $namespace,
?string $builderClass = null,
): self {
return new self(
new ContainerConfiguration($namespace),
new Generators\Delegating($configurationFile),
new Generators\Delegating($configurationFile, $builderClass),
new ParameterBag(),
);
}
Expand Down
14 changes: 10 additions & 4 deletions src/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@
abstract class Generator
{
private Compiler $compiler;
/** @var class-string<SymfonyBuilder> */
private string $builderClass;

public function __construct(private string $configurationFile)
{
$this->compiler = new Compiler();
/** @param class-string<SymfonyBuilder>|null $builderClass */
public function __construct(
private string $configurationFile,
?string $builderClass = null,
) {
$this->compiler = new Compiler();
$this->builderClass = $builderClass ?? SymfonyBuilder::class;
}

/**
Expand All @@ -41,7 +47,7 @@ private function loadContainer(ContainerConfiguration $config, ConfigCache $dump

public function initializeContainer(ContainerConfiguration $config): SymfonyBuilder
{
$container = new SymfonyBuilder();
$container = new $this->builderClass();
$container->addResource(new FileResource($this->configurationFile));

$loader = $this->getLoader($container, $config->getPaths());
Expand Down
38 changes: 23 additions & 15 deletions test/CompilerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,13 @@
use SplFileInfo;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\Container;

use function bin2hex;
use function count;
use function exec;
use function file_get_contents;
use function file_put_contents;
use function iterator_to_array;
use function mkdir;
use function random_bytes;
use function realpath;

/**
* @covers \Lcobucci\DependencyInjection\Compiler
Expand Down Expand Up @@ -52,7 +49,7 @@ final class CompilerTest extends TestCase
public function configureDependencies(): void
{
vfsStream::setup(
'tests',
'tests-compilation',
null,
['services.yml' => 'services: { testing: { class: stdClass } }'],
);
Expand All @@ -66,8 +63,8 @@ public function configureDependencies(): void
$this->dump = new ConfigCache($this->dumpDir . '/AppContainer.php', false);

$this->config = new ContainerConfiguration(
'Me\\MyApp',
[vfsStream::url('tests/services.yml')],
'Me\\CompilationTest',
[vfsStream::url('tests-compilation/services.yml')],
[
[$this->parameters, PassConfig::TYPE_BEFORE_OPTIMIZATION],
[[MakeServicesPublic::class, []], PassConfig::TYPE_BEFORE_OPTIMIZATION],
Expand All @@ -79,18 +76,12 @@ public function configureDependencies(): void

private function createDumpDirectory(): string
{
$dir = __DIR__ . '/../tmp/' . bin2hex(random_bytes(5)) . '/me_myapp';
$dir = vfsStream::url('tests-compilation/tmp/me_myapp');
mkdir($dir, 0777, true);

return $dir;
}

/** @after */
public function cleanUpDumpDirectory(): void
{
exec('rm -rf ' . realpath($this->dumpDir . '/../../'));
}

/** @test */
public function compileShouldCreateMultipleFilesForDevelopmentMode(): void
{
Expand Down Expand Up @@ -142,7 +133,7 @@ public function compileShouldTrackChangesOnTheConfigurationFile(): void
public function compileShouldAllowForLazyServices(): void
{
file_put_contents(
vfsStream::url('tests/services.yml'),
vfsStream::url('tests-compilation/services.yml'),
'services: { testing: { class: stdClass, lazy: true } }',
);

Expand All @@ -168,6 +159,23 @@ public function compilationShouldBeSkippedWhenFileAlreadyExists(): void
self::assertCount(1, $generatedFiles);
}

/** @test */
public function compileShouldUseCustomContainerBuilders(): void
{
$compiler = new Compiler();
$compiler->compile(
$this->config,
$this->dump,
new Yaml(__FILE__, CustomContainerBuilderForTests::class),
);

$container = include $this->dumpDir . '/AppContainer.php';

self::assertInstanceOf(Container::class, $container);
self::assertTrue($container->hasParameter('built-with-very-special-builder'));
self::assertTrue($container->getParameter('built-with-very-special-builder'));
}

/** @return PHPGenerator<string, SplFileInfo> */
private function getGeneratedFiles(?string $dir = null): PHPGenerator
{
Expand Down
35 changes: 31 additions & 4 deletions test/ContainerBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,53 @@ public function configureDependencies(): void
* @covers ::__construct
* @covers ::setDefaultConfiguration
*/
public function namedConstructorsShouldSimplifyTheObjectCreation(string $method, Generator $generator): void
{
public function namedConstructorsShouldSimplifyTheObjectCreation(
string $method,
Generator $generator,
?string $builderClass = null,
): void {
$expected = new ContainerBuilder(
new ContainerConfiguration('Lcobucci\\DependencyInjection'),
$generator,
new ParameterBag(),
);

// @phpstan-ignore-next-line
self::assertEquals($expected, ContainerBuilder::$method(__FILE__, __NAMESPACE__));
self::assertEquals($expected, ContainerBuilder::$method(__FILE__, __NAMESPACE__, $builderClass));
}

/** @return iterable<string, array{string, Generator}> */
/** @return iterable<string, array{string, Generator, 2?: class-string<\Symfony\Component\DependencyInjection\ContainerBuilder>}> */
public function supportedFormats(): iterable
{
yield 'default' => ['default', new Generators\Xml(__FILE__)];
yield 'xml' => ['xml', new Generators\Xml(__FILE__)];
yield 'yaml' => ['yaml', new Generators\Yaml(__FILE__)];
yield 'php' => ['php', new Generators\Php(__FILE__)];
yield 'delegating' => ['delegating', new Generators\Delegating(__FILE__)];

yield 'xml with custom builder' => [
'xml',
new Generators\Xml(__FILE__, CustomContainerBuilderForTests::class),
CustomContainerBuilderForTests::class,
];

yield 'yaml with custom builder' => [
'yaml',
new Generators\Yaml(__FILE__, CustomContainerBuilderForTests::class),
CustomContainerBuilderForTests::class,
];

yield 'php with custom builder' => [
'php',
new Generators\Php(__FILE__, CustomContainerBuilderForTests::class),
CustomContainerBuilderForTests::class,
];

yield 'delegating with custom builder' => [
'delegating',
new Generators\Delegating(__FILE__, CustomContainerBuilderForTests::class),
CustomContainerBuilderForTests::class,
];
}

/**
Expand Down
16 changes: 16 additions & 0 deletions test/CustomContainerBuilderForTests.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);

namespace Lcobucci\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder as SymfonyBuilder;

final class CustomContainerBuilderForTests extends SymfonyBuilder
{
public function compile(bool $resolveEnvPlaceholders = false): void
{
$this->parameterBag->set('built-with-very-special-builder', true);

parent::compile($resolveEnvPlaceholders);
}
}
31 changes: 25 additions & 6 deletions test/GeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,25 @@ public function initializeContainerShouldAddTheConfigurationFileAsAResource(): v
self::assertEquals([new FileResource(__FILE__)], $container->getResources());
}

/**
* @test
*
* @covers ::__construct
* @covers ::initializeContainer
*/
public function initializeContainerCanOptionallyUseACustomClass(): void
{
$generator = $this->getMockForAbstractClass(
Generator::class,
[__FILE__, CustomContainerBuilderForTests::class],
);

self::assertInstanceOf(
CustomContainerBuilderForTests::class,
$generator->initializeContainer(new ContainerConfiguration('Me\\MyApp')),
);
}

/**
* @test
*
Expand All @@ -60,27 +79,27 @@ public function initializeContainerShouldAddTheConfigurationFileAsAResource(): v
public function generateShouldCompileAndLoadTheContainer(): void
{
vfsStream::setup(
'tests',
'tests-generation',
null,
['services.yml' => 'services: { testing: { class: stdClass, public: true } }'],
);

$config = new ContainerConfiguration(
'Me\\MyApp',
[vfsStream::url('tests/services.yml')],
'Me\\GenerationTest',
[vfsStream::url('tests-generation/services.yml')],
[
[new ParameterBag(['app.devmode' => true]), PassConfig::TYPE_BEFORE_OPTIMIZATION],
[
new DumpXmlContainer(
new ConfigCache(vfsStream::url('tests/dump.xml'), true),
new ConfigCache(vfsStream::url('tests-generation/dump.xml'), true),
),
PassConfig::TYPE_AFTER_REMOVING,
-255,
],
],
);

$dump = new ConfigCache(vfsStream::url('tests/container.php'), false);
$dump = new ConfigCache(vfsStream::url('tests-generation/container.php'), false);

$this->generator->method('getLoader')->willReturnCallback(
static function (SymfonyBuilder $container, array $paths): YamlFileLoader {
Expand All @@ -94,6 +113,6 @@ static function (SymfonyBuilder $container, array $paths): YamlFileLoader {
$container = $this->generator->generate($config, $dump);

self::assertInstanceOf(stdClass::class, $container->get('testing'));
self::assertFileExists(vfsStream::url('tests/dump.xml'));
self::assertFileExists(vfsStream::url('tests-generation/dump.xml'));
}
}

0 comments on commit 0fde70e

Please sign in to comment.