Skip to content

Commit

Permalink
ParametersExtension, Container: redesigned way of handling dynamic pa…
Browse files Browse the repository at this point in the history
…rameters via getParameter() [Closes #291][Closes #288]

- parameters with expressions are automatically treated as dynamic
  • Loading branch information
dg committed Sep 25, 2023
1 parent e30f774 commit 80d8f38
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/DI/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public function getConfig(): array
public function setDynamicParameterNames(array $names)
{
assert($this->extensions[self::Parameters] instanceof Extensions\ParametersExtension);
$this->extensions[self::Parameters]->dynamicParams = $names;
$this->extensions[self::Parameters]->dynamicParams = array_flip($names);
return $this;
}

Expand Down
15 changes: 15 additions & 0 deletions src/DI/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ public function getParameters(): array
}


public function getParameter($key)
{
if (!array_key_exists($key, $this->parameters)) {
$this->parameters[$key] = $this->getDynamicParameter($key);
}
return $this->parameters[$key];
}


protected function getDynamicParameter($key)
{
throw new Nette\InvalidStateException(sprintf("Parameter '%s' not found. Check if 'di › export › parameters' is enabled.", $key));
}


/**
* Adds the service to the container.
* @param object $service service or its factory
Expand Down
2 changes: 1 addition & 1 deletion src/DI/Definitions/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*
* @property string|array|Definition|Reference|null $entity
*/
final class Statement implements Nette\Schema\DynamicParameter
final class Statement
{
use Nette\SmartObject;

Expand Down
67 changes: 46 additions & 21 deletions src/DI/Extensions/ParametersExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
final class ParametersExtension extends Nette\DI\CompilerExtension
{
/** @var string[] */
/** @var array<string, scalar> */
public $dynamicParams = [];

/** @var string[][] */
Expand All @@ -38,13 +38,19 @@ public function loadConfiguration()
{
$builder = $this->getContainerBuilder();
$params = $this->config;
$resolver = new Nette\DI\Resolver($builder);
$generator = new Nette\DI\PhpGenerator($builder);

foreach ($this->dynamicParams as $key) {
$params[$key] = array_key_exists($key, $params)
? new DynamicParameter($generator->formatPhp('($this->parameters[?] \?\? ?)', $resolver->completeArguments(Nette\DI\Helpers::filterArguments([$key, $params[$key]]))))
: new DynamicParameter((new Nette\PhpGenerator\Dumper)->format('$this->parameters[?]', $key));
// expressions are automatically treated as dynamic params
foreach ($params as $key => $value) {
$tmp = [$value];
array_walk_recursive($tmp, function ($val) use ($key): void {
if ($val instanceof Nette\DI\Definitions\Statement) {
$this->dynamicParams[$key] = true;
}
});
}

foreach ($this->dynamicParams as $key => $foo) {
$params[$key] = new DynamicParameter((new Nette\PhpGenerator\Dumper)->format('$this->getParameter(?)', $key));
}

$builder->parameters = Nette\DI\Helpers::expand($params, $params, true);
Expand All @@ -58,21 +64,40 @@ public function loadConfiguration()

public function afterCompile(Nette\PhpGenerator\ClassType $class)
{
$parameters = $this->getContainerBuilder()->parameters;
array_walk_recursive($parameters, function (&$val): void {
if ($val instanceof Nette\DI\Definitions\Statement || $val instanceof DynamicParameter) {
$val = null;
}
});

$cnstr = $class->getMethod('__construct');
$cnstr->addBody('$this->parameters += ?;', [$parameters]);
foreach ($this->dynamicValidators as [$param, $expected]) {
if ($param instanceof Nette\DI\Definitions\Statement) {
continue;
}
$builder = $this->getContainerBuilder();

$staticParams = $builder->parameters;
foreach ($staticParams as $key => $value) {
$tmp = [$value];
array_walk_recursive($tmp, function ($val) use ($key, &$staticParams): void {
if ($val instanceof DynamicParameter) {
$this->dynamicParams[$key] = true;
unset($staticParams[$key]);
}
});
}

$cnstr->addBody('Nette\Utils\Validators::assert(?, ?, ?);', [$param, $expected, 'dynamic parameter']);
$class->getMethod('__construct')
->addBody('$this->parameters += ?;', [$staticParams]);

if (!$this->dynamicParams) {
return;
}

$getter = $class->addMethod('getDynamicParameter')->setProtected();
$getter->addParameter('key');
$resolver = new Nette\DI\Resolver($builder);
$generator = new Nette\DI\PhpGenerator($builder);
$getter->addBody('switch (true) {');
foreach ($this->dynamicParams as $key => $foo) {
$value = Nette\DI\Helpers::expand($this->config[$key] ?? null, $builder->parameters);
$value = $resolver->completeArguments(Nette\DI\Helpers::filterArguments([$value]));
$getter->addBody("\tcase \$key === ?: return ?;", [$key, $generator->convertArguments($value)[0]]);
}
$getter->addBody("\tdefault: parent::getDynamicParameter(\$key);\n};");

foreach ($this->dynamicValidators as [$param, $expected]) {
$this->initialization->addBody('Nette\Utils\Validators::assert(?, ?, ?);', [$param, $expected, 'dynamic parameter']);
}
}
}
8 changes: 7 additions & 1 deletion src/DI/PhpGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ public function formatStatement(Statement $statement): string
* @internal
*/
public function formatPhp(string $statement, array $args): string
{
return (new Php\Dumper)->format($statement, ...$this->convertArguments($args));
}


public function convertArguments(array $args): array
{
array_walk_recursive($args, function (&$val): void {
if ($val instanceof Statement) {
Expand All @@ -186,7 +192,7 @@ public function formatPhp(string $statement, array $args): string
}
}
});
return (new Php\Dumper)->format($statement, ...$args);
return $args;
}


Expand Down
29 changes: 0 additions & 29 deletions tests/DI/Compiler.dynamicParameters.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,6 @@ test('', function () {
});


test('', function () {
$compiler = new DI\Compiler;
$compiler->setDynamicParameterNames(['dynamic']);
$container = createContainer($compiler, '
parameters:
dynamic: ::trim(" a ")
services:
one: Service(%dynamic%)
');
Assert::same('a', $container->getService('one')->arg);
});


test('', function () {
$compiler = new DI\Compiler;
$compiler->setDynamicParameterNames(['dynamic']);
Expand All @@ -126,18 +112,3 @@ test('', function () {
');
Assert::same('hello', $container->getService('one')->arg);
});


test('', function () {
$compiler = new DI\Compiler;
$compiler->setDynamicParameterNames(['dynamic']);
Assert::exception(function () use ($compiler) {
createContainer($compiler, '
parameters:
dynamic: @one
services:
one: Service
');
}, Nette\DI\ServiceCreationException::class, "Reference to missing service 'one'.");
});
14 changes: 9 additions & 5 deletions tests/DI/Compiler.dynamicParameters.validator.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::exception(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
string: %dynamic%
', ['dynamic' => 123]);
$container->initialize();
}, Nette\Utils\AssertionException::class, 'The dynamic parameter expects to be string, int 123 given.');
});

Expand All @@ -42,11 +43,12 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::exception(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
string: %dynamic%
', ['dynamic' => null]);
$container->initialize();
}, Nette\Utils\AssertionException::class, 'The dynamic parameter expects to be string, null given.');
});

Expand All @@ -56,11 +58,12 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::exception(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
string: %dynamic.sub%
', ['dynamic' => ['sub' => 123]]);
$container->initialize();
}, Nette\Utils\AssertionException::class, 'The dynamic parameter expects to be string, int 123 given.');
});

