🌐 AI搜索 & 代理 主页
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ CHANGELOG
* Add argument `$target` to `ContainerBuilder::registerAliasForArgument()`
* Deprecate registering a service without a class when its id is a non-existing FQCN
* Allow multiple `#[AsDecorator]` attributes
* Handle returning arrays and config-builders from config files

7.3
---
Expand Down
51 changes: 37 additions & 14 deletions src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,42 @@ public function load(mixed $resource, ?string $type = null): mixed
$this->setCurrentDir(\dirname($path));
$this->container->fileExists($path);

// Force load ContainerConfigurator to make env(), param() etc available.
class_exists(ContainerConfigurator::class);

// the closure forbids access to the private scope in the included file
$load = \Closure::bind(function ($path, $env) use ($container, $loader, $resource, $type) {
return include $path;
}, $this, ProtectedPhpFileLoader::class);

try {
$callback = $load($path, $this->env);
if (1 === $result = $load($path, $this->env)) {
$result = null;
}

if (\is_object($callback) && \is_callable($callback)) {
$this->executeCallback($callback, new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource, $this->env), $path);
if (\is_object($result) && \is_callable($result)) {
$result = $this->executeCallback($result, new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource, $this->env), $path);
}
if ($result instanceof ConfigBuilderInterface) {
$this->loadExtensionConfig($result->getExtensionAlias(), ContainerConfigurator::processValue($result->toArray()));
} elseif (is_iterable($result)) {
foreach ($result as $key => $config) {
if ($config instanceof ConfigBuilderInterface) {
if (\is_string($key) && $config->getExtensionAlias() !== $key) {
throw new InvalidArgumentException(\sprintf('The extension alias "%s" of the "%s" config builder does not match the key "%s" in file "%s".', $config->getExtensionAlias(), get_debug_type($config), $key, $path));
}
$this->loadExtensionConfig($config->getExtensionAlias(), ContainerConfigurator::processValue($config->toArray()));
} elseif (!\is_string($key) || !\is_array($config)) {
throw new InvalidArgumentException(\sprintf('The configuration returned in file "%s" must yield only string-keyed arrays or ConfigBuilderInterface values.', $path));
} else {
$this->loadExtensionConfig($key, ContainerConfigurator::processValue($config));
}
}
} elseif (null !== $result) {
throw new InvalidArgumentException(\sprintf('The return value in config file "%s" is invalid.', $path));
}

$this->loadExtensionConfigs();
} finally {
$this->instanceof = [];
$this->registerAliasesForSinglyImplementedInterfaces();
Expand All @@ -92,7 +117,7 @@ public function supports(mixed $resource, ?string $type = null): bool
/**
* Resolve the parameters to the $callback and execute it.
*/
private function executeCallback(callable $callback, ContainerConfigurator $containerConfigurator, string $path): void
private function executeCallback(callable $callback, ContainerConfigurator $containerConfigurator, string $path): mixed
{
$callback = $callback(...);
$arguments = [];
Expand Down Expand Up @@ -125,7 +150,7 @@ private function executeCallback(callable $callback, ContainerConfigurator $cont
}

if ($excluded) {
return;
return null;
}

foreach ($r->getParameters() as $parameter) {
Expand Down Expand Up @@ -163,21 +188,19 @@ private function executeCallback(callable $callback, ContainerConfigurator $cont
}
}

// Force load ContainerConfigurator to make env(), param() etc available.
class_exists(ContainerConfigurator::class);

++$this->importing;
try {
$callback(...$arguments);
return $callback(...$arguments);
} catch (\Throwable $e) {
$configBuilders = [];
throw $e;
} finally {
--$this->importing;
}

foreach ($configBuilders as $configBuilder) {
$this->loadExtensionConfig($configBuilder->getExtensionAlias(), ContainerConfigurator::processValue($configBuilder->toArray()));
foreach ($configBuilders as $configBuilder) {
$this->loadExtensionConfig($configBuilder->getExtensionAlias(), ContainerConfigurator::processValue($configBuilder->toArray()));
}
}

$this->loadExtensionConfigs();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ class AcmeConfig implements ConfigBuilderInterface

private $nested;

public function __construct(array $config = [])
{
$this->color = $config['color'] ?? null;
$this->nested = $config['nested'] ?? null;
}

public function color($value)
{
$this->color = $value;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

use Symfony\Component\DependencyInjection\Tests\Fixtures\AcmeConfig;

$config = new AcmeConfig();
$config->color('red');

return $config;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

use Symfony\Component\DependencyInjection\Tests\Fixtures\AcmeConfig;

return function () {
yield new AcmeConfig(['color' => 'red']);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

return new \stdClass();
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

use Symfony\Component\DependencyInjection\Tests\Fixtures\AcmeConfig;

return [
'acme' => ['color' => 'red'],
new AcmeConfig(),
];
Original file line number Diff line number Diff line change
Expand Up @@ -319,4 +319,50 @@ public function testNamedClosure()
$dumper = new PhpDumper($container);
$this->assertStringEqualsFile(\dirname(__DIR__).'/Fixtures/php/named_closure_compiled.php', $dumper->dump());
}

public function testReturnsConfigBuilderObject()
{
$container = new ContainerBuilder();
$container->registerExtension(new \AcmeExtension());
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Fixtures/config'), 'prod', new ConfigBuilderGenerator(sys_get_temp_dir()));

$loader->load('return_config_builder.php');

$this->assertSame([['color' => 'red']], $container->getExtensionConfig('acme'));
}

public function testReturnsIterableOfArraysAndBuilders()
{
$container = new ContainerBuilder();
$container->registerExtension(new \AcmeExtension());
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Fixtures/config'), 'prod', new ConfigBuilderGenerator(sys_get_temp_dir()));

$loader->load('return_iterable_configs.php');

$configs = $container->getExtensionConfig('acme');
$this->assertCount(2, $configs);
$this->assertSame('red', $configs[0]['color']);
$this->assertArrayHasKey('color', $configs[1]);
}

public function testThrowsOnInvalidReturnType()
{
$container = new ContainerBuilder();
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Fixtures/config'), 'prod', new ConfigBuilderGenerator(sys_get_temp_dir()));

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessageMatches('/The return value in config file/');

$loader->load('return_invalid_types.php');
}

public function testReturnsGenerator()
{
$container = new ContainerBuilder();
$container->registerExtension(new \AcmeExtension());
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Fixtures/config'), 'prod', new ConfigBuilderGenerator(sys_get_temp_dir()));

$loader->load('return_generator.php');
$this->assertSame([['color' => 'red']], $container->getExtensionConfig('acme'));
}
}
Loading