Skip to content

Commit

Permalink
Reworked parameters. Added tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
laurentmuller committed Dec 16, 2024
1 parent 2aaf330 commit 2c1b35c
Show file tree
Hide file tree
Showing 21 changed files with 1,140 additions and 140 deletions.
8 changes: 4 additions & 4 deletions src/Attribute/Parameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function __construct(public string $name, public mixed $default = null)
*
* @return self|null the attribute instance, if found; null otherwise
*
* @throws \ReflectionException
* @throws \ReflectionException if the class does not exist
*
* @psalm-template T of object
*
Expand Down Expand Up @@ -66,7 +66,7 @@ public static function getAttributInstance(object|string $objectOrClass, string
*
* @return mixed the default value
*
* @throws \ReflectionException
* @throws \ReflectionException if the class does not exist
*
* @psalm-template T of object
*
Expand All @@ -86,7 +86,7 @@ public static function getDefaultValue(object|string $objectOrClass, string $nam
*
* @return ?string the parameter name or null if no attribute is found
*
* @throws \ReflectionException
* @throws \ReflectionException if the class does not exist
*
* @psalm-template T of object
*
Expand All @@ -107,7 +107,7 @@ public static function getName(object|string $objectOrClass, string $name): ?str
*
* @return bool true if default value; false otherwise
*
* @throws \ReflectionException
* @throws \ReflectionException if the class does not exist
*
* @psalm-template T of object
*
Expand Down
4 changes: 2 additions & 2 deletions src/Controller/TestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -520,14 +520,14 @@ public function testParameter(ApplicationParameters $application, UserParameters
->setCalculations(8);

$application->getProduct();
$application->getDefaultValue();
$application->getDefault();

$application->getSecurity()
->setCaptcha(true)
->setLevel(StrengthLevel::MEDIUM);

$user->getHomePage()
->setCalculations(10);
->setCalculations(7);
$user->getOption()
->setPrintAddress(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@
use Symfony\Contracts\Cache\CacheInterface;

/**
* Abstract parameters container.
*
* @template TProperty of AbstractProperty
*
* @psalm-type TValue = scalar|array|\BackedEnum|\DateTimeInterface|EntityInterface|null
*/
abstract class AbstractParameterContainer
abstract class AbstractParameters
{
private ?PropertyAccessor $accessor = null;

Expand All @@ -50,53 +54,18 @@ protected function createParameter(string $class, ?ParameterInterface $default =
$metaDatas = $this->getMetaDatas($parameter);

foreach ($metaDatas as $metaData) {
$value = null;
$property = $this->findProperty($metaData->name);
if (!$property instanceof AbstractProperty) {
/** @psalm-var mixed $value */
$value = $this->getDefaultPropertyValue($accessor, $default, $metaData);
if (null !== $value) {
$accessor->setValue($parameter, $metaData->property, $value);
}
continue;
}
switch ($metaData->type) {
case 'bool':
$value = $property->getBoolean();
break;
case 'int':
$value = $property->getInteger();
break;
case 'float':
$value = $property->getFloat();
break;
case 'string':
$value = $property->getValue();
break;
case 'array':
$value = $property->getArray();
break;
case 'DateTimeInterface':
$value = $property->getDate();
break;
default:
if ($metaData->isBackedEnumType()) {
$value = $this->getBackEnum($metaData->type, $property);
break;
}
if ($metaData->isEntityInterfaceType()) {
$value = $this->getEntity($metaData->type, $property->getInteger());
break;
}
throw new \LogicException(\sprintf('Unsupported type "%s" for property "%s".', $metaData->type, $metaData->property));
if ($property instanceof AbstractProperty) {
$value = $this->getPropertyValue($metaData, $property);
} else {
$value = $this->getDefaultPropertyValue($metaData, $default, $accessor);
}

if (null !== $value) {
$accessor->setValue($parameter, $metaData->property, $value);
}
}

/** @psalm-var T */
/** @phpstan-var T */
return $parameter;
}

Expand All @@ -120,16 +89,14 @@ protected function getAccessor(): PropertyAccessor
*/
protected function getBackEnum(string $type, AbstractProperty $property): ?\BackedEnum
{
if ($this->IsStringEnum($type)) {
if ($this->isStringEnum($type)) {
return $type::tryFrom((string) $property->getValue());
}

return $type::tryFrom($property->getInteger());
}

/**
* Gets the given parameter type from the cache.
*
* @template T of ParameterInterface
*
* @psalm-param class-string<T> $class
Expand All @@ -145,25 +112,30 @@ protected function getCachedParameter(string $class, ?ParameterInterface $defaul
);
}

/**
* @psalm-return TValue
*/
protected function getDefaultPropertyValue(
PropertyAccessor $accessor,
MetaData $metaData,
?ParameterInterface $parameter,
MetaData $metaData
PropertyAccessor $accessor
): mixed {
if (!$parameter instanceof ParameterInterface) {
return $metaData->default;
if ($parameter instanceof ParameterInterface) {
/** @psalm-var TValue */
return $accessor->getValue($parameter, $metaData->property);
}

return $accessor->getValue($parameter, $metaData->property);
/** @psalm-var TValue */
return $metaData->default;
}

/**
* @psalm-param class-string<EntityInterface> $type
*/
protected function getEntity(string $type, int $value): ?EntityInterface
protected function getEntity(string $type, AbstractProperty $property): ?EntityInterface
{
/** psalm-var ?EntityInterface */
return $this->manager->getRepository($type)->find($value);
return $this->manager->getRepository($type)
->find($property->getInteger());
}

/**
Expand Down Expand Up @@ -194,6 +166,18 @@ protected function getMetaDatas(ParameterInterface $parameter): array
return $metaDatas;
}

/**
* @psalm-return TValue
*/
protected function getParameterPropertyValue(
MetaData $metaData,
ParameterInterface $parameter,
PropertyAccessor $accessor
): mixed {
/** @psalm-var TValue */
return $accessor->getValue($parameter, $metaData->property);
}

/**
* @return \ReflectionProperty[]
*/
Expand All @@ -205,33 +189,42 @@ protected function getProperties(ParameterInterface $parameter): array
}

/**
* @psalm-return class-string<TProperty>
* @psalm-return TValue
*/
abstract protected function getPropertyClass(): string;
protected function getPropertyValue(MetaData $metaData, AbstractProperty $property): mixed
{
return match (true) {
'array' === $metaData->type => $property->getArray(),
'bool' === $metaData->type => $property->getBoolean(),
'float' === $metaData->type => $property->getFloat(),
'int' === $metaData->type => $property->getInteger(),
'string' === $metaData->type => $property->getValue(),
'DateTimeInterface' === $metaData->type => $property->getDate(),
$metaData->isBackedEnumType() => $this->getBackEnum($metaData->type, $property),
$metaData->isEntityInterfaceType() => $this->getEntity($metaData->type, $property),
default => throw new \LogicException(\sprintf('Unsupported type "%s" for property "%s".', $metaData->type, $metaData->property))
};
}

/**
* @return AbstractRepository<TProperty>
*/
abstract protected function getRepository(): AbstractRepository;

/**
* @param class-string<\BackedEnum> $type
*/
protected function IsStringEnum(string $type): bool
protected function isStringEnum(string $type): bool
{
try {
$enum = new \ReflectionEnum($type);
if (!$enum->isBacked()) {
throw new \LogicException(\sprintf('Type "%s" is not a backed enum.', $type));
}

return 'string' === (string) $enum->getBackingType();
} catch (\ReflectionException $e) {
throw new \LogicException(\sprintf('Unable to get enum for type "%s".', $type), $e->getCode(), $e);
}
}

/**
* @template T of ParameterInterface
*
* @psalm-param T|null $parameter
* @psalm-param T|null $default
*/
protected function saveParameter(?ParameterInterface $parameter, ?ParameterInterface $default = null): bool
{
if (!$parameter instanceof ParameterInterface) {
Expand All @@ -240,17 +233,14 @@ protected function saveParameter(?ParameterInterface $parameter, ?ParameterInter

$changed = false;
$accessor = $this->getAccessor();
$repository = $this->getRepository();
$metaDatas = $this->getMetaDatas($parameter);
/** @psalm-var AbstractRepository<TProperty> $repository */
$repository = $this->manager->getRepository($this->getPropertyClass());

try {
foreach ($metaDatas as $metaData) {
$property = $this->findProperty($metaData->name);
/** @psalm-var mixed $value */
$value = $accessor->getValue($parameter, $metaData->property);
/** @psalm-var mixed $defaultValue */
$defaultValue = $this->getDefaultPropertyValue($accessor, $default, $metaData);
$value = $this->getParameterPropertyValue($metaData, $parameter, $accessor);
$defaultValue = $this->getDefaultPropertyValue($metaData, $default, $accessor);
if ($value === $defaultValue || Parameter::isDefaultValue($parameter, $metaData->property, $value)) {
if ($property instanceof AbstractProperty) {
$repository->remove($property, false);
Expand All @@ -268,7 +258,7 @@ protected function saveParameter(?ParameterInterface $parameter, ?ParameterInter
}

if ($changed) {
// $repository->flush();
$repository->flush();
$this->cache->delete($parameter::getCacheKey());
}

Expand All @@ -277,4 +267,20 @@ protected function saveParameter(?ParameterInterface $parameter, ?ParameterInter
throw new \LogicException(\sprintf('Unable to save parameter "%s".', \get_debug_type($parameter)), $e->getCode(), $e);
}
}

/**
* @param array<?ParameterInterface> $parameters
* @param array<?ParameterInterface> $defaults
*/
protected function saveParameters(array $parameters, array $defaults = []): bool
{
$saved = false;
for ($i = 0, $count = \count($parameters); $i < $count; ++$i) {
if ($this->saveParameter($parameters[$i], $defaults[$i] ?? null)) {
$saved = true;
}
}

return $saved;
}
}
Loading

0 comments on commit 2c1b35c

Please sign in to comment.