Expand All @@ -70,11 +73,12 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::noError(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
intnull: %dynamic%
', ['dynamic' => 123]);
$container->initialize();
});
});

Expand All @@ -84,7 +88,7 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::noError(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
intnull: %dynamic%
Expand Down
34 changes: 28 additions & 6 deletions tests/DI/Compiler.parameters.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,27 @@ test('', function () {
services:
one: Service(%bar%)
');
Assert::null($container->parameters['bar']);
Assert::same('a', $container->getParameter('bar'));
Assert::same('a', $container->getService('one')->arg);
});


test('', function () {
$compiler = new DI\Compiler;
$container = createContainer($compiler, '
parameters:
bar: ::trim(" a ")
expand: hello%bar%
services:
one: Service(%expand%)
');
Assert::same('a', $container->getParameter('bar'));
Assert::same('helloa', $container->getParameter('expand'));
Assert::same('helloa', $container->getService('one')->arg);
});


test('', function () {
$compiler = new DI\Compiler;
$container = createContainer($compiler, '
Expand All @@ -66,7 +82,7 @@ test('', function () {
services:
one: Service(%bar%)
');
Assert::null($container->parameters['bar']);
Assert::same('Service::method hello', $container->getParameter('bar'));
Assert::same('Service::method hello', $container->getService('one')->arg);
});

Expand All @@ -81,7 +97,7 @@ test('', function () {
one: Service(%bar%)
two: Service(two)
');
Assert::same('@two', $container->parameters['bar']); // not resolved
Assert::same('@two', $container->getParameter('bar')); // not resolved
Assert::same($container->getService('two'), $container->getService('one')->arg);
});

Expand All @@ -96,7 +112,7 @@ test('', function () {
one: Service(%bar%)
two: Service(two)
');
Assert::null($container->parameters['bar']);
Assert::type(Service::class, $container->getParameter('bar'));
Assert::same($container->getService('two'), $container->getService('one')->arg->arg);
});

Expand All @@ -111,6 +127,12 @@ test('', function () {
one: Service(%bar%)
two: Service(two)
');
Assert::null($container->parameters['bar']);
Assert::same([$container->getService('two')], $container->getService('one')->arg);

Assert::exception(
function () use ($container) {
$container->getParameter('bar');
},
Nette\InvalidStateException::class,
'Circular reference detected for services: one.'
);
});
3 changes: 2 additions & 1 deletion tests/DI/DIExtension.exportParameters.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ test('', function () {
parameters: true
', ['dynamic' => 123]);

Assert::same(['dynamic' => 123, 'key' => null], $container->parameters);
Assert::same(['dynamic' => 123], $container->parameters);
Assert::same(123, $container->getParameter('key'));
});


Expand Down

0 comments on commit 80d8f38

Please sign in to comment.