khartir / typed-config
A tool to create valid config objects from typed classes and data-arrays
Requires
- php: ^7.2|^8.0
Requires (Dev)
- ext-json: *
- doctrine/annotations: ^1.11
- friends-of-phpspec/phpspec-code-coverage: ^5
- infection/infection: 0.*
- phpspec/phpspec: ^6
- phpstan/phpstan: ^0.12
- phpstan/phpstan-strict-rules: ^0.12
- slevomat/coding-standard: ^6.4
- squizlabs/php_codesniffer: ^3.5
Suggests
- doctrine/annotations: If you want to use annotations with PHP < 8
This package is not auto-updated.
Last update: 2025-03-09 03:11:00 UTC
README
A package to create a typed configuration in PHP
Installation
composer require khartir/typed-config
Usage
Given some classes like this:
class Config {
private $foo;
private $barConfig;
public function __construct(string $foo, BarConfig $barConfig) {
$this->foo = $foo;
$this->barConfig = $barConfig;
}
}
class BarConfig {
private $value;
public function __construct(string $value) {
$this->value = $value;
}
}
You can create populated objects like this:
$builder = new \Khartir\TypedConfig\Builder();
$data = [
['barConfig' => ['value' => 'dummy']],
['foo' => 'bar'],
];
$config = $builder->build(Config::class, $data);
snake_case keys to camelCase config
If your array-keys are snake_case
and your properties are camelCase
you can add a converter:
$builder = new \Khartir\TypedConfig\Builder(new \Khartir\TypedConfig\Extractor\SnakeCaseExtractor());
Extending
Writing a custom resolver
Resolvers determine the value of a parameter from the list of values given to the builder.
You can write custom resolvers to implement special logic.
The example contains a resolver to transform strings to \DateTime
-Objects.
class DateTimeResolver implements ResolverInterface
{
public const DATE_FORMAT = 'Y-m-d H:i:s';
public function canResolve(\ReflectionParameter $parameter): bool
{
return \in_array(
ReflectionHelper::getTypeName($parameter),
[\DateTimeImmutable::class, \DateTime::class],
true
);
}
public function resolve(\ReflectionParameter $parameter, array $values): \DateTimeInterface
{
$value = \end($values);
$class = ReflectionHelper::getTypeName($parameter);
$parsed = $class::createFromFormat(self::DATE_FORMAT, $value);
if (false === $parsed) {
throw InvalidArgumentException::createForParameter($value, $parameter);
}
return $parsed;
}
}
Testing
One problem with the nested config objects this library allows is testing. Specifically the need to define values for all values even though a specific test only cares about a couple of values.
For this reason, the DummyValueBuilder
exists. Simply instantiate it the same as you would your
Builder
.
The values for your config are then determined by the following rules:
- If you pass in a value, it is used, just like with the normal
Builder
. If the parameter has a default value, that value is used.
This can be disabled, by calling
$dummyBuilder->useDefaultValue(false)
.If the parameter is nullable,
null
is used.This can be disabled, by calling
$dummyBuilder->useNullable(false)
.If none of the above match, a default value matching the parameter type is used.
The list of default values as
type => value
pairs can be:- overwritten be calling
setDefaultValues
- added to be calling
addDefaultValues
The following defaults are used, if none are otherwise configured:
[ 'int' => 0, 'string' => '', 'float' => 0.0, 'array' => [], 'bool' => false, ]
- overwritten be calling