-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Config Loader that may fetch values from XML using XPath
- Loading branch information
Showing
6 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Buggregator\Trap\Service\Config; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
interface ConfigAttribute | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Buggregator\Trap\Service\Config; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
final class ConfigLoader | ||
{ | ||
private \SimpleXMLElement|null $xml = null; | ||
|
||
/** | ||
* @param null|callable(): non-empty-string $xmlProvider | ||
*/ | ||
public function __construct( | ||
?callable $xmlProvider = null, | ||
) | ||
{ | ||
// Check SimpleXML extension | ||
if (!\extension_loaded('simplexml')) { | ||
return; | ||
} | ||
|
||
try { | ||
$xml = $xmlProvider === null | ||
? \file_get_contents(\dirname(__DIR__, 2) . '/trap.xml') | ||
: $xmlProvider(); | ||
} catch (\Throwable) { | ||
return; | ||
} | ||
|
||
$this->xml = \is_string($xml) ? \simplexml_load_string($xml) : null; | ||
} | ||
|
||
public function hidrate(object $config): void | ||
{ | ||
// Read class properties | ||
$reflection = new \ReflectionObject($config); | ||
foreach ($reflection->getProperties() as $property) { | ||
$attributes = $property->getAttributes(ConfigAttribute::class, \ReflectionAttribute::IS_INSTANCEOF); | ||
if (\count($attributes) === 0) { | ||
continue; | ||
} | ||
|
||
$this->injectValue($config, $property, $attributes); | ||
Check failure on line 47 in src/Service/Config/ConfigLoader.php GitHub Actions / Psalm Validation (PHP 8.2, OS ubuntu-latest)InvalidArgument
|
||
} | ||
} | ||
|
||
/** | ||
* @param \ReflectionProperty $property | ||
* @param array<\ReflectionAttribute> $attributes | ||
*/ | ||
private function injectValue(object $config, \ReflectionProperty $property, array $attributes): void | ||
{ | ||
foreach ($attributes as $attribute) { | ||
$attribute = $attribute->newInstance(); | ||
|
||
$value = match (true) { | ||
$attribute instanceof XPath => $this->xml?->xpath($attribute->path), | ||
default => null, | ||
}; | ||
|
||
if ($value === null) { | ||
continue; | ||
} | ||
|
||
// Cast value to the property type | ||
$type = $property->getType(); | ||
$result = match (true) { | ||
$type === null => $value[0], | ||
$type->allowsNull() && $value[0] === '' => null, | ||
Check failure on line 73 in src/Service/Config/ConfigLoader.php GitHub Actions / Psalm Validation (PHP 8.2, OS ubuntu-latest)TypeDoesNotContainType
Check failure on line 73 in src/Service/Config/ConfigLoader.php GitHub Actions / Psalm Validation (PHP 8.2, OS ubuntu-latest)TypeDoesNotContainType
|
||
$type->isBuiltin() => match ($type->getName()) { | ||
Check failure on line 74 in src/Service/Config/ConfigLoader.php GitHub Actions / Psalm Validation (PHP 8.2, OS ubuntu-latest)UndefinedMethod
Check failure on line 74 in src/Service/Config/ConfigLoader.php GitHub Actions / Psalm Validation (PHP 8.2, OS ubuntu-latest)UndefinedMethod
|
||
'int' => (int) $value[0], | ||
'float' => (float) $value[0], | ||
'bool' => \filter_var($value[0], FILTER_VALIDATE_BOOLEAN), | ||
default => $value[0], | ||
}, | ||
default => $value[0], | ||
}; | ||
|
||
// Set the property value | ||
$property->setValue($config, $result); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Buggregator\Trap\Service\Config; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
#[\Attribute(\Attribute::TARGET_PROPERTY)] | ||
final class XPath implements ConfigAttribute | ||
{ | ||
public function __construct( | ||
public string $path, | ||
) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Buggregator\Trap\Tests\Unit\Service\Config; | ||
|
||
use Buggregator\Trap\Service\Config\ConfigLoader; | ||
use Buggregator\Trap\Service\Config\XPath; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
final class ConfigLoaderTest extends TestCase | ||
{ | ||
public function testSimpleHydration(): void | ||
{ | ||
$dto = new class() { | ||
#[XPath('/trap/container/@myBool')] | ||
public bool $myBool; | ||
#[XPath('/trap/container/MyInt/@value')] | ||
public int $myInt; | ||
#[XPath('/trap/@my-string')] | ||
public string $myString; | ||
#[XPath('/trap/container/MyFloat/@value')] | ||
public float $myFloat; | ||
}; | ||
|
||
$xml = <<<'XML' | ||
<?xml version="1.0"?> | ||
<trap my-string="foo-bar"> | ||
<container myBool="true"> | ||
<MyInt value="200"/> | ||
<MyFloat value="42"/> | ||
</container> | ||
</trap> | ||
XML; | ||
|
||
$loader = new ConfigLoader(fn() => $xml); | ||
$loader->hidrate($dto); | ||
|
||
self::assertTrue($dto->myBool); | ||
self::assertSame(200, $dto->myInt); | ||
self::assertSame('foo-bar', $dto->myString); | ||
self::assertSame(42.0, $dto->myFloat); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0"?> | ||
<trap> | ||
<frontend port="8000"> | ||
<EventsBuffer maxSize="200"/> | ||
</frontend> | ||
</trap> |