-
-
Notifications
You must be signed in to change notification settings - Fork 74
Description
(the original description of the issue is outdated and there's no issue with Structure, please read the discussion below)
Version: 3.0.13
Bug Description
Configuration of Nette extension can be configured by getConfigSchema() which returns Nette\Schema\Elements\Structure. The actual config is then returned as stdClass.
Consider using this config in the extension:
return Expect::structure([
'redis_client_factory' => Expect::structure([
'prefix' => Expect::string()->dynamic()->nullable(),
'replication' => Expect::structure([
'service' => Expect::string()->dynamic(),
'sentinels' => Expect::arrayOf(Expect::string())->dynamic()
])
])
]);If I don't provide any configuration value to the redis_client_factory.prefix, the default (null) is used. All is fine to this point.
The issue is that the DI container reports, that the redis_client_factory.prefix is missing, even if it is nullable. The reason why this happen is this snippet:
Lines 72 to 78 in 5f0b849
| if (is_array($val) && array_key_exists($key, $val)) { | |
| $val = $val[$key]; | |
| } elseif ($val instanceof DynamicParameter) { | |
| $val = new DynamicParameter($val . '[' . var_export($key, true) . ']'); | |
| } else { | |
| throw new Nette\InvalidArgumentException(sprintf("Missing parameter '%s'.", $part)); | |
| } |
In the past, DI could work with the assumption that the config is always array, but it's not true anymore. Now our code throws an exception from the else branch.
- The first condition evaluates to
false, because our config ($val) is not anarray, butstdClass - Theoretical attempt to use
array_key_exists()withstdClasswould work for now, but it is deprecated - you'd get "array_key_exists(): Using array_key_exists() on objects is deprecated. Use isset() or property_exists() instead".
Steps To Reproduce
The fastest would be to try to use such extension in your Nette application. Use the snippet from above in your testing extension.
FooExtension
<?php
namespace Crm\FooModule\DI;
use Nette;
use Nette\DI\CompilerExtension;
use Nette\Schema\Expect;
final class FooModuleExtension extends CompilerExtension
{
public function getConfigSchema(): Nette\Schema\Schema
{
return Expect::structure([
'redis_client_factory' => Expect::structure([
'prefix' => Expect::string()->dynamic()->nullable(),
'replication' => Expect::structure([
'service' => Expect::string()->dynamic(),
'sentinels' => Expect::arrayOf(Expect::string())->dynamic()
])
])
]);
}
}Now enable the extension in your Nette application:
extensions:
foo: Crm\FooModule\DI\FooModuleExtensionExpected Behavior
Since the property was defined as nullable(), DI should consider "no value" defaulting to null as a valid extension configuration.
Possible Solution
If I understand the code correctly, another elseif branch for stdClass using property_exists() should be sufficient, but I can't tell for sure.
Thanks for checking.