From 81ed21bba8b8659755eac455e99d9c8279fe0e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AF=D1=86=D0=B5=D0=BD=D0=BA=D0=BE=20=D0=90=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D0=B9?= Date: Tue, 12 Apr 2022 21:49:10 +0300 Subject: [PATCH 001/100] Refactore: structure --- src/ClassTransformer.php | 137 +------------- src/DTO/Property.php | 77 ++++++++ src/Exceptions/InvalidArgumentException.php | 7 + src/Exceptions/ValueNotFoundException.php | 7 + src/PropertyHelper.php | 38 ++++ src/PropertyTransformer.php | 196 ++++++++++++++++++++ tests/DTO/UserDTO.php | 2 +- tests/SetInvalidDataTest.php | 34 ++++ 8 files changed, 365 insertions(+), 133 deletions(-) create mode 100644 src/DTO/Property.php create mode 100644 src/Exceptions/InvalidArgumentException.php create mode 100644 src/Exceptions/ValueNotFoundException.php create mode 100644 src/PropertyHelper.php create mode 100644 src/PropertyTransformer.php create mode 100644 tests/SetInvalidDataTest.php diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 58b4563..b384b94 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -2,6 +2,7 @@ namespace ClassTransformer; +use ClassTransformer\DTO\Property; use ReflectionClass; use ReflectionException; use ReflectionNamedType; @@ -95,147 +96,19 @@ private static function extractArrayConverting(array $className, $args): array */ private static function dataConverting(string $className, ...$args) { - // arguments transferred as named arguments (for php8) - $isNamedArguments = count(func_get_args()) === 1; - if (!class_exists($className)) { throw new ClassNotFoundException("Class $className not found. Please check the class path you specified."); } - $refInstance = new ReflectionClass($className); - - if (empty($args)) { - return new $className(); - } - - // if exist custom transform method if (method_exists($className, 'transform')) { - if ($isNamedArguments) { - return $className::transform(...$args); - } - return $className::transform($args[0]); - } - - // if dynamic arguments, named ones lie immediately in the root, if they were passed as an array, then they need to be unpacked - $inArgs = $isNamedArguments ? $args : $args[0]; - - if (is_object($inArgs)) { - $inArgs = (array)$inArgs; - } - $inArgs ??= []; - - $instance = new $className(); - foreach ($refInstance->getProperties() as $item) { - $property = $refInstance->getProperty($item->name); - $propertyType = $property->getType(); - $propertyClassTypeName = self::getPropertyTypes($propertyType); - - if (array_key_exists($item->name, $inArgs)) { - $value = $inArgs[$item->name]; - } else { - $writingStyle = $item->getAttributes(WritingStyle::class); - - if (empty($writingStyle)) { - continue; - } - foreach ($writingStyle as $style) { - $styles = $style->getArguments(); - if ((in_array(WritingStyle::STYLE_SNAKE_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) && array_key_exists(WritingStyleUtil::strToSnakeCase($item->name), $inArgs)) { - $value = $inArgs[WritingStyleUtil::strToSnakeCase($item->name)]; - break; - } - if ((in_array(WritingStyle::STYLE_CAMEL_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) && array_key_exists(WritingStyleUtil::strToCamelCase($item->name), $inArgs)) { - $value = $inArgs[WritingStyleUtil::strToCamelCase($item->name)]; - break; - } - } - - if (!isset($value)) { - continue; - } - } - - if (self::propertyIsScalar($propertyClassTypeName)) { - $instance->{$item->name} = $value; - continue; - } - - if (in_array('array', $propertyClassTypeName, true)) { - // ConvertArray - $arrayTypeAttr = $item->getAttributes(ConvertArray::class); - if (!empty($arrayTypeAttr)) { - $arrayType = $arrayTypeAttr[0]->getArguments()[0]; - } else { - $arrayType = self::getClassFromPhpDoc($property->getDocComment()); - } - - if (!empty($arrayType) && !empty($value) && !self::propertyIsScalar($arrayType)) { - foreach ($value as $el) { - /** @phpstan-ignore-next-line */ - $instance->{$item->name}[] = self::dataConverting($arrayType, $el); - } - continue; - } - - $instance->{$item->name} = $value; - continue; - } - - if ($propertyType instanceof ReflectionNamedType) { - /** @phpstan-ignore-next-line */ - $instance->{$item->name} = self::dataConverting($propertyType->getName(), $value); - continue; - } - $instance->{$item->name} = $value; + return $className::transform(...$args); } - return $instance; - } - /** - * @param array|string $type - * - * @return bool - */ - private static function propertyIsScalar(array|string $type): bool - { - if (is_array($type)) { - return count(array_intersect($type, ['int', 'float', 'string', 'bool', 'mixed'])) > 0; + if (empty($args)) { + return new $className(); } - return in_array($type, ['int', 'float', 'string', 'bool', 'mixed']); + return PropertyTransformer::init($className, ...$args)->transform(); } - /** - * @param string|false $phpDoc - * - * @return string|null - */ - private static function getClassFromPhpDoc($phpDoc): ?string - { - if ($phpDoc) { - preg_match('/array<([a-zA-Z\d\\\]+)>/m', $phpDoc, $arrayType); - return $arrayType[1] ?? null; - } - return null; - } - /** - * @param ReflectionType|null $propertyType - * - * @return array - */ - private static function getPropertyTypes(?ReflectionType $propertyType): array - { - if ($propertyType instanceof ReflectionUnionType) { - return array_map( - static function ($item) { - return $item->getName(); - }, - $propertyType->getTypes() - ); - } - if ($propertyType instanceof ReflectionNamedType) { - return [$propertyType->getName()]; - } - return []; - } } diff --git a/src/DTO/Property.php b/src/DTO/Property.php new file mode 100644 index 0000000..9cc8704 --- /dev/null +++ b/src/DTO/Property.php @@ -0,0 +1,77 @@ + + */ +class Property +{ + public ReflectionProperty $property; + + public function __construct(ReflectionProperty $property) + { + $this->property = $property; + } + + public function getType() + { + return $this->property->getType(); + } + + /** + * @param ReflectionType|null $propertyType + * + * @return array + */ + public function getTypes(): array + { + $types = []; + if ($this->getType() instanceof ReflectionUnionType) { + $types = array_map( + static function ($item) { + return $item->getName(); + }, + $this->getType()->getTypes() + ); + } + if ($this->getType() instanceof ReflectionNamedType) { + $types = [$this->getType()->getName()]; + } + + if ($this->getType()->allowsNull()) { + $types [] = 'null'; + } + return $types; + } + + /** + * @return bool + */ + public function isScalar(): bool + { + return count(array_intersect($this->getTypes(), [ 'int', 'float', 'double', 'string', 'bool', 'mixed'])) > 0; + } + + /** + * @return bool + */ + public function isArray(): bool + { + return in_array('array', $this->getTypes(), true); + } + + /** + */ + public function getDocComment(): bool|string + { + return $this->property->getDocComment(); + } +} diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php new file mode 100644 index 0000000..f368b93 --- /dev/null +++ b/src/Exceptions/InvalidArgumentException.php @@ -0,0 +1,7 @@ + + */ +class PropertyHelper +{ + /** + * @param string|false $phpDoc + * + * @return string|null + */ + public static function getClassFromPhpDoc($phpDoc): ?string + { + if ($phpDoc) { + preg_match('/array<([a-zA-Z\d\\\]+)>/m', $phpDoc, $arrayType); + return $arrayType[1] ?? null; + } + return null; + } + + /** + * @param array|string $type + * + * @return bool + */ + public static function propertyIsScalar(array|string $type): bool + { + if (is_array($type)) { + return count(array_intersect($type, ['int', 'float', 'string', 'bool', 'mixed'])) > 0; + } + return in_array($type, ['int', 'float', 'string', 'bool', 'mixed']); + } +} diff --git a/src/PropertyTransformer.php b/src/PropertyTransformer.php new file mode 100644 index 0000000..84b1f6b --- /dev/null +++ b/src/PropertyTransformer.php @@ -0,0 +1,196 @@ + + */ +class PropertyTransformer +{ + /** class-string $className */ + private string $className; + + /** @var null|array */ + private ?array $args; + + /** @var bool Arguments transfer as named arguments (for php8) */ + private $isNamedArguments; + + /** + * @template T + * + * @param class-string $className + * @param array|object|null $args + * + * @throws ClassNotFoundException + */ + public function __construct(string $className, ...$args) + { + $this->className = $className; + $this->validate(); + + $this->isNamedArguments = count(func_get_args()) === 1; + + // if dynamic arguments, named ones lie immediately in the root, if they were passed as an array, then they need to be unpacked + $inArgs = $this->isNamedArguments ? $args : $args[0]; + + if (is_object($inArgs)) { + $inArgs = (array)$inArgs; + } + $this->args = $inArgs ?? []; + } + + public static function init(string $className, ...$args) + { + return new self($className, ...$args); + } + + private function validate() + { + if (!class_exists($this->className)) { + throw new ClassNotFoundException("Class $this->className not found. Please check the class path you specified."); + } + } + + /** + * @template T + * + * @return T + * @throws ClassNotFoundException|ReflectionException + */ + public function transform() + { + $refInstance = new ReflectionClass($this->className); + + // if exist custom transform method + if (method_exists($this->className, 'transform')) { + return $this->className::transform($this->args); + } + + $instance = new $this->className(); + foreach ($refInstance->getProperties() as $item) { + $property = new Property($refInstance->getProperty($item->name)); + + try { + $value = $this->searchValue($item); + } catch (ValueNotFoundException) { + continue; + } + $this->validateFieldType($property, $value); + + if ($property->isScalar()) { + $instance->{$item->name} = $value; + continue; + } + + if ($property->isArray()) { + $arrayTypeAttr = $item->getAttributes(ConvertArray::class); + if (!empty($arrayTypeAttr)) { + $arrayType = $arrayTypeAttr[0]->getArguments()[0]; + } else { + $arrayType = PropertyHelper::getClassFromPhpDoc($property->getDocComment()); + } + + if (!empty($arrayType) && !empty($value) && !PropertyHelper::propertyIsScalar($arrayType)) { + foreach ($value as $el) { + /** @phpstan-ignore-next-line */ + $instance->{$item->name}[] = self::init($arrayType, $el)->transform(); + } + continue; + } + + $instance->{$item->name} = $value; + continue; + } + + if ($property->getType() instanceof ReflectionNamedType) { + /** @phpstan-ignore-next-line */ + $instance->{$item->name} = self::init($property->getType()->getName(), $value)->transform(); + continue; + } + $instance->{$item->name} = $value; + } + return $instance; + } + + /** + * Check the value for type compliance + * + * @param Property $item + * @param mixed $value + * + * @return bool + */ + private function validateFieldType(Property $item, mixed $value) + { + $clearType = match (gettype($value)) { + 'integer' => 'int', + 'boolean' => 'bool', + 'NULL' => 'null', + default => gettype($value) + }; + $types = $item->getTypes(); + if (in_array('float', $types)) { + $types [] = 'double'; + } + + //TODO: Проверить атрибут приведения типа + if (empty($types)) { + return true; + } + if (!in_array($clearType, $types)) { + $propertyName = $item->property->getName(); + throw new InvalidArgumentException("The \"$propertyName\" property has the wrong type. Expected type (" + . implode("|", $item->getTypes()) . "), received \"" . $clearType . "\""); + } + + return true; + } + + /** + * @param $item + * + * @return mixed|object|void + * @throws ValueNotFoundException + */ + private function searchValue($item) + { + if (array_key_exists($item->name, $this->args)) { + return $this->args[$item->name]; + } + + $writingStyle = $item->getAttributes(WritingStyle::class); + + if (empty($writingStyle)) { + throw new ValueNotFoundException(); + } + foreach ($writingStyle as $style) { + $styles = $style->getArguments(); + if ( + (in_array(WritingStyle::STYLE_SNAKE_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) && + array_key_exists(WritingStyleUtil::strToSnakeCase($item->name), $this->args) + ) { + return $this->args[WritingStyleUtil::strToSnakeCase($item->name)]; + } + if ( + (in_array(WritingStyle::STYLE_CAMEL_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) && + array_key_exists(WritingStyleUtil::strToCamelCase($item->name), $this->args) + ) { + return $this->args[WritingStyleUtil::strToCamelCase($item->name)]; + } + } + throw new ValueNotFoundException(); + } +} diff --git a/tests/DTO/UserDTO.php b/tests/DTO/UserDTO.php index a526db9..f9e1c4d 100644 --- a/tests/DTO/UserDTO.php +++ b/tests/DTO/UserDTO.php @@ -6,7 +6,7 @@ class UserDTO { public int $id; - public string $email; + public ?string $email; public float $balance; public mixed $mixed; } diff --git a/tests/SetInvalidDataTest.php b/tests/SetInvalidDataTest.php new file mode 100644 index 0000000..fe0dea2 --- /dev/null +++ b/tests/SetInvalidDataTest.php @@ -0,0 +1,34 @@ +expectException(InvalidArgumentException::class); + $data = [ + 'id' => 1, + 'email' => 7, + 'balance' => 128.41 + ]; + ClassTransformer::transform(UserDTO::class, $data); + } + +} From be1ca94326e36fdaec6691545f2592019d632a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AF=D1=86=D0=B5=D0=BD=D0=BA=D0=BE=20=D0=90=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D0=B9?= Date: Wed, 13 Apr 2022 13:11:39 +0300 Subject: [PATCH 002/100] Fix: style --- coverage.xml | 187 +++++++++++++++++++++++++++++++++++ src/ClassTransformer.php | 2 - src/DTO/Property.php | 14 ++- src/PropertyHelper.php | 4 +- src/PropertyTransformer.php | 49 +++++---- tests/SetInvalidDataTest.php | 4 +- 6 files changed, 232 insertions(+), 28 deletions(-) create mode 100644 coverage.xml diff --git a/coverage.xml b/coverage.xml new file mode 100644 index 0000000..4b7db6c --- /dev/null +++ b/coverage.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index b384b94..2f34b07 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -109,6 +109,4 @@ private static function dataConverting(string $className, ...$args) } return PropertyTransformer::init($className, ...$args)->transform(); } - - } diff --git a/src/DTO/Property.php b/src/DTO/Property.php index 9cc8704..2aeffea 100644 --- a/src/DTO/Property.php +++ b/src/DTO/Property.php @@ -16,19 +16,23 @@ class Property { public ReflectionProperty $property; + /** + * @param ReflectionProperty $property + */ public function __construct(ReflectionProperty $property) { $this->property = $property; } + /** + * @return ReflectionType|null + */ public function getType() { return $this->property->getType(); } /** - * @param ReflectionType|null $propertyType - * * @return array */ public function getTypes(): array @@ -45,8 +49,8 @@ static function ($item) { if ($this->getType() instanceof ReflectionNamedType) { $types = [$this->getType()->getName()]; } - - if ($this->getType()->allowsNull()) { + + if ($this->getType() !== null && $this->getType()->allowsNull()) { $types [] = 'null'; } return $types; @@ -57,7 +61,7 @@ static function ($item) { */ public function isScalar(): bool { - return count(array_intersect($this->getTypes(), [ 'int', 'float', 'double', 'string', 'bool', 'mixed'])) > 0; + return count(array_intersect($this->getTypes(), ['int', 'float', 'double', 'string', 'bool', 'mixed'])) > 0; } /** diff --git a/src/PropertyHelper.php b/src/PropertyHelper.php index 4de5b92..cde7b15 100644 --- a/src/PropertyHelper.php +++ b/src/PropertyHelper.php @@ -10,13 +10,13 @@ class PropertyHelper { /** - * @param string|false $phpDoc + * @param string|bool $phpDoc * * @return string|null */ public static function getClassFromPhpDoc($phpDoc): ?string { - if ($phpDoc) { + if (is_string($phpDoc)) { preg_match('/array<([a-zA-Z\d\\\]+)>/m', $phpDoc, $arrayType); return $arrayType[1] ?? null; } diff --git a/src/PropertyTransformer.php b/src/PropertyTransformer.php index 84b1f6b..967cbc7 100644 --- a/src/PropertyTransformer.php +++ b/src/PropertyTransformer.php @@ -11,6 +11,7 @@ use ReflectionClass; use ReflectionException; use ReflectionNamedType; +use ReflectionProperty; /** * Class ClassTransformerService @@ -19,11 +20,14 @@ */ class PropertyTransformer { - /** class-string $className */ + /** + * @template T + * class-string $className + */ private string $className; - /** @var null|array */ - private ?array $args; + /** @var array */ + private array $args; /** @var bool Arguments transfer as named arguments (for php8) */ private $isNamedArguments; @@ -31,7 +35,7 @@ class PropertyTransformer /** * @template T * - * @param class-string $className + * @param string|class-string $className * @param array|object|null $args * * @throws ClassNotFoundException @@ -52,11 +56,24 @@ public function __construct(string $className, ...$args) $this->args = $inArgs ?? []; } - public static function init(string $className, ...$args) + /** + * @template T + * + * @param string|class-string $className + * @param array|object|null $args + * + * @return PropertyTransformer + * @throws ClassNotFoundException + */ + public static function init(string $className, ...$args): PropertyTransformer { return new self($className, ...$args); } + /** + * @return void + * @throws ClassNotFoundException + */ private function validate() { if (!class_exists($this->className)) { @@ -72,13 +89,16 @@ private function validate() */ public function transform() { + /** @phpstan-ignore-next-line */ $refInstance = new ReflectionClass($this->className); // if exist custom transform method if (method_exists($this->className, 'transform')) { + /** @phpstan-ignore-next-line */ return $this->className::transform($this->args); } + /** @var T $instance */ $instance = new $this->className(); foreach ($refInstance->getProperties() as $item) { $property = new Property($refInstance->getProperty($item->name)); @@ -88,7 +108,7 @@ public function transform() } catch (ValueNotFoundException) { continue; } - $this->validateFieldType($property, $value); + //$this->validateFieldType($property, $value); if ($property->isScalar()) { $instance->{$item->name} = $value; @@ -105,7 +125,6 @@ public function transform() if (!empty($arrayType) && !empty($value) && !PropertyHelper::propertyIsScalar($arrayType)) { foreach ($value as $el) { - /** @phpstan-ignore-next-line */ $instance->{$item->name}[] = self::init($arrayType, $el)->transform(); } continue; @@ -116,7 +135,6 @@ public function transform() } if ($property->getType() instanceof ReflectionNamedType) { - /** @phpstan-ignore-next-line */ $instance->{$item->name} = self::init($property->getType()->getName(), $value)->transform(); continue; } @@ -131,9 +149,9 @@ public function transform() * @param Property $item * @param mixed $value * - * @return bool + * @return void */ - private function validateFieldType(Property $item, mixed $value) + /*private function validateFieldType(Property $item, mixed $value): void { $clearType = match (gettype($value)) { 'integer' => 'int', @@ -146,26 +164,23 @@ private function validateFieldType(Property $item, mixed $value) $types [] = 'double'; } - //TODO: Проверить атрибут приведения типа if (empty($types)) { - return true; + return; } if (!in_array($clearType, $types)) { $propertyName = $item->property->getName(); throw new InvalidArgumentException("The \"$propertyName\" property has the wrong type. Expected type (" . implode("|", $item->getTypes()) . "), received \"" . $clearType . "\""); } - - return true; - } + }*/ /** - * @param $item + * @param ReflectionProperty $item * * @return mixed|object|void * @throws ValueNotFoundException */ - private function searchValue($item) + private function searchValue(ReflectionProperty $item) { if (array_key_exists($item->name, $this->args)) { return $this->args[$item->name]; diff --git a/tests/SetInvalidDataTest.php b/tests/SetInvalidDataTest.php index fe0dea2..3e04c8d 100644 --- a/tests/SetInvalidDataTest.php +++ b/tests/SetInvalidDataTest.php @@ -20,7 +20,7 @@ class SetInvalidDataTest extends TestCase /** * @throws ReflectionException|InvalidArgumentException */ - public function testBaseArray(): void + /*public function testBaseArray(): void { $this->expectException(InvalidArgumentException::class); $data = [ @@ -29,6 +29,6 @@ public function testBaseArray(): void 'balance' => 128.41 ]; ClassTransformer::transform(UserDTO::class, $data); - } + }*/ } From 64d4f0ed630bc6db369c3a1cf6ac7806d48718c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AF=D1=86=D0=B5=D0=BD=D0=BA=D0=BE=20=D0=90=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D0=B9?= Date: Wed, 11 May 2022 14:14:20 +0300 Subject: [PATCH 003/100] clear code --- src/PropertyTransformer.php | 55 +++++++------------------------------ 1 file changed, 10 insertions(+), 45 deletions(-) diff --git a/src/PropertyTransformer.php b/src/PropertyTransformer.php index 967cbc7..8743b3c 100644 --- a/src/PropertyTransformer.php +++ b/src/PropertyTransformer.php @@ -2,16 +2,15 @@ namespace ClassTransformer; +use ReflectionClass; +use ReflectionProperty; +use ReflectionException; +use ReflectionNamedType; +use ClassTransformer\DTO\Property; use ClassTransformer\Attributes\ConvertArray; use ClassTransformer\Attributes\WritingStyle; -use ClassTransformer\DTO\Property; use ClassTransformer\Exceptions\ClassNotFoundException; -use ClassTransformer\Exceptions\InvalidArgumentException; use ClassTransformer\Exceptions\ValueNotFoundException; -use ReflectionClass; -use ReflectionException; -use ReflectionNamedType; -use ReflectionProperty; /** * Class ClassTransformerService @@ -29,9 +28,6 @@ class PropertyTransformer /** @var array */ private array $args; - /** @var bool Arguments transfer as named arguments (for php8) */ - private $isNamedArguments; - /** * @template T * @@ -45,10 +41,9 @@ public function __construct(string $className, ...$args) $this->className = $className; $this->validate(); - $this->isNamedArguments = count(func_get_args()) === 1; - + // Arguments transfer as named arguments (for php8) // if dynamic arguments, named ones lie immediately in the root, if they were passed as an array, then they need to be unpacked - $inArgs = $this->isNamedArguments ? $args : $args[0]; + $inArgs = count(func_get_args()) === 1 ? $args : $args[0]; if (is_object($inArgs)) { $inArgs = (array)$inArgs; @@ -100,15 +95,15 @@ public function transform() /** @var T $instance */ $instance = new $this->className(); + foreach ($refInstance->getProperties() as $item) { $property = new Property($refInstance->getProperty($item->name)); try { - $value = $this->searchValue($item); + $value = $this->getValue($item); } catch (ValueNotFoundException) { continue; } - //$this->validateFieldType($property, $value); if ($property->isScalar()) { $instance->{$item->name} = $value; @@ -143,36 +138,6 @@ public function transform() return $instance; } - /** - * Check the value for type compliance - * - * @param Property $item - * @param mixed $value - * - * @return void - */ - /*private function validateFieldType(Property $item, mixed $value): void - { - $clearType = match (gettype($value)) { - 'integer' => 'int', - 'boolean' => 'bool', - 'NULL' => 'null', - default => gettype($value) - }; - $types = $item->getTypes(); - if (in_array('float', $types)) { - $types [] = 'double'; - } - - if (empty($types)) { - return; - } - if (!in_array($clearType, $types)) { - $propertyName = $item->property->getName(); - throw new InvalidArgumentException("The \"$propertyName\" property has the wrong type. Expected type (" - . implode("|", $item->getTypes()) . "), received \"" . $clearType . "\""); - } - }*/ /** * @param ReflectionProperty $item @@ -180,7 +145,7 @@ public function transform() * @return mixed|object|void * @throws ValueNotFoundException */ - private function searchValue(ReflectionProperty $item) + private function getValue(ReflectionProperty $item) { if (array_key_exists($item->name, $this->args)) { return $this->args[$item->name]; From 306890a354beb7c860988bfe0811cf43a592da56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AF=D1=86=D0=B5=D0=BD=D0=BA=D0=BE=20=D0=90=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D0=B9?= Date: Wed, 11 May 2022 14:43:46 +0300 Subject: [PATCH 004/100] (Issues-4) Add: attribute NotTransform to exclude transformation --- src/Attributes/NotTransform.php | 10 +++++++ src/DTO/Property.php | 28 +++++++++++++++++++ src/PropertyTransformer.php | 3 +- tests/DTO/UserNotTransformDTO.php | 15 ++++++++++ tests/DTO/UserNotTransformRelationDTO.php | 13 +++++++++ tests/NotTransformTest.php | 34 +++++++++++++++++++++++ tests/SetInvalidDataTest.php | 34 ----------------------- 7 files changed, 102 insertions(+), 35 deletions(-) create mode 100644 src/Attributes/NotTransform.php create mode 100644 tests/DTO/UserNotTransformDTO.php create mode 100644 tests/DTO/UserNotTransformRelationDTO.php create mode 100644 tests/NotTransformTest.php delete mode 100644 tests/SetInvalidDataTest.php diff --git a/src/Attributes/NotTransform.php b/src/Attributes/NotTransform.php new file mode 100644 index 0000000..7c71b8b --- /dev/null +++ b/src/Attributes/NotTransform.php @@ -0,0 +1,10 @@ +property->getDocComment(); } + + /** + * @param string|null $name + * + * @return bool + */ + public function existsAttribute(?string $name = null): bool + { + return $this->getAttributes($name) !== null; + } + + /** + * @param string|null $name + * + * @return \ReflectionAttribute[]|null + */ + public function getAttributes(?string $name = null) + { + $attr = $this->property->getAttributes($name); + if (!empty($attr)) { + return $attr; + } + return null; + } } diff --git a/src/PropertyTransformer.php b/src/PropertyTransformer.php index 8743b3c..7bd98fd 100644 --- a/src/PropertyTransformer.php +++ b/src/PropertyTransformer.php @@ -2,6 +2,7 @@ namespace ClassTransformer; +use ClassTransformer\Attributes\NotTransform; use ReflectionClass; use ReflectionProperty; use ReflectionException; @@ -105,7 +106,7 @@ public function transform() continue; } - if ($property->isScalar()) { + if ($property->isScalar() || $property->existsAttribute(NotTransform::class)) { $instance->{$item->name} = $value; continue; } diff --git a/tests/DTO/UserNotTransformDTO.php b/tests/DTO/UserNotTransformDTO.php new file mode 100644 index 0000000..1903195 --- /dev/null +++ b/tests/DTO/UserNotTransformDTO.php @@ -0,0 +1,15 @@ + 'corey', + 'relation' => new UserNotTransformRelationDTO(), + ]; + $model = ClassTransformer::transform(UserNotTransformDTO::class, $data); + $this->assertTrue(true); + } + +} diff --git a/tests/SetInvalidDataTest.php b/tests/SetInvalidDataTest.php deleted file mode 100644 index 3e04c8d..0000000 --- a/tests/SetInvalidDataTest.php +++ /dev/null @@ -1,34 +0,0 @@ -expectException(InvalidArgumentException::class); - $data = [ - 'id' => 1, - 'email' => 7, - 'balance' => 128.41 - ]; - ClassTransformer::transform(UserDTO::class, $data); - }*/ - -} From e6b86b2ac15bda285ad8a4a1b70e9a7c3b3ad7c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AF=D1=86=D0=B5=D0=BD=D0=BA=D0=BE=20=D0=90=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=D0=B5=D0=B9?= Date: Fri, 10 Jun 2022 21:22:54 +0300 Subject: [PATCH 005/100] upd version --- composer.json | 2 +- composer.lock | 168 +++++++++++-------------------------------- src/DTO/Property.php | 9 ++- 3 files changed, 48 insertions(+), 131 deletions(-) diff --git a/composer.json b/composer.json index f0eac8a..83f887e 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "1.1.3", + "version": "1.2", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index 8504cc1..9693c21 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dc7634fd776110304335e00357e2a312", + "content-hash": "fd77d0516562a2d4a512e59d57c36828", "packages": [], "packages-dev": [ { @@ -261,16 +261,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.13.2", + "version": "v4.14.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "210577fe3cf7badcc5814d99455df46564f3c077" + "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", - "reference": "210577fe3cf7badcc5814d99455df46564f3c077", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", + "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", "shasum": "" }, "require": { @@ -311,9 +311,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" }, - "time": "2021-11-30T19:35:32+00:00" + "time": "2022-05-31T20:59:12+00:00" }, { "name": "phar-io/manifest", @@ -538,16 +538,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" + "reference": "77a32518733312af16a44300404e945338981de3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", "shasum": "" }, "require": { @@ -582,9 +582,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" }, - "time": "2022-01-04T19:58:01+00:00" + "time": "2022-03-15T21:29:03+00:00" }, { "name": "phpspec/prophecy", @@ -1037,16 +1037,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.18", + "version": "9.5.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1b5856028273bfd855e60a887278857d872ec67a" + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b5856028273bfd855e60a887278857d872ec67a", - "reference": "1b5856028273bfd855e60a887278857d872ec67a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", "shasum": "" }, "require": { @@ -1076,7 +1076,7 @@ "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^2.3.4", + "sebastian/type": "^3.0", "sebastian/version": "^3.0.2" }, "require-dev": { @@ -1124,7 +1124,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.18" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" }, "funding": [ { @@ -1136,7 +1136,7 @@ "type": "github" } ], - "time": "2022-03-08T06:52:28+00:00" + "time": "2022-04-01T12:37:26+00:00" }, { "name": "sebastian/cli-parser", @@ -1504,16 +1504,16 @@ }, { "name": "sebastian/environment", - "version": "5.1.3", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", "shasum": "" }, "require": { @@ -1555,7 +1555,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" }, "funding": [ { @@ -1563,7 +1563,7 @@ "type": "github" } ], - "time": "2020-09-28T05:52:38+00:00" + "time": "2022-04-03T09:37:03+00:00" }, { "name": "sebastian/exporter", @@ -1995,28 +1995,28 @@ }, { "name": "sebastian/type", - "version": "2.3.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" + "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2039,7 +2039,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + "source": "https://github.com/sebastianbergmann/type/tree/3.0.0" }, "funding": [ { @@ -2047,7 +2047,7 @@ "type": "github" } ], - "time": "2021-06-15T12:49:02+00:00" + "time": "2022-03-15T09:54:48+00:00" }, { "name": "sebastian/version", @@ -2158,88 +2158,6 @@ }, "time": "2021-12-12T21:44:58+00:00" }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.25.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "30885182c981ab175d4d034db0f6f469898070ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", - "reference": "30885182c981ab175d4d034db0f6f469898070ab", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-10-20T20:35:02+00:00" - }, { "name": "theseer/tokenizer", "version": "1.2.1", @@ -2292,21 +2210,21 @@ }, { "name": "webmozart/assert", - "version": "1.10.0", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "symfony/polyfill-ctype": "^1.8" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" }, "conflict": { "phpstan/phpstan": "<0.12.20", @@ -2344,9 +2262,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.10.0" + "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, - "time": "2021-03-09T10:59:23+00:00" + "time": "2022-06-03T18:03:27+00:00" } ], "aliases": [], @@ -2358,5 +2276,5 @@ "php": "^8.0" }, "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/src/DTO/Property.php b/src/DTO/Property.php index 7645c69..3b13709 100644 --- a/src/DTO/Property.php +++ b/src/DTO/Property.php @@ -2,10 +2,9 @@ namespace ClassTransformer\DTO; -use ClassTransformer\Attributes\ConvertArray; -use ReflectionNamedType; -use ReflectionProperty; use ReflectionType; +use ReflectionProperty; +use ReflectionNamedType; use ReflectionUnionType; /** @@ -96,9 +95,9 @@ public function existsAttribute(?string $name = null): bool /** * @param string|null $name * - * @return \ReflectionAttribute[]|null + * @return null|array */ - public function getAttributes(?string $name = null) + public function getAttributes(?string $name = null): ?array { $attr = $this->property->getAttributes($name); if (!empty($attr)) { From 482b2a8e6d1c63afd07ca17bb01b2041971e47df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Dec 2022 16:05:36 +0300 Subject: [PATCH 006/100] mini fix --- .gitattributes | 6 + .gitignore | 8 +- composer.json | 5 +- composer.lock | 1891 +++++++++++++++++++++++++++++------ phpbench.json | 4 + phpstan.neon | 2 +- src/ClassTransformer.php | 13 +- src/DTO/Property.php | 8 +- src/PropertyHelper.php | 10 +- src/PropertyTransformer.php | 13 +- src/WritingStyleUtil.php | 10 +- 11 files changed, 1611 insertions(+), 359 deletions(-) create mode 100644 .gitattributes create mode 100644 phpbench.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..940a431 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +tests/ export-ignore +phpunit.xml export-ignore +phpbench.json export-ignore +phpstan.xml export-ignore +coverage.xml export-ignore +phpstan.neon export-ignore diff --git a/.gitignore b/.gitignore index 260e91d..3bb351a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,5 @@ +.DS_Store .idea vendor -/.phpunit.result.cache - -.phpunit.cache/code-coverage/ - +.phpunit.result.cache .phpunit.cache/ - -.DS_Store diff --git a/composer.json b/composer.json index 83f887e..4cecd9a 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "1.2", + "version": "1.3", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, @@ -33,8 +33,9 @@ "php": "^8.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.82", "mockery/mockery": "^1.0", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "*" }, diff --git a/composer.lock b/composer.lock index 9693c21..3934508 100644 --- a/composer.lock +++ b/composer.lock @@ -4,9 +4,82 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fd77d0516562a2d4a512e59d57c36828", + "content-hash": "60f864d776d355a77ff8840e0b42f9e6", "packages": [], "packages-dev": [ + { + "name": "doctrine/annotations", + "version": "1.13.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "648b0343343565c4a056bfc8392201385e8d89f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/648b0343343565c4a056bfc8392201385e8d89f0", + "reference": "648b0343343565c4a056bfc8392201385e8d89f0", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^1.4.10 || ^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", + "symfony/cache": "^4.4 || ^5.2", + "vimeo/psalm": "^4.10" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.13.3" + }, + "time": "2022-07-02T10:48:51+00:00" + }, { "name": "doctrine/instantiator", "version": "1.4.1", @@ -77,6 +150,82 @@ ], "time": "2022-03-03T08:28:38+00:00" }, + { + "name": "doctrine/lexer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2022-02-28T11:07:21+00:00" + }, { "name": "hamcrest/hamcrest-php", "version": "v2.0.1", @@ -130,16 +279,16 @@ }, { "name": "mockery/mockery", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac" + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", - "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", + "url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e", + "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e", "shasum": "" }, "require": { @@ -196,9 +345,9 @@ ], "support": { "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.5.0" + "source": "https://github.com/mockery/mockery/tree/1.5.1" }, - "time": "2022-01-20T13:18:17+00:00" + "time": "2022-09-07T15:32:08+00:00" }, { "name": "myclabs/deep-copy", @@ -261,16 +410,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.14.0", + "version": "v4.15.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" + "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", + "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", "shasum": "" }, "require": { @@ -311,9 +460,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1" }, - "time": "2022-05-31T20:59:12+00:00" + "time": "2022-09-04T07:30:47+00:00" }, { "name": "phar-io/manifest", @@ -427,31 +576,37 @@ "time": "2022-02-21T01:04:05+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "phpbench/container", + "version": "2.2.1", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/phpbench/container.git", + "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/phpbench/container/zipball/6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", + "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "psr/container": "^1.0|^2.0", + "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8" }, "type": "library", "extra": { "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src/" + "PhpBench\\DependencyInjection\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -460,59 +615,49 @@ ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Daniel Leech", + "email": "daniel@dantleech.com" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], + "description": "Simple, configurable, service container.", "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "issues": "https://github.com/phpbench/container/issues", + "source": "https://github.com/phpbench/container/tree/2.2.1" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2022-01-25T10:17:35+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "name": "phpbench/dom", + "version": "0.3.2", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "url": "https://github.com/phpbench/dom.git", + "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/phpbench/dom/zipball/b013b717832ddbaadf2a40984b04bc66af9a7110", + "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110", "shasum": "" }, "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" + "ext-dom": "*", + "php": "^7.2||^8.0" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "friendsofphp/php-cs-fixer": "^2.18", + "phpstan/phpstan": "^0.12.83", + "phpunit/phpunit": "^8.0||^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "PhpBench\\Dom\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -521,52 +666,82 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "name": "Daniel Leech", + "email": "daniel@dantleech.com" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "description": "DOM wrapper to simplify working with the PHP DOM implementation", "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "issues": "https://github.com/phpbench/dom/issues", + "source": "https://github.com/phpbench/dom/tree/0.3.2" }, - "time": "2021-10-19T17:43:47+00:00" + "time": "2021-09-24T15:26:07+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "1.6.1", + "name": "phpbench/phpbench", + "version": "1.2.7", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" + "url": "https://github.com/phpbench/phpbench.git", + "reference": "dce145304abbb16c8d9af69c19d96f47e9d0e670" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/dce145304abbb16c8d9af69c19d96f47e9d0e670", + "reference": "dce145304abbb16c8d9af69c19d96f47e9d0e670", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" + "doctrine/annotations": "^1.13", + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "ext-tokenizer": "*", + "php": "^7.3 || ^8.0", + "phpbench/container": "^2.1", + "phpbench/dom": "~0.3.1", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "seld/jsonlint": "^1.1", + "symfony/console": "^4.2 || ^5.0 || ^6.0", + "symfony/filesystem": "^4.2 || ^5.0 || ^6.0", + "symfony/finder": "^4.2 || ^5.0 || ^6.0", + "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0", + "symfony/process": "^4.2 || ^5.0 || ^6.0", + "webmozart/glob": "^4.6" }, "require-dev": { - "ext-tokenizer": "*", - "psalm/phar": "^4.8" + "dantleech/invoke": "^2.0", + "friendsofphp/php-cs-fixer": "^3.0", + "jangregor/phpstan-prophecy": "^1.0", + "phpspec/prophecy": "^1.12", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^8.5.8 || ^9.0", + "symfony/error-handler": "^5.2 || ^6.0", + "symfony/var-dumper": "^4.0 || ^5.0 || ^6.0" }, + "suggest": { + "ext-xdebug": "For Xdebug profiling extension." + }, + "bin": [ + "bin/phpbench" + ], "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev" + "dev-master": "1.2-dev" } }, "autoload": { + "files": [ + "lib/Report/Func/functions.php" + ], "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "PhpBench\\": "lib/", + "PhpBench\\Extensions\\XDebug\\": "extensions/xdebug/lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -575,100 +750,39 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Daniel Leech", + "email": "daniel@dantleech.com" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "description": "PHP Benchmarking Framework", "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" - }, - "time": "2022-03-15T21:29:03+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } + "issues": "https://github.com/phpbench/phpbench/issues", + "source": "https://github.com/phpbench/phpbench/tree/1.2.7" }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, + "funding": [ { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "url": "https://github.com/dantleech", + "type": "github" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" - }, - "time": "2021-12-08T12:19:24+00:00" + "time": "2022-10-15T09:57:51+00:00" }, { "name": "phpstan/phpstan", - "version": "0.12.99", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" + "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", + "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", "shasum": "" }, "require": { - "php": "^7.1|^8.0" + "php": "^7.2|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -678,11 +792,6 @@ "phpstan.phar" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.12-dev" - } - }, "autoload": { "files": [ "bootstrap.php" @@ -693,9 +802,13 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.99" + "source": "https://github.com/phpstan/phpstan/tree/1.9.1" }, "funding": [ { @@ -706,36 +819,32 @@ "url": "https://github.com/phpstan", "type": "github" }, - { - "url": "https://www.patreon.com/phpstan", - "type": "patreon" - }, { "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", "type": "tidelift" } ], - "time": "2021-09-12T20:09:55+00:00" + "time": "2022-11-04T13:35:59+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.15", + "version": "9.2.18", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" + "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/12fddc491826940cf9b7e88ad9664cf51f0f6d0a", + "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.13.0", + "nikic/php-parser": "^4.14", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -784,7 +893,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.18" }, "funding": [ { @@ -792,7 +901,7 @@ "type": "github" } ], - "time": "2022-03-07T09:28:20+00:00" + "time": "2022-10-27T13:35:33+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1037,16 +1146,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.20", + "version": "9.5.26", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" + "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", - "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2", + "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2", "shasum": "" }, "require": { @@ -1061,7 +1170,6 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", "phpunit/php-code-coverage": "^9.2.13", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", @@ -1069,20 +1177,16 @@ "phpunit/php-timer": "^5.0.2", "sebastian/cli-parser": "^1.0.1", "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", + "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.3", "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", + "sebastian/exporter": "^4.0.5", "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", + "sebastian/type": "^3.2", "sebastian/version": "^3.0.2" }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, "suggest": { "ext-soap": "*", "ext-xdebug": "*" @@ -1124,7 +1228,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26" }, "funding": [ { @@ -1134,164 +1238,320 @@ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2022-04-01T12:37:26+00:00" + "time": "2022-10-28T06:00:21+00:00" }, { - "name": "sebastian/cli-parser", - "version": "1.0.1", + "name": "psr/cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Cache\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { - "name": "sebastian/code-unit", - "version": "1.0.8", + "name": "psr/container", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "php": ">=7.4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Container\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "name": "psr/log", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Log\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Looks up which function or method a line of code belongs to", + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", @@ -1307,16 +1567,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -1369,7 +1629,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { @@ -1377,7 +1637,7 @@ "type": "github" } ], - "time": "2020-10-26T15:49:45+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", @@ -1567,16 +1827,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "shasum": "" }, "require": { @@ -1632,7 +1892,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" }, "funding": [ { @@ -1640,7 +1900,7 @@ "type": "github" } ], - "time": "2021-11-11T14:18:36+00:00" + "time": "2022-09-14T06:03:37+00:00" }, { "name": "sebastian/global-state", @@ -1995,16 +2255,16 @@ }, { "name": "sebastian/type", - "version": "3.0.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", "shasum": "" }, "require": { @@ -2016,7 +2276,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2039,7 +2299,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.0.0" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" }, "funding": [ { @@ -2047,7 +2307,7 @@ "type": "github" } ], - "time": "2022-03-15T09:54:48+00:00" + "time": "2022-09-12T14:47:03+00:00" }, { "name": "sebastian/version", @@ -2102,18 +2362,82 @@ ], "time": "2020-09-28T06:39:44+00:00" }, + { + "name": "seld/jsonlint", + "version": "1.9.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "4211420d25eba80712bff236a98960ef68b866b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/4211420d25eba80712bff236a98960ef68b866b7", + "reference": "4211420d25eba80712bff236a98960ef68b866b7", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.9.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2022-04-01T13:37:23+00:00" + }, { "name": "squizlabs/php_codesniffer", - "version": "3.6.2", + "version": "3.7.1", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a" + "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a", - "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", + "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", "shasum": "" }, "require": { @@ -2156,93 +2480,135 @@ "source": "https://github.com/squizlabs/PHP_CodeSniffer", "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" }, - "time": "2021-12-12T21:44:58+00:00" + "time": "2022-06-18T07:21:10+00:00" }, { - "name": "theseer/tokenizer", - "version": "1.2.1", + "name": "symfony/console", + "version": "v6.1.7", "source": { "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "url": "https://github.com/symfony/console.git", + "reference": "a1282bd0c096e0bdb8800b104177e2ce404d8815" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/symfony/console/zipball/a1282bd0c096e0bdb8800b104177e2ce404d8815", + "reference": "a1282bd0c096e0bdb8800b104177e2ce404d8815", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" }, "type": "library", "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/symfony/console/tree/v6.1.7" }, "funding": [ { - "url": "https://github.com/theseer", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2022-10-26T21:42:49+00:00" }, { - "name": "webmozart/assert", - "version": "1.11.0", + "name": "symfony/deprecation-contracts", + "version": "v3.1.1", "source": { "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", "shasum": "" }, "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10-dev" + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } + "files": [ + "function.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2250,21 +2616,888 @@ ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-02-25T11:15:52+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.1.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "4d216a2beef096edf040a070117c39ca2abce307" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4d216a2beef096edf040a070117c39ca2abce307", + "reference": "4d216a2beef096edf040a070117c39ca2abce307", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.1.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-09-21T20:29:40+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/39696bff2c2970b3779a5cac7bf9f0b88fc2b709", + "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-29T07:42:06+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v6.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a3016f5442e28386ded73c43a32a5b68586dd1c4", + "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-02-25T11:15:52+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "433d05519ce6990bf3530fba6957499d327395c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", + "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/process", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/a6506e99cfad7059b1ab5cab395854a0a0c21292", + "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T17:24:16+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/925e713fe8fcacf6bc05e936edd8dd5441a21239", + "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Assertions to validate method input/output with nice error messages.", + "time": "2022-05-30T19:18:58+00:00" + }, + { + "name": "symfony/string", + "version": "v6.1.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "823f143370880efcbdfa2dbca946b3358c4707e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/823f143370880efcbdfa2dbca946b3358c4707e5", + "reference": "823f143370880efcbdfa2dbca946b3358c4707e5", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.0" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", "keywords": [ - "assert", - "check", - "validate" + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.1.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-10-10T09:34:31+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "webmozart/glob", + "version": "4.6.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/glob.git", + "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/glob/zipball/3c17f7dec3d9d0e87b575026011f2e75a56ed655", + "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "symfony/filesystem": "^5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Glob\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } ], + "description": "A PHP implementation of Ant's glob.", "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "issues": "https://github.com/webmozarts/glob/issues", + "source": "https://github.com/webmozarts/glob/tree/4.6.0" }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2022-05-24T19:45:58+00:00" } ], "aliases": [], diff --git a/phpbench.json b/phpbench.json new file mode 100644 index 0000000..0df230f --- /dev/null +++ b/phpbench.json @@ -0,0 +1,4 @@ +{ + "$schema":"./vendor/phpbench/phpbench/phpbench.schema.json", + "runner.bootstrap": "vendor/autoload.php" +} diff --git a/phpstan.neon b/phpstan.neon index b5f0745..a0e6887 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,2 +1,2 @@ parameters: - level: 8 + level: 9 diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 2f34b07..44a593e 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -2,14 +2,7 @@ namespace ClassTransformer; -use ClassTransformer\DTO\Property; -use ReflectionClass; use ReflectionException; -use ReflectionNamedType; -use ReflectionType; -use ReflectionUnionType; -use ClassTransformer\Attributes\WritingStyle; -use ClassTransformer\Attributes\ConvertArray; use ClassTransformer\Exceptions\ClassNotFoundException; /** @@ -17,7 +10,7 @@ * * @package ClassTransformer */ -class ClassTransformer +final class ClassTransformer { /** * Class-transformer function to transform our object into a typed object @@ -36,13 +29,13 @@ public static function transform(string|array $className, ...$args) return self::dataConverting($className, ...$args); } - if (count(func_get_args()) === 1) { + if (sizeof(func_get_args()) === 1) { throw new \RuntimeException('Input parameter error. Named arguments are not supported for an anonymous array of classes'); } if (empty($args) || !is_array($args[0])) { return null; } - if (count($className) === 1) { + if (sizeof($className) === 1) { return self::anonymousArrayConverting($className[0], $args[0]); } diff --git a/src/DTO/Property.php b/src/DTO/Property.php index 3b13709..c67c8c0 100644 --- a/src/DTO/Property.php +++ b/src/DTO/Property.php @@ -7,12 +7,16 @@ use ReflectionNamedType; use ReflectionUnionType; +use function sizeof; +use function in_array; +use function array_intersect; + /** * Class Property * * @author yzen.dev */ -class Property +final class Property { /** * @var ReflectionProperty @@ -64,7 +68,7 @@ static function ($item) { */ public function isScalar(): bool { - return count(array_intersect($this->getTypes(), ['int', 'float', 'double', 'string', 'bool', 'mixed'])) > 0; + return sizeof(array_intersect($this->getTypes(), ['int', 'float', 'double', 'string', 'bool', 'mixed'])) > 0; } /** diff --git a/src/PropertyHelper.php b/src/PropertyHelper.php index cde7b15..65364f5 100644 --- a/src/PropertyHelper.php +++ b/src/PropertyHelper.php @@ -2,12 +2,16 @@ namespace ClassTransformer; +use function in_array; +use function is_array; +use function sizeof; +use function array_intersect; /** * Class PropertyHelper * * @author yzen.dev */ -class PropertyHelper +final class PropertyHelper { /** * @param string|bool $phpDoc @@ -22,7 +26,7 @@ public static function getClassFromPhpDoc($phpDoc): ?string } return null; } - + /** * @param array|string $type * @@ -31,7 +35,7 @@ public static function getClassFromPhpDoc($phpDoc): ?string public static function propertyIsScalar(array|string $type): bool { if (is_array($type)) { - return count(array_intersect($type, ['int', 'float', 'string', 'bool', 'mixed'])) > 0; + return sizeof(array_intersect($type, ['int', 'float', 'string', 'bool', 'mixed'])) > 0; } return in_array($type, ['int', 'float', 'string', 'bool', 'mixed']); } diff --git a/src/PropertyTransformer.php b/src/PropertyTransformer.php index 7bd98fd..435cc0e 100644 --- a/src/PropertyTransformer.php +++ b/src/PropertyTransformer.php @@ -2,23 +2,27 @@ namespace ClassTransformer; -use ClassTransformer\Attributes\NotTransform; use ReflectionClass; use ReflectionProperty; use ReflectionException; use ReflectionNamedType; use ClassTransformer\DTO\Property; +use ClassTransformer\Attributes\NotTransform; use ClassTransformer\Attributes\ConvertArray; use ClassTransformer\Attributes\WritingStyle; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\Exceptions\ValueNotFoundException; +use function sizeof; +use function func_get_args; +use function array_key_exists; + /** * Class ClassTransformerService * * @author yzen.dev */ -class PropertyTransformer +final class PropertyTransformer { /** * @template T @@ -44,7 +48,7 @@ public function __construct(string $className, ...$args) // Arguments transfer as named arguments (for php8) // if dynamic arguments, named ones lie immediately in the root, if they were passed as an array, then they need to be unpacked - $inArgs = count(func_get_args()) === 1 ? $args : $args[0]; + $inArgs = sizeof(func_get_args()) === 1 ? $args : $args[0]; if (is_object($inArgs)) { $inArgs = (array)$inArgs; @@ -70,7 +74,7 @@ public static function init(string $className, ...$args): PropertyTransformer * @return void * @throws ClassNotFoundException */ - private function validate() + private function validate(): void { if (!class_exists($this->className)) { throw new ClassNotFoundException("Class $this->className not found. Please check the class path you specified."); @@ -139,7 +143,6 @@ public function transform() return $instance; } - /** * @param ReflectionProperty $item * diff --git a/src/WritingStyleUtil.php b/src/WritingStyleUtil.php index dd343d8..3a23e63 100644 --- a/src/WritingStyleUtil.php +++ b/src/WritingStyleUtil.php @@ -2,13 +2,20 @@ namespace ClassTransformer; +use function ucwords; +use function lcfirst; +use function strtolower; +use function str_replace; +use function preg_replace; + /** * */ -class WritingStyleUtil +final class WritingStyleUtil { /** * @param string $string + * * @return string */ public static function strToSnakeCase(string $string): string @@ -19,6 +26,7 @@ public static function strToSnakeCase(string $string): string /** * @param string $string + * * @return string */ public static function strToCamelCase(string $string): string From a41ca1dcdfa3e1ecaac428e4ab00ef2514185e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Sat, 24 Dec 2022 22:29:24 +0300 Subject: [PATCH 007/100] add: phpdocs for attributes --- src/Attributes/ConvertArray.php | 3 +++ src/Attributes/NotTransform.php | 3 +++ src/ClassTransformer.php | 4 +++- src/DTO/Property.php | 18 +++++++++++++----- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Attributes/ConvertArray.php b/src/Attributes/ConvertArray.php index 03eaf01..2a7191d 100644 --- a/src/Attributes/ConvertArray.php +++ b/src/Attributes/ConvertArray.php @@ -4,6 +4,9 @@ namespace ClassTransformer\Attributes; +/** + * An attribute for properties that are an array that allows you to specify the type of element + */ #[\Attribute(\Attribute::TARGET_PARAMETER)] final class ConvertArray { diff --git a/src/Attributes/NotTransform.php b/src/Attributes/NotTransform.php index 7c71b8b..43cdd23 100644 --- a/src/Attributes/NotTransform.php +++ b/src/Attributes/NotTransform.php @@ -4,6 +4,9 @@ namespace ClassTransformer\Attributes; +/** + * Attribute for properties that don't need to be converted + */ #[\Attribute(\Attribute::TARGET_PARAMETER)] final class NotTransform { diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 44a593e..ebc39ba 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -32,9 +32,11 @@ public static function transform(string|array $className, ...$args) if (sizeof(func_get_args()) === 1) { throw new \RuntimeException('Input parameter error. Named arguments are not supported for an anonymous array of classes'); } + if (empty($args) || !is_array($args[0])) { return null; } + if (sizeof($className) === 1) { return self::anonymousArrayConverting($className[0], $args[0]); } @@ -51,7 +53,7 @@ public static function transform(string|array $className, ...$args) * @return array * @throws ClassNotFoundException|ReflectionException */ - private static function anonymousArrayConverting(string $className, $args) + private static function anonymousArrayConverting(string $className, array $args): array { $result = []; foreach ($args as $item) { diff --git a/src/DTO/Property.php b/src/DTO/Property.php index c67c8c0..384b1d8 100644 --- a/src/DTO/Property.php +++ b/src/DTO/Property.php @@ -34,7 +34,7 @@ public function __construct(ReflectionProperty $property) /** * @return ReflectionType|null */ - public function getType() + public function getType(): ?ReflectionType { return $this->property->getType(); } @@ -45,16 +45,21 @@ public function getType() public function getTypes(): array { $types = []; - if ($this->getType() instanceof ReflectionUnionType) { + $currentType = $this->getType(); + if ($currentType === null) { + return []; + } + if ($currentType instanceof ReflectionUnionType) { $types = array_map( static function ($item) { return $item->getName(); }, - $this->getType()->getTypes() + $currentType->getTypes() ); } - if ($this->getType() instanceof ReflectionNamedType) { - $types = [$this->getType()->getName()]; + + if ($currentType instanceof ReflectionNamedType) { + $types = [$currentType->getName()]; } if ($this->getType() !== null && $this->getType()->allowsNull()) { @@ -64,6 +69,8 @@ static function ($item) { } /** + * Finds whether a variable is a scalar + * * @return bool */ public function isScalar(): bool @@ -72,6 +79,7 @@ public function isScalar(): bool } /** + * Finds whether a variable is an array * @return bool */ public function isArray(): bool From 5583062309886e9bfc2fca41015498ab03811662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Sun, 8 Jan 2023 17:09:26 +0300 Subject: [PATCH 008/100] add: support enum in 8.1 --- composer.json | 2 +- src/PropertyTransformer.php | 26 +++++++++++++++++--- tests/Benchmark/GetEnumBench.php | 36 +++++++++++++++++++++++++++ tests/DTO/ColorEnum.php | 15 ++++++++++++ tests/DTO/ColorScalarEnum.php | 15 ++++++++++++ tests/DTO/ExampleWithEnumDTO.php | 14 +++++++++++ tests/EnumTest.php | 42 ++++++++++++++++++++++++++++++++ 7 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 tests/Benchmark/GetEnumBench.php create mode 100644 tests/DTO/ColorEnum.php create mode 100644 tests/DTO/ColorScalarEnum.php create mode 100644 tests/DTO/ExampleWithEnumDTO.php create mode 100644 tests/EnumTest.php diff --git a/composer.json b/composer.json index 4cecd9a..82d65b3 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ } }, "require": { - "php": "^8.0" + "php": "^8.1" }, "require-dev": { "mockery/mockery": "^1.0", diff --git a/src/PropertyTransformer.php b/src/PropertyTransformer.php index 435cc0e..ffd6969 100644 --- a/src/PropertyTransformer.php +++ b/src/PropertyTransformer.php @@ -33,6 +33,8 @@ final class PropertyTransformer /** @var array */ private array $args; + private ReflectionClass $refInstance; + /** * @template T * @@ -45,6 +47,9 @@ public function __construct(string $className, ...$args) { $this->className = $className; $this->validate(); + + /** @phpstan-ignore-next-line */ + $this->refInstance = new ReflectionClass($this->className); // Arguments transfer as named arguments (for php8) // if dynamic arguments, named ones lie immediately in the root, if they were passed as an array, then they need to be unpacked @@ -53,6 +58,9 @@ public function __construct(string $className, ...$args) if (is_object($inArgs)) { $inArgs = (array)$inArgs; } + if (is_string($inArgs)) { + $inArgs = [$inArgs]; + } $this->args = $inArgs ?? []; } @@ -89,8 +97,6 @@ private function validate(): void */ public function transform() { - /** @phpstan-ignore-next-line */ - $refInstance = new ReflectionClass($this->className); // if exist custom transform method if (method_exists($this->className, 'transform')) { @@ -101,9 +107,12 @@ public function transform() /** @var T $instance */ $instance = new $this->className(); - foreach ($refInstance->getProperties() as $item) { - $property = new Property($refInstance->getProperty($item->name)); + foreach ($this->refInstance->getProperties() as $item) { + $property = new Property($this->refInstance->getProperty($item->name)); + //var_dump($property->getTypes()); + //var_dump($property); + // die(0); try { $value = $this->getValue($item); } catch (ValueNotFoundException) { @@ -135,6 +144,15 @@ public function transform() } if ($property->getType() instanceof ReflectionNamedType) { + /** @phpstan-ignore-next-line */ + $propertyClass = $property->getType()->getName(); + $childrenRefInstance = new ReflectionClass($propertyClass); + if ($childrenRefInstance->isEnum()) { + $value = constant($propertyClass . '::' . $value); + $instance->{$item->name} = $value; + continue; + } + $instance->{$item->name} = self::init($property->getType()->getName(), $value)->transform(); continue; } diff --git a/tests/Benchmark/GetEnumBench.php b/tests/Benchmark/GetEnumBench.php new file mode 100644 index 0000000..89a55b6 --- /dev/null +++ b/tests/Benchmark/GetEnumBench.php @@ -0,0 +1,36 @@ +hasConstant($key)) { + $value = $reflection->getConstant($key); + } + } + +} diff --git a/tests/DTO/ColorEnum.php b/tests/DTO/ColorEnum.php new file mode 100644 index 0000000..c61f670 --- /dev/null +++ b/tests/DTO/ColorEnum.php @@ -0,0 +1,15 @@ + + */ +enum ColorEnum +{ + case Red; + case Black; + case White; +} diff --git a/tests/DTO/ColorScalarEnum.php b/tests/DTO/ColorScalarEnum.php new file mode 100644 index 0000000..2aa4d6c --- /dev/null +++ b/tests/DTO/ColorScalarEnum.php @@ -0,0 +1,15 @@ + + */ +enum ColorScalarEnum: string +{ + case Red = "R"; + case Black = "B"; + case White = "W"; +} diff --git a/tests/DTO/ExampleWithEnumDTO.php b/tests/DTO/ExampleWithEnumDTO.php new file mode 100644 index 0000000..fd439f3 --- /dev/null +++ b/tests/DTO/ExampleWithEnumDTO.php @@ -0,0 +1,14 @@ + + */ +class ExampleWithEnumDTO +{ + public ColorEnum $colorEnum; + public ColorScalarEnum $colorScalarEnum; +} diff --git a/tests/EnumTest.php b/tests/EnumTest.php new file mode 100644 index 0000000..2c242c3 --- /dev/null +++ b/tests/EnumTest.php @@ -0,0 +1,42 @@ + 'Red', + 'colorScalarEnum' => 'Red', + ]; + $model = ClassTransformer::transform(ExampleWithEnumDTO::class, $data); + + self::assertInstanceOf(ExampleWithEnumDTO::class, $model); + self::assertInstanceOf(ColorEnum::class, $model->colorEnum); + self::assertEquals(ColorEnum::Red, $model->colorEnum); + self::assertInstanceOf(ColorScalarEnum::class, $model->colorScalarEnum); + self::assertEquals(ColorScalarEnum::Red, $model->colorScalarEnum); + } + +} From 1e0b525a3b980e6cc520e992ce53cdd304bf92fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Sun, 8 Jan 2023 17:10:23 +0300 Subject: [PATCH 009/100] remove old --- src/PropertyTransformer.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/PropertyTransformer.php b/src/PropertyTransformer.php index ffd6969..b63a1ea 100644 --- a/src/PropertyTransformer.php +++ b/src/PropertyTransformer.php @@ -47,7 +47,7 @@ public function __construct(string $className, ...$args) { $this->className = $className; $this->validate(); - + /** @phpstan-ignore-next-line */ $this->refInstance = new ReflectionClass($this->className); @@ -58,9 +58,7 @@ public function __construct(string $className, ...$args) if (is_object($inArgs)) { $inArgs = (array)$inArgs; } - if (is_string($inArgs)) { - $inArgs = [$inArgs]; - } + $this->args = $inArgs ?? []; } @@ -152,7 +150,7 @@ public function transform() $instance->{$item->name} = $value; continue; } - + $instance->{$item->name} = self::init($property->getType()->getName(), $value)->transform(); continue; } From 6d7e0d804025c17da082d2579be7008f84d31ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Sun, 8 Jan 2023 17:17:38 +0300 Subject: [PATCH 010/100] add test benck --- tests/Benchmark/CheckBench.php | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/Benchmark/CheckBench.php diff --git a/tests/Benchmark/CheckBench.php b/tests/Benchmark/CheckBench.php new file mode 100644 index 0000000..8d23a63 --- /dev/null +++ b/tests/Benchmark/CheckBench.php @@ -0,0 +1,56 @@ +getRecursiveObject(); + $productOne = new ProductDTO(); + $productOne->id = 1; + $productOne->name = 'phone'; + $productOne->price = 43.03; + + $productTwo = new ProductDTO(); + $productTwo->id = 2; + $productTwo->name = 'bread'; + $productTwo->price = 10.56; + + $user = new UserDTO(); + $user->id = 1; + $user->email = 'fake@mail.com'; + $user->balance = 10012.23; + + $data = new PurchaseDTO(); + $data->products = [$productOne, $productTwo]; + $data->user = $user; + } + + /** + * @Revs(5000) + */ + public function benchTransformReflection(): void + { + $data = $this->getRecursiveObject(); + $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); + } + +} From 0521bad72f3fff6247ff19090d1aa0bebbc5a120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Sun, 8 Jan 2023 20:40:06 +0300 Subject: [PATCH 011/100] remove old --- src/ClassTransformer.php | 1 + src/PropertyTransformer.php | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index ebc39ba..e3a2d8f 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -57,6 +57,7 @@ private static function anonymousArrayConverting(string $className, array $args) { $result = []; foreach ($args as $item) { + /** @var T $item */ $result [] = self::dataConverting($className, $item); } return $result; diff --git a/src/PropertyTransformer.php b/src/PropertyTransformer.php index b63a1ea..b7773f4 100644 --- a/src/PropertyTransformer.php +++ b/src/PropertyTransformer.php @@ -95,7 +95,6 @@ private function validate(): void */ public function transform() { - // if exist custom transform method if (method_exists($this->className, 'transform')) { /** @phpstan-ignore-next-line */ @@ -108,9 +107,6 @@ public function transform() foreach ($this->refInstance->getProperties() as $item) { $property = new Property($this->refInstance->getProperty($item->name)); - //var_dump($property->getTypes()); - //var_dump($property); - // die(0); try { $value = $this->getValue($item); } catch (ValueNotFoundException) { @@ -179,13 +175,13 @@ private function getValue(ReflectionProperty $item) foreach ($writingStyle as $style) { $styles = $style->getArguments(); if ( - (in_array(WritingStyle::STYLE_SNAKE_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) && + (in_array(WritingStyle::STYLE_SNAKE_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) & array_key_exists(WritingStyleUtil::strToSnakeCase($item->name), $this->args) ) { return $this->args[WritingStyleUtil::strToSnakeCase($item->name)]; } if ( - (in_array(WritingStyle::STYLE_CAMEL_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) && + (in_array(WritingStyle::STYLE_CAMEL_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) & array_key_exists(WritingStyleUtil::strToCamelCase($item->name), $this->args) ) { return $this->args[WritingStyleUtil::strToCamelCase($item->name)]; From 55f63d8a4acaeb8d18927d93f230d20bcdc0b072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 17 Jan 2023 13:04:43 +0300 Subject: [PATCH 012/100] issue: support enums --- src/ClassTransformer.php | 2 +- src/DTO/Property.php | 10 ++++++++ src/PropertyTransformer.php | 45 ++++++++++++++++------------------ tests/Benchmark/CheckBench.php | 3 +-- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index e3a2d8f..f39584d 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -103,6 +103,6 @@ private static function dataConverting(string $className, ...$args) if (empty($args)) { return new $className(); } - return PropertyTransformer::init($className, ...$args)->transform(); + return (new PropertyTransformer($className, ...$args))->transform(); } } diff --git a/src/DTO/Property.php b/src/DTO/Property.php index 384b1d8..940778a 100644 --- a/src/DTO/Property.php +++ b/src/DTO/Property.php @@ -2,6 +2,7 @@ namespace ClassTransformer\DTO; +use ClassTransformer\Attributes\NotTransform; use ReflectionType; use ReflectionProperty; use ReflectionNamedType; @@ -80,6 +81,7 @@ public function isScalar(): bool /** * Finds whether a variable is an array + * * @return bool */ public function isArray(): bool @@ -104,6 +106,14 @@ public function existsAttribute(?string $name = null): bool return $this->getAttributes($name) !== null; } + /** + * @return bool + */ + public function notTransform(): bool + { + return $this->existsAttribute(NotTransform::class); + } + /** * @param string|null $name * diff --git a/src/PropertyTransformer.php b/src/PropertyTransformer.php index b7773f4..fbdc083 100644 --- a/src/PropertyTransformer.php +++ b/src/PropertyTransformer.php @@ -33,23 +33,34 @@ final class PropertyTransformer /** @var array */ private array $args; + /** + * @var ReflectionClass + */ private ReflectionClass $refInstance; /** * @template T * - * @param string|class-string $className + * @param ReflectionClass|string $class * @param array|object|null $args * * @throws ClassNotFoundException + * @throws ReflectionException */ - public function __construct(string $className, ...$args) + public function __construct(ReflectionClass|string $class, ...$args) { - $this->className = $className; - $this->validate(); + if ($class instanceof ReflectionClass) { + $this->className = $class->getName(); + /** @phpstan-ignore-next-line */ + $this->refInstance = $class; + } else { + $this->className = $class; - /** @phpstan-ignore-next-line */ - $this->refInstance = new ReflectionClass($this->className); + $this->validate(); + + /** @phpstan-ignore-next-line */ + $this->refInstance = new ReflectionClass($this->className); + } // Arguments transfer as named arguments (for php8) // if dynamic arguments, named ones lie immediately in the root, if they were passed as an array, then they need to be unpacked @@ -58,22 +69,8 @@ public function __construct(string $className, ...$args) if (is_object($inArgs)) { $inArgs = (array)$inArgs; } - - $this->args = $inArgs ?? []; - } - /** - * @template T - * - * @param string|class-string $className - * @param array|object|null $args - * - * @return PropertyTransformer - * @throws ClassNotFoundException - */ - public static function init(string $className, ...$args): PropertyTransformer - { - return new self($className, ...$args); + $this->args = $inArgs ?? []; } /** @@ -113,7 +110,7 @@ public function transform() continue; } - if ($property->isScalar() || $property->existsAttribute(NotTransform::class)) { + if ($property->isScalar() || $property->notTransform()) { $instance->{$item->name} = $value; continue; } @@ -128,7 +125,7 @@ public function transform() if (!empty($arrayType) && !empty($value) && !PropertyHelper::propertyIsScalar($arrayType)) { foreach ($value as $el) { - $instance->{$item->name}[] = self::init($arrayType, $el)->transform(); + $instance->{$item->name}[] = (new self($arrayType, $el))->transform(); } continue; } @@ -147,7 +144,7 @@ public function transform() continue; } - $instance->{$item->name} = self::init($property->getType()->getName(), $value)->transform(); + $instance->{$item->name} = (new self($childrenRefInstance, $value))->transform(); continue; } $instance->{$item->name} = $value; diff --git a/tests/Benchmark/CheckBench.php b/tests/Benchmark/CheckBench.php index 8d23a63..3c09477 100644 --- a/tests/Benchmark/CheckBench.php +++ b/tests/Benchmark/CheckBench.php @@ -23,7 +23,6 @@ class CheckBench extends TestCase */ public function benchBaseReflection(): void { - $data = $this->getRecursiveObject(); $productOne = new ProductDTO(); $productOne->id = 1; $productOne->name = 'phone'; @@ -50,7 +49,7 @@ public function benchBaseReflection(): void public function benchTransformReflection(): void { $data = $this->getRecursiveObject(); - $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); + ClassTransformer::transform(PurchaseDTO::class, $data); } } From 995673dab9bcec559b6c1e196f26040e3d1e510d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 17 Jan 2023 20:50:02 +0300 Subject: [PATCH 013/100] issue: refactoring --- composer.json | 14 +- composer.lock | 355 ++++++++++-------- coverage.xml | 187 --------- src/ClassTransformable.php | 19 + src/ClassTransformer.php | 80 +--- ...rtyTransformer.php => GenericInstance.php} | 111 ++---- src/{DTO/Property.php => GenericProperty.php} | 8 +- src/PropertyHelper.php | 10 +- src/TransformBuilder.php | 62 +++ src/Validators/ClassExistsValidator.php | 25 ++ tests/Benchmark/CheckBench.php | 55 --- tests/Benchmark/GetEnumBench.php | 36 -- tests/ClassTransformerExceptionsTest.php | 9 - tests/ClassTransformerFromArrayTest.php | 17 +- tests/DTO/ColorEnum.php | 15 - tests/DTO/CustomTransformUserDTO.php | 8 +- tests/DTO/CustomTransformUserDTOArray.php | 13 +- tests/DTO/ExampleWithEnumDTO.php | 14 - tests/DTO/UserNotTransformRelationDTO.php | 2 +- tests/EnumTest.php | 42 --- 20 files changed, 384 insertions(+), 698 deletions(-) delete mode 100644 coverage.xml create mode 100644 src/ClassTransformable.php rename src/{PropertyTransformer.php => GenericInstance.php} (54%) rename src/{DTO/Property.php => GenericProperty.php} (97%) create mode 100644 src/TransformBuilder.php create mode 100644 src/Validators/ClassExistsValidator.php delete mode 100644 tests/Benchmark/CheckBench.php delete mode 100644 tests/Benchmark/GetEnumBench.php delete mode 100644 tests/DTO/ColorEnum.php delete mode 100644 tests/DTO/ExampleWithEnumDTO.php delete mode 100644 tests/EnumTest.php diff --git a/composer.json b/composer.json index 82d65b3..9072851 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "minimum-stability": "dev", "prefer-stable": true, "license": "MIT", - "keywords": [ "php", "object", "class", "transformer","convert", "class-transformer"], + "keywords": [ "php", "object", "class", "transformer", "convert", "class-transformer" ], "authors": [ { "name": "Andey Iatsenko", @@ -30,14 +30,14 @@ } }, "require": { - "php": "^8.1" + "php": "^8.0" }, "require-dev": { - "mockery/mockery": "^1.0", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "*" + "mockery/mockery": "1.5.1", + "phpbench/phpbench": "1.2.8", + "phpstan/phpstan": "1.9.12", + "phpunit/phpunit": "9.5.28", + "squizlabs/php_codesniffer": "3.7.1" }, "scripts": { "phpunit": [ diff --git a/composer.lock b/composer.lock index 3934508..c913798 100644 --- a/composer.lock +++ b/composer.lock @@ -4,37 +4,40 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "60f864d776d355a77ff8840e0b42f9e6", + "content-hash": "c3b63bca52c0fd6871611546770f8673", "packages": [], "packages-dev": [ { "name": "doctrine/annotations", - "version": "1.13.3", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "648b0343343565c4a056bfc8392201385e8d89f0" + "reference": "d02c9f3742044e17d5fa8d28d8402a2d95c33302" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/648b0343343565c4a056bfc8392201385e8d89f0", - "reference": "648b0343343565c4a056bfc8392201385e8d89f0", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/d02c9f3742044e17d5fa8d28d8402a2d95c33302", + "reference": "d02c9f3742044e17d5fa8d28d8402a2d95c33302", "shasum": "" }, "require": { - "doctrine/lexer": "1.*", + "doctrine/lexer": "^2 || ^3", "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", + "php": "^7.2 || ^8.0", "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^6.0 || ^8.1", - "phpstan/phpstan": "^1.4.10 || ^1.8.0", - "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", - "symfony/cache": "^4.4 || ^5.2", + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6", "vimeo/psalm": "^4.10" }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, "type": "library", "autoload": { "psr-4": { @@ -76,36 +79,79 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.13.3" + "source": "https://github.com/doctrine/annotations/tree/2.0.0" }, - "time": "2022-07-02T10:48:51+00:00" + "time": "2022-12-19T18:17:20+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "shasum": "" + }, + "require": { + "php": "^7.1|^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + }, + "time": "2022-05-02T15:47:09+00:00" }, { "name": "doctrine/instantiator", - "version": "1.4.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -132,7 +178,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -148,35 +194,37 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "doctrine/lexer", - "version": "1.2.3", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", - "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.0", "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9.0", + "doctrine/coding-standard": "^9 || ^10", "phpstan/phpstan": "^1.3", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.11" + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^4.11 || ^5.0" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -208,7 +256,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.3" + "source": "https://github.com/doctrine/lexer/tree/2.1.0" }, "funding": [ { @@ -224,7 +272,7 @@ "type": "tidelift" } ], - "time": "2022-02-28T11:07:21+00:00" + "time": "2022-12-14T08:49:07+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -410,16 +458,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.1", + "version": "v4.15.3", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900" + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", "shasum": "" }, "require": { @@ -460,9 +508,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" }, - "time": "2022-09-04T07:30:47+00:00" + "time": "2023-01-16T22:05:37+00:00" }, { "name": "phar-io/manifest", @@ -679,20 +727,20 @@ }, { "name": "phpbench/phpbench", - "version": "1.2.7", + "version": "1.2.8", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "dce145304abbb16c8d9af69c19d96f47e9d0e670" + "reference": "3f7b3c200f86727de7a14bde94adb68a88e1bafc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/dce145304abbb16c8d9af69c19d96f47e9d0e670", - "reference": "dce145304abbb16c8d9af69c19d96f47e9d0e670", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/3f7b3c200f86727de7a14bde94adb68a88e1bafc", + "reference": "3f7b3c200f86727de7a14bde94adb68a88e1bafc", "shasum": "" }, "require": { - "doctrine/annotations": "^1.13", + "doctrine/annotations": "^1.13 || ^2.0", "ext-dom": "*", "ext-json": "*", "ext-pcre": "*", @@ -757,7 +805,7 @@ "description": "PHP Benchmarking Framework", "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.2.7" + "source": "https://github.com/phpbench/phpbench/tree/1.2.8" }, "funding": [ { @@ -765,20 +813,20 @@ "type": "github" } ], - "time": "2022-10-15T09:57:51+00:00" + "time": "2023-01-14T13:08:42+00:00" }, { "name": "phpstan/phpstan", - "version": "1.9.1", + "version": "1.9.12", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f" + "reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", - "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/44a338ff0d5572c13fd77dfd91addb96e48c29f8", + "reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8", "shasum": "" }, "require": { @@ -808,7 +856,7 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.9.1" + "source": "https://github.com/phpstan/phpstan/tree/1.9.12" }, "funding": [ { @@ -824,20 +872,20 @@ "type": "tidelift" } ], - "time": "2022-11-04T13:35:59+00:00" + "time": "2023-01-17T10:44:04+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.18", + "version": "9.2.23", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a" + "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/12fddc491826940cf9b7e88ad9664cf51f0f6d0a", - "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", + "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", "shasum": "" }, "require": { @@ -893,7 +941,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.18" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.23" }, "funding": [ { @@ -901,7 +949,7 @@ "type": "github" } ], - "time": "2022-10-27T13:35:33+00:00" + "time": "2022-12-28T12:41:10+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1146,20 +1194,20 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.26", + "version": "9.5.28", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2" + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2", - "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e", + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", + "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -1228,7 +1276,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28" }, "funding": [ { @@ -1244,7 +1292,7 @@ "type": "tidelift" } ], - "time": "2022-10-28T06:00:21+00:00" + "time": "2023-01-14T12:32:24+00:00" }, { "name": "psr/cache", @@ -2484,21 +2532,20 @@ }, { "name": "symfony/console", - "version": "v6.1.7", + "version": "v6.0.17", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a1282bd0c096e0bdb8800b104177e2ce404d8815" + "reference": "2ab307342a7233b9a260edd5ef94087aaca57d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a1282bd0c096e0bdb8800b104177e2ce404d8815", - "reference": "a1282bd0c096e0bdb8800b104177e2ce404d8815", + "url": "https://api.github.com/repos/symfony/console/zipball/2ab307342a7233b9a260edd5ef94087aaca57d18", + "reference": "2ab307342a7233b9a260edd5ef94087aaca57d18", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.0.2", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^1.1|^2|^3", "symfony/string": "^5.4|^6.0" @@ -2560,7 +2607,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.1.7" + "source": "https://github.com/symfony/console/tree/v6.0.17" }, "funding": [ { @@ -2576,29 +2623,29 @@ "type": "tidelift" } ], - "time": "2022-10-26T21:42:49+00:00" + "time": "2022-12-28T14:21:34+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.1.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.0-dev" }, "thanks": { "name": "symfony/contracts", @@ -2627,7 +2674,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" }, "funding": [ { @@ -2643,24 +2690,24 @@ "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/filesystem", - "version": "v6.1.5", + "version": "v6.0.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4d216a2beef096edf040a070117c39ca2abce307" + "reference": "3adca49133bd055ebe6011ed1e012be3c908af79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4d216a2beef096edf040a070117c39ca2abce307", - "reference": "4d216a2beef096edf040a070117c39ca2abce307", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/3adca49133bd055ebe6011ed1e012be3c908af79", + "reference": "3adca49133bd055ebe6011ed1e012be3c908af79", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, @@ -2690,7 +2737,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.1.5" + "source": "https://github.com/symfony/filesystem/tree/v6.0.13" }, "funding": [ { @@ -2706,27 +2753,24 @@ "type": "tidelift" } ], - "time": "2022-09-21T20:29:40+00:00" + "time": "2022-09-21T20:25:27+00:00" }, { "name": "symfony/finder", - "version": "v6.1.3", + "version": "v6.0.17", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709" + "reference": "d467d625fc88f7cebf96f495e588a7196a669db1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/39696bff2c2970b3779a5cac7bf9f0b88fc2b709", - "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709", + "url": "https://api.github.com/repos/symfony/finder/zipball/d467d625fc88f7cebf96f495e588a7196a669db1", + "reference": "d467d625fc88f7cebf96f495e588a7196a669db1", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "symfony/filesystem": "^6.0" + "php": ">=8.0.2" }, "type": "library", "autoload": { @@ -2754,7 +2798,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.1.3" + "source": "https://github.com/symfony/finder/tree/v6.0.17" }, "funding": [ { @@ -2770,24 +2814,24 @@ "type": "tidelift" } ], - "time": "2022-07-29T07:42:06+00:00" + "time": "2022-12-22T17:53:58+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.1.0", + "version": "v6.0.3", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4" + "reference": "51f7006670febe4cbcbae177cbffe93ff833250d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a3016f5442e28386ded73c43a32a5b68586dd1c4", - "reference": "a3016f5442e28386ded73c43a32a5b68586dd1c4", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/51f7006670febe4cbcbae177cbffe93ff833250d", + "reference": "51f7006670febe4cbcbae177cbffe93ff833250d", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", @@ -2821,7 +2865,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.1.0" + "source": "https://github.com/symfony/options-resolver/tree/v6.0.3" }, "funding": [ { @@ -2837,20 +2881,20 @@ "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { @@ -2865,7 +2909,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2903,7 +2947,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -2919,20 +2963,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "433d05519ce6990bf3530fba6957499d327395c2" + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", - "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", "shasum": "" }, "require": { @@ -2944,7 +2988,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2984,7 +3028,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" }, "funding": [ { @@ -3000,20 +3044,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", "shasum": "" }, "require": { @@ -3025,7 +3069,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3068,7 +3112,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" }, "funding": [ { @@ -3084,20 +3128,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -3112,7 +3156,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3151,7 +3195,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -3167,24 +3211,24 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/process", - "version": "v6.1.3", + "version": "v6.0.11", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292" + "reference": "44270a08ccb664143dede554ff1c00aaa2247a43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/a6506e99cfad7059b1ab5cab395854a0a0c21292", - "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292", + "url": "https://api.github.com/repos/symfony/process/zipball/44270a08ccb664143dede554ff1c00aaa2247a43", + "reference": "44270a08ccb664143dede554ff1c00aaa2247a43", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.0.2" }, "type": "library", "autoload": { @@ -3212,7 +3256,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.1.3" + "source": "https://github.com/symfony/process/tree/v6.0.11" }, "funding": [ { @@ -3228,24 +3272,24 @@ "type": "tidelift" } ], - "time": "2022-06-27T17:24:16+00:00" + "time": "2022-06-27T17:10:44+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.1.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239" + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/925e713fe8fcacf6bc05e936edd8dd5441a21239", - "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "psr/container": "^2.0" }, "conflict": { @@ -3257,7 +3301,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.0-dev" }, "thanks": { "name": "symfony/contracts", @@ -3267,10 +3311,7 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3297,7 +3338,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.1.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" }, "funding": [ { @@ -3313,24 +3354,24 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:18:58+00:00" + "time": "2022-05-30T19:17:58+00:00" }, { "name": "symfony/string", - "version": "v6.1.7", + "version": "v6.0.17", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "823f143370880efcbdfa2dbca946b3358c4707e5" + "reference": "3f57003dd8a67ed76870cc03092f8501db7788d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/823f143370880efcbdfa2dbca946b3358c4707e5", - "reference": "823f143370880efcbdfa2dbca946b3358c4707e5", + "url": "https://api.github.com/repos/symfony/string/zipball/3f57003dd8a67ed76870cc03092f8501db7788d9", + "reference": "3f57003dd8a67ed76870cc03092f8501db7788d9", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -3382,7 +3423,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.1.7" + "source": "https://github.com/symfony/string/tree/v6.0.17" }, "funding": [ { @@ -3398,7 +3439,7 @@ "type": "tidelift" } ], - "time": "2022-10-10T09:34:31+00:00" + "time": "2022-12-14T15:52:41+00:00" }, { "name": "theseer/tokenizer", diff --git a/coverage.xml b/coverage.xml deleted file mode 100644 index 4b7db6c..0000000 --- a/coverage.xml +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/ClassTransformable.php b/src/ClassTransformable.php new file mode 100644 index 0000000..384bf41 --- /dev/null +++ b/src/ClassTransformable.php @@ -0,0 +1,19 @@ + $args + * + * @return T + * @throws ReflectionException + */ + public function transform(...$args): mixed; +} diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index f39584d..3c48198 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -8,6 +8,7 @@ /** * Class ClassTransformer * + * @template T of ClassTransformable * @package ClassTransformer */ final class ClassTransformer @@ -15,94 +16,49 @@ final class ClassTransformer /** * Class-transformer function to transform our object into a typed object * - * @template T - * - * @param class-string|array> $className + * @param class-string $className * @param array|object|null $args * - * @return null|T|array - * @throws ClassNotFoundException|ReflectionException + * @return null|T + * @throws ClassNotFoundException + * @throws ReflectionException */ - public static function transform(string|array $className, ...$args) + public static function transform(string $className, ...$args) { - if (is_string($className)) { - return self::dataConverting($className, ...$args); - } - - if (sizeof(func_get_args()) === 1) { - throw new \RuntimeException('Input parameter error. Named arguments are not supported for an anonymous array of classes'); - } - - if (empty($args) || !is_array($args[0])) { - return null; - } - - if (sizeof($className) === 1) { - return self::anonymousArrayConverting($className[0], $args[0]); - } - - return self::extractArrayConverting($className, $args[0]); + return (new TransformBuilder($className, ...$args)) + ->build(); } /** - * @template T - * * @param class-string $className - * @param array $args + * @param array>|array $args * - * @return array - * @throws ClassNotFoundException|ReflectionException + * @return null|array|array + * @throws ClassNotFoundException + * @throws ReflectionException */ - private static function anonymousArrayConverting(string $className, array $args): array + public static function transformCollection(string $className, array $args): ?array { $result = []; foreach ($args as $item) { - /** @var T $item */ - $result [] = self::dataConverting($className, $item); + $result [] = self::transform($className, $item); } return $result; } /** - * @template T - * * @param array> $className - * @param array $args + * @param array>|array $args * - * @return array + * @return null|array|array * @throws ClassNotFoundException|ReflectionException */ - private static function extractArrayConverting(array $className, $args): array + public static function transformMultiple(array $className, array $args): ?array { $result = []; foreach ($className as $key => $class) { - $result [] = self::dataConverting($class, $args[$key]); + $result [] = self::transform($class, $args[$key]); } return $result; } - - /** - * @template T - * - * @param class-string $className - * @param array|object|null $args - * - * @return T - * @throws ClassNotFoundException|ReflectionException - */ - private static function dataConverting(string $className, ...$args) - { - if (!class_exists($className)) { - throw new ClassNotFoundException("Class $className not found. Please check the class path you specified."); - } - - if (method_exists($className, 'transform')) { - return $className::transform(...$args); - } - - if (empty($args)) { - return new $className(); - } - return (new PropertyTransformer($className, ...$args))->transform(); - } } diff --git a/src/PropertyTransformer.php b/src/GenericInstance.php similarity index 54% rename from src/PropertyTransformer.php rename to src/GenericInstance.php index fbdc083..de09f60 100644 --- a/src/PropertyTransformer.php +++ b/src/GenericInstance.php @@ -6,103 +6,69 @@ use ReflectionProperty; use ReflectionException; use ReflectionNamedType; -use ClassTransformer\DTO\Property; -use ClassTransformer\Attributes\NotTransform; +use ClassTransformer\WritingStyleUtil; use ClassTransformer\Attributes\ConvertArray; use ClassTransformer\Attributes\WritingStyle; +use ClassTransformer\Validators\ClassExistsValidator; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\Exceptions\ValueNotFoundException; -use function sizeof; -use function func_get_args; -use function array_key_exists; - /** - * Class ClassTransformerService + * Class GenericInstance + * + * @template T of ClassTransformable * * @author yzen.dev */ -final class PropertyTransformer +final class GenericInstance { - /** - * @template T - * class-string $className - */ - private string $className; + /** @var class-string $class */ + private string $class; - /** @var array */ + /** @var array $args */ private array $args; /** - * @var ReflectionClass + * @param class-string $class + * + * @throws ClassNotFoundException */ - private ReflectionClass $refInstance; + public function __construct(string $class) + { + new ClassExistsValidator($class); + + $this->class = $class; + } /** - * @template T - * - * @param ReflectionClass|string $class * @param array|object|null $args * + * @return T * @throws ClassNotFoundException - * @throws ReflectionException */ - public function __construct(ReflectionClass|string $class, ...$args) + public function transform(...$args): mixed { - if ($class instanceof ReflectionClass) { - $this->className = $class->getName(); - /** @phpstan-ignore-next-line */ - $this->refInstance = $class; - } else { - $this->className = $class; - - $this->validate(); + /** @var T $instance */ + $instance = new $this->class(); + try { + $refInstance = new ReflectionClass($this->class); /** @phpstan-ignore-next-line */ - $this->refInstance = new ReflectionClass($this->className); + } catch (ReflectionException) { + throw new ClassNotFoundException('Class ' . $this->class . ' not found. Please check the class path you specified.'); } - // Arguments transfer as named arguments (for php8) - // if dynamic arguments, named ones lie immediately in the root, if they were passed as an array, then they need to be unpacked - $inArgs = sizeof(func_get_args()) === 1 ? $args : $args[0]; + // Unpacking named arguments + $inArgs = sizeof(func_get_args()) === 1 ? $args[0] : $args; if (is_object($inArgs)) { $inArgs = (array)$inArgs; } $this->args = $inArgs ?? []; - } - /** - * @return void - * @throws ClassNotFoundException - */ - private function validate(): void - { - if (!class_exists($this->className)) { - throw new ClassNotFoundException("Class $this->className not found. Please check the class path you specified."); - } - } - - /** - * @template T - * - * @return T - * @throws ClassNotFoundException|ReflectionException - */ - public function transform() - { - // if exist custom transform method - if (method_exists($this->className, 'transform')) { - /** @phpstan-ignore-next-line */ - return $this->className::transform($this->args); - } - - /** @var T $instance */ - $instance = new $this->className(); - - foreach ($this->refInstance->getProperties() as $item) { - $property = new Property($this->refInstance->getProperty($item->name)); + foreach ($refInstance->getProperties() as $item) { + $property = new GenericProperty($item); try { $value = $this->getValue($item); @@ -123,9 +89,9 @@ public function transform() $arrayType = PropertyHelper::getClassFromPhpDoc($property->getDocComment()); } - if (!empty($arrayType) && !empty($value) && !PropertyHelper::propertyIsScalar($arrayType)) { + if (!empty($arrayType) && !empty($value) && is_array($value) && !PropertyHelper::propertyIsScalar($arrayType)) { foreach ($value as $el) { - $instance->{$item->name}[] = (new self($arrayType, $el))->transform(); + $instance->{$item->name}[] = (new TransformBuilder($arrayType, $el))->build(); } continue; } @@ -135,16 +101,9 @@ public function transform() } if ($property->getType() instanceof ReflectionNamedType) { - /** @phpstan-ignore-next-line */ + /** @var class-string $propertyClass */ $propertyClass = $property->getType()->getName(); - $childrenRefInstance = new ReflectionClass($propertyClass); - if ($childrenRefInstance->isEnum()) { - $value = constant($propertyClass . '::' . $value); - $instance->{$item->name} = $value; - continue; - } - - $instance->{$item->name} = (new self($childrenRefInstance, $value))->transform(); + $instance->{$item->name} = (new TransformBuilder($propertyClass, $value))->build(); continue; } $instance->{$item->name} = $value; @@ -155,7 +114,7 @@ public function transform() /** * @param ReflectionProperty $item * - * @return mixed|object|void + * @return mixed|object|array|null * @throws ValueNotFoundException */ private function getValue(ReflectionProperty $item) diff --git a/src/DTO/Property.php b/src/GenericProperty.php similarity index 97% rename from src/DTO/Property.php rename to src/GenericProperty.php index 940778a..a5cfe84 100644 --- a/src/DTO/Property.php +++ b/src/GenericProperty.php @@ -1,23 +1,23 @@ */ -final class Property +final class GenericProperty { /** * @var ReflectionProperty diff --git a/src/PropertyHelper.php b/src/PropertyHelper.php index 65364f5..7ea2190 100644 --- a/src/PropertyHelper.php +++ b/src/PropertyHelper.php @@ -6,6 +6,7 @@ use function is_array; use function sizeof; use function array_intersect; + /** * Class PropertyHelper * @@ -26,17 +27,14 @@ public static function getClassFromPhpDoc($phpDoc): ?string } return null; } - + /** - * @param array|string $type + * @param string $type * * @return bool */ - public static function propertyIsScalar(array|string $type): bool + public static function propertyIsScalar(string $type): bool { - if (is_array($type)) { - return sizeof(array_intersect($type, ['int', 'float', 'string', 'bool', 'mixed'])) > 0; - } return in_array($type, ['int', 'float', 'string', 'bool', 'mixed']); } } diff --git a/src/TransformBuilder.php b/src/TransformBuilder.php new file mode 100644 index 0000000..d670a22 --- /dev/null +++ b/src/TransformBuilder.php @@ -0,0 +1,62 @@ + $class + */ + private string $class; + + /** @var array $args */ + private array $args; + + /** + * @param class-string $class + * @param array|object|null|mixed $args + * + * @throws ClassNotFoundException + */ + public function __construct(string $class, ...$args) + { + new ClassExistsValidator($class); + + $this->class = $class; + $this->args = $args; + } + + /** + * @return T + * @throws ReflectionException|ClassNotFoundException + */ + public function build() + { + if (method_exists($this->class, 'transform')) { + /** @var T $instance */ + $instance = new $this->class(); + $instance->transform(...$this->args); + } else { + $generic = new GenericInstance($this->class); + /** @var T $instance */ + /** @phpstan-ignore-next-line */ + $instance = $generic->transform(...$this->args); + } + + if (method_exists($instance, 'afterTransform')) { + $instance->afterTransform(); + } + + return $instance; + } +} diff --git a/src/Validators/ClassExistsValidator.php b/src/Validators/ClassExistsValidator.php new file mode 100644 index 0000000..ec6916c --- /dev/null +++ b/src/Validators/ClassExistsValidator.php @@ -0,0 +1,25 @@ + + */ +class ClassExistsValidator +{ + /** + * @param class-string $className + * + * @throws ClassNotFoundException + */ + public function __construct($className) + { + if (!class_exists($className)) { + throw new ClassNotFoundException("Class $className not found. Please check the class path you specified."); + } + } +} diff --git a/tests/Benchmark/CheckBench.php b/tests/Benchmark/CheckBench.php deleted file mode 100644 index 3c09477..0000000 --- a/tests/Benchmark/CheckBench.php +++ /dev/null @@ -1,55 +0,0 @@ -id = 1; - $productOne->name = 'phone'; - $productOne->price = 43.03; - - $productTwo = new ProductDTO(); - $productTwo->id = 2; - $productTwo->name = 'bread'; - $productTwo->price = 10.56; - - $user = new UserDTO(); - $user->id = 1; - $user->email = 'fake@mail.com'; - $user->balance = 10012.23; - - $data = new PurchaseDTO(); - $data->products = [$productOne, $productTwo]; - $data->user = $user; - } - - /** - * @Revs(5000) - */ - public function benchTransformReflection(): void - { - $data = $this->getRecursiveObject(); - ClassTransformer::transform(PurchaseDTO::class, $data); - } - -} diff --git a/tests/Benchmark/GetEnumBench.php b/tests/Benchmark/GetEnumBench.php deleted file mode 100644 index 89a55b6..0000000 --- a/tests/Benchmark/GetEnumBench.php +++ /dev/null @@ -1,36 +0,0 @@ -hasConstant($key)) { - $value = $reflection->getConstant($key); - } - } - -} diff --git a/tests/ClassTransformerExceptionsTest.php b/tests/ClassTransformerExceptionsTest.php index 175f654..a316c07 100644 --- a/tests/ClassTransformerExceptionsTest.php +++ b/tests/ClassTransformerExceptionsTest.php @@ -31,13 +31,4 @@ public function testClassNotFoundPhp8(): void ClassTransformer::transform(FakeClassDTO::class, fake: ['exception']); } - - - public function testInvalidExtractArray(): void - { - $this->expectException(\RuntimeException::class); - $userDTO = ClassTransformer::transform([UserDTO::class], a: 1); - self::assertInstanceOf(UserDTO::class, $userDTO); - self::assertTrue(!isset($userDTO->id)); - } } diff --git a/tests/ClassTransformerFromArrayTest.php b/tests/ClassTransformerFromArrayTest.php index 8a6c441..2131aac 100644 --- a/tests/ClassTransformerFromArrayTest.php +++ b/tests/ClassTransformerFromArrayTest.php @@ -66,11 +66,11 @@ public function testNullArray(): void /** * @throws ReflectionException|ClassNotFoundException */ - public function testAnonymousArray(): void + public function testTransformCollection(): void { $data = $this->getArrayUsers(); - $users = ClassTransformer::transform([UserDTO::class], $data); + $users = ClassTransformer::transformCollection(UserDTO::class, $data); self::assertCount(count($data), $users); foreach ($users as $key => $user) { @@ -84,12 +84,12 @@ public function testAnonymousArray(): void /** * @throws ReflectionException|ClassNotFoundException */ - public function testExtractArrayConverting(): void + public function testTransformMultiple(): void { $userData = $this->getBaseArrayData(); $purchaseData = $this->getRecursiveArrayData(); - $result = ClassTransformer::transform([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); + $result = ClassTransformer::transformMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); [$user, $purchase] = $result; @@ -101,15 +101,6 @@ public function testExtractArrayConverting(): void self::assertInstanceOf(PurchaseDTO::class, $purchase); } - /** - * @throws ReflectionException|ClassNotFoundException - */ - public function testInvalidExtractArray(): void - { - $userDTO = ClassTransformer::transform([UserDTO::class], null); - self::assertNull($userDTO); - } - /** * @throws ReflectionException|ClassNotFoundException */ diff --git a/tests/DTO/ColorEnum.php b/tests/DTO/ColorEnum.php deleted file mode 100644 index c61f670..0000000 --- a/tests/DTO/ColorEnum.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ -enum ColorEnum -{ - case Red; - case Black; - case White; -} diff --git a/tests/DTO/CustomTransformUserDTO.php b/tests/DTO/CustomTransformUserDTO.php index 5adf05e..d9d0159 100644 --- a/tests/DTO/CustomTransformUserDTO.php +++ b/tests/DTO/CustomTransformUserDTO.php @@ -15,11 +15,9 @@ class CustomTransformUserDTO * @param $fio * @return CustomTransformUserDTO */ - public static function transform($login, $fio) + public function transform($login, $fio) { - $dto = new self(); - $dto->email = $login; - $dto->username = $fio; - return $dto; + $this->email = $login; + $this->username = $fio; } } diff --git a/tests/DTO/CustomTransformUserDTOArray.php b/tests/DTO/CustomTransformUserDTOArray.php index 17c5c45..ff80839 100644 --- a/tests/DTO/CustomTransformUserDTOArray.php +++ b/tests/DTO/CustomTransformUserDTOArray.php @@ -8,15 +8,10 @@ class CustomTransformUserDTOArray { public string $email; public string $username; - - /** - * @return CustomTransformUserDTO - */ - public static function transform($args) + + public function transform($args) { - $dto = new self(); - $dto->email = $args['login']; - $dto->username = $args['fio']; - return $dto; + $this->email = $args['login']; + $this->username = $args['fio']; } } diff --git a/tests/DTO/ExampleWithEnumDTO.php b/tests/DTO/ExampleWithEnumDTO.php deleted file mode 100644 index fd439f3..0000000 --- a/tests/DTO/ExampleWithEnumDTO.php +++ /dev/null @@ -1,14 +0,0 @@ - - */ -class ExampleWithEnumDTO -{ - public ColorEnum $colorEnum; - public ColorScalarEnum $colorScalarEnum; -} diff --git a/tests/DTO/UserNotTransformRelationDTO.php b/tests/DTO/UserNotTransformRelationDTO.php index cdc772e..c54728e 100644 --- a/tests/DTO/UserNotTransformRelationDTO.php +++ b/tests/DTO/UserNotTransformRelationDTO.php @@ -6,7 +6,7 @@ class UserNotTransformRelationDTO { - public static function transform() + public function transform() { throw new \Exception(); } diff --git a/tests/EnumTest.php b/tests/EnumTest.php deleted file mode 100644 index 2c242c3..0000000 --- a/tests/EnumTest.php +++ /dev/null @@ -1,42 +0,0 @@ - 'Red', - 'colorScalarEnum' => 'Red', - ]; - $model = ClassTransformer::transform(ExampleWithEnumDTO::class, $data); - - self::assertInstanceOf(ExampleWithEnumDTO::class, $model); - self::assertInstanceOf(ColorEnum::class, $model->colorEnum); - self::assertEquals(ColorEnum::Red, $model->colorEnum); - self::assertInstanceOf(ColorScalarEnum::class, $model->colorScalarEnum); - self::assertEquals(ColorScalarEnum::Red, $model->colorScalarEnum); - } - -} From b93fd36fa3719ab95ecefdba472d3cb03dad69b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 17 Jan 2023 22:43:15 +0300 Subject: [PATCH 014/100] composer update --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index c913798..73f1ef4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c3b63bca52c0fd6871611546770f8673", + "content-hash": "310860ec6cfd82dcb4d10119524a4bb6", "packages": [], "packages-dev": [ { From dc9ee3c4a2ecf8dee276ecd203f2c3f2c3ec040a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 18 Jan 2023 00:31:43 +0300 Subject: [PATCH 015/100] add: support enum in 8.1 --- composer.json | 4 +- composer.lock | 222 ++++++++---------- src/GenericInstance.php | 21 +- src/PropertyHelper.php | 40 ---- ...ritingStyleUtil.php => TransformUtils.php} | 26 +- tests/DTO/ColorEnum.php | 15 ++ tests/DTO/ExampleWithEnumDTO.php | 14 ++ tests/EnumTest.php | 42 ++++ 8 files changed, 205 insertions(+), 179 deletions(-) delete mode 100644 src/PropertyHelper.php rename src/{WritingStyleUtil.php => TransformUtils.php} (53%) create mode 100644 tests/DTO/ColorEnum.php create mode 100644 tests/DTO/ExampleWithEnumDTO.php create mode 100644 tests/EnumTest.php diff --git a/composer.json b/composer.json index 9072851..dc76782 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "1.3", + "version": "2.0", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, @@ -30,7 +30,7 @@ } }, "require": { - "php": "^8.0" + "php": "^8.1" }, "require-dev": { "mockery/mockery": "1.5.1", diff --git a/composer.lock b/composer.lock index 73f1ef4..98a7b3f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "310860ec6cfd82dcb4d10119524a4bb6", + "content-hash": "ab0db88bfc1e5def7c1a62dc7287b31c", "packages": [], "packages-dev": [ { @@ -83,75 +83,32 @@ }, "time": "2022-12-19T18:17:20+00:00" }, - { - "name": "doctrine/deprecations", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "shasum": "" - }, - "require": { - "php": "^7.1|^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" - }, - "time": "2022-05-02T15:47:09+00:00" - }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -178,7 +135,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -194,32 +151,31 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "doctrine/lexer", - "version": "2.1.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" + "reference": "84a527db05647743d50373e0ec53a152f2cde568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", + "reference": "84a527db05647743d50373e0ec53a152f2cde568", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.5", "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^4.11 || ^5.0" + "vimeo/psalm": "^5.0" }, "type": "library", "autoload": { @@ -256,7 +212,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/2.1.0" + "source": "https://github.com/doctrine/lexer/tree/3.0.0" }, "funding": [ { @@ -272,7 +228,7 @@ "type": "tidelift" } ], - "time": "2022-12-14T08:49:07+00:00" + "time": "2022-12-15T16:57:16+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -2532,20 +2488,21 @@ }, { "name": "symfony/console", - "version": "v6.0.17", + "version": "v6.2.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2ab307342a7233b9a260edd5ef94087aaca57d18" + "reference": "0f579613e771dba2dbb8211c382342a641f5da06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2ab307342a7233b9a260edd5ef94087aaca57d18", - "reference": "2ab307342a7233b9a260edd5ef94087aaca57d18", + "url": "https://api.github.com/repos/symfony/console/zipball/0f579613e771dba2dbb8211c382342a641f5da06", + "reference": "0f579613e771dba2dbb8211c382342a641f5da06", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^1.1|^2|^3", "symfony/string": "^5.4|^6.0" @@ -2607,7 +2564,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.17" + "source": "https://github.com/symfony/console/tree/v6.2.3" }, "funding": [ { @@ -2623,29 +2580,29 @@ "type": "tidelift" } ], - "time": "2022-12-28T14:21:34+00:00" + "time": "2022-12-28T14:26:22+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.2", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -2674,7 +2631,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" }, "funding": [ { @@ -2690,24 +2647,24 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/filesystem", - "version": "v6.0.13", + "version": "v6.2.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3adca49133bd055ebe6011ed1e012be3c908af79" + "reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3adca49133bd055ebe6011ed1e012be3c908af79", - "reference": "3adca49133bd055ebe6011ed1e012be3c908af79", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/50b2523c874605cf3d4acf7a9e2b30b6a440a016", + "reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, @@ -2737,7 +2694,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.0.13" + "source": "https://github.com/symfony/filesystem/tree/v6.2.0" }, "funding": [ { @@ -2753,24 +2710,27 @@ "type": "tidelift" } ], - "time": "2022-09-21T20:25:27+00:00" + "time": "2022-11-20T13:01:27+00:00" }, { "name": "symfony/finder", - "version": "v6.0.17", + "version": "v6.2.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d467d625fc88f7cebf96f495e588a7196a669db1" + "reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d467d625fc88f7cebf96f495e588a7196a669db1", - "reference": "d467d625fc88f7cebf96f495e588a7196a669db1", + "url": "https://api.github.com/repos/symfony/finder/zipball/81eefbddfde282ee33b437ba5e13d7753211ae8e", + "reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" }, "type": "library", "autoload": { @@ -2798,7 +2758,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.0.17" + "source": "https://github.com/symfony/finder/tree/v6.2.3" }, "funding": [ { @@ -2814,24 +2774,24 @@ "type": "tidelift" } ], - "time": "2022-12-22T17:53:58+00:00" + "time": "2022-12-22T17:55:15+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.0.3", + "version": "v6.2.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "51f7006670febe4cbcbae177cbffe93ff833250d" + "reference": "d28f02acde71ff75e957082cd36e973df395f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/51f7006670febe4cbcbae177cbffe93ff833250d", - "reference": "51f7006670febe4cbcbae177cbffe93ff833250d", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d28f02acde71ff75e957082cd36e973df395f626", + "reference": "d28f02acde71ff75e957082cd36e973df395f626", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", @@ -2865,7 +2825,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.0.3" + "source": "https://github.com/symfony/options-resolver/tree/v6.2.0" }, "funding": [ { @@ -2881,7 +2841,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2022-11-02T09:08:04+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3215,20 +3175,20 @@ }, { "name": "symfony/process", - "version": "v6.0.11", + "version": "v6.2.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "44270a08ccb664143dede554ff1c00aaa2247a43" + "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/44270a08ccb664143dede554ff1c00aaa2247a43", - "reference": "44270a08ccb664143dede554ff1c00aaa2247a43", + "url": "https://api.github.com/repos/symfony/process/zipball/ba6e55359f8f755fe996c58a81e00eaa67a35877", + "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -3256,7 +3216,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.0.11" + "source": "https://github.com/symfony/process/tree/v6.2.0" }, "funding": [ { @@ -3272,24 +3232,24 @@ "type": "tidelift" } ], - "time": "2022-06-27T17:10:44+00:00" + "time": "2022-11-02T09:08:04+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.0.2", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", - "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75", + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "psr/container": "^2.0" }, "conflict": { @@ -3301,7 +3261,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -3311,7 +3271,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3338,7 +3301,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.2.0" }, "funding": [ { @@ -3354,24 +3317,24 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:17:58+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/string", - "version": "v6.0.17", + "version": "v6.2.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "3f57003dd8a67ed76870cc03092f8501db7788d9" + "reference": "863219fd713fa41cbcd285a79723f94672faff4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/3f57003dd8a67ed76870cc03092f8501db7788d9", - "reference": "3f57003dd8a67ed76870cc03092f8501db7788d9", + "url": "https://api.github.com/repos/symfony/string/zipball/863219fd713fa41cbcd285a79723f94672faff4d", + "reference": "863219fd713fa41cbcd285a79723f94672faff4d", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -3383,6 +3346,7 @@ "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", "symfony/translation-contracts": "^2.0|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, @@ -3423,7 +3387,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.17" + "source": "https://github.com/symfony/string/tree/v6.2.2" }, "funding": [ { @@ -3439,7 +3403,7 @@ "type": "tidelift" } ], - "time": "2022-12-14T15:52:41+00:00" + "time": "2022-12-14T16:11:27+00:00" }, { "name": "theseer/tokenizer", @@ -3547,7 +3511,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.0" + "php": "^8.1" }, "platform-dev": [], "plugin-api-version": "2.3.0" diff --git a/src/GenericInstance.php b/src/GenericInstance.php index de09f60..6dc18d3 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -6,7 +6,6 @@ use ReflectionProperty; use ReflectionException; use ReflectionNamedType; -use ClassTransformer\WritingStyleUtil; use ClassTransformer\Attributes\ConvertArray; use ClassTransformer\Attributes\WritingStyle; use ClassTransformer\Validators\ClassExistsValidator; @@ -86,10 +85,10 @@ public function transform(...$args): mixed if (!empty($arrayTypeAttr)) { $arrayType = $arrayTypeAttr[0]->getArguments()[0]; } else { - $arrayType = PropertyHelper::getClassFromPhpDoc($property->getDocComment()); + $arrayType = TransformUtils::getClassFromPhpDoc($property->getDocComment()); } - if (!empty($arrayType) && !empty($value) && is_array($value) && !PropertyHelper::propertyIsScalar($arrayType)) { + if (!empty($arrayType) && !empty($value) && is_array($value) && !TransformUtils::propertyIsScalar($arrayType)) { foreach ($value as $el) { $instance->{$item->name}[] = (new TransformBuilder($arrayType, $el))->build(); } @@ -103,6 +102,14 @@ public function transform(...$args): mixed if ($property->getType() instanceof ReflectionNamedType) { /** @var class-string $propertyClass */ $propertyClass = $property->getType()->getName(); + + $childrenRefInstance = new ReflectionClass($propertyClass); + if ($childrenRefInstance->isEnum()) { + $value = constant($propertyClass . '::' . $value); + $instance->{$item->name} = $value; + continue; + } + $instance->{$item->name} = (new TransformBuilder($propertyClass, $value))->build(); continue; } @@ -132,15 +139,15 @@ private function getValue(ReflectionProperty $item) $styles = $style->getArguments(); if ( (in_array(WritingStyle::STYLE_SNAKE_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) & - array_key_exists(WritingStyleUtil::strToSnakeCase($item->name), $this->args) + array_key_exists(TransformUtils::strToSnakeCase($item->name), $this->args) ) { - return $this->args[WritingStyleUtil::strToSnakeCase($item->name)]; + return $this->args[TransformUtils::strToSnakeCase($item->name)]; } if ( (in_array(WritingStyle::STYLE_CAMEL_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) & - array_key_exists(WritingStyleUtil::strToCamelCase($item->name), $this->args) + array_key_exists(TransformUtils::strToCamelCase($item->name), $this->args) ) { - return $this->args[WritingStyleUtil::strToCamelCase($item->name)]; + return $this->args[TransformUtils::strToCamelCase($item->name)]; } } throw new ValueNotFoundException(); diff --git a/src/PropertyHelper.php b/src/PropertyHelper.php deleted file mode 100644 index 7ea2190..0000000 --- a/src/PropertyHelper.php +++ /dev/null @@ -1,40 +0,0 @@ - - */ -final class PropertyHelper -{ - /** - * @param string|bool $phpDoc - * - * @return string|null - */ - public static function getClassFromPhpDoc($phpDoc): ?string - { - if (is_string($phpDoc)) { - preg_match('/array<([a-zA-Z\d\\\]+)>/m', $phpDoc, $arrayType); - return $arrayType[1] ?? null; - } - return null; - } - - /** - * @param string $type - * - * @return bool - */ - public static function propertyIsScalar(string $type): bool - { - return in_array($type, ['int', 'float', 'string', 'bool', 'mixed']); - } -} diff --git a/src/WritingStyleUtil.php b/src/TransformUtils.php similarity index 53% rename from src/WritingStyleUtil.php rename to src/TransformUtils.php index 3a23e63..d73fc3a 100644 --- a/src/WritingStyleUtil.php +++ b/src/TransformUtils.php @@ -11,7 +11,7 @@ /** * */ -final class WritingStyleUtil +final class TransformUtils { /** * @param string $string @@ -33,4 +33,28 @@ public static function strToCamelCase(string $string): string { return lcfirst(str_replace('_', '', ucwords($string, '_'))); } + + /** + * @param string|bool $phpDoc + * + * @return string|null + */ + public static function getClassFromPhpDoc($phpDoc): ?string + { + if (is_string($phpDoc)) { + preg_match('/array<([a-zA-Z\d\\\]+)>/m', $phpDoc, $arrayType); + return $arrayType[1] ?? null; + } + return null; + } + + /** + * @param string $type + * + * @return bool + */ + public static function propertyIsScalar(string $type): bool + { + return in_array($type, ['int', 'float', 'string', 'bool', 'mixed']); + } } diff --git a/tests/DTO/ColorEnum.php b/tests/DTO/ColorEnum.php new file mode 100644 index 0000000..c61f670 --- /dev/null +++ b/tests/DTO/ColorEnum.php @@ -0,0 +1,15 @@ + + */ +enum ColorEnum +{ + case Red; + case Black; + case White; +} diff --git a/tests/DTO/ExampleWithEnumDTO.php b/tests/DTO/ExampleWithEnumDTO.php new file mode 100644 index 0000000..fd439f3 --- /dev/null +++ b/tests/DTO/ExampleWithEnumDTO.php @@ -0,0 +1,14 @@ + + */ +class ExampleWithEnumDTO +{ + public ColorEnum $colorEnum; + public ColorScalarEnum $colorScalarEnum; +} diff --git a/tests/EnumTest.php b/tests/EnumTest.php new file mode 100644 index 0000000..2c242c3 --- /dev/null +++ b/tests/EnumTest.php @@ -0,0 +1,42 @@ + 'Red', + 'colorScalarEnum' => 'Red', + ]; + $model = ClassTransformer::transform(ExampleWithEnumDTO::class, $data); + + self::assertInstanceOf(ExampleWithEnumDTO::class, $model); + self::assertInstanceOf(ColorEnum::class, $model->colorEnum); + self::assertEquals(ColorEnum::Red, $model->colorEnum); + self::assertInstanceOf(ColorScalarEnum::class, $model->colorScalarEnum); + self::assertEquals(ColorScalarEnum::Red, $model->colorScalarEnum); + } + +} From 5fac325f69506e04d105886a820053f43eaf0ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 18 Jan 2023 10:18:34 +0300 Subject: [PATCH 016/100] Add: tests --- src/GenericInstance.php | 14 ++++-------- tests/AfterTransformTest.php | 35 +++++++++++++++++++++++++++++ tests/DTO/UserAfterTransformDTO.php | 16 +++++++++++++ tests/Units/GenericInstanceTest.php | 33 +++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 tests/AfterTransformTest.php create mode 100644 tests/DTO/UserAfterTransformDTO.php create mode 100644 tests/Units/GenericInstanceTest.php diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 6dc18d3..7366b6f 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -50,12 +50,7 @@ public function transform(...$args): mixed /** @var T $instance */ $instance = new $this->class(); - try { - $refInstance = new ReflectionClass($this->class); - /** @phpstan-ignore-next-line */ - } catch (ReflectionException) { - throw new ClassNotFoundException('Class ' . $this->class . ' not found. Please check the class path you specified.'); - } + $refInstance = new ReflectionClass($this->class); // Unpacking named arguments $inArgs = sizeof(func_get_args()) === 1 ? $args[0] : $args; @@ -102,14 +97,13 @@ public function transform(...$args): mixed if ($property->getType() instanceof ReflectionNamedType) { /** @var class-string $propertyClass */ $propertyClass = $property->getType()->getName(); - - $childrenRefInstance = new ReflectionClass($propertyClass); - if ($childrenRefInstance->isEnum()) { + + if (enum_exists($property->getType()->getName())) { $value = constant($propertyClass . '::' . $value); $instance->{$item->name} = $value; continue; } - + $instance->{$item->name} = (new TransformBuilder($propertyClass, $value))->build(); continue; } diff --git a/tests/AfterTransformTest.php b/tests/AfterTransformTest.php new file mode 100644 index 0000000..60aed55 --- /dev/null +++ b/tests/AfterTransformTest.php @@ -0,0 +1,35 @@ +getBaseObject(); + $userDTO = ClassTransformer::transform(UserAfterTransformDTO::class, $data); + self::assertInstanceOf(UserAfterTransformDTO::class, $userDTO); + self::assertEquals($data->id, $userDTO->id); + self::assertEquals($data->email, $userDTO->email); + self::assertEquals(777, $userDTO->balance); + } +} diff --git a/tests/DTO/UserAfterTransformDTO.php b/tests/DTO/UserAfterTransformDTO.php new file mode 100644 index 0000000..23eb8ef --- /dev/null +++ b/tests/DTO/UserAfterTransformDTO.php @@ -0,0 +1,16 @@ +balance = 777; + } +} diff --git a/tests/Units/GenericInstanceTest.php b/tests/Units/GenericInstanceTest.php new file mode 100644 index 0000000..041a798 --- /dev/null +++ b/tests/Units/GenericInstanceTest.php @@ -0,0 +1,33 @@ +expectException(ClassNotFoundException::class); + new GenericInstance('Test\Fake\Class'); + } +} From aca1c6b0e1b307cae9f64111a013019e10ea3f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 18 Jan 2023 10:32:48 +0300 Subject: [PATCH 017/100] add bench test --- tests/Benchmark/CheckBench.php | 76 +++++++++++++++++++ tests/{ => Benchmark}/DTO/ProductDTO.php | 2 +- tests/{ => Benchmark}/DTO/PurchaseDTO.php | 2 +- tests/{ => Benchmark}/DTO/UserDTO.php | 2 +- tests/DTO/BasketDTO.php | 10 --- .../{ => Integration}/AfterTransformTest.php | 11 +-- .../ClassTransformerExceptionsTest.php | 7 +- .../ClassTransformerFromArrayTest.php | 19 ++--- .../ClassTransformerFromObjectTest.php | 16 ++-- .../CustomTransformerTest.php | 10 +-- .../{ => Integration}/DTO/ArrayScalarDTO.php | 2 +- tests/Integration/DTO/BasketDTO.php | 10 +++ tests/{ => Integration}/DTO/ColorEnum.php | 2 +- .../{ => Integration}/DTO/ColorScalarEnum.php | 2 +- .../DTO/CustomTransformUserDTO.php | 2 +- .../DTO/CustomTransformUserDTOArray.php | 2 +- .../DTO/ExampleWithEnumDTO.php | 2 +- tests/{ => Integration}/DTO/FakeClassDTO.php | 2 +- tests/Integration/DTO/ProductDTO.php | 12 +++ tests/Integration/DTO/PurchaseDTO.php | 16 ++++ tests/{ => Integration}/DTO/UnionTypeDTO.php | 2 +- .../DTO/UserAfterTransformDTO.php | 2 +- tests/Integration/DTO/UserDTO.php | 12 +++ .../DTO/UserEmptyTypeDTO.php | 2 +- .../DTO/UserNoTypeArrayDTO.php | 2 +- .../DTO/UserNotTransformDTO.php | 2 +- .../DTO/UserNotTransformRelationDTO.php | 2 +- .../DTO/WritingStyleCamelCaseDTO.php | 2 +- .../DTO/WritingStyleEmpyDTO.php | 2 +- .../DTO/WritingStyleSnakeCaseDTO.php | 2 +- tests/{ => Integration}/EnumTest.php | 15 ++-- tests/{ => Integration}/FakerData.php | 2 +- tests/{ => Integration}/NotTransformTest.php | 10 +-- tests/{ => Integration}/UnionTypeTest.php | 9 +-- tests/{ => Integration}/WritingStyleTest.php | 12 +-- tests/Units/GenericInstanceTest.php | 11 +-- 36 files changed, 200 insertions(+), 96 deletions(-) create mode 100644 tests/Benchmark/CheckBench.php rename tests/{ => Benchmark}/DTO/ProductDTO.php (80%) rename tests/{ => Benchmark}/DTO/PurchaseDTO.php (87%) rename tests/{ => Benchmark}/DTO/UserDTO.php (82%) delete mode 100644 tests/DTO/BasketDTO.php rename tests/{ => Integration}/AfterTransformTest.php (82%) rename tests/{ => Integration}/ClassTransformerExceptionsTest.php (91%) rename tests/{ => Integration}/ClassTransformerFromArrayTest.php (95%) rename tests/{ => Integration}/ClassTransformerFromObjectTest.php (96%) rename tests/{ => Integration}/CustomTransformerTest.php (92%) rename tests/{ => Integration}/DTO/ArrayScalarDTO.php (87%) create mode 100644 tests/Integration/DTO/BasketDTO.php rename tests/{ => Integration}/DTO/ColorEnum.php (81%) rename tests/{ => Integration}/DTO/ColorScalarEnum.php (84%) rename tests/{ => Integration}/DTO/CustomTransformUserDTO.php (91%) rename tests/{ => Integration}/DTO/CustomTransformUserDTOArray.php (88%) rename tests/{ => Integration}/DTO/ExampleWithEnumDTO.php (85%) rename tests/{ => Integration}/DTO/FakeClassDTO.php (79%) create mode 100644 tests/Integration/DTO/ProductDTO.php create mode 100644 tests/Integration/DTO/PurchaseDTO.php rename tests/{ => Integration}/DTO/UnionTypeDTO.php (81%) rename tests/{ => Integration}/DTO/UserAfterTransformDTO.php (86%) create mode 100644 tests/Integration/DTO/UserDTO.php rename tests/{ => Integration}/DTO/UserEmptyTypeDTO.php (77%) rename tests/{ => Integration}/DTO/UserNoTypeArrayDTO.php (76%) rename tests/{ => Integration}/DTO/UserNotTransformDTO.php (86%) rename tests/{ => Integration}/DTO/UserNotTransformRelationDTO.php (81%) rename tests/{ => Integration}/DTO/WritingStyleCamelCaseDTO.php (90%) rename tests/{ => Integration}/DTO/WritingStyleEmpyDTO.php (85%) rename tests/{ => Integration}/DTO/WritingStyleSnakeCaseDTO.php (89%) rename tests/{ => Integration}/EnumTest.php (81%) rename tests/{ => Integration}/FakerData.php (99%) rename tests/{ => Integration}/NotTransformTest.php (83%) rename tests/{ => Integration}/UnionTypeTest.php (94%) rename tests/{ => Integration}/WritingStyleTest.php (91%) diff --git a/tests/Benchmark/CheckBench.php b/tests/Benchmark/CheckBench.php new file mode 100644 index 0000000..5e0ad91 --- /dev/null +++ b/tests/Benchmark/CheckBench.php @@ -0,0 +1,76 @@ +getPurcheseObject(); + $productOne = new ProductDTO(); + $productOne->id = $data['products'][0]['id']; + $productOne->name = $data['products'][0]['name']; + $productOne->price = $data['products'][0]['price']; + + $productTwo = new ProductDTO(); + $productTwo->id = $data['products'][0]['id']; + $productTwo->name = $data['products'][0]['name']; + $productTwo->price = $data['products'][0]['price']; + + $user = new UserDTO(); + $user->id = $data['user']['id']; + $user->email = $data['user']['email']; + $user->balance = $data['user']['balance']; + + $data = new PurchaseDTO(); + $data->products = [$productOne, $productTwo]; + $data->user = $user; + } + + /** + * @Revs(5000) + */ + public function benchTransformReflection(): void + { + $data = $this->getPurcheseObject(); + ClassTransformer::transform(PurchaseDTO::class, $data); + } + + public function getPurcheseObject(): array + { + return [ + 'products' => [ + [ + 'id' => 1, + 'name' => 'phone', + 'price' => 43.03, + ], + [ + 'id' => 2, + 'name' => 'bread', + 'price' => 10.56, + ] + ], + 'user'=>[ + 'id' => 1, + 'email' => 'fake@mail.com', + 'balance' => 10012.23, + ] + ]; + } +} diff --git a/tests/DTO/ProductDTO.php b/tests/Benchmark/DTO/ProductDTO.php similarity index 80% rename from tests/DTO/ProductDTO.php rename to tests/Benchmark/DTO/ProductDTO.php index 9e41125..83b8680 100644 --- a/tests/DTO/ProductDTO.php +++ b/tests/Benchmark/DTO/ProductDTO.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\DTO; +namespace Tests\Benchmark\DTO; class ProductDTO { diff --git a/tests/DTO/PurchaseDTO.php b/tests/Benchmark/DTO/PurchaseDTO.php similarity index 87% rename from tests/DTO/PurchaseDTO.php rename to tests/Benchmark/DTO/PurchaseDTO.php index 1272f8e..12d8db6 100644 --- a/tests/DTO/PurchaseDTO.php +++ b/tests/Benchmark/DTO/PurchaseDTO.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\DTO; +namespace Tests\Benchmark\DTO; use ClassTransformer\Attributes\ConvertArray; diff --git a/tests/DTO/UserDTO.php b/tests/Benchmark/DTO/UserDTO.php similarity index 82% rename from tests/DTO/UserDTO.php rename to tests/Benchmark/DTO/UserDTO.php index f9e1c4d..5b2288d 100644 --- a/tests/DTO/UserDTO.php +++ b/tests/Benchmark/DTO/UserDTO.php @@ -1,7 +1,7 @@ $orders Order list */ - public array $orders; -} diff --git a/tests/AfterTransformTest.php b/tests/Integration/AfterTransformTest.php similarity index 82% rename from tests/AfterTransformTest.php rename to tests/Integration/AfterTransformTest.php index 60aed55..9f070e2 100644 --- a/tests/AfterTransformTest.php +++ b/tests/Integration/AfterTransformTest.php @@ -2,16 +2,13 @@ declare(strict_types=1); -namespace Tests; +namespace Tests\Integration; -use ReflectionException; -use Tests\DTO\UserAfterTransformDTO; -use Tests\DTO\WritingStyleCamelCaseDTO; -use PHPUnit\Framework\TestCase; use ClassTransformer\ClassTransformer; use ClassTransformer\Exceptions\ClassNotFoundException; -use Tests\DTO\WritingStyleEmpyDTO; -use Tests\DTO\WritingStyleSnakeCaseDTO; +use PHPUnit\Framework\TestCase; +use ReflectionException; +use Tests\Integration\DTO\UserAfterTransformDTO; /** * Class AfterTransformTest diff --git a/tests/ClassTransformerExceptionsTest.php b/tests/Integration/ClassTransformerExceptionsTest.php similarity index 91% rename from tests/ClassTransformerExceptionsTest.php rename to tests/Integration/ClassTransformerExceptionsTest.php index a316c07..9c8880a 100644 --- a/tests/ClassTransformerExceptionsTest.php +++ b/tests/Integration/ClassTransformerExceptionsTest.php @@ -2,13 +2,12 @@ declare(strict_types=1); -namespace Tests; +namespace Tests\Integration; +use ClassTransformer\ClassTransformer; use ClassTransformer\Exceptions\ClassNotFoundException; -use Tests\DTO\FakeClassDTO; use PHPUnit\Framework\TestCase; -use ClassTransformer\ClassTransformer; -use Tests\DTO\UserDTO; +use Tests\Integration\DTO\FakeClassDTO; /** * Class ClassTransformerExceptionsTest diff --git a/tests/ClassTransformerFromArrayTest.php b/tests/Integration/ClassTransformerFromArrayTest.php similarity index 95% rename from tests/ClassTransformerFromArrayTest.php rename to tests/Integration/ClassTransformerFromArrayTest.php index 2131aac..ca397e8 100644 --- a/tests/ClassTransformerFromArrayTest.php +++ b/tests/Integration/ClassTransformerFromArrayTest.php @@ -2,18 +2,19 @@ declare(strict_types=1); -namespace Tests; +namespace Tests\Integration; -use Tests\DTO\ArrayScalarDTO; -use Tests\DTO\UserDTO; -use Tests\DTO\BasketDTO; -use ReflectionException; -use Tests\DTO\ProductDTO; -use Tests\DTO\PurchaseDTO; -use Tests\DTO\UserEmptyTypeDTO; -use PHPUnit\Framework\TestCase; use ClassTransformer\ClassTransformer; use ClassTransformer\Exceptions\ClassNotFoundException; +use PHPUnit\Framework\TestCase; +use ReflectionException; +use Tests\Integration\DTO\ArrayScalarDTO; +use Tests\Integration\DTO\BasketDTO; +use Tests\Integration\DTO\ProductDTO; +use Tests\Integration\DTO\PurchaseDTO; +use Tests\Integration\DTO\UserDTO; +use Tests\Integration\DTO\UserEmptyTypeDTO; +use function Tests\count; /** * Class ClassTransformerTest diff --git a/tests/ClassTransformerFromObjectTest.php b/tests/Integration/ClassTransformerFromObjectTest.php similarity index 96% rename from tests/ClassTransformerFromObjectTest.php rename to tests/Integration/ClassTransformerFromObjectTest.php index e4d7dab..1ba7a94 100644 --- a/tests/ClassTransformerFromObjectTest.php +++ b/tests/Integration/ClassTransformerFromObjectTest.php @@ -2,17 +2,17 @@ declare(strict_types=1); -namespace Tests; +namespace Tests\Integration; -use Tests\DTO\UserDTO; -use ReflectionException; -use Tests\DTO\BasketDTO; -use Tests\DTO\ProductDTO; -use Tests\DTO\PurchaseDTO; -use PHPUnit\Framework\TestCase; -use Tests\DTO\UserEmptyTypeDTO; use ClassTransformer\ClassTransformer; use ClassTransformer\Exceptions\ClassNotFoundException; +use PHPUnit\Framework\TestCase; +use ReflectionException; +use Tests\Integration\DTO\BasketDTO; +use Tests\Integration\DTO\ProductDTO; +use Tests\Integration\DTO\PurchaseDTO; +use Tests\Integration\DTO\UserDTO; +use Tests\Integration\DTO\UserEmptyTypeDTO; /** * Class ClassTransformerTest diff --git a/tests/CustomTransformerTest.php b/tests/Integration/CustomTransformerTest.php similarity index 92% rename from tests/CustomTransformerTest.php rename to tests/Integration/CustomTransformerTest.php index a44a1e6..5e4dd4d 100644 --- a/tests/CustomTransformerTest.php +++ b/tests/Integration/CustomTransformerTest.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace Tests; +namespace Tests\Integration; +use ClassTransformer\ClassTransformer; use ClassTransformer\Exceptions\ClassNotFoundException; use PHPUnit\Framework\TestCase; -use ClassTransformer\ClassTransformer; use ReflectionException; -use Tests\DTO\CustomTransformUserDTO; -use Tests\DTO\CustomTransformUserDTOArray; -use Tests\DTO\UserNoTypeArrayDTO; +use Tests\Integration\DTO\CustomTransformUserDTO; +use Tests\Integration\DTO\CustomTransformUserDTOArray; +use Tests\Integration\DTO\UserNoTypeArrayDTO; /** * Class CustomTransformerTest diff --git a/tests/DTO/ArrayScalarDTO.php b/tests/Integration/DTO/ArrayScalarDTO.php similarity index 87% rename from tests/DTO/ArrayScalarDTO.php rename to tests/Integration/DTO/ArrayScalarDTO.php index f604622..0957a0f 100644 --- a/tests/DTO/ArrayScalarDTO.php +++ b/tests/Integration/DTO/ArrayScalarDTO.php @@ -1,7 +1,7 @@ $orders Order list */ + public array $orders; +} diff --git a/tests/DTO/ColorEnum.php b/tests/Integration/DTO/ColorEnum.php similarity index 81% rename from tests/DTO/ColorEnum.php rename to tests/Integration/DTO/ColorEnum.php index c61f670..41858b1 100644 --- a/tests/DTO/ColorEnum.php +++ b/tests/Integration/DTO/ColorEnum.php @@ -1,6 +1,6 @@ Date: Wed, 18 Jan 2023 12:04:25 +0300 Subject: [PATCH 018/100] fix scalar enum --- src/GenericInstance.php | 8 ++++++-- tests/Integration/EnumTest.php | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 7366b6f..c0dc0eb 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -99,8 +99,12 @@ public function transform(...$args): mixed $propertyClass = $property->getType()->getName(); if (enum_exists($property->getType()->getName())) { - $value = constant($propertyClass . '::' . $value); - $instance->{$item->name} = $value; + if (method_exists($propertyClass, 'from')) { + /** @var \UnitEnum $propertyClass */ + $instance->{$item->name} = $propertyClass::from($value); + } else { + $instance->{$item->name} = constant($propertyClass . '::' . $value); + } continue; } diff --git a/tests/Integration/EnumTest.php b/tests/Integration/EnumTest.php index e75b1d7..d7679f5 100644 --- a/tests/Integration/EnumTest.php +++ b/tests/Integration/EnumTest.php @@ -25,10 +25,10 @@ public function testEmptyWritingStyle(): void { $data = [ 'colorEnum' => 'Red', - 'colorScalarEnum' => 'Red', + 'colorScalarEnum' => 'R', ]; $model = ClassTransformer::transform(ExampleWithEnumDTO::class, $data); - + self::assertInstanceOf(ExampleWithEnumDTO::class, $model); self::assertInstanceOf(ColorEnum::class, $model->colorEnum); self::assertEquals(ColorEnum::Red, $model->colorEnum); From 2331a101940a68f9de03dc223f412f93873c09a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 18 Jan 2023 15:44:08 +0300 Subject: [PATCH 019/100] add: argument resource --- src/ArgumentsResource.php | 86 +++++++++++++++++++ src/GenericInstance.php | 64 +++----------- src/GenericProperty.php | 65 ++++++++------ src/TransformBuilder.php | 4 +- tests/Benchmark/CheckBench.php | 9 +- tests/Benchmark/DTO/UserDTO.php | 7 +- tests/Benchmark/DTO/UserTypeEnum.php | 14 +++ tests/Benchmark/FullCheckBench.php | 86 +++++++++++++++++++ tests/Benchmark/LiteCheckBench.php | 51 +++++++++++ .../ClassTransformerFromArrayTest.php | 2 +- tests/Units/GenericInstanceTest.php | 5 +- 11 files changed, 307 insertions(+), 86 deletions(-) create mode 100644 src/ArgumentsResource.php create mode 100644 tests/Benchmark/DTO/UserTypeEnum.php create mode 100644 tests/Benchmark/FullCheckBench.php create mode 100644 tests/Benchmark/LiteCheckBench.php diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php new file mode 100644 index 0000000..afdbfae --- /dev/null +++ b/src/ArgumentsResource.php @@ -0,0 +1,86 @@ + + */ +final class ArgumentsResource +{ + /** @var array $args */ + private array $args; + + /** + * @param array|object|null $args + */ + public function __construct(...$args) + { + // Unpacking named arguments + $inArgs = sizeof(func_get_args()) === 1 ? $args[0] : $args; + + if (is_object($inArgs)) { + $inArgs = (array)$inArgs; + } + + $this->args = $inArgs ?? []; + } + + /** + * @param GenericProperty $genericProperty + * + * @return mixed|object|array|null + * @throws ValueNotFoundException + */ + public function getValue(GenericProperty $genericProperty): mixed + { + if (array_key_exists($genericProperty->name, $this->args)) { + return $this->args[$genericProperty->name]; + } + + $writingStyle = $genericProperty->getAttribute(WritingStyle::class); + + if (empty($writingStyle)) { + throw new ValueNotFoundException(); + } + + $styles = $writingStyle->getArguments(); + + if (empty($styles)) { + throw new ValueNotFoundException(); + } + + $snakeCase = TransformUtils::strToSnakeCase($genericProperty->name); + $camelCase = TransformUtils::strToCamelCase($genericProperty->name); + + if ( + (in_array(WritingStyle::STYLE_SNAKE_CASE, $styles, true) || in_array(WritingStyle::STYLE_ALL, $styles, true)) & + array_key_exists($snakeCase, $this->args) + ) { + return $this->args[$snakeCase]; + } + if ( + (in_array(WritingStyle::STYLE_CAMEL_CASE, $styles, true) || in_array(WritingStyle::STYLE_ALL, $styles, true)) & + array_key_exists($camelCase, $this->args) + ) { + return $this->args[$camelCase]; + } + throw new ValueNotFoundException(); + } + + /** + * @return array + */ + public function getArgs(): array + { + return $this->args; + } +} diff --git a/src/GenericInstance.php b/src/GenericInstance.php index c0dc0eb..6b0501f 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -24,19 +24,19 @@ final class GenericInstance /** @var class-string $class */ private string $class; - /** @var array $args */ - private array $args; + /** * @param class-string $class * * @throws ClassNotFoundException */ - public function __construct(string $class) + public function __construct(string $class, ArgumentsResource $argumentsResource) { new ClassExistsValidator($class); $this->class = $class; + $this->argumentsResource = $argumentsResource; } /** @@ -45,38 +45,29 @@ public function __construct(string $class) * @return T * @throws ClassNotFoundException */ - public function transform(...$args): mixed + public function transform(): mixed { /** @var T $instance */ $instance = new $this->class(); $refInstance = new ReflectionClass($this->class); - // Unpacking named arguments - $inArgs = sizeof(func_get_args()) === 1 ? $args[0] : $args; - - if (is_object($inArgs)) { - $inArgs = (array)$inArgs; - } - - $this->args = $inArgs ?? []; - foreach ($refInstance->getProperties() as $item) { $property = new GenericProperty($item); try { - $value = $this->getValue($item); + $value = $this->argumentsResource->getValue($property); } catch (ValueNotFoundException) { continue; } - if ($property->isScalar() || $property->notTransform()) { + if ($property->isScalar || $property->notTransform()) { $instance->{$item->name} = $value; continue; } if ($property->isArray()) { - $arrayTypeAttr = $item->getAttributes(ConvertArray::class); + $arrayTypeAttr = $property->getAttributes(ConvertArray::class); if (!empty($arrayTypeAttr)) { $arrayType = $arrayTypeAttr[0]->getArguments()[0]; } else { @@ -94,11 +85,11 @@ public function transform(...$args): mixed continue; } - if ($property->getType() instanceof ReflectionNamedType) { + if ($property->type instanceof ReflectionNamedType) { /** @var class-string $propertyClass */ - $propertyClass = $property->getType()->getName(); + $propertyClass = $property->type->getName(); - if (enum_exists($property->getType()->getName())) { + if ($property->isEnum()) { if (method_exists($propertyClass, 'from')) { /** @var \UnitEnum $propertyClass */ $instance->{$item->name} = $propertyClass::from($value); @@ -115,39 +106,4 @@ public function transform(...$args): mixed } return $instance; } - - /** - * @param ReflectionProperty $item - * - * @return mixed|object|array|null - * @throws ValueNotFoundException - */ - private function getValue(ReflectionProperty $item) - { - if (array_key_exists($item->name, $this->args)) { - return $this->args[$item->name]; - } - - $writingStyle = $item->getAttributes(WritingStyle::class); - - if (empty($writingStyle)) { - throw new ValueNotFoundException(); - } - foreach ($writingStyle as $style) { - $styles = $style->getArguments(); - if ( - (in_array(WritingStyle::STYLE_SNAKE_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) & - array_key_exists(TransformUtils::strToSnakeCase($item->name), $this->args) - ) { - return $this->args[TransformUtils::strToSnakeCase($item->name)]; - } - if ( - (in_array(WritingStyle::STYLE_CAMEL_CASE, $styles) || in_array(WritingStyle::STYLE_ALL, $styles)) & - array_key_exists(TransformUtils::strToCamelCase($item->name), $this->args) - ) { - return $this->args[TransformUtils::strToCamelCase($item->name)]; - } - } - throw new ValueNotFoundException(); - } } diff --git a/src/GenericProperty.php b/src/GenericProperty.php index a5cfe84..703932f 100644 --- a/src/GenericProperty.php +++ b/src/GenericProperty.php @@ -4,6 +4,7 @@ use ReflectionType; use ReflectionProperty; +use ReflectionAttribute; use ReflectionNamedType; use ReflectionUnionType; use ClassTransformer\Attributes\NotTransform; @@ -24,61 +25,63 @@ final class GenericProperty */ public ReflectionProperty $property; + readonly public ?ReflectionType $type; + readonly public array $types; + + /** @var class-string|string $propertyClass */ + readonly public string $name; + readonly public bool $isScalar; + /** * @param ReflectionProperty $property */ public function __construct(ReflectionProperty $property) { $this->property = $property; + $this->type = $this->property->getType(); + $this->name = $this->property->name; + $this->types = $this->initTypes(); + $this->isScalar = sizeof(array_intersect($this->types, ['int', 'float', 'double', 'string', 'bool', 'mixed'])) > 0; } /** - * @return ReflectionType|null + * @return bool */ - public function getType(): ?ReflectionType + public function isEnum(): bool { - return $this->property->getType(); + return enum_exists($this->type->getName()); } /** * @return array */ - public function getTypes(): array + private function initTypes(): array { $types = []; - $currentType = $this->getType(); - if ($currentType === null) { + + if ($this->type === null) { return []; } - if ($currentType instanceof ReflectionUnionType) { + + if ($this->type instanceof ReflectionUnionType) { $types = array_map( static function ($item) { return $item->getName(); }, - $currentType->getTypes() + $this->type->getTypes() ); } - if ($currentType instanceof ReflectionNamedType) { - $types = [$currentType->getName()]; + if ($this->type instanceof ReflectionNamedType) { + $types = [$this->type->getName()]; } - if ($this->getType() !== null && $this->getType()->allowsNull()) { + if ($this->type !== null && $this->type->allowsNull()) { $types [] = 'null'; } return $types; } - /** - * Finds whether a variable is a scalar - * - * @return bool - */ - public function isScalar(): bool - { - return sizeof(array_intersect($this->getTypes(), ['int', 'float', 'double', 'string', 'bool', 'mixed'])) > 0; - } - /** * Finds whether a variable is an array * @@ -86,7 +89,7 @@ public function isScalar(): bool */ public function isArray(): bool { - return in_array('array', $this->getTypes(), true); + return in_array('array', $this->types, true); } /** @@ -111,13 +114,13 @@ public function existsAttribute(?string $name = null): bool */ public function notTransform(): bool { - return $this->existsAttribute(NotTransform::class); + return $this->notTransform = $this->existsAttribute(NotTransform::class); } /** * @param string|null $name * - * @return null|array + * @return null|ReflectionAttribute[] */ public function getAttributes(?string $name = null): ?array { @@ -127,4 +130,18 @@ public function getAttributes(?string $name = null): ?array } return null; } + + /** + * @param string|null $name + * + * @return null|ReflectionAttribute + */ + public function getAttribute(?string $name = null): ?ReflectionAttribute + { + $attr = $this->property->getAttributes($name); + if (!empty($attr)) { + return $attr[0]; + } + return null; + } } diff --git a/src/TransformBuilder.php b/src/TransformBuilder.php index d670a22..7c3ccbf 100644 --- a/src/TransformBuilder.php +++ b/src/TransformBuilder.php @@ -47,10 +47,10 @@ public function build() $instance = new $this->class(); $instance->transform(...$this->args); } else { - $generic = new GenericInstance($this->class); + $generic = new GenericInstance($this->class, new ArgumentsResource(...$this->args)); /** @var T $instance */ /** @phpstan-ignore-next-line */ - $instance = $generic->transform(...$this->args); + $instance = $generic->transform(); } if (method_exists($instance, 'afterTransform')) { diff --git a/tests/Benchmark/CheckBench.php b/tests/Benchmark/CheckBench.php index 5e0ad91..42086c9 100644 --- a/tests/Benchmark/CheckBench.php +++ b/tests/Benchmark/CheckBench.php @@ -2,16 +2,18 @@ namespace Tests\Benchmark; -use ClassTransformer\ClassTransformer; use PHPUnit\Framework\TestCase; +use Tests\Benchmark\DTO\UserDTO; use Tests\Benchmark\DTO\ProductDTO; use Tests\Benchmark\DTO\PurchaseDTO; -use Tests\Benchmark\DTO\UserDTO; +use ClassTransformer\ClassTransformer; /** * Class CheckBench * * @package Tests\Benchmark + * + * ./vendor/bin/phpbench run tests/Benchmark/CheckBench.php --report=default */ class CheckBench extends TestCase { @@ -22,6 +24,7 @@ class CheckBench extends TestCase public function benchBaseReflection(): void { $data = $this->getPurcheseObject(); + $productOne = new ProductDTO(); $productOne->id = $data['products'][0]['id']; $productOne->name = $data['products'][0]['name']; @@ -66,7 +69,7 @@ public function getPurcheseObject(): array 'price' => 10.56, ] ], - 'user'=>[ + 'user' => [ 'id' => 1, 'email' => 'fake@mail.com', 'balance' => 10012.23, diff --git a/tests/Benchmark/DTO/UserDTO.php b/tests/Benchmark/DTO/UserDTO.php index 5b2288d..2241e34 100644 --- a/tests/Benchmark/DTO/UserDTO.php +++ b/tests/Benchmark/DTO/UserDTO.php @@ -3,10 +3,15 @@ namespace Tests\Benchmark\DTO; +use ClassTransformer\Attributes\WritingStyle; + class UserDTO { public int $id; + public UserTypeEnum $type; public ?string $email; public float $balance; - public mixed $mixed; + + #[WritingStyle(WritingStyle::STYLE_CAMEL_CASE)] + public string $real_address; } diff --git a/tests/Benchmark/DTO/UserTypeEnum.php b/tests/Benchmark/DTO/UserTypeEnum.php new file mode 100644 index 0000000..dd7781e --- /dev/null +++ b/tests/Benchmark/DTO/UserTypeEnum.php @@ -0,0 +1,14 @@ + + */ +enum UserTypeEnum: string +{ + case Admin = 'admin'; + case Client = 'client'; +} diff --git a/tests/Benchmark/FullCheckBench.php b/tests/Benchmark/FullCheckBench.php new file mode 100644 index 0000000..af41700 --- /dev/null +++ b/tests/Benchmark/FullCheckBench.php @@ -0,0 +1,86 @@ +getPurcheseObject(); + + $productOne = new ProductDTO(); + $productOne->id = $data['products'][0]['id']; + $productOne->name = $data['products'][0]['name']; + $productOne->price = $data['products'][0]['price']; + + $productTwo = new ProductDTO(); + $productTwo->id = $data['products'][0]['id']; + $productTwo->name = $data['products'][0]['name']; + $productTwo->price = $data['products'][0]['price']; + + $user = new UserDTO(); + $user->id = $data['user']['id']; + $user->email = $data['user']['email']; + $user->balance = $data['user']['balance']; + $user->real_address = $data['user']['real_address'] ?? $data['user']['realAddress']; + $user->type = UserTypeEnum::from($data['user']['type']); + + $data = new PurchaseDTO(); + $data->products = [$productOne, $productTwo]; + $data->user = $user; + } + + /** + * @Revs(5000) + * @Iterations(5) + */ + public function benchTransformReflection(): void + { + $data = $this->getPurcheseObject(); + ClassTransformer::transform(PurchaseDTO::class, $data); + } + + public function getPurcheseObject(): array + { + return [ + 'products' => [ + [ + 'id' => 1, + 'name' => 'phone', + 'price' => 43.03, + ], + [ + 'id' => 2, + 'name' => 'bread', + 'price' => 10.56, + ] + ], + 'user' => [ + 'id' => 1, + 'email' => 'fake@mail.com', + 'balance' => 10012.23, + 'type' => 'admin', + 'realAddress' => 'test address', + ] + ]; + } +} diff --git a/tests/Benchmark/LiteCheckBench.php b/tests/Benchmark/LiteCheckBench.php new file mode 100644 index 0000000..1d41111 --- /dev/null +++ b/tests/Benchmark/LiteCheckBench.php @@ -0,0 +1,51 @@ +getPurcheseObject(); + + $productOne = new ProductDTO(); + $productOne->id = $data['id']; + $productOne->name = $data['name']; + $productOne->price = $data['price']; + } + + /** + * @Revs(5000) + */ + public function benchTransformReflection(): void + { + $data = $this->getPurcheseObject(); + ClassTransformer::transform(ProductDTO::class, $data); + } + + public function getPurcheseObject(): array + { + return [ + 'id' => 1, + 'name' => 'phone', + 'price' => 43.03, + ]; + } +} diff --git a/tests/Integration/ClassTransformerFromArrayTest.php b/tests/Integration/ClassTransformerFromArrayTest.php index ca397e8..ce7d705 100644 --- a/tests/Integration/ClassTransformerFromArrayTest.php +++ b/tests/Integration/ClassTransformerFromArrayTest.php @@ -14,7 +14,7 @@ use Tests\Integration\DTO\PurchaseDTO; use Tests\Integration\DTO\UserDTO; use Tests\Integration\DTO\UserEmptyTypeDTO; -use function Tests\count; +use function \count; /** * Class ClassTransformerTest diff --git a/tests/Units/GenericInstanceTest.php b/tests/Units/GenericInstanceTest.php index a949deb..3650b57 100644 --- a/tests/Units/GenericInstanceTest.php +++ b/tests/Units/GenericInstanceTest.php @@ -4,6 +4,7 @@ namespace Tests\Units; +use ClassTransformer\ArgumentsResource; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\GenericInstance; use PHPUnit\Framework\TestCase; @@ -12,17 +13,19 @@ /** * Class AfterTransformTest + * * @package Tests */ class GenericInstanceTest extends TestCase { use FakerData; + /** * @throws ReflectionException|ClassNotFoundException */ public function testAfterTransformStyle(): void { $this->expectException(ClassNotFoundException::class); - new GenericInstance('Test\Fake\Class'); + new GenericInstance('Test\Fake\Class', new ArgumentsResource()); } } From 458cd24a65da9be0e34251efba08cad4ce416952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 18 Jan 2023 18:29:13 +0300 Subject: [PATCH 020/100] optimize --- src/ArgumentsResource.php | 24 ++++++------------------ src/GenericInstance.php | 11 +++++++---- src/GenericProperty.php | 31 +++++++++---------------------- src/TransformUtils.php | 5 ++++- 4 files changed, 26 insertions(+), 45 deletions(-) diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index afdbfae..3d6b36f 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -6,7 +6,9 @@ use ClassTransformer\Exceptions\ValueNotFoundException; use function sizeof; -use function in_array; +use function is_object; +use function func_get_args; +use function array_intersect; use function array_key_exists; /** @@ -48,7 +50,7 @@ public function getValue(GenericProperty $genericProperty): mixed $writingStyle = $genericProperty->getAttribute(WritingStyle::class); - if (empty($writingStyle)) { + if ($writingStyle === null) { throw new ValueNotFoundException(); } @@ -61,26 +63,12 @@ public function getValue(GenericProperty $genericProperty): mixed $snakeCase = TransformUtils::strToSnakeCase($genericProperty->name); $camelCase = TransformUtils::strToCamelCase($genericProperty->name); - if ( - (in_array(WritingStyle::STYLE_SNAKE_CASE, $styles, true) || in_array(WritingStyle::STYLE_ALL, $styles, true)) & - array_key_exists($snakeCase, $this->args) - ) { + if (sizeof(array_intersect([WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 & array_key_exists($snakeCase, $this->args)) { return $this->args[$snakeCase]; } - if ( - (in_array(WritingStyle::STYLE_CAMEL_CASE, $styles, true) || in_array(WritingStyle::STYLE_ALL, $styles, true)) & - array_key_exists($camelCase, $this->args) - ) { + if (sizeof(array_intersect([WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 & array_key_exists($camelCase, $this->args)) { return $this->args[$camelCase]; } throw new ValueNotFoundException(); } - - /** - * @return array - */ - public function getArgs(): array - { - return $this->args; - } } diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 6b0501f..537a300 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -3,15 +3,16 @@ namespace ClassTransformer; use ReflectionClass; -use ReflectionProperty; -use ReflectionException; use ReflectionNamedType; use ClassTransformer\Attributes\ConvertArray; -use ClassTransformer\Attributes\WritingStyle; use ClassTransformer\Validators\ClassExistsValidator; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\Exceptions\ValueNotFoundException; +use function is_array; +use function constant; +use function method_exists; + /** * Class GenericInstance * @@ -24,7 +25,9 @@ final class GenericInstance /** @var class-string $class */ private string $class; - + /** @var ArgumentsResource $argumentsResource */ + private ArgumentsResource $argumentsResource; + /** * @param class-string $class diff --git a/src/GenericProperty.php b/src/GenericProperty.php index 703932f..e4b2ff0 100644 --- a/src/GenericProperty.php +++ b/src/GenericProperty.php @@ -11,6 +11,7 @@ use function sizeof; use function in_array; +use function enum_exists; use function array_intersect; /** @@ -57,26 +58,22 @@ public function isEnum(): bool */ private function initTypes(): array { - $types = []; - if ($this->type === null) { return []; } - + + $types = []; if ($this->type instanceof ReflectionUnionType) { - $types = array_map( - static function ($item) { - return $item->getName(); - }, - $this->type->getTypes() - ); + foreach ($this->type->getTypes() as $type) { + $types [] = $type->getName(); + } } if ($this->type instanceof ReflectionNamedType) { $types = [$this->type->getName()]; } - if ($this->type !== null && $this->type->allowsNull()) { + if ($this->type->allowsNull()) { $types [] = 'null'; } return $types; @@ -99,22 +96,12 @@ public function getDocComment(): bool|string return $this->property->getDocComment(); } - /** - * @param string|null $name - * - * @return bool - */ - public function existsAttribute(?string $name = null): bool - { - return $this->getAttributes($name) !== null; - } - /** * @return bool */ public function notTransform(): bool { - return $this->notTransform = $this->existsAttribute(NotTransform::class); + return $this->getAttributes(NotTransform::class) !== null; } /** @@ -130,7 +117,7 @@ public function getAttributes(?string $name = null): ?array } return null; } - + /** * @param string|null $name * diff --git a/src/TransformUtils.php b/src/TransformUtils.php index d73fc3a..b607bf6 100644 --- a/src/TransformUtils.php +++ b/src/TransformUtils.php @@ -4,9 +4,12 @@ use function ucwords; use function lcfirst; +use function is_string; use function strtolower; +use function preg_match; use function str_replace; use function preg_replace; +use function array_key_exists; /** * @@ -55,6 +58,6 @@ public static function getClassFromPhpDoc($phpDoc): ?string */ public static function propertyIsScalar(string $type): bool { - return in_array($type, ['int', 'float', 'string', 'bool', 'mixed']); + return array_key_exists($type, ['int', 'float', 'string', 'bool', 'mixed']); } } From 2c0c9b7a27228bed452862a7e2dcb62f49c3d810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 19 Jan 2023 17:41:20 +0300 Subject: [PATCH 021/100] add static cache. refactoring. setter mutator --- src/ArgumentsResource.php | 6 +- src/ClassTransformable.php | 3 - src/ClassTransformer.php | 5 +- src/GenericInstance.php | 162 ++++++++++++------ src/GenericProperty.php | 42 ++++- src/TransformBuilder.php | 6 +- src/TransformUtils.php | 54 ++++-- tests/Benchmark/DTO/ProductDTO.php | 2 + tests/Benchmark/FullCheckBench.php | 34 ++-- tests/Integration/AfterTransformTest.php | 7 +- .../ClassTransformerFromArrayTest.php | 5 +- .../Integration/CustomSetterAttributeTest.php | 36 ++++ .../DTO/CustomSetterAttibuteDTO.php | 21 +++ 13 files changed, 274 insertions(+), 109 deletions(-) create mode 100644 tests/Integration/CustomSetterAttributeTest.php create mode 100644 tests/Integration/DTO/CustomSetterAttibuteDTO.php diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index 3d6b36f..b80bcf0 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -60,9 +60,9 @@ public function getValue(GenericProperty $genericProperty): mixed throw new ValueNotFoundException(); } - $snakeCase = TransformUtils::strToSnakeCase($genericProperty->name); - $camelCase = TransformUtils::strToCamelCase($genericProperty->name); - + $snakeCase = TransformUtils::attributeToSnakeCase($genericProperty->name); + $camelCase = TransformUtils::attributeToCamelCase($genericProperty->name); + if (sizeof(array_intersect([WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 & array_key_exists($snakeCase, $this->args)) { return $this->args[$snakeCase]; } diff --git a/src/ClassTransformable.php b/src/ClassTransformable.php index 384bf41..db5860d 100644 --- a/src/ClassTransformable.php +++ b/src/ClassTransformable.php @@ -2,8 +2,6 @@ namespace ClassTransformer; -use ReflectionException; - /** * @template T */ @@ -13,7 +11,6 @@ interface ClassTransformable * @param array $args * * @return T - * @throws ReflectionException */ public function transform(...$args): mixed; } diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 3c48198..270b673 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -2,7 +2,6 @@ namespace ClassTransformer; -use ReflectionException; use ClassTransformer\Exceptions\ClassNotFoundException; /** @@ -21,7 +20,6 @@ final class ClassTransformer * * @return null|T * @throws ClassNotFoundException - * @throws ReflectionException */ public static function transform(string $className, ...$args) { @@ -35,7 +33,6 @@ public static function transform(string $className, ...$args) * * @return null|array|array * @throws ClassNotFoundException - * @throws ReflectionException */ public static function transformCollection(string $className, array $args): ?array { @@ -51,7 +48,7 @@ public static function transformCollection(string $className, array $args): ?arr * @param array>|array $args * * @return null|array|array - * @throws ClassNotFoundException|ReflectionException + * @throws ClassNotFoundException */ public static function transformMultiple(array $className, array $args): ?array { diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 537a300..844323e 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -22,15 +22,23 @@ */ final class GenericInstance { - /** @var class-string $class */ + /** @var class-string $class */ private string $class; /** @var ArgumentsResource $argumentsResource */ private ArgumentsResource $argumentsResource; + /** @var T $genericInstance */ + private $genericInstance; /** - * @param class-string $class + * @var array + */ + private static $propertiesTypesCache = []; + + + /** + * @param class-string $class * * @throws ClassNotFoundException */ @@ -40,22 +48,31 @@ public function __construct(string $class, ArgumentsResource $argumentsResource) $this->class = $class; $this->argumentsResource = $argumentsResource; + + $this->genericInstance = new $this->class(); } /** - * @param array|object|null $args - * - * @return T - * @throws ClassNotFoundException + * @return \ReflectionProperty[] */ - public function transform(): mixed + public function getProperties(): array { - /** @var T $instance */ - $instance = new $this->class(); + if (isset(static::$propertiesTypesCache[$this->class])) { + return static::$propertiesTypesCache[$this->class]; + } $refInstance = new ReflectionClass($this->class); + return static::$propertiesTypesCache[$this->class] = $refInstance->getProperties(); + } - foreach ($refInstance->getProperties() as $item) { + /** + * @return T + * @throws ClassNotFoundException + */ + public function transform(): mixed + { + $properties = $this->getProperties(); + foreach ($properties as $item) { $property = new GenericProperty($item); try { @@ -64,49 +81,96 @@ public function transform(): mixed continue; } - if ($property->isScalar || $property->notTransform()) { - $instance->{$item->name} = $value; + if ($this->hasSetMutator($property->name)) { + $this->genericInstance->{TransformUtils::mutationSetterToCamelCase($property->name)}($value); continue; } - if ($property->isArray()) { - $arrayTypeAttr = $property->getAttributes(ConvertArray::class); - if (!empty($arrayTypeAttr)) { - $arrayType = $arrayTypeAttr[0]->getArguments()[0]; - } else { - $arrayType = TransformUtils::getClassFromPhpDoc($property->getDocComment()); - } - - if (!empty($arrayType) && !empty($value) && is_array($value) && !TransformUtils::propertyIsScalar($arrayType)) { - foreach ($value as $el) { - $instance->{$item->name}[] = (new TransformBuilder($arrayType, $el))->build(); - } - continue; - } - - $instance->{$item->name} = $value; - continue; - } + $this->genericInstance->{$property->name} = $this->castAttribute($property, $value); + } + return $this->genericInstance; + } - if ($property->type instanceof ReflectionNamedType) { - /** @var class-string $propertyClass */ - $propertyClass = $property->type->getName(); - - if ($property->isEnum()) { - if (method_exists($propertyClass, 'from')) { - /** @var \UnitEnum $propertyClass */ - $instance->{$item->name} = $propertyClass::from($value); - } else { - $instance->{$item->name} = constant($propertyClass . '::' . $value); - } - continue; - } - - $instance->{$item->name} = (new TransformBuilder($propertyClass, $value))->build(); - continue; - } - $instance->{$item->name} = $value; + /** + * @param GenericProperty $property + * @param int|string|array|object $value + * + * @return mixed + * @throws ClassNotFoundException + */ + private function castAttribute(GenericProperty $property, $value) + { + if ($property->isScalar || $property->notTransform()) { + return $value; + } + + if ($property->isArray()) { + return $this->castArray($property, $value); } - return $instance; + + if ($property->isEnum()) { + return $this->castEnum($property, $value); + } + + if ($property->type instanceof ReflectionNamedType) { + /** @var class-string $propertyClass */ + $propertyClass = $property->type->getName(); + + return (new TransformBuilder($propertyClass, $value))->build(); + } + return $value; + } + + /** + * @param GenericProperty $property + * @param mixed $value + * + * @return array + * @throws ClassNotFoundException + */ + private function castArray(GenericProperty $property, $value) + { + $arrayTypeAttr = $property->getAttribute(ConvertArray::class); + if ($arrayTypeAttr !== null) { + $arrayType = $arrayTypeAttr->getArguments()[0]; + } else { + $arrayType = TransformUtils::getClassFromPhpDoc($property->getDocComment()); + } + + if (empty($arrayType) || !is_array($value)) { + return $value; + } + $array = []; + foreach ($value as $el) { + $array[] = (new TransformBuilder($arrayType, $el))->build(); + } + return $array; + } + + /** + * @param GenericProperty $property + * @param int|string $value + * + * @return mixed + */ + private function castEnum(GenericProperty $property, int|string $value) + { + $propertyClass = $property->type->getName(); + if (method_exists($propertyClass, 'from')) { + /** @var \BackedEnum $propertyClass */ + return $propertyClass::from($value); + } + + return constant($propertyClass . '::' . $value); + } + + /** + * @param string $key + * + * @return bool + */ + public function hasSetMutator(string $key): bool + { + return method_exists($this->class, TransformUtils::mutationSetterToCamelCase($key)); } } diff --git a/src/GenericProperty.php b/src/GenericProperty.php index e4b2ff0..d266a1f 100644 --- a/src/GenericProperty.php +++ b/src/GenericProperty.php @@ -21,17 +21,30 @@ */ final class GenericProperty { - /** - * @var ReflectionProperty - */ + /** @var ReflectionProperty */ public ReflectionProperty $property; + /** @var null|ReflectionType|ReflectionUnionType|ReflectionNamedType */ readonly public ?ReflectionType $type; + + /** @var array|string[] */ readonly public array $types; /** @var class-string|string $propertyClass */ readonly public string $name; + + /** @var string */ + readonly public string $class; + + /** @var bool */ readonly public bool $isScalar; + + + + /** @var array>> */ + private static $attributeTypesCache = []; + + /** * @param ReflectionProperty $property @@ -39,6 +52,7 @@ final class GenericProperty public function __construct(ReflectionProperty $property) { $this->property = $property; + $this->class = $property->class; $this->type = $this->property->getType(); $this->name = $this->property->name; $this->types = $this->initTypes(); @@ -50,7 +64,10 @@ public function __construct(ReflectionProperty $property) */ public function isEnum(): bool { - return enum_exists($this->type->getName()); + if ($this->type instanceof ReflectionNamedType) { + return enum_exists($this->type->getName()); + } + return false; } /** @@ -58,6 +75,10 @@ public function isEnum(): bool */ private function initTypes(): array { + if (isset(static::$attributeTypesCache[$this->class][$this->name])) { + return static::$attributeTypesCache[$this->class][$this->name]; + } + if ($this->type === null) { return []; } @@ -76,7 +97,8 @@ private function initTypes(): array if ($this->type->allowsNull()) { $types [] = 'null'; } - return $types; + + return static::$attributeTypesCache[$this->class][$this->name] = $types; } /** @@ -105,9 +127,10 @@ public function notTransform(): bool } /** - * @param string|null $name + * @param class-string|null $name * - * @return null|ReflectionAttribute[] + * @template T + * @return null|ReflectionAttribute[] */ public function getAttributes(?string $name = null): ?array { @@ -119,9 +142,10 @@ public function getAttributes(?string $name = null): ?array } /** - * @param string|null $name + * @param class-string|null $name * - * @return null|ReflectionAttribute + * @template T + * @return null|T */ public function getAttribute(?string $name = null): ?ReflectionAttribute { diff --git a/src/TransformBuilder.php b/src/TransformBuilder.php index 7c3ccbf..bad44e6 100644 --- a/src/TransformBuilder.php +++ b/src/TransformBuilder.php @@ -2,7 +2,6 @@ namespace ClassTransformer; -use ReflectionException; use ClassTransformer\Validators\ClassExistsValidator; use ClassTransformer\Exceptions\ClassNotFoundException; @@ -19,7 +18,7 @@ final class TransformBuilder */ private string $class; - /** @var array $args */ + /** @var array|object|null|mixed $args */ private array $args; /** @@ -38,7 +37,7 @@ public function __construct(string $class, ...$args) /** * @return T - * @throws ReflectionException|ClassNotFoundException + * @throws ClassNotFoundException */ public function build() { @@ -49,7 +48,6 @@ public function build() } else { $generic = new GenericInstance($this->class, new ArgumentsResource(...$this->args)); /** @var T $instance */ - /** @phpstan-ignore-next-line */ $instance = $generic->transform(); } diff --git a/src/TransformUtils.php b/src/TransformUtils.php index b607bf6..64b02fe 100644 --- a/src/TransformUtils.php +++ b/src/TransformUtils.php @@ -16,25 +16,55 @@ */ final class TransformUtils { + /** @var array */ + private static array $camelCache = []; + + /** @var array */ + private static array $snakeCache = []; + + /** @var array */ + private static array $mutationSetterCache = []; + /** - * @param string $string + * @param string $key * * @return string */ - public static function strToSnakeCase(string $string): string + public static function attributeToSnakeCase(string $key): string { - $str = preg_replace('/(?<=\d)(?=[A-Za-z])|(?<=[A-Za-z])(?=\d)|(?<=[a-z])(?=[A-Z])/', '_', $string) ?? ''; - return strtolower($str); + if (isset(static::$snakeCache[$key])) { + return static::$snakeCache[$key]; + } + $str = preg_replace('/(?<=\d)(?=[A-Za-z])|(?<=[A-Za-z])(?=\d)|(?<=[a-z])(?=[A-Z])/', '_', $key) ?? ''; + return static::$snakeCache[$key] = strtolower($str); } /** - * @param string $string + * @param string $key * * @return string */ - public static function strToCamelCase(string $string): string + public static function attributeToCamelCase(string $key): string { - return lcfirst(str_replace('_', '', ucwords($string, '_'))); + if (isset(static::$camelCache[$key])) { + return static::$camelCache[$key]; + } + $str = lcfirst(str_replace('_', '', ucwords($key, '_'))); + return static::$camelCache[$key] = $str; + } + + /** + * @param string $key + * + * @return string + */ + public static function mutationSetterToCamelCase(string $key): string + { + if (isset(static::$mutationSetterCache[$key])) { + return static::$mutationSetterCache[$key]; + } + $str = 'set' . ucfirst(self::attributeToCamelCase($key)) . 'Attribute'; + return static::$mutationSetterCache[$key] = $str; } /** @@ -50,14 +80,4 @@ public static function getClassFromPhpDoc($phpDoc): ?string } return null; } - - /** - * @param string $type - * - * @return bool - */ - public static function propertyIsScalar(string $type): bool - { - return array_key_exists($type, ['int', 'float', 'string', 'bool', 'mixed']); - } } diff --git a/tests/Benchmark/DTO/ProductDTO.php b/tests/Benchmark/DTO/ProductDTO.php index 83b8680..c177146 100644 --- a/tests/Benchmark/DTO/ProductDTO.php +++ b/tests/Benchmark/DTO/ProductDTO.php @@ -9,4 +9,6 @@ class ProductDTO public int $id; public string $name; public float $price; + public string $description; + public int $count; } diff --git a/tests/Benchmark/FullCheckBench.php b/tests/Benchmark/FullCheckBench.php index af41700..a8e9683 100644 --- a/tests/Benchmark/FullCheckBench.php +++ b/tests/Benchmark/FullCheckBench.php @@ -13,7 +13,7 @@ * Class CheckBench * * @package Tests\Benchmark - * + * * ./vendor/bin/phpbench run tests/Benchmark/CheckBench.php --report=default */ class FullCheckBench extends TestCase @@ -27,15 +27,7 @@ public function benchBaseReflection(): void { $data = $this->getPurcheseObject(); - $productOne = new ProductDTO(); - $productOne->id = $data['products'][0]['id']; - $productOne->name = $data['products'][0]['name']; - $productOne->price = $data['products'][0]['price']; - - $productTwo = new ProductDTO(); - $productTwo->id = $data['products'][0]['id']; - $productTwo->name = $data['products'][0]['name']; - $productTwo->price = $data['products'][0]['price']; + $purchase = new PurchaseDTO(); $user = new UserDTO(); $user->id = $data['user']['id']; @@ -43,10 +35,15 @@ public function benchBaseReflection(): void $user->balance = $data['user']['balance']; $user->real_address = $data['user']['real_address'] ?? $data['user']['realAddress']; $user->type = UserTypeEnum::from($data['user']['type']); + $purchase->user = $user; - $data = new PurchaseDTO(); - $data->products = [$productOne, $productTwo]; - $data->user = $user; + foreach ($data['products'] as $product) { + $newProduct = new ProductDTO(); + $newProduct->id = $product['id']; + $newProduct->name = $product['name']; + $newProduct->price = $product['price']; + $purchase->products []= $newProduct; + } } /** @@ -67,11 +64,22 @@ public function getPurcheseObject(): array 'id' => 1, 'name' => 'phone', 'price' => 43.03, + 'description' => 'test description for phone', + 'count' => 123 ], [ 'id' => 2, 'name' => 'bread', 'price' => 10.56, + 'description' => 'test description for bread', + 'count' => 321 + ], + [ + 'id' => 2, + 'name' => 'book', + 'price' => 5.5, + 'description' => 'test description for book', + 'count' => 333 ] ], 'user' => [ diff --git a/tests/Integration/AfterTransformTest.php b/tests/Integration/AfterTransformTest.php index 9f070e2..52509b1 100644 --- a/tests/Integration/AfterTransformTest.php +++ b/tests/Integration/AfterTransformTest.php @@ -4,11 +4,10 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\Exceptions\ClassNotFoundException; use PHPUnit\Framework\TestCase; -use ReflectionException; +use ClassTransformer\ClassTransformer; use Tests\Integration\DTO\UserAfterTransformDTO; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class AfterTransformTest @@ -18,7 +17,7 @@ class AfterTransformTest extends TestCase { use FakerData; /** - * @throws ReflectionException|ClassNotFoundException + * @throws ClassNotFoundException */ public function testAfterTransformStyle(): void { diff --git a/tests/Integration/ClassTransformerFromArrayTest.php b/tests/Integration/ClassTransformerFromArrayTest.php index ce7d705..beed108 100644 --- a/tests/Integration/ClassTransformerFromArrayTest.php +++ b/tests/Integration/ClassTransformerFromArrayTest.php @@ -7,7 +7,6 @@ use ClassTransformer\ClassTransformer; use ClassTransformer\Exceptions\ClassNotFoundException; use PHPUnit\Framework\TestCase; -use ReflectionException; use Tests\Integration\DTO\ArrayScalarDTO; use Tests\Integration\DTO\BasketDTO; use Tests\Integration\DTO\ProductDTO; @@ -26,7 +25,7 @@ class ClassTransformerFromArrayTest extends TestCase use FakerData; /** - * @throws ReflectionException|ClassNotFoundException + * @throws ClassNotFoundException */ public function testBaseArray(): void { @@ -42,7 +41,7 @@ public function testBaseArray(): void } /** - * @throws ReflectionException|ClassNotFoundException + * @throws ClassNotFoundException */ public function testScalarArray(): void { diff --git a/tests/Integration/CustomSetterAttributeTest.php b/tests/Integration/CustomSetterAttributeTest.php new file mode 100644 index 0000000..cc3f2d1 --- /dev/null +++ b/tests/Integration/CustomSetterAttributeTest.php @@ -0,0 +1,36 @@ + 77, + 'real_address' => 'TEST ADDRESS', + 'userName' => 'yzen', + ]; + $userDTO = ClassTransformer::transform(CustomSetterAttibuteDTO::class, $data); + self::assertInstanceOf(CustomSetterAttibuteDTO::class, $userDTO); + self::assertEquals($data['id'], $userDTO->id); + self::assertEquals(strtolower($data['real_address']), $userDTO->real_address); + self::assertEquals(strtoupper($data['userName']), $userDTO->userName); + } +} diff --git a/tests/Integration/DTO/CustomSetterAttibuteDTO.php b/tests/Integration/DTO/CustomSetterAttibuteDTO.php new file mode 100644 index 0000000..4ebcedf --- /dev/null +++ b/tests/Integration/DTO/CustomSetterAttibuteDTO.php @@ -0,0 +1,21 @@ +real_address = strtolower($value); + } + + public function setUserNameAttribute(string $value) + { + $this->userName = strtoupper($value); + } +} From a6c62fd947a5619ecc5d29c315019686d9a778b2 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Thu, 19 Jan 2023 17:42:51 +0300 Subject: [PATCH 022/100] Update tests.yml --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fb2ae37..aa1d53a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,7 +2,7 @@ name: Run tests on: push: - branches: [ master, dev ] + branches: [ master, dev, feature/cache-v2-php8.1 ] pull_request: branches: [ master, dev ] @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.1", "8.0"] + php: ["8.1"] dependency-version: [prefer-lowest, prefer-stable] name: P${{ matrix.php }} - ${{ matrix.dependency-version }} From c1bbf649e0a2774f86e50c1e3dfb0aebd4f9c86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 19 Jan 2023 18:41:16 +0300 Subject: [PATCH 023/100] cache php attributes --- src/ArgumentsResource.php | 5 +++-- src/GenericProperty.php | 31 ++++++++++--------------------- src/TransformUtils.php | 4 ++-- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index b80bcf0..7462f93 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -61,14 +61,15 @@ public function getValue(GenericProperty $genericProperty): mixed } $snakeCase = TransformUtils::attributeToSnakeCase($genericProperty->name); - $camelCase = TransformUtils::attributeToCamelCase($genericProperty->name); - if (sizeof(array_intersect([WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 & array_key_exists($snakeCase, $this->args)) { return $this->args[$snakeCase]; } + + $camelCase = TransformUtils::attributeToCamelCase($genericProperty->name); if (sizeof(array_intersect([WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 & array_key_exists($camelCase, $this->args)) { return $this->args[$camelCase]; } + throw new ValueNotFoundException(); } } diff --git a/src/GenericProperty.php b/src/GenericProperty.php index d266a1f..d44d549 100644 --- a/src/GenericProperty.php +++ b/src/GenericProperty.php @@ -38,13 +38,13 @@ final class GenericProperty /** @var bool */ readonly public bool $isScalar; - - /** @var array>> */ private static $attributeTypesCache = []; - - + + /** @var array>> */ + private static $attributesCache = []; + /** * @param ReflectionProperty $property @@ -123,22 +123,7 @@ public function getDocComment(): bool|string */ public function notTransform(): bool { - return $this->getAttributes(NotTransform::class) !== null; - } - - /** - * @param class-string|null $name - * - * @template T - * @return null|ReflectionAttribute[] - */ - public function getAttributes(?string $name = null): ?array - { - $attr = $this->property->getAttributes($name); - if (!empty($attr)) { - return $attr; - } - return null; + return $this->getAttribute(NotTransform::class) !== null; } /** @@ -149,9 +134,13 @@ public function getAttributes(?string $name = null): ?array */ public function getAttribute(?string $name = null): ?ReflectionAttribute { + if (isset(static::$attributesCache[$this->class][$this->name][$name])) { + return static::$attributesCache[$this->class][$this->name][$name]; + } + $attr = $this->property->getAttributes($name); if (!empty($attr)) { - return $attr[0]; + return static::$attributesCache[$this->class][$this->name][$name] = $attr[0]; } return null; } diff --git a/src/TransformUtils.php b/src/TransformUtils.php index 64b02fe..db25c9e 100644 --- a/src/TransformUtils.php +++ b/src/TransformUtils.php @@ -18,10 +18,10 @@ final class TransformUtils { /** @var array */ private static array $camelCache = []; - + /** @var array */ private static array $snakeCache = []; - + /** @var array */ private static array $mutationSetterCache = []; From cc21eba2514c48c8c76f1f3ffdc362bfe25fd698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 19 Jan 2023 18:41:23 +0300 Subject: [PATCH 024/100] add unit tests --- tests/Units/ArgumentsResourceTest.php | 72 +++++++++++++++++++++++++++ tests/Units/DTO/UserDTO.php | 25 ++++++++++ tests/Units/TransformUtilsTest.php | 46 +++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 tests/Units/ArgumentsResourceTest.php create mode 100644 tests/Units/DTO/UserDTO.php create mode 100644 tests/Units/TransformUtilsTest.php diff --git a/tests/Units/ArgumentsResourceTest.php b/tests/Units/ArgumentsResourceTest.php new file mode 100644 index 0000000..93ebe26 --- /dev/null +++ b/tests/Units/ArgumentsResourceTest.php @@ -0,0 +1,72 @@ + 1]; + $resource = new ArgumentsResource($data); + $value = $resource->getValue( + new GenericProperty(new \ReflectionProperty(UserDTO::class, 'id')) + ); + $this->assertEquals($data['id'], $value); + } + + public function testBaseValueNotFoundException(): void + { + $this->expectException(ValueNotFoundException::class); + $resource = new ArgumentsResource(['test' => 1]); + $resource->getValue( + new GenericProperty(new \ReflectionProperty(UserDTO::class, 'id')) + ); + } + + public function testEmptyWritingStyle(): void + { + $this->expectException(ValueNotFoundException::class); + $data = ['id' => 1]; + $resource = new ArgumentsResource($data); + $value = $resource->getValue( + new GenericProperty(new \ReflectionProperty(UserDTO::class, 'addressOne')) + ); + $this->assertEquals($data['id'], $value); + } + + public function testCamelCaseWritingStyleKey(): void + { + $data = ['addressTwo' => 'test']; + $resource = new ArgumentsResource($data); + $value = $resource->getValue( + new GenericProperty(new \ReflectionProperty(UserDTO::class, 'address_two')) + ); + $this->assertEquals($data['addressTwo'], $value); + } + + public function testSnakeCaseWritingStyleKey(): void + { + $data = ['address_three' => 'test']; + $resource = new ArgumentsResource($data); + $value = $resource->getValue( + new GenericProperty(new \ReflectionProperty(UserDTO::class, 'addressThree')) + ); + $this->assertEquals($data['address_three'], $value); + } + + public function testFinalValueNotFoundException(): void + { + $this->expectException(ValueNotFoundException::class); + $resource = new ArgumentsResource(['test' => 1]); + $resource->getValue( + new GenericProperty(new \ReflectionProperty(UserDTO::class, 'balance')) + ); + } +} diff --git a/tests/Units/DTO/UserDTO.php b/tests/Units/DTO/UserDTO.php new file mode 100644 index 0000000..4ce4ffa --- /dev/null +++ b/tests/Units/DTO/UserDTO.php @@ -0,0 +1,25 @@ +assertEquals('example_camel_case', $snakeCase); + // test for cache + $snakeCase = TransformUtils::attributeToSnakeCase('exampleCamelCase'); + $this->assertEquals('example_camel_case', $snakeCase); + } + + public function testAttributeToCamelCase(): void + { + $camelCase = TransformUtils::attributeToCamelCase('example_snake_case'); + $this->assertEquals('exampleSnakeCase', $camelCase); + // test for cache + $camelCase = TransformUtils::attributeToCamelCase('example_snake_case'); + $this->assertEquals('exampleSnakeCase', $camelCase); + } + + public function testMutationSetterToCamelCase(): void + { + $setter = TransformUtils::mutationSetterToCamelCase('example'); + $this->assertEquals('setExampleAttribute', $setter); + // test for cache + $setter = TransformUtils::mutationSetterToCamelCase('example'); + $this->assertEquals('setExampleAttribute', $setter); + } + + public function testGetClassFromPhpDoc(): void + { + $type = TransformUtils::getClassFromPhpDoc('/** @var array<\Tests\Integration\DTO\PurchaseDTO> $orders Order list */'); + $this->assertEquals('\Tests\Integration\DTO\PurchaseDTO', $type); + + $type = TransformUtils::getClassFromPhpDoc('/** @var array<> $orders Order list */'); + $this->assertNull($type); + } +} From bdd2b331a6ad75bf5ef1f14aa2eb5ab89128b76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Fri, 20 Jan 2023 10:46:32 +0300 Subject: [PATCH 025/100] issue phpstan --- phpstan.neon | 1 + src/ArgumentsResource.php | 2 +- src/GenericProperty.php | 5 ++--- src/TransformBuilder.php | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index a0e6887..df3a0ba 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,2 +1,3 @@ parameters: level: 9 + checkGenericClassInNonGenericObjectType: false diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index 7462f93..b12782d 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -22,7 +22,7 @@ final class ArgumentsResource private array $args; /** - * @param array|object|null $args + * @param mixed $args */ public function __construct(...$args) { diff --git a/src/GenericProperty.php b/src/GenericProperty.php index d44d549..b500b32 100644 --- a/src/GenericProperty.php +++ b/src/GenericProperty.php @@ -22,7 +22,7 @@ final class GenericProperty { /** @var ReflectionProperty */ - public ReflectionProperty $property; + readonly public ReflectionProperty $property; /** @var null|ReflectionType|ReflectionUnionType|ReflectionNamedType */ readonly public ?ReflectionType $type; @@ -45,7 +45,6 @@ final class GenericProperty /** @var array>> */ private static $attributesCache = []; - /** * @param ReflectionProperty $property */ @@ -130,7 +129,7 @@ public function notTransform(): bool * @param class-string|null $name * * @template T - * @return null|T + * @return null|ReflectionAttribute */ public function getAttribute(?string $name = null): ?ReflectionAttribute { diff --git a/src/TransformBuilder.php b/src/TransformBuilder.php index bad44e6..74131d0 100644 --- a/src/TransformBuilder.php +++ b/src/TransformBuilder.php @@ -18,8 +18,8 @@ final class TransformBuilder */ private string $class; - /** @var array|object|null|mixed $args */ - private array $args; + /** @var iterable $args */ + private $args; /** * @param class-string $class From 2d14f50975e2e7f0d67aa330998c7b32666ad34a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 30 Jan 2023 20:36:12 +0300 Subject: [PATCH 026/100] add: aliases field --- src/ArgumentsResource.php | 18 ++++++++++++++++++ src/Attributes/FieldAlias.php | 19 +++++++++++++++++++ tests/Integration/DTO/WithAliasDTO.php | 16 ++++++++++++++++ tests/Integration/WritingStyleTest.php | 18 ++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 src/Attributes/FieldAlias.php create mode 100644 tests/Integration/DTO/WithAliasDTO.php diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index b12782d..3181317 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -2,6 +2,7 @@ namespace ClassTransformer; +use ClassTransformer\Attributes\FieldAlias; use ClassTransformer\Attributes\WritingStyle; use ClassTransformer\Exceptions\ValueNotFoundException; @@ -48,6 +49,23 @@ public function getValue(GenericProperty $genericProperty): mixed return $this->args[$genericProperty->name]; } + $aliasesAttr = $genericProperty->getAttribute(FieldAlias::class); + + if ($aliasesAttr !== null) { + $aliases = $aliasesAttr->getArguments(); + if (!empty($aliases)) { + $aliases = $aliases[0]; + if (is_string($aliases)) { + $aliases = [$aliases]; + } + foreach ($aliases as $alias) { + if (array_key_exists($alias, $this->args)) { + return $this->args[$alias]; + } + } + } + } + $writingStyle = $genericProperty->getAttribute(WritingStyle::class); if ($writingStyle === null) { diff --git a/src/Attributes/FieldAlias.php b/src/Attributes/FieldAlias.php new file mode 100644 index 0000000..ce81378 --- /dev/null +++ b/src/Attributes/FieldAlias.php @@ -0,0 +1,19 @@ +contact_email); } + /** + * @throws ReflectionException|ClassNotFoundException + */ + public function testAliasTransform(): void + { + $data = [ + 'userFio' => 'corey', + 'phone' => '10101010', + ]; + $model = ClassTransformer::transform(WithAliasDTO::class, $data); + + self::assertInstanceOf(WithAliasDTO::class, $model); + + self::assertEquals($data['userFio'], $model->fio); + self::assertEquals($data['phone'], $model->contact); + } + } From e844971ea8b404ec6d90d1e387cb3ef76e438769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 30 Jan 2023 21:09:04 +0300 Subject: [PATCH 027/100] Fix: scalar array --- src/Attributes/ConvertArray.php | 3 +++ src/GenericInstance.php | 19 +++++++++++++++++-- .../ClassTransformerFromArrayTest.php | 11 ++++++++--- tests/Integration/DTO/ArrayScalarDTO.php | 5 +++-- tests/Integration/FakerData.php | 7 ------- 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/Attributes/ConvertArray.php b/src/Attributes/ConvertArray.php index 2a7191d..1cf6241 100644 --- a/src/Attributes/ConvertArray.php +++ b/src/Attributes/ConvertArray.php @@ -10,4 +10,7 @@ #[\Attribute(\Attribute::TARGET_PARAMETER)] final class ConvertArray { + public function __construct(string $type) + { + } } diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 844323e..0d4f676 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -137,12 +137,27 @@ private function castArray(GenericProperty $property, $value) $arrayType = TransformUtils::getClassFromPhpDoc($property->getDocComment()); } - if (empty($arrayType) || !is_array($value)) { + if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { return $value; } + + if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { + $array = []; + foreach ($value as $el) { + $array[] = (new TransformBuilder($arrayType, $el))->build(); + } + return $array; + } + $array = []; foreach ($value as $el) { - $array[] = (new TransformBuilder($arrayType, $el))->build(); + $array[] = match ($arrayType) { + 'string' => (string)$el, + 'int' => (int)$el, + 'float' => (float)$el, + 'bool' => (bool)$el, + default => $el + }; } return $array; } diff --git a/tests/Integration/ClassTransformerFromArrayTest.php b/tests/Integration/ClassTransformerFromArrayTest.php index beed108..eee802b 100644 --- a/tests/Integration/ClassTransformerFromArrayTest.php +++ b/tests/Integration/ClassTransformerFromArrayTest.php @@ -45,9 +45,14 @@ public function testBaseArray(): void */ public function testScalarArray(): void { - $data = $this->getDataWithScalarArray(); - $userDTO = ClassTransformer::transform(ArrayScalarDTO::class, $data); - self::assertInstanceOf(ArrayScalarDTO::class, $userDTO); + $data = [ + 'stringList' => [100, 200, 300], + 'intList' => [100, 200, 300] + ]; + $dto = ClassTransformer::transform(ArrayScalarDTO::class, $data); + self::assertInstanceOf(ArrayScalarDTO::class, $dto); + self::assertIsString($dto->stringList[0]); + self::assertIsInt($dto->intList[0]); } /** diff --git a/tests/Integration/DTO/ArrayScalarDTO.php b/tests/Integration/DTO/ArrayScalarDTO.php index 0957a0f..1886cf9 100644 --- a/tests/Integration/DTO/ArrayScalarDTO.php +++ b/tests/Integration/DTO/ArrayScalarDTO.php @@ -9,7 +9,8 @@ class ArrayScalarDTO { public $id; - /** @var null|array */ #[ConvertArray('string')] - public ?array $products; + public ?array $stringList; + #[ConvertArray('int')] + public ?array $intList; } diff --git a/tests/Integration/FakerData.php b/tests/Integration/FakerData.php index 35e5874..9b77331 100644 --- a/tests/Integration/FakerData.php +++ b/tests/Integration/FakerData.php @@ -15,13 +15,6 @@ public function getBaseArrayData(): array ]; } - public function getDataWithScalarArray(): array - { - return [ - 'id' => 1, - 'history' => ['test1', 'test2', 'test3'] - ]; - } public function getArrayUsers(): array { From ec3c6040faf00266b6fadd09c0b3f5951bdf365b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 30 Jan 2023 23:11:48 +0300 Subject: [PATCH 028/100] upd bench --- src/GenericInstance.php | 3 +-- tests/Benchmark/DTO/UserDTO.php | 2 ++ tests/Benchmark/FullCheckBench.php | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 0d4f676..5457030 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -141,15 +141,14 @@ private function castArray(GenericProperty $property, $value) return $value; } + $array = []; if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { - $array = []; foreach ($value as $el) { $array[] = (new TransformBuilder($arrayType, $el))->build(); } return $array; } - $array = []; foreach ($value as $el) { $array[] = match ($arrayType) { 'string' => (string)$el, diff --git a/tests/Benchmark/DTO/UserDTO.php b/tests/Benchmark/DTO/UserDTO.php index 2241e34..f32d3e8 100644 --- a/tests/Benchmark/DTO/UserDTO.php +++ b/tests/Benchmark/DTO/UserDTO.php @@ -3,12 +3,14 @@ namespace Tests\Benchmark\DTO; +use ClassTransformer\Attributes\FieldAlias; use ClassTransformer\Attributes\WritingStyle; class UserDTO { public int $id; public UserTypeEnum $type; + #[FieldAlias('contact')] public ?string $email; public float $balance; diff --git a/tests/Benchmark/FullCheckBench.php b/tests/Benchmark/FullCheckBench.php index a8e9683..1de1cac 100644 --- a/tests/Benchmark/FullCheckBench.php +++ b/tests/Benchmark/FullCheckBench.php @@ -31,7 +31,7 @@ public function benchBaseReflection(): void $user = new UserDTO(); $user->id = $data['user']['id']; - $user->email = $data['user']['email']; + $user->email = $data['user']['contact']; $user->balance = $data['user']['balance']; $user->real_address = $data['user']['real_address'] ?? $data['user']['realAddress']; $user->type = UserTypeEnum::from($data['user']['type']); @@ -84,7 +84,7 @@ public function getPurcheseObject(): array ], 'user' => [ 'id' => 1, - 'email' => 'fake@mail.com', + 'contact' => 'fake@mail.com', 'balance' => 10012.23, 'type' => 'admin', 'realAddress' => 'test address', From 77980256cae884159b0a3b36607c37fb1cf95369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 09:38:06 +0300 Subject: [PATCH 029/100] fix: typo --- tests/Integration/CustomSetterAttributeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/CustomSetterAttributeTest.php b/tests/Integration/CustomSetterAttributeTest.php index cc3f2d1..07ab07d 100644 --- a/tests/Integration/CustomSetterAttributeTest.php +++ b/tests/Integration/CustomSetterAttributeTest.php @@ -20,7 +20,7 @@ class CustomSetterAttributeTest extends TestCase /** * @throws ReflectionException|ClassNotFoundException */ - public function testAfterTransformStyle(): void + public function testCustomSetterAttibute(): void { $data = [ 'id' => 77, From 465c75bc9a14483d81625dbd6f89e52ecb0e8ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 14:49:20 +0300 Subject: [PATCH 030/100] issue: docs --- docs/index.rst | 21 +++++++++++++++++++++ docs/installation.rst | 11 +++++++++++ docs/usage.rst | 31 +++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 docs/index.rst create mode 100644 docs/installation.rst create mode 100644 docs/usage.rst diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..355dd19 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,21 @@ +ClassTransformer +================ + +This library will allow you to easily convert any data set into the object you need. You are not required to change the structure of classes, inherit them from external modules, etc. No dancing with tambourines - just data and the right class. + +It is considered good practice to write code independent of third-party packages and frameworks. The code is divided into services, domain zones, various layers, etc. + +To transfer data between layers, the **DataTransfer Object** (DTO) template is usually used. A DTO is an object that is used to encapsulate data and send it from one application subsystem to another. + +Thus, services/methods work with a specific object and the data necessary for it. At the same time, it does not matter where this data was obtained from, it can be an http request, a database, a file, etc. + +Accordingly, each time the service is called, we need to initialize this DTO. But it is not effective to compare data manually each time, and it affects the readability of the code, especially if the object is complex. + +This is where this package comes to the rescue, which takes care of all the work with mapping and initialization of the necessary DTO. + + +.. toctree:: + :maxdepth: 2 + + installation + usage diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..2e95ac8 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,11 @@ +Installation +========== + +The package can be installed via composer: + +Composer Install +---------------- + +.. code-block:: bash + + $ composer require yzen.dev/plain-to-class diff --git a/docs/usage.rst b/docs/usage.rst new file mode 100644 index 0000000..bec6eec --- /dev/null +++ b/docs/usage.rst @@ -0,0 +1,31 @@ +Usage +============= + + +Common use case: + +.. code-block:: php + + namespace DTO; + + class CreateUserDTO + { + public string $email; + public float $balance; + } + +.. code-block:: php + + $data = [ + 'email' => 'test@mail.com', + 'balance' => 128.41, + ]; + $dto = ClassTransformer::transform(CreateUserDTO::class, $data); + var_dump($dto); + +Output: + +.. code-block:: bash + object(\LoginDTO) + 'email' => string(13) "test@mail.com" + 'balance' => float(128.41) From 20f2ccb8a00f7e361dc8bd05c23a67c0de456688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 16:52:38 +0300 Subject: [PATCH 031/100] issue docs --- README.RU.md | 334 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 215 ++++++++++++++++++++----------- docs/usage.rst | 265 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 740 insertions(+), 74 deletions(-) create mode 100644 README.RU.md diff --git a/README.RU.md b/README.RU.md new file mode 100644 index 0000000..59253d7 --- /dev/null +++ b/README.RU.md @@ -0,0 +1,334 @@ +## ClassTransformer + +![Packagist Version](https://img.shields.io/packagist/v/yzen.dev/plain-to-class?color=blue&label=version) +![GitHub Workflow Status](https://img.shields.io/github/workflow/status/yzen-dev/plain-to-class/Run%20tests?label=tests&logo=github) +[![Coverage](https://codecov.io/gh/yzen-dev/plain-to-class/branch/master/graph/badge.svg?token=QAO8STLPMI)](https://codecov.io/gh/yzen-dev/plain-to-class) +![License](https://img.shields.io/github/license/yzen-dev/plain-to-class) +![Packagist Downloads](https://img.shields.io/packagist/dm/yzen.dev/plain-to-class) +![Packagist Downloads](https://img.shields.io/packagist/dt/yzen.dev/plain-to-class) + +> Увы, но я не владею английским, так что документация была составлена с помощью Google Translate :С +> Я буду рад, если вы поможете мне более грамотно описать документацию :) + +Эта библиотека позволит вам легко преобразовать любой набор данных в нужный вам объект. От вас не требуется менять структуру классов, наследовать их от внешних модулей и т.д. Никаких танцев с бубнами - только данные и нужный класс. + +Хорошей практикой считается написание кода независимого от сторонних пакетов и фреймворков. Код разбивается на сервисы, доменные зоны, различные слои и т.д. Для передачи данных между слоями как правило используются шаблон DataTransfer Object (DTO). DTO - это объект, который используется для инкапсуляции данных и отправки их из одной подсистемы приложения в другую. + +Тем самым сервисы/методы работают с конкретным объектом и данными необходимым для него. При этом не важно откуда эти данные были получены, это может быть http запрос, БД, файл и т.д. + +Соответственно при каждом вызове сервиса нам необходимо инициализировать данное DTO. Но сопоставлять каждый раз данные в ручную это не эффективно, и сказывается на читабельности кода, особенно если объект сложный. + +Здесь на помощь приходит данный пакет, который берет на себя всю работу с мапингом и инициализацией необходимой DTO. + +## :scroll: **Установка** + +Пакет может быть установлен с помощью composer: + +``` +композитору требуется yzen.dev/plain-to-class +``` + +> Примечание: Текущая версия пакета поддерживает только PHP 8.1 +. + +> Для PHP версии 7.4 вы можете ознакомиться с документацией +> в [версия v0.*](https://github.com/yzen-dev/plain-to-class/tree/php-7.4 ). +> +>Для PHP версии 8.0 вы можете ознакомиться с документацией +> в [версия v1.*](https://github.com/yzen-dev/plain-to-class/tree/php-8.0 ). + +## :scroll: **Использование** + +Общий вариант использования: + +### :scroll: **Base** + +``` +namespace DTO; + +class CreateUserDTO +{ + public string $email; + public float $balance; +} +``` + +```php +$data = [ + 'email' => 'test@mail.com', + 'balance' => 128.41, +]; +$dto = ClassTransformer::transform(CreateUserDTO::class, $data); +var_dump($dto); +``` + +Result: + +```php +object(\LoginDTO) + 'email' => string(13) "test@mail.com" + 'balance' => float(128.41) +``` + +Также с версии php 8 вы можете передавать именованные аргументы: + +```php +$dto = ClassTransformer::transform(CreateUserDTO::class, + email: 'test@mail.com', + balance: 128.41 + ); +``` + +Если свойство не является скалярным типом, и у него явно указан класс, оно будет автоматически рекурсивно к нему +приведено. + +```php +class ProductDTO +{ + public int $id; + public string $name; +} + +class PurchaseDTO +{ + public ProductDTO $product; + public float $cost; +} + +$data = [ + 'product' => ['id' => 1, 'name' => 'phone'], + 'cost' => 10012.23, +]; + +$purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); +var_dump($purchaseDTO); +``` + +Результат: + +```php +object(PurchaseDTO) + public ProductDTO 'product' => + object(ProductDTO) + public int 'id' => int 1 + public string 'name' => string 'phone' (length=5) + public float 'cost' => float 10012.23 +``` + +### :scroll: **Коллекция** + +Если у вас есть массив объектов определенного класса, то вы должны указать для него атрибут ConvertArray, передав его в +какой класс вам нужно привести элементы. + +Также можно указать класс в PHP DOC, но тогда вам нужно написать полный путь к этомуклассу `array <\DTO\ProductDTO>`. +Это делается для того, чтобы точно знать, какой экземпляр нужно создать. Поскольку Reflection не предоставляет готовых +функций для получения файла `use`. Помимо `use`, вы можете указать псевдоним, и его будет сложнее отследить. Пример: + +```php + +class ProductDTO +{ + public int $id; + public string $name; +} + +class PurchaseDTO +{ + #[ConvertArray(ProductDTO::class)] + public array $products; +} + +$data = [ + 'products' => [ + ['id' => 1, 'name' => 'phone',], + ['id' => 2, 'name' => 'bread',], + ], +]; +$purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); +``` + +#### :scroll: **Анонимная коллекция** + +В случае, если вам нужно преобразовать массив данных в массив объектов класса, вы можете реализовать это с помощью +метода `transformCollection`. + +```php +$data = [ + ['id' => 1, 'name' => 'phone'], + ['id' => 2, 'name' => 'bread'], +]; +$products = ClassTransformer::transformCollection(ProductDTO::class, $data); +``` + +В результате этого выполнения вы получите массив объектов ProductDTO + +```php +array(2) { + [0]=> + object(ProductDTO) { + ["id"]=> int(1) + ["name"]=> string(5) "phone" + } + [1]=> + object(ProductDTO) { + ["id"]=> int(2) + ["name"]=> string(5) "bread" + } +} +``` + +Вам также может потребоваться поэлементное преобразование массива. В таком случае вы можете передать массив классов, +который затем можно легко распаковать + +```php + $userData = ['id' => 1, 'email' => 'test@test.com', 'balance' => 10012.23]; + $purchaseData = [ + 'products' => [ + ['id' => 1, 'name' => 'phone',], + ['id' => 2, 'name' => 'bread',], + ], + 'user' => ['id' => 3, 'email' => 'fake@mail.com', 'balance' => 10012.23,], + ]; + + $result = ClassTransformer::transformMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); + + [$user, $purchase] = $result; + var_dump($user); + var_dump($purchase); +``` + +Result: + +```php +object(UserDTO) (3) { + ["id"] => int(1) + ["email"]=> string(13) "test@test.com" + ["balance"]=> float(10012.23) +} + +object(PurchaseDTO) (2) { + ["products"]=> + array(2) { + [0]=> + object(ProductDTO)#349 (3) { + ["id"]=> int(1) + ["name"]=> string(5) "phone" + } + [1]=> + object(ProductDTO)#348 (3) { + ["id"]=> int(2) + ["name"]=> string(5) "bread" + } + } + ["user"]=> + object(UserDTO)#332 (3) { + ["id"]=> int(3) + ["email"]=> string(13) "fake@mail.com" + ["balance"]=> float(10012.23) + } +} +``` + +### :scroll: ** Стиль написания** + +Постоянная проблема со стилем написания, например, в базе данных это snake_case, а в коде camelCase. +И их постоянно нужно как-то трансформировать. Пакет позаботится об этом, вам просто нужно указать +атрибут WritingStyle в свойстве: + +```php +class WritingStyleSnakeCaseDTO +{ + #[WritingStyle(WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_SNAKE_CASE)] + public string $contact_fio; + + #[WritingStyle(WritingStyle::STYLE_CAMEL_CASE)] + public string $contact_email; +} + + + $data = [ + 'contactFio' => 'yzen.dev', + 'contactEmail' => 'test@mail.com', +]; +$model = ClassTransformer::transform(WritingStyleSnakeCaseDTO::class, $data); +var_dump($model); +``` + +```php +RESULT: + +object(WritingStyleSnakeCaseDTO) (2) { + ["contact_fio"]=> string(8) "yzen.dev" + ["contact_email"]=> string(13) "test@mail.com" +} +``` + +### :scroll: **Alias** + +Для свойства можно задать различные возможные alias'ы, которые будут также искаться в источнике данных. Это может быть +полезно если DTO формируется по разным источникам данных. + +```php +class WithAliasDTO +{ + #[FieldAlias('userFio')] + public string $fio; + + #[FieldAlias(['email', 'phone'])] + public string $contact; +} +``` + +### :scroll: **Кастомизация сеттеров** + +Если поле требует дополнительной обработки при его инициализации, вы можете мутировать его сеттер. Для это создайте в +классе метод следующего формата - `set{$name}Attribute`. Пример: + +```php +class UserDTO +{ + public int $id; + public string $real_address; + + public function setRealAddressAttribute(string $value) + { + $this->real_address = strtolower($value); + } +} +``` + +### :scroll: **Пост обработка** + +Внутри класса вы можете создать метод `afterTransform`, который вызовется сразу по завершению преобразования. В нем мы +можете описать свою дополнительную логику проверки или преобразования работая уже с состоянием объекта. + +```php +class UserDTO +{ + public int $id; + public float $balance; + + public function afterTransform() + { + $this->balance = 777; + } +} +``` + +### :scroll: **Кастомное преобразование** + +Если вам требуется полностью свое преобразование, то вы можете в классе создать метод transform. В таком случае никакие +обработки библиотеки не вызываются, вся ответственность преобразования переходит на ваш класс. + +```php +class CustomTransformUserDTOArray +{ + public string $email; + public string $username; + + public function transform($args) + { + $this->email = $args['login']; + $this->username = $args['fio']; + } +} +``` diff --git a/README.md b/README.md index cfd29a2..646de1b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Class-transformer helper +## ClassTransformer ![Packagist Version](https://img.shields.io/packagist/v/yzen.dev/plain-to-class?color=blue&label=version) ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/yzen-dev/plain-to-class/Run%20tests?label=tests&logo=github) @@ -7,24 +7,22 @@ ![Packagist Downloads](https://img.shields.io/packagist/dm/yzen.dev/plain-to-class) ![Packagist Downloads](https://img.shields.io/packagist/dt/yzen.dev/plain-to-class) -> Alas, I do not speak English, and the documentation was compiled through google translator :( -> I will be glad if you can help me describe the documentation more correctly :) +> Alas, I do not speak English, and the documentation was compiled through google translator :( I will be glad if you can help me describe the documentation more correctly :) -This package will help you transform any dataset into a structured object. This is very convenient when values obtained -from a query, database, or any other place can be easily cast to the object you need. But what exactly is this -convenient? +This library will allow you to easily convert any data set into the object you need. You are not required to change the structure of classes, inherit them from external modules, etc. No dancing with tambourines - just data and the right class. -When writing code, it is very important to separate logic, adhere to the principle of single responsibility, reduce -dependence on other services, and much more. +It is considered good practice to write code independent of third-party packages and frameworks. The code is divided into services, domain zones, various layers, etc. -When creating a new service to create a user, you only need the necessary data set - name, email and phone. Why do you -need to check around separately arrays, separately objects, check for the presence of keys through isset. It is much -more convenient to make a DTO model with which the service will already work. +To transfer data between layers, the **DataTransfer Object** (DTO) template is usually used. A DTO is an object that is used to encapsulate data and send it from one application subsystem to another. -This approach guarantees that the service will work with the data it needs, full typing, there is no need to check for -the presence of keys if it is an array. +Thus, services/methods work with a specific object and the data necessary for it. At the same time, it does not matter where this data was obtained from, it can be an http request, a database, a file, etc. -## :scroll: **Installation** +Accordingly, each time the service is called, we need to initialize this DTO. But it is not effective to compare data manually each time, and it affects the readability of the code, especially if the object is complex. + +This is where this package comes to the rescue, which takes care of all the work with mapping and initialization of the necessary DTO. + + +## :scroll: **Установка** The package can be installed via composer: @@ -32,17 +30,20 @@ The package can be installed via composer: composer require yzen.dev/plain-to-class ``` ->Note: The current version of the package supports only PHP^8.0. +> Note: The current version of the package supports only PHP 8.1 +. + +> For PHP version 7.4, you can read the documentation in [version v0.*](https://github.com/yzen-dev/plain-to-class/tree/php-7.4). > ->For PHP version 7.4, you can read the documentation in [version v0.*](https://github.com/yzen-dev/plain-to-class/tree/php-7.4). +> For PHP version 8.0, you can read the documentation in [version v1.*](https://github.com/yzen-dev/plain-to-class/tree/php-8.0). ## :scroll: **Usage** Common use case: + ### :scroll: **Base** -```php +``` namespace DTO; class CreateUserDTO @@ -57,7 +58,7 @@ $data = [ 'email' => 'test@mail.com', 'balance' => 128.41, ]; -$dto = ClassTransformer::transform(CreateUserDTO::class,$data); +$dto = ClassTransformer::transform(CreateUserDTO::class, $data); var_dump($dto); ``` @@ -80,40 +81,60 @@ $dto = ClassTransformer::transform(CreateUserDTO::class, If the property is not of a scalar type, but a class of another DTO is allowed, it will also be automatically converted. -### :scroll: **Collection** - -If you have an array of objects of a certain class, then you must specify the ConvertArray attribute to it, passing it -to which class you need to cast the elements. - -It is also possible to specify the class in PHP DOC, but then you need to write the full path to this -class `array <\ DTO \ ProductDTO>`. This is done in order to know exactly which instance you need to create. Since -Reflection does not provide out-of-the-box functions for getting the `use *` file. Besides `use *`, you can specify an -alias, and it will be more difficult to trace it. Example: - ```php - class ProductDTO { public int $id; public string $name; } +class PurchaseDTO +{ + public ProductDTO $product; + public float $cost; +} -class UserDTO +$data = [ + 'product' => ['id' => 1, 'name' => 'phone'], + 'cost' => 10012.23, +]; + +$purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); +var_dump($purchaseDTO); +``` + +Output: + +```php +object(PurchaseDTO) + public ProductDTO 'product' => + object(ProductDTO) + public int 'id' => int 1 + public string 'name' => string 'phone' (length=5) + public float 'cost' => float 10012.23 +``` + +### :scroll: **Коллекция** + +If you have an array of objects of a certain class, then you must specify the ConvertArray attribute for it, passing it to which class you need to bring the elements. + +You can also specify a class in PHP DOC, but then you need to write the full path to this class `array <\DTO\ProductDTO>`. +This is done in order to know exactly which instance you need to create. Since Reflection does not provide out-of-the-box functions for getting the `use *` file. Besides `use *`, you can specify an alias, and it will be more difficult to trace it. +Example: + + +```php + +class ProductDTO { public int $id; - public string $email; - public string $balance; + public string $name; } - class PurchaseDTO { #[ConvertArray(ProductDTO::class)] public array $products; - - /** @var UserDTO $user */ - public UserDTO $user; } $data = [ @@ -121,45 +142,24 @@ $data = [ ['id' => 1, 'name' => 'phone',], ['id' => 2, 'name' => 'bread',], ], - 'user' => ['id' => 1, 'email' => 'test@test.com', 'balance' => 10012.23,], ]; $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); -var_dump($purchaseDTO); ``` -```php -object(PurchaseDTO)[345] - public array 'products' => - array (size=2) - 0 => - object(ProductDTO)[1558] - public int 'id' => int 1 - public string 'name' => string 'phone' (length=5) - 1 => - object(ProductDTO)[1563] - public int 'id' => int 2 - public string 'name' => string 'bread' (length=5) - public UserDTO 'user' => - object(UserDTO)[1559] - public int 'id' => int 1 - public string 'email' => string 'test@test.com' (length=13) - public float 'balance' => float 10012.23 -``` +#### :scroll: **Anonymous array** -### :scroll: **Anonymous array** - -In case you need to convert an array of data into an array of class objects, you can implement this through an anonymous -array. To do this, you just need to wrap the class in **[]** +In case you need to convert an array of data into an array of class objects, you can implement this using +the `transformCollection` method. ```php $data = [ - ['id' => 1, 'name' => 'phone'], - ['id' => 2, 'name' => 'bread'], - ]; -$products = ClassTransformer::transform([ProductDTO::class], $data); + ['id' => 1, 'name' => 'phone'], + ['id' => 2, 'name' => 'bread'], +]; +$products = ClassTransformer::transformCollection(ProductDTO::class, $data); ``` -The result of this execution you will get an array of objects ProductDTO +As a result of this execution, you will get an array of ProductDTO objects ```php array(2) { @@ -176,8 +176,8 @@ array(2) { } ``` -You may also need an element-by-element array transformation. In such a case, you can pass an array of classes, which -can then be easily unpacked +You may also need a piecemeal transformation of the array. In this case, you can pass an array of classes, +which can then be easily unpacked. ```php $userData = ['id' => 1, 'email' => 'test@test.com', 'balance' => 10012.23]; @@ -189,7 +189,7 @@ can then be easily unpacked 'user' => ['id' => 3, 'email' => 'fake@mail.com', 'balance' => 10012.23,], ]; - $result = ClassTransformer::transform([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); + $result = ClassTransformer::transformMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); [$user, $purchase] = $result; var_dump($user); @@ -230,9 +230,7 @@ object(PurchaseDTO) (2) { ### :scroll: **Writing style** -A constant problem with the style of writing, for example, in the database it is snake_case, and in the camelCase code. -And they constantly need to be transformed somehow. The package takes care of this, you just need to specify the -WritingStyle attribute on the property: +A constant problem with the style of writing, for example, in the database it is snake_case, and in the camelCase code. And they constantly need to be transformed somehow. The package takes care of this, you just need to specify the WritingStyle attribute on the property: ```php class WritingStyleSnakeCaseDTO @@ -246,9 +244,9 @@ class WritingStyleSnakeCaseDTO $data = [ - 'contactFio' => 'yzen.dev', - 'contactEmail' => 'test@mail.com', - ]; + 'contactFio' => 'yzen.dev', + 'contactEmail' => 'test@mail.com', +]; $model = ClassTransformer::transform(WritingStyleSnakeCaseDTO::class, $data); var_dump($model); ``` @@ -261,3 +259,72 @@ object(WritingStyleSnakeCaseDTO) (2) { ["contact_email"]=> string(13) "test@mail.com" } ``` + +### :scroll: **Alias** + +Various possible aliases can be set for the property, which will also be searched in the data source. This can be +useful if the DTO is generated from different data sources. + +```php +class WithAliasDTO +{ + #[FieldAlias('userFio')] + public string $fio; + + #[FieldAlias(['email', 'phone'])] + public string $contact; +} +``` + +### :scroll: **Custom setter** + +Если поле требует дополнительной обработки при его инициализации, вы можете мутировать его сеттер. Для это создайте в классе метод следующего формата - `set{$name}Attribute`. Пример: + +```php +class UserDTO +{ + public int $id; + public string $real_address; + + public function setRealAddressAttribute(string $value) + { + $this->real_address = strtolower($value); + } +} +``` + +### :scroll: **After Transform** + +Inside the class, you can create the `afterTransform` method, which will be called immediately after the conversion is completed. In it, we +can describe our additional verification or transformation logic by already working with the state of the object. + +```php +class UserDTO +{ + public int $id; + public float $balance; + + public function afterTransform() + { + $this->balance = 777; + } +} +``` + +### :scroll: **Custom transform** + +If you need to completely transform yourself, then you can create a transform method in the class. In this case, no library processing is called, all the responsibility of the conversion passes to your class. + +```php +class CustomTransformUserDTOArray +{ + public string $email; + public string $username; + + public function transform($args) + { + $this->email = $args['login']; + $this->username = $args['fio']; + } +} +``` diff --git a/docs/usage.rst b/docs/usage.rst index bec6eec..56a0fcf 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -29,3 +29,268 @@ Output: object(\LoginDTO) 'email' => string(13) "test@mail.com" 'balance' => float(128.41) + +Also for php 8 you can pass named arguments: + +.. code-block:: php + + $dto = ClassTransformer::transform(CreateUserDTO::class, + email: 'test@mail.com', + balance: 128.41 + ); + +If the property is not of a scalar type, but a class of another DTO is allowed, it will also be automatically converted. + +.. code-block:: php + + class ProductDTO + { + public int $id; + public string $name; + } + + class PurchaseDTO + { + public ProductDTO $product; + public float $cost; + } + + $data = [ + 'product' => ['id' => 1, 'name' => 'phone'], + 'cost' => 10012.23, + ]; + + $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); + var_dump($purchaseDTO); + +.. code-block:: bash + + object(PurchaseDTO) + public ProductDTO 'product' => + object(ProductDTO) + public int 'id' => int 1 + public string 'name' => string 'phone' (length=5) + public float 'cost' => float 10012.23 + + +Collection +---------- + +If you have an array of objects of a certain class, then you must specify the ConvertArray attribute for it, passing it to which class you need to bring the elements. + +You can also specify a class in PHP DOC, but then you need to write the full path to this class `array <\DTO\ProductDTO>`. +This is done in order to know exactly which instance you need to create. Since Reflection does not provide out-of-the-box functions for getting the use * file. Besides use *, you can specify an alias, and it will be more difficult to trace it. +Example: + +.. code-block:: php + class ProductDTO + { + public int $id; + public string $name; + } + + class PurchaseDTO + { + #[ConvertArray(ProductDTO::class)] + public array $products; + } + + $data = [ + 'products' => [ + ['id' => 1, 'name' => 'phone',], + ['id' => 2, 'name' => 'bread',], + ], + ]; + $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); + + +Anonymous array +--------------- + +In case you need to convert an array of data into an array of class objects, you can implement this using +the `transformCollection` method. + +.. code-block:: php + + $data = [ + ['id' => 1, 'name' => 'phone'], + ['id' => 2, 'name' => 'bread'], + ]; + $products = ClassTransformer::transformCollection(ProductDTO::class, $data); + + +As a result of this execution, you will get an array of ProductDTO objects + +.. code-block:: php + + array(2) { + [0]=> + object(ProductDTO) { + ["id"]=> int(1) + ["name"]=> string(5) "phone" + } + [1]=> + object(ProductDTO) { + ["id"]=> int(2) + ["name"]=> string(5) "bread" + } + } + +You may also need a piecemeal transformation of the array. In this case, you can pass an array of classes, +which can then be easily unpacked. + +.. code-block:: php + + $userData = ['id' => 1, 'email' => 'test@test.com', 'balance' => 10012.23]; + $purchaseData = [ + 'products' => [ + ['id' => 1, 'name' => 'phone',], + ['id' => 2, 'name' => 'bread',], + ], + 'user' => ['id' => 3, 'email' => 'fake@mail.com', 'balance' => 10012.23,], + ]; + + $result = ClassTransformer::transformMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); + + [$user, $purchase] = $result; + var_dump($user); + var_dump($purchase); + +Result: + +.. code-block:: bash + + object(UserDTO) (3) { + ["id"] => int(1) + ["email"]=> string(13) "test@test.com" + ["balance"]=> float(10012.23) + } + + object(PurchaseDTO) (2) { + ["products"]=> + array(2) { + [0]=> + object(ProductDTO)#349 (3) { + ["id"]=> int(1) + ["name"]=> string(5) "phone" + } + [1]=> + object(ProductDTO)#348 (3) { + ["id"]=> int(2) + ["name"]=> string(5) "bread" + } + } + ["user"]=> + object(UserDTO)#332 (3) { + ["id"]=> int(3) + ["email"]=> string(13) "fake@mail.com" + ["balance"]=> float(10012.23) + } + } + +Writing style +_____________ + +A constant problem with the style of writing, for example, in the database it is snake_case, and in the camelCase code. And they constantly need to be transformed somehow. The package takes care of this, you just need to specify the WritingStyle attribute on the property: + +.. code-block:: php + + class WritingStyleSnakeCaseDTO + { + #[WritingStyle(WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_SNAKE_CASE)] + public string $contact_fio; + + #[WritingStyle(WritingStyle::STYLE_CAMEL_CASE)] + public string $contact_email; + } + + + $data = [ + 'contactFio' => 'yzen.dev', + 'contactEmail' => 'test@mail.com', + ]; + $model = ClassTransformer::transform(WritingStyleSnakeCaseDTO::class, $data); + var_dump($model); + +Output: + +.. code-block:: bash + + object(WritingStyleSnakeCaseDTO) (2) { + ["contact_fio"]=> string(8) "yzen.dev" + ["contact_email"]=> string(13) "test@mail.com" + } + +Alias +_____ + +Various possible aliases can be set for the property, which will also be searched in the data source. This can be +useful if the DTO is generated from different data sources. + +.. code-block:: php + + class WithAliasDTO + { + #[FieldAlias('userFio')] + public string $fio; + + #[FieldAlias(['email', 'phone'])] + public string $contact; + } + + +Custom setter +_____________ + +If a field requires additional processing during its initialization, you can mutate its setter. To do this, create a method in the following format in the class - `set{$name}Attribute`. Example: + +.. code-block:: php + + class UserDTO + { + public int $id; + public string $real_address; + + public function setRealAddressAttribute(string $value) + { + $this->real_address = strtolower($value); + } + } + +After Transform +_______________ + +Inside the class, you can create the `afterTransform` method, which will be called immediately after the conversion is completed. In it, we +can describe our additional verification or transformation logic by already working with the state of the object. + +.. code-block:: php + + class UserDTO + { + public int $id; + public float $balance; + + public function afterTransform() + { + $this->balance = 777; + } + } + +Custom transform +________________ + +If you need to completely transform yourself, then you can create a transform method in the class. In this case, no library processing is called, all the responsibility of the conversion passes to your class. + +.. code-block:: php + + class CustomTransformUserDTOArray + { + public string $email; + public string $username; + + public function transform($args) + { + $this->email = $args['login']; + $this->username = $args['fio']; + } + } From 4fad5105996ac8f3b4f08e5484169bba3f7086f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 16:55:12 +0300 Subject: [PATCH 032/100] fix header --- docs/usage.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 56a0fcf..f765237 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -189,7 +189,7 @@ Result: } Writing style -_____________ +------------- A constant problem with the style of writing, for example, in the database it is snake_case, and in the camelCase code. And they constantly need to be transformed somehow. The package takes care of this, you just need to specify the WritingStyle attribute on the property: @@ -222,7 +222,7 @@ Output: } Alias -_____ +----- Various possible aliases can be set for the property, which will also be searched in the data source. This can be useful if the DTO is generated from different data sources. @@ -240,7 +240,7 @@ useful if the DTO is generated from different data sources. Custom setter -_____________ +------------- If a field requires additional processing during its initialization, you can mutate its setter. To do this, create a method in the following format in the class - `set{$name}Attribute`. Example: @@ -258,7 +258,7 @@ If a field requires additional processing during its initialization, you can mut } After Transform -_______________ +--------------- Inside the class, you can create the `afterTransform` method, which will be called immediately after the conversion is completed. In it, we can describe our additional verification or transformation logic by already working with the state of the object. @@ -277,7 +277,7 @@ can describe our additional verification or transformation logic by already work } Custom transform -________________ +---------------- If you need to completely transform yourself, then you can create a transform method in the class. In this case, no library processing is called, all the responsibility of the conversion passes to your class. From 1915257a8428dd2b1a85ef180566bda496aa0559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 17:35:36 +0300 Subject: [PATCH 033/100] fix: codestyle --- src/ArgumentsResource.php | 8 +++----- src/Attributes/ConvertArray.php | 8 ++++++-- src/Attributes/FieldAlias.php | 7 ++++--- src/ClassTransformer.php | 6 +++--- src/GenericInstance.php | 14 ++++++++------ src/TransformBuilder.php | 2 +- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index 3181317..6b894e8 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -23,18 +23,16 @@ final class ArgumentsResource private array $args; /** - * @param mixed $args + * @param iterable|mixed ...$args */ public function __construct(...$args) { // Unpacking named arguments $inArgs = sizeof(func_get_args()) === 1 ? $args[0] : $args; - if (is_object($inArgs)) { - $inArgs = (array)$inArgs; - } + $inArgs = (array)$inArgs; - $this->args = $inArgs ?? []; + $this->args = $inArgs; } /** diff --git a/src/Attributes/ConvertArray.php b/src/Attributes/ConvertArray.php index 1cf6241..f6d5612 100644 --- a/src/Attributes/ConvertArray.php +++ b/src/Attributes/ConvertArray.php @@ -10,7 +10,11 @@ #[\Attribute(\Attribute::TARGET_PARAMETER)] final class ConvertArray { - public function __construct(string $type) - { + /** + * @param string $type + */ + public function __construct( + public string $type + ) { } } diff --git a/src/Attributes/FieldAlias.php b/src/Attributes/FieldAlias.php index ce81378..87d52a8 100644 --- a/src/Attributes/FieldAlias.php +++ b/src/Attributes/FieldAlias.php @@ -11,9 +11,10 @@ final class FieldAlias { /** - * @param string|array $aliases + * @param string|array $aliases */ - public function __construct(string|array $aliases) - { + public function __construct( + public string|array $aliases + ) { } } diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 270b673..85ee3e1 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -16,7 +16,7 @@ final class ClassTransformer * Class-transformer function to transform our object into a typed object * * @param class-string $className - * @param array|object|null $args + * @param iterable ...$args * * @return null|T * @throws ClassNotFoundException @@ -29,7 +29,7 @@ public static function transform(string $className, ...$args) /** * @param class-string $className - * @param array>|array $args + * @param array> $args * * @return null|array|array * @throws ClassNotFoundException @@ -45,7 +45,7 @@ public static function transformCollection(string $className, array $args): ?arr /** * @param array> $className - * @param array>|array $args + * @param array> $args * * @return null|array|array * @throws ClassNotFoundException diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 5457030..688d20b 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -93,7 +93,7 @@ public function transform(): mixed /** * @param GenericProperty $property - * @param int|string|array|object $value + * @param mixed $value * * @return mixed * @throws ClassNotFoundException @@ -104,11 +104,11 @@ private function castAttribute(GenericProperty $property, $value) return $value; } - if ($property->isArray()) { + if ($property->isArray() && is_array($value)) { return $this->castArray($property, $value); } - if ($property->isEnum()) { + if ($property->isEnum() && (is_string($value) || is_int($value))) { return $this->castEnum($property, $value); } @@ -116,6 +116,7 @@ private function castAttribute(GenericProperty $property, $value) /** @var class-string $propertyClass */ $propertyClass = $property->type->getName(); + /** @phpstan-ignore-next-line */ return (new TransformBuilder($propertyClass, $value))->build(); } return $value; @@ -123,12 +124,12 @@ private function castAttribute(GenericProperty $property, $value) /** * @param GenericProperty $property - * @param mixed $value + * @param array $value * - * @return array + * @return array * @throws ClassNotFoundException */ - private function castArray(GenericProperty $property, $value) + private function castArray(GenericProperty $property, array $value): array { $arrayTypeAttr = $property->getAttribute(ConvertArray::class); if ($arrayTypeAttr !== null) { @@ -169,6 +170,7 @@ private function castArray(GenericProperty $property, $value) */ private function castEnum(GenericProperty $property, int|string $value) { + /** @phpstan-ignore-next-line */ $propertyClass = $property->type->getName(); if (method_exists($propertyClass, 'from')) { /** @var \BackedEnum $propertyClass */ diff --git a/src/TransformBuilder.php b/src/TransformBuilder.php index 74131d0..d764306 100644 --- a/src/TransformBuilder.php +++ b/src/TransformBuilder.php @@ -23,7 +23,7 @@ final class TransformBuilder /** * @param class-string $class - * @param array|object|null|mixed $args + * @param iterable ...$args * * @throws ClassNotFoundException */ From 97b6c8269c17c47c2b85436a6e20e3e91babfbbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 17:36:36 +0300 Subject: [PATCH 034/100] fix: typo --- tests/Benchmark/FullCheckBench.php | 2 +- tests/Benchmark/LiteCheckBench.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Benchmark/FullCheckBench.php b/tests/Benchmark/FullCheckBench.php index 1de1cac..c2b463f 100644 --- a/tests/Benchmark/FullCheckBench.php +++ b/tests/Benchmark/FullCheckBench.php @@ -14,7 +14,7 @@ * * @package Tests\Benchmark * - * ./vendor/bin/phpbench run tests/Benchmark/CheckBench.php --report=default + * ./vendor/bin/phpbench run tests/Benchmark/FullCheckBench.php --report=default */ class FullCheckBench extends TestCase { diff --git a/tests/Benchmark/LiteCheckBench.php b/tests/Benchmark/LiteCheckBench.php index 1d41111..8aa16c6 100644 --- a/tests/Benchmark/LiteCheckBench.php +++ b/tests/Benchmark/LiteCheckBench.php @@ -13,7 +13,7 @@ * * @package Tests\Benchmark * - * ./vendor/bin/phpbench run tests/Benchmark/CheckBench.php --report=default + * ./vendor/bin/phpbench run tests/Benchmark/LiteCheckBench.php --report=default */ class LiteCheckBench extends TestCase { From 03bf6a4ee24f6b8723e7683422d8493a34907c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 17:40:15 +0300 Subject: [PATCH 035/100] add link to docs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 646de1b..d16c353 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Accordingly, each time the service is called, we need to initialize this DTO. Bu This is where this package comes to the rescue, which takes care of all the work with mapping and initialization of the necessary DTO. +[Documentation](https://plain-to-class.readthedocs.io) ## :scroll: **Установка** From 98c4562d9c6120c7a90989660a5bc0e29131f18d Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Thu, 2 Feb 2023 18:12:41 +0300 Subject: [PATCH 036/100] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index d16c353..b410089 100644 --- a/README.md +++ b/README.md @@ -329,3 +329,14 @@ class CustomTransformUserDTOArray } } ``` + +### Comparison +I also made a comparison with current analogues and here are the main disadvantages +- Works only for a specific framework +- Force to inherit or change your current class structure +- Conversion takes longer + +Below is an example of my benchmark comparison + +https://github.com/yzen-dev/php-dto-transform-benchmark +![image](https://user-images.githubusercontent.com/24630195/216361904-e2cf5674-071b-4e3e-9ecd-937f88c472f5.png) From 1992223e1633a0888556db4d621254b11f930877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 18:35:36 +0300 Subject: [PATCH 037/100] composer update --- composer.lock | 84 +++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/composer.lock b/composer.lock index 98a7b3f..b8f87f9 100644 --- a/composer.lock +++ b/composer.lock @@ -832,16 +832,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.23", + "version": "9.2.24", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c" + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", - "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed", "shasum": "" }, "require": { @@ -897,7 +897,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.23" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24" }, "funding": [ { @@ -905,7 +905,7 @@ "type": "github" } ], - "time": "2022-12-28T12:41:10+00:00" + "time": "2023-01-26T08:26:55+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2488,16 +2488,16 @@ }, { "name": "symfony/console", - "version": "v6.2.3", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0f579613e771dba2dbb8211c382342a641f5da06" + "reference": "3e294254f2191762c1d137aed4b94e966965e985" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0f579613e771dba2dbb8211c382342a641f5da06", - "reference": "0f579613e771dba2dbb8211c382342a641f5da06", + "url": "https://api.github.com/repos/symfony/console/zipball/3e294254f2191762c1d137aed4b94e966965e985", + "reference": "3e294254f2191762c1d137aed4b94e966965e985", "shasum": "" }, "require": { @@ -2564,7 +2564,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.3" + "source": "https://github.com/symfony/console/tree/v6.2.5" }, "funding": [ { @@ -2580,7 +2580,7 @@ "type": "tidelift" } ], - "time": "2022-12-28T14:26:22+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2651,16 +2651,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.2.0", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016" + "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/50b2523c874605cf3d4acf7a9e2b30b6a440a016", - "reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e59e8a4006afd7f5654786a83b4fcb8da98f4593", + "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593", "shasum": "" }, "require": { @@ -2694,7 +2694,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.0" + "source": "https://github.com/symfony/filesystem/tree/v6.2.5" }, "funding": [ { @@ -2710,20 +2710,20 @@ "type": "tidelift" } ], - "time": "2022-11-20T13:01:27+00:00" + "time": "2023-01-20T17:45:48+00:00" }, { "name": "symfony/finder", - "version": "v6.2.3", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e" + "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/81eefbddfde282ee33b437ba5e13d7753211ae8e", - "reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e", + "url": "https://api.github.com/repos/symfony/finder/zipball/c90dc446976a612e3312a97a6ec0069ab0c2099c", + "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c", "shasum": "" }, "require": { @@ -2758,7 +2758,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.2.3" + "source": "https://github.com/symfony/finder/tree/v6.2.5" }, "funding": [ { @@ -2774,20 +2774,20 @@ "type": "tidelift" } ], - "time": "2022-12-22T17:55:15+00:00" + "time": "2023-01-20T17:45:48+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.2.0", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "d28f02acde71ff75e957082cd36e973df395f626" + "reference": "e8324d44f5af99ec2ccec849934a242f64458f86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d28f02acde71ff75e957082cd36e973df395f626", - "reference": "d28f02acde71ff75e957082cd36e973df395f626", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/e8324d44f5af99ec2ccec849934a242f64458f86", + "reference": "e8324d44f5af99ec2ccec849934a242f64458f86", "shasum": "" }, "require": { @@ -2825,7 +2825,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.2.0" + "source": "https://github.com/symfony/options-resolver/tree/v6.2.5" }, "funding": [ { @@ -2841,7 +2841,7 @@ "type": "tidelift" } ], - "time": "2022-11-02T09:08:04+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3175,16 +3175,16 @@ }, { "name": "symfony/process", - "version": "v6.2.0", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877" + "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/ba6e55359f8f755fe996c58a81e00eaa67a35877", - "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877", + "url": "https://api.github.com/repos/symfony/process/zipball/9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", + "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", "shasum": "" }, "require": { @@ -3216,7 +3216,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.2.0" + "source": "https://github.com/symfony/process/tree/v6.2.5" }, "funding": [ { @@ -3232,7 +3232,7 @@ "type": "tidelift" } ], - "time": "2022-11-02T09:08:04+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/service-contracts", @@ -3321,16 +3321,16 @@ }, { "name": "symfony/string", - "version": "v6.2.2", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "863219fd713fa41cbcd285a79723f94672faff4d" + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/863219fd713fa41cbcd285a79723f94672faff4d", - "reference": "863219fd713fa41cbcd285a79723f94672faff4d", + "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", "shasum": "" }, "require": { @@ -3387,7 +3387,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.2" + "source": "https://github.com/symfony/string/tree/v6.2.5" }, "funding": [ { @@ -3403,7 +3403,7 @@ "type": "tidelift" } ], - "time": "2022-12-14T16:11:27+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "theseer/tokenizer", From 126a2730f3ae6cd5444cbe0af2b3f4cec60d0d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 18:53:27 +0300 Subject: [PATCH 038/100] add support 8.0 --- README.md | 3 - composer.json | 2 +- composer.lock | 222 +++++++++++++++++++-------------- src/GenericProperty.php | 16 +-- tests/Integration/EnumTest.php | 5 + 5 files changed, 144 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index b410089..bfdadfd 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,6 @@ composer require yzen.dev/plain-to-class > Note: The current version of the package supports only PHP 8.1 +. > For PHP version 7.4, you can read the documentation in [version v0.*](https://github.com/yzen-dev/plain-to-class/tree/php-7.4). -> -> For PHP version 8.0, you can read the documentation in [version v1.*](https://github.com/yzen-dev/plain-to-class/tree/php-8.0). - ## :scroll: **Usage** Common use case: diff --git a/composer.json b/composer.json index dc76782..7250d14 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ } }, "require": { - "php": "^8.1" + "php": "^8.0" }, "require-dev": { "mockery/mockery": "1.5.1", diff --git a/composer.lock b/composer.lock index b8f87f9..f0e721d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ab0db88bfc1e5def7c1a62dc7287b31c", + "content-hash": "35f5dd89fee4f0894a145a868a90ffdc", "packages": [], "packages-dev": [ { @@ -83,32 +83,75 @@ }, "time": "2022-12-19T18:17:20+00:00" }, + { + "name": "doctrine/deprecations", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "shasum": "" + }, + "require": { + "php": "^7.1|^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + }, + "time": "2022-05-02T15:47:09+00:00" + }, { "name": "doctrine/instantiator", - "version": "2.0.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -135,7 +178,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -151,31 +194,32 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "doctrine/lexer", - "version": "3.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "84a527db05647743d50373e0ec53a152f2cde568" + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", - "reference": "84a527db05647743d50373e0ec53a152f2cde568", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", "shasum": "" }, "require": { - "php": "^8.1" + "doctrine/deprecations": "^1.0", + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^9.5", + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.0" + "vimeo/psalm": "^4.11 || ^5.0" }, "type": "library", "autoload": { @@ -212,7 +256,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.0" + "source": "https://github.com/doctrine/lexer/tree/2.1.0" }, "funding": [ { @@ -228,7 +272,7 @@ "type": "tidelift" } ], - "time": "2022-12-15T16:57:16+00:00" + "time": "2022-12-14T08:49:07+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -2488,21 +2532,20 @@ }, { "name": "symfony/console", - "version": "v6.2.5", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3e294254f2191762c1d137aed4b94e966965e985" + "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3e294254f2191762c1d137aed4b94e966965e985", - "reference": "3e294254f2191762c1d137aed4b94e966965e985", + "url": "https://api.github.com/repos/symfony/console/zipball/c3ebc83d031b71c39da318ca8b7a07ecc67507ed", + "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.0.2", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^1.1|^2|^3", "symfony/string": "^5.4|^6.0" @@ -2564,7 +2607,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.5" + "source": "https://github.com/symfony/console/tree/v6.0.19" }, "funding": [ { @@ -2580,29 +2623,29 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2023-01-01T08:36:10+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.0", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", - "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.0-dev" }, "thanks": { "name": "symfony/contracts", @@ -2631,7 +2674,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" }, "funding": [ { @@ -2647,24 +2690,24 @@ "type": "tidelift" } ], - "time": "2022-11-25T10:21:52+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/filesystem", - "version": "v6.2.5", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593" + "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/e59e8a4006afd7f5654786a83b4fcb8da98f4593", - "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/3d49eec03fda1f0fc19b7349fbbe55ebc1004214", + "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, @@ -2694,7 +2737,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.5" + "source": "https://github.com/symfony/filesystem/tree/v6.0.19" }, "funding": [ { @@ -2710,27 +2753,24 @@ "type": "tidelift" } ], - "time": "2023-01-20T17:45:48+00:00" + "time": "2023-01-20T17:44:14+00:00" }, { "name": "symfony/finder", - "version": "v6.2.5", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c" + "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/c90dc446976a612e3312a97a6ec0069ab0c2099c", - "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c", + "url": "https://api.github.com/repos/symfony/finder/zipball/5cc9cac6586fc0c28cd173780ca696e419fefa11", + "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "symfony/filesystem": "^6.0" + "php": ">=8.0.2" }, "type": "library", "autoload": { @@ -2758,7 +2798,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.2.5" + "source": "https://github.com/symfony/finder/tree/v6.0.19" }, "funding": [ { @@ -2774,24 +2814,24 @@ "type": "tidelift" } ], - "time": "2023-01-20T17:45:48+00:00" + "time": "2023-01-20T17:44:14+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.2.5", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "e8324d44f5af99ec2ccec849934a242f64458f86" + "reference": "6a180d1c45e0d9797470ca9eb46215692de00fa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/e8324d44f5af99ec2ccec849934a242f64458f86", - "reference": "e8324d44f5af99ec2ccec849934a242f64458f86", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/6a180d1c45e0d9797470ca9eb46215692de00fa3", + "reference": "6a180d1c45e0d9797470ca9eb46215692de00fa3", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", @@ -2825,7 +2865,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.2.5" + "source": "https://github.com/symfony/options-resolver/tree/v6.0.19" }, "funding": [ { @@ -2841,7 +2881,7 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2023-01-01T08:36:10+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3175,20 +3215,20 @@ }, { "name": "symfony/process", - "version": "v6.2.5", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7" + "reference": "2114fd60f26a296cc403a7939ab91478475a33d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", - "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", + "url": "https://api.github.com/repos/symfony/process/zipball/2114fd60f26a296cc403a7939ab91478475a33d4", + "reference": "2114fd60f26a296cc403a7939ab91478475a33d4", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.0.2" }, "type": "library", "autoload": { @@ -3216,7 +3256,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.2.5" + "source": "https://github.com/symfony/process/tree/v6.0.19" }, "funding": [ { @@ -3232,24 +3272,24 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2023-01-01T08:36:10+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.2.0", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75" + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75", - "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "psr/container": "^2.0" }, "conflict": { @@ -3261,7 +3301,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.0-dev" }, "thanks": { "name": "symfony/contracts", @@ -3271,10 +3311,7 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3301,7 +3338,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.2.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" }, "funding": [ { @@ -3317,24 +3354,24 @@ "type": "tidelift" } ], - "time": "2022-11-25T10:21:52+00:00" + "time": "2022-05-30T19:17:58+00:00" }, { "name": "symfony/string", - "version": "v6.2.5", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0" + "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", - "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", + "url": "https://api.github.com/repos/symfony/string/zipball/d9e72497367c23e08bf94176d2be45b00a9d232a", + "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -3346,7 +3383,6 @@ "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", "symfony/translation-contracts": "^2.0|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, @@ -3387,7 +3423,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.5" + "source": "https://github.com/symfony/string/tree/v6.0.19" }, "funding": [ { @@ -3403,7 +3439,7 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:38:09+00:00" + "time": "2023-01-01T08:36:10+00:00" }, { "name": "theseer/tokenizer", @@ -3511,7 +3547,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.1" + "php": "^8.0" }, "platform-dev": [], "plugin-api-version": "2.3.0" diff --git a/src/GenericProperty.php b/src/GenericProperty.php index b500b32..d7192f5 100644 --- a/src/GenericProperty.php +++ b/src/GenericProperty.php @@ -11,7 +11,6 @@ use function sizeof; use function in_array; -use function enum_exists; use function array_intersect; /** @@ -22,22 +21,22 @@ final class GenericProperty { /** @var ReflectionProperty */ - readonly public ReflectionProperty $property; + public ReflectionProperty $property; /** @var null|ReflectionType|ReflectionUnionType|ReflectionNamedType */ - readonly public ?ReflectionType $type; + public ?ReflectionType $type; /** @var array|string[] */ - readonly public array $types; + public array $types; /** @var class-string|string $propertyClass */ - readonly public string $name; + public string $name; /** @var string */ - readonly public string $class; + public string $class; /** @var bool */ - readonly public bool $isScalar; + public bool $isScalar; /** @var array>> */ private static $attributeTypesCache = []; @@ -63,6 +62,9 @@ public function __construct(ReflectionProperty $property) */ public function isEnum(): bool { + if (!function_exists('enum_exists')) { + return false; + } if ($this->type instanceof ReflectionNamedType) { return enum_exists($this->type->getName()); } diff --git a/tests/Integration/EnumTest.php b/tests/Integration/EnumTest.php index d7679f5..71820c0 100644 --- a/tests/Integration/EnumTest.php +++ b/tests/Integration/EnumTest.php @@ -23,6 +23,11 @@ class EnumTest extends TestCase */ public function testEmptyWritingStyle(): void { + if (PHP_MAJOR_VERSION < 8.1) { + $this->markTestSkipped('Php version mismatch'); + return; + } + $data = [ 'colorEnum' => 'Red', 'colorScalarEnum' => 'R', From 5c7b0dcdf5bd6dbe269b84fc63a343dac13198cb Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Thu, 2 Feb 2023 18:56:22 +0300 Subject: [PATCH 039/100] Update tests.yml --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aa1d53a..35b4bc6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.1"] + php: ["8.1", "8.0"] dependency-version: [prefer-lowest, prefer-stable] name: P${{ matrix.php }} - ${{ matrix.dependency-version }} From d043a3280a8b06b229bbb3735287d9925ea6a19c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 19:00:30 +0300 Subject: [PATCH 040/100] fix: test --- tests/Integration/EnumTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Integration/EnumTest.php b/tests/Integration/EnumTest.php index 71820c0..0b5c0c0 100644 --- a/tests/Integration/EnumTest.php +++ b/tests/Integration/EnumTest.php @@ -14,6 +14,7 @@ /** * Class EnumTest + * * @package Tests */ class EnumTest extends TestCase @@ -23,11 +24,11 @@ class EnumTest extends TestCase */ public function testEmptyWritingStyle(): void { - if (PHP_MAJOR_VERSION < 8.1) { + if (PHP_MAJOR_VERSION < 8 || (PHP_MAJOR_VERSION === 8 && PHP_MINOR_VERSION < 1)) { $this->markTestSkipped('Php version mismatch'); return; } - + $data = [ 'colorEnum' => 'Red', 'colorScalarEnum' => 'R', From 6cfe6eff488d916af4750e5b1fec73a15ef76bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 19:01:41 +0300 Subject: [PATCH 041/100] fix array --- src/GenericInstance.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 688d20b..e421f00 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -104,7 +104,7 @@ private function castAttribute(GenericProperty $property, $value) return $value; } - if ($property->isArray() && is_array($value)) { + if ($property->isArray()) { return $this->castArray($property, $value); } @@ -124,12 +124,12 @@ private function castAttribute(GenericProperty $property, $value) /** * @param GenericProperty $property - * @param array $value + * @param array|mixed $value * - * @return array + * @return array|mixed * @throws ClassNotFoundException */ - private function castArray(GenericProperty $property, array $value): array + private function castArray(GenericProperty $property, $value): mixed { $arrayTypeAttr = $property->getAttribute(ConvertArray::class); if ($arrayTypeAttr !== null) { From ea681293d14c7831d4add84405d20a4a0f5281e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 19:46:13 +0300 Subject: [PATCH 042/100] upd: ru docs --- README.RU.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.RU.md b/README.RU.md index 59253d7..38d25e1 100644 --- a/README.RU.md +++ b/README.RU.md @@ -12,11 +12,11 @@ Эта библиотека позволит вам легко преобразовать любой набор данных в нужный вам объект. От вас не требуется менять структуру классов, наследовать их от внешних модулей и т.д. Никаких танцев с бубнами - только данные и нужный класс. -Хорошей практикой считается написание кода независимого от сторонних пакетов и фреймворков. Код разбивается на сервисы, доменные зоны, различные слои и т.д. Для передачи данных между слоями как правило используются шаблон DataTransfer Object (DTO). DTO - это объект, который используется для инкапсуляции данных и отправки их из одной подсистемы приложения в другую. +Хорошей практикой считается написание кода независимого от сторонних пакетов и фреймворков. Код разбивается на сервисы, доменные зоны, различные слои и т.д. Для передачи данных между слоями, как правило, используется шаблон DataTransfer Object (DTO). DTO - это объект, который необходим для инкапсуляции данных и отправки их из одной подсистемы приложения в другую. -Тем самым сервисы/методы работают с конкретным объектом и данными необходимым для него. При этом не важно откуда эти данные были получены, это может быть http запрос, БД, файл и т.д. +Таким образом, сервисы/методы работают с конкретным объектом и данными необходимым для него. При этом неважно, откуда эти данные были получены - это может быть http запрос, БД, файл и т.д. -Соответственно при каждом вызове сервиса нам необходимо инициализировать данное DTO. Но сопоставлять каждый раз данные в ручную это не эффективно, и сказывается на читабельности кода, особенно если объект сложный. +Соответственно, при каждом вызове сервиса нам необходимо инициализировать данное DTO. Но сопоставлять каждый раз данные вручную - неэффективно, и сказывается на читабельности кода, особенно если объект сложный. Здесь на помощь приходит данный пакет, который берет на себя всю работу с мапингом и инициализацией необходимой DTO. @@ -116,12 +116,11 @@ object(PurchaseDTO) ### :scroll: **Коллекция** -Если у вас есть массив объектов определенного класса, то вы должны указать для него атрибут ConvertArray, передав его в -какой класс вам нужно привести элементы. +Если у вас есть массив объектов определенного класса, то вы должны указать для него атрибут ConvertArray, передав ему в какой класс вам нужно привести элементы. -Также можно указать класс в PHP DOC, но тогда вам нужно написать полный путь к этомуклассу `array <\DTO\ProductDTO>`. +Также можно указать класс в PHP DOC, но тогда вам нужно написать полный путь к этому классу `array <\DTO\ProductDTO>`. Это делается для того, чтобы точно знать, какой экземпляр нужно создать. Поскольку Reflection не предоставляет готовых -функций для получения файла `use`. Помимо `use`, вы можете указать псевдоним, и его будет сложнее отследить. Пример: +функций для получения файла `use`. Помимо `use`, вы можете указать псевдоним и его будет сложнее отследить. Пример: ```php @@ -148,7 +147,7 @@ $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); #### :scroll: **Анонимная коллекция** -В случае, если вам нужно преобразовать массив данных в массив объектов класса, вы можете реализовать это с помощью +В случае если вам нужно преобразовать массив данных в массив объектов класса, вы можете реализовать это с помощью метода `transformCollection`. ```php @@ -159,7 +158,7 @@ $data = [ $products = ClassTransformer::transformCollection(ProductDTO::class, $data); ``` -В результате этого выполнения вы получите массив объектов ProductDTO +В результате этого вы получите массив объектов ProductDTO ```php array(2) { From 81bb8088f8fc645578b50bf56ddf25672fcbbcd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 19:53:56 +0300 Subject: [PATCH 043/100] fix doc --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bfdadfd..dbc0a46 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ This is where this package comes to the rescue, which takes care of all the work [Documentation](https://plain-to-class.readthedocs.io) -## :scroll: **Установка** +## :scroll: **Installation** The package can be installed via composer: @@ -34,6 +34,7 @@ composer require yzen.dev/plain-to-class > Note: The current version of the package supports only PHP 8.1 +. > For PHP version 7.4, you can read the documentation in [version v0.*](https://github.com/yzen-dev/plain-to-class/tree/php-7.4). + ## :scroll: **Usage** Common use case: @@ -112,7 +113,7 @@ object(PurchaseDTO) public float 'cost' => float 10012.23 ``` -### :scroll: **Коллекция** +### :scroll: **Collection** If you have an array of objects of a certain class, then you must specify the ConvertArray attribute for it, passing it to which class you need to bring the elements. From 09b1d81add6e3b46e3563c35daae3fd10efe5d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 20:03:00 +0300 Subject: [PATCH 044/100] add intro --- README.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index dbc0a46..1d9bd75 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,18 @@ This is where this package comes to the rescue, which takes care of all the work [Documentation](https://plain-to-class.readthedocs.io) -## :scroll: **Installation** +- [Installation](#Installation) +- [Usage](#Usage) + - [Collection](#Collection) + - [Anonymous array](#anonymous-array) + - [Writing style](#writing-style) + - [Alias](#alias) + - [Custom setter](#custom-setter) + - [After Transform](#after-transform) + - [Custom transform](#custom-transform) + - [Comparison](#Comparison) + +## **Installation** The package can be installed via composer: @@ -35,12 +46,12 @@ composer require yzen.dev/plain-to-class > For PHP version 7.4, you can read the documentation in [version v0.*](https://github.com/yzen-dev/plain-to-class/tree/php-7.4). -## :scroll: **Usage** +## **Usage** Common use case: -### :scroll: **Base** +### **Base** ``` namespace DTO; @@ -113,7 +124,7 @@ object(PurchaseDTO) public float 'cost' => float 10012.23 ``` -### :scroll: **Collection** +### **Collection** If you have an array of objects of a certain class, then you must specify the ConvertArray attribute for it, passing it to which class you need to bring the elements. @@ -145,7 +156,7 @@ $data = [ $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); ``` -#### :scroll: **Anonymous array** +### **Anonymous array** In case you need to convert an array of data into an array of class objects, you can implement this using the `transformCollection` method. @@ -227,7 +238,7 @@ object(PurchaseDTO) (2) { } ``` -### :scroll: **Writing style** +### **Writing style** A constant problem with the style of writing, for example, in the database it is snake_case, and in the camelCase code. And they constantly need to be transformed somehow. The package takes care of this, you just need to specify the WritingStyle attribute on the property: @@ -259,7 +270,7 @@ object(WritingStyleSnakeCaseDTO) (2) { } ``` -### :scroll: **Alias** +### **Alias** Various possible aliases can be set for the property, which will also be searched in the data source. This can be useful if the DTO is generated from different data sources. @@ -275,7 +286,7 @@ class WithAliasDTO } ``` -### :scroll: **Custom setter** +### **Custom setter** Если поле требует дополнительной обработки при его инициализации, вы можете мутировать его сеттер. Для это создайте в классе метод следующего формата - `set{$name}Attribute`. Пример: @@ -292,7 +303,7 @@ class UserDTO } ``` -### :scroll: **After Transform** +### **After Transform** Inside the class, you can create the `afterTransform` method, which will be called immediately after the conversion is completed. In it, we can describe our additional verification or transformation logic by already working with the state of the object. @@ -310,7 +321,7 @@ class UserDTO } ``` -### :scroll: **Custom transform** +### **Custom transform** If you need to completely transform yourself, then you can create a transform method in the class. In this case, no library processing is called, all the responsibility of the conversion passes to your class. From 6d5796b61974dc362bca0ff0f8d47842d7cb3628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 2 Feb 2023 20:17:01 +0300 Subject: [PATCH 045/100] fix lang --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d9bd75..118ee2c 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,8 @@ class WithAliasDTO ### **Custom setter** -Если поле требует дополнительной обработки при его инициализации, вы можете мутировать его сеттер. Для это создайте в классе метод следующего формата - `set{$name}Attribute`. Пример: +If a field requires additional processing during its initialization, you can mutator. To define a mutator, define a set{Attribute}Attribute method on your model where {Attribute} is cased name of the property you wish to access. +This mutator will be automatically called when we attempt to set the value of the real_address attribute on the model: ```php class UserDTO From d9d6e4d0925f231be2b156332abdcc2db9369295 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Fri, 3 Feb 2023 09:35:28 +0300 Subject: [PATCH 046/100] upd shields --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 118ee2c..065cd38 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ ## ClassTransformer - ![Packagist Version](https://img.shields.io/packagist/v/yzen.dev/plain-to-class?color=blue&label=version) -![GitHub Workflow Status](https://img.shields.io/github/workflow/status/yzen-dev/plain-to-class/Run%20tests?label=tests&logo=github) +![GitHub Workflow Status (with branch)](https://img.shields.io/github/actions/workflow/status/yzen-dev/plain-to-class/tests.yml?branch=master) [![Coverage](https://codecov.io/gh/yzen-dev/plain-to-class/branch/master/graph/badge.svg?token=QAO8STLPMI)](https://codecov.io/gh/yzen-dev/plain-to-class) ![License](https://img.shields.io/github/license/yzen-dev/plain-to-class) +![readthedocs](https://img.shields.io/readthedocs/plain-to-class) ![Packagist Downloads](https://img.shields.io/packagist/dm/yzen.dev/plain-to-class) ![Packagist Downloads](https://img.shields.io/packagist/dt/yzen.dev/plain-to-class) From c33716666bd4d8ff1fc387434dc94a52383f845a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Fri, 3 Feb 2023 09:42:34 +0300 Subject: [PATCH 047/100] upd readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 065cd38..77c75c9 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,7 @@ class WithAliasDTO ### **Custom setter** -If a field requires additional processing during its initialization, you can mutator. To define a mutator, define a set{Attribute}Attribute method on your model where {Attribute} is cased name of the property you wish to access. +If a field requires additional processing during its initialization, you can mutator. To define a mutator, define a set{Attribute}Attribute method on your class where {Attribute} is cased name of the property you wish to access. This mutator will be automatically called when we attempt to set the value of the real_address attribute on the model: ```php From 553ae11768f35c7b9ccbde8703379d3c40394720 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 6 Feb 2023 09:46:30 +0300 Subject: [PATCH 048/100] fix code block --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 77c75c9..927245a 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Common use case: ### **Base** -``` +```php namespace DTO; class CreateUserDTO From 7d175229d590e6556b2966ce04b8222b1ad64fc1 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Tue, 14 Feb 2023 10:58:25 +0300 Subject: [PATCH 049/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 927245a..e8f0661 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ The package can be installed via composer: composer require yzen.dev/plain-to-class ``` -> Note: The current version of the package supports only PHP 8.1 +. +> Note: The current version of the package supports only PHP 8.0 +. > For PHP version 7.4, you can read the documentation in [version v0.*](https://github.com/yzen-dev/plain-to-class/tree/php-7.4). From a94162005a3df91d599b29dad3b701cb3145ec9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 10 Apr 2023 14:49:19 +0300 Subject: [PATCH 050/100] Upd: separation of entities --- src/ClassTransformerConfig.php | 16 +++ src/GenericInstance.php | 145 ++-------------------- src/GenericProperty.php | 101 +++++++++++++++ src/Reflection/CacheReflectionClass.php | 69 ++++++++++ src/Reflection/ReflectionClass.php | 10 ++ src/Reflection/RuntimeReflectionClass.php | 69 ++++++++++ src/TransformBuilder.php | 10 +- tests/Benchmark/CheckBench.php | 79 ------------ tests/Benchmark/FullCheckBench.php | 17 ++- tests/Benchmark/LiteCheckBench.php | 51 -------- tests/Units/GenericInstanceTest.php | 31 ----- 11 files changed, 299 insertions(+), 299 deletions(-) create mode 100644 src/ClassTransformerConfig.php create mode 100644 src/Reflection/CacheReflectionClass.php create mode 100644 src/Reflection/ReflectionClass.php create mode 100644 src/Reflection/RuntimeReflectionClass.php delete mode 100644 tests/Benchmark/CheckBench.php delete mode 100644 tests/Benchmark/LiteCheckBench.php delete mode 100644 tests/Units/GenericInstanceTest.php diff --git a/src/ClassTransformerConfig.php b/src/ClassTransformerConfig.php new file mode 100644 index 0000000..04c3652 --- /dev/null +++ b/src/ClassTransformerConfig.php @@ -0,0 +1,16 @@ + $class */ - private string $class; + /** @var ReflectionClass $class */ + private ReflectionClass $class; /** @var ArgumentsResource $argumentsResource */ private ArgumentsResource $argumentsResource; @@ -31,38 +28,18 @@ final class GenericInstance /** @var T $genericInstance */ private $genericInstance; - /** - * @var array - */ - private static $propertiesTypesCache = []; - /** - * @param class-string $class - * - * @throws ClassNotFoundException + * @param ReflectionClass $class + * @param ArgumentsResource $argumentsResource */ - public function __construct(string $class, ArgumentsResource $argumentsResource) + public function __construct(ReflectionClass $class, ArgumentsResource $argumentsResource) { - new ClassExistsValidator($class); - $this->class = $class; + $this->argumentsResource = $argumentsResource; - $this->genericInstance = new $this->class(); - } - - /** - * @return \ReflectionProperty[] - */ - public function getProperties(): array - { - if (isset(static::$propertiesTypesCache[$this->class])) { - return static::$propertiesTypesCache[$this->class]; - } - - $refInstance = new ReflectionClass($this->class); - return static::$propertiesTypesCache[$this->class] = $refInstance->getProperties(); + $this->genericInstance = new ($class->getClass()); } /** @@ -71,122 +48,24 @@ public function getProperties(): array */ public function transform(): mixed { - $properties = $this->getProperties(); - foreach ($properties as $item) { - $property = new GenericProperty($item); + $properties = $this->class->getProperties(); + foreach ($properties as $property) { try { $value = $this->argumentsResource->getValue($property); } catch (ValueNotFoundException) { continue; } - if ($this->hasSetMutator($property->name)) { + if ($property->hasSetMutator()) { $this->genericInstance->{TransformUtils::mutationSetterToCamelCase($property->name)}($value); continue; } - $this->genericInstance->{$property->name} = $this->castAttribute($property, $value); + $this->genericInstance->{$property->name} = $property->castAttribute($value); } return $this->genericInstance; } - /** - * @param GenericProperty $property - * @param mixed $value - * - * @return mixed - * @throws ClassNotFoundException - */ - private function castAttribute(GenericProperty $property, $value) - { - if ($property->isScalar || $property->notTransform()) { - return $value; - } - - if ($property->isArray()) { - return $this->castArray($property, $value); - } - - if ($property->isEnum() && (is_string($value) || is_int($value))) { - return $this->castEnum($property, $value); - } - - if ($property->type instanceof ReflectionNamedType) { - /** @var class-string $propertyClass */ - $propertyClass = $property->type->getName(); - - /** @phpstan-ignore-next-line */ - return (new TransformBuilder($propertyClass, $value))->build(); - } - return $value; - } - - /** - * @param GenericProperty $property - * @param array|mixed $value - * - * @return array|mixed - * @throws ClassNotFoundException - */ - private function castArray(GenericProperty $property, $value): mixed - { - $arrayTypeAttr = $property->getAttribute(ConvertArray::class); - if ($arrayTypeAttr !== null) { - $arrayType = $arrayTypeAttr->getArguments()[0]; - } else { - $arrayType = TransformUtils::getClassFromPhpDoc($property->getDocComment()); - } - if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { - return $value; - } - - $array = []; - if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { - foreach ($value as $el) { - $array[] = (new TransformBuilder($arrayType, $el))->build(); - } - return $array; - } - - foreach ($value as $el) { - $array[] = match ($arrayType) { - 'string' => (string)$el, - 'int' => (int)$el, - 'float' => (float)$el, - 'bool' => (bool)$el, - default => $el - }; - } - return $array; - } - - /** - * @param GenericProperty $property - * @param int|string $value - * - * @return mixed - */ - private function castEnum(GenericProperty $property, int|string $value) - { - /** @phpstan-ignore-next-line */ - $propertyClass = $property->type->getName(); - if (method_exists($propertyClass, 'from')) { - /** @var \BackedEnum $propertyClass */ - return $propertyClass::from($value); - } - - return constant($propertyClass . '::' . $value); - } - - /** - * @param string $key - * - * @return bool - */ - public function hasSetMutator(string $key): bool - { - return method_exists($this->class, TransformUtils::mutationSetterToCamelCase($key)); - } } diff --git a/src/GenericProperty.php b/src/GenericProperty.php index d7192f5..5dd7263 100644 --- a/src/GenericProperty.php +++ b/src/GenericProperty.php @@ -2,6 +2,8 @@ namespace ClassTransformer; +use ClassTransformer\Attributes\ConvertArray; +use ClassTransformer\Exceptions\ClassNotFoundException; use ReflectionType; use ReflectionProperty; use ReflectionAttribute; @@ -145,4 +147,103 @@ public function getAttribute(?string $name = null): ?ReflectionAttribute } return null; } + + /** + * @param GenericProperty $property + * @param mixed $value + * + * @return mixed + * @throws ClassNotFoundException + */ + public function castAttribute($value) + { + if ($this->isScalar || $this->notTransform()) { + return $value; + } + + if ($this->isArray()) { + return $this->castArray($value); + } + + if ($this->isEnum() && (is_string($value) || is_int($value))) { + return $this->castEnum($value); + } + + if ($this->type instanceof ReflectionNamedType) { + $propertyClass = $this->type->getName(); + + /** @phpstan-ignore-next-line */ + return (new TransformBuilder($propertyClass, $value))->build(); + } + return $value; + } + + + /** + * @param GenericProperty $property + * @param array|mixed $value + * + * @return array|mixed + * @throws ClassNotFoundException + */ + private function castArray($value): mixed + { + $arrayTypeAttr = $this->getAttribute(ConvertArray::class); + if ($arrayTypeAttr !== null) { + $arrayType = $arrayTypeAttr->getArguments()[0]; + } else { + $arrayType = TransformUtils::getClassFromPhpDoc($this->getDocComment()); + } + + if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { + return $value; + } + + $array = []; + if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { + foreach ($value as $el) { + $array[] = (new TransformBuilder($arrayType, $el))->build(); + } + return $array; + } + + foreach ($value as $el) { + $array[] = match ($arrayType) { + 'string' => (string)$el, + 'int' => (int)$el, + 'float' => (float)$el, + 'bool' => (bool)$el, + default => $el + }; + } + return $array; + } + + /** + * @param GenericProperty $property + * @param int|string $value + * + * @return mixed + */ + private function castEnum(int|string $value) + { + /** @phpstan-ignore-next-line */ + $propertyClass = $this->type->getName(); + if (method_exists($propertyClass, 'from')) { + /** @var \BackedEnum $propertyClass */ + return $propertyClass::from($value); + } + + return constant($propertyClass . '::' . $value); + } + + /** + * @param string $key + * + * @return bool + */ + public function hasSetMutator(): bool + { + return method_exists($this->class, TransformUtils::mutationSetterToCamelCase($this->name)); + } } diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php new file mode 100644 index 0000000..0622723 --- /dev/null +++ b/src/Reflection/CacheReflectionClass.php @@ -0,0 +1,69 @@ + + */ +final class CacheReflectionClass implements ReflectionClass +{ + /** @var class-string $class */ + private string $class; + + /** + * @var array + */ + private static $propertiesTypesCache = []; + + + /** + * @param class-string $class + * + * @throws ClassNotFoundException + */ + public function __construct(string $class) + { + new ClassExistsValidator($class); + + $this->class = $class; + } + + /** + * @return \ReflectionProperty[] + * @throws \ReflectionException + */ + public function getProperties(): array + { + if (isset(static::$propertiesTypesCache[$this->class])) { + return static::$propertiesTypesCache[$this->class]; + } + + $refInstance = new PhpReflectionClass($this->class); + + $properties = $refInstance->getProperties(); + $result = []; + foreach ($properties as $item) { + $result [] = new GenericProperty($item); + } + + return static::$propertiesTypesCache[$this->class] = $result; + } + + /** + * @return string + */ + public function getClass(): string + { + return $this->class; + } +} diff --git a/src/Reflection/ReflectionClass.php b/src/Reflection/ReflectionClass.php new file mode 100644 index 0000000..dcb2ad2 --- /dev/null +++ b/src/Reflection/ReflectionClass.php @@ -0,0 +1,10 @@ + + */ +final class RuntimeReflectionClass implements ReflectionClass +{ + /** @var class-string $class */ + private string $class; + + /** + * @var array + */ + private static $propertiesTypesCache = []; + + + /** + * @param class-string $class + * + * @throws ClassNotFoundException + */ + public function __construct(string $class) + { + new ClassExistsValidator($class); + + $this->class = $class; + } + + /** + * @return \ReflectionProperty[] + * @throws \ReflectionException + */ + public function getProperties(): array + { + if (isset(static::$propertiesTypesCache[$this->class])) { + return static::$propertiesTypesCache[$this->class]; + } + + $refInstance = new PhpReflectionClass($this->class); + + $properties = $refInstance->getProperties(); + $result = []; + foreach ($properties as $item) { + $result [] = new GenericProperty($item); + } + + return static::$propertiesTypesCache[$this->class] = $result; + } + + /** + * @return string + */ + public function getClass(): string + { + return $this->class; + } +} diff --git a/src/TransformBuilder.php b/src/TransformBuilder.php index d764306..a1e9ccd 100644 --- a/src/TransformBuilder.php +++ b/src/TransformBuilder.php @@ -2,6 +2,8 @@ namespace ClassTransformer; +use ClassTransformer\Reflection\CacheReflectionClass; +use ClassTransformer\Reflection\RuntimeReflectionClass; use ClassTransformer\Validators\ClassExistsValidator; use ClassTransformer\Exceptions\ClassNotFoundException; @@ -46,7 +48,12 @@ public function build() $instance = new $this->class(); $instance->transform(...$this->args); } else { - $generic = new GenericInstance($this->class, new ArgumentsResource(...$this->args)); + if (ClassTransformerConfig::$cache) { + $reflection = new CacheReflectionClass($this->class); + } else { + $reflection = new RuntimeReflectionClass($this->class); + } + $generic = new GenericInstance($reflection, new ArgumentsResource(...$this->args)); /** @var T $instance */ $instance = $generic->transform(); } @@ -57,4 +64,5 @@ public function build() return $instance; } + } diff --git a/tests/Benchmark/CheckBench.php b/tests/Benchmark/CheckBench.php deleted file mode 100644 index 42086c9..0000000 --- a/tests/Benchmark/CheckBench.php +++ /dev/null @@ -1,79 +0,0 @@ -getPurcheseObject(); - - $productOne = new ProductDTO(); - $productOne->id = $data['products'][0]['id']; - $productOne->name = $data['products'][0]['name']; - $productOne->price = $data['products'][0]['price']; - - $productTwo = new ProductDTO(); - $productTwo->id = $data['products'][0]['id']; - $productTwo->name = $data['products'][0]['name']; - $productTwo->price = $data['products'][0]['price']; - - $user = new UserDTO(); - $user->id = $data['user']['id']; - $user->email = $data['user']['email']; - $user->balance = $data['user']['balance']; - - $data = new PurchaseDTO(); - $data->products = [$productOne, $productTwo]; - $data->user = $user; - } - - /** - * @Revs(5000) - */ - public function benchTransformReflection(): void - { - $data = $this->getPurcheseObject(); - ClassTransformer::transform(PurchaseDTO::class, $data); - } - - public function getPurcheseObject(): array - { - return [ - 'products' => [ - [ - 'id' => 1, - 'name' => 'phone', - 'price' => 43.03, - ], - [ - 'id' => 2, - 'name' => 'bread', - 'price' => 10.56, - ] - ], - 'user' => [ - 'id' => 1, - 'email' => 'fake@mail.com', - 'balance' => 10012.23, - ] - ]; - } -} diff --git a/tests/Benchmark/FullCheckBench.php b/tests/Benchmark/FullCheckBench.php index c2b463f..b3b7efe 100644 --- a/tests/Benchmark/FullCheckBench.php +++ b/tests/Benchmark/FullCheckBench.php @@ -3,6 +3,7 @@ namespace Tests\Benchmark; use ClassTransformer\ClassTransformer; +use ClassTransformer\ClassTransformerConfig; use PHPUnit\Framework\TestCase; use Tests\Benchmark\DTO\ProductDTO; use Tests\Benchmark\DTO\PurchaseDTO; @@ -20,8 +21,7 @@ class FullCheckBench extends TestCase { /** - * @Revs(5000) - * @Iterations(5) + * @Revs(10000) */ public function benchBaseReflection(): void { @@ -47,14 +47,23 @@ public function benchBaseReflection(): void } /** - * @Revs(5000) - * @Iterations(5) + * @Revs(10000) */ public function benchTransformReflection(): void { $data = $this->getPurcheseObject(); ClassTransformer::transform(PurchaseDTO::class, $data); } + + /** + * @Revs(10000) + */ + public function benchTransformCacheReflection(): void + { + $data = $this->getPurcheseObject(); + ClassTransformerConfig::$cache = true; + ClassTransformer::transform(PurchaseDTO::class, $data); + } public function getPurcheseObject(): array { diff --git a/tests/Benchmark/LiteCheckBench.php b/tests/Benchmark/LiteCheckBench.php deleted file mode 100644 index 8aa16c6..0000000 --- a/tests/Benchmark/LiteCheckBench.php +++ /dev/null @@ -1,51 +0,0 @@ -getPurcheseObject(); - - $productOne = new ProductDTO(); - $productOne->id = $data['id']; - $productOne->name = $data['name']; - $productOne->price = $data['price']; - } - - /** - * @Revs(5000) - */ - public function benchTransformReflection(): void - { - $data = $this->getPurcheseObject(); - ClassTransformer::transform(ProductDTO::class, $data); - } - - public function getPurcheseObject(): array - { - return [ - 'id' => 1, - 'name' => 'phone', - 'price' => 43.03, - ]; - } -} diff --git a/tests/Units/GenericInstanceTest.php b/tests/Units/GenericInstanceTest.php deleted file mode 100644 index 3650b57..0000000 --- a/tests/Units/GenericInstanceTest.php +++ /dev/null @@ -1,31 +0,0 @@ -expectException(ClassNotFoundException::class); - new GenericInstance('Test\Fake\Class', new ArgumentsResource()); - } -} From e40415279cf2cc695e7fff34a29dd16d08158d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 10 Apr 2023 15:59:29 +0300 Subject: [PATCH 051/100] Issue: cache module --- src/ArgumentsResource.php | 19 +- src/ClassTransformer.php | 1 + src/{ => Contracts}/ClassTransformable.php | 2 +- src/Contracts/ReflectionClass.php | 20 +++ src/Contracts/ReflectionProperty.php | 42 +++++ src/GenericInstance.php | 16 +- src/Reflection/CacheReflectionClass.php | 5 +- src/Reflection/CacheReflectionProperty.php | 168 ++++++++++++++++++ src/Reflection/ReflectionClass.php | 10 -- src/Reflection/RuntimeReflectionClass.php | 5 +- .../RuntimeReflectionProperty.php} | 120 +++---------- src/TransformBuilder.php | 1 + src/ValueCasting.php | 125 +++++++++++++ tests/Units/ArgumentsResourceTest.php | 17 +- 14 files changed, 415 insertions(+), 136 deletions(-) rename src/{ => Contracts}/ClassTransformable.php (82%) create mode 100644 src/Contracts/ReflectionClass.php create mode 100644 src/Contracts/ReflectionProperty.php create mode 100644 src/Reflection/CacheReflectionProperty.php delete mode 100644 src/Reflection/ReflectionClass.php rename src/{GenericProperty.php => Reflection/RuntimeReflectionProperty.php} (60%) create mode 100644 src/ValueCasting.php diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index 6b894e8..08519fb 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -4,13 +4,14 @@ use ClassTransformer\Attributes\FieldAlias; use ClassTransformer\Attributes\WritingStyle; +use ClassTransformer\Contracts\ReflectionProperty; use ClassTransformer\Exceptions\ValueNotFoundException; -use function sizeof; -use function is_object; -use function func_get_args; +use function is_string; use function array_intersect; use function array_key_exists; +use function func_get_args; +use function sizeof; /** * Class GenericProperty @@ -36,15 +37,15 @@ public function __construct(...$args) } /** - * @param GenericProperty $genericProperty + * @param ReflectionProperty $genericProperty * * @return mixed|object|array|null * @throws ValueNotFoundException */ - public function getValue(GenericProperty $genericProperty): mixed + public function getValue(ReflectionProperty $genericProperty): mixed { - if (array_key_exists($genericProperty->name, $this->args)) { - return $this->args[$genericProperty->name]; + if (array_key_exists($genericProperty->getName(), $this->args)) { + return $this->args[$genericProperty->getName()]; } $aliasesAttr = $genericProperty->getAttribute(FieldAlias::class); @@ -76,12 +77,12 @@ public function getValue(GenericProperty $genericProperty): mixed throw new ValueNotFoundException(); } - $snakeCase = TransformUtils::attributeToSnakeCase($genericProperty->name); + $snakeCase = TransformUtils::attributeToSnakeCase($genericProperty->getName()); if (sizeof(array_intersect([WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 & array_key_exists($snakeCase, $this->args)) { return $this->args[$snakeCase]; } - $camelCase = TransformUtils::attributeToCamelCase($genericProperty->name); + $camelCase = TransformUtils::attributeToCamelCase($genericProperty->getName()); if (sizeof(array_intersect([WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 & array_key_exists($camelCase, $this->args)) { return $this->args[$camelCase]; } diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 85ee3e1..54328b7 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -2,6 +2,7 @@ namespace ClassTransformer; +use ClassTransformer\Contracts\ClassTransformable; use ClassTransformer\Exceptions\ClassNotFoundException; /** diff --git a/src/ClassTransformable.php b/src/Contracts/ClassTransformable.php similarity index 82% rename from src/ClassTransformable.php rename to src/Contracts/ClassTransformable.php index db5860d..706598d 100644 --- a/src/ClassTransformable.php +++ b/src/Contracts/ClassTransformable.php @@ -1,6 +1,6 @@ + */ + public function getProperties(): array; + + /** + * @return string + */ + public function getClass(): string; +} diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php new file mode 100644 index 0000000..8b284e2 --- /dev/null +++ b/src/Contracts/ReflectionProperty.php @@ -0,0 +1,42 @@ +class = $class; - + $this->argumentsResource = $argumentsResource; $this->genericInstance = new ($class->getClass()); @@ -58,14 +55,13 @@ public function transform(): mixed } if ($property->hasSetMutator()) { - $this->genericInstance->{TransformUtils::mutationSetterToCamelCase($property->name)}($value); + $this->genericInstance->{TransformUtils::mutationSetterToCamelCase($property->getName())}($value); continue; } - $this->genericInstance->{$property->name} = $property->castAttribute($value); + $caster = new ValueCasting($property); + $this->genericInstance->{$property->getName()} = $caster->castAttribute($value); } return $this->genericInstance; } - - } diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 0622723..f09795c 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -2,7 +2,8 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\ClassTransformable; +use ClassTransformer\Contracts\ClassTransformable; +use ClassTransformer\Contracts\ReflectionClass; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\GenericProperty; use ClassTransformer\Validators\ClassExistsValidator; @@ -53,7 +54,7 @@ public function getProperties(): array $properties = $refInstance->getProperties(); $result = []; foreach ($properties as $item) { - $result [] = new GenericProperty($item); + $result [] = new CacheReflectionProperty($item); } return static::$propertiesTypesCache[$this->class] = $result; diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php new file mode 100644 index 0000000..233374a --- /dev/null +++ b/src/Reflection/CacheReflectionProperty.php @@ -0,0 +1,168 @@ + + */ +final class CacheReflectionProperty implements \ClassTransformer\Contracts\ReflectionProperty +{ + /** @var ReflectionProperty $class */ + private ReflectionProperty $property; + + + /** + * @param ReflectionProperty $property + */ + public function __construct(ReflectionProperty $property) + { + $this->property = $property; + } + + /** + * @param RuntimeReflectionProperty $property + * @param mixed $value + * + * @return mixed + * @throws ClassNotFoundException + */ + public function castAttribute($value) + { + if ($this->isScalar || $this->notTransform()) { + return $value; + } + + if ($this->isArray()) { + return $this->castArray($value); + } + + if ($this->isEnum() && (is_string($value) || is_int($value))) { + return $this->castEnum($value); + } + + if ($this->type instanceof ReflectionNamedType) { + $propertyClass = $this->type->getName(); + + /** @phpstan-ignore-next-line */ + return (new TransformBuilder($propertyClass, $value))->build(); + } + return $value; + } + + + /** + * @param RuntimeReflectionProperty $property + * @param array|mixed $value + * + * @return array|mixed + * @throws ClassNotFoundException + */ + private function castArray($value): mixed + { + $arrayTypeAttr = $this->getAttribute(ConvertArray::class); + if ($arrayTypeAttr !== null) { + $arrayType = $arrayTypeAttr->getArguments()[0]; + } else { + $arrayType = TransformUtils::getClassFromPhpDoc($this->getDocComment()); + } + + if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { + return $value; + } + + $array = []; + if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { + foreach ($value as $el) { + $array[] = (new TransformBuilder($arrayType, $el))->build(); + } + return $array; + } + + foreach ($value as $el) { + $array[] = match ($arrayType) { + 'string' => (string)$el, + 'int' => (int)$el, + 'float' => (float)$el, + 'bool' => (bool)$el, + default => $el + }; + } + return $array; + } + + /** + * @param RuntimeReflectionProperty $property + * @param int|string $value + * + * @return mixed + */ + private function castEnum(int|string $value) + { + /** @phpstan-ignore-next-line */ + $propertyClass = $this->type->getName(); + if (method_exists($propertyClass, 'from')) { + /** @var \BackedEnum $propertyClass */ + return $propertyClass::from($value); + } + + return constant($propertyClass . '::' . $value); + } + + /** + * @param string $key + * + * @return bool + */ + public function hasSetMutator(): bool + { + return $this->property->hasSetMutator; + } + + public function isEnum(): bool + { + // TODO: Implement isEnum() method. + } + + public function isArray(): bool + { + // TODO: Implement isArray() method. + } + + public function notTransform(): bool + { + // TODO: Implement notTransform() method. + } + + public function isScalar(): bool + { + // TODO: Implement isScalar() method. + } + + public function isTransformable(): bool + { + // TODO: Implement isTransformable() method. + } + + public function getName(): string + { + // TODO: Implement getName() method. + } + + public function getTypeName(): string + { + // TODO: Implement getTypeName() method. + } + + public function getAttribute(string $name) + { + // TODO: Implement getAttribute() method. + } +} diff --git a/src/Reflection/ReflectionClass.php b/src/Reflection/ReflectionClass.php deleted file mode 100644 index dcb2ad2..0000000 --- a/src/Reflection/ReflectionClass.php +++ /dev/null @@ -1,10 +0,0 @@ -getProperties(); $result = []; foreach ($properties as $item) { - $result [] = new GenericProperty($item); + $result [] = new RuntimeReflectionProperty($item); } return static::$propertiesTypesCache[$this->class] = $result; diff --git a/src/GenericProperty.php b/src/Reflection/RuntimeReflectionProperty.php similarity index 60% rename from src/GenericProperty.php rename to src/Reflection/RuntimeReflectionProperty.php index 5dd7263..9ce3daa 100644 --- a/src/GenericProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -1,28 +1,30 @@ */ -final class GenericProperty +final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\ReflectionProperty { - /** @var ReflectionProperty */ + /** @var \ClassTransformer\Contracts\ReflectionProperty */ public ReflectionProperty $property; /** @var null|ReflectionType|ReflectionUnionType|ReflectionNamedType */ @@ -47,7 +49,7 @@ final class GenericProperty private static $attributesCache = []; /** - * @param ReflectionProperty $property + * @param \ClassTransformer\Contracts\ReflectionProperty $property */ public function __construct(ReflectionProperty $property) { @@ -148,102 +150,34 @@ public function getAttribute(?string $name = null): ?ReflectionAttribute return null; } + /** - * @param GenericProperty $property - * @param mixed $value + * @param string $key * - * @return mixed - * @throws ClassNotFoundException + * @return bool */ - public function castAttribute($value) + public function hasSetMutator(): bool { - if ($this->isScalar || $this->notTransform()) { - return $value; - } - - if ($this->isArray()) { - return $this->castArray($value); - } - - if ($this->isEnum() && (is_string($value) || is_int($value))) { - return $this->castEnum($value); - } - - if ($this->type instanceof ReflectionNamedType) { - $propertyClass = $this->type->getName(); - - /** @phpstan-ignore-next-line */ - return (new TransformBuilder($propertyClass, $value))->build(); - } - return $value; + return method_exists($this->class, TransformUtils::mutationSetterToCamelCase($this->name)); } - - /** - * @param GenericProperty $property - * @param array|mixed $value - * - * @return array|mixed - * @throws ClassNotFoundException - */ - private function castArray($value): mixed + public function isScalar(): bool { - $arrayTypeAttr = $this->getAttribute(ConvertArray::class); - if ($arrayTypeAttr !== null) { - $arrayType = $arrayTypeAttr->getArguments()[0]; - } else { - $arrayType = TransformUtils::getClassFromPhpDoc($this->getDocComment()); - } - - if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { - return $value; - } - - $array = []; - if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { - foreach ($value as $el) { - $array[] = (new TransformBuilder($arrayType, $el))->build(); - } - return $array; - } - - foreach ($value as $el) { - $array[] = match ($arrayType) { - 'string' => (string)$el, - 'int' => (int)$el, - 'float' => (float)$el, - 'bool' => (bool)$el, - default => $el - }; - } - return $array; + return $this->isScalar; } - /** - * @param GenericProperty $property - * @param int|string $value - * - * @return mixed - */ - private function castEnum(int|string $value) + public function isTransformable(): bool { - /** @phpstan-ignore-next-line */ - $propertyClass = $this->type->getName(); - if (method_exists($propertyClass, 'from')) { - /** @var \BackedEnum $propertyClass */ - return $propertyClass::from($value); - } - - return constant($propertyClass . '::' . $value); + return $this->type instanceof ReflectionNamedType; + } + + public function getTypeName(): string + { + return $this->type->getName(); } - /** - * @param string $key - * - * @return bool - */ - public function hasSetMutator(): bool + public function getName(): string { - return method_exists($this->class, TransformUtils::mutationSetterToCamelCase($this->name)); + return $this->name; } } diff --git a/src/TransformBuilder.php b/src/TransformBuilder.php index a1e9ccd..cd7fdac 100644 --- a/src/TransformBuilder.php +++ b/src/TransformBuilder.php @@ -2,6 +2,7 @@ namespace ClassTransformer; +use ClassTransformer\Contracts\ClassTransformable; use ClassTransformer\Reflection\CacheReflectionClass; use ClassTransformer\Reflection\RuntimeReflectionClass; use ClassTransformer\Validators\ClassExistsValidator; diff --git a/src/ValueCasting.php b/src/ValueCasting.php new file mode 100644 index 0000000..a58b03b --- /dev/null +++ b/src/ValueCasting.php @@ -0,0 +1,125 @@ + + */ +final class ValueCasting +{ + /** @var ReflectionProperty $property */ + private ReflectionProperty $property; + + + /** + * @param ReflectionClass $class + * @param ArgumentsResource $argumentsResource + */ + public function __construct(ReflectionProperty $property) + { + $this->property = $property; + } + + /** + * @param RuntimeReflectionProperty $property + * @param mixed $value + * + * @return mixed + * @throws ClassNotFoundException + */ + public function castAttribute($value) + { + if ($this->property->isScalar() || $this->property->notTransform()) { + return $value; + } + + if ($this->property->isArray()) { + return $this->castArray($value); + } + + if ($this->property->isEnum() && (is_string($value) || is_int($value))) { + return $this->castEnum($value); + } + + if ($this->property->isTransformable()) { + $propertyClass = $this->property->getTypeName(); + + /** @phpstan-ignore-next-line */ + return (new TransformBuilder($propertyClass, $value))->build(); + } + + return $value; + } + + + /** + * @param RuntimeReflectionProperty $property + * @param array|mixed $value + * + * @return array|mixed + * @throws ClassNotFoundException + */ + private function castArray($value): mixed + { + /** @var \ReflectionAttribute $arrayTypeAttr */ + $arrayTypeAttr = $this->property->getAttribute(ConvertArray::class); + if ($arrayTypeAttr !== null) { + + $arrayType = $arrayTypeAttr->getArguments()[0]; + } else { + $arrayType = TransformUtils::getClassFromPhpDoc($this->property->getDocComment()); + } + + if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { + return $value; + } + + $array = []; + if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { + foreach ($value as $el) { + $array[] = (new TransformBuilder($arrayType, $el))->build(); + } + return $array; + } + + foreach ($value as $el) { + $array[] = match ($arrayType) { + 'string' => (string)$el, + 'int' => (int)$el, + 'float' => (float)$el, + 'bool' => (bool)$el, + default => $el + }; + } + return $array; + } + + /** + * @param RuntimeReflectionProperty $property + * @param int|string $value + * + * @return mixed + */ + private function castEnum(int|string $value) + { + $propertyClass = $this->property->getTypeName(); + if (method_exists($propertyClass, 'from')) { + /** @var \BackedEnum $propertyClass */ + return $propertyClass::from($value); + } + + return constant($propertyClass . '::' . $value); + } +} diff --git a/tests/Units/ArgumentsResourceTest.php b/tests/Units/ArgumentsResourceTest.php index 93ebe26..d0bfdd8 100644 --- a/tests/Units/ArgumentsResourceTest.php +++ b/tests/Units/ArgumentsResourceTest.php @@ -2,11 +2,10 @@ namespace Tests\Units; -use ClassTransformer\Attributes\WritingStyle; -use PHPUnit\Framework\TestCase; -use ClassTransformer\GenericProperty; use ClassTransformer\ArgumentsResource; use ClassTransformer\Exceptions\ValueNotFoundException; +use ClassTransformer\Reflection\RuntimeReflectionProperty; +use PHPUnit\Framework\TestCase; use Tests\Units\DTO\UserDTO; class ArgumentsResourceTest extends TestCase @@ -16,7 +15,7 @@ public function testBaseKey(): void $data = ['id' => 1]; $resource = new ArgumentsResource($data); $value = $resource->getValue( - new GenericProperty(new \ReflectionProperty(UserDTO::class, 'id')) + new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'id')) ); $this->assertEquals($data['id'], $value); } @@ -26,7 +25,7 @@ public function testBaseValueNotFoundException(): void $this->expectException(ValueNotFoundException::class); $resource = new ArgumentsResource(['test' => 1]); $resource->getValue( - new GenericProperty(new \ReflectionProperty(UserDTO::class, 'id')) + new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'id')) ); } @@ -36,7 +35,7 @@ public function testEmptyWritingStyle(): void $data = ['id' => 1]; $resource = new ArgumentsResource($data); $value = $resource->getValue( - new GenericProperty(new \ReflectionProperty(UserDTO::class, 'addressOne')) + new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'addressOne')) ); $this->assertEquals($data['id'], $value); } @@ -46,7 +45,7 @@ public function testCamelCaseWritingStyleKey(): void $data = ['addressTwo' => 'test']; $resource = new ArgumentsResource($data); $value = $resource->getValue( - new GenericProperty(new \ReflectionProperty(UserDTO::class, 'address_two')) + new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'address_two')) ); $this->assertEquals($data['addressTwo'], $value); } @@ -56,7 +55,7 @@ public function testSnakeCaseWritingStyleKey(): void $data = ['address_three' => 'test']; $resource = new ArgumentsResource($data); $value = $resource->getValue( - new GenericProperty(new \ReflectionProperty(UserDTO::class, 'addressThree')) + new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'addressThree')) ); $this->assertEquals($data['address_three'], $value); } @@ -66,7 +65,7 @@ public function testFinalValueNotFoundException(): void $this->expectException(ValueNotFoundException::class); $resource = new ArgumentsResource(['test' => 1]); $resource->getValue( - new GenericProperty(new \ReflectionProperty(UserDTO::class, 'balance')) + new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'balance')) ); } } From dcbb07a1df488fffbbc22503e0b908f434f73418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 10 Apr 2023 22:44:36 +0300 Subject: [PATCH 052/100] issue: cache class --- .gitattributes | 1 + .gitignore | 1 + ...4b10aba5a9ca641a0132be8ff3ddc49245e390.xml | 2 + README.RU.md | 2 - bin/plain-to-class-clear | 32 + bin/plain-to-class-clear.bat | 7 + composer.json | 7 +- composer.lock | 1085 ++++++++++++++++- phpstan.neon | 3 - psalm.xml | 17 + src/ArgumentsResource.php | 23 +- src/Attributes/ConvertArray.php | 5 +- src/Attributes/FieldAlias.php | 5 +- src/Attributes/NotTransform.php | 2 + src/Attributes/WritingStyle.php | 2 + src/CacheGenerator/CacheGenerator.php | 110 ++ src/ClassTransformer.php | 48 +- src/ClassTransformerConfig.php | 2 +- src/Contracts/ClassTransformable.php | 5 +- src/Contracts/ReflectionClass.php | 2 +- src/Contracts/ReflectionProperty.php | 36 +- src/Exceptions/ClassNotFoundException.php | 3 + src/Exceptions/InvalidArgumentException.php | 4 + src/Exceptions/ValueNotFoundException.php | 3 + src/GenericInstance.php | 5 +- src/Reflection/CacheReflectionClass.php | 41 +- src/Reflection/CacheReflectionProperty.php | 174 +-- src/Reflection/RuntimeReflectionClass.php | 8 +- src/Reflection/RuntimeReflectionProperty.php | 42 +- src/TransformBuilder.php | 69 -- src/Validators/ClassExistsValidator.php | 6 +- src/ValueCasting.php | 29 +- tests/Benchmark/AddressBench.php | 144 +++ tests/Benchmark/DTO/AddressClean.php | 415 +++++++ tests/Benchmark/DTO/AddressDto.php | 14 + .../DTO/{ProductDTO.php => ProductDto.php} | 2 +- tests/Benchmark/DTO/PurchaseDTO.php | 16 - tests/Benchmark/DTO/PurchaseDto.php | 18 + .../DTO/{UserDTO.php => UserDto.php} | 2 +- tests/Benchmark/DTO/UserTypeEnum.php | 2 - tests/Benchmark/FullCheckBench.php | 47 +- tests/Integration/DTO/ColorEnum.php | 2 - tests/Integration/DTO/ColorScalarEnum.php | 2 - tests/Integration/DTO/ExampleWithEnumDTO.php | 2 - 44 files changed, 2101 insertions(+), 346 deletions(-) create mode 100644 .phpbench/storage/7e7/4/a/134b10aba5a9ca641a0132be8ff3ddc49245e390.xml create mode 100755 bin/plain-to-class-clear create mode 100755 bin/plain-to-class-clear.bat delete mode 100644 phpstan.neon create mode 100644 psalm.xml create mode 100644 src/CacheGenerator/CacheGenerator.php delete mode 100644 src/TransformBuilder.php create mode 100644 tests/Benchmark/AddressBench.php create mode 100644 tests/Benchmark/DTO/AddressClean.php create mode 100644 tests/Benchmark/DTO/AddressDto.php rename tests/Benchmark/DTO/{ProductDTO.php => ProductDto.php} (91%) delete mode 100644 tests/Benchmark/DTO/PurchaseDTO.php create mode 100644 tests/Benchmark/DTO/PurchaseDto.php rename tests/Benchmark/DTO/{UserDTO.php => UserDto.php} (96%) diff --git a/.gitattributes b/.gitattributes index 940a431..86d2c1e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,5 +2,6 @@ tests/ export-ignore phpunit.xml export-ignore phpbench.json export-ignore phpstan.xml export-ignore +psalm.xml export-ignore coverage.xml export-ignore phpstan.neon export-ignore diff --git a/.gitignore b/.gitignore index 3bb351a..0af138f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store .idea +.cache vendor .phpunit.result.cache .phpunit.cache/ diff --git a/.phpbench/storage/7e7/4/a/134b10aba5a9ca641a0132be8ff3ddc49245e390.xml b/.phpbench/storage/7e7/4/a/134b10aba5a9ca641a0132be8ff3ddc49245e390.xml new file mode 100644 index 0000000..594f679 --- /dev/null +++ b/.phpbench/storage/7e7/4/a/134b10aba5a9ca641a0132be8ff3ddc49245e390.xml @@ -0,0 +1,2 @@ + +DarwinMacBook-Pro-Andrej.local22.3.0Darwin Kernel Version 22.3.0: Mon Jan 30 20:38:37 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T6000arm648.2.3/opt/homebrew/etc/php/8.2/php.iniCore, date, libxml, openssl, pcre, sqlite3, zlib, bcmath, bz2, calendar, ctype, curl, dba, dom, hash, FFI, fileinfo, filter, ftp, gd, gettext, gmp, json, iconv, intl, SPL, ldap, mbstring, session, standard, odbc, pcntl, exif, mysqlnd, PDO, pdo_dblib, pdo_mysql, PDO_ODBC, pdo_pgsql, pdo_sqlite, pgsql, Phar, posix, pspell, random, readline, Reflection, mysqli, shmop, SimpleXML, soap, sockets, sodium, sysvmsg, sysvsem, sysvshm, tidy, tokenizer, xml, xmlreader, xmlwriter, xsl, zip, Zend OPcache1gitfeature/cachee40415279cf2cc695e7fff34a29dd16d08158d540.012159347534180.185012817382812.6462078094482 diff --git a/README.RU.md b/README.RU.md index 38d25e1..1431699 100644 --- a/README.RU.md +++ b/README.RU.md @@ -7,8 +7,6 @@ ![Packagist Downloads](https://img.shields.io/packagist/dm/yzen.dev/plain-to-class) ![Packagist Downloads](https://img.shields.io/packagist/dt/yzen.dev/plain-to-class) -> Увы, но я не владею английским, так что документация была составлена с помощью Google Translate :С -> Я буду рад, если вы поможете мне более грамотно описать документацию :) Эта библиотека позволит вам легко преобразовать любой набор данных в нужный вам объект. От вас не требуется менять структуру классов, наследовать их от внешних модулей и т.д. Никаких танцев с бубнами - только данные и нужный класс. diff --git a/bin/plain-to-class-clear b/bin/plain-to-class-clear new file mode 100755 index 0000000..4af5062 --- /dev/null +++ b/bin/plain-to-class-clear @@ -0,0 +1,32 @@ +#!/usr/bin/env php +=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-20T17:52:18+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, + { + "name": "composer/pcre", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-17T09:50:14+00:00" + }, + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, { "name": "doctrine/annotations", "version": "2.0.0", @@ -274,6 +695,168 @@ ], "time": "2022-12-14T08:49:07+00:00" }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.2", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + }, + "time": "2022-03-02T22:36:06+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "0.5.1", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/b58e5a3933e541dc286cc91fc4f3898bbc6f1623", + "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^9.5.26 || ^8.5.31", + "theofidry/php-cs-fixer-config": "^1.0", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/0.5.1" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2022-12-24T12:35:10+00:00" + }, { "name": "hamcrest/hamcrest-php", "version": "v2.0.1", @@ -448,13 +1031,64 @@ "issues": "https://github.com/myclabs/DeepCopy/issues", "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" }, - "funding": [ + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.2.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956", + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" } ], - "time": "2022-03-03T13:19:32+00:00" + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0" + }, + "time": "2023-04-09T17:37:40+00:00" }, { "name": "nikic/php-parser", @@ -816,63 +1450,217 @@ "time": "2023-01-14T13:08:42+00:00" }, { - "name": "phpstan/phpstan", - "version": "1.9.12", + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", "source": { "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8" + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/44a338ff0d5572c13fd77dfd91addb96e48c29f8", - "reference": "44a338ff0d5572c13fd77dfd91addb96e48c29f8", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": "^7.2|^8.0" - }, - "conflict": { - "phpstan/phpstan-shim": "*" + "php": "^7.2 || ^8.0" }, - "bin": [ - "phpstan", - "phpstan.phar" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, "autoload": { - "files": [ - "bootstrap.php" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "PHPStan - PHP Static Analysis Tool", + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", "keywords": [ - "dev", + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", "static analysis" ], "support": { - "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.9.12" + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" }, - "funding": [ + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://github.com/ondrejmirtes", - "type": "github" + "name": "Mike van Riel", + "email": "me@mikevanriel.com" }, { - "url": "https://github.com/phpstan", - "type": "github" - }, + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "dfc078e8af9c99210337325ff5aa152872c98714" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714", + "reference": "dfc078e8af9c99210337325ff5aa152872c98714", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1" + }, + "time": "2023-03-27T19:02:04+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.18.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f", + "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" ], - "time": "2023-01-17T10:44:04+00:00" + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1" + }, + "time": "2023-04-07T11:51:11+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2474,6 +3262,69 @@ ], "time": "2022-04-01T13:37:23+00:00" }, + { + "name": "spatie/array-to-xml", + "version": "3.1.5", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/13f76acef5362d15c71ae1ac6350cc3df5e25e43", + "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.1.5" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-12-24T13:43:51+00:00" + }, { "name": "squizlabs/php_codesniffer", "version": "3.7.1", @@ -3491,6 +4342,170 @@ ], "time": "2021-07-28T10:34:58+00:00" }, + { + "name": "vimeo/psalm", + "version": "5.9.0", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "8b9ad1eb9e8b7d3101f949291da2b9f7767cd163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/8b9ad1eb9e8b7d3101f949291da2b9f7767cd163", + "reference": "8b9ad1eb9e8b7d3101f949291da2b9f7767cd163", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.2", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.14", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0", + "sebastian/diff": "^4.0 || ^5.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^4.1.6 || ^5.0 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "amphp/phpunit-util": "^2.0", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^4.4 || ^5.0 || ^6.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" + } + ], + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php", + "static analysis" + ], + "support": { + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm/tree/5.9.0" + }, + "time": "2023-03-29T21:38:21+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, { "name": "webmozart/glob", "version": "4.6.0", diff --git a/phpstan.neon b/phpstan.neon deleted file mode 100644 index df3a0ba..0000000 --- a/phpstan.neon +++ /dev/null @@ -1,3 +0,0 @@ -parameters: - level: 9 - checkGenericClassInNonGenericObjectType: false diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..22b7de8 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index 08519fb..2bc0321 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -16,14 +16,15 @@ /** * Class GenericProperty * - * @author yzen.dev + * @psalm-api */ final class ArgumentsResource { /** @var array $args */ - private array $args; + private array $args = []; /** + * * @param iterable|mixed ...$args */ public function __construct(...$args) @@ -48,10 +49,9 @@ public function getValue(ReflectionProperty $genericProperty): mixed return $this->args[$genericProperty->getName()]; } - $aliasesAttr = $genericProperty->getAttribute(FieldAlias::class); + $aliases = $genericProperty->getAttributeArguments(FieldAlias::class); - if ($aliasesAttr !== null) { - $aliases = $aliasesAttr->getArguments(); + if ($aliases !== null) { if (!empty($aliases)) { $aliases = $aliases[0]; if (is_string($aliases)) { @@ -65,25 +65,20 @@ public function getValue(ReflectionProperty $genericProperty): mixed } } - $writingStyle = $genericProperty->getAttribute(WritingStyle::class); + $styles = $genericProperty->getAttributeArguments(WritingStyle::class); - if ($writingStyle === null) { + if ($styles === null) { throw new ValueNotFoundException(); } - $styles = $writingStyle->getArguments(); - - if (empty($styles)) { - throw new ValueNotFoundException(); - } $snakeCase = TransformUtils::attributeToSnakeCase($genericProperty->getName()); - if (sizeof(array_intersect([WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 & array_key_exists($snakeCase, $this->args)) { + if (sizeof(array_intersect([WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 && array_key_exists($snakeCase, $this->args)) { return $this->args[$snakeCase]; } $camelCase = TransformUtils::attributeToCamelCase($genericProperty->getName()); - if (sizeof(array_intersect([WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 & array_key_exists($camelCase, $this->args)) { + if (sizeof(array_intersect([WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 && array_key_exists($camelCase, $this->args)) { return $this->args[$camelCase]; } diff --git a/src/Attributes/ConvertArray.php b/src/Attributes/ConvertArray.php index f6d5612..3ee4535 100644 --- a/src/Attributes/ConvertArray.php +++ b/src/Attributes/ConvertArray.php @@ -6,6 +6,8 @@ /** * An attribute for properties that are an array that allows you to specify the type of element + * + * @psalm-api */ #[\Attribute(\Attribute::TARGET_PARAMETER)] final class ConvertArray @@ -15,6 +17,7 @@ final class ConvertArray */ public function __construct( public string $type - ) { + ) + { } } diff --git a/src/Attributes/FieldAlias.php b/src/Attributes/FieldAlias.php index 87d52a8..c386553 100644 --- a/src/Attributes/FieldAlias.php +++ b/src/Attributes/FieldAlias.php @@ -5,7 +5,7 @@ namespace ClassTransformer\Attributes; /** - * + * @psalm-api */ #[\Attribute(\Attribute::TARGET_PARAMETER)] final class FieldAlias @@ -15,6 +15,7 @@ final class FieldAlias */ public function __construct( public string|array $aliases - ) { + ) + { } } diff --git a/src/Attributes/NotTransform.php b/src/Attributes/NotTransform.php index 43cdd23..85628e5 100644 --- a/src/Attributes/NotTransform.php +++ b/src/Attributes/NotTransform.php @@ -6,6 +6,8 @@ /** * Attribute for properties that don't need to be converted + * + * @psalm-api */ #[\Attribute(\Attribute::TARGET_PARAMETER)] final class NotTransform diff --git a/src/Attributes/WritingStyle.php b/src/Attributes/WritingStyle.php index 74ea374..0973112 100644 --- a/src/Attributes/WritingStyle.php +++ b/src/Attributes/WritingStyle.php @@ -6,6 +6,8 @@ /** * Attribute for specifying the style of passed arguments + * + * @psalm-api */ #[\Attribute(\Attribute::TARGET_PARAMETER)] final class WritingStyle diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php new file mode 100644 index 0000000..ead2c53 --- /dev/null +++ b/src/CacheGenerator/CacheGenerator.php @@ -0,0 +1,110 @@ +class = $class; + } + + /** + * @return bool + */ + public function cacheExists() + { + $class = str_replace('\\', '_', $this->class); + return file_exists('./.cache/' . $class . '.cache.php'); + } + + /** + * @return void + * @throws \ReflectionException + */ + public function generate() + { + + if (!file_exists(__DIR__ . '/../../.cache')) { + mkdir(__DIR__ . '/../../.cache', 0777, true); + } + + $class = str_replace('\\', '_', $this->class); + $path = __DIR__ . '/../../.cache/' . $class . '.cache.php'; + + if (file_exists($path)) { + unlink($path); + } + + $cache = [ + 'properties' => [] + ]; + + $refInstance = new PhpReflectionClass($this->class); + + $properties = $refInstance->getProperties(); + + foreach ($properties as $item) { + $property = new RuntimeReflectionProperty($item); + if ($property->type instanceof \ReflectionNamedType) { + $type = $property->type->getName(); + } else { + $type = $property->type; + } + $attrs = $property->property->getAttributes(); + if (!empty($attrs)) { + $attributes = []; + foreach ($attrs as $attr) { + $attributes[$attr->getName()] = $attr->getArguments(); + } + + } else { + $attributes = []; + } + $cache['properties'][] = [ + 'class' => $property->class, + 'name' => $property->name, + 'type' => $type, + 'types' => $property->types, + 'isScalar' => $property->isScalar, + 'hasSetMutator' => $property->hasSetMutator(), + 'isArray' => $property->isArray(), + 'isEnum' => $property->isEnum(), + 'notTransform' => $property->notTransform(), + 'isTransformable' => $property->isTransformable(), + 'typeName' => $property->getTypeName(), + 'docComment' => $property->getDocComment(), + 'attributes' => $attributes, + ]; + } + + file_put_contents( + $path, + 'class); + return require_once __DIR__ . '/../../.cache/' . $class . '.cache.php'; + } +} diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 54328b7..ab3d8e2 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -2,37 +2,61 @@ namespace ClassTransformer; -use ClassTransformer\Contracts\ClassTransformable; +use ClassTransformer\Reflection\CacheReflectionClass; +use ClassTransformer\Validators\ClassExistsValidator; use ClassTransformer\Exceptions\ClassNotFoundException; +use ClassTransformer\Reflection\RuntimeReflectionClass; /** * Class ClassTransformer * - * @template T of ClassTransformable - * @package ClassTransformer + * @psalm-api + * @psalm-immutable */ final class ClassTransformer { /** * Class-transformer function to transform our object into a typed object * - * @param class-string $className + * @template TClass + * + * @param class-string $className * @param iterable ...$args * - * @return null|T + * @return null|TClass * @throws ClassNotFoundException */ public static function transform(string $className, ...$args) { - return (new TransformBuilder($className, ...$args)) - ->build(); + new ClassExistsValidator($className); + if (method_exists($className, 'transform')) { + $instance = new $className(); + $instance->transform(...$args); + } else { + if (ClassTransformerConfig::$cache) { + $reflection = new CacheReflectionClass($className); + } else { + $reflection = new RuntimeReflectionClass($className); + } + $generic = new GenericInstance($reflection, new ArgumentsResource(...$args)); + /** @var TClass $instance */ + $instance = $generic->transform(); + } + + if (method_exists($instance, 'afterTransform')) { + $instance->afterTransform(); + } + + return $instance; } /** - * @param class-string $className + * @template TClass + * + * @param class-string $className * @param array> $args * - * @return null|array|array + * @return null|array|array * @throws ClassNotFoundException */ public static function transformCollection(string $className, array $args): ?array @@ -45,10 +69,12 @@ public static function transformCollection(string $className, array $args): ?arr } /** - * @param array> $className + * @template TClass + * + * @param array> $className * @param array> $args * - * @return null|array|array + * @return null|array|array * @throws ClassNotFoundException */ public static function transformMultiple(array $className, array $args): ?array diff --git a/src/ClassTransformerConfig.php b/src/ClassTransformerConfig.php index 04c3652..6a012f1 100644 --- a/src/ClassTransformerConfig.php +++ b/src/ClassTransformerConfig.php @@ -7,7 +7,7 @@ /** * Class ClassTransformerConfig * - * @package ClassTransformer + * @psalm-api */ final class ClassTransformerConfig { diff --git a/src/Contracts/ClassTransformable.php b/src/Contracts/ClassTransformable.php index 706598d..1f8b692 100644 --- a/src/Contracts/ClassTransformable.php +++ b/src/Contracts/ClassTransformable.php @@ -3,14 +3,15 @@ namespace ClassTransformer\Contracts; /** - * @template T + * @psalm-api + * @template-covariant TClass */ interface ClassTransformable { /** * @param array $args * - * @return T + * @return TClass */ public function transform(...$args): mixed; } diff --git a/src/Contracts/ReflectionClass.php b/src/Contracts/ReflectionClass.php index d94b768..6ddfeb4 100644 --- a/src/Contracts/ReflectionClass.php +++ b/src/Contracts/ReflectionClass.php @@ -3,7 +3,7 @@ namespace ClassTransformer\Contracts; /** - * + * @psalm-api */ interface ReflectionClass { diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index 8b284e2..0644d6d 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -2,18 +2,50 @@ namespace ClassTransformer\Contracts; +/** + * @psalm-api + */ interface ReflectionProperty { + /** + * @return bool + */ public function isScalar(): bool; + /** + * @return bool + */ public function isTransformable(): bool; + /** + * @return string + */ public function getName(): string; - public function getTypeName(): string; + /** + * @return class-string + */ + public function getTypeName(): ?string; + /** + * @param string $name + * + * @return mixed + */ public function getAttribute(string $name); + /** + * @param string $name + * + * @return ?array + */ + public function getAttributeArguments(string $name): ?array; + + /** + * @return bool|string + */ + public function getDocComment(): bool|string; + /** * Finds whether a variable is an enum * @@ -34,8 +66,6 @@ public function isArray(): bool; public function notTransform(): bool; /** - * @param string $key - * * @return bool */ public function hasSetMutator(): bool; diff --git a/src/Exceptions/ClassNotFoundException.php b/src/Exceptions/ClassNotFoundException.php index b0db09f..9ca4e41 100644 --- a/src/Exceptions/ClassNotFoundException.php +++ b/src/Exceptions/ClassNotFoundException.php @@ -2,6 +2,9 @@ namespace ClassTransformer\Exceptions; +/** + * @psalm-api + */ class ClassNotFoundException extends \Exception { } diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php index f368b93..cf9d32c 100644 --- a/src/Exceptions/InvalidArgumentException.php +++ b/src/Exceptions/InvalidArgumentException.php @@ -2,6 +2,10 @@ namespace ClassTransformer\Exceptions; + +/** + * @psalm-api + */ class InvalidArgumentException extends \Exception { } diff --git a/src/Exceptions/ValueNotFoundException.php b/src/Exceptions/ValueNotFoundException.php index 553afa4..c6e3b4d 100644 --- a/src/Exceptions/ValueNotFoundException.php +++ b/src/Exceptions/ValueNotFoundException.php @@ -2,6 +2,9 @@ namespace ClassTransformer\Exceptions; +/** + * @psalm-api + */ class ValueNotFoundException extends \Exception { } diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 20a1a7a..1a7eb95 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -2,17 +2,16 @@ namespace ClassTransformer; -use ClassTransformer\Contracts\ClassTransformable; use ClassTransformer\Contracts\ReflectionClass; +use ClassTransformer\Contracts\ClassTransformable; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\Exceptions\ValueNotFoundException; /** * Class GenericInstance * + * @psalm-api * @template T of ClassTransformable - * - * @author yzen.dev */ final class GenericInstance { diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index f09795c..1be985d 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -2,19 +2,17 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Contracts\ClassTransformable; use ClassTransformer\Contracts\ReflectionClass; -use ClassTransformer\Exceptions\ClassNotFoundException; -use ClassTransformer\GenericProperty; +use ClassTransformer\Contracts\ClassTransformable; +use ClassTransformer\CacheGenerator\CacheGenerator; use ClassTransformer\Validators\ClassExistsValidator; -use ReflectionClass as PhpReflectionClass; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class RuntimeReflectionClass * + * @psalm-api * @template T of ClassTransformable - * - * @author yzen.dev */ final class CacheReflectionClass implements ReflectionClass { @@ -49,14 +47,35 @@ public function getProperties(): array return static::$propertiesTypesCache[$this->class]; } - $refInstance = new PhpReflectionClass($this->class); + $cache = new CacheGenerator($this->class); - $properties = $refInstance->getProperties(); - $result = []; - foreach ($properties as $item) { - $result [] = new CacheReflectionProperty($item); + if (!$cache->cacheExists()) { + $cache->generate(); } + $class = $cache->get(); + + $result = []; + $class['properties'] = array_map(function ($item) { + $property = new CacheReflectionProperty(); + + $property->class = $item['class']; + $property->name = $item['name']; + $property->type = $item['type']; + $property->types = $item['types']; + $property->isScalar = $item['isScalar']; + $property->hasSetMutator = $item['hasSetMutator']; + $property->isArray = $item['isArray']; + $property->isEnum = $item['isEnum']; + $property->notTransform = $item['notTransform']; + $property->isTransformable = $item['isTransformable']; + $property->typeName = $item['typeName']; + $property->docComment = $item['docComment']; + $property->attributes = $item['attributes']; + + return $property; + }, $class['properties']); + return static::$propertiesTypesCache[$this->class] = $result; } diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 233374a..82d5311 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -2,167 +2,127 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Contracts\ReflectionProperty; -use ClassTransformer\TransformUtils; -use ClassTransformer\TransformBuilder; -use ClassTransformer\Attributes\ConvertArray; -use ClassTransformer\Exceptions\ClassNotFoundException; +use ReflectionProperty; /** * Class GenericProperty * - * @author yzen.dev + * @psalm-api */ final class CacheReflectionProperty implements \ClassTransformer\Contracts\ReflectionProperty { - /** @var ReflectionProperty $class */ - private ReflectionProperty $property; + public string $class; + public string $name; + public string $type; + public array $types; + public bool $isScalar; + public bool $hasSetMutator; + public bool $isArray; + public bool $isEnum; + public bool $notTransform; + public bool $isTransformable; + public ?string $typeName; + public string $docComment; + public array $attributes; /** - * @param ReflectionProperty $property */ - public function __construct(ReflectionProperty $property) + public function __construct() { - $this->property = $property; } /** - * @param RuntimeReflectionProperty $property - * @param mixed $value - * - * @return mixed - * @throws ClassNotFoundException + * @return bool */ - public function castAttribute($value) + public function hasSetMutator(): bool { - if ($this->isScalar || $this->notTransform()) { - return $value; - } - - if ($this->isArray()) { - return $this->castArray($value); - } - - if ($this->isEnum() && (is_string($value) || is_int($value))) { - return $this->castEnum($value); - } - - if ($this->type instanceof ReflectionNamedType) { - $propertyClass = $this->type->getName(); - - /** @phpstan-ignore-next-line */ - return (new TransformBuilder($propertyClass, $value))->build(); - } - return $value; + return $this->hasSetMutator; } - /** - * @param RuntimeReflectionProperty $property - * @param array|mixed $value - * - * @return array|mixed - * @throws ClassNotFoundException + * @return bool */ - private function castArray($value): mixed + public function isEnum(): bool { - $arrayTypeAttr = $this->getAttribute(ConvertArray::class); - if ($arrayTypeAttr !== null) { - $arrayType = $arrayTypeAttr->getArguments()[0]; - } else { - $arrayType = TransformUtils::getClassFromPhpDoc($this->getDocComment()); - } - - if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { - return $value; - } - - $array = []; - if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { - foreach ($value as $el) { - $array[] = (new TransformBuilder($arrayType, $el))->build(); - } - return $array; - } - - foreach ($value as $el) { - $array[] = match ($arrayType) { - 'string' => (string)$el, - 'int' => (int)$el, - 'float' => (float)$el, - 'bool' => (bool)$el, - default => $el - }; - } - return $array; + return $this->isEnum; } /** - * @param RuntimeReflectionProperty $property - * @param int|string $value - * - * @return mixed + * @return bool */ - private function castEnum(int|string $value) + public function isArray(): bool { - /** @phpstan-ignore-next-line */ - $propertyClass = $this->type->getName(); - if (method_exists($propertyClass, 'from')) { - /** @var \BackedEnum $propertyClass */ - return $propertyClass::from($value); - } - - return constant($propertyClass . '::' . $value); + return $this->isArray; } /** - * @param string $key - * * @return bool */ - public function hasSetMutator(): bool + public function notTransform(): bool { - return $this->property->hasSetMutator; + return $this->notTransform; } - public function isEnum(): bool + /** + * @return bool + */ + public function isScalar(): bool { - // TODO: Implement isEnum() method. + return $this->isScalar; } - public function isArray(): bool + /** + * @return bool + */ + public function isTransformable(): bool { - // TODO: Implement isArray() method. + return $this->isTransformable; } - public function notTransform(): bool + /** + * @return string + */ + public function getName(): string { - // TODO: Implement notTransform() method. + return $this->name; } - public function isScalar(): bool + /** + * @return null|class-string + */ + public function getTypeName(): ?string { - // TODO: Implement isScalar() method. + return $this->typeName; } - public function isTransformable(): bool - { - // TODO: Implement isTransformable() method. - } - public function getName(): string + /** + * @param string $name + * + * @return mixed|null + */ + public function getAttribute(string $name) { - // TODO: Implement getName() method. + return $this->attributes[$name] ?? null; } - public function getTypeName(): string + /** + * @param string|null $name + * + * @return mixed|null + */ + public function getAttributeArguments(?string $name = null): ?array { - // TODO: Implement getTypeName() method. + return $this->attributes[$name] ?? null; } - public function getAttribute(string $name) + /** + * @return bool|string + */ + public function getDocComment(): bool|string { - // TODO: Implement getAttribute() method. + return $this->docComment ?? false; } + } diff --git a/src/Reflection/RuntimeReflectionClass.php b/src/Reflection/RuntimeReflectionClass.php index 82e1aca..cbf2da2 100644 --- a/src/Reflection/RuntimeReflectionClass.php +++ b/src/Reflection/RuntimeReflectionClass.php @@ -2,19 +2,17 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Contracts\ClassTransformable; use ClassTransformer\Contracts\ReflectionClass; -use ClassTransformer\Exceptions\ClassNotFoundException; -use ClassTransformer\GenericProperty; +use ClassTransformer\Contracts\ClassTransformable; use ClassTransformer\Validators\ClassExistsValidator; +use ClassTransformer\Exceptions\ClassNotFoundException; use ReflectionClass as PhpReflectionClass; /** * Class RuntimeReflectionClass * + * @psalm-api * @template T of ClassTransformable - * - * @author yzen.dev */ final class RuntimeReflectionClass implements ReflectionClass { diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index 9ce3daa..28c4891 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -2,10 +2,7 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Attributes\ConvertArray; use ClassTransformer\Attributes\NotTransform; -use ClassTransformer\Exceptions\ClassNotFoundException; -use ClassTransformer\TransformBuilder; use ClassTransformer\TransformUtils; use ReflectionAttribute; use ReflectionNamedType; @@ -19,8 +16,6 @@ /** * Class GenericProperty - * - * @author yzen.dev */ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\ReflectionProperty { @@ -36,7 +31,7 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref /** @var class-string|string $propertyClass */ public string $name; - /** @var string */ + /** @var class-string */ public string $class; /** @var bool */ @@ -49,7 +44,7 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref private static $attributesCache = []; /** - * @param \ClassTransformer\Contracts\ReflectionProperty $property + * @param \ReflectionProperty $property */ public function __construct(ReflectionProperty $property) { @@ -150,10 +145,21 @@ public function getAttribute(?string $name = null): ?ReflectionAttribute return null; } - /** - * @param string $key + * @param string|null $name * + * @return array|null + */ + public function getAttributeArguments(?string $name = null): ?array + { + $attr = $this->getAttribute($name); + if ($attr !== null) { + return $attr->getArguments(); + } + return null; + } + + /** * @return bool */ public function hasSetMutator(): bool @@ -161,21 +167,33 @@ public function hasSetMutator(): bool return method_exists($this->class, TransformUtils::mutationSetterToCamelCase($this->name)); } + /** + * @return bool + */ public function isScalar(): bool { return $this->isScalar; } + /** + * @return bool + */ public function isTransformable(): bool { return $this->type instanceof ReflectionNamedType; } - - public function getTypeName(): string + + /** + * @return null|class-string + */ + public function getTypeName(): ?string { - return $this->type->getName(); + return $this->type?->getName() ?? null; } + /** + * @return string + */ public function getName(): string { return $this->name; diff --git a/src/TransformBuilder.php b/src/TransformBuilder.php deleted file mode 100644 index cd7fdac..0000000 --- a/src/TransformBuilder.php +++ /dev/null @@ -1,69 +0,0 @@ - $class - */ - private string $class; - - /** @var iterable $args */ - private $args; - - /** - * @param class-string $class - * @param iterable ...$args - * - * @throws ClassNotFoundException - */ - public function __construct(string $class, ...$args) - { - new ClassExistsValidator($class); - - $this->class = $class; - $this->args = $args; - } - - /** - * @return T - * @throws ClassNotFoundException - */ - public function build() - { - if (method_exists($this->class, 'transform')) { - /** @var T $instance */ - $instance = new $this->class(); - $instance->transform(...$this->args); - } else { - if (ClassTransformerConfig::$cache) { - $reflection = new CacheReflectionClass($this->class); - } else { - $reflection = new RuntimeReflectionClass($this->class); - } - $generic = new GenericInstance($reflection, new ArgumentsResource(...$this->args)); - /** @var T $instance */ - $instance = $generic->transform(); - } - - if (method_exists($instance, 'afterTransform')) { - $instance->afterTransform(); - } - - return $instance; - } - -} diff --git a/src/Validators/ClassExistsValidator.php b/src/Validators/ClassExistsValidator.php index ec6916c..82d2549 100644 --- a/src/Validators/ClassExistsValidator.php +++ b/src/Validators/ClassExistsValidator.php @@ -6,17 +6,15 @@ /** * Class ClassExistsValidator - * - * @author yzen.dev */ class ClassExistsValidator { /** - * @param class-string $className + * @param string $className * * @throws ClassNotFoundException */ - public function __construct($className) + public function __construct(string $className) { if (!class_exists($className)) { throw new ClassNotFoundException("Class $className not found. Please check the class path you specified."); diff --git a/src/ValueCasting.php b/src/ValueCasting.php index a58b03b..d358100 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -12,10 +12,6 @@ /** * Class GenericInstance - * - * @template T of ClassTransformable - * - * @author yzen.dev */ final class ValueCasting { @@ -24,16 +20,14 @@ final class ValueCasting /** - * @param ReflectionClass $class - * @param ArgumentsResource $argumentsResource + * @param ReflectionProperty $property */ public function __construct(ReflectionProperty $property) { $this->property = $property; } - + /** - * @param RuntimeReflectionProperty $property * @param mixed $value * * @return mixed @@ -56,16 +50,14 @@ public function castAttribute($value) if ($this->property->isTransformable()) { $propertyClass = $this->property->getTypeName(); - /** @phpstan-ignore-next-line */ - return (new TransformBuilder($propertyClass, $value))->build(); + return ClassTransformer::transform($propertyClass, $value); } - + return $value; } /** - * @param RuntimeReflectionProperty $property * @param array|mixed $value * * @return array|mixed @@ -73,11 +65,9 @@ public function castAttribute($value) */ private function castArray($value): mixed { - /** @var \ReflectionAttribute $arrayTypeAttr */ - $arrayTypeAttr = $this->property->getAttribute(ConvertArray::class); - if ($arrayTypeAttr !== null) { - - $arrayType = $arrayTypeAttr->getArguments()[0]; + $arrayTypeAttr = $this->property->getAttributeArguments(ConvertArray::class); + if ($arrayTypeAttr !== null && isset($arrayTypeAttr[0])) { + $arrayType = $arrayTypeAttr[0]; } else { $arrayType = TransformUtils::getClassFromPhpDoc($this->property->getDocComment()); } @@ -89,7 +79,7 @@ private function castArray($value): mixed $array = []; if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { foreach ($value as $el) { - $array[] = (new TransformBuilder($arrayType, $el))->build(); + $array[] = ClassTransformer::transform($arrayType, $el); } return $array; } @@ -107,12 +97,11 @@ private function castArray($value): mixed } /** - * @param RuntimeReflectionProperty $property * @param int|string $value * * @return mixed */ - private function castEnum(int|string $value) + private function castEnum(int|string $value): mixed { $propertyClass = $this->property->getTypeName(); if (method_exists($propertyClass, 'from')) { diff --git a/tests/Benchmark/AddressBench.php b/tests/Benchmark/AddressBench.php new file mode 100644 index 0000000..0a3d2c6 --- /dev/null +++ b/tests/Benchmark/AddressBench.php @@ -0,0 +1,144 @@ +getData()); + } + + /** + * @Revs(10000) + */ + public function benchCache(): void + { + ClassTransformerConfig::$cache = true; + ClassTransformer::transform(AddressClean::class, $this->getData()); + } + + + private function getData() + { + return json_decode('{ + "source": "мск сухонска 11/-89", + "result": "г Москва, ул Сухонская, д 11, кв 89", + "postal_code": "127642", + "country": "Россия", + "country_iso_code": "RU", + "federal_district": "Центральный", + "region_fias_id": "0c5b2444-70a0-4932-980c-b4dc0d3f02b5", + "region_kladr_id": "7700000000000", + "region_iso_code": "RU-MOW", + "region_with_type": "г Москва", + "region_type": "г", + "region_type_full": "город", + "region": "Москва", + "area_fias_id": null, + "area_kladr_id": null, + "area_with_type": null, + "area_type": null, + "area_type_full": null, + "area": null, + "city_fias_id": null, + "city_kladr_id": null, + "city_with_type": null, + "city_type": null, + "city_type_full": null, + "city": null, + "city_area": "Северо-восточный", + "city_district_fias_id": null, + "city_district_kladr_id": null, + "city_district_with_type": "р-н Северное Медведково", + "city_district_type": "р-н", + "city_district_type_full": "район", + "city_district": "Северное Медведково", + "settlement_fias_id": null, + "settlement_kladr_id": null, + "settlement_with_type": null, + "settlement_type": null, + "settlement_type_full": null, + "settlement": null, + "street_fias_id": "95dbf7fb-0dd4-4a04-8100-4f6c847564b5", + "street_kladr_id": "77000000000283600", + "street_with_type": "ул Сухонская", + "street_type": "ул", + "street_type_full": "улица", + "street": "Сухонская", + "house_fias_id": "5ee84ac0-eb9a-4b42-b814-2f5f7c27c255", + "house_kladr_id": "7700000000028360004", + "house_cadnum": "77:02:0004008:1017", + "house_type": "д", + "house_type_full": "дом", + "house": "11", + "block_type": null, + "block_type_full": null, + "block": null, + "entrance": null, + "floor": null, + "flat_fias_id": "f26b876b-6857-4951-b060-ec6559f04a9a", + "flat_cadnum": "77:02:0004008:4143", + "flat_type": "кв", + "flat_type_full": "квартира", + "flat": "89", + "flat_area": "34.6", + "square_meter_price": "239953", + "flat_price": "8302374", + "postal_box": null, + "fias_id": "f26b876b-6857-4951-b060-ec6559f04a9a", + "fias_code": "77000000000000028360004", + "fias_level": "9", + "fias_actuality_state": "0", + "kladr_id": "7700000000028360004", + "capital_marker": "0", + "okato": "45280583000", + "oktmo": "45362000", + "tax_office": "7715", + "tax_office_legal": "7715", + "timezone": "UTC+3", + "geo_lat": "55.8782557", + "geo_lon": "37.65372", + "beltway_hit": "IN_MKAD", + "beltway_distance": null, + "qc_geo": 0, + "qc_complete": 0, + "qc_house": 2, + "qc": 0, + "unparsed_parts": null, + "metro": [ + { + "distance": 1.1, + "line": "Калужско-Рижская", + "name": "Бабушкинская" + }, + { + "distance": 1.2, + "line": "Калужско-Рижская", + "name": "Медведково" + }, + { + "distance": 2.5, + "line": "Калужско-Рижская", + "name": "Свиблово" + } + ] + }'); + } +} diff --git a/tests/Benchmark/DTO/AddressClean.php b/tests/Benchmark/DTO/AddressClean.php new file mode 100644 index 0000000..b19b3ab --- /dev/null +++ b/tests/Benchmark/DTO/AddressClean.php @@ -0,0 +1,415 @@ + + */ +class AddressClean +{ + /** + * @var null|string Исходный адрес одной строкой + */ + public ?string $source; + + /** + * @var null|string Стандартизованный адрес одной строкой + */ + public ?string $result; + + /** + * @var null|string Индекс + */ + public ?string $postal_code; + + /** + * @var null|string Страна + */ + public ?string $country; + + /** + * @var null|string Cтрана iso alfa2 + */ + public ?string $country_iso_code; + + /** + * @var null|string Федеральный округ + */ + public ?string $federal_district; + + /** + * @var null|string Код ФИАС региона + */ + public ?string $region_fias_id; + + /** + * @var null|string Код КЛАДР региона + */ + public ?string $region_kladr_id; + + /** + * @var null|string ISO-код региона + */ + public ?string $region_iso_code; + + /** + * @var null|string Регион с типом + */ + public ?string $region_with_type; + + /** + * @var null|string Тип региона (сокращенный) + */ + public ?string $region_type; + + /** + * @var null|string Тип региона + */ + public ?string $region_type_full; + + /** + * @var null|string Регион + */ + public ?string $region; + + /** + * @var null|string Код ФИАС района в регионе + */ + public ?string $area_fias_id; + + /** + * @var null|string Код КЛАДР района в регионе + */ + public ?string $area_kladr_id; + + /** + * @var null|string Район в регионе с типом + */ + public ?string $area_with_type; + + /** + * @var null|string Тип района в регионе (сокращенный) + */ + public ?string $area_type; + + /** + * @var null|string Тип района в регионе + */ + public ?string $area_type_full; + + /** + * @var null|string Район в регионе + */ + public ?string $area; + + /** + * @var null|string Код ФИАС города + */ + public ?string $city_fias_id; + + /** + * @var null|string Код КЛАДР города + */ + public ?string $city_kladr_id; + + /** + * @var null|string Город с типом + */ + public ?string $city_with_type; + + /** + * @var null|string Тип города (сокращенный) + */ + public ?string $city_type; + + /** + * @var null|string Тип города + */ + public ?string $city_type_full; + + /** + * @var null|string Город + */ + public ?string $city; + + /** + * @var null|string Административный округ (только для Москвы) + */ + public ?string $city_area; + + /** + * @var null|string Код ФИАС района города (не заполняется) + */ + public ?string $city_district_fias_id; + + /** + * @var null|string Код КЛАДР района города (не заполняется) + */ + public ?string $city_district_kladr_id; + + /** + * @var null|string Район города с типом + */ + public ?string $city_district_with_type; + + /** + * @var null|string Тип района города (сокращенный) + */ + public ?string $city_district_type; + + /** + * @var null|string Тип района города + */ + public ?string $city_district_type_full; + + /** + * @var null|string Район города + */ + public ?string $city_district; + + /** + * @var null|string Код ФИАС нас. пункта + */ + public ?string $settlement_fias_id; + + /** + * @var null|string Код КЛАДР нас. пункта + */ + public ?string $settlement_kladr_id; + + /** + * @var null|string Населенный пункт с типом + */ + public ?string $settlement_with_type; + + /** + * @var null|string Тип населенного пункта (сокращенный) + */ + public ?string $settlement_type; + + /** + * @var null|string Тип населенного пункта + */ + public ?string $settlement_type_full; + + /** + * @var null|string Населенный пункт + */ + public ?string $settlement; + + /** + * @var null|string Код ФИАС улицы + */ + public ?string $street_fias_id; + + /** + * @var null|string Код КЛАДР улицы + */ + public ?string $street_kladr_id; + + /** + * @var null|string Улица с типом + */ + public ?string $street_with_type; + + /** + * @var null|string Тип улицы (сокращенный) + */ + public ?string $street_type; + + /** + * @var null|string Тип улицы + */ + public ?string $street_type_full; + + /** + * @var null|string Улица + */ + public ?string $street; + + /** + * @var null|string Код ФИАС дома + */ + public ?string $house_fias_id; + + /** + * @var null|string Код КЛАДР дома + */ + public ?string $house_kladr_id; + + /** + * @var null|string Тип дома (сокращенный) + */ + public ?string $house_type; + + /** + * @var null|string Тип дома + */ + public ?string $house_type_full; + + /** + * @var null|string Дом + */ + public ?string $house; + + /** + * @var null|string Тип корпуса/строения (сокращенный) + */ + public ?string $block_type; + + /** + * @var null|string Тип корпуса/строения + */ + public ?string $block_type_full; + + /** + * @var null|string Корпус/строение + */ + public ?string $block; + + /** + * @var null|string Тип квартиры (сокращенный) + */ + public ?string $flat_type; + + /** + * @var null|string Тип квартиры + */ + public ?string $flat_type_full; + + /** + * @var null|string Квартира + */ + public ?string $flat; + + /** + * @var null|float Площадь квартиры + */ + public ?float $flat_area; + + /** + * @var null|string Рыночная стоимость м² + */ + public ?string $square_meter_price; + + /** + * @var null|string Рыночная стоимость квартиры + */ + public ?string $flat_price; + + /** + * @var null|string Абонентский ящик + */ + public ?string $postal_box; + + /** + * @var null|string Код ФИАС: + * @see \AlexBklnv\DaData\Enums\FiasIdEnum + */ + public ?string $fias_id; + + /** + * @var null|int Уровень детализации, до которого адрес найден в ФИАС + * @see \AlexBklnv\DaData\Enums\FiasLevelEnum + */ + public ?int $fias_level; + + /** + * @var null|string Код КЛАДР + */ + public ?string $kladr_id; + + /** + * @var null|int Является ли город центром + * @see \AlexBklnv\DaData\Enums\CapitalMarkerEnum + */ + public ?int $capital_marker; + + /** + * @var null|string Код ОКАТО + */ + public ?string $okato; + + /** + * @var null|string Код ОКТМО + */ + public ?string $oktmo; + + /** + * @var null|string Код ИФНС для физических лиц + */ + public ?string $tax_office; + + /** + * @var null|string Код ИФНС для организаций (не заполняется) + */ + public ?string $tax_office_legal; + + /** + * @var null|string Часовой пояс + */ + public ?string $timezone; + + /** + * @var null|float Координаты: широта + */ + public ?float $geo_lat; + + /** + * @var null|float Координаты: долгота + */ + public ?float $geo_lon; + + /** + * @var null|string Внутри кольцевой? + * @see \AlexBklnv\DaData\Enums\BeltwayHitEnum + */ + public ?string $beltway_hit; + + /** + * @var null|int Расстояние от кольцевой в км. Заполнено, только если beltway_hit = OUT_MKAD или OUT_KAD, иначе пустое. + */ + public ?int $beltway_distance; + + /** + * @var null|int Код точности координат + * @see \AlexBklnv\DaData\Enums\QcEnum + */ + public ?int $qc; + + /** + * @var null|int Код точности координат + * @see \AlexBklnv\DaData\Enums\QcGeoEnum + */ + public ?int $qc_geo; + + /** + * @var null|int Код полноты + * @see \AlexBklnv\DaData\Enums\QcCompeteEnum + */ + public ?int $qc_complete; + + /** + * @var null|int Признак наличия дома в ФИАС - уточняет вероятность успешной доставки письма + * @link https://dadata.ru/api/clean/address/#qc_house + */ + public ?int $qc_house; + + /** + * @var null|string Нераспознанная часть адреса. Для адреса + */ + public ?string $unparsed_parts; + + /** + * @return string + */ + public function __toString(): string + { + return $this->result; + } +} diff --git a/tests/Benchmark/DTO/AddressDto.php b/tests/Benchmark/DTO/AddressDto.php new file mode 100644 index 0000000..b4d430e --- /dev/null +++ b/tests/Benchmark/DTO/AddressDto.php @@ -0,0 +1,14 @@ + */ enum UserTypeEnum: string { diff --git a/tests/Benchmark/FullCheckBench.php b/tests/Benchmark/FullCheckBench.php index b3b7efe..be46455 100644 --- a/tests/Benchmark/FullCheckBench.php +++ b/tests/Benchmark/FullCheckBench.php @@ -5,9 +5,10 @@ use ClassTransformer\ClassTransformer; use ClassTransformer\ClassTransformerConfig; use PHPUnit\Framework\TestCase; -use Tests\Benchmark\DTO\ProductDTO; -use Tests\Benchmark\DTO\PurchaseDTO; -use Tests\Benchmark\DTO\UserDTO; +use Tests\Benchmark\DTO\AddressDto; +use Tests\Benchmark\DTO\ProductDto; +use Tests\Benchmark\DTO\PurchaseDto; +use Tests\Benchmark\DTO\UserDto; use Tests\Benchmark\DTO\UserTypeEnum; /** @@ -27,9 +28,9 @@ public function benchBaseReflection(): void { $data = $this->getPurcheseObject(); - $purchase = new PurchaseDTO(); + $purchase = new PurchaseDto(); - $user = new UserDTO(); + $user = new UserDto(); $user->id = $data['user']['id']; $user->email = $data['user']['contact']; $user->balance = $data['user']['balance']; @@ -37,12 +38,21 @@ public function benchBaseReflection(): void $user->type = UserTypeEnum::from($data['user']['type']); $purchase->user = $user; + $address = new AddressDto(); + $address->city = $data['address']['city']; + $address->street = $data['address']['street']; + $address->house = $data['address']['house']; + + $purchase->address = $address; + + $purchase->createdAt = new \DateTime($data['createdAt']); + foreach ($data['products'] as $product) { - $newProduct = new ProductDTO(); + $newProduct = new ProductDto(); $newProduct->id = $product['id']; $newProduct->name = $product['name']; $newProduct->price = $product['price']; - $purchase->products []= $newProduct; + $purchase->products [] = $newProduct; } } @@ -52,22 +62,23 @@ public function benchBaseReflection(): void public function benchTransformReflection(): void { $data = $this->getPurcheseObject(); - ClassTransformer::transform(PurchaseDTO::class, $data); + ClassTransformer::transform(PurchaseDto::class, $data); } - + /** * @Revs(10000) */ public function benchTransformCacheReflection(): void { - $data = $this->getPurcheseObject(); ClassTransformerConfig::$cache = true; - ClassTransformer::transform(PurchaseDTO::class, $data); + $data = $this->getPurcheseObject(); + ClassTransformer::transform(PurchaseDto::class, $data); } public function getPurcheseObject(): array { return [ + 'createdAt' => '2023-04-10 12:30:23', 'products' => [ [ 'id' => 1, @@ -84,11 +95,18 @@ public function getPurcheseObject(): array 'count' => 321 ], [ - 'id' => 2, + 'id' => 3, 'name' => 'book', 'price' => 5.5, 'description' => 'test description for book', 'count' => 333 + ], + [ + 'id' => 4, + 'name' => 'PC', + 'price' => 100, + 'description' => 'test description for PC', + 'count' => 7 ] ], 'user' => [ @@ -97,6 +115,11 @@ public function getPurcheseObject(): array 'balance' => 10012.23, 'type' => 'admin', 'realAddress' => 'test address', + ], + 'address' => [ + 'city' => 'NY', + 'street' => 'street', + 'house' => '14', ] ]; } diff --git a/tests/Integration/DTO/ColorEnum.php b/tests/Integration/DTO/ColorEnum.php index 41858b1..50e5571 100644 --- a/tests/Integration/DTO/ColorEnum.php +++ b/tests/Integration/DTO/ColorEnum.php @@ -4,8 +4,6 @@ /** * Class ColorEnum - * - * @author yzen.dev */ enum ColorEnum { diff --git a/tests/Integration/DTO/ColorScalarEnum.php b/tests/Integration/DTO/ColorScalarEnum.php index bddc951..0f089d0 100644 --- a/tests/Integration/DTO/ColorScalarEnum.php +++ b/tests/Integration/DTO/ColorScalarEnum.php @@ -4,8 +4,6 @@ /** * Class ColorScalarEnum - * - * @author yzen.dev */ enum ColorScalarEnum: string { diff --git a/tests/Integration/DTO/ExampleWithEnumDTO.php b/tests/Integration/DTO/ExampleWithEnumDTO.php index afd44b0..b04a64b 100644 --- a/tests/Integration/DTO/ExampleWithEnumDTO.php +++ b/tests/Integration/DTO/ExampleWithEnumDTO.php @@ -4,8 +4,6 @@ /** * Class ExampleWithEnumDTO - * - * @author yzen.dev */ class ExampleWithEnumDTO { From 6e6ad4623ca65b3f75ca1825697d337f4ec0d88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 11 Apr 2023 11:17:01 +0300 Subject: [PATCH 053/100] add: ignore psalm --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0af138f..e198c17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store .idea .cache +.psalm vendor .phpunit.result.cache .phpunit.cache/ From 7a813c76276c1b4a6ea6f4eb532f00a522c8f800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 11 Apr 2023 11:28:10 +0300 Subject: [PATCH 054/100] upd: transformable --- src/CacheGenerator/CacheGenerator.php | 3 +-- src/Contracts/ReflectionProperty.php | 14 ++--------- src/Reflection/CacheReflectionClass.php | 3 +-- src/Reflection/CacheReflectionProperty.php | 26 +++----------------- src/Reflection/RuntimeReflectionProperty.php | 14 +++-------- src/ValueCasting.php | 9 +++---- 6 files changed, 15 insertions(+), 54 deletions(-) diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index ead2c53..00b4074 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -86,8 +86,7 @@ public function generate() 'isArray' => $property->isArray(), 'isEnum' => $property->isEnum(), 'notTransform' => $property->notTransform(), - 'isTransformable' => $property->isTransformable(), - 'typeName' => $property->getTypeName(), + 'transformable' => $property->transformable(), 'docComment' => $property->getDocComment(), 'attributes' => $attributes, ]; diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index 0644d6d..f3b0062 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -13,20 +13,15 @@ interface ReflectionProperty public function isScalar(): bool; /** - * @return bool + * @return false|class-string */ - public function isTransformable(): bool; + public function transformable(): false|string; /** * @return string */ public function getName(): string; - /** - * @return class-string - */ - public function getTypeName(): ?string; - /** * @param string $name * @@ -60,11 +55,6 @@ public function isEnum(): bool; */ public function isArray(): bool; - /** - * @return bool - */ - public function notTransform(): bool; - /** * @return bool */ diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 1be985d..70f4156 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -68,8 +68,7 @@ public function getProperties(): array $property->isArray = $item['isArray']; $property->isEnum = $item['isEnum']; $property->notTransform = $item['notTransform']; - $property->isTransformable = $item['isTransformable']; - $property->typeName = $item['typeName']; + $property->transformable = $item['transformable']; $property->docComment = $item['docComment']; $property->attributes = $item['attributes']; diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 82d5311..ef835b4 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -21,8 +21,7 @@ final class CacheReflectionProperty implements \ClassTransformer\Contracts\Refle public bool $isArray; public bool $isEnum; public bool $notTransform; - public bool $isTransformable; - public ?string $typeName; + public bool $transformable; public string $docComment; public array $attributes; @@ -57,11 +56,11 @@ public function isArray(): bool } /** - * @return bool + * @return false|class-string */ - public function notTransform(): bool + public function transformable(): false|string { - return $this->notTransform; + return $this->transformable; } /** @@ -72,14 +71,6 @@ public function isScalar(): bool return $this->isScalar; } - /** - * @return bool - */ - public function isTransformable(): bool - { - return $this->isTransformable; - } - /** * @return string */ @@ -88,15 +79,6 @@ public function getName(): string return $this->name; } - /** - * @return null|class-string - */ - public function getTypeName(): ?string - { - return $this->typeName; - } - - /** * @param string $name * diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index 28c4891..2adce57 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -176,19 +176,11 @@ public function isScalar(): bool } /** - * @return bool - */ - public function isTransformable(): bool - { - return $this->type instanceof ReflectionNamedType; - } - - /** - * @return null|class-string + * @return false|class-string */ - public function getTypeName(): ?string + public function transformable(): false|string { - return $this->type?->getName() ?? null; + return $this->type instanceof ReflectionNamedType ? $this->type->getName() : false; } /** diff --git a/src/ValueCasting.php b/src/ValueCasting.php index d358100..c37dd6b 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -47,9 +47,8 @@ public function castAttribute($value) return $this->castEnum($value); } - if ($this->property->isTransformable()) { - $propertyClass = $this->property->getTypeName(); - + $propertyClass = $this->property->transformable(); + if ($propertyClass) { return ClassTransformer::transform($propertyClass, $value); } @@ -103,8 +102,8 @@ private function castArray($value): mixed */ private function castEnum(int|string $value): mixed { - $propertyClass = $this->property->getTypeName(); - if (method_exists($propertyClass, 'from')) { + $propertyClass = $this->property->transformable(); + if ($propertyClass && method_exists($propertyClass, 'from')) { /** @var \BackedEnum $propertyClass */ return $propertyClass::from($value); } From 6ba846a7e6e1f5b636a1a478636d2a52fe9c29c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 11 Apr 2023 12:05:28 +0300 Subject: [PATCH 055/100] issue: import --- src/Attributes/FieldAlias.php | 7 ++-- src/Attributes/NotTransform.php | 4 ++- src/Attributes/WritingStyle.php | 4 ++- src/CacheGenerator/CacheGenerator.php | 34 +++++++++++--------- src/Contracts/ReflectionClass.php | 1 - src/Contracts/ReflectionProperty.php | 2 +- src/Exceptions/InvalidArgumentException.php | 1 - src/Exceptions/ValueNotFoundException.php | 4 ++- src/Reflection/CacheReflectionClass.php | 6 ++-- src/Reflection/CacheReflectionProperty.php | 12 +++++-- src/Reflection/RuntimeReflectionProperty.php | 11 ++++--- 11 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/Attributes/FieldAlias.php b/src/Attributes/FieldAlias.php index c386553..fdd4abb 100644 --- a/src/Attributes/FieldAlias.php +++ b/src/Attributes/FieldAlias.php @@ -4,10 +4,12 @@ namespace ClassTransformer\Attributes; +use Attribute; + /** * @psalm-api */ -#[\Attribute(\Attribute::TARGET_PARAMETER)] +#[Attribute(Attribute::TARGET_PARAMETER)] final class FieldAlias { /** @@ -15,7 +17,6 @@ final class FieldAlias */ public function __construct( public string|array $aliases - ) - { + ) { } } diff --git a/src/Attributes/NotTransform.php b/src/Attributes/NotTransform.php index 85628e5..4e8f751 100644 --- a/src/Attributes/NotTransform.php +++ b/src/Attributes/NotTransform.php @@ -4,12 +4,14 @@ namespace ClassTransformer\Attributes; +use Attribute; + /** * Attribute for properties that don't need to be converted * * @psalm-api */ -#[\Attribute(\Attribute::TARGET_PARAMETER)] +#[Attribute(Attribute::TARGET_PARAMETER)] final class NotTransform { } diff --git a/src/Attributes/WritingStyle.php b/src/Attributes/WritingStyle.php index 0973112..4d42e40 100644 --- a/src/Attributes/WritingStyle.php +++ b/src/Attributes/WritingStyle.php @@ -4,12 +4,14 @@ namespace ClassTransformer\Attributes; +use Attribute; + /** * Attribute for specifying the style of passed arguments * * @psalm-api */ -#[\Attribute(\Attribute::TARGET_PARAMETER)] +#[Attribute(Attribute::TARGET_PARAMETER)] final class WritingStyle { /** Check all possible styles */ diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index 00b4074..391f287 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -2,6 +2,9 @@ namespace ClassTransformer\CacheGenerator; +use RuntimeException; +use ReflectionException; +use ReflectionNamedType; use ReflectionClass as PhpReflectionClass; use ClassTransformer\Reflection\RuntimeReflectionProperty; @@ -12,7 +15,6 @@ */ class CacheGenerator { - /** @param class-string $class */ private string $class; @@ -27,21 +29,25 @@ public function __construct(string $class) /** * @return bool */ - public function cacheExists() + public function cacheExists(): bool { $class = str_replace('\\', '_', $this->class); return file_exists('./.cache/' . $class . '.cache.php'); } /** - * @return void - * @throws \ReflectionException + * @return array + * @throws ReflectionException */ - public function generate() + public function generate(): array { - if (!file_exists(__DIR__ . '/../../.cache')) { - mkdir(__DIR__ . '/../../.cache', 0777, true); + if ( + !file_exists(__DIR__ . '/../../.cache') && + !mkdir($concurrentDirectory = __DIR__ . '/../../.cache', 0777, true) && + !is_dir($concurrentDirectory) + ) { + throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); } $class = str_replace('\\', '_', $this->class); @@ -61,20 +67,17 @@ public function generate() foreach ($properties as $item) { $property = new RuntimeReflectionProperty($item); - if ($property->type instanceof \ReflectionNamedType) { + if ($property->type instanceof ReflectionNamedType) { $type = $property->type->getName(); } else { $type = $property->type; } $attrs = $property->property->getAttributes(); + $attributes = []; if (!empty($attrs)) { - $attributes = []; foreach ($attrs as $attr) { $attributes[$attr->getName()] = $attr->getArguments(); } - - } else { - $attributes = []; } $cache['properties'][] = [ 'class' => $property->class, @@ -96,14 +99,15 @@ public function generate() $path, 'class); - return require_once __DIR__ . '/../../.cache/' . $class . '.cache.php'; + return require __DIR__ . '/../../.cache/' . $class . '.cache.php'; } } diff --git a/src/Contracts/ReflectionClass.php b/src/Contracts/ReflectionClass.php index 6ddfeb4..37ce0b0 100644 --- a/src/Contracts/ReflectionClass.php +++ b/src/Contracts/ReflectionClass.php @@ -7,7 +7,6 @@ */ interface ReflectionClass { - /** * @return array */ diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index f3b0062..2086e4d 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -27,7 +27,7 @@ public function getName(): string; * * @return mixed */ - public function getAttribute(string $name); + public function getAttribute(string $name): mixed; /** * @param string $name diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php index cf9d32c..549a1c5 100644 --- a/src/Exceptions/InvalidArgumentException.php +++ b/src/Exceptions/InvalidArgumentException.php @@ -2,7 +2,6 @@ namespace ClassTransformer\Exceptions; - /** * @psalm-api */ diff --git a/src/Exceptions/ValueNotFoundException.php b/src/Exceptions/ValueNotFoundException.php index c6e3b4d..8ec918b 100644 --- a/src/Exceptions/ValueNotFoundException.php +++ b/src/Exceptions/ValueNotFoundException.php @@ -2,9 +2,11 @@ namespace ClassTransformer\Exceptions; +use RuntimeException; + /** * @psalm-api */ -class ValueNotFoundException extends \Exception +class ValueNotFoundException extends RuntimeException { } diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 70f4156..9eb1a3e 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -2,6 +2,8 @@ namespace ClassTransformer\Reflection; +use ReflectionProperty; +use ReflectionException; use ClassTransformer\Contracts\ReflectionClass; use ClassTransformer\Contracts\ClassTransformable; use ClassTransformer\CacheGenerator\CacheGenerator; @@ -20,9 +22,9 @@ final class CacheReflectionClass implements ReflectionClass private string $class; /** - * @var array + * @var array */ - private static $propertiesTypesCache = []; + private static array $propertiesTypesCache = []; /** diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index ef835b4..407e855 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -2,8 +2,6 @@ namespace ClassTransformer\Reflection; -use ReflectionProperty; - /** * Class GenericProperty * @@ -55,6 +53,14 @@ public function isArray(): bool return $this->isArray; } + /** + * @return bool + */ + public function notTransform(): bool + { + return $this->notTransform; + } + /** * @return false|class-string */ @@ -84,7 +90,7 @@ public function getName(): string * * @return mixed|null */ - public function getAttribute(string $name) + public function getAttribute(string $name): mixed { return $this->attributes[$name] ?? null; } diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index 2adce57..ca309af 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -2,14 +2,15 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Attributes\NotTransform; -use ClassTransformer\TransformUtils; -use ReflectionAttribute; -use ReflectionNamedType; -use ReflectionProperty; use ReflectionType; +use ReflectionProperty; +use ReflectionNamedType; use ReflectionUnionType; +use ReflectionAttribute; +use ClassTransformer\TransformUtils; +use ClassTransformer\Attributes\NotTransform; +use function method_exists; use function array_intersect; use function in_array; use function sizeof; From 8fc0aac505a714cb5bd0d5308ccc3477a3c17f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 11 Apr 2023 12:45:27 +0300 Subject: [PATCH 056/100] issue: cache --- .gitignore | 1 + composer.json | 3 +- composer.lock | 377 +++++++++++------- src/ArgumentsResource.php | 27 +- src/Attributes/ConvertArray.php | 9 +- src/CacheGenerator/CacheGenerator.php | 1 - src/Reflection/CacheReflectionClass.php | 54 ++- src/Reflection/CacheReflectionProperty.php | 30 +- src/Reflection/RuntimeReflectionProperty.php | 34 +- src/ValueCasting.php | 4 +- tests/Benchmark/FullCheckBench.php | 11 +- .../ClassTransformerFromCacheTest.php | 55 +++ 12 files changed, 377 insertions(+), 229 deletions(-) create mode 100644 tests/Integration/ClassTransformerFromCacheTest.php diff --git a/.gitignore b/.gitignore index e198c17..0f89a30 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .idea .cache .psalm +.phpbench vendor .phpunit.result.cache .phpunit.cache/ diff --git a/composer.json b/composer.json index 154e622..03bc148 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,8 @@ "phpbench/phpbench": "1.2.8", "phpunit/phpunit": "9.5.28", "squizlabs/php_codesniffer": "3.7.1", - "vimeo/psalm": "^5.9" + "vimeo/psalm": "^5.9", + "symfony/var-dumper": "^6.2" }, "bin": [ "bin/plain-to-class-clear" diff --git a/composer.lock b/composer.lock index 8aadcb0..5f207cb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "afd4c8ecddd5f8ffcba70766974f2b40", + "content-hash": "60c1fd5c9ed799e633776b8f027e9892", "packages": [], "packages-dev": [ { @@ -430,16 +430,16 @@ }, { "name": "doctrine/annotations", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "d02c9f3742044e17d5fa8d28d8402a2d95c33302" + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/d02c9f3742044e17d5fa8d28d8402a2d95c33302", - "reference": "d02c9f3742044e17d5fa8d28d8402a2d95c33302", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", "shasum": "" }, "require": { @@ -500,9 +500,9 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.0" + "source": "https://github.com/doctrine/annotations/tree/2.0.1" }, - "time": "2022-12-19T18:17:20+00:00" + "time": "2023-02-02T22:02:53+00:00" }, { "name": "doctrine/deprecations", @@ -549,30 +549,30 @@ }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -599,7 +599,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -615,32 +615,31 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "doctrine/lexer", - "version": "2.1.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" + "reference": "84a527db05647743d50373e0ec53a152f2cde568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", + "reference": "84a527db05647743d50373e0ec53a152f2cde568", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.5", "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^4.11 || ^5.0" + "vimeo/psalm": "^5.0" }, "type": "library", "autoload": { @@ -677,7 +676,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/2.1.0" + "source": "https://github.com/doctrine/lexer/tree/3.0.0" }, "funding": [ { @@ -693,7 +692,7 @@ "type": "tidelift" } ], - "time": "2022-12-14T08:49:07+00:00" + "time": "2022-12-15T16:57:16+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -982,16 +981,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { @@ -1029,7 +1028,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ { @@ -1037,7 +1036,7 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2023-03-08T13:26:56+00:00" }, { "name": "netresearch/jsonmapper", @@ -1092,16 +1091,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.3", + "version": "v4.15.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", "shasum": "" }, "require": { @@ -1142,9 +1141,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" }, - "time": "2023-01-16T22:05:37+00:00" + "time": "2023-03-05T19:49:14+00:00" }, { "name": "phar-io/manifest", @@ -1310,25 +1309,25 @@ }, { "name": "phpbench/dom", - "version": "0.3.2", + "version": "0.3.3", "source": { "type": "git", "url": "https://github.com/phpbench/dom.git", - "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110" + "reference": "786a96db538d0def931f5b19225233ec42ec7a72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/dom/zipball/b013b717832ddbaadf2a40984b04bc66af9a7110", - "reference": "b013b717832ddbaadf2a40984b04bc66af9a7110", + "url": "https://api.github.com/repos/phpbench/dom/zipball/786a96db538d0def931f5b19225233ec42ec7a72", + "reference": "786a96db538d0def931f5b19225233ec42ec7a72", "shasum": "" }, "require": { "ext-dom": "*", - "php": "^7.2||^8.0" + "php": "^7.3||^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.18", - "phpstan/phpstan": "^0.12.83", + "friendsofphp/php-cs-fixer": "^3.14", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^8.0||^9.0" }, "type": "library", @@ -1355,9 +1354,9 @@ "description": "DOM wrapper to simplify working with the PHP DOM implementation", "support": { "issues": "https://github.com/phpbench/dom/issues", - "source": "https://github.com/phpbench/dom/tree/0.3.2" + "source": "https://github.com/phpbench/dom/tree/0.3.3" }, - "time": "2021-09-24T15:26:07+00:00" + "time": "2023-03-06T23:46:57+00:00" }, { "name": "phpbench/phpbench", @@ -1664,23 +1663,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.24", + "version": "9.2.26", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed" + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", - "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.14", + "nikic/php-parser": "^4.15", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -1695,8 +1694,8 @@ "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { @@ -1729,7 +1728,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" }, "funding": [ { @@ -1737,7 +1736,7 @@ "type": "github" } ], - "time": "2023-01-26T08:26:55+00:00" + "time": "2023-03-06T12:58:08+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2600,16 +2599,16 @@ }, { "name": "sebastian/environment", - "version": "5.1.4", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { @@ -2651,7 +2650,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -2659,7 +2658,7 @@ "type": "github" } ], - "time": "2022-04-03T09:37:03+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", @@ -2973,16 +2972,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { @@ -3021,10 +3020,10 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { @@ -3032,7 +3031,7 @@ "type": "github" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", @@ -3091,16 +3090,16 @@ }, { "name": "sebastian/type", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { @@ -3135,7 +3134,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { @@ -3143,7 +3142,7 @@ "type": "github" } ], - "time": "2022-09-12T14:47:03+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", @@ -3383,20 +3382,21 @@ }, { "name": "symfony/console", - "version": "v6.0.19", + "version": "v6.2.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed" + "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c3ebc83d031b71c39da318ca8b7a07ecc67507ed", - "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed", + "url": "https://api.github.com/repos/symfony/console/zipball/3582d68a64a86ec25240aaa521ec8bc2342b369b", + "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^1.1|^2|^3", "symfony/string": "^5.4|^6.0" @@ -3453,12 +3453,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.19" + "source": "https://github.com/symfony/console/tree/v6.2.8" }, "funding": [ { @@ -3474,29 +3474,29 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-03-29T21:42:15+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.2", + "version": "v3.2.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -3525,7 +3525,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" }, "funding": [ { @@ -3541,24 +3541,24 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2023-03-01T10:25:55+00:00" }, { "name": "symfony/filesystem", - "version": "v6.0.19", + "version": "v6.2.7", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214" + "reference": "82b6c62b959f642d000456f08c6d219d749215b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3d49eec03fda1f0fc19b7349fbbe55ebc1004214", - "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", + "reference": "82b6c62b959f642d000456f08c6d219d749215b3", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, @@ -3588,7 +3588,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.0.19" + "source": "https://github.com/symfony/filesystem/tree/v6.2.7" }, "funding": [ { @@ -3604,24 +3604,27 @@ "type": "tidelift" } ], - "time": "2023-01-20T17:44:14+00:00" + "time": "2023-02-14T08:44:56+00:00" }, { "name": "symfony/finder", - "version": "v6.0.19", + "version": "v6.2.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11" + "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5cc9cac6586fc0c28cd173780ca696e419fefa11", - "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11", + "url": "https://api.github.com/repos/symfony/finder/zipball/20808dc6631aecafbe67c186af5dcb370be3a0eb", + "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" }, "type": "library", "autoload": { @@ -3649,7 +3652,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.0.19" + "source": "https://github.com/symfony/finder/tree/v6.2.7" }, "funding": [ { @@ -3665,24 +3668,24 @@ "type": "tidelift" } ], - "time": "2023-01-20T17:44:14+00:00" + "time": "2023-02-16T09:57:23+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.0.19", + "version": "v6.2.7", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "6a180d1c45e0d9797470ca9eb46215692de00fa3" + "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/6a180d1c45e0d9797470ca9eb46215692de00fa3", - "reference": "6a180d1c45e0d9797470ca9eb46215692de00fa3", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/aa0e85b53bbb2b4951960efd61d295907eacd629", + "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", @@ -3716,7 +3719,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.0.19" + "source": "https://github.com/symfony/options-resolver/tree/v6.2.7" }, "funding": [ { @@ -3732,7 +3735,7 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-02-14T08:44:56+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4066,20 +4069,20 @@ }, { "name": "symfony/process", - "version": "v6.0.19", + "version": "v6.2.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "2114fd60f26a296cc403a7939ab91478475a33d4" + "reference": "75ed64103df4f6615e15a7fe38b8111099f47416" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/2114fd60f26a296cc403a7939ab91478475a33d4", - "reference": "2114fd60f26a296cc403a7939ab91478475a33d4", + "url": "https://api.github.com/repos/symfony/process/zipball/75ed64103df4f6615e15a7fe38b8111099f47416", + "reference": "75ed64103df4f6615e15a7fe38b8111099f47416", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -4107,7 +4110,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.0.19" + "source": "https://github.com/symfony/process/tree/v6.2.8" }, "funding": [ { @@ -4123,24 +4126,24 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-03-09T16:20:02+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.0.2", + "version": "v3.2.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" + "reference": "a8c9cedf55f314f3a186041d19537303766df09a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", - "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", + "reference": "a8c9cedf55f314f3a186041d19537303766df09a", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "psr/container": "^2.0" }, "conflict": { @@ -4152,7 +4155,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -4162,7 +4165,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4189,7 +4195,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" }, "funding": [ { @@ -4205,24 +4211,24 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:17:58+00:00" + "time": "2023-03-01T10:32:47+00:00" }, { "name": "symfony/string", - "version": "v6.0.19", + "version": "v6.2.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a" + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d9e72497367c23e08bf94176d2be45b00a9d232a", - "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a", + "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -4234,6 +4240,7 @@ "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", "symfony/translation-contracts": "^2.0|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, @@ -4274,7 +4281,95 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.19" + "source": "https://github.com/symfony/string/tree/v6.2.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-03-20T16:06:02+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "d37ab6787be2db993747b6218fcc96e8e3bb4bd0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d37ab6787be2db993747b6218fcc96e8e3bb4bd0", + "reference": "d37ab6787be2db993747b6218fcc96e8e3bb4bd0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "phpunit/phpunit": "<5.4.3", + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.2.8" }, "funding": [ { @@ -4290,7 +4385,7 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-03-29T21:42:15+00:00" }, { "name": "theseer/tokenizer", diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index 2bc0321..401dbaf 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -21,10 +21,10 @@ final class ArgumentsResource { /** @var array $args */ - private array $args = []; + private array $args; /** - * + * * @param iterable|mixed ...$args */ public function __construct(...$args) @@ -51,16 +51,14 @@ public function getValue(ReflectionProperty $genericProperty): mixed $aliases = $genericProperty->getAttributeArguments(FieldAlias::class); - if ($aliases !== null) { - if (!empty($aliases)) { - $aliases = $aliases[0]; - if (is_string($aliases)) { - $aliases = [$aliases]; - } - foreach ($aliases as $alias) { - if (array_key_exists($alias, $this->args)) { - return $this->args[$alias]; - } + if (!empty($aliases)) { + $aliases = $aliases[0]; + if (is_string($aliases)) { + $aliases = [$aliases]; + } + foreach ($aliases as $alias) { + if (array_key_exists($alias, $this->args)) { + return $this->args[$alias]; } } } @@ -71,14 +69,13 @@ public function getValue(ReflectionProperty $genericProperty): mixed throw new ValueNotFoundException(); } - $snakeCase = TransformUtils::attributeToSnakeCase($genericProperty->getName()); - if (sizeof(array_intersect([WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 && array_key_exists($snakeCase, $this->args)) { + if (array_key_exists($snakeCase, $this->args) && sizeof(array_intersect([WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_ALL], $styles)) > 0) { return $this->args[$snakeCase]; } $camelCase = TransformUtils::attributeToCamelCase($genericProperty->getName()); - if (sizeof(array_intersect([WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_ALL], $styles)) > 0 && array_key_exists($camelCase, $this->args)) { + if (array_key_exists($camelCase, $this->args) && sizeof(array_intersect([WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_ALL], $styles)) > 0) { return $this->args[$camelCase]; } diff --git a/src/Attributes/ConvertArray.php b/src/Attributes/ConvertArray.php index 3ee4535..15e5512 100644 --- a/src/Attributes/ConvertArray.php +++ b/src/Attributes/ConvertArray.php @@ -4,20 +4,21 @@ namespace ClassTransformer\Attributes; +use Attribute; + /** * An attribute for properties that are an array that allows you to specify the type of element * * @psalm-api */ -#[\Attribute(\Attribute::TARGET_PARAMETER)] +#[Attribute(Attribute::TARGET_PARAMETER)] final class ConvertArray { /** - * @param string $type + * @param class-string $type */ public function __construct( public string $type - ) - { + ) { } } diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index 391f287..2858b88 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -41,7 +41,6 @@ public function cacheExists(): bool */ public function generate(): array { - if ( !file_exists(__DIR__ . '/../../.cache') && !mkdir($concurrentDirectory = __DIR__ . '/../../.cache', 0777, true) && diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 9eb1a3e..16af95c 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -40,44 +40,42 @@ public function __construct(string $class) } /** - * @return \ReflectionProperty[] - * @throws \ReflectionException + * @return ReflectionProperty[] + * @throws ReflectionException */ public function getProperties(): array { - if (isset(static::$propertiesTypesCache[$this->class])) { - return static::$propertiesTypesCache[$this->class]; + if (isset(self::$propertiesTypesCache[$this->class])) { + return self::$propertiesTypesCache[$this->class]; } $cache = new CacheGenerator($this->class); if (!$cache->cacheExists()) { - $cache->generate(); + $class = $cache->generate(); + } else { + $class = $cache->get(); } - $class = $cache->get(); - - $result = []; - $class['properties'] = array_map(function ($item) { - $property = new CacheReflectionProperty(); - - $property->class = $item['class']; - $property->name = $item['name']; - $property->type = $item['type']; - $property->types = $item['types']; - $property->isScalar = $item['isScalar']; - $property->hasSetMutator = $item['hasSetMutator']; - $property->isArray = $item['isArray']; - $property->isEnum = $item['isEnum']; - $property->notTransform = $item['notTransform']; - $property->transformable = $item['transformable']; - $property->docComment = $item['docComment']; - $property->attributes = $item['attributes']; - - return $property; - }, $class['properties']); - - return static::$propertiesTypesCache[$this->class] = $result; + $properties = array_map( + static fn($item) => new CacheReflectionProperty( + $item['class'], + $item['name'], + $item['type'], + $item['types'], + $item['isScalar'], + $item['hasSetMutator'], + $item['isArray'], + $item['isEnum'], + $item['notTransform'], + $item['transformable'], + $item['docComment'], + $item['attributes'], + ), + $class['properties'] + ); + + return self::$propertiesTypesCache[$this->class] = $properties; } /** diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 407e855..900c300 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -9,24 +9,22 @@ */ final class CacheReflectionProperty implements \ClassTransformer\Contracts\ReflectionProperty { - - public string $class; - public string $name; - public string $type; - public array $types; - public bool $isScalar; - public bool $hasSetMutator; - public bool $isArray; - public bool $isEnum; - public bool $notTransform; - public bool $transformable; - public string $docComment; - public array $attributes; - /** */ - public function __construct() - { + public function __construct( + public string $class, + public string $name, + public ?string $type, + public array $types, + public bool $isScalar, + public bool $hasSetMutator, + public bool $isArray, + public bool $isEnum, + public bool $notTransform, + public false|string $transformable, + public string $docComment, + public array $attributes, + ) { } /** diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index ca309af..711179c 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -20,11 +20,11 @@ */ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\ReflectionProperty { - /** @var \ClassTransformer\Contracts\ReflectionProperty */ + /** @var ReflectionProperty */ public ReflectionProperty $property; - /** @var null|ReflectionType|ReflectionUnionType|ReflectionNamedType */ - public ?ReflectionType $type; + /** @var ReflectionType|ReflectionNamedType|ReflectionUnionType|null */ + public ReflectionType|ReflectionNamedType|ReflectionUnionType|null $type; /** @var array|string[] */ public array $types; @@ -38,14 +38,14 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref /** @var bool */ public bool $isScalar; - /** @var array>> */ - private static $attributeTypesCache = []; + /** @var array>> */ + private static array $attributeTypesCache = []; - /** @var array>> */ - private static $attributesCache = []; + /** @var array>> */ + private static array $attributesCache = []; /** - * @param \ReflectionProperty $property + * @param ReflectionProperty $property */ public function __construct(ReflectionProperty $property) { @@ -76,8 +76,8 @@ public function isEnum(): bool */ private function initTypes(): array { - if (isset(static::$attributeTypesCache[$this->class][$this->name])) { - return static::$attributeTypesCache[$this->class][$this->name]; + if (isset(self::$attributeTypesCache[$this->class][$this->name])) { + return self::$attributeTypesCache[$this->class][$this->name]; } if ($this->type === null) { @@ -99,7 +99,7 @@ private function initTypes(): array $types [] = 'null'; } - return static::$attributeTypesCache[$this->class][$this->name] = $types; + return self::$attributeTypesCache[$this->class][$this->name] = $types; } /** @@ -135,13 +135,13 @@ public function notTransform(): bool */ public function getAttribute(?string $name = null): ?ReflectionAttribute { - if (isset(static::$attributesCache[$this->class][$this->name][$name])) { - return static::$attributesCache[$this->class][$this->name][$name]; + if (isset(self::$attributesCache[$this->class][$this->name][$name])) { + return self::$attributesCache[$this->class][$this->name][$name]; } $attr = $this->property->getAttributes($name); if (!empty($attr)) { - return static::$attributesCache[$this->class][$this->name][$name] = $attr[0]; + return self::$attributesCache[$this->class][$this->name][$name] = $attr[0]; } return null; } @@ -153,11 +153,7 @@ public function getAttribute(?string $name = null): ?ReflectionAttribute */ public function getAttributeArguments(?string $name = null): ?array { - $attr = $this->getAttribute($name); - if ($attr !== null) { - return $attr->getArguments(); - } - return null; + return $this->getAttribute($name)?->getArguments(); } /** diff --git a/src/ValueCasting.php b/src/ValueCasting.php index c37dd6b..3617cd0 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -33,7 +33,7 @@ public function __construct(ReflectionProperty $property) * @return mixed * @throws ClassNotFoundException */ - public function castAttribute($value) + public function castAttribute(mixed $value): mixed { if ($this->property->isScalar() || $this->property->notTransform()) { return $value; @@ -46,7 +46,7 @@ public function castAttribute($value) if ($this->property->isEnum() && (is_string($value) || is_int($value))) { return $this->castEnum($value); } - + $propertyClass = $this->property->transformable(); if ($propertyClass) { return ClassTransformer::transform($propertyClass, $value); diff --git a/tests/Benchmark/FullCheckBench.php b/tests/Benchmark/FullCheckBench.php index be46455..433da5e 100644 --- a/tests/Benchmark/FullCheckBench.php +++ b/tests/Benchmark/FullCheckBench.php @@ -54,6 +54,8 @@ public function benchBaseReflection(): void $newProduct->price = $product['price']; $purchase->products [] = $newProduct; } + + $this->assertEquals($data['user']['id'], $purchase->user->id); } /** @@ -62,7 +64,9 @@ public function benchBaseReflection(): void public function benchTransformReflection(): void { $data = $this->getPurcheseObject(); - ClassTransformer::transform(PurchaseDto::class, $data); + $purchase = ClassTransformer::transform(PurchaseDto::class, $data); + + $this->assertEquals($data['user']['id'], $purchase->user->id); } /** @@ -72,7 +76,10 @@ public function benchTransformCacheReflection(): void { ClassTransformerConfig::$cache = true; $data = $this->getPurcheseObject(); - ClassTransformer::transform(PurchaseDto::class, $data); + + $purchase = ClassTransformer::transform(PurchaseDto::class, $data); + + $this->assertEquals($data['user']['id'], $purchase->user->id); } public function getPurcheseObject(): array diff --git a/tests/Integration/ClassTransformerFromCacheTest.php b/tests/Integration/ClassTransformerFromCacheTest.php new file mode 100644 index 0000000..5e34a47 --- /dev/null +++ b/tests/Integration/ClassTransformerFromCacheTest.php @@ -0,0 +1,55 @@ +markTestSkipped('cache'); + return; + ClassTransformerConfig::$cache = true; + $data = $this->getRecursiveObject(); + $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); + self::assertInstanceOf(PurchaseDTO::class, $purchaseDTO); + self::assertInstanceOf(UserDTO::class, $purchaseDTO->user); + self::assertEquals($data->user->id, $purchaseDTO->user->id); + self::assertEquals($data->user->email, $purchaseDTO->user->email); + self::assertEquals($data->user->balance, $purchaseDTO->user->balance); + self::assertIsInt($purchaseDTO->user->id); + self::assertIsString($purchaseDTO->user->email); + self::assertIsFloat($purchaseDTO->user->balance); + foreach ($purchaseDTO->products as $key => $product) { + self::assertInstanceOf(ProductDTO::class, $product); + self::assertEquals($data->products[$key]->id, $product->id); + self::assertEquals($data->products[$key]->name, $product->name); + self::assertEquals($data->products[$key]->price, $product->price); + self::assertIsInt($product->id); + self::assertIsString($product->name); + self::assertIsFloat($product->price); + } + } +} From 4888f8ab9704b5b2a4381666fed0acf5ed05d5fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 11 Apr 2023 12:56:27 +0300 Subject: [PATCH 057/100] mini fix --- src/ClassTransformer.php | 1 + src/TransformUtils.php | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index ab3d8e2..8669d61 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -29,6 +29,7 @@ final class ClassTransformer public static function transform(string $className, ...$args) { new ClassExistsValidator($className); + if (method_exists($className, 'transform')) { $instance = new $className(); $instance->transform(...$args); diff --git a/src/TransformUtils.php b/src/TransformUtils.php index db25c9e..76c34a9 100644 --- a/src/TransformUtils.php +++ b/src/TransformUtils.php @@ -32,11 +32,11 @@ final class TransformUtils */ public static function attributeToSnakeCase(string $key): string { - if (isset(static::$snakeCache[$key])) { - return static::$snakeCache[$key]; + if (isset(self::$snakeCache[$key])) { + return self::$snakeCache[$key]; } $str = preg_replace('/(?<=\d)(?=[A-Za-z])|(?<=[A-Za-z])(?=\d)|(?<=[a-z])(?=[A-Z])/', '_', $key) ?? ''; - return static::$snakeCache[$key] = strtolower($str); + return self::$snakeCache[$key] = strtolower($str); } /** @@ -46,11 +46,11 @@ public static function attributeToSnakeCase(string $key): string */ public static function attributeToCamelCase(string $key): string { - if (isset(static::$camelCache[$key])) { - return static::$camelCache[$key]; + if (isset(self::$camelCache[$key])) { + return self::$camelCache[$key]; } $str = lcfirst(str_replace('_', '', ucwords($key, '_'))); - return static::$camelCache[$key] = $str; + return self::$camelCache[$key] = $str; } /** @@ -60,11 +60,11 @@ public static function attributeToCamelCase(string $key): string */ public static function mutationSetterToCamelCase(string $key): string { - if (isset(static::$mutationSetterCache[$key])) { - return static::$mutationSetterCache[$key]; + if (isset(self::$mutationSetterCache[$key])) { + return self::$mutationSetterCache[$key]; } $str = 'set' . ucfirst(self::attributeToCamelCase($key)) . 'Attribute'; - return static::$mutationSetterCache[$key] = $str; + return self::$mutationSetterCache[$key] = $str; } /** From 6a222d7714514495ab266d0a49ef45fcdcd43f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 11 Apr 2023 13:53:58 +0300 Subject: [PATCH 058/100] upd --- src/ValueCasting.php | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/ValueCasting.php b/src/ValueCasting.php index 3617cd0..f2001e1 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -3,12 +3,13 @@ namespace ClassTransformer; use ClassTransformer\Attributes\ConvertArray; -use ClassTransformer\Contracts\ClassTransformable; -use ClassTransformer\Contracts\ReflectionClass; use ClassTransformer\Contracts\ReflectionProperty; use ClassTransformer\Exceptions\ClassNotFoundException; -use ClassTransformer\Exceptions\ValueNotFoundException; -use ClassTransformer\Reflection\RuntimeReflectionProperty; + +use function method_exists; +use function is_array; +use function in_array; +use function array_map; /** * Class GenericInstance @@ -46,7 +47,7 @@ public function castAttribute(mixed $value): mixed if ($this->property->isEnum() && (is_string($value) || is_int($value))) { return $this->castEnum($value); } - + $propertyClass = $this->property->transformable(); if ($propertyClass) { return ClassTransformer::transform($propertyClass, $value); @@ -75,24 +76,18 @@ private function castArray($value): mixed return $value; } - $array = []; if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { - foreach ($value as $el) { - $array[] = ClassTransformer::transform($arrayType, $el); - } - return $array; - } + return array_map(static fn($el) => ClassTransformer::transform($arrayType, $el), $value); - foreach ($value as $el) { - $array[] = match ($arrayType) { - 'string' => (string)$el, - 'int' => (int)$el, - 'float' => (float)$el, - 'bool' => (bool)$el, - default => $el - }; } - return $array; + + return array_map(static fn($el) => match ($arrayType) { + 'string' => (string)$el, + 'int' => (int)$el, + 'float' => (float)$el, + 'bool' => (bool)$el, + default => $el + }, $value); } /** From 9e5f616b35601a5da0a4eabfa2dfcf25b805e303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 2 May 2023 09:32:21 +0300 Subject: [PATCH 059/100] upd tests --- composer.json | 6 +- composer.lock | 642 +++++++----------- phpunit.xml | 19 +- src/Attributes/ConvertArray.php | 4 +- src/CacheGenerator/CacheGenerator.php | 61 +- src/ClassTransformer.php | 2 +- src/ClassTransformerConfig.php | 1 + src/GenericInstance.php | 13 +- src/Reflection/CacheReflectionClass.php | 2 +- src/Reflection/CacheReflectionProperty.php | 3 +- src/Reflection/RuntimeReflectionProperty.php | 2 +- src/ValueCasting.php | 8 +- tests/Integration/AfterTransformTest.php | 1 + .../ClassTransformerExceptionsTest.php | 8 +- .../ClassTransformerFromArrayTest.php | 12 +- .../ClassTransformerFromCacheTest.php | 23 +- .../ClassTransformerFromObjectTest.php | 8 +- .../Integration/CustomSetterAttributeTest.php | 8 +- tests/Integration/CustomTransformerTest.php | 8 +- tests/Integration/DTO/PurchaseDTO.php | 3 + tests/Integration/DTO/UserCacheableDTO.php | 29 + tests/Integration/EnumTest.php | 1 - tests/Integration/NotTransformTest.php | 6 +- tests/Integration/UnionTypeTest.php | 6 +- tests/Integration/WritingStyleTest.php | 13 +- tests/Units/CacheGeneratorTest.php | 73 ++ tests/Units/CacheReflectionClassTest.php | 50 ++ tests/Units/DTO/UserCacheableDTO.php | 29 + 28 files changed, 562 insertions(+), 479 deletions(-) create mode 100644 tests/Integration/DTO/UserCacheableDTO.php create mode 100644 tests/Units/CacheGeneratorTest.php create mode 100644 tests/Units/CacheReflectionClassTest.php create mode 100644 tests/Units/DTO/UserCacheableDTO.php diff --git a/composer.json b/composer.json index 03bc148..c89e9e2 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "require-dev": { "mockery/mockery": "1.5.1", "phpbench/phpbench": "1.2.8", - "phpunit/phpunit": "9.5.28", + "phpunit/phpunit": "10.1.2", "squizlabs/php_codesniffer": "3.7.1", "vimeo/psalm": "^5.9", "symfony/var-dumper": "^6.2" @@ -47,8 +47,8 @@ "phpunit": [ "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml" ], - "phpstan": [ - "./vendor/bin/phpstan analyse -c ./phpstan.neon src" + "psalm": [ + "./vendor/bin/psalm --show-info=true" ], "phpcs": [ "./vendor/bin/phpcs --standard=./phpcs.xml -n --no-cache -s" diff --git a/composer.lock b/composer.lock index 5f207cb..c15fd0d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "60c1fd5c9ed799e633776b8f027e9892", + "content-hash": "09bdbc60bf7ad325d910687cc8948cfa", "packages": [], "packages-dev": [ { @@ -547,76 +547,6 @@ }, "time": "2022-05-02T15:47:09+00:00" }, - { - "name": "doctrine/instantiator", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:23:10+00:00" - }, { "name": "doctrine/lexer", "version": "3.0.0", @@ -1618,16 +1548,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.18.1", + "version": "1.20.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f" + "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f", - "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6c04009f6cae6eda2f040745b6b846080ef069c2", + "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2", "shasum": "" }, "require": { @@ -1657,22 +1587,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.3" }, - "time": "2023-04-07T11:51:11+00:00" + "time": "2023-04-25T09:01:03+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.26", + "version": "10.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + "reference": "884a0da7f9f46f28b2cb69134217fd810b793974" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/884a0da7f9f46f28b2cb69134217fd810b793974", + "reference": "884a0da7f9f46f28b2cb69134217fd810b793974", "shasum": "" }, "require": { @@ -1680,18 +1610,18 @@ "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -1700,7 +1630,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "10.1-dev" } }, "autoload": { @@ -1728,7 +1658,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.1" }, "funding": [ { @@ -1736,32 +1667,32 @@ "type": "github" } ], - "time": "2023-03-06T12:58:08+00:00" + "time": "2023-04-17T12:15:40+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/fd9329ab3368f59fe1fe808a189c51086bd4b6bd", + "reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1788,7 +1719,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.1" }, "funding": [ { @@ -1796,28 +1727,28 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2023-02-10T16:53:14+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-pcntl": "*" @@ -1825,7 +1756,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1851,7 +1782,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" }, "funding": [ { @@ -1859,32 +1790,32 @@ "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2023-02-03T06:56:09+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d", + "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -1910,7 +1841,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0" }, "funding": [ { @@ -1918,32 +1849,32 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2023-02-03T06:56:46+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1969,7 +1900,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" }, "funding": [ { @@ -1977,24 +1908,23 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2023-02-03T06:57:52+00:00" }, { "name": "phpunit/phpunit", - "version": "9.5.28", + "version": "10.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e" + "reference": "6f0cd95be71add539f8fd2be25b2a4a29789000b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e", - "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6f0cd95be71add539f8fd2be25b2a4a29789000b", + "reference": "6f0cd95be71add539f8fd2be25b2a4a29789000b", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -2004,27 +1934,26 @@ "myclabs/deep-copy": "^1.10.1", "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-invoker": "^4.0", + "phpunit/php-text-template": "^3.0", + "phpunit/php-timer": "^6.0", + "sebastian/cli-parser": "^2.0", + "sebastian/code-unit": "^2.0", + "sebastian/comparator": "^5.0", + "sebastian/diff": "^5.0", + "sebastian/environment": "^6.0", + "sebastian/exporter": "^5.0", + "sebastian/global-state": "^6.0", + "sebastian/object-enumerator": "^5.0", + "sebastian/recursion-context": "^5.0", + "sebastian/type": "^4.0", + "sebastian/version": "^4.0" }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "ext-soap": "To be able to generate mocks based on WSDL files" }, "bin": [ "phpunit" @@ -2032,7 +1961,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-main": "10.1-dev" } }, "autoload": { @@ -2063,7 +1992,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.1.2" }, "funding": [ { @@ -2079,7 +2009,7 @@ "type": "tidelift" } ], - "time": "2023-01-14T12:32:24+00:00" + "time": "2023-04-22T07:38:19+00:00" }, { "name": "psr/cache", @@ -2235,28 +2165,28 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae", + "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -2279,7 +2209,7 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0" }, "funding": [ { @@ -2287,32 +2217,32 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2023-02-03T06:58:15+00:00" }, { "name": "sebastian/code-unit", - "version": "1.0.8", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -2335,7 +2265,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" }, "funding": [ { @@ -2343,32 +2273,32 @@ "type": "github" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2023-02-03T06:58:43+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -2390,7 +2320,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" }, "funding": [ { @@ -2398,34 +2328,36 @@ "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2023-02-03T06:59:15+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", + "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2464,7 +2396,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" }, "funding": [ { @@ -2472,33 +2404,33 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2023-02-03T07:07:16+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6", + "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" + "nikic/php-parser": "^4.10", + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -2521,7 +2453,7 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0" }, "funding": [ { @@ -2529,33 +2461,33 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2023-02-03T06:59:47+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3", + "phpunit/phpunit": "^10.0", "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2587,7 +2519,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" }, "funding": [ { @@ -2595,27 +2528,27 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2023-05-01T07:48:21+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951", + "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-posix": "*" @@ -2623,7 +2556,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2642,7 +2575,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -2650,7 +2583,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.0.1" }, "funding": [ { @@ -2658,34 +2592,34 @@ "type": "github" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2023-04-11T05:39:26+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", + "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2727,7 +2661,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" }, "funding": [ { @@ -2735,38 +2669,35 @@ "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2023-02-03T07:06:49+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.5", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "reference": "aab257c712de87b90194febd52e4d184551c2d44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/aab257c712de87b90194febd52e4d184551c2d44", + "reference": "aab257c712de87b90194febd52e4d184551c2d44", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2791,7 +2722,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.0" }, "funding": [ { @@ -2799,33 +2730,33 @@ "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2023-02-03T07:07:38+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130", + "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" + "nikic/php-parser": "^4.10", + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -2848,7 +2779,7 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0" }, "funding": [ { @@ -2856,34 +2787,34 @@ "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2023-02-03T07:08:02+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2905,7 +2836,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, "funding": [ { @@ -2913,32 +2844,32 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2023-02-03T07:08:32+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -2960,7 +2891,7 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { @@ -2968,32 +2899,32 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -3023,62 +2954,7 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" }, "funding": [ { @@ -3086,32 +2962,32 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2023-02-03T07:05:40+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -3134,7 +3010,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { @@ -3142,29 +3018,29 @@ "type": "github" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -3187,7 +3063,7 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, "funding": [ { @@ -3195,7 +3071,7 @@ "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { "name": "seld/jsonlint", @@ -3382,16 +3258,16 @@ }, { "name": "symfony/console", - "version": "v6.2.8", + "version": "v6.2.10", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b" + "reference": "12288d9f4500f84a4d02254d4aa968b15488476f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3582d68a64a86ec25240aaa521ec8bc2342b369b", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b", + "url": "https://api.github.com/repos/symfony/console/zipball/12288d9f4500f84a4d02254d4aa968b15488476f", + "reference": "12288d9f4500f84a4d02254d4aa968b15488476f", "shasum": "" }, "require": { @@ -3458,7 +3334,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.8" + "source": "https://github.com/symfony/console/tree/v6.2.10" }, "funding": [ { @@ -3474,7 +3350,7 @@ "type": "tidelift" } ], - "time": "2023-03-29T21:42:15+00:00" + "time": "2023-04-28T13:37:43+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3545,16 +3421,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.2.7", + "version": "v6.2.10", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3" + "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894", + "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894", "shasum": "" }, "require": { @@ -3588,7 +3464,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.7" + "source": "https://github.com/symfony/filesystem/tree/v6.2.10" }, "funding": [ { @@ -3604,7 +3480,7 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2023-04-18T13:46:08+00:00" }, { "name": "symfony/finder", @@ -4069,16 +3945,16 @@ }, { "name": "symfony/process", - "version": "v6.2.8", + "version": "v6.2.10", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "75ed64103df4f6615e15a7fe38b8111099f47416" + "reference": "b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/75ed64103df4f6615e15a7fe38b8111099f47416", - "reference": "75ed64103df4f6615e15a7fe38b8111099f47416", + "url": "https://api.github.com/repos/symfony/process/zipball/b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e", + "reference": "b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e", "shasum": "" }, "require": { @@ -4110,7 +3986,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.2.8" + "source": "https://github.com/symfony/process/tree/v6.2.10" }, "funding": [ { @@ -4126,7 +4002,7 @@ "type": "tidelift" } ], - "time": "2023-03-09T16:20:02+00:00" + "time": "2023-04-18T13:56:57+00:00" }, { "name": "symfony/service-contracts", @@ -4301,16 +4177,16 @@ }, { "name": "symfony/var-dumper", - "version": "v6.2.8", + "version": "v6.2.10", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "d37ab6787be2db993747b6218fcc96e8e3bb4bd0" + "reference": "41a750a23412ca76fdbbf5096943b4134272c1ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d37ab6787be2db993747b6218fcc96e8e3bb4bd0", - "reference": "d37ab6787be2db993747b6218fcc96e8e3bb4bd0", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/41a750a23412ca76fdbbf5096943b4134272c1ab", + "reference": "41a750a23412ca76fdbbf5096943b4134272c1ab", "shasum": "" }, "require": { @@ -4369,7 +4245,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.2.8" + "source": "https://github.com/symfony/var-dumper/tree/v6.2.10" }, "funding": [ { @@ -4385,7 +4261,7 @@ "type": "tidelift" } ], - "time": "2023-03-29T21:42:15+00:00" + "time": "2023-04-18T13:46:08+00:00" }, { "name": "theseer/tokenizer", diff --git a/phpunit.xml b/phpunit.xml index d1f3d3f..60d4068 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,25 +1,26 @@ + cacheDirectory=".phpunit.cache" + beStrictAboutCoverageMetadata="true" +> ./tests - - + + ./src - + + ./src/Attributes + + diff --git a/src/Attributes/ConvertArray.php b/src/Attributes/ConvertArray.php index 15e5512..6d4a6dc 100644 --- a/src/Attributes/ConvertArray.php +++ b/src/Attributes/ConvertArray.php @@ -6,6 +6,7 @@ use Attribute; + /** * An attribute for properties that are an array that allows you to specify the type of element * @@ -19,6 +20,5 @@ final class ConvertArray */ public function __construct( public string $type - ) { - } + ) {} } diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index 2858b88..f82a771 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -2,6 +2,7 @@ namespace ClassTransformer\CacheGenerator; +use ClassTransformer\ClassTransformerConfig; use RuntimeException; use ReflectionException; use ReflectionNamedType; @@ -12,45 +13,31 @@ * Class CacheGenerator * * @template TClass + * @psalm-api + * @psalm-immutable */ class CacheGenerator { - /** @param class-string $class */ + /** @psalm-param class-string $class */ private string $class; /** - * @param class-string $class + * @param class-string $class */ public function __construct(string $class) { $this->class = $class; } - /** - * @return bool - */ - public function cacheExists(): bool - { - $class = str_replace('\\', '_', $this->class); - return file_exists('./.cache/' . $class . '.cache.php'); - } - /** * @return array * @throws ReflectionException */ public function generate(): array { - if ( - !file_exists(__DIR__ . '/../../.cache') && - !mkdir($concurrentDirectory = __DIR__ . '/../../.cache', 0777, true) && - !is_dir($concurrentDirectory) - ) { - throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); - } - + $this->makeCacheDir(ClassTransformerConfig::$cachePath); $class = str_replace('\\', '_', $this->class); - $path = __DIR__ . '/../../.cache/' . $class . '.cache.php'; + $path = ClassTransformerConfig::$cachePath . '/' . $class . '.cache.php'; if (file_exists($path)) { unlink($path); @@ -59,7 +46,7 @@ public function generate(): array $cache = [ 'properties' => [] ]; - + $refInstance = new PhpReflectionClass($this->class); $properties = $refInstance->getProperties(); @@ -107,6 +94,36 @@ public function generate(): array public function get(): array { $class = str_replace('\\', '_', $this->class); - return require __DIR__ . '/../../.cache/' . $class . '.cache.php'; + return require ClassTransformerConfig::$cachePath . '/' . $class . '.cache.php'; + } + + /** + * @return bool + */ + public function cacheExists(): bool + { + $class = str_replace('\\', '_', $this->class); + return file_exists('./.cache/' . $class . '.cache.php'); + } + + + /** + * @param string|null $path + * + * @return void + */ + private function makeCacheDir(?string $path): void + { + $concurrentDirectory = ClassTransformerConfig::$cachePath; + if ( + empty($path) || + ( + !file_exists($concurrentDirectory) && + !mkdir($concurrentDirectory, 0777, true) && + !is_dir($concurrentDirectory) + ) + ) { + throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); + } } } diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 8669d61..78fd956 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -21,7 +21,7 @@ final class ClassTransformer * @template TClass * * @param class-string $className - * @param iterable ...$args + * @param iterable|object ...$args * * @return null|TClass * @throws ClassNotFoundException diff --git a/src/ClassTransformerConfig.php b/src/ClassTransformerConfig.php index 6a012f1..5f84944 100644 --- a/src/ClassTransformerConfig.php +++ b/src/ClassTransformerConfig.php @@ -13,4 +13,5 @@ final class ClassTransformerConfig { /** @var bool */ public static bool $cache = false; + public static string $cachePath = __DIR__ . '/../.cache'; } diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 1a7eb95..f690f13 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -21,9 +21,6 @@ final class GenericInstance /** @var ArgumentsResource $argumentsResource */ private ArgumentsResource $argumentsResource; - /** @var T $genericInstance */ - private $genericInstance; - /** * @param ReflectionClass $class @@ -34,8 +31,6 @@ public function __construct(ReflectionClass $class, ArgumentsResource $arguments $this->class = $class; $this->argumentsResource = $argumentsResource; - - $this->genericInstance = new ($class->getClass()); } /** @@ -45,6 +40,8 @@ public function __construct(ReflectionClass $class, ArgumentsResource $arguments public function transform(): mixed { $properties = $this->class->getProperties(); + /** @var T $genericInstance */ + $genericInstance = new ($this->class->getClass()); foreach ($properties as $property) { try { @@ -54,13 +51,13 @@ public function transform(): mixed } if ($property->hasSetMutator()) { - $this->genericInstance->{TransformUtils::mutationSetterToCamelCase($property->getName())}($value); + $genericInstance->{TransformUtils::mutationSetterToCamelCase($property->getName())}($value); continue; } $caster = new ValueCasting($property); - $this->genericInstance->{$property->getName()} = $caster->castAttribute($value); + $genericInstance->{$property->getName()} = $caster->castAttribute($value); } - return $this->genericInstance; + return $genericInstance; } } diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 16af95c..983f8ca 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -40,7 +40,7 @@ public function __construct(string $class) } /** - * @return ReflectionProperty[] + * @return CacheReflectionProperty[] * @throws ReflectionException */ public function getProperties(): array diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 900c300..4041cd6 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -108,7 +108,6 @@ public function getAttributeArguments(?string $name = null): ?array */ public function getDocComment(): bool|string { - return $this->docComment ?? false; + return !empty($this->docComment) ? $this->docComment : false; } - } diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index 711179c..70b915f 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -38,7 +38,7 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref /** @var bool */ public bool $isScalar; - /** @var array>> */ + /** @var array>>> */ private static array $attributeTypesCache = []; /** @var array>> */ diff --git a/src/ValueCasting.php b/src/ValueCasting.php index f2001e1..d307a2f 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -44,7 +44,7 @@ public function castAttribute(mixed $value): mixed return $this->castArray($value); } - if ($this->property->isEnum() && (is_string($value) || is_int($value))) { + if ((is_string($value) || is_int($value)) && $this->property->isEnum()) { return $this->castEnum($value); } @@ -102,7 +102,9 @@ private function castEnum(int|string $value): mixed /** @var \BackedEnum $propertyClass */ return $propertyClass::from($value); } - - return constant($propertyClass . '::' . $value); + if (is_string($propertyClass) && is_string($value)) { + return constant($propertyClass . '::' . $value); + } + return $value; } } diff --git a/tests/Integration/AfterTransformTest.php b/tests/Integration/AfterTransformTest.php index 52509b1..c0ddcd4 100644 --- a/tests/Integration/AfterTransformTest.php +++ b/tests/Integration/AfterTransformTest.php @@ -16,6 +16,7 @@ class AfterTransformTest extends TestCase { use FakerData; + /** * @throws ClassNotFoundException */ diff --git a/tests/Integration/ClassTransformerExceptionsTest.php b/tests/Integration/ClassTransformerExceptionsTest.php index 9c8880a..d0c7ff2 100644 --- a/tests/Integration/ClassTransformerExceptionsTest.php +++ b/tests/Integration/ClassTransformerExceptionsTest.php @@ -4,20 +4,20 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\Exceptions\ClassNotFoundException; use PHPUnit\Framework\TestCase; +use ClassTransformer\ClassTransformer; use Tests\Integration\DTO\FakeClassDTO; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class ClassTransformerExceptionsTest + * * @package Tests */ class ClassTransformerExceptionsTest extends TestCase { use FakerData; - - + public function testClassNotFound(): void { $this->expectException(ClassNotFoundException::class); diff --git a/tests/Integration/ClassTransformerFromArrayTest.php b/tests/Integration/ClassTransformerFromArrayTest.php index eee802b..c2472e6 100644 --- a/tests/Integration/ClassTransformerFromArrayTest.php +++ b/tests/Integration/ClassTransformerFromArrayTest.php @@ -4,16 +4,18 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\Exceptions\ClassNotFoundException; +use ReflectionException; use PHPUnit\Framework\TestCase; -use Tests\Integration\DTO\ArrayScalarDTO; +use Tests\Integration\DTO\UserDTO; use Tests\Integration\DTO\BasketDTO; use Tests\Integration\DTO\ProductDTO; use Tests\Integration\DTO\PurchaseDTO; -use Tests\Integration\DTO\UserDTO; +use ClassTransformer\ClassTransformer; +use Tests\Integration\DTO\ArrayScalarDTO; use Tests\Integration\DTO\UserEmptyTypeDTO; -use function \count; +use ClassTransformer\Exceptions\ClassNotFoundException; + +use function count; /** * Class ClassTransformerTest diff --git a/tests/Integration/ClassTransformerFromCacheTest.php b/tests/Integration/ClassTransformerFromCacheTest.php index 5e34a47..57328bd 100644 --- a/tests/Integration/ClassTransformerFromCacheTest.php +++ b/tests/Integration/ClassTransformerFromCacheTest.php @@ -4,16 +4,13 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\ClassTransformerConfig; -use ClassTransformer\Exceptions\ClassNotFoundException; use PHPUnit\Framework\TestCase; -use ReflectionException; -use Tests\Integration\DTO\BasketDTO; +use Tests\Integration\DTO\UserDTO; use Tests\Integration\DTO\ProductDTO; use Tests\Integration\DTO\PurchaseDTO; -use Tests\Integration\DTO\UserDTO; -use Tests\Integration\DTO\UserEmptyTypeDTO; +use ClassTransformer\ClassTransformer; +use ClassTransformer\ClassTransformerConfig; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class ClassTransformerTest @@ -25,15 +22,18 @@ class ClassTransformerFromCacheTest extends TestCase use FakerData; /** - * @throws ReflectionException|ClassNotFoundException + * @throws ClassNotFoundException */ public function testRecursiveObject(): void { - $this->markTestSkipped('cache'); - return; - ClassTransformerConfig::$cache = true; + //$this->markTestSkipped('cache'); + //return; $data = $this->getRecursiveObject(); + $data->orders = $this->getArrayUsers(); + ClassTransformerConfig::$cache = true; $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); + ClassTransformerConfig::$cache = false; + self::assertInstanceOf(PurchaseDTO::class, $purchaseDTO); self::assertInstanceOf(UserDTO::class, $purchaseDTO->user); self::assertEquals($data->user->id, $purchaseDTO->user->id); @@ -42,6 +42,7 @@ public function testRecursiveObject(): void self::assertIsInt($purchaseDTO->user->id); self::assertIsString($purchaseDTO->user->email); self::assertIsFloat($purchaseDTO->user->balance); + foreach ($purchaseDTO->products as $key => $product) { self::assertInstanceOf(ProductDTO::class, $product); self::assertEquals($data->products[$key]->id, $product->id); diff --git a/tests/Integration/ClassTransformerFromObjectTest.php b/tests/Integration/ClassTransformerFromObjectTest.php index 1ba7a94..830d535 100644 --- a/tests/Integration/ClassTransformerFromObjectTest.php +++ b/tests/Integration/ClassTransformerFromObjectTest.php @@ -4,15 +4,15 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\Exceptions\ClassNotFoundException; -use PHPUnit\Framework\TestCase; use ReflectionException; +use PHPUnit\Framework\TestCase; +use Tests\Integration\DTO\UserDTO; use Tests\Integration\DTO\BasketDTO; use Tests\Integration\DTO\ProductDTO; +use ClassTransformer\ClassTransformer; use Tests\Integration\DTO\PurchaseDTO; -use Tests\Integration\DTO\UserDTO; use Tests\Integration\DTO\UserEmptyTypeDTO; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class ClassTransformerTest diff --git a/tests/Integration/CustomSetterAttributeTest.php b/tests/Integration/CustomSetterAttributeTest.php index 07ab07d..7512684 100644 --- a/tests/Integration/CustomSetterAttributeTest.php +++ b/tests/Integration/CustomSetterAttributeTest.php @@ -4,19 +4,21 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\Exceptions\ClassNotFoundException; -use PHPUnit\Framework\TestCase; use ReflectionException; +use PHPUnit\Framework\TestCase; +use ClassTransformer\ClassTransformer; use Tests\Integration\DTO\CustomSetterAttibuteDTO; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class CustomSetterAttributeTest + * * @package Tests */ class CustomSetterAttributeTest extends TestCase { use FakerData; + /** * @throws ReflectionException|ClassNotFoundException */ diff --git a/tests/Integration/CustomTransformerTest.php b/tests/Integration/CustomTransformerTest.php index 5e4dd4d..463d8ef 100644 --- a/tests/Integration/CustomTransformerTest.php +++ b/tests/Integration/CustomTransformerTest.php @@ -4,13 +4,13 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\Exceptions\ClassNotFoundException; -use PHPUnit\Framework\TestCase; use ReflectionException; +use PHPUnit\Framework\TestCase; +use ClassTransformer\ClassTransformer; +use Tests\Integration\DTO\UserNoTypeArrayDTO; use Tests\Integration\DTO\CustomTransformUserDTO; use Tests\Integration\DTO\CustomTransformUserDTOArray; -use Tests\Integration\DTO\UserNoTypeArrayDTO; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class CustomTransformerTest diff --git a/tests/Integration/DTO/PurchaseDTO.php b/tests/Integration/DTO/PurchaseDTO.php index 748c1e7..3696953 100644 --- a/tests/Integration/DTO/PurchaseDTO.php +++ b/tests/Integration/DTO/PurchaseDTO.php @@ -13,4 +13,7 @@ class PurchaseDTO /** @var UserDTO $user */ public UserDTO $user; + + /** @var array $orders Order list */ + public array $clients; } diff --git a/tests/Integration/DTO/UserCacheableDTO.php b/tests/Integration/DTO/UserCacheableDTO.php new file mode 100644 index 0000000..f0083c7 --- /dev/null +++ b/tests/Integration/DTO/UserCacheableDTO.php @@ -0,0 +1,29 @@ + $orders Order list */ + public array $orders; + + #[WritingStyle()] + public ?string $addressOne; + + #[WritingStyle(WritingStyle::STYLE_CAMEL_CASE)] + public ?string $address_two; + + #[WritingStyle(WritingStyle::STYLE_SNAKE_CASE)] + public ?string $addressThree; + + #[WritingStyle(WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_CAMEL_CASE)] + public float $balance; +} diff --git a/tests/Integration/EnumTest.php b/tests/Integration/EnumTest.php index 0b5c0c0..7e1ec9d 100644 --- a/tests/Integration/EnumTest.php +++ b/tests/Integration/EnumTest.php @@ -26,7 +26,6 @@ public function testEmptyWritingStyle(): void { if (PHP_MAJOR_VERSION < 8 || (PHP_MAJOR_VERSION === 8 && PHP_MINOR_VERSION < 1)) { $this->markTestSkipped('Php version mismatch'); - return; } $data = [ diff --git a/tests/Integration/NotTransformTest.php b/tests/Integration/NotTransformTest.php index 5a88a6e..97933ba 100644 --- a/tests/Integration/NotTransformTest.php +++ b/tests/Integration/NotTransformTest.php @@ -4,12 +4,12 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\Exceptions\ClassNotFoundException; -use PHPUnit\Framework\TestCase; use ReflectionException; +use PHPUnit\Framework\TestCase; +use ClassTransformer\ClassTransformer; use Tests\Integration\DTO\UserNotTransformDTO; use Tests\Integration\DTO\UserNotTransformRelationDTO; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class UnionTypeTest diff --git a/tests/Integration/UnionTypeTest.php b/tests/Integration/UnionTypeTest.php index 32364bb..6284019 100644 --- a/tests/Integration/UnionTypeTest.php +++ b/tests/Integration/UnionTypeTest.php @@ -4,11 +4,11 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\Exceptions\ClassNotFoundException; -use PHPUnit\Framework\TestCase; use ReflectionException; +use PHPUnit\Framework\TestCase; +use ClassTransformer\ClassTransformer; use Tests\Integration\DTO\UnionTypeDTO; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class UnionTypeTest diff --git a/tests/Integration/WritingStyleTest.php b/tests/Integration/WritingStyleTest.php index 0146dde..b2b0e2e 100644 --- a/tests/Integration/WritingStyleTest.php +++ b/tests/Integration/WritingStyleTest.php @@ -4,17 +4,18 @@ namespace Tests\Integration; -use ClassTransformer\ClassTransformer; -use ClassTransformer\Exceptions\ClassNotFoundException; -use PHPUnit\Framework\TestCase; use ReflectionException; +use PHPUnit\Framework\TestCase; +use ClassTransformer\ClassTransformer; use Tests\Integration\DTO\WithAliasDTO; -use Tests\Integration\DTO\WritingStyleCamelCaseDTO; use Tests\Integration\DTO\WritingStyleEmpyDTO; +use Tests\Integration\DTO\WritingStyleCamelCaseDTO; use Tests\Integration\DTO\WritingStyleSnakeCaseDTO; +use ClassTransformer\Exceptions\ClassNotFoundException; /** * Class UnionTypeTest + * * @package Tests */ class WritingStyleTest extends TestCase @@ -34,9 +35,9 @@ public function testEmptyWritingStyle(): void self::assertTrue(!isset($model->contactFio)); self::assertTrue(!isset($model->contactEmail)); } - + /** - * @throws ReflectionException|ClassNotFoundException + * @throws ClassNotFoundException */ public function testSnakeCaseTransform(): void { diff --git a/tests/Units/CacheGeneratorTest.php b/tests/Units/CacheGeneratorTest.php new file mode 100644 index 0000000..b22d664 --- /dev/null +++ b/tests/Units/CacheGeneratorTest.php @@ -0,0 +1,73 @@ +expectException(RuntimeException::class); + $class = new ReflectionClass(CacheGenerator::class); + $method = $class->getMethod('makeCacheDir'); + $method->setAccessible(true); + $method->invokeArgs(new CacheGenerator(UserCacheableDTO::class), [null]); + } + + public function testGenerateCache(): void + { + $cacheGenerator = new CacheGenerator(UserCacheableDTO::class); + + $class = str_replace('\\', '_', UserCacheableDTO::class); + $this->assertFileDoesNotExist(ClassTransformerConfig::$cachePath . '/' . $class . '.cache.php'); + + $this->assertFalse($cacheGenerator->cacheExists()); + $cacheGenerator->generate(); + $this->assertFileExists(ClassTransformerConfig::$cachePath . '/' . $class . '.cache.php'); + $cache = $cacheGenerator->get(); + + $this->assertIsArray($cache); + $this->assertArrayHasKey('properties', $cache); + + $this->assertEquals(UserCacheableDTO::class, $cache['properties'][0]['class']); + $this->assertEquals('id', $cache['properties'][0]['name']); + $this->assertEquals('int', $cache['properties'][0]['type']); + $this->assertTrue($cache['properties'][0]['isScalar']); + $this->assertFalse($cache['properties'][0]['hasSetMutator']); + $this->assertFalse($cache['properties'][0]['isArray']); + $this->assertFalse($cache['properties'][0]['isEnum']); + $this->assertFalse($cache['properties'][0]['notTransform']); + $this->assertFalse($cache['properties'][0]['docComment']); + + $cacheGenerator->generate(); + } +} diff --git a/tests/Units/CacheReflectionClassTest.php b/tests/Units/CacheReflectionClassTest.php new file mode 100644 index 0000000..69769a9 --- /dev/null +++ b/tests/Units/CacheReflectionClassTest.php @@ -0,0 +1,50 @@ +getProperties(); + + $this->assertEquals(UserCacheableDTO::class, $reflectionProperties[0]->class); + $this->assertEquals('id', $reflectionProperties[0]->name); + $this->assertEquals('int', $reflectionProperties[0]->type); + $this->assertTrue($reflectionProperties[0]->isScalar()); + $this->assertFalse($reflectionProperties[0]->hasSetMutator()); + $this->assertFalse($reflectionProperties[0]->isArray()); + $this->assertFalse($reflectionProperties[0]->isEnum()); + $this->assertFalse($reflectionProperties[0]->notTransform()); + $this->assertEmpty($reflectionProperties[0]->getDocComment()); + $this->assertEmpty($reflectionProperties[0]->getAttribute('addressThree')); + } +} diff --git a/tests/Units/DTO/UserCacheableDTO.php b/tests/Units/DTO/UserCacheableDTO.php new file mode 100644 index 0000000..732badf --- /dev/null +++ b/tests/Units/DTO/UserCacheableDTO.php @@ -0,0 +1,29 @@ + $orders Order list */ + public array $orders; + + #[WritingStyle()] + public ?string $addressOne; + + #[WritingStyle(WritingStyle::STYLE_CAMEL_CASE)] + public ?string $address_two; + + #[WritingStyle(WritingStyle::STYLE_SNAKE_CASE)] + public ?string $addressThree; + + #[WritingStyle(WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_CAMEL_CASE)] + public float $balance; +} From ae5a92bcb152ed7b42a5822ca484c32f474806e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 11:07:00 +0300 Subject: [PATCH 060/100] add infection --- .gitignore | 13 +- ...4b10aba5a9ca641a0132be8ff3ddc49245e390.xml | 2 - composer.json | 44 +- composer.lock | 1084 ++++++++++++++--- infection.json.dist | 17 + phpunit.xml | 2 +- psalm.xml | 21 +- src/ArgumentsResource.php | 2 + src/CacheGenerator/CacheGenerator.php | 86 +- src/ClassTransformer.php | 27 +- src/ClassTransformerConfig.php | 3 +- src/Contracts/ClassTransformable.php | 17 - src/Contracts/ReflectionProperty.php | 12 +- src/GenericInstance.php | 6 +- src/Reflection/CacheReflectionClass.php | 30 +- src/Reflection/CacheReflectionProperty.php | 16 +- src/Reflection/RuntimeReflectionClass.php | 15 +- src/Reflection/RuntimeReflectionProperty.php | 32 +- src/TransformUtils.php | 4 +- src/Validators/ClassExistsValidator.php | 2 + src/ValueCasting.php | 17 +- tests/Benchmark/FullCheckBench.php | 6 +- tests/Benchmark/SerializeVsArrayBench.php | 132 ++ tests/ClearCache.php | 40 + .../ClassTransformerFromArrayTest.php | 26 + .../ClassTransformerFromCacheTest.php | 11 +- tests/Integration/DTO/ConstructDto.php | 21 + tests/Integration/DTO/EmptyClassDto.php | 9 + tests/Integration/DTO/UnionTypeDTO.php | 12 - tests/Integration/DTO/UserDTO.php | 1 + tests/Integration/FakerData.php | 4 +- tests/Integration/UnionTypeTest.php | 57 - tests/Units/ArgumentsResourceTest.php | 20 +- tests/Units/CacheGeneratorTest.php | 52 +- tests/Units/CacheReflectionClassTest.php | 10 +- tests/Units/DTO/ColorEnum.php | 13 + tests/Units/DTO/ExtendedDto.php | 53 + tests/Units/DTO/UserCacheableDTO.php | 2 + tests/Units/DTO/UserDTO.php | 8 + tests/Units/RuntimeReflectionPropertyTest.php | 27 + tests/Units/TransformUtilsTest.php | 6 +- tests/Units/ValueCastingTest.php | 97 ++ 42 files changed, 1660 insertions(+), 399 deletions(-) delete mode 100644 .phpbench/storage/7e7/4/a/134b10aba5a9ca641a0132be8ff3ddc49245e390.xml create mode 100644 infection.json.dist delete mode 100644 src/Contracts/ClassTransformable.php create mode 100644 tests/Benchmark/SerializeVsArrayBench.php create mode 100644 tests/ClearCache.php create mode 100644 tests/Integration/DTO/ConstructDto.php create mode 100644 tests/Integration/DTO/EmptyClassDto.php delete mode 100644 tests/Integration/DTO/UnionTypeDTO.php delete mode 100644 tests/Integration/UnionTypeTest.php create mode 100644 tests/Units/DTO/ColorEnum.php create mode 100644 tests/Units/DTO/ExtendedDto.php create mode 100644 tests/Units/RuntimeReflectionPropertyTest.php create mode 100644 tests/Units/ValueCastingTest.php diff --git a/.gitignore b/.gitignore index 0f89a30..2a3afbb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ -.DS_Store .idea -.cache -.psalm -.phpbench -vendor -.phpunit.result.cache -.phpunit.cache/ +.DS_Store +infection.json + +/.cache +/.tmp +/vendor diff --git a/.phpbench/storage/7e7/4/a/134b10aba5a9ca641a0132be8ff3ddc49245e390.xml b/.phpbench/storage/7e7/4/a/134b10aba5a9ca641a0132be8ff3ddc49245e390.xml deleted file mode 100644 index 594f679..0000000 --- a/.phpbench/storage/7e7/4/a/134b10aba5a9ca641a0132be8ff3ddc49245e390.xml +++ /dev/null @@ -1,2 +0,0 @@ - -DarwinMacBook-Pro-Andrej.local22.3.0Darwin Kernel Version 22.3.0: Mon Jan 30 20:38:37 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T6000arm648.2.3/opt/homebrew/etc/php/8.2/php.iniCore, date, libxml, openssl, pcre, sqlite3, zlib, bcmath, bz2, calendar, ctype, curl, dba, dom, hash, FFI, fileinfo, filter, ftp, gd, gettext, gmp, json, iconv, intl, SPL, ldap, mbstring, session, standard, odbc, pcntl, exif, mysqlnd, PDO, pdo_dblib, pdo_mysql, PDO_ODBC, pdo_pgsql, pdo_sqlite, pgsql, Phar, posix, pspell, random, readline, Reflection, mysqli, shmop, SimpleXML, soap, sockets, sodium, sysvmsg, sysvsem, sysvshm, tidy, tokenizer, xml, xmlreader, xmlwriter, xsl, zip, Zend OPcache1gitfeature/cachee40415279cf2cc695e7fff34a29dd16d08158d540.012159347534180.185012817382812.6462078094482 diff --git a/composer.json b/composer.json index c89e9e2..277c004 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,10 @@ "source": "https://github.com/yzen-dev/plain-to-class" }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "infection/extension-installer": true + } }, "autoload": { "psr-4": { @@ -33,40 +36,25 @@ "php": "^8.0" }, "require-dev": { + "infection/infection": "^0.27.0", "mockery/mockery": "1.5.1", "phpbench/phpbench": "1.2.8", - "phpunit/phpunit": "10.1.2", + "phpunit/phpunit": "10.2.1", "squizlabs/php_codesniffer": "3.7.1", - "vimeo/psalm": "^5.9", - "symfony/var-dumper": "^6.2" + "symfony/var-dumper": "6.3", + "vimeo/psalm": "5.12.0" }, "bin": [ "bin/plain-to-class-clear" ], "scripts": { - "phpunit": [ - "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml" - ], - "psalm": [ - "./vendor/bin/psalm --show-info=true" - ], - "phpcs": [ - "./vendor/bin/phpcs --standard=./phpcs.xml -n --no-cache -s" - ], - "phpcbf": [ - "./vendor/bin/phpcbf --standard=./phpcs.xml -n --no-cache -s" - ], - "coverage": [ - "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml --coverage-text --colors=never --coverage-clover coverage.xml" - ], - "coverage-html": [ - "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml --coverage-html public/coverage --coverage-text" - ] - }, - "scripts-descriptions": { - "phpunit": "Run tests", - "coverage": "Checking code coverage", - "phpstan": "Run static analyze", - "phpcs": "Checking codestyle" + "phpunit": "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml", + "infection": "./vendor/bin/infection", + "infection-html": "./vendor/bin/infection --logger-html='.tmp/mutation-report.html'", + "psalm": "./vendor/bin/psalm --show-info=true", + "phpcs": "./vendor/bin/phpcs --standard=./phpcs.xml -n --no-cache -s", + "phpcbf": "./vendor/bin/phpcbf --standard=./phpcs.xml -n --no-cache -s", + "coverage": "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml --coverage-text --colors=never --coverage-clover coverage.xml", + "coverage-html": "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml --coverage-html public/coverage --coverage-text" } } diff --git a/composer.lock b/composer.lock index c15fd0d..0548925 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "09bdbc60bf7ad325d910687cc8948cfa", + "content-hash": "b7082f94604d41724aec9e4261228201", "packages": [], "packages-dev": [ { @@ -173,6 +173,97 @@ ], "time": "2021-03-30T17:13:30+00:00" }, + { + "name": "colinodell/json5", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/colinodell/json5.git", + "reference": "15b063f8cb5e6deb15f0cd39123264ec0d19c710" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinodell/json5/zipball/15b063f8cb5e6deb15f0cd39123264ec0d19c710", + "reference": "15b063f8cb5e6deb15f0cd39123264ec0d19c710", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1.3|^8.0" + }, + "conflict": { + "scrutinizer/ocular": "1.7.*" + }, + "require-dev": { + "mikehaertl/php-shellcommand": "^1.2.5", + "phpstan/phpstan": "^1.4", + "scrutinizer/ocular": "^1.6", + "squizlabs/php_codesniffer": "^2.3 || ^3.0", + "symfony/finder": "^4.4|^5.4|^6.0", + "symfony/phpunit-bridge": "^5.4|^6.0" + }, + "bin": [ + "bin/json5" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/global.php" + ], + "psr-4": { + "ColinODell\\Json5\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Developer" + } + ], + "description": "UTF-8 compatible JSON5 parser for PHP", + "homepage": "https://github.com/colinodell/json5", + "keywords": [ + "JSON5", + "json", + "json5_decode", + "json_decode" + ], + "support": { + "issues": "https://github.com/colinodell/json5/issues", + "source": "https://github.com/colinodell/json5/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://www.patreon.com/colinodell", + "type": "patreon" + } + ], + "time": "2022-12-27T16:44:40+00:00" + }, { "name": "composer/pcre", "version": "3.1.0", @@ -506,25 +597,29 @@ }, { "name": "doctrine/deprecations", - "version": "v1.0.0", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", "shasum": "" }, "require": { - "php": "^7.1|^8.0" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -543,9 +638,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" }, - "time": "2022-05-02T15:47:09+00:00" + "time": "2023-06-03T09:27:29+00:00" }, { "name": "doctrine/lexer", @@ -837,6 +932,385 @@ }, "time": "2020-07-09T08:09:16+00:00" }, + { + "name": "infection/abstract-testframework-adapter", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/infection/abstract-testframework-adapter.git", + "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/abstract-testframework-adapter/zipball/18925e20d15d1a5995bb85c9dc09e8751e1e069b", + "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.8", + "friendsofphp/php-cs-fixer": "^2.17", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Infection\\AbstractTestFramework\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Abstract Test Framework Adapter for Infection", + "support": { + "issues": "https://github.com/infection/abstract-testframework-adapter/issues", + "source": "https://github.com/infection/abstract-testframework-adapter/tree/0.5.0" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-08-17T18:49:12+00:00" + }, + { + "name": "infection/extension-installer", + "version": "0.1.2", + "source": { + "type": "git", + "url": "https://github.com/infection/extension-installer.git", + "reference": "9b351d2910b9a23ab4815542e93d541e0ca0cdcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/extension-installer/zipball/9b351d2910b9a23ab4815542e93d541e0ca0cdcf", + "reference": "9b351d2910b9a23ab4815542e93d541e0ca0cdcf", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1 || ^2.0" + }, + "require-dev": { + "composer/composer": "^1.9 || ^2.0", + "friendsofphp/php-cs-fixer": "^2.18, <2.19", + "infection/infection": "^0.15.2", + "php-coveralls/php-coveralls": "^2.4", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.10", + "phpstan/phpstan-phpunit": "^0.12.6", + "phpstan/phpstan-strict-rules": "^0.12.2", + "phpstan/phpstan-webmozart-assert": "^0.12.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^4.8" + }, + "type": "composer-plugin", + "extra": { + "class": "Infection\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "Infection\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Infection Extension Installer", + "support": { + "issues": "https://github.com/infection/extension-installer/issues", + "source": "https://github.com/infection/extension-installer/tree/0.1.2" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-10-20T22:08:34+00:00" + }, + { + "name": "infection/include-interceptor", + "version": "0.2.5", + "source": { + "type": "git", + "url": "https://github.com/infection/include-interceptor.git", + "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/include-interceptor/zipball/0cc76d95a79d9832d74e74492b0a30139904bdf7", + "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7", + "shasum": "" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "infection/infection": "^0.15.0", + "phan/phan": "^2.4 || ^3", + "php-coveralls/php-coveralls": "^2.2", + "phpstan/phpstan": "^0.12.8", + "phpunit/phpunit": "^8.5", + "vimeo/psalm": "^3.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Infection\\StreamWrapper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Stream Wrapper: Include Interceptor. Allows to replace included (autoloaded) file with another one.", + "support": { + "issues": "https://github.com/infection/include-interceptor/issues", + "source": "https://github.com/infection/include-interceptor/tree/0.2.5" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-08-09T10:03:57+00:00" + }, + { + "name": "infection/infection", + "version": "0.27.0", + "source": { + "type": "git", + "url": "https://github.com/infection/infection.git", + "reference": "a9ff8171577d98b887d7f16428edd81ff69ce887" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/infection/zipball/a9ff8171577d98b887d7f16428edd81ff69ce887", + "reference": "a9ff8171577d98b887d7f16428edd81ff69ce887", + "shasum": "" + }, + "require": { + "colinodell/json5": "^2.2", + "composer-runtime-api": "^2.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "fidry/cpu-core-counter": "^0.4.0 || ^0.5.0", + "infection/abstract-testframework-adapter": "^0.5.0", + "infection/extension-installer": "^0.1.0", + "infection/include-interceptor": "^0.2.5", + "justinrainbow/json-schema": "^5.2.10", + "nikic/php-parser": "^4.15.1", + "ondram/ci-detector": "^4.1.0", + "php": "^8.1", + "sanmai/later": "^0.1.1", + "sanmai/pipeline": "^5.1 || ^6", + "sebastian/diff": "^3.0.2 || ^4.0 || ^5.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/process": "^5.4 || ^6.0", + "thecodingmachine/safe": "^2.1.2", + "webmozart/assert": "^1.11" + }, + "conflict": { + "antecedent/patchwork": "<2.1.25", + "dg/bypass-finals": "<1.4.1", + "phpunit/php-code-coverage": ">9,<9.1.4 || >9.2.17,<9.2.21" + }, + "require-dev": { + "brianium/paratest": "^6.3", + "ext-simplexml": "*", + "fidry/makefile": "^0.2.0", + "helmich/phpunit-json-assert": "^3.0", + "phpspec/prophecy": "^1.15", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.1.0", + "phpstan/phpstan": "^1.10.15", + "phpstan/phpstan-phpunit": "^1.0.0", + "phpstan/phpstan-strict-rules": "^1.1.0", + "phpstan/phpstan-webmozart-assert": "^1.0.2", + "phpunit/phpunit": "^9.5.5", + "rector/rector": "^0.16.0", + "sidz/phpstan-rules": "^0.2.1", + "symfony/phpunit-bridge": "^5.4 || ^6.0", + "symfony/yaml": "^5.4 || ^6.0", + "thecodingmachine/phpstan-safe-rule": "^1.2.0" + }, + "bin": [ + "bin/infection" + ], + "type": "library", + "autoload": { + "psr-4": { + "Infection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com", + "homepage": "https://twitter.com/maks_rafalko" + }, + { + "name": "Oleg Zhulnev", + "homepage": "https://github.com/sidz" + }, + { + "name": "Gert de Pagter", + "homepage": "https://github.com/BackEndTea" + }, + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com", + "homepage": "https://twitter.com/tfidry" + }, + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com", + "homepage": "https://www.alexeykopytko.com" + }, + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Infection is a Mutation Testing framework for PHP. The mutation adequacy score can be used to measure the effectiveness of a test set in terms of its ability to detect faults.", + "keywords": [ + "coverage", + "mutant", + "mutation framework", + "mutation testing", + "testing", + "unit testing" + ], + "support": { + "issues": "https://github.com/infection/infection/issues", + "source": "https://github.com/infection/infection/tree/0.27.0" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2023-05-16T05:28:04+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "5.2.12", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12" + }, + "time": "2022-04-13T08:02:27+00:00" + }, { "name": "mockery/mockery", "version": "1.5.1", @@ -1021,16 +1495,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.15.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -1071,9 +1545,87 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2023-05-19T20:20:00+00:00" + }, + { + "name": "ondram/ci-detector", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/OndraM/ci-detector.git", + "reference": "8a4b664e916df82ff26a44709942dfd593fa6f30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/OndraM/ci-detector/zipball/8a4b664e916df82ff26a44709942dfd593fa6f30", + "reference": "8a4b664e916df82ff26a44709942dfd593fa6f30", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.2", + "lmc/coding-standard": "^1.3 || ^2.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0.5", + "phpstan/phpstan": "^0.12.58", + "phpstan/phpstan-phpunit": "^0.12.16", + "phpunit/phpunit": "^7.1 || ^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "OndraM\\CiDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ondřej Machulda", + "email": "ondrej.machulda@gmail.com" + } + ], + "description": "Detect continuous integration environment and provide unified access to properties of current build", + "keywords": [ + "CircleCI", + "Codeship", + "Wercker", + "adapter", + "appveyor", + "aws", + "aws codebuild", + "azure", + "azure devops", + "azure pipelines", + "bamboo", + "bitbucket", + "buddy", + "ci-info", + "codebuild", + "continuous integration", + "continuousphp", + "devops", + "drone", + "github", + "gitlab", + "interface", + "jenkins", + "pipelines", + "sourcehut", + "teamcity", + "travis" + ], + "support": { + "issues": "https://github.com/OndraM/ci-detector/issues", + "source": "https://github.com/OndraM/ci-detector/tree/4.1.0" + }, + "time": "2021-04-14T09:16:52+00:00" }, { "name": "phar-io/manifest", @@ -1490,16 +2042,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.7.1", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714" + "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d", + "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d", "shasum": "" }, "require": { @@ -1542,28 +2094,30 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2" }, - "time": "2023-03-27T19:02:04+00:00" + "time": "2023-05-30T18:13:47+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.20.3", + "version": "1.22.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2" + "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6c04009f6cae6eda2f040745b6b846080ef069c2", - "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", + "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", @@ -1587,22 +2141,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.3" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.0" }, - "time": "2023-04-25T09:01:03+00:00" + "time": "2023-06-01T12:35:21+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.1", + "version": "10.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "884a0da7f9f46f28b2cb69134217fd810b793974" + "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/884a0da7f9f46f28b2cb69134217fd810b793974", - "reference": "884a0da7f9f46f28b2cb69134217fd810b793974", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", + "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", "shasum": "" }, "require": { @@ -1659,7 +2213,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.1" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2" }, "funding": [ { @@ -1667,20 +2221,20 @@ "type": "github" } ], - "time": "2023-04-17T12:15:40+00:00" + "time": "2023-05-22T09:04:27+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.0.1", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd" + "reference": "5647d65443818959172645e7ed999217360654b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/fd9329ab3368f59fe1fe808a189c51086bd4b6bd", - "reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/5647d65443818959172645e7ed999217360654b6", + "reference": "5647d65443818959172645e7ed999217360654b6", "shasum": "" }, "require": { @@ -1719,7 +2273,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.1" + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.2" }, "funding": [ { @@ -1727,7 +2282,7 @@ "type": "github" } ], - "time": "2023-02-10T16:53:14+00:00" + "time": "2023-05-07T09:13:23+00:00" }, { "name": "phpunit/php-invoker", @@ -1912,16 +2467,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.1.2", + "version": "10.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "6f0cd95be71add539f8fd2be25b2a4a29789000b" + "reference": "599b33294350e8f51163119d5670512f98b0490d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6f0cd95be71add539f8fd2be25b2a4a29789000b", - "reference": "6f0cd95be71add539f8fd2be25b2a4a29789000b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/599b33294350e8f51163119d5670512f98b0490d", + "reference": "599b33294350e8f51163119d5670512f98b0490d", "shasum": "" }, "require": { @@ -1961,7 +2516,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "10.2-dev" } }, "autoload": { @@ -1993,7 +2548,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.1.2" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.1" }, "funding": [ { @@ -2009,7 +2564,7 @@ "type": "tidelift" } ], - "time": "2023-04-22T07:38:19+00:00" + "time": "2023-06-05T05:15:51+00:00" }, { "name": "psr/cache", @@ -2163,6 +2718,129 @@ }, "time": "2021-07-14T16:46:02+00:00" }, + { + "name": "sanmai/later", + "version": "0.1.2", + "source": { + "type": "git", + "url": "https://github.com/sanmai/later.git", + "reference": "9b659fecef2030193fd02402955bc39629d5606f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sanmai/later/zipball/9b659fecef2030193fd02402955bc39629d5606f", + "reference": "9b659fecef2030193fd02402955bc39629d5606f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.13", + "infection/infection": ">=0.10.5", + "phan/phan": ">=2", + "php-coveralls/php-coveralls": "^2.0", + "phpstan/phpstan": ">=0.10", + "phpunit/phpunit": ">=7.4", + "vimeo/psalm": ">=2" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Later\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com" + } + ], + "description": "Later: deferred wrapper object", + "support": { + "issues": "https://github.com/sanmai/later/issues", + "source": "https://github.com/sanmai/later/tree/0.1.2" + }, + "funding": [ + { + "url": "https://github.com/sanmai", + "type": "github" + } + ], + "time": "2021-01-02T10:26:44+00:00" + }, + { + "name": "sanmai/pipeline", + "version": "v6.7", + "source": { + "type": "git", + "url": "https://github.com/sanmai/pipeline.git", + "reference": "0e5c45c8046298212347a0bfb659126af8e75d2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sanmai/pipeline/zipball/0e5c45c8046298212347a0bfb659126af8e75d2e", + "reference": "0e5c45c8046298212347a0bfb659126af8e75d2e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.8", + "friendsofphp/php-cs-fixer": "^3", + "infection/infection": ">=0.10.5", + "league/pipeline": "^0.3 || ^1.0", + "phan/phan": ">=1.1", + "php-coveralls/php-coveralls": "^2.4.1", + "phpstan/phpstan": ">=0.10", + "phpunit/phpunit": ">=9.4", + "vimeo/psalm": ">=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "v6.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Pipeline\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com" + } + ], + "description": "General-purpose collections pipeline", + "support": { + "issues": "https://github.com/sanmai/pipeline/issues", + "source": "https://github.com/sanmai/pipeline/tree/v6.7" + }, + "funding": [ + { + "url": "https://github.com/sanmai", + "type": "github" + } + ], + "time": "2023-04-29T11:21:51+00:00" + }, { "name": "sebastian/cli-parser", "version": "2.0.0", @@ -3075,16 +3753,16 @@ }, { "name": "seld/jsonlint", - "version": "1.9.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "4211420d25eba80712bff236a98960ef68b866b7" + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/4211420d25eba80712bff236a98960ef68b866b7", - "reference": "4211420d25eba80712bff236a98960ef68b866b7", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/594fd6462aad8ecee0b45ca5045acea4776667f1", + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1", "shasum": "" }, "require": { @@ -3123,7 +3801,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.9.0" + "source": "https://github.com/Seldaek/jsonlint/tree/1.10.0" }, "funding": [ { @@ -3135,20 +3813,20 @@ "type": "tidelift" } ], - "time": "2022-04-01T13:37:23+00:00" + "time": "2023-05-11T13:16:46+00:00" }, { "name": "spatie/array-to-xml", - "version": "3.1.5", + "version": "3.1.6", "source": { "type": "git", "url": "https://github.com/spatie/array-to-xml.git", - "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43" + "reference": "e210b98957987c755372465be105d32113f339a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/13f76acef5362d15c71ae1ac6350cc3df5e25e43", - "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/e210b98957987c755372465be105d32113f339a4", + "reference": "e210b98957987c755372465be105d32113f339a4", "shasum": "" }, "require": { @@ -3186,7 +3864,7 @@ "xml" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/3.1.5" + "source": "https://github.com/spatie/array-to-xml/tree/3.1.6" }, "funding": [ { @@ -3198,7 +3876,7 @@ "type": "github" } ], - "time": "2022-12-24T13:43:51+00:00" + "time": "2023-05-11T14:04:07+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -3258,23 +3936,23 @@ }, { "name": "symfony/console", - "version": "v6.2.10", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "12288d9f4500f84a4d02254d4aa968b15488476f" + "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/12288d9f4500f84a4d02254d4aa968b15488476f", - "reference": "12288d9f4500f84a4d02254d4aa968b15488476f", + "url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", + "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", + "symfony/service-contracts": "^2.5|^3", "symfony/string": "^5.4|^6.0" }, "conflict": { @@ -3296,12 +3974,6 @@ "symfony/process": "^5.4|^6.0", "symfony/var-dumper": "^5.4|^6.0" }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, "type": "library", "autoload": { "psr-4": { @@ -3334,7 +4006,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.10" + "source": "https://github.com/symfony/console/tree/v6.3.0" }, "funding": [ { @@ -3350,20 +4022,20 @@ "type": "tidelift" } ], - "time": "2023-04-28T13:37:43+00:00" + "time": "2023-05-29T12:49:39+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", "shasum": "" }, "require": { @@ -3372,7 +4044,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -3401,7 +4073,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" }, "funding": [ { @@ -3417,20 +4089,20 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:25:55+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/filesystem", - "version": "v6.2.10", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894" + "reference": "97b698e1d77d356304def77a8d0cd73090b359ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894", - "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/97b698e1d77d356304def77a8d0cd73090b359ea", + "reference": "97b698e1d77d356304def77a8d0cd73090b359ea", "shasum": "" }, "require": { @@ -3464,7 +4136,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.10" + "source": "https://github.com/symfony/filesystem/tree/v6.3.0" }, "funding": [ { @@ -3480,20 +4152,20 @@ "type": "tidelift" } ], - "time": "2023-04-18T13:46:08+00:00" + "time": "2023-05-30T17:12:32+00:00" }, { "name": "symfony/finder", - "version": "v6.2.7", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb" + "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/20808dc6631aecafbe67c186af5dcb370be3a0eb", - "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb", + "url": "https://api.github.com/repos/symfony/finder/zipball/d9b01ba073c44cef617c7907ce2419f8d00d75e2", + "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2", "shasum": "" }, "require": { @@ -3528,7 +4200,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.2.7" + "source": "https://github.com/symfony/finder/tree/v6.3.0" }, "funding": [ { @@ -3544,25 +4216,25 @@ "type": "tidelift" } ], - "time": "2023-02-16T09:57:23+00:00" + "time": "2023-04-02T01:25:41+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.2.7", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629" + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/aa0e85b53bbb2b4951960efd61d295907eacd629", - "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3" + "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -3595,7 +4267,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.2.7" + "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" }, "funding": [ { @@ -3611,7 +4283,7 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2023-05-12T14:21:09+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3945,16 +4617,16 @@ }, { "name": "symfony/process", - "version": "v6.2.10", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e" + "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e", - "reference": "b34cdbc9c5e75d45a3703e63a48ad07aafa8bf2e", + "url": "https://api.github.com/repos/symfony/process/zipball/8741e3ed7fe2e91ec099e02446fb86667a0f1628", + "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628", "shasum": "" }, "require": { @@ -3986,7 +4658,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.2.10" + "source": "https://github.com/symfony/process/tree/v6.3.0" }, "funding": [ { @@ -4002,20 +4674,20 @@ "type": "tidelift" } ], - "time": "2023-04-18T13:56:57+00:00" + "time": "2023-05-19T08:06:44+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.2.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a" + "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", "shasum": "" }, "require": { @@ -4025,13 +4697,10 @@ "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -4071,7 +4740,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" }, "funding": [ { @@ -4087,20 +4756,20 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/string", - "version": "v6.2.8", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" + "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", + "url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f", + "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f", "shasum": "" }, "require": { @@ -4111,13 +4780,13 @@ "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", "symfony/intl": "^6.2", - "symfony/translation-contracts": "^2.0|^3.0", + "symfony/translation-contracts": "^2.5|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, "type": "library", @@ -4157,7 +4826,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.8" + "source": "https://github.com/symfony/string/tree/v6.3.0" }, "funding": [ { @@ -4173,20 +4842,20 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:06:02+00:00" + "time": "2023-03-21T21:06:29+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.2.10", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "41a750a23412ca76fdbbf5096943b4134272c1ab" + "reference": "6acdcd5c122074ee9f7b051e4fb177025c277a0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/41a750a23412ca76fdbbf5096943b4134272c1ab", - "reference": "41a750a23412ca76fdbbf5096943b4134272c1ab", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6acdcd5c122074ee9f7b051e4fb177025c277a0e", + "reference": "6acdcd5c122074ee9f7b051e4fb177025c277a0e", "shasum": "" }, "require": { @@ -4194,7 +4863,6 @@ "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "phpunit/phpunit": "<5.4.3", "symfony/console": "<5.4" }, "require-dev": { @@ -4204,11 +4872,6 @@ "symfony/uid": "^5.4|^6.0", "twig/twig": "^2.13|^3.0.4" }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" - }, "bin": [ "Resources/bin/var-dump-server" ], @@ -4245,7 +4908,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.2.10" + "source": "https://github.com/symfony/var-dumper/tree/v6.3.0" }, "funding": [ { @@ -4261,7 +4924,146 @@ "type": "tidelift" } ], - "time": "2023-04-18T13:46:08+00:00" + "time": "2023-05-25T13:09:35+00:00" + }, + { + "name": "thecodingmachine/safe", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/3115ecd6b4391662b4931daac4eba6b07a2ac1f0", + "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.2", + "thecodingmachine/phpstan-strict-rules": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "files": [ + "deprecated/apc.php", + "deprecated/array.php", + "deprecated/datetime.php", + "deprecated/libevent.php", + "deprecated/misc.php", + "deprecated/password.php", + "deprecated/mssql.php", + "deprecated/stats.php", + "deprecated/strings.php", + "lib/special_cases.php", + "deprecated/mysqli.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "deprecated/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v2.5.0" + }, + "time": "2023-04-05T11:54:14+00:00" }, { "name": "theseer/tokenizer", @@ -4315,16 +5117,16 @@ }, { "name": "vimeo/psalm", - "version": "5.9.0", + "version": "5.12.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "8b9ad1eb9e8b7d3101f949291da2b9f7767cd163" + "reference": "f90118cdeacd0088e7215e64c0c99ceca819e176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/8b9ad1eb9e8b7d3101f949291da2b9f7767cd163", - "reference": "8b9ad1eb9e8b7d3101f949291da2b9f7767cd163", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/f90118cdeacd0088e7215e64c0c99ceca819e176", + "reference": "f90118cdeacd0088e7215e64c0c99ceca819e176", "shasum": "" }, "require": { @@ -4415,9 +5217,9 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.9.0" + "source": "https://github.com/vimeo/psalm/tree/5.12.0" }, - "time": "2023-03-29T21:38:21+00:00" + "time": "2023-05-22T21:19:03+00:00" }, { "name": "webmozart/assert", diff --git a/infection.json.dist b/infection.json.dist new file mode 100644 index 0000000..55412af --- /dev/null +++ b/infection.json.dist @@ -0,0 +1,17 @@ +{ + "source": { + "directories": [ + "src" + ] + }, + "logs": { + "text": "php:\/\/stderr", + "stryker": { + "report": "master" + } + }, + "mutators": { + "@default": true, + "PublicVisibility": false + } +} diff --git a/phpunit.xml b/phpunit.xml index 60d4068..9350ff0 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ beStrictAboutOutputDuringTests="true" failOnRisky="true" failOnWarning="true" - cacheDirectory=".phpunit.cache" + cacheDirectory=".tmp/.phpunit/" beStrictAboutCoverageMetadata="true" > diff --git a/psalm.xml b/psalm.xml index 22b7de8..22ce9d0 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,12 +1,22 @@ @@ -14,4 +24,11 @@ + + + + + + + diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index 401dbaf..d30c6a7 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -1,5 +1,7 @@ $class */ + private const DIR_PERMISSION = 0777; + + /** @psalm-param class-string $class */ private string $class; /** - * @param class-string $class + * @param class-string $class */ public function __construct(string $class) { @@ -30,24 +35,19 @@ public function __construct(string $class) } /** - * @return array - * @throws ReflectionException + * @psalm-suppress UnusedMethodCall + * @return array + * @throws ReflectionException|RuntimeException */ public function generate(): array { $this->makeCacheDir(ClassTransformerConfig::$cachePath); $class = str_replace('\\', '_', $this->class); - $path = ClassTransformerConfig::$cachePath . '/' . $class . '.cache.php'; + $path = ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'; - if (file_exists($path)) { - unlink($path); - } + $cache = ['properties' => []]; - $cache = [ - 'properties' => [] - ]; - - $refInstance = new PhpReflectionClass($this->class); + $refInstance = new ReflectionClass($this->class); $properties = $refInstance->getProperties(); @@ -58,43 +58,52 @@ public function generate(): array } else { $type = $property->type; } - $attrs = $property->property->getAttributes(); - $attributes = []; - if (!empty($attrs)) { - foreach ($attrs as $attr) { - $attributes[$attr->getName()] = $attr->getArguments(); - } - } - $cache['properties'][] = [ - 'class' => $property->class, - 'name' => $property->name, - 'type' => $type, - 'types' => $property->types, - 'isScalar' => $property->isScalar, - 'hasSetMutator' => $property->hasSetMutator(), - 'isArray' => $property->isArray(), - 'isEnum' => $property->isEnum(), - 'notTransform' => $property->notTransform(), - 'transformable' => $property->transformable(), - 'docComment' => $property->getDocComment(), - 'attributes' => $attributes, - ]; + + $cache['properties'][] = new CacheReflectionProperty( + $property->class, + $property->name, + (string)$type, + $property->types, + $property->isScalar, + $property->hasSetMutator(), + $property->isArray(), + $property->isEnum(), + $property->notTransform(), + $property->transformable(), + $property->getDocComment(), + $this->getArguments($property), + ); } file_put_contents( $path, - '> + */ + private function getArguments(RuntimeReflectionProperty $property): array + { + $attrs = $property->property->getAttributes(); + $attributes = []; + foreach ($attrs as $attr) { + $attributes[$attr->getName()] = $attr->getArguments(); + } + return $attributes; + } + /** * @return array */ public function get(): array { $class = str_replace('\\', '_', $this->class); - return require ClassTransformerConfig::$cachePath . '/' . $class . '.cache.php'; + return unserialize(file_get_contents(ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php')); } /** @@ -111,6 +120,7 @@ public function cacheExists(): bool * @param string|null $path * * @return void + * @throws RuntimeException */ private function makeCacheDir(?string $path): void { @@ -119,7 +129,7 @@ private function makeCacheDir(?string $path): void empty($path) || ( !file_exists($concurrentDirectory) && - !mkdir($concurrentDirectory, 0777, true) && + !mkdir($concurrentDirectory, self::DIR_PERMISSION, true) && !is_dir($concurrentDirectory) ) ) { diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 78fd956..4fc7bb2 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -1,5 +1,7 @@ $className + * @param class-string $className * @param iterable|object ...$args * - * @return null|TClass + * @return null|T * @throws ClassNotFoundException */ - public static function transform(string $className, ...$args) + public static function transform(string $className, ...$args): mixed { new ClassExistsValidator($className); - + if (method_exists($className, 'transform')) { $instance = new $className(); $instance->transform(...$args); @@ -40,7 +41,7 @@ public static function transform(string $className, ...$args) $reflection = new RuntimeReflectionClass($className); } $generic = new GenericInstance($reflection, new ArgumentsResource(...$args)); - /** @var TClass $instance */ + /** @var T $instance */ $instance = $generic->transform(); } @@ -52,12 +53,10 @@ public static function transform(string $className, ...$args) } /** - * @template TClass - * - * @param class-string $className + * @param class-string $className * @param array> $args * - * @return null|array|array + * @return null|array|array * @throws ClassNotFoundException */ public static function transformCollection(string $className, array $args): ?array @@ -70,12 +69,10 @@ public static function transformCollection(string $className, array $args): ?arr } /** - * @template TClass - * - * @param array> $className + * @param array> $className * @param array> $args * - * @return null|array|array + * @return null|array|array * @throws ClassNotFoundException */ public static function transformMultiple(array $className, array $args): ?array diff --git a/src/ClassTransformerConfig.php b/src/ClassTransformerConfig.php index 5f84944..341a534 100644 --- a/src/ClassTransformerConfig.php +++ b/src/ClassTransformerConfig.php @@ -1,8 +1,9 @@ $args - * - * @return TClass - */ - public function transform(...$args): mixed; -} diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index 2086e4d..582a39d 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -22,6 +22,11 @@ public function transformable(): false|string; */ public function getName(): string; + /** + * @return string + */ + public function getType(): ?string; + /** * @param string $name * @@ -39,7 +44,7 @@ public function getAttributeArguments(string $name): ?array; /** * @return bool|string */ - public function getDocComment(): bool|string; + public function getDocComment(): string; /** * Finds whether a variable is an enum @@ -59,4 +64,9 @@ public function isArray(): bool; * @return bool */ public function hasSetMutator(): bool; + + /** + * @return bool + */ + public function notTransform(): bool; } diff --git a/src/GenericInstance.php b/src/GenericInstance.php index f690f13..0fbf495 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -1,9 +1,11 @@ $class */ + /** @var class-string $class */ private string $class; /** @@ -28,7 +29,7 @@ final class CacheReflectionClass implements ReflectionClass /** - * @param class-string $class + * @param class-string $class * * @throws ClassNotFoundException */ @@ -51,31 +52,14 @@ public function getProperties(): array $cache = new CacheGenerator($this->class); + /** @var CacheReflectionClass $class */ if (!$cache->cacheExists()) { $class = $cache->generate(); } else { $class = $cache->get(); } - $properties = array_map( - static fn($item) => new CacheReflectionProperty( - $item['class'], - $item['name'], - $item['type'], - $item['types'], - $item['isScalar'], - $item['hasSetMutator'], - $item['isArray'], - $item['isEnum'], - $item['notTransform'], - $item['transformable'], - $item['docComment'], - $item['attributes'], - ), - $class['properties'] - ); - - return self::$propertiesTypesCache[$this->class] = $properties; + return self::$propertiesTypesCache[$this->class] = $class['properties']; } /** diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 4041cd6..00c8c1d 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -1,5 +1,7 @@ type; + } + /** * @return bool */ @@ -104,10 +114,10 @@ public function getAttributeArguments(?string $name = null): ?array } /** - * @return bool|string + * @return string */ - public function getDocComment(): bool|string + public function getDocComment(): string { - return !empty($this->docComment) ? $this->docComment : false; + return $this->docComment; } } diff --git a/src/Reflection/RuntimeReflectionClass.php b/src/Reflection/RuntimeReflectionClass.php index cbf2da2..383842f 100644 --- a/src/Reflection/RuntimeReflectionClass.php +++ b/src/Reflection/RuntimeReflectionClass.php @@ -1,9 +1,10 @@ $class */ + /** @var class-string $class */ private string $class; /** - * @var array + * @var array */ private static $propertiesTypesCache = []; /** - * @param class-string $class + * @param class-string $class * * @throws ClassNotFoundException */ @@ -38,7 +39,7 @@ public function __construct(string $class) } /** - * @return \ReflectionProperty[] + * @return RuntimeReflectionProperty[] * @throws \ReflectionException */ public function getProperties(): array @@ -54,7 +55,7 @@ public function getProperties(): array foreach ($properties as $item) { $result [] = new RuntimeReflectionProperty($item); } - + return static::$propertiesTypesCache[$this->class] = $result; } diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index 70b915f..e3d3904 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -1,11 +1,12 @@ isScalar = sizeof(array_intersect($this->types, ['int', 'float', 'double', 'string', 'bool', 'mixed'])) > 0; } + /** + * @return null|string + */ + public function getType(): ?string + { + if ($this->type instanceof ReflectionNamedType) { + return $this->type->getName(); + } + return $this->type; + } + /** * @return bool */ @@ -85,11 +97,6 @@ private function initTypes(): array } $types = []; - if ($this->type instanceof ReflectionUnionType) { - foreach ($this->type->getTypes() as $type) { - $types [] = $type->getName(); - } - } if ($this->type instanceof ReflectionNamedType) { $types = [$this->type->getName()]; @@ -114,9 +121,10 @@ public function isArray(): bool /** */ - public function getDocComment(): bool|string + public function getDocComment(): string { - return $this->property->getDocComment(); + $doc = $this->property->getDocComment(); + return $doc !== false ? $doc : ''; } /** @@ -128,7 +136,7 @@ public function notTransform(): bool } /** - * @param class-string|null $name + * @param class-string|null $name * * @template T * @return null|ReflectionAttribute @@ -173,7 +181,7 @@ public function isScalar(): bool } /** - * @return false|class-string + * @return false|non-empty-string */ public function transformable(): false|string { diff --git a/src/TransformUtils.php b/src/TransformUtils.php index 76c34a9..852b5d0 100644 --- a/src/TransformUtils.php +++ b/src/TransformUtils.php @@ -1,5 +1,7 @@ /m', $phpDoc, $arrayType); + preg_match('/array<([a-zA-Z\d\\\]+)>/', $phpDoc, $arrayType); return $arrayType[1] ?? null; } return null; diff --git a/src/Validators/ClassExistsValidator.php b/src/Validators/ClassExistsValidator.php index 82d2549..e2a79c0 100644 --- a/src/Validators/ClassExistsValidator.php +++ b/src/Validators/ClassExistsValidator.php @@ -1,5 +1,7 @@ property->isScalar() || $this->property->notTransform()) { - return $value; + return match ($this->property->getType()) { + 'string' => (string)$value, + 'int' => (int)$value, + 'float' => (float)$value, + 'bool' => (bool)$value, + default => $value + }; } if ($this->property->isArray()) { @@ -66,6 +74,7 @@ public function castAttribute(mixed $value): mixed private function castArray($value): mixed { $arrayTypeAttr = $this->property->getAttributeArguments(ConvertArray::class); + if ($arrayTypeAttr !== null && isset($arrayTypeAttr[0])) { $arrayType = $arrayTypeAttr[0]; } else { @@ -75,17 +84,15 @@ private function castArray($value): mixed if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { return $value; } - - if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'mixed'])) { + if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'boolean', 'mixed'])) { return array_map(static fn($el) => ClassTransformer::transform($arrayType, $el), $value); - } return array_map(static fn($el) => match ($arrayType) { 'string' => (string)$el, 'int' => (int)$el, 'float' => (float)$el, - 'bool' => (bool)$el, + 'bool', 'boolean' => (bool)$el, default => $el }, $value); } diff --git a/tests/Benchmark/FullCheckBench.php b/tests/Benchmark/FullCheckBench.php index 433da5e..dbed71e 100644 --- a/tests/Benchmark/FullCheckBench.php +++ b/tests/Benchmark/FullCheckBench.php @@ -2,14 +2,14 @@ namespace Tests\Benchmark; -use ClassTransformer\ClassTransformer; -use ClassTransformer\ClassTransformerConfig; use PHPUnit\Framework\TestCase; +use Tests\Benchmark\DTO\UserDto; use Tests\Benchmark\DTO\AddressDto; use Tests\Benchmark\DTO\ProductDto; use Tests\Benchmark\DTO\PurchaseDto; -use Tests\Benchmark\DTO\UserDto; use Tests\Benchmark\DTO\UserTypeEnum; +use ClassTransformer\ClassTransformer; +use ClassTransformer\ClassTransformerConfig; /** * Class CheckBench diff --git a/tests/Benchmark/SerializeVsArrayBench.php b/tests/Benchmark/SerializeVsArrayBench.php new file mode 100644 index 0000000..82ade5f --- /dev/null +++ b/tests/Benchmark/SerializeVsArrayBench.php @@ -0,0 +1,132 @@ + [] + ]; + + $refInstance = new \ReflectionClass(UserDto::class); + + $properties = $refInstance->getProperties(); + + foreach ($properties as $item) { + $property = new RuntimeReflectionProperty($item); + if ($property->type instanceof ReflectionNamedType) { + $type = $property->type->getName(); + } else { + $type = $property->type; + } + $attrs = $property->property->getAttributes(); + $attributes = []; + if (!empty($attrs)) { + foreach ($attrs as $attr) { + $attributes[$attr->getName()] = $attr->getArguments(); + } + } + $cache['properties'][] = new CacheReflectionProperty( + $property->class, + $property->name, + $type, + $property->types, + $property->isScalar, + $property->hasSetMutator(), + $property->isArray(), + $property->isEnum(), + $property->notTransform(), + $property->transformable(), + $property->getDocComment(), + $attributes, + ); + } + + file_put_contents( + $path, + serialize($cache) + ); + + } + public function benchArrayReflection(): void + { + $class = str_replace('\\', '_', UserDto::class); + $path = ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache-array.php'; + + if (file_exists($path)) { + unlink($path); + } + + $cache = [ + 'properties' => [] + ]; + + $refInstance = new \ReflectionClass(UserDto::class); + + $properties = $refInstance->getProperties(); + + foreach ($properties as $item) { + $property = new RuntimeReflectionProperty($item); + if ($property->type instanceof ReflectionNamedType) { + $type = $property->type->getName(); + } else { + $type = $property->type; + } + $attrs = $property->property->getAttributes(); + $attributes = []; + if (!empty($attrs)) { + foreach ($attrs as $attr) { + $attributes[$attr->getName()] = $attr->getArguments(); + } + } + $cache['properties'][] = [ + 'class' => $property->class, + 'name' => $property->name, + 'type' => $type, + 'types' => $property->types, + 'isScalar' => $property->isScalar, + 'hasSetMutator' => $property->hasSetMutator(), + 'isArray' => $property->isArray(), + 'isEnum' => $property->isEnum(), + 'notTransform' => $property->notTransform(), + 'transformable' => $property->transformable(), + 'docComment' => $property->getDocComment(), + 'attributes' => $attributes, + ]; + } + + file_put_contents( + $path, + 'deleteDir(__DIR__ . '/../.cache'); + } else { + if (file_exists(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache')) { + $this->deleteDir(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache'); + } + } + } + + private function deleteDir($dirPath) + { + if (!is_dir($dirPath)) { + throw new InvalidArgumentException("$dirPath must be a directory"); + } + if (!str_ends_with($dirPath, '/')) { + $dirPath .= '/'; + } + $files = glob($dirPath . '*', GLOB_MARK); + + foreach ($files as $file) { + if (is_dir($file)) { + $this->deleteDir($file); + } else { + unlink($file); + } + } + rmdir($dirPath); + } + +} diff --git a/tests/Integration/ClassTransformerFromArrayTest.php b/tests/Integration/ClassTransformerFromArrayTest.php index c2472e6..e1d2225 100644 --- a/tests/Integration/ClassTransformerFromArrayTest.php +++ b/tests/Integration/ClassTransformerFromArrayTest.php @@ -6,6 +6,8 @@ use ReflectionException; use PHPUnit\Framework\TestCase; +use Tests\Integration\DTO\ConstructDto; +use Tests\Integration\DTO\EmptyClassDto; use Tests\Integration\DTO\UserDTO; use Tests\Integration\DTO\BasketDTO; use Tests\Integration\DTO\ProductDTO; @@ -37,9 +39,33 @@ public function testBaseArray(): void self::assertEquals($data['id'], $userDTO->id); self::assertEquals($data['email'], $userDTO->email); self::assertEquals($data['balance'], $userDTO->balance); + self::assertEquals($data['isBlocked'], $userDTO->isBlocked); self::assertIsInt($userDTO->id); self::assertIsString($userDTO->email); self::assertIsFloat($userDTO->balance); + self::assertIsBool($userDTO->isBlocked); + } + + /*public function testConstructFormatArray(): void + { + $data = $this->getBaseArrayData(); + $userDTO = ClassTransformer::transform(ConstructDto::class, $data); + self::assertInstanceOf(UserDTO::class, $userDTO); + self::assertEquals($data['id'], $userDTO->id); + self::assertEquals($data['email'], $userDTO->email); + self::assertEquals($data['balance'], $userDTO->balance); + self::assertIsInt($userDTO->id); + self::assertIsString($userDTO->email); + self::assertIsFloat($userDTO->balance); + }*/ + + /** + */ + public function testEmptyClass(): void + { + $data = $this->getBaseArrayData(); + $instance = ClassTransformer::transform(EmptyClassDto::class, $data); + self::assertInstanceOf(EmptyClassDto::class, $instance); } /** diff --git a/tests/Integration/ClassTransformerFromCacheTest.php b/tests/Integration/ClassTransformerFromCacheTest.php index 57328bd..8c2985e 100644 --- a/tests/Integration/ClassTransformerFromCacheTest.php +++ b/tests/Integration/ClassTransformerFromCacheTest.php @@ -5,6 +5,7 @@ namespace Tests\Integration; use PHPUnit\Framework\TestCase; +use Tests\ClearCache; use Tests\Integration\DTO\UserDTO; use Tests\Integration\DTO\ProductDTO; use Tests\Integration\DTO\PurchaseDTO; @@ -19,7 +20,7 @@ */ class ClassTransformerFromCacheTest extends TestCase { - use FakerData; + use FakerData, ClearCache; /** * @throws ClassNotFoundException @@ -33,7 +34,7 @@ public function testRecursiveObject(): void ClassTransformerConfig::$cache = true; $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); ClassTransformerConfig::$cache = false; - + self::assertInstanceOf(PurchaseDTO::class, $purchaseDTO); self::assertInstanceOf(UserDTO::class, $purchaseDTO->user); self::assertEquals($data->user->id, $purchaseDTO->user->id); @@ -53,4 +54,10 @@ public function testRecursiveObject(): void self::assertIsFloat($product->price); } } + + protected function tearDown(): void + { + $this->clearCache(); + } + } diff --git a/tests/Integration/DTO/ConstructDto.php b/tests/Integration/DTO/ConstructDto.php new file mode 100644 index 0000000..09cb496 --- /dev/null +++ b/tests/Integration/DTO/ConstructDto.php @@ -0,0 +1,21 @@ + 1, 'email' => 'fake@mail.com', - 'balance' => 128.41 + 'balance' => 128.41, + 'isBlocked' => false ]; } @@ -63,6 +64,7 @@ public function getBaseObject(): \stdClass $data->id = 1; $data->email = 'fake@mail.com'; $data->balance = 128.43; + $data->isBlocked = false; return $data; } diff --git a/tests/Integration/UnionTypeTest.php b/tests/Integration/UnionTypeTest.php deleted file mode 100644 index 6284019..0000000 --- a/tests/Integration/UnionTypeTest.php +++ /dev/null @@ -1,57 +0,0 @@ - 1, - 'email' => 'test@mail.com', - 'balance' => 10.97, - ]; - $userDTO = ClassTransformer::transform(UnionTypeDTO::class, $data); - - self::assertInstanceOf(UnionTypeDTO::class, $userDTO); - - self::assertEquals($data['id'], $userDTO->id); - self::assertEquals($data['email'], $userDTO->email); - self::assertEquals($data['balance'], $userDTO->balance); - } - - /** - * @throws ReflectionException|ClassNotFoundException - */ - public function testCustomTransformUnion(): void - { - $data = [ - 'id' => '1', - 'email' => 'test@mail.com', - 'balance' => '10.97', - ]; - $userDTO = ClassTransformer::transform(UnionTypeDTO::class, $data); - - self::assertInstanceOf(UnionTypeDTO::class, $userDTO); - - self::assertEquals($data['id'], $userDTO->id); - self::assertEquals($data['email'], $userDTO->email); - self::assertEquals($data['balance'], $userDTO->balance); - } - -} diff --git a/tests/Units/ArgumentsResourceTest.php b/tests/Units/ArgumentsResourceTest.php index d0bfdd8..c7b89af 100644 --- a/tests/Units/ArgumentsResourceTest.php +++ b/tests/Units/ArgumentsResourceTest.php @@ -19,7 +19,7 @@ public function testBaseKey(): void ); $this->assertEquals($data['id'], $value); } - + public function testBaseValueNotFoundException(): void { $this->expectException(ValueNotFoundException::class); @@ -48,6 +48,15 @@ public function testCamelCaseWritingStyleKey(): void new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'address_two')) ); $this->assertEquals($data['addressTwo'], $value); + + $data = ['test_case' => 'test2']; + + $this->expectException(ValueNotFoundException::class); + $resource2 = new ArgumentsResource($data); + + $resource2->getValue( + new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'testCase')) + ); } public function testSnakeCaseWritingStyleKey(): void @@ -58,6 +67,15 @@ public function testSnakeCaseWritingStyleKey(): void new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'addressThree')) ); $this->assertEquals($data['address_three'], $value); + + $data = ['testCase' => 'test2']; + + $this->expectException(ValueNotFoundException::class); + $resource2 = new ArgumentsResource($data); + + $resource2->getValue( + new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'test_case')) + ); } public function testFinalValueNotFoundException(): void diff --git a/tests/Units/CacheGeneratorTest.php b/tests/Units/CacheGeneratorTest.php index b22d664..9f88230 100644 --- a/tests/Units/CacheGeneratorTest.php +++ b/tests/Units/CacheGeneratorTest.php @@ -2,15 +2,20 @@ namespace Tests\Units; +use ClassTransformer\Attributes\WritingStyle; use ClassTransformer\CacheGenerator\CacheGenerator; use ClassTransformer\ClassTransformerConfig; +use ClassTransformer\Reflection\CacheReflectionProperty; use PHPUnit\Framework\TestCase; use ReflectionClass; use RuntimeException; +use Tests\ClearCache; +use Tests\Units\DTO\ColorEnum; use Tests\Units\DTO\UserCacheableDTO; class CacheGeneratorTest extends TestCase { + use ClearCache; protected function setUp(): void { parent::setUp(); @@ -24,7 +29,7 @@ protected function setUp(): void } $class = str_replace('\\', '_', UserCacheableDTO::class); - $path = __DIR__ . '/../../.cache' . '/' . $class . '.cache.php'; + $path = __DIR__ . '/../../.cache' . DIRECTORY_SEPARATOR . $class . '.cache.php'; if (file_exists($path)) { unlink($path); @@ -48,26 +53,47 @@ public function testGenerateCache(): void $cacheGenerator = new CacheGenerator(UserCacheableDTO::class); $class = str_replace('\\', '_', UserCacheableDTO::class); - $this->assertFileDoesNotExist(ClassTransformerConfig::$cachePath . '/' . $class . '.cache.php'); + $this->assertFileDoesNotExist(ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'); $this->assertFalse($cacheGenerator->cacheExists()); $cacheGenerator->generate(); - $this->assertFileExists(ClassTransformerConfig::$cachePath . '/' . $class . '.cache.php'); + $this->assertFileExists(ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'); + $this->assertTrue($cacheGenerator->cacheExists()); $cache = $cacheGenerator->get(); - $this->assertIsArray($cache); $this->assertArrayHasKey('properties', $cache); - $this->assertEquals(UserCacheableDTO::class, $cache['properties'][0]['class']); - $this->assertEquals('id', $cache['properties'][0]['name']); - $this->assertEquals('int', $cache['properties'][0]['type']); - $this->assertTrue($cache['properties'][0]['isScalar']); - $this->assertFalse($cache['properties'][0]['hasSetMutator']); - $this->assertFalse($cache['properties'][0]['isArray']); - $this->assertFalse($cache['properties'][0]['isEnum']); - $this->assertFalse($cache['properties'][0]['notTransform']); - $this->assertFalse($cache['properties'][0]['docComment']); + /** @var CacheReflectionProperty $property */ + $property = $cache['properties'][0]; + + $this->assertEquals(UserCacheableDTO::class, $property->class); + $this->assertEquals('id', $property->name); + $this->assertEquals('int', $property->type); + $this->assertTrue($property->isScalar); + $this->assertFalse($property->hasSetMutator); + $this->assertFalse($property->isArray); + $this->assertFalse($property->isEnum); + $this->assertFalse($property->notTransform); + $this->assertEmpty($property->docComment); + $this->assertIsArray($property->attributes); + $this->assertEmpty($property->attributes); + + $property = $cache['properties'][5]; + $this->assertIsArray($property->attributes); + $this->assertCount(1, $property->attributes); + $this->assertIsArray($property->attributes[WritingStyle::class]); + $this->assertEquals(WritingStyle::STYLE_CAMEL_CASE, $property->attributes[WritingStyle::class][0]); + + /** @var CacheReflectionProperty $property */ + $property = $cache['properties'][8]; + $this->assertIsString($property->getType()); + $this->assertEquals(ColorEnum::class, $property->getType()); $cacheGenerator->generate(); } + + protected function tearDown(): void + { + $this->clearCache(); + } } diff --git a/tests/Units/CacheReflectionClassTest.php b/tests/Units/CacheReflectionClassTest.php index 69769a9..157dcf1 100644 --- a/tests/Units/CacheReflectionClassTest.php +++ b/tests/Units/CacheReflectionClassTest.php @@ -6,11 +6,14 @@ use ClassTransformer\Reflection\CacheReflectionClass; use RuntimeException; use PHPUnit\Framework\TestCase; +use Tests\ClearCache; use Tests\Units\DTO\UserCacheableDTO; use ClassTransformer\CacheGenerator\CacheGenerator; class CacheReflectionClassTest extends TestCase { + use ClearCache; + protected function setUp(): void { parent::setUp(); @@ -24,7 +27,7 @@ protected function setUp(): void } $class = str_replace('\\', '_', UserCacheableDTO::class); - $path = ClassTransformerConfig::$cachePath . '/' . $class . '.cache.php'; + $path = ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'; if (file_exists($path)) { unlink($path); @@ -47,4 +50,9 @@ public function testGetCache(): void $this->assertEmpty($reflectionProperties[0]->getDocComment()); $this->assertEmpty($reflectionProperties[0]->getAttribute('addressThree')); } + + protected function tearDown(): void + { + $this->clearCache(); + } } diff --git a/tests/Units/DTO/ColorEnum.php b/tests/Units/DTO/ColorEnum.php new file mode 100644 index 0000000..a99c835 --- /dev/null +++ b/tests/Units/DTO/ColorEnum.php @@ -0,0 +1,13 @@ + */ + public array $intItems; + + /** @var array */ + public array $floatItems; + + /** @var array */ + public array $stringItems; + + /** @var array */ + public array $boolItems; + + /** @var array */ + public array $booleanItems; + + /** @var array */ + public array $mixedItems; + + public function setEmailAttribute($value) + { + $this->email = $value; + } +} diff --git a/tests/Units/DTO/UserCacheableDTO.php b/tests/Units/DTO/UserCacheableDTO.php index 732badf..efe3a5a 100644 --- a/tests/Units/DTO/UserCacheableDTO.php +++ b/tests/Units/DTO/UserCacheableDTO.php @@ -26,4 +26,6 @@ class UserCacheableDTO #[WritingStyle(WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_CAMEL_CASE)] public float $balance; + + public ColorEnum $color; } diff --git a/tests/Units/DTO/UserDTO.php b/tests/Units/DTO/UserDTO.php index 4ce4ffa..4dee6f4 100644 --- a/tests/Units/DTO/UserDTO.php +++ b/tests/Units/DTO/UserDTO.php @@ -22,4 +22,12 @@ class UserDTO #[WritingStyle(WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_CAMEL_CASE)] public float $balance; + + #[WritingStyle(WritingStyle::STYLE_CAMEL_CASE)] + public ?string $testCase; + + #[WritingStyle(WritingStyle::STYLE_SNAKE_CASE)] + public ?string $test_case; + + public ColorEnum $color; } diff --git a/tests/Units/RuntimeReflectionPropertyTest.php b/tests/Units/RuntimeReflectionPropertyTest.php new file mode 100644 index 0000000..3dde508 --- /dev/null +++ b/tests/Units/RuntimeReflectionPropertyTest.php @@ -0,0 +1,27 @@ +assertEquals('string', $property->getType()); + $this->assertTrue($property->isScalar()); + $this->assertTrue($property->hasSetMutator()); + $this->assertFalse($property->isEnum()); + $this->assertFalse($property->isArray()); + $this->assertEquals('email', $property->getName()); + + $property = new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'color')); + $this->assertTrue($property->isEnum()); + $this->assertEquals(ColorEnum::class, $property->getType()); + } +} diff --git a/tests/Units/TransformUtilsTest.php b/tests/Units/TransformUtilsTest.php index 01999fa..fa03e19 100644 --- a/tests/Units/TransformUtilsTest.php +++ b/tests/Units/TransformUtilsTest.php @@ -25,7 +25,7 @@ public function testAttributeToCamelCase(): void $camelCase = TransformUtils::attributeToCamelCase('example_snake_case'); $this->assertEquals('exampleSnakeCase', $camelCase); } - + public function testMutationSetterToCamelCase(): void { $setter = TransformUtils::mutationSetterToCamelCase('example'); @@ -34,10 +34,10 @@ public function testMutationSetterToCamelCase(): void $setter = TransformUtils::mutationSetterToCamelCase('example'); $this->assertEquals('setExampleAttribute', $setter); } - + public function testGetClassFromPhpDoc(): void { - $type = TransformUtils::getClassFromPhpDoc('/** @var array<\Tests\Integration\DTO\PurchaseDTO> $orders Order list */'); + $type = TransformUtils::getClassFromPhpDoc('/** ' . PHP_EOL . '* @var array<\Tests\Integration\DTO\PurchaseDTO> $orders Order list ' . PHP_EOL . ' */'); $this->assertEquals('\Tests\Integration\DTO\PurchaseDTO', $type); $type = TransformUtils::getClassFromPhpDoc('/** @var array<> $orders Order list */'); diff --git a/tests/Units/ValueCastingTest.php b/tests/Units/ValueCastingTest.php new file mode 100644 index 0000000..e8034b2 --- /dev/null +++ b/tests/Units/ValueCastingTest.php @@ -0,0 +1,97 @@ +castAttribute(2); + $this->assertEquals(2, $value); + + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'isBlocked')) + ); + $value = $caster->castAttribute(0); + $this->assertIsBool($value); + $this->assertFalse($value); + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'email')) + ); + $value = $caster->castAttribute(101010); + $this->assertIsString($value); + $this->assertEquals('101010', $value); + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'balance')) + ); + $value = $caster->castAttribute('12'); + $this->assertIsFloat($value); + $this->assertEquals(12.00, $value); + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'id')) + ); + $value = $caster->castAttribute('1'); + $this->assertIsInt($value); + $this->assertEquals(1, $value); + } + + public function testCreateArrayPropery(): void + { + // Array check + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'intItems')) + ); + $value = $caster->castAttribute(['1']); + $this->assertIsInt($value[0]); + $this->assertEquals(1, $value[0]); + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'floatItems')) + ); + $value = $caster->castAttribute(['10']); + $this->assertIsFloat($value[0]); + $this->assertEquals(10.0, $value[0]); + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'stringItems')) + ); + $value = $caster->castAttribute([10]); + $this->assertIsString($value[0]); + $this->assertEquals('10', $value[0]); + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'boolItems')) + ); + $value = $caster->castAttribute([0]); + $this->assertIsBool($value[0]); + $this->assertFalse($value[0]); + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'booleanItems')) + ); + $value = $caster->castAttribute([0]); + $this->assertIsBool($value[0]); + $this->assertFalse($value[0]); + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'mixedItems')) + ); + $value = $caster->castAttribute(['10']); + $this->assertIsString($value[0]); + $this->assertEquals('10', $value[0]); + } +} From fadb4e017a7ee102d26c0978f3f9a89e3bdecea3 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 12 Jun 2023 11:09:52 +0300 Subject: [PATCH 061/100] Update tests.yml --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 35b4bc6..7ecec8c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,7 +2,7 @@ name: Run tests on: push: - branches: [ master, dev, feature/cache-v2-php8.1 ] + branches: [ master, dev, v3.0.0-dev ] pull_request: branches: [ master, dev ] @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.1", "8.0"] + php: ["8.2", "8.1", "8.0"] dependency-version: [prefer-lowest, prefer-stable] name: P${{ matrix.php }} - ${{ matrix.dependency-version }} From af59a775b90a43b9d90c069f2bb2e95df6c14880 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 12 Jun 2023 11:24:17 +0300 Subject: [PATCH 062/100] Create mutation.yml --- .github/workflows/mutation.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/mutation.yml diff --git a/.github/workflows/mutation.yml b/.github/workflows/mutation.yml new file mode 100644 index 0000000..57eb226 --- /dev/null +++ b/.github/workflows/mutation.yml @@ -0,0 +1,31 @@ +name: mutation test + +on: + pull_request: + paths-ignore: + - 'docs/**' + - 'README.md' + - 'CHANGELOG.md' + - '.gitignore' + - '.gitattributes' + - 'psalm.xml' + + push: + paths-ignore: + - 'docs/**' + - 'README.md' + - 'CHANGELOG.md' + - '.gitignore' + - '.gitattributes' + - 'psalm.xml' + +jobs: + mutation: + uses: yiisoft/actions/.github/workflows/roave-infection.yml@master + with: + os: >- + ['ubuntu-latest'] + php: >- + ['8.2'] + secrets: + STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} From 3bfd624164eb071afef65ec28e6cd289da42b7d5 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 12 Jun 2023 11:30:28 +0300 Subject: [PATCH 063/100] Update mutation.yml --- .github/workflows/mutation.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.github/workflows/mutation.yml b/.github/workflows/mutation.yml index 57eb226..d40a9b6 100644 --- a/.github/workflows/mutation.yml +++ b/.github/workflows/mutation.yml @@ -2,22 +2,8 @@ name: mutation test on: pull_request: - paths-ignore: - - 'docs/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - 'psalm.xml' push: - paths-ignore: - - 'docs/**' - - 'README.md' - - 'CHANGELOG.md' - - '.gitignore' - - '.gitattributes' - - 'psalm.xml' jobs: mutation: From 7e08f8c3bc8fbb1fc25c47b24cc8da85fb8a8a18 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 12 Jun 2023 11:30:43 +0300 Subject: [PATCH 064/100] Update code-analyze.yml --- .github/workflows/code-analyze.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/code-analyze.yml b/.github/workflows/code-analyze.yml index ba860e0..9453ed4 100644 --- a/.github/workflows/code-analyze.yml +++ b/.github/workflows/code-analyze.yml @@ -2,9 +2,8 @@ name: Code analyze on: push: - branches: [ master, dev ] + pull_request: - branches: [ master, dev ] jobs: build: From 06261f6be917c397d21d253c01fff0944304a6f5 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 12 Jun 2023 11:30:59 +0300 Subject: [PATCH 065/100] Update tests.yml --- .github/workflows/tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7ecec8c..137ba46 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,9 +2,8 @@ name: Run tests on: push: - branches: [ master, dev, v3.0.0-dev ] + pull_request: - branches: [ master, dev ] jobs: tests: From 18f32d7350ac0ad00788c6af57e05d4b13863d33 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 12 Jun 2023 11:37:20 +0300 Subject: [PATCH 066/100] Update and rename code-analyze.yml to psalm.yml --- .github/workflows/{code-analyze.yml => psalm.yml} | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) rename .github/workflows/{code-analyze.yml => psalm.yml} (81%) diff --git a/.github/workflows/code-analyze.yml b/.github/workflows/psalm.yml similarity index 81% rename from .github/workflows/code-analyze.yml rename to .github/workflows/psalm.yml index 9453ed4..ff7cde6 100644 --- a/.github/workflows/code-analyze.yml +++ b/.github/workflows/psalm.yml @@ -1,4 +1,4 @@ -name: Code analyze +name: psalm on: push: @@ -31,9 +31,6 @@ jobs: if: steps.composer-cache.outputs.cache-hit != 'true' run: composer install --prefer-dist --no-progress --no-suggest - - name: Run phpstan - run: composer run-script phpstan - - - name: Check codestyle - run: composer run-script phpcs + - name: Static analysis. + run: vendor/bin/psalm --shepherd --stats --output-format=github --php-version=${{ matrix.php }} From 1f8dc3c975c3c344a973d6379fa4a3dd1f8cae7a Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 12 Jun 2023 11:39:32 +0300 Subject: [PATCH 067/100] Update psalm.yml --- .github/workflows/psalm.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index ff7cde6..f9c2293 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -6,7 +6,7 @@ on: pull_request: jobs: - build: + psalm: runs-on: ubuntu-latest @@ -17,7 +17,8 @@ jobs: - name: PHP setup uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php: >- + ['8.0', '8.1', '8.2'] - name: Cache Composer packages id: composer-cache From bb89c859ac11db76cf592f3cf15e6e6766a5db76 Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 12 Jun 2023 11:56:52 +0300 Subject: [PATCH 068/100] Update mutation.yml --- .github/workflows/mutation.yml | 41 +++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/.github/workflows/mutation.yml b/.github/workflows/mutation.yml index d40a9b6..7a83281 100644 --- a/.github/workflows/mutation.yml +++ b/.github/workflows/mutation.yml @@ -1,17 +1,38 @@ name: mutation test on: - pull_request: - push: + + pull_request: jobs: mutation: - uses: yiisoft/actions/.github/workflows/roave-infection.yml@master - with: - os: >- - ['ubuntu-latest'] - php: >- - ['8.2'] - secrets: - STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + # Setup PHP versions, run checks + - name: PHP setup + uses: shivammathur/setup-php@v2 + with: + php: >- + ['8.2'] + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Run mutation + run: ./vendor/bin/infection + env: + STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} From cef0ed1b0ccfff1ccaf6bc629630ac164eff9921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 11:57:35 +0300 Subject: [PATCH 069/100] upd infection --- README.md | 3 ++ composer.json | 6 +-- composer.lock | 115 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e8f0661..34787b8 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ ![Packagist Downloads](https://img.shields.io/packagist/dm/yzen.dev/plain-to-class) ![Packagist Downloads](https://img.shields.io/packagist/dt/yzen.dev/plain-to-class) +[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyzen-dev%2Fplain-to-class%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yzen-dev/plain-to-class/master) + + > Alas, I do not speak English, and the documentation was compiled through google translator :( I will be glad if you can help me describe the documentation more correctly :) This library will allow you to easily convert any data set into the object you need. You are not required to change the structure of classes, inherit them from external modules, etc. No dancing with tambourines - just data and the right class. diff --git a/composer.json b/composer.json index 277c004..5ebacc6 100644 --- a/composer.json +++ b/composer.json @@ -36,10 +36,10 @@ "php": "^8.0" }, "require-dev": { - "infection/infection": "^0.27.0", "mockery/mockery": "1.5.1", "phpbench/phpbench": "1.2.8", "phpunit/phpunit": "10.2.1", + "roave/infection-static-analysis-plugin": "^1.32", "squizlabs/php_codesniffer": "3.7.1", "symfony/var-dumper": "6.3", "vimeo/psalm": "5.12.0" @@ -49,8 +49,8 @@ ], "scripts": { "phpunit": "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml", - "infection": "./vendor/bin/infection", - "infection-html": "./vendor/bin/infection --logger-html='.tmp/mutation-report.html'", + "infection": "./vendor/bin/roave-infection-static-analysis-plugin", + "infection-html": "./vendor/bin/roave-infection-static-analysis-plugin --logger-html='.tmp/mutation-report.html'", "psalm": "./vendor/bin/psalm --show-info=true", "phpcs": "./vendor/bin/phpcs --standard=./phpcs.xml -n --no-cache -s", "phpcbf": "./vendor/bin/phpcbf --standard=./phpcs.xml -n --no-cache -s", diff --git a/composer.lock b/composer.lock index 0548925..41339d7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b7082f94604d41724aec9e4261228201", + "content-hash": "826b49d1956bb4698d53ea540d16223f", "packages": [], "packages-dev": [ { @@ -1549,6 +1549,68 @@ }, "time": "2023-05-19T20:20:00+00:00" }, + { + "name": "ocramius/package-versions", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/PackageVersions.git", + "reference": "065921ed7cb2a6861443d91138d0a4378316af8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/065921ed7cb2a6861443d91138d0a4378316af8d", + "reference": "065921ed7cb2a6861443d91138d0a4378316af8d", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2.0", + "php": "~8.1.0 || ~8.2.0" + }, + "replace": { + "composer/package-versions-deprecated": "*" + }, + "require-dev": { + "composer/composer": "^2.4.4", + "doctrine/coding-standard": "^10.0.0", + "ext-zip": "^1.15.0", + "phpunit/phpunit": "^9.5.26", + "roave/infection-static-analysis-plugin": "^1.25.0", + "vimeo/psalm": "^4.29.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/Ocramius/PackageVersions/issues", + "source": "https://github.com/Ocramius/PackageVersions/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ocramius/package-versions", + "type": "tidelift" + } + ], + "time": "2022-10-31T12:51:46+00:00" + }, { "name": "ondram/ci-detector", "version": "4.1.0", @@ -2718,6 +2780,57 @@ }, "time": "2021-07-14T16:46:02+00:00" }, + { + "name": "roave/infection-static-analysis-plugin", + "version": "1.32.0", + "source": { + "type": "git", + "url": "https://github.com/Roave/infection-static-analysis-plugin.git", + "reference": "885d4f82458e48a87a6b4b81cbb7e35e35234c8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/infection-static-analysis-plugin/zipball/885d4f82458e48a87a6b4b81cbb7e35e35234c8d", + "reference": "885d4f82458e48a87a6b4b81cbb7e35e35234c8d", + "shasum": "" + }, + "require": { + "infection/infection": "0.27.0", + "ocramius/package-versions": "^2.7.0", + "php": "~8.1.0 || ~8.2.0", + "sanmai/later": "^0.1.2", + "vimeo/psalm": "^4.30.0 || ^5.0.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "phpunit/phpunit": "^10.1.3" + }, + "bin": [ + "bin/roave-infection-static-analysis-plugin" + ], + "type": "library", + "autoload": { + "psr-4": { + "Roave\\InfectionStaticAnalysis\\": "src/Roave/InfectionStaticAnalysis" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Static analysis on top of mutation testing - prevents escaped mutants from being invalid according to static analysis", + "support": { + "issues": "https://github.com/Roave/infection-static-analysis-plugin/issues", + "source": "https://github.com/Roave/infection-static-analysis-plugin/tree/1.32.0" + }, + "time": "2023-05-21T01:01:51+00:00" + }, { "name": "sanmai/later", "version": "0.1.2", From e1625842481a042434ed00fbd635e4b9e8a7c19d Mon Sep 17 00:00:00 2001 From: Andrey Iatsenko Date: Mon, 12 Jun 2023 12:15:36 +0300 Subject: [PATCH 070/100] Create cs.yml --- .github/workflows/cs.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/cs.yml diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml new file mode 100644 index 0000000..6e452b7 --- /dev/null +++ b/.github/workflows/cs.yml @@ -0,0 +1,29 @@ +name: Coding Standards + +on: + pull_request: + push: + branches: + - main + - master + +jobs: + coding-standards: + name: Coding Standards + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.0 + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Check codestyle + run: composer run-script phpcs From eac57fe331eb2d999b76903752979f59359b3033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 19:04:16 +0300 Subject: [PATCH 071/100] upd: badge --- infection.json.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infection.json.dist b/infection.json.dist index 55412af..3c8f8cf 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -7,7 +7,7 @@ "logs": { "text": "php:\/\/stderr", "stryker": { - "report": "master" + "badge": "main" } }, "mutators": { From 4fdd306cc96c2684c99bb994dd14e5a5149c58fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 19:18:48 +0300 Subject: [PATCH 072/100] upd tests --- .github/workflows/tests.yml | 3 +-- src/Attributes/ConvertArray.php | 4 ++-- src/ClassTransformerConfig.php | 1 - src/GenericInstance.php | 2 +- src/Reflection/CacheReflectionProperty.php | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 137ba46..d1a0191 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,8 +12,7 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.2", "8.1", "8.0"] - dependency-version: [prefer-lowest, prefer-stable] + php: ["8.2", "8.1"] name: P${{ matrix.php }} - ${{ matrix.dependency-version }} steps: diff --git a/src/Attributes/ConvertArray.php b/src/Attributes/ConvertArray.php index 6d4a6dc..15e5512 100644 --- a/src/Attributes/ConvertArray.php +++ b/src/Attributes/ConvertArray.php @@ -6,7 +6,6 @@ use Attribute; - /** * An attribute for properties that are an array that allows you to specify the type of element * @@ -20,5 +19,6 @@ final class ConvertArray */ public function __construct( public string $type - ) {} + ) { + } } diff --git a/src/ClassTransformerConfig.php b/src/ClassTransformerConfig.php index 341a534..c3511f4 100644 --- a/src/ClassTransformerConfig.php +++ b/src/ClassTransformerConfig.php @@ -4,7 +4,6 @@ namespace ClassTransformer; - /** * Class ClassTransformerConfig * diff --git a/src/GenericInstance.php b/src/GenericInstance.php index 0fbf495..e4030df 100644 --- a/src/GenericInstance.php +++ b/src/GenericInstance.php @@ -5,7 +5,7 @@ namespace ClassTransformer; use ClassTransformer\Contracts\ReflectionClass; -use \ReflectionClass as PhpReflectionClass; +use ReflectionClass as PhpReflectionClass; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\Exceptions\ValueNotFoundException; diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 00c8c1d..70cb158 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -36,7 +36,7 @@ public function getType(): ?string { return $this->type; } - + /** * @return bool */ From 3434007264ffc196cd25f532966a81435a903d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 20:00:12 +0300 Subject: [PATCH 073/100] fix: psalm --- src/ArgumentsResource.php | 12 ++-- src/Attributes/FieldAlias.php | 2 +- src/CacheGenerator/CacheGenerator.php | 67 ++++++++++---------- src/ClassTransformer.php | 10 ++- src/Contracts/ReflectionProperty.php | 7 +- src/Reflection/CacheReflectionClass.php | 3 +- src/Reflection/CacheReflectionProperty.php | 17 ++++- src/Reflection/RuntimeReflectionProperty.php | 16 ++++- 8 files changed, 87 insertions(+), 47 deletions(-) diff --git a/src/ArgumentsResource.php b/src/ArgumentsResource.php index d30c6a7..512a9ef 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsResource.php @@ -27,14 +27,16 @@ final class ArgumentsResource /** * - * @param iterable|mixed ...$args + * @param iterable|array ...$args */ public function __construct(...$args) { // Unpacking named arguments $inArgs = sizeof(func_get_args()) === 1 ? $args[0] : $args; - $inArgs = (array)$inArgs; + if (!is_array($inArgs)) { + $inArgs = (array)$inArgs; + } $this->args = $inArgs; } @@ -51,13 +53,9 @@ public function getValue(ReflectionProperty $genericProperty): mixed return $this->args[$genericProperty->getName()]; } - $aliases = $genericProperty->getAttributeArguments(FieldAlias::class); + $aliases = $genericProperty->getAliases(); if (!empty($aliases)) { - $aliases = $aliases[0]; - if (is_string($aliases)) { - $aliases = [$aliases]; - } foreach ($aliases as $alias) { if (array_key_exists($alias, $this->args)) { return $this->args[$alias]; diff --git a/src/Attributes/FieldAlias.php b/src/Attributes/FieldAlias.php index fdd4abb..206bcf3 100644 --- a/src/Attributes/FieldAlias.php +++ b/src/Attributes/FieldAlias.php @@ -16,7 +16,7 @@ final class FieldAlias * @param string|array $aliases */ public function __construct( - public string|array $aliases + public array $aliases ) { } } diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index 9f97ab8..99bc372 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -16,8 +16,6 @@ * Class CacheGenerator * * @template TClass - * @psalm-api - * @psalm-immutable */ class CacheGenerator { @@ -35,51 +33,54 @@ public function __construct(string $class) } /** - * @psalm-suppress UnusedMethodCall - * @return array + * @return array{properties: array} * @throws ReflectionException|RuntimeException */ public function generate(): array { $this->makeCacheDir(ClassTransformerConfig::$cachePath); $class = str_replace('\\', '_', $this->class); - $path = ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'; - - $cache = ['properties' => []]; $refInstance = new ReflectionClass($this->class); $properties = $refInstance->getProperties(); - foreach ($properties as $item) { - $property = new RuntimeReflectionProperty($item); - if ($property->type instanceof ReflectionNamedType) { - $type = $property->type->getName(); - } else { - $type = $property->type; - } - - $cache['properties'][] = new CacheReflectionProperty( - $property->class, - $property->name, - (string)$type, - $property->types, - $property->isScalar, - $property->hasSetMutator(), - $property->isArray(), - $property->isEnum(), - $property->notTransform(), - $property->transformable(), - $property->getDocComment(), - $this->getArguments($property), - ); + $cache = [ + 'properties'=>array_map(fn($el) => $this->convertToCacheProperty(new RuntimeReflectionProperty($el)), $properties) + ]; + + $path = ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'; + file_put_contents($path, serialize($cache)); + return $cache; + } + + /** + * @param RuntimeReflectionProperty $property + * + * @return CacheReflectionProperty + */ + private function convertToCacheProperty(RuntimeReflectionProperty $property): CacheReflectionProperty + { + if ($property->type instanceof ReflectionNamedType) { + $type = $property->type->getName(); + } else { + $type = $property->type; } - file_put_contents( - $path, - serialize($cache) + return new CacheReflectionProperty( + $property->class, + $property->name, + (string)$type, + $property->types, + $property->isScalar, + $property->hasSetMutator(), + $property->isArray(), + $property->isEnum(), + $property->notTransform(), + $property->transformable(), + $property->getDocComment(), + $this->getArguments($property), ); - return $cache; } /** diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 4fc7bb2..fac635c 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -13,14 +13,14 @@ * Class ClassTransformer * * @psalm-api - * @psalm-immutable - * @template T of object */ final class ClassTransformer { /** * Class-transformer function to transform our object into a typed object * + * @template T of object + * * @param class-string $className * @param iterable|object ...$args * @@ -53,6 +53,9 @@ public static function transform(string $className, ...$args): mixed } /** + * + * @template T of object + * * @param class-string $className * @param array> $args * @@ -69,6 +72,9 @@ public static function transformCollection(string $className, array $args): ?arr } /** + * + * @template T of object + * * @param array> $className * @param array> $args * diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index 582a39d..69ca1de 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -37,7 +37,7 @@ public function getAttribute(string $name): mixed; /** * @param string $name * - * @return ?array + * @return null|array */ public function getAttributeArguments(string $name): ?array; @@ -69,4 +69,9 @@ public function hasSetMutator(): bool; * @return bool */ public function notTransform(): bool; + + /** + * @return array + */ + public function getAliases(): array; } diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 8bb50a1..513ff85 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -10,6 +10,7 @@ use ClassTransformer\CacheGenerator\CacheGenerator; use ClassTransformer\Validators\ClassExistsValidator; use ClassTransformer\Exceptions\ClassNotFoundException; +use RuntimeException; /** * Class RuntimeReflectionClass @@ -42,7 +43,7 @@ public function __construct(string $class) /** * @return CacheReflectionProperty[] - * @throws ReflectionException + * @throws ReflectionException|RuntimeException */ public function getProperties(): array { diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 70cb158..ab2b298 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -4,6 +4,8 @@ namespace ClassTransformer\Reflection; +use ClassTransformer\Attributes\FieldAlias; + /** * Class GenericProperty * @@ -106,7 +108,7 @@ public function getAttribute(string $name): mixed /** * @param string|null $name * - * @return mixed|null + * @return null|array */ public function getAttributeArguments(?string $name = null): ?array { @@ -120,4 +122,17 @@ public function getDocComment(): string { return $this->docComment; } + + /** + * @return array + */ + public function getAliases(): array + { + $aliases = $this->getAttributeArguments(FieldAlias::class); + $aliases = $aliases[0]; + if (is_string($aliases)) { + $aliases = [$aliases]; + } + return $aliases; + } } diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index e3d3904..c0f419f 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -4,6 +4,7 @@ namespace ClassTransformer\Reflection; +use ClassTransformer\Attributes\FieldAlias; use ReflectionType; use ReflectionProperty; use ReflectionNamedType; @@ -157,7 +158,7 @@ public function getAttribute(?string $name = null): ?ReflectionAttribute /** * @param string|null $name * - * @return array|null + * @return null|array */ public function getAttributeArguments(?string $name = null): ?array { @@ -195,4 +196,17 @@ public function getName(): string { return $this->name; } + + /** + * @return array + */ + public function getAliases(): array + { + $aliases = $this->getAttributeArguments(FieldAlias::class); + $aliases = $aliases[0]; + if (is_string($aliases)) { + $aliases = [$aliases]; + } + return $aliases; + } } From 4c38052385a2528bfa111909ef686163558db0cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 21:33:16 +0300 Subject: [PATCH 074/100] fix psalm --- psalm.xml | 2 +- src/Attributes/FieldAlias.php | 2 +- src/CacheGenerator/CacheGenerator.php | 4 ++-- src/Contracts/ReflectionProperty.php | 2 +- src/Reflection/CacheReflectionClass.php | 2 +- src/Reflection/CacheReflectionProperty.php | 5 +++++ src/Reflection/RuntimeReflectionProperty.php | 16 +++++++++++----- 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/psalm.xml b/psalm.xml index 22ce9d0..434ca8b 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ $aliases */ public function __construct( - public array $aliases + public string|array $aliases ) { } } diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index 99bc372..f8a198a 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -21,11 +21,11 @@ class CacheGenerator { private const DIR_PERMISSION = 0777; - /** @psalm-param class-string $class */ + /** @psalm-param class-string $class */ private string $class; /** - * @param class-string $class + * @param class-string $class */ public function __construct(string $class) { diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index 69ca1de..619eb6c 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -42,7 +42,7 @@ public function getAttribute(string $name): mixed; public function getAttributeArguments(string $name): ?array; /** - * @return bool|string + * @return string */ public function getDocComment(): string; diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 513ff85..565daaf 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -24,7 +24,7 @@ final class CacheReflectionClass implements ReflectionClass private string $class; /** - * @var array + * @var array */ private static array $propertiesTypesCache = []; diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index ab2b298..54952ea 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -129,6 +129,11 @@ public function getDocComment(): string public function getAliases(): array { $aliases = $this->getAttributeArguments(FieldAlias::class); + + if (empty($aliases)) { + return []; + } + $aliases = $aliases[0]; if (is_string($aliases)) { $aliases = [$aliases]; diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index c0f419f..104dce7 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -40,7 +40,7 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref /** @var bool */ public bool $isScalar; - /** @var array>>> */ + /** @var array>> */ private static array $attributeTypesCache = []; /** @var array>> */ @@ -67,7 +67,7 @@ public function getType(): ?string if ($this->type instanceof ReflectionNamedType) { return $this->type->getName(); } - return $this->type; + return (string)$this->type; } /** @@ -121,6 +121,7 @@ public function isArray(): bool } /** + * @return string */ public function getDocComment(): string { @@ -137,12 +138,12 @@ public function notTransform(): bool } /** - * @param class-string|null $name + * @param string $name * * @template T * @return null|ReflectionAttribute */ - public function getAttribute(?string $name = null): ?ReflectionAttribute + public function getAttribute(string $name): ?ReflectionAttribute { if (isset(self::$attributesCache[$this->class][$this->name][$name])) { return self::$attributesCache[$this->class][$this->name][$name]; @@ -182,7 +183,7 @@ public function isScalar(): bool } /** - * @return false|non-empty-string + * @return false|class-string */ public function transformable(): false|string { @@ -203,6 +204,11 @@ public function getName(): string public function getAliases(): array { $aliases = $this->getAttributeArguments(FieldAlias::class); + + if (empty($aliases)) { + return []; + } + $aliases = $aliases[0]; if (is_string($aliases)) { $aliases = [$aliases]; From ae18b60b96a9f415b96292d496e3d6dfc2439473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 21:38:33 +0300 Subject: [PATCH 075/100] fix code sniffer --- .github/workflows/cs.yml | 3 --- src/CacheGenerator/CacheGenerator.php | 2 +- src/Reflection/CacheReflectionProperty.php | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml index 6e452b7..50bf340 100644 --- a/.github/workflows/cs.yml +++ b/.github/workflows/cs.yml @@ -3,9 +3,6 @@ name: Coding Standards on: pull_request: push: - branches: - - main - - master jobs: coding-standards: diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index f8a198a..c7e8cd3 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -46,7 +46,7 @@ public function generate(): array $properties = $refInstance->getProperties(); $cache = [ - 'properties'=>array_map(fn($el) => $this->convertToCacheProperty(new RuntimeReflectionProperty($el)), $properties) + 'properties' => array_map(fn($el) => $this->convertToCacheProperty(new RuntimeReflectionProperty($el)), $properties) ]; $path = ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'; diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 54952ea..f258bb9 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -129,11 +129,11 @@ public function getDocComment(): string public function getAliases(): array { $aliases = $this->getAttributeArguments(FieldAlias::class); - + if (empty($aliases)) { return []; } - + $aliases = $aliases[0]; if (is_string($aliases)) { $aliases = [$aliases]; From f40aafa15d64a9128477a32a0f7f091d92b76f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 21:40:13 +0300 Subject: [PATCH 076/100] composer fix version --- .github/workflows/cs.yml | 2 +- composer.json | 2 +- composer.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml index 50bf340..0893e6e 100644 --- a/.github/workflows/cs.yml +++ b/.github/workflows/cs.yml @@ -16,7 +16,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.1 - name: Install dependencies if: steps.composer-cache.outputs.cache-hit != 'true' diff --git a/composer.json b/composer.json index 5ebacc6..0ab82ec 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "mockery/mockery": "1.5.1", "phpbench/phpbench": "1.2.8", "phpunit/phpunit": "10.2.1", - "roave/infection-static-analysis-plugin": "^1.32", + "roave/infection-static-analysis-plugin": "1.32", "squizlabs/php_codesniffer": "3.7.1", "symfony/var-dumper": "6.3", "vimeo/psalm": "5.12.0" diff --git a/composer.lock b/composer.lock index 41339d7..f263662 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "826b49d1956bb4698d53ea540d16223f", + "content-hash": "6d209336cd7129a8e9377f0bd731e229", "packages": [], "packages-dev": [ { From 5a90ce33f14734145c13d0a9820f855546c32634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 22:37:55 +0300 Subject: [PATCH 077/100] add: shields type coverage --- README.md | 2 ++ composer.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 34787b8..374ce90 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ ![Packagist Downloads](https://img.shields.io/packagist/dt/yzen.dev/plain-to-class) [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyzen-dev%2Fplain-to-class%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yzen-dev/plain-to-class/master) +[![type-coverage](https://shepherd.dev/github/yzen.dev/plain-to-class/coverage.svg)](https://shepherd.dev/github/yzen.dev/plain-to-class) +[![psalm-level](https://shepherd.dev/github/yzen.dev/plain-to-class/level.svg)](https://shepherd.dev/github/yzen.dev/plain-to-class) > Alas, I do not speak English, and the documentation was compiled through google translator :( I will be glad if you can help me describe the documentation more correctly :) diff --git a/composer.json b/composer.json index 0ab82ec..0cf2e2e 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "phpunit": "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml", "infection": "./vendor/bin/roave-infection-static-analysis-plugin", "infection-html": "./vendor/bin/roave-infection-static-analysis-plugin --logger-html='.tmp/mutation-report.html'", - "psalm": "./vendor/bin/psalm --show-info=true", + "psalm": "./vendor/bin/psalm", "phpcs": "./vendor/bin/phpcs --standard=./phpcs.xml -n --no-cache -s", "phpcbf": "./vendor/bin/phpcbf --standard=./phpcs.xml -n --no-cache -s", "coverage": "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml --coverage-text --colors=never --coverage-clover coverage.xml", From de46e4b2cc1af6dd69c116cdd9f612ad867ead38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Mon, 12 Jun 2023 22:42:55 +0300 Subject: [PATCH 078/100] fix: user name --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 374ce90..f0c58e9 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ ![Packagist Downloads](https://img.shields.io/packagist/dt/yzen.dev/plain-to-class) [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyzen-dev%2Fplain-to-class%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yzen-dev/plain-to-class/master) -[![type-coverage](https://shepherd.dev/github/yzen.dev/plain-to-class/coverage.svg)](https://shepherd.dev/github/yzen.dev/plain-to-class) -[![psalm-level](https://shepherd.dev/github/yzen.dev/plain-to-class/level.svg)](https://shepherd.dev/github/yzen.dev/plain-to-class) +[![type-coverage](https://shepherd.dev/github/yzen-dev/plain-to-class/coverage.svg)](https://shepherd.dev/github/yzen-dev/plain-to-class) +[![psalm-level](https://shepherd.dev/github/yzen-dev/plain-to-class/level.svg)](https://shepherd.dev/github/yzen-dev/plain-to-class) > Alas, I do not speak English, and the documentation was compiled through google translator :( I will be glad if you can help me describe the documentation more correctly :) From 0fcc03acaf4a8e9fc745616abb905661d8ad8772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 13 Jun 2023 19:03:58 +0300 Subject: [PATCH 079/100] issue: architecture --- ...tsResource.php => ArgumentsRepository.php} | 11 +- src/CacheGenerator/CacheGenerator.php | 47 +- src/ClassRepository.php | 61 +++ src/ClassTransformer.php | 26 +- src/ClassTransformerConfig.php | 6 +- ...lass.php => ReflectionClassRepository.php} | 2 +- src/Exceptions/ClassNotFoundException.php | 4 +- src/Exceptions/InvalidArgumentException.php | 4 +- src/Hydrator.php | 136 ++++++ src/HydratorConfig.php | 28 ++ ...enericInstance.php => InstanceBuilder.php} | 29 +- src/Reflection/CacheReflectionClass.php | 35 +- src/Reflection/CacheReflectionProperty.php | 3 + src/Reflection/RuntimeReflectionClass.php | 38 +- src/Reflection/RuntimeReflectionProperty.php | 7 +- src/TransformUtils.php | 2 +- src/Validators/ClassExistsValidator.php | 2 + src/ValueCasting.php | 13 +- tests/Benchmark/AddressBench.php | 144 ------ .../Bid/Dto/Address/AddressClean.php | 248 +++++++++++ tests/Benchmark/Bid/Dto/Address/MetroDto.php | 14 + .../Benchmark/{DTO => Bid/Dto}/ProductDto.php | 2 +- .../{DTO => Bid/Dto}/PurchaseDto.php | 7 +- tests/Benchmark/{DTO => Bid/Dto}/UserDto.php | 7 +- .../{DTO => Bid/Dto}/UserTypeEnum.php | 2 +- tests/Benchmark/Bid/FullCheckBench.php | 328 ++++++++++++++ tests/Benchmark/DTO/AddressClean.php | 415 ------------------ tests/Benchmark/DTO/AddressDto.php | 14 - tests/Benchmark/FullCheckBench.php | 133 ------ tests/Benchmark/SerializeVsArrayBench.php | 19 +- .../ClassTransformerFromCacheTest.php | 4 +- tests/Integration/DTO/WithAliasDTO.php | 5 +- tests/Units/ArgumentsResourceTest.php | 18 +- tests/Units/CacheGeneratorTest.php | 2 +- tests/Units/CacheReflectionClassTest.php | 4 +- 35 files changed, 968 insertions(+), 852 deletions(-) rename src/{ArgumentsResource.php => ArgumentsRepository.php} (93%) create mode 100644 src/ClassRepository.php rename src/Contracts/{ReflectionClass.php => ReflectionClassRepository.php} (87%) create mode 100644 src/Hydrator.php create mode 100644 src/HydratorConfig.php rename src/{GenericInstance.php => InstanceBuilder.php} (56%) delete mode 100644 tests/Benchmark/AddressBench.php create mode 100644 tests/Benchmark/Bid/Dto/Address/AddressClean.php create mode 100644 tests/Benchmark/Bid/Dto/Address/MetroDto.php rename tests/Benchmark/{DTO => Bid/Dto}/ProductDto.php (83%) rename tests/Benchmark/{DTO => Bid/Dto}/PurchaseDto.php (68%) rename tests/Benchmark/{DTO => Bid/Dto}/UserDto.php (80%) rename tests/Benchmark/{DTO => Bid/Dto}/UserTypeEnum.php (77%) create mode 100644 tests/Benchmark/Bid/FullCheckBench.php delete mode 100644 tests/Benchmark/DTO/AddressClean.php delete mode 100644 tests/Benchmark/DTO/AddressDto.php delete mode 100644 tests/Benchmark/FullCheckBench.php diff --git a/src/ArgumentsResource.php b/src/ArgumentsRepository.php similarity index 93% rename from src/ArgumentsResource.php rename to src/ArgumentsRepository.php index 512a9ef..59d9c0e 100644 --- a/src/ArgumentsResource.php +++ b/src/ArgumentsRepository.php @@ -4,23 +4,22 @@ namespace ClassTransformer; -use ClassTransformer\Attributes\FieldAlias; use ClassTransformer\Attributes\WritingStyle; use ClassTransformer\Contracts\ReflectionProperty; use ClassTransformer\Exceptions\ValueNotFoundException; -use function is_string; +use function sizeof; +use function is_array; +use function func_get_args; use function array_intersect; use function array_key_exists; -use function func_get_args; -use function sizeof; /** * Class GenericProperty * * @psalm-api */ -final class ArgumentsResource +final class ArgumentsRepository { /** @var array $args */ private array $args; @@ -44,7 +43,7 @@ public function __construct(...$args) /** * @param ReflectionProperty $genericProperty * - * @return mixed|object|array|null + * @return mixed * @throws ValueNotFoundException */ public function getValue(ReflectionProperty $genericProperty): mixed diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index c7e8cd3..c93ed58 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -4,14 +4,23 @@ namespace ClassTransformer\CacheGenerator; -use ClassTransformer\ClassTransformerConfig; -use ClassTransformer\Reflection\CacheReflectionProperty; use ReflectionClass; use RuntimeException; use ReflectionException; use ReflectionNamedType; +use ClassTransformer\HydratorConfig; +use ClassTransformer\Reflection\CacheReflectionClass; +use ClassTransformer\Exceptions\ClassNotFoundException; +use ClassTransformer\Reflection\CacheReflectionProperty; use ClassTransformer\Reflection\RuntimeReflectionProperty; +use function mkdir; +use function is_dir; +use function str_replace; +use function unserialize; +use function file_exists; +use function file_get_contents; + /** * Class CacheGenerator * @@ -21,15 +30,36 @@ class CacheGenerator { private const DIR_PERMISSION = 0777; + private HydratorConfig $config; + /** @psalm-param class-string $class */ private string $class; /** * @param class-string $class */ - public function __construct(string $class) + public function __construct(string $class, HydratorConfig $config = null) { $this->class = $class; + $this->config = $config ?? new HydratorConfig(); + } + + + /** + * @param string $class + * + * @return CacheReflectionClass + * @throws ReflectionException + * @throws ClassNotFoundException + */ + public function getClass(): CacheReflectionClass + { + if (!$this->cacheExists()) { + $classCache = $this->generate(); + } else { + $classCache = $this->get(); + } + return new CacheReflectionClass($this->class, $classCache['properties']); } /** @@ -38,7 +68,7 @@ public function __construct(string $class) */ public function generate(): array { - $this->makeCacheDir(ClassTransformerConfig::$cachePath); + $this->makeCacheDir($this->config->cachePath); $class = str_replace('\\', '_', $this->class); $refInstance = new ReflectionClass($this->class); @@ -49,7 +79,7 @@ public function generate(): array 'properties' => array_map(fn($el) => $this->convertToCacheProperty(new RuntimeReflectionProperty($el)), $properties) ]; - $path = ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'; + $path = $this->config->cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'; file_put_contents($path, serialize($cache)); return $cache; } @@ -99,12 +129,12 @@ private function getArguments(RuntimeReflectionProperty $property): array } /** - * @return array + * @return array{properties: array} */ public function get(): array { $class = str_replace('\\', '_', $this->class); - return unserialize(file_get_contents(ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php')); + return unserialize(file_get_contents($this->config->cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php')); } /** @@ -116,7 +146,6 @@ public function cacheExists(): bool return file_exists('./.cache/' . $class . '.cache.php'); } - /** * @param string|null $path * @@ -125,7 +154,7 @@ public function cacheExists(): bool */ private function makeCacheDir(?string $path): void { - $concurrentDirectory = ClassTransformerConfig::$cachePath; + $concurrentDirectory = $this->config->cachePath; if ( empty($path) || ( diff --git a/src/ClassRepository.php b/src/ClassRepository.php new file mode 100644 index 0000000..6855b64 --- /dev/null +++ b/src/ClassRepository.php @@ -0,0 +1,61 @@ + + */ +final class ClassRepository +{ + /** @var class-string $class */ + private string $class; + + /** @var ReflectionClassRepository $class */ + private ReflectionClassRepository $classRepository; + + /** + * @var array + */ + private static array $propertiesTypesCache = []; + + + /** + * @param class-string $class + * + * @throws ClassNotFoundException + */ + public function __construct( + string $class, + ReflectionClassRepository $classRepository + ) { + $this->class = $class; + $this->classRepository = $classRepository; + } + + /** + * @return ReflectionProperty[] + */ + public function getProperties(): array + { + if (isset(self::$propertiesTypesCache[$this->class])) { + return self::$propertiesTypesCache[$this->class]; + } + + return self::$propertiesTypesCache[$this->class] = $this->classRepository->getProperties(); + } + + /** + * @return string + */ + public function getClass(): string + { + return $this->class; + } +} diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index fac635c..3ed1f87 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -4,10 +4,7 @@ namespace ClassTransformer; -use ClassTransformer\Reflection\CacheReflectionClass; -use ClassTransformer\Validators\ClassExistsValidator; use ClassTransformer\Exceptions\ClassNotFoundException; -use ClassTransformer\Reflection\RuntimeReflectionClass; /** * Class ClassTransformer @@ -29,27 +26,8 @@ final class ClassTransformer */ public static function transform(string $className, ...$args): mixed { - new ClassExistsValidator($className); - - if (method_exists($className, 'transform')) { - $instance = new $className(); - $instance->transform(...$args); - } else { - if (ClassTransformerConfig::$cache) { - $reflection = new CacheReflectionClass($className); - } else { - $reflection = new RuntimeReflectionClass($className); - } - $generic = new GenericInstance($reflection, new ArgumentsResource(...$args)); - /** @var T $instance */ - $instance = $generic->transform(); - } - - if (method_exists($instance, 'afterTransform')) { - $instance->afterTransform(); - } - - return $instance; + return (new Hydrator()) + ->create($className, ...$args); } /** diff --git a/src/ClassTransformerConfig.php b/src/ClassTransformerConfig.php index c3511f4..04aa8d8 100644 --- a/src/ClassTransformerConfig.php +++ b/src/ClassTransformerConfig.php @@ -11,7 +11,9 @@ */ final class ClassTransformerConfig { - /** @var bool */ - public static bool $cache = false; + /** @var bool Cache mode enabled */ + public static bool $cacheEnabled = false; + + /** @var string Path to the cache directory */ public static string $cachePath = __DIR__ . '/../.cache'; } diff --git a/src/Contracts/ReflectionClass.php b/src/Contracts/ReflectionClassRepository.php similarity index 87% rename from src/Contracts/ReflectionClass.php rename to src/Contracts/ReflectionClassRepository.php index 37ce0b0..8ec6f8b 100644 --- a/src/Contracts/ReflectionClass.php +++ b/src/Contracts/ReflectionClassRepository.php @@ -5,7 +5,7 @@ /** * @psalm-api */ -interface ReflectionClass +interface ReflectionClassRepository { /** * @return array diff --git a/src/Exceptions/ClassNotFoundException.php b/src/Exceptions/ClassNotFoundException.php index 9ca4e41..8f182b9 100644 --- a/src/Exceptions/ClassNotFoundException.php +++ b/src/Exceptions/ClassNotFoundException.php @@ -2,9 +2,11 @@ namespace ClassTransformer\Exceptions; +use Exception; + /** * @psalm-api */ -class ClassNotFoundException extends \Exception +class ClassNotFoundException extends Exception { } diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php index 549a1c5..0276cc3 100644 --- a/src/Exceptions/InvalidArgumentException.php +++ b/src/Exceptions/InvalidArgumentException.php @@ -2,9 +2,11 @@ namespace ClassTransformer\Exceptions; +use Exception; + /** * @psalm-api */ -class InvalidArgumentException extends \Exception +class InvalidArgumentException extends Exception { } diff --git a/src/Hydrator.php b/src/Hydrator.php new file mode 100644 index 0000000..130b1b6 --- /dev/null +++ b/src/Hydrator.php @@ -0,0 +1,136 @@ + + */ +class Hydrator +{ + private HydratorConfig $config; + + /** + * @var array + */ + private static array $classRepositoryCache = []; + + /** + */ + public function __construct(HydratorConfig $config = null) + { + $this->config = $config ?? new HydratorConfig(); + } + + /** + * Create instance T class + * + * @param class-string $class + * @param iterable|object ...$args + * + * @return null|T + * @throws ClassNotFoundException + */ + public function create(string $class, ...$args) + { + new ClassExistsValidator($class); + + $instance = $this->getInstance($class, ...$args); + + if (method_exists($instance, 'afterTransform')) { + $instance->afterTransform(); + } + + return $instance; + } + + /** + * @param class-string $class + * @param array> $args + * + * @return null|array|array + * @throws ClassNotFoundException + */ + public function createCollection(string $class, array $args): ?array + { + $result = []; + foreach ($args as $item) { + $result [] = $this->create($class, $item); + } + return $result; + } + + /** + * @param array> $className + * @param array> $args + * + * @return null|array|array + * @throws ClassNotFoundException + */ + public function createMultiple(array $classes, array $args): ?array + { + $result = []; + foreach ($classes as $key => $class) { + $result [] = $this->create($class, $args[$key]); + } + return $result; + } + + /** + * @param class-string $class + * @param ...$args + * + * @return mixed + * @throws ClassNotFoundException + */ + private function getInstance(string $class, ...$args) + { + if (method_exists($class, 'transform')) { + $instance = new $class(); + $instance->transform(...$args); + return $instance; + } + + return (new InstanceBuilder( + $this->createClassRepository($class), + new ArgumentsRepository(...$args), + $this->config + )) + ->build(); + } + + /** + * @param class-string $class + * + * @return ClassRepository + * @throws ClassNotFoundException|ReflectionException + */ + private function createClassRepository(string $class): ClassRepository + { + if (isset(self::$classRepositoryCache[$class])) { + return self::$classRepositoryCache[$class]; + } + + if ($this->config->cacheEnabled) { + $cache = new CacheGenerator($class, $this->config); + $repository = $cache->getClass(); + } else { + $repository = new RuntimeReflectionClass($class); + } + + return self::$classRepositoryCache[$class] = new ClassRepository( + $class, + $repository + ); + } +} diff --git a/src/HydratorConfig.php b/src/HydratorConfig.php new file mode 100644 index 0000000..ad25959 --- /dev/null +++ b/src/HydratorConfig.php @@ -0,0 +1,28 @@ +cacheEnabled = $cacheEnabled ?? ClassTransformerConfig::$cacheEnabled; + $this->cachePath = $cachePath ?? ClassTransformerConfig::$cachePath; + } +} diff --git a/src/GenericInstance.php b/src/InstanceBuilder.php similarity index 56% rename from src/GenericInstance.php rename to src/InstanceBuilder.php index e4030df..af48499 100644 --- a/src/GenericInstance.php +++ b/src/InstanceBuilder.php @@ -4,8 +4,6 @@ namespace ClassTransformer; -use ClassTransformer\Contracts\ReflectionClass; -use ReflectionClass as PhpReflectionClass; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\Exceptions\ValueNotFoundException; @@ -15,31 +13,34 @@ * @psalm-api * @template T */ -final class GenericInstance +final class InstanceBuilder { - /** @var ReflectionClass $class */ - private ReflectionClass $class; + private HydratorConfig $config; + + /** @var ClassRepository $classRepository */ + private ClassRepository $class; - /** @var ArgumentsResource $argumentsResource */ - private ArgumentsResource $argumentsResource; + /** @var ArgumentsRepository $argumentsRepository */ + private ArgumentsRepository $argumentsRepository; /** - * @param ReflectionClass $class - * @param ArgumentsResource $argumentsResource + * @param ClassRepository $class + * @param ArgumentsRepository $argumentsRepository */ - public function __construct(ReflectionClass $class, ArgumentsResource $argumentsResource) + public function __construct(ClassRepository $class, ArgumentsRepository $argumentsRepository, HydratorConfig $config = null) { $this->class = $class; + $this->config = $config ?? new HydratorConfig(); - $this->argumentsResource = $argumentsResource; + $this->argumentsRepository = $argumentsRepository; } /** * @return T * @throws ClassNotFoundException */ - public function transform(): mixed + public function build(): mixed { $properties = $this->class->getProperties(); /** @var T $genericInstance */ @@ -47,7 +48,7 @@ public function transform(): mixed foreach ($properties as $property) { try { - $value = $this->argumentsResource->getValue($property); + $value = $this->argumentsRepository->getValue($property); } catch (ValueNotFoundException) { continue; } @@ -57,7 +58,7 @@ public function transform(): mixed continue; } - $caster = new ValueCasting($property); + $caster = new ValueCasting($property, $this->config); $genericInstance->{$property->getName()} = $caster->castAttribute($value); } return $genericInstance; diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 565daaf..562492e 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -4,13 +4,12 @@ namespace ClassTransformer\Reflection; -use ReflectionProperty; +use RuntimeException; use ReflectionException; -use ClassTransformer\Contracts\ReflectionClass; use ClassTransformer\CacheGenerator\CacheGenerator; use ClassTransformer\Validators\ClassExistsValidator; use ClassTransformer\Exceptions\ClassNotFoundException; -use RuntimeException; +use ClassTransformer\Contracts\ReflectionClassRepository; /** * Class RuntimeReflectionClass @@ -18,27 +17,22 @@ * @psalm-api * @template T */ -final class CacheReflectionClass implements ReflectionClass +final class CacheReflectionClass implements ReflectionClassRepository { /** @var class-string $class */ private string $class; - - /** - * @var array - */ - private static array $propertiesTypesCache = []; - + + private array $properties; /** * @param class-string $class * * @throws ClassNotFoundException */ - public function __construct(string $class) + public function __construct(string $class, array $properties) { - new ClassExistsValidator($class); - $this->class = $class; + $this->properties = $properties; } /** @@ -47,20 +41,7 @@ public function __construct(string $class) */ public function getProperties(): array { - if (isset(self::$propertiesTypesCache[$this->class])) { - return self::$propertiesTypesCache[$this->class]; - } - - $cache = new CacheGenerator($this->class); - - /** @var CacheReflectionClass $class */ - if (!$cache->cacheExists()) { - $class = $cache->generate(); - } else { - $class = $cache->get(); - } - - return self::$propertiesTypesCache[$this->class] = $class['properties']; + return $this->properties; } /** diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index f258bb9..d30ca4d 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -6,6 +6,8 @@ use ClassTransformer\Attributes\FieldAlias; +use function is_string; + /** * Class GenericProperty * @@ -135,6 +137,7 @@ public function getAliases(): array } $aliases = $aliases[0]; + if (is_string($aliases)) { $aliases = [$aliases]; } diff --git a/src/Reflection/RuntimeReflectionClass.php b/src/Reflection/RuntimeReflectionClass.php index 383842f..5584b5c 100644 --- a/src/Reflection/RuntimeReflectionClass.php +++ b/src/Reflection/RuntimeReflectionClass.php @@ -4,10 +4,12 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Contracts\ReflectionClass; -use ClassTransformer\Validators\ClassExistsValidator; -use ClassTransformer\Exceptions\ClassNotFoundException; -use ReflectionClass as PhpReflectionClass; +use ReflectionClass; +use ReflectionException; +use InvalidArgumentException; +use ClassTransformer\Contracts\ReflectionClassRepository; + +use function array_map; /** * Class RuntimeReflectionClass @@ -15,48 +17,34 @@ * @psalm-api * @template T */ -final class RuntimeReflectionClass implements ReflectionClass +final class RuntimeReflectionClass implements ReflectionClassRepository { /** @var class-string $class */ private string $class; - /** - * @var array - */ - private static $propertiesTypesCache = []; - - /** * @param class-string $class - * - * @throws ClassNotFoundException */ public function __construct(string $class) { - new ClassExistsValidator($class); - $this->class = $class; } /** * @return RuntimeReflectionProperty[] - * @throws \ReflectionException + * @throws ReflectionException|InvalidArgumentException */ public function getProperties(): array { - if (isset(static::$propertiesTypesCache[$this->class])) { - return static::$propertiesTypesCache[$this->class]; - } + $refInstance = new ReflectionClass($this->class); - $refInstance = new PhpReflectionClass($this->class); + if (!$refInstance->isInstantiable()) { + throw new InvalidArgumentException('Class ' . $this->class . ' is not instantiable.'); + } $properties = $refInstance->getProperties(); - $result = []; - foreach ($properties as $item) { - $result [] = new RuntimeReflectionProperty($item); - } - return static::$propertiesTypesCache[$this->class] = $result; + return array_map(static fn($item) => new RuntimeReflectionProperty($item), $properties); } /** diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index 104dce7..cb9b92d 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -4,18 +4,18 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Attributes\FieldAlias; use ReflectionType; use ReflectionProperty; use ReflectionNamedType; use ReflectionAttribute; use ClassTransformer\TransformUtils; +use ClassTransformer\Attributes\FieldAlias; use ClassTransformer\Attributes\NotTransform; +use function sizeof; +use function in_array; use function method_exists; use function array_intersect; -use function in_array; -use function sizeof; /** * Class GenericProperty @@ -210,6 +210,7 @@ public function getAliases(): array } $aliases = $aliases[0]; + if (is_string($aliases)) { $aliases = [$aliases]; } diff --git a/src/TransformUtils.php b/src/TransformUtils.php index 852b5d0..83cca17 100644 --- a/src/TransformUtils.php +++ b/src/TransformUtils.php @@ -4,6 +4,7 @@ namespace ClassTransformer; +use function ucfirst; use function ucwords; use function lcfirst; use function is_string; @@ -11,7 +12,6 @@ use function preg_match; use function str_replace; use function preg_replace; -use function array_key_exists; /** * diff --git a/src/Validators/ClassExistsValidator.php b/src/Validators/ClassExistsValidator.php index e2a79c0..015873b 100644 --- a/src/Validators/ClassExistsValidator.php +++ b/src/Validators/ClassExistsValidator.php @@ -6,6 +6,8 @@ use ClassTransformer\Exceptions\ClassNotFoundException; +use function class_exists; + /** * Class ClassExistsValidator */ diff --git a/src/ValueCasting.php b/src/ValueCasting.php index 4a31d86..7b2aacd 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -18,16 +18,20 @@ */ final class ValueCasting { + + private HydratorConfig $config; + /** @var ReflectionProperty $property */ private ReflectionProperty $property; - /** * @param ReflectionProperty $property + * @param HydratorConfig|null $config */ - public function __construct(ReflectionProperty $property) + public function __construct(ReflectionProperty $property, HydratorConfig $config = null) { $this->property = $property; + $this->config = $config ?? new HydratorConfig(); } /** @@ -58,7 +62,8 @@ public function castAttribute(mixed $value): mixed $propertyClass = $this->property->transformable(); if ($propertyClass) { - return ClassTransformer::transform($propertyClass, $value); + return (new Hydrator($this->config)) + ->create($propertyClass, $value); } return $value; @@ -85,7 +90,7 @@ private function castArray($value): mixed return $value; } if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'boolean', 'mixed'])) { - return array_map(static fn($el) => ClassTransformer::transform($arrayType, $el), $value); + return array_map(fn($el) => (new Hydrator($this->config))->create($arrayType, $el), $value); } return array_map(static fn($el) => match ($arrayType) { diff --git a/tests/Benchmark/AddressBench.php b/tests/Benchmark/AddressBench.php deleted file mode 100644 index 0a3d2c6..0000000 --- a/tests/Benchmark/AddressBench.php +++ /dev/null @@ -1,144 +0,0 @@ -getData()); - } - - /** - * @Revs(10000) - */ - public function benchCache(): void - { - ClassTransformerConfig::$cache = true; - ClassTransformer::transform(AddressClean::class, $this->getData()); - } - - - private function getData() - { - return json_decode('{ - "source": "мск сухонска 11/-89", - "result": "г Москва, ул Сухонская, д 11, кв 89", - "postal_code": "127642", - "country": "Россия", - "country_iso_code": "RU", - "federal_district": "Центральный", - "region_fias_id": "0c5b2444-70a0-4932-980c-b4dc0d3f02b5", - "region_kladr_id": "7700000000000", - "region_iso_code": "RU-MOW", - "region_with_type": "г Москва", - "region_type": "г", - "region_type_full": "город", - "region": "Москва", - "area_fias_id": null, - "area_kladr_id": null, - "area_with_type": null, - "area_type": null, - "area_type_full": null, - "area": null, - "city_fias_id": null, - "city_kladr_id": null, - "city_with_type": null, - "city_type": null, - "city_type_full": null, - "city": null, - "city_area": "Северо-восточный", - "city_district_fias_id": null, - "city_district_kladr_id": null, - "city_district_with_type": "р-н Северное Медведково", - "city_district_type": "р-н", - "city_district_type_full": "район", - "city_district": "Северное Медведково", - "settlement_fias_id": null, - "settlement_kladr_id": null, - "settlement_with_type": null, - "settlement_type": null, - "settlement_type_full": null, - "settlement": null, - "street_fias_id": "95dbf7fb-0dd4-4a04-8100-4f6c847564b5", - "street_kladr_id": "77000000000283600", - "street_with_type": "ул Сухонская", - "street_type": "ул", - "street_type_full": "улица", - "street": "Сухонская", - "house_fias_id": "5ee84ac0-eb9a-4b42-b814-2f5f7c27c255", - "house_kladr_id": "7700000000028360004", - "house_cadnum": "77:02:0004008:1017", - "house_type": "д", - "house_type_full": "дом", - "house": "11", - "block_type": null, - "block_type_full": null, - "block": null, - "entrance": null, - "floor": null, - "flat_fias_id": "f26b876b-6857-4951-b060-ec6559f04a9a", - "flat_cadnum": "77:02:0004008:4143", - "flat_type": "кв", - "flat_type_full": "квартира", - "flat": "89", - "flat_area": "34.6", - "square_meter_price": "239953", - "flat_price": "8302374", - "postal_box": null, - "fias_id": "f26b876b-6857-4951-b060-ec6559f04a9a", - "fias_code": "77000000000000028360004", - "fias_level": "9", - "fias_actuality_state": "0", - "kladr_id": "7700000000028360004", - "capital_marker": "0", - "okato": "45280583000", - "oktmo": "45362000", - "tax_office": "7715", - "tax_office_legal": "7715", - "timezone": "UTC+3", - "geo_lat": "55.8782557", - "geo_lon": "37.65372", - "beltway_hit": "IN_MKAD", - "beltway_distance": null, - "qc_geo": 0, - "qc_complete": 0, - "qc_house": 2, - "qc": 0, - "unparsed_parts": null, - "metro": [ - { - "distance": 1.1, - "line": "Калужско-Рижская", - "name": "Бабушкинская" - }, - { - "distance": 1.2, - "line": "Калужско-Рижская", - "name": "Медведково" - }, - { - "distance": 2.5, - "line": "Калужско-Рижская", - "name": "Свиблово" - } - ] - }'); - } -} diff --git a/tests/Benchmark/Bid/Dto/Address/AddressClean.php b/tests/Benchmark/Bid/Dto/Address/AddressClean.php new file mode 100644 index 0000000..86e7bfd --- /dev/null +++ b/tests/Benchmark/Bid/Dto/Address/AddressClean.php @@ -0,0 +1,248 @@ +getPurcheseObject(); + + $purchase = new PurchaseDto(); + + $user = new UserDto(); + $user->id = $data['user']['id']; + $user->email = $data['user']['email'] ?? $data['user']['contact']; + $user->balance = $data['user']['balance']; + $user->real_address = $data['user']['real_address'] ?? $data['user']['realAddress']; + $user->type = UserTypeEnum::from($data['user']['type']); + $user->createdAt = new \DateTime($data['user']['createdAt']); + $purchase->user = $user; + + $address = new AddressClean(); + $address->source = $data['address']['source']; + $address->result = $data['address']['result']; + $address->postal_code = $data['address']['postal_code']; + $address->country = $data['address']['country']; + $address->country_iso_code = $data['address']['country_iso_code']; + $address->federal_district = $data['address']['federal_district']; + $address->region_fias_id = $data['address']['region_fias_id']; + $address->region_kladr_id = $data['address']['region_kladr_id']; + $address->region_iso_code = $data['address']['region_iso_code']; + $address->region_with_type = $data['address']['region_with_type']; + $address->region_type = $data['address']['region_type']; + $address->region_type_full = $data['address']['region_type_full']; + $address->region = $data['address']['region']; + $address->area_fias_id = $data['address']['area_fias_id']; + $address->area_kladr_id = $data['address']['area_kladr_id']; + $address->area_with_type = $data['address']['area_with_type']; + $address->area_type = $data['address']['area_type']; + $address->area_type_full = $data['address']['area_type_full']; + $address->area = $data['address']['area']; + $address->city_fias_id = $data['address']['city_fias_id']; + $address->city_kladr_id = $data['address']['city_kladr_id']; + $address->city_with_type = $data['address']['city_with_type']; + $address->city_type = $data['address']['city_type']; + $address->city_type_full = $data['address']['city_type_full']; + $address->city = $data['address']['city']; + $address->city_area = $data['address']['city_area']; + $address->city_district_fias_id = $data['address']['city_district_fias_id']; + $address->city_district_kladr_id = $data['address']['city_district_kladr_id']; + $address->city_district_with_type = $data['address']['city_district_with_type']; + $address->city_district_type = $data['address']['city_district_type']; + $address->city_district_type_full = $data['address']['city_district_type_full']; + $address->city_district = $data['address']['city_district']; + $address->settlement_fias_id = $data['address']['settlement_fias_id']; + $address->settlement_kladr_id = $data['address']['settlement_kladr_id']; + $address->settlement_with_type = $data['address']['settlement_with_type']; + $address->settlement_type = $data['address']['settlement_type']; + $address->settlement_type_full = $data['address']['settlement_type_full']; + $address->settlement = $data['address']['settlement']; + $address->street_fias_id = $data['address']['street_fias_id']; + $address->street_kladr_id = $data['address']['street_kladr_id']; + $address->street_with_type = $data['address']['street_with_type']; + $address->street_type = $data['address']['street_type']; + $address->street_type_full = $data['address']['street_type_full']; + $address->street = $data['address']['street']; + $address->house_fias_id = $data['address']['house_fias_id']; + $address->house_kladr_id = $data['address']['house_kladr_id']; + $address->house_type = $data['address']['house_type']; + $address->house_type_full = $data['address']['house_type_full']; + $address->house = $data['address']['house']; + $address->block_type = $data['address']['block_type']; + $address->block_type_full = $data['address']['block_type_full']; + $address->block = $data['address']['block']; + $address->flat_fias_id = $data['address']['flat_fias_id']; + $address->flat_type = $data['address']['flat_type']; + $address->flat_type_full = $data['address']['flat_type_full']; + $address->flat = $data['address']['flat']; + $address->flat_area = $data['address']['flat_area']; + $address->square_meter_price = $data['address']['square_meter_price']; + $address->flat_price = $data['address']['flat_price']; + $address->postal_box = $data['address']['postal_box']; + $address->fias_id = $data['address']['fias_id']; + $address->fias_code = $data['address']['fias_code']; + $address->fias_level = $data['address']['fias_level']; + $address->kladr_id = $data['address']['kladr_id']; + $address->capital_marker = $data['address']['capital_marker']; + $address->okato = $data['address']['okato']; + $address->oktmo = $data['address']['oktmo']; + $address->tax_office = $data['address']['tax_office']; + $address->tax_office_legal = $data['address']['tax_office_legal']; + $address->timezone = $data['address']['timezone']; + $address->geo_lat = $data['address']['geo_lat']; + $address->geo_lon = $data['address']['geo_lon']; + $address->beltway_hit = $data['address']['beltway_hit']; + $address->beltway_distance = $data['address']['beltway_distance']; + $address->qc_geo = $data['address']['qc_geo']; + $address->qc_complete = $data['address']['qc_complete']; + $address->qc_house = $data['address']['qc_house']; + $address->qc = $data['address']['qc']; + $address->unparsed_parts = $data['address']['unparsed_parts']; + + foreach ($data['address']['metro'] as $item) { + $metro = new MetroDto(); + $metro->distance = $item['distance']; + $metro->line = $item['line']; + $metro->name = $item['name']; + $address->metro [] = $metro; + } + + $purchase->address = $address; + + $purchase->createdAt = new \DateTime($data['createdAt']); + + foreach ($data['products'] as $product) { + $newProduct = new ProductDto(); + $newProduct->id = $product['id']; + $newProduct->name = $product['name']; + $newProduct->price = $product['price']; + $newProduct->count = $product['count']; + $purchase->products [] = $newProduct; + } + + $this->assertEquals($data['user']['id'], $purchase->user->id); + } + + /** + * @Revs(10000) + */ + public function benchTransformCacheReflection(): void + { + $data = $this->getPurcheseObject(); + + $purchase = (new Hydrator(new HydratorConfig(true))) + ->create(PurchaseDto::class, $data); + + $this->assertEquals($data['user']['id'], $purchase->user->id); + } + + /** + * @Revs(10000) + */ + public function benchTransformReflection(): void + { + $data = $this->getPurcheseObject(); + $purchase = (new Hydrator()) + ->create(PurchaseDto::class, $data); + + $this->assertEquals($data['user']['id'], $purchase->user->id); + } + + public function getPurcheseObject(): array + { + return [ + 'createdAt' => '2023-04-10 12:30:23', + 'products' => [ + [ + 'id' => 1, + 'name' => 'phone', + 'price' => 43.03, + 'description' => 'test description for phone', + 'count' => 123 + ], + [ + 'id' => 2, + 'name' => 'bread', + 'price' => 10.56, + 'description' => 'test description for bread', + 'count' => 321 + ], + [ + 'id' => 3, + 'name' => 'book', + 'price' => 5.5, + 'description' => 'test description for book', + 'count' => 333 + ], + [ + 'id' => 4, + 'name' => 'PC', + 'price' => 100, + 'description' => 'test description for PC', + 'count' => 7 + ] + ], + 'user' => [ + 'id' => 1, + 'contact' => 'fake@mail.com', + 'balance' => 10012.23, + 'type' => 'admin', + 'realAddress' => 'test address', + 'createdAt' => '2023-04-10 12:30:23', + ], + 'address' => $this->getAddress() + ]; + } + + private function getAddress() + { + return [ + "source" => "мск сухонска 11/-89", + "result" => "г Москва, ул Сухонская, д 11, кв 89", + "postal_code" => "127642", + "country" => "Россия", + "country_iso_code" => "RU", + "federal_district" => "Центральный", + "region_fias_id" => "0c5b2444-70a0-4932-980c-b4dc0d3f02b5", + "region_kladr_id" => "7700000000000", + "region_iso_code" => "RU-MOW", + "region_with_type" => "г Москва", + "region_type" => "г", + "region_type_full" => "город", + "region" => "Москва", + "area_fias_id" => null, + "area_kladr_id" => null, + "area_with_type" => null, + "area_type" => null, + "area_type_full" => null, + "area" => null, + "city_fias_id" => null, + "city_kladr_id" => null, + "city_with_type" => null, + "city_type" => null, + "city_type_full" => null, + "city" => null, + "city_area" => "Северо-восточный", + "city_district_fias_id" => null, + "city_district_kladr_id" => null, + "city_district_with_type" => "р-н Северное Медведково", + "city_district_type" => "р-н", + "city_district_type_full" => "район", + "city_district" => "Северное Медведково", + "settlement_fias_id" => null, + "settlement_kladr_id" => null, + "settlement_with_type" => null, + "settlement_type" => null, + "settlement_type_full" => null, + "settlement" => null, + "street_fias_id" => "95dbf7fb-0dd4-4a04-8100-4f6c847564b5", + "street_kladr_id" => "77000000000283600", + "street_with_type" => "ул Сухонская", + "street_type" => "ул", + "street_type_full" => "улица", + "street" => "Сухонская", + "house_fias_id" => "5ee84ac0-eb9a-4b42-b814-2f5f7c27c255", + "house_kladr_id" => "7700000000028360004", + "house_type" => "д", + "house_type_full" => "дом", + "house" => "11", + "block_type" => null, + "block_type_full" => null, + "block" => null, + "entrance" => null, + "floor" => null, + "flat_fias_id" => "f26b876b-6857-4951-b060-ec6559f04a9a", + "flat_type" => "кв", + "flat_type_full" => "квартира", + "flat" => "89", + "flat_area" => "34.6", + "square_meter_price" => "239953", + "flat_price" => "8302374", + "postal_box" => null, + "fias_id" => "f26b876b-6857-4951-b060-ec6559f04a9a", + "fias_code" => "77000000000000028360004", + "fias_level" => "9", + "fias_actuality_state" => "0", + "kladr_id" => "7700000000028360004", + "capital_marker" => "0", + "okato" => "45280583000", + "oktmo" => "45362000", + "tax_office" => "7715", + "tax_office_legal" => "7715", + "timezone" => "UTC+3", + "geo_lat" => "55.8782557", + "geo_lon" => "37.65372", + "beltway_hit" => "IN_MKAD", + "beltway_distance" => null, + "qc_geo" => 0, + "qc_complete" => 0, + "qc_house" => 2, + "qc" => 0, + "unparsed_parts" => null, + "metro" => [ + [ + "distance" => 1.1, + "line" => "Калужско-Рижская", + "name" => "Бабушкинская" + ], + [ + "distance" => 1.2, + "line" => "Калужско-Рижская", + "name" => "Медведково" + ], + [ + "distance" => 2.5, + "line" => "Калужско-Рижская", + "name" => "Свиблово" + ] + ] + ]; + } +} diff --git a/tests/Benchmark/DTO/AddressClean.php b/tests/Benchmark/DTO/AddressClean.php deleted file mode 100644 index b19b3ab..0000000 --- a/tests/Benchmark/DTO/AddressClean.php +++ /dev/null @@ -1,415 +0,0 @@ - - */ -class AddressClean -{ - /** - * @var null|string Исходный адрес одной строкой - */ - public ?string $source; - - /** - * @var null|string Стандартизованный адрес одной строкой - */ - public ?string $result; - - /** - * @var null|string Индекс - */ - public ?string $postal_code; - - /** - * @var null|string Страна - */ - public ?string $country; - - /** - * @var null|string Cтрана iso alfa2 - */ - public ?string $country_iso_code; - - /** - * @var null|string Федеральный округ - */ - public ?string $federal_district; - - /** - * @var null|string Код ФИАС региона - */ - public ?string $region_fias_id; - - /** - * @var null|string Код КЛАДР региона - */ - public ?string $region_kladr_id; - - /** - * @var null|string ISO-код региона - */ - public ?string $region_iso_code; - - /** - * @var null|string Регион с типом - */ - public ?string $region_with_type; - - /** - * @var null|string Тип региона (сокращенный) - */ - public ?string $region_type; - - /** - * @var null|string Тип региона - */ - public ?string $region_type_full; - - /** - * @var null|string Регион - */ - public ?string $region; - - /** - * @var null|string Код ФИАС района в регионе - */ - public ?string $area_fias_id; - - /** - * @var null|string Код КЛАДР района в регионе - */ - public ?string $area_kladr_id; - - /** - * @var null|string Район в регионе с типом - */ - public ?string $area_with_type; - - /** - * @var null|string Тип района в регионе (сокращенный) - */ - public ?string $area_type; - - /** - * @var null|string Тип района в регионе - */ - public ?string $area_type_full; - - /** - * @var null|string Район в регионе - */ - public ?string $area; - - /** - * @var null|string Код ФИАС города - */ - public ?string $city_fias_id; - - /** - * @var null|string Код КЛАДР города - */ - public ?string $city_kladr_id; - - /** - * @var null|string Город с типом - */ - public ?string $city_with_type; - - /** - * @var null|string Тип города (сокращенный) - */ - public ?string $city_type; - - /** - * @var null|string Тип города - */ - public ?string $city_type_full; - - /** - * @var null|string Город - */ - public ?string $city; - - /** - * @var null|string Административный округ (только для Москвы) - */ - public ?string $city_area; - - /** - * @var null|string Код ФИАС района города (не заполняется) - */ - public ?string $city_district_fias_id; - - /** - * @var null|string Код КЛАДР района города (не заполняется) - */ - public ?string $city_district_kladr_id; - - /** - * @var null|string Район города с типом - */ - public ?string $city_district_with_type; - - /** - * @var null|string Тип района города (сокращенный) - */ - public ?string $city_district_type; - - /** - * @var null|string Тип района города - */ - public ?string $city_district_type_full; - - /** - * @var null|string Район города - */ - public ?string $city_district; - - /** - * @var null|string Код ФИАС нас. пункта - */ - public ?string $settlement_fias_id; - - /** - * @var null|string Код КЛАДР нас. пункта - */ - public ?string $settlement_kladr_id; - - /** - * @var null|string Населенный пункт с типом - */ - public ?string $settlement_with_type; - - /** - * @var null|string Тип населенного пункта (сокращенный) - */ - public ?string $settlement_type; - - /** - * @var null|string Тип населенного пункта - */ - public ?string $settlement_type_full; - - /** - * @var null|string Населенный пункт - */ - public ?string $settlement; - - /** - * @var null|string Код ФИАС улицы - */ - public ?string $street_fias_id; - - /** - * @var null|string Код КЛАДР улицы - */ - public ?string $street_kladr_id; - - /** - * @var null|string Улица с типом - */ - public ?string $street_with_type; - - /** - * @var null|string Тип улицы (сокращенный) - */ - public ?string $street_type; - - /** - * @var null|string Тип улицы - */ - public ?string $street_type_full; - - /** - * @var null|string Улица - */ - public ?string $street; - - /** - * @var null|string Код ФИАС дома - */ - public ?string $house_fias_id; - - /** - * @var null|string Код КЛАДР дома - */ - public ?string $house_kladr_id; - - /** - * @var null|string Тип дома (сокращенный) - */ - public ?string $house_type; - - /** - * @var null|string Тип дома - */ - public ?string $house_type_full; - - /** - * @var null|string Дом - */ - public ?string $house; - - /** - * @var null|string Тип корпуса/строения (сокращенный) - */ - public ?string $block_type; - - /** - * @var null|string Тип корпуса/строения - */ - public ?string $block_type_full; - - /** - * @var null|string Корпус/строение - */ - public ?string $block; - - /** - * @var null|string Тип квартиры (сокращенный) - */ - public ?string $flat_type; - - /** - * @var null|string Тип квартиры - */ - public ?string $flat_type_full; - - /** - * @var null|string Квартира - */ - public ?string $flat; - - /** - * @var null|float Площадь квартиры - */ - public ?float $flat_area; - - /** - * @var null|string Рыночная стоимость м² - */ - public ?string $square_meter_price; - - /** - * @var null|string Рыночная стоимость квартиры - */ - public ?string $flat_price; - - /** - * @var null|string Абонентский ящик - */ - public ?string $postal_box; - - /** - * @var null|string Код ФИАС: - * @see \AlexBklnv\DaData\Enums\FiasIdEnum - */ - public ?string $fias_id; - - /** - * @var null|int Уровень детализации, до которого адрес найден в ФИАС - * @see \AlexBklnv\DaData\Enums\FiasLevelEnum - */ - public ?int $fias_level; - - /** - * @var null|string Код КЛАДР - */ - public ?string $kladr_id; - - /** - * @var null|int Является ли город центром - * @see \AlexBklnv\DaData\Enums\CapitalMarkerEnum - */ - public ?int $capital_marker; - - /** - * @var null|string Код ОКАТО - */ - public ?string $okato; - - /** - * @var null|string Код ОКТМО - */ - public ?string $oktmo; - - /** - * @var null|string Код ИФНС для физических лиц - */ - public ?string $tax_office; - - /** - * @var null|string Код ИФНС для организаций (не заполняется) - */ - public ?string $tax_office_legal; - - /** - * @var null|string Часовой пояс - */ - public ?string $timezone; - - /** - * @var null|float Координаты: широта - */ - public ?float $geo_lat; - - /** - * @var null|float Координаты: долгота - */ - public ?float $geo_lon; - - /** - * @var null|string Внутри кольцевой? - * @see \AlexBklnv\DaData\Enums\BeltwayHitEnum - */ - public ?string $beltway_hit; - - /** - * @var null|int Расстояние от кольцевой в км. Заполнено, только если beltway_hit = OUT_MKAD или OUT_KAD, иначе пустое. - */ - public ?int $beltway_distance; - - /** - * @var null|int Код точности координат - * @see \AlexBklnv\DaData\Enums\QcEnum - */ - public ?int $qc; - - /** - * @var null|int Код точности координат - * @see \AlexBklnv\DaData\Enums\QcGeoEnum - */ - public ?int $qc_geo; - - /** - * @var null|int Код полноты - * @see \AlexBklnv\DaData\Enums\QcCompeteEnum - */ - public ?int $qc_complete; - - /** - * @var null|int Признак наличия дома в ФИАС - уточняет вероятность успешной доставки письма - * @link https://dadata.ru/api/clean/address/#qc_house - */ - public ?int $qc_house; - - /** - * @var null|string Нераспознанная часть адреса. Для адреса - */ - public ?string $unparsed_parts; - - /** - * @return string - */ - public function __toString(): string - { - return $this->result; - } -} diff --git a/tests/Benchmark/DTO/AddressDto.php b/tests/Benchmark/DTO/AddressDto.php deleted file mode 100644 index b4d430e..0000000 --- a/tests/Benchmark/DTO/AddressDto.php +++ /dev/null @@ -1,14 +0,0 @@ -getPurcheseObject(); - - $purchase = new PurchaseDto(); - - $user = new UserDto(); - $user->id = $data['user']['id']; - $user->email = $data['user']['contact']; - $user->balance = $data['user']['balance']; - $user->real_address = $data['user']['real_address'] ?? $data['user']['realAddress']; - $user->type = UserTypeEnum::from($data['user']['type']); - $purchase->user = $user; - - $address = new AddressDto(); - $address->city = $data['address']['city']; - $address->street = $data['address']['street']; - $address->house = $data['address']['house']; - - $purchase->address = $address; - - $purchase->createdAt = new \DateTime($data['createdAt']); - - foreach ($data['products'] as $product) { - $newProduct = new ProductDto(); - $newProduct->id = $product['id']; - $newProduct->name = $product['name']; - $newProduct->price = $product['price']; - $purchase->products [] = $newProduct; - } - - $this->assertEquals($data['user']['id'], $purchase->user->id); - } - - /** - * @Revs(10000) - */ - public function benchTransformReflection(): void - { - $data = $this->getPurcheseObject(); - $purchase = ClassTransformer::transform(PurchaseDto::class, $data); - - $this->assertEquals($data['user']['id'], $purchase->user->id); - } - - /** - * @Revs(10000) - */ - public function benchTransformCacheReflection(): void - { - ClassTransformerConfig::$cache = true; - $data = $this->getPurcheseObject(); - - $purchase = ClassTransformer::transform(PurchaseDto::class, $data); - - $this->assertEquals($data['user']['id'], $purchase->user->id); - } - - public function getPurcheseObject(): array - { - return [ - 'createdAt' => '2023-04-10 12:30:23', - 'products' => [ - [ - 'id' => 1, - 'name' => 'phone', - 'price' => 43.03, - 'description' => 'test description for phone', - 'count' => 123 - ], - [ - 'id' => 2, - 'name' => 'bread', - 'price' => 10.56, - 'description' => 'test description for bread', - 'count' => 321 - ], - [ - 'id' => 3, - 'name' => 'book', - 'price' => 5.5, - 'description' => 'test description for book', - 'count' => 333 - ], - [ - 'id' => 4, - 'name' => 'PC', - 'price' => 100, - 'description' => 'test description for PC', - 'count' => 7 - ] - ], - 'user' => [ - 'id' => 1, - 'contact' => 'fake@mail.com', - 'balance' => 10012.23, - 'type' => 'admin', - 'realAddress' => 'test address', - ], - 'address' => [ - 'city' => 'NY', - 'street' => 'street', - 'house' => '14', - ] - ]; - } -} diff --git a/tests/Benchmark/SerializeVsArrayBench.php b/tests/Benchmark/SerializeVsArrayBench.php index 82ade5f..6b8ead0 100644 --- a/tests/Benchmark/SerializeVsArrayBench.php +++ b/tests/Benchmark/SerializeVsArrayBench.php @@ -2,12 +2,12 @@ namespace Tests\Benchmark; -use ReflectionNamedType; -use PHPUnit\Framework\TestCase; use ClassTransformer\ClassTransformerConfig; use ClassTransformer\Reflection\CacheReflectionProperty; use ClassTransformer\Reflection\RuntimeReflectionProperty; -use Tests\Benchmark\DTO\UserDto; +use PHPUnit\Framework\TestCase; +use ReflectionNamedType; +use Tests\Benchmark\Bid\Dto\UserDto; /** * Class CheckBench @@ -18,7 +18,11 @@ */ class SerializeVsArrayBench extends TestCase { - + public function __construct() + { + parent::__construct('bench'); + } + /** * @Revs(10000) @@ -69,13 +73,14 @@ public function benchSerializeReflection(): void $attributes, ); } - + file_put_contents( $path, serialize($cache) ); - + } + public function benchArrayReflection(): void { $class = str_replace('\\', '_', UserDto::class); @@ -127,6 +132,6 @@ public function benchArrayReflection(): void $path, 'getRecursiveObject(); $data->orders = $this->getArrayUsers(); - ClassTransformerConfig::$cache = true; + ClassTransformerConfig::$cacheEnabled = true; $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); - ClassTransformerConfig::$cache = false; + ClassTransformerConfig::$cacheEnabled = false; self::assertInstanceOf(PurchaseDTO::class, $purchaseDTO); self::assertInstanceOf(UserDTO::class, $purchaseDTO->user); diff --git a/tests/Integration/DTO/WithAliasDTO.php b/tests/Integration/DTO/WithAliasDTO.php index e1cd213..f8a1a07 100644 --- a/tests/Integration/DTO/WithAliasDTO.php +++ b/tests/Integration/DTO/WithAliasDTO.php @@ -8,9 +8,10 @@ class WithAliasDTO { + #[FieldAlias(['email', 'phone'])] + public string $contact; #[FieldAlias('userFio')] public string $fio; - #[FieldAlias(['email', 'phone'])] - public string $contact; + } diff --git a/tests/Units/ArgumentsResourceTest.php b/tests/Units/ArgumentsResourceTest.php index c7b89af..3dcc9c6 100644 --- a/tests/Units/ArgumentsResourceTest.php +++ b/tests/Units/ArgumentsResourceTest.php @@ -2,7 +2,7 @@ namespace Tests\Units; -use ClassTransformer\ArgumentsResource; +use ClassTransformer\ArgumentsRepository; use ClassTransformer\Exceptions\ValueNotFoundException; use ClassTransformer\Reflection\RuntimeReflectionProperty; use PHPUnit\Framework\TestCase; @@ -13,7 +13,7 @@ class ArgumentsResourceTest extends TestCase public function testBaseKey(): void { $data = ['id' => 1]; - $resource = new ArgumentsResource($data); + $resource = new ArgumentsRepository($data); $value = $resource->getValue( new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'id')) ); @@ -23,7 +23,7 @@ public function testBaseKey(): void public function testBaseValueNotFoundException(): void { $this->expectException(ValueNotFoundException::class); - $resource = new ArgumentsResource(['test' => 1]); + $resource = new ArgumentsRepository(['test' => 1]); $resource->getValue( new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'id')) ); @@ -33,7 +33,7 @@ public function testEmptyWritingStyle(): void { $this->expectException(ValueNotFoundException::class); $data = ['id' => 1]; - $resource = new ArgumentsResource($data); + $resource = new ArgumentsRepository($data); $value = $resource->getValue( new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'addressOne')) ); @@ -43,7 +43,7 @@ public function testEmptyWritingStyle(): void public function testCamelCaseWritingStyleKey(): void { $data = ['addressTwo' => 'test']; - $resource = new ArgumentsResource($data); + $resource = new ArgumentsRepository($data); $value = $resource->getValue( new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'address_two')) ); @@ -52,7 +52,7 @@ public function testCamelCaseWritingStyleKey(): void $data = ['test_case' => 'test2']; $this->expectException(ValueNotFoundException::class); - $resource2 = new ArgumentsResource($data); + $resource2 = new ArgumentsRepository($data); $resource2->getValue( new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'testCase')) @@ -62,7 +62,7 @@ public function testCamelCaseWritingStyleKey(): void public function testSnakeCaseWritingStyleKey(): void { $data = ['address_three' => 'test']; - $resource = new ArgumentsResource($data); + $resource = new ArgumentsRepository($data); $value = $resource->getValue( new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'addressThree')) ); @@ -71,7 +71,7 @@ public function testSnakeCaseWritingStyleKey(): void $data = ['testCase' => 'test2']; $this->expectException(ValueNotFoundException::class); - $resource2 = new ArgumentsResource($data); + $resource2 = new ArgumentsRepository($data); $resource2->getValue( new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'test_case')) @@ -81,7 +81,7 @@ public function testSnakeCaseWritingStyleKey(): void public function testFinalValueNotFoundException(): void { $this->expectException(ValueNotFoundException::class); - $resource = new ArgumentsResource(['test' => 1]); + $resource = new ArgumentsRepository(['test' => 1]); $resource->getValue( new RuntimeReflectionProperty(new \ReflectionProperty(UserDTO::class, 'balance')) ); diff --git a/tests/Units/CacheGeneratorTest.php b/tests/Units/CacheGeneratorTest.php index 9f88230..cc39a43 100644 --- a/tests/Units/CacheGeneratorTest.php +++ b/tests/Units/CacheGeneratorTest.php @@ -50,7 +50,7 @@ public function testGenerateException(): void public function testGenerateCache(): void { - $cacheGenerator = new CacheGenerator(UserCacheableDTO::class); + $cacheGenerator = new CacheGenerator(UserCacheableDTO::class, ); $class = str_replace('\\', '_', UserCacheableDTO::class); $this->assertFileDoesNotExist(ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'); diff --git a/tests/Units/CacheReflectionClassTest.php b/tests/Units/CacheReflectionClassTest.php index 157dcf1..7d46374 100644 --- a/tests/Units/CacheReflectionClassTest.php +++ b/tests/Units/CacheReflectionClassTest.php @@ -34,7 +34,7 @@ protected function setUp(): void } } - public function testGetCache(): void + /* public function testGetCache(): void { $reflection = new CacheReflectionClass(UserCacheableDTO::class); $reflectionProperties = $reflection->getProperties(); @@ -49,7 +49,7 @@ public function testGetCache(): void $this->assertFalse($reflectionProperties[0]->notTransform()); $this->assertEmpty($reflectionProperties[0]->getDocComment()); $this->assertEmpty($reflectionProperties[0]->getAttribute('addressThree')); - } + }*/ protected function tearDown(): void { From e841949aac77a28ceeda750796fc044aa27f3e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 13 Jun 2023 21:41:19 +0300 Subject: [PATCH 080/100] issue part 1 --- src/CacheGenerator/CacheGenerator.php | 9 +- src/ClassTransformer.php | 1 + src/ClassTransformerConfig.php | 3 +- src/Contracts/ReflectionProperty.php | 11 +-- src/Hydrator.php | 7 ++ src/InstanceBuilder.php | 3 +- src/Reflection/CacheReflectionProperty.php | 18 +--- src/Reflection/RuntimeReflectionProperty.php | 88 +++++++------------ src/Reflection/Types/ArrayType.php | 14 +++ src/Reflection/Types/EnumType.php | 13 +++ src/Reflection/Types/TransformableType.php | 13 +++ src/Reflection/Types/TypeEnums.php | 18 ++++ src/ValueCasting.php | 50 ++++++----- .../ClassTransformerFromArrayTest.php | 4 +- tests/Integration/DTO/ArrayScalarDTO.php | 5 +- tests/Units/CacheGeneratorTest.php | 1 - tests/Units/DTO/ExtendedDto.php | 2 +- tests/Units/RuntimeReflectionPropertyTest.php | 1 - 18 files changed, 143 insertions(+), 118 deletions(-) create mode 100644 src/Reflection/Types/ArrayType.php create mode 100644 src/Reflection/Types/EnumType.php create mode 100644 src/Reflection/Types/TransformableType.php create mode 100644 src/Reflection/Types/TypeEnums.php diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index c93ed58..147bbc6 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -91,20 +91,13 @@ public function generate(): array */ private function convertToCacheProperty(RuntimeReflectionProperty $property): CacheReflectionProperty { - if ($property->type instanceof ReflectionNamedType) { - $type = $property->type->getName(); - } else { - $type = $property->type; - } return new CacheReflectionProperty( $property->class, $property->name, - (string)$type, - $property->types, + $property->type, $property->isScalar, $property->hasSetMutator(), - $property->isArray(), $property->isEnum(), $property->notTransform(), $property->transformable(), diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 3ed1f87..9282586 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -10,6 +10,7 @@ * Class ClassTransformer * * @psalm-api + * @deprecated */ final class ClassTransformer { diff --git a/src/ClassTransformerConfig.php b/src/ClassTransformerConfig.php index 04aa8d8..d548fb8 100644 --- a/src/ClassTransformerConfig.php +++ b/src/ClassTransformerConfig.php @@ -8,12 +8,13 @@ * Class ClassTransformerConfig * * @psalm-api + * @deprecated */ final class ClassTransformerConfig { /** @var bool Cache mode enabled */ public static bool $cacheEnabled = false; - + /** @var string Path to the cache directory */ public static string $cachePath = __DIR__ . '/../.cache'; } diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index 619eb6c..4c8d91c 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -13,9 +13,9 @@ interface ReflectionProperty public function isScalar(): bool; /** - * @return false|class-string + * @return bool */ - public function transformable(): false|string; + public function transformable(): bool; /** * @return string @@ -53,13 +53,6 @@ public function getDocComment(): string; */ public function isEnum(): bool; - /** - * Finds whether a variable is an array - * - * @return bool - */ - public function isArray(): bool; - /** * @return bool */ diff --git a/src/Hydrator.php b/src/Hydrator.php index 130b1b6..cfba7ab 100644 --- a/src/Hydrator.php +++ b/src/Hydrator.php @@ -32,6 +32,13 @@ public function __construct(HydratorConfig $config = null) $this->config = $config ?? new HydratorConfig(); } + /** + */ + public static function init(HydratorConfig $config = null) + { + return new self($config); + } + /** * Create instance T class * diff --git a/src/InstanceBuilder.php b/src/InstanceBuilder.php index af48499..c0049df 100644 --- a/src/InstanceBuilder.php +++ b/src/InstanceBuilder.php @@ -16,7 +16,7 @@ final class InstanceBuilder { private HydratorConfig $config; - + /** @var ClassRepository $classRepository */ private ClassRepository $class; @@ -60,6 +60,7 @@ public function build(): mixed $caster = new ValueCasting($property, $this->config); $genericInstance->{$property->getName()} = $caster->castAttribute($value); + } return $genericInstance; } diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index d30ca4d..10f8f56 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -21,15 +21,13 @@ public function __construct( public string $class, public string $name, public ?string $type, - public array $types, public bool $isScalar, public bool $hasSetMutator, - public bool $isArray, public bool $isEnum, public bool $notTransform, - public false|string $transformable, + public bool $transformable, public string $docComment, - public array $attributes, + public array $attributes ) { } @@ -57,14 +55,6 @@ public function isEnum(): bool return $this->isEnum; } - /** - * @return bool - */ - public function isArray(): bool - { - return $this->isArray; - } - /** * @return bool */ @@ -74,9 +64,9 @@ public function notTransform(): bool } /** - * @return false|class-string + * @return bool */ - public function transformable(): false|string + public function transformable(): bool { return $this->transformable; } diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index cb9b92d..d48b6ce 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -4,6 +4,7 @@ namespace ClassTransformer\Reflection; +use ClassTransformer\Reflection\Types\TypeEnums; use ReflectionType; use ReflectionProperty; use ReflectionNamedType; @@ -25,8 +26,8 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref /** @var ReflectionProperty */ public ReflectionProperty $property; - /** @var ReflectionType|ReflectionNamedType|null */ - public ReflectionType|ReflectionNamedType|null $type; + /** @var string */ + public string $type; /** @var array|string[] */ public array $types; @@ -40,8 +41,8 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref /** @var bool */ public bool $isScalar; - /** @var array>> */ - private static array $attributeTypesCache = []; + /** @var bool */ + public bool $nullable; /** @var array>> */ private static array $attributesCache = []; @@ -53,21 +54,31 @@ public function __construct(ReflectionProperty $property) { $this->property = $property; $this->class = $property->class; - $this->type = $this->property->getType(); + $type = $this->property->getType(); + + if ($type === null) { + $this->type = TypeEnums::TYPE_MIXED; + $this->nullable = true; + $this->isScalar = true; + } else if ($type instanceof ReflectionNamedType) { + $this->type = $type->getName(); + $this->nullable = $type->allowsNull(); + $this->isScalar = $type->isBuiltin(); + } else { + $this->isScalar = $type->isBuiltin(); + $this->nullable = $type->allowsNull(); + $this->type = (string)$type; + } + $this->name = $this->property->name; - $this->types = $this->initTypes(); - $this->isScalar = sizeof(array_intersect($this->types, ['int', 'float', 'double', 'string', 'bool', 'mixed'])) > 0; + + } - /** - * @return null|string - */ + public function getType(): ?string { - if ($this->type instanceof ReflectionNamedType) { - return $this->type->getName(); - } - return (string)$this->type; + return $this->type; } /** @@ -78,46 +89,7 @@ public function isEnum(): bool if (!function_exists('enum_exists')) { return false; } - if ($this->type instanceof ReflectionNamedType) { - return enum_exists($this->type->getName()); - } - return false; - } - - /** - * @return array - */ - private function initTypes(): array - { - if (isset(self::$attributeTypesCache[$this->class][$this->name])) { - return self::$attributeTypesCache[$this->class][$this->name]; - } - - if ($this->type === null) { - return []; - } - - $types = []; - - if ($this->type instanceof ReflectionNamedType) { - $types = [$this->type->getName()]; - } - - if ($this->type->allowsNull()) { - $types [] = 'null'; - } - - return self::$attributeTypesCache[$this->class][$this->name] = $types; - } - - /** - * Finds whether a variable is an array - * - * @return bool - */ - public function isArray(): bool - { - return in_array('array', $this->types, true); + return !$this->isScalar && enum_exists($this->type); } /** @@ -183,11 +155,11 @@ public function isScalar(): bool } /** - * @return false|class-string + * @return bool */ - public function transformable(): false|string + public function transformable(): bool { - return $this->type instanceof ReflectionNamedType ? $this->type->getName() : false; + return !$this->isScalar && $this->type !== TypeEnums::TYPE_ARRAY; } /** @@ -210,7 +182,7 @@ public function getAliases(): array } $aliases = $aliases[0]; - + if (is_string($aliases)) { $aliases = [$aliases]; } diff --git a/src/Reflection/Types/ArrayType.php b/src/Reflection/Types/ArrayType.php new file mode 100644 index 0000000..54ca1b1 --- /dev/null +++ b/src/Reflection/Types/ArrayType.php @@ -0,0 +1,14 @@ + + */ +class ArrayType +{ + public string $itemsType; + public string $isScalar; +} diff --git a/src/Reflection/Types/EnumType.php b/src/Reflection/Types/EnumType.php new file mode 100644 index 0000000..0786ff9 --- /dev/null +++ b/src/Reflection/Types/EnumType.php @@ -0,0 +1,13 @@ + + */ +class EnumType +{ + +} diff --git a/src/Reflection/Types/TransformableType.php b/src/Reflection/Types/TransformableType.php new file mode 100644 index 0000000..96a4e84 --- /dev/null +++ b/src/Reflection/Types/TransformableType.php @@ -0,0 +1,13 @@ + + */ +class TransformableType +{ + public string $class; +} diff --git a/src/Reflection/Types/TypeEnums.php b/src/Reflection/Types/TypeEnums.php new file mode 100644 index 0000000..1eb3b65 --- /dev/null +++ b/src/Reflection/Types/TypeEnums.php @@ -0,0 +1,18 @@ + + */ +class TypeEnums +{ + public const TYPE_STRING = 'string'; + public const TYPE_INTEGER = 'int'; + public const TYPE_FLOAT = 'float'; + public const TYPE_BOOLEAN = 'bool'; + public const TYPE_ARRAY = 'array'; + public const TYPE_MIXED = 'mixed'; +} diff --git a/src/ValueCasting.php b/src/ValueCasting.php index 7b2aacd..eb8cdce 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -8,6 +8,7 @@ use ClassTransformer\Contracts\ReflectionProperty; use ClassTransformer\Exceptions\ClassNotFoundException; +use ClassTransformer\Reflection\Types\TypeEnums; use function method_exists; use function is_array; use function in_array; @@ -19,6 +20,9 @@ final class ValueCasting { + /** + * @var HydratorConfig + */ private HydratorConfig $config; /** @var ReflectionProperty $property */ @@ -42,17 +46,11 @@ public function __construct(ReflectionProperty $property, HydratorConfig $config */ public function castAttribute(mixed $value): mixed { - if ($this->property->isScalar() || $this->property->notTransform()) { - return match ($this->property->getType()) { - 'string' => (string)$value, - 'int' => (int)$value, - 'float' => (float)$value, - 'bool' => (bool)$value, - default => $value - }; + if (($this->property->isScalar() && $this->property->getType() !== TypeEnums::TYPE_ARRAY) || $this->property->notTransform()) { + return $this->castScalar($this->property->getType(), $value); } - if ($this->property->isArray()) { + if ($this->property->getType() === TypeEnums::TYPE_ARRAY) { return $this->castArray($value); } @@ -60,16 +58,32 @@ public function castAttribute(mixed $value): mixed return $this->castEnum($value); } - $propertyClass = $this->property->transformable(); - if ($propertyClass) { + if ($this->property->transformable()) { return (new Hydrator($this->config)) - ->create($propertyClass, $value); + ->create($this->property->getType(), $value); } return $value; } + /** + * @param string $type + * @param mixed $value + * + * @return mixed + */ + private function castScalar(string $type, mixed $value): mixed + { + return match ($type) { + TypeEnums::TYPE_STRING => (string)$value, + TypeEnums::TYPE_INTEGER => (int)$value, + TypeEnums::TYPE_FLOAT => (float)$value, + TypeEnums::TYPE_BOOLEAN => (bool)$value, + default => $value + }; + } + /** * @param array|mixed $value * @@ -89,17 +103,11 @@ private function castArray($value): mixed if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { return $value; } - if (!in_array($arrayType, ['int', 'float', 'string', 'bool', 'boolean', 'mixed'])) { + if (!in_array($arrayType, [TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_STRING, TypeEnums::TYPE_BOOLEAN, TypeEnums::TYPE_MIXED])) { return array_map(fn($el) => (new Hydrator($this->config))->create($arrayType, $el), $value); } - return array_map(static fn($el) => match ($arrayType) { - 'string' => (string)$el, - 'int' => (int)$el, - 'float' => (float)$el, - 'bool', 'boolean' => (bool)$el, - default => $el - }, $value); + return array_map(fn($item) => $this->castScalar($arrayType, $item), $value); } /** @@ -109,7 +117,7 @@ private function castArray($value): mixed */ private function castEnum(int|string $value): mixed { - $propertyClass = $this->property->transformable(); + $propertyClass = $this->property->getType(); if ($propertyClass && method_exists($propertyClass, 'from')) { /** @var \BackedEnum $propertyClass */ return $propertyClass::from($value); diff --git a/tests/Integration/ClassTransformerFromArrayTest.php b/tests/Integration/ClassTransformerFromArrayTest.php index e1d2225..59b3636 100644 --- a/tests/Integration/ClassTransformerFromArrayTest.php +++ b/tests/Integration/ClassTransformerFromArrayTest.php @@ -75,12 +75,14 @@ public function testScalarArray(): void { $data = [ 'stringList' => [100, 200, 300], - 'intList' => [100, 200, 300] + 'intList' => [400, 500, 600] ]; $dto = ClassTransformer::transform(ArrayScalarDTO::class, $data); self::assertInstanceOf(ArrayScalarDTO::class, $dto); self::assertIsString($dto->stringList[0]); + self::assertEquals($dto->stringList[0], '100'); self::assertIsInt($dto->intList[0]); + self::assertEquals($dto->intList[0], 400); } /** diff --git a/tests/Integration/DTO/ArrayScalarDTO.php b/tests/Integration/DTO/ArrayScalarDTO.php index 1886cf9..7adae41 100644 --- a/tests/Integration/DTO/ArrayScalarDTO.php +++ b/tests/Integration/DTO/ArrayScalarDTO.php @@ -7,10 +7,11 @@ class ArrayScalarDTO { - public $id; - #[ConvertArray('string')] public ?array $stringList; + public $id; + + #[ConvertArray('int')] public ?array $intList; } diff --git a/tests/Units/CacheGeneratorTest.php b/tests/Units/CacheGeneratorTest.php index cc39a43..c2b4b2e 100644 --- a/tests/Units/CacheGeneratorTest.php +++ b/tests/Units/CacheGeneratorTest.php @@ -71,7 +71,6 @@ public function testGenerateCache(): void $this->assertEquals('int', $property->type); $this->assertTrue($property->isScalar); $this->assertFalse($property->hasSetMutator); - $this->assertFalse($property->isArray); $this->assertFalse($property->isEnum); $this->assertFalse($property->notTransform); $this->assertEmpty($property->docComment); diff --git a/tests/Units/DTO/ExtendedDto.php b/tests/Units/DTO/ExtendedDto.php index cfd82af..5526b23 100644 --- a/tests/Units/DTO/ExtendedDto.php +++ b/tests/Units/DTO/ExtendedDto.php @@ -40,7 +40,7 @@ class ExtendedDto /** @var array */ public array $boolItems; - /** @var array */ + /** @var array */ public array $booleanItems; /** @var array */ diff --git a/tests/Units/RuntimeReflectionPropertyTest.php b/tests/Units/RuntimeReflectionPropertyTest.php index 3dde508..6d25fa9 100644 --- a/tests/Units/RuntimeReflectionPropertyTest.php +++ b/tests/Units/RuntimeReflectionPropertyTest.php @@ -17,7 +17,6 @@ public function testCreatePropery(): void $this->assertTrue($property->isScalar()); $this->assertTrue($property->hasSetMutator()); $this->assertFalse($property->isEnum()); - $this->assertFalse($property->isArray()); $this->assertEquals('email', $property->getName()); $property = new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'color')); From 98d6642104147b4c0e24e0da2b471b2dd1236c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 14 Jun 2023 09:36:14 +0300 Subject: [PATCH 081/100] upd: class for type --- composer.json | 2 +- src/CacheGenerator/CacheGenerator.php | 6 +- src/Contracts/ReflectionProperty.php | 23 ++--- src/{Reflection/Types => Enums}/TypeEnums.php | 2 +- src/Reflection/CacheReflectionProperty.php | 26 +----- src/Reflection/RuntimeReflectionProperty.php | 77 +++-------------- src/Reflection/Types/ArrayType.php | 22 ++++- src/Reflection/Types/EnumType.php | 4 +- src/Reflection/Types/PropertyType.php | 37 ++++++++ src/Reflection/Types/PropertyTypeFactory.php | 85 +++++++++++++++++++ src/Reflection/Types/ScalarType.php | 12 +++ src/Reflection/Types/TransformableType.php | 5 +- src/ValueCasting.php | 45 +++++----- tests/Integration/AfterTransformTest.php | 6 +- tests/Units/CacheGeneratorTest.php | 9 +- tests/Units/RuntimeReflectionPropertyTest.php | 11 +-- 16 files changed, 216 insertions(+), 156 deletions(-) rename src/{Reflection/Types => Enums}/TypeEnums.php (88%) create mode 100644 src/Reflection/Types/PropertyType.php create mode 100644 src/Reflection/Types/PropertyTypeFactory.php create mode 100644 src/Reflection/Types/ScalarType.php diff --git a/composer.json b/composer.json index 0cf2e2e..88d185b 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ } }, "require": { - "php": "^8.0" + "php": "^8.1" }, "require-dev": { "mockery/mockery": "1.5.1", diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index 147bbc6..f7b641b 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -91,16 +91,12 @@ public function generate(): array */ private function convertToCacheProperty(RuntimeReflectionProperty $property): CacheReflectionProperty { - return new CacheReflectionProperty( $property->class, $property->name, - $property->type, - $property->isScalar, + $property->getType(), $property->hasSetMutator(), - $property->isEnum(), $property->notTransform(), - $property->transformable(), $property->getDocComment(), $this->getArguments($property), ); diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index 4c8d91c..b735089 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -2,20 +2,13 @@ namespace ClassTransformer\Contracts; +use ClassTransformer\Reflection\Types\PropertyType; + /** * @psalm-api */ interface ReflectionProperty { - /** - * @return bool - */ - public function isScalar(): bool; - - /** - * @return bool - */ - public function transformable(): bool; /** * @return string @@ -23,9 +16,9 @@ public function transformable(): bool; public function getName(): string; /** - * @return string + * @return PropertyType */ - public function getType(): ?string; + public function getType(): PropertyType; /** * @param string $name @@ -45,13 +38,7 @@ public function getAttributeArguments(string $name): ?array; * @return string */ public function getDocComment(): string; - - /** - * Finds whether a variable is an enum - * - * @return bool - */ - public function isEnum(): bool; + /** * @return bool diff --git a/src/Reflection/Types/TypeEnums.php b/src/Enums/TypeEnums.php similarity index 88% rename from src/Reflection/Types/TypeEnums.php rename to src/Enums/TypeEnums.php index 1eb3b65..d0a93ba 100644 --- a/src/Reflection/Types/TypeEnums.php +++ b/src/Enums/TypeEnums.php @@ -1,6 +1,6 @@ type; } @@ -63,22 +61,6 @@ public function notTransform(): bool return $this->notTransform; } - /** - * @return bool - */ - public function transformable(): bool - { - return $this->transformable; - } - - /** - * @return bool - */ - public function isScalar(): bool - { - return $this->isScalar; - } - /** * @return string */ diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index d48b6ce..95be521 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -4,19 +4,16 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Reflection\Types\TypeEnums; -use ReflectionType; -use ReflectionProperty; -use ReflectionNamedType; -use ReflectionAttribute; -use ClassTransformer\TransformUtils; use ClassTransformer\Attributes\FieldAlias; use ClassTransformer\Attributes\NotTransform; - -use function sizeof; -use function in_array; +use ClassTransformer\Enums\TypeEnums; +use ClassTransformer\Reflection\Types\PropertyType; +use ClassTransformer\Reflection\Types\PropertyTypeFactory; +use ClassTransformer\TransformUtils; +use ReflectionAttribute; +use ReflectionNamedType; +use ReflectionProperty; use function method_exists; -use function array_intersect; /** * Class GenericProperty @@ -26,11 +23,8 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref /** @var ReflectionProperty */ public ReflectionProperty $property; - /** @var string */ - public string $type; - - /** @var array|string[] */ - public array $types; + /** @var PropertyType */ + private PropertyType $type; /** @var class-string|string $propertyClass */ public string $name; @@ -38,11 +32,6 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref /** @var class-string */ public string $class; - /** @var bool */ - public bool $isScalar; - - /** @var bool */ - public bool $nullable; /** @var array>> */ private static array $attributesCache = []; @@ -54,44 +43,16 @@ public function __construct(ReflectionProperty $property) { $this->property = $property; $this->class = $property->class; - $type = $this->property->getType(); - - if ($type === null) { - $this->type = TypeEnums::TYPE_MIXED; - $this->nullable = true; - $this->isScalar = true; - } else if ($type instanceof ReflectionNamedType) { - $this->type = $type->getName(); - $this->nullable = $type->allowsNull(); - $this->isScalar = $type->isBuiltin(); - } else { - $this->isScalar = $type->isBuiltin(); - $this->nullable = $type->allowsNull(); - $this->type = (string)$type; - } - $this->name = $this->property->name; - + $this->type = PropertyTypeFactory::create($this); } - - public function getType(): ?string + public function getType(): PropertyType { return $this->type; } - /** - * @return bool - */ - public function isEnum(): bool - { - if (!function_exists('enum_exists')) { - return false; - } - return !$this->isScalar && enum_exists($this->type); - } - /** * @return string */ @@ -146,22 +107,6 @@ public function hasSetMutator(): bool return method_exists($this->class, TransformUtils::mutationSetterToCamelCase($this->name)); } - /** - * @return bool - */ - public function isScalar(): bool - { - return $this->isScalar; - } - - /** - * @return bool - */ - public function transformable(): bool - { - return !$this->isScalar && $this->type !== TypeEnums::TYPE_ARRAY; - } - /** * @return string */ diff --git a/src/Reflection/Types/ArrayType.php b/src/Reflection/Types/ArrayType.php index 54ca1b1..73f91a3 100644 --- a/src/Reflection/Types/ArrayType.php +++ b/src/Reflection/Types/ArrayType.php @@ -2,13 +2,31 @@ namespace ClassTransformer\Reflection\Types; +use ClassTransformer\Enums\TypeEnums; + /** * Class ArrayType * * @author yzen.dev */ -class ArrayType +class ArrayType extends PropertyType { public string $itemsType; - public string $isScalar; + public bool $isScalarItems; + + /** + * @return string + */ + public function getItemsType(): string + { + return $this->itemsType; + } + + /** + * @return bool + */ + public function isScalarItems(): bool + { + return $this->isScalarItems; + } } diff --git a/src/Reflection/Types/EnumType.php b/src/Reflection/Types/EnumType.php index 0786ff9..b5f39b3 100644 --- a/src/Reflection/Types/EnumType.php +++ b/src/Reflection/Types/EnumType.php @@ -3,11 +3,11 @@ namespace ClassTransformer\Reflection\Types; /** - * Class ArrayType + * Class EnumType * * @author yzen.dev */ -class EnumType +class EnumType extends PropertyType { } diff --git a/src/Reflection/Types/PropertyType.php b/src/Reflection/Types/PropertyType.php new file mode 100644 index 0000000..b6ca718 --- /dev/null +++ b/src/Reflection/Types/PropertyType.php @@ -0,0 +1,37 @@ + + */ +class PropertyType +{ + public function __construct( + private bool $nullable, + private string $typeStr, + private bool $isScalar + ) { + } + + public function isNullable() + { + return $this->nullable; + } + + public function isScalar(): bool + { + return $this->isScalar; + } + + public function getTypeStr() + { + return $this->typeStr; + } +} diff --git a/src/Reflection/Types/PropertyTypeFactory.php b/src/Reflection/Types/PropertyTypeFactory.php new file mode 100644 index 0000000..bc4f407 --- /dev/null +++ b/src/Reflection/Types/PropertyTypeFactory.php @@ -0,0 +1,85 @@ + + */ +class PropertyTypeFactory +{ + public static function create(RuntimeReflectionProperty $property) + { + $reflectionType = $property->property->getType(); + if ($reflectionType === null) { + $type = TypeEnums::TYPE_MIXED; + $isNullable = true; + $isScalar = true; + } elseif ($reflectionType instanceof ReflectionNamedType) { + $type = $reflectionType->getName(); + $isNullable = $reflectionType->allowsNull(); + $isScalar = $reflectionType->isBuiltin(); + } else { + $type = (string)$reflectionType; + $isScalar = $reflectionType->isBuiltin(); + $isNullable = $reflectionType->allowsNull(); + } + + if (($isScalar && $type !== TypeEnums::TYPE_ARRAY) || $property->notTransform()) { + return new ScalarType( + $isNullable, + $type, + $isScalar + ); + } + + if ($type === TypeEnums::TYPE_ARRAY) { + $arrayTypeAttr = $property->getAttributeArguments(ConvertArray::class); + + if ($arrayTypeAttr !== null && isset($arrayTypeAttr[0])) { + $arrayType = $arrayTypeAttr[0]; + } else { + $arrayType = TransformUtils::getClassFromPhpDoc($property->getDocComment()); + } + $arrayType ??= TypeEnums::TYPE_MIXED; + $type = new ArrayType( + $isNullable, + $type, + $isScalar + ); + $type->itemsType = $arrayType ?? TypeEnums::TYPE_MIXED; + $type->isScalarItems = in_array($arrayType, [TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_STRING, TypeEnums::TYPE_BOOLEAN, TypeEnums::TYPE_MIXED]); + + return $type; + } + + if (function_exists('enum_exists') && !$isScalar && enum_exists($type)) { + return new EnumType( + $isNullable, + $type, + $isScalar + ); + } + + if (!$isScalar) { + return new TransformableType( + $isNullable, + $type, + $isScalar + ); + } + + return new PropertyType( + $isNullable, + $type, + $isScalar + ); + } +} diff --git a/src/Reflection/Types/ScalarType.php b/src/Reflection/Types/ScalarType.php new file mode 100644 index 0000000..6729748 --- /dev/null +++ b/src/Reflection/Types/ScalarType.php @@ -0,0 +1,12 @@ + + */ +class ScalarType extends PropertyType +{ +} diff --git a/src/Reflection/Types/TransformableType.php b/src/Reflection/Types/TransformableType.php index 96a4e84..dcfb0cd 100644 --- a/src/Reflection/Types/TransformableType.php +++ b/src/Reflection/Types/TransformableType.php @@ -3,11 +3,10 @@ namespace ClassTransformer\Reflection\Types; /** - * Class ArrayType + * Class TransformableType * * @author yzen.dev */ -class TransformableType +class TransformableType extends PropertyType { - public string $class; } diff --git a/src/ValueCasting.php b/src/ValueCasting.php index eb8cdce..973c37f 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -6,13 +6,15 @@ use ClassTransformer\Attributes\ConvertArray; use ClassTransformer\Contracts\ReflectionProperty; +use ClassTransformer\Enums\TypeEnums; use ClassTransformer\Exceptions\ClassNotFoundException; - -use ClassTransformer\Reflection\Types\TypeEnums; -use function method_exists; -use function is_array; -use function in_array; +use ClassTransformer\Reflection\Types\ArrayType; +use ClassTransformer\Reflection\Types\EnumType; +use ClassTransformer\Reflection\Types\TransformableType; use function array_map; +use function in_array; +use function is_array; +use function method_exists; /** * Class GenericInstance @@ -46,21 +48,22 @@ public function __construct(ReflectionProperty $property, HydratorConfig $config */ public function castAttribute(mixed $value): mixed { - if (($this->property->isScalar() && $this->property->getType() !== TypeEnums::TYPE_ARRAY) || $this->property->notTransform()) { - return $this->castScalar($this->property->getType(), $value); + + if (($this->property->getType()->isScalar() && !$this->property->getType() instanceof ArrayType) || $this->property->notTransform()) { + return $this->castScalar($this->property->getType()->getTypeStr(), $value); } - if ($this->property->getType() === TypeEnums::TYPE_ARRAY) { + if ($this->property->getType() instanceof ArrayType) { return $this->castArray($value); } - - if ((is_string($value) || is_int($value)) && $this->property->isEnum()) { + + if ((is_string($value) || is_int($value)) && $this->property->getType() instanceof EnumType) { return $this->castEnum($value); } - if ($this->property->transformable()) { + if ($this->property->getType() instanceof TransformableType) { return (new Hydrator($this->config)) - ->create($this->property->getType(), $value); + ->create($this->property->getType()->getTypeStr(), $value); } return $value; @@ -92,22 +95,14 @@ private function castScalar(string $type, mixed $value): mixed */ private function castArray($value): mixed { - $arrayTypeAttr = $this->property->getAttributeArguments(ConvertArray::class); - - if ($arrayTypeAttr !== null && isset($arrayTypeAttr[0])) { - $arrayType = $arrayTypeAttr[0]; - } else { - $arrayType = TransformUtils::getClassFromPhpDoc($this->property->getDocComment()); - } - - if (empty($arrayType) || !is_array($value) || $arrayType === 'mixed') { + if (!is_array($value) || $this->property->getType()->getTypeStr() === TypeEnums::TYPE_MIXED) { return $value; } - if (!in_array($arrayType, [TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_STRING, TypeEnums::TYPE_BOOLEAN, TypeEnums::TYPE_MIXED])) { - return array_map(fn($el) => (new Hydrator($this->config))->create($arrayType, $el), $value); + if (!$this->property->getType()->isScalarItems()) { + return array_map(fn($el) => (new Hydrator($this->config))->create($this->property->getType()->getItemsType(), $el), $value); } - return array_map(fn($item) => $this->castScalar($arrayType, $item), $value); + return array_map(fn($item) => $this->castScalar($this->property->getType()->getItemsType(), $item), $value); } /** @@ -117,7 +112,7 @@ private function castArray($value): mixed */ private function castEnum(int|string $value): mixed { - $propertyClass = $this->property->getType(); + $propertyClass = $this->property->getType()->getTypeStr(); if ($propertyClass && method_exists($propertyClass, 'from')) { /** @var \BackedEnum $propertyClass */ return $propertyClass::from($value); diff --git a/tests/Integration/AfterTransformTest.php b/tests/Integration/AfterTransformTest.php index c0ddcd4..100a65b 100644 --- a/tests/Integration/AfterTransformTest.php +++ b/tests/Integration/AfterTransformTest.php @@ -4,6 +4,7 @@ namespace Tests\Integration; +use ClassTransformer\Hydrator; use PHPUnit\Framework\TestCase; use ClassTransformer\ClassTransformer; use Tests\Integration\DTO\UserAfterTransformDTO; @@ -23,7 +24,10 @@ class AfterTransformTest extends TestCase public function testAfterTransformStyle(): void { $data = $this->getBaseObject(); - $userDTO = ClassTransformer::transform(UserAfterTransformDTO::class, $data); + + $userDTO = (new Hydrator()) + ->create(UserAfterTransformDTO::class, $data); + self::assertInstanceOf(UserAfterTransformDTO::class, $userDTO); self::assertEquals($data->id, $userDTO->id); self::assertEquals($data->email, $userDTO->email); diff --git a/tests/Units/CacheGeneratorTest.php b/tests/Units/CacheGeneratorTest.php index c2b4b2e..7498aa4 100644 --- a/tests/Units/CacheGeneratorTest.php +++ b/tests/Units/CacheGeneratorTest.php @@ -6,6 +6,7 @@ use ClassTransformer\CacheGenerator\CacheGenerator; use ClassTransformer\ClassTransformerConfig; use ClassTransformer\Reflection\CacheReflectionProperty; +use ClassTransformer\Reflection\Types\EnumType; use PHPUnit\Framework\TestCase; use ReflectionClass; use RuntimeException; @@ -68,10 +69,8 @@ public function testGenerateCache(): void $this->assertEquals(UserCacheableDTO::class, $property->class); $this->assertEquals('id', $property->name); - $this->assertEquals('int', $property->type); - $this->assertTrue($property->isScalar); + $this->assertEquals('int', $property->getType()->getTypeStr()); $this->assertFalse($property->hasSetMutator); - $this->assertFalse($property->isEnum); $this->assertFalse($property->notTransform); $this->assertEmpty($property->docComment); $this->assertIsArray($property->attributes); @@ -85,8 +84,8 @@ public function testGenerateCache(): void /** @var CacheReflectionProperty $property */ $property = $cache['properties'][8]; - $this->assertIsString($property->getType()); - $this->assertEquals(ColorEnum::class, $property->getType()); + $this->assertInstanceOf(EnumType::class, $property->getType()); + $this->assertEquals(ColorEnum::class, $property->getType()->getTypeStr()); $cacheGenerator->generate(); } diff --git a/tests/Units/RuntimeReflectionPropertyTest.php b/tests/Units/RuntimeReflectionPropertyTest.php index 6d25fa9..a5bdce5 100644 --- a/tests/Units/RuntimeReflectionPropertyTest.php +++ b/tests/Units/RuntimeReflectionPropertyTest.php @@ -2,6 +2,7 @@ namespace Tests\Units; +use ClassTransformer\Reflection\Types\EnumType; use PHPUnit\Framework\TestCase; use Tests\Units\DTO\ColorEnum; use Tests\Units\DTO\ExtendedDto; @@ -13,14 +14,14 @@ class RuntimeReflectionPropertyTest extends TestCase public function testCreatePropery(): void { $property = new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'email')); - $this->assertEquals('string', $property->getType()); - $this->assertTrue($property->isScalar()); + $this->assertEquals('string', $property->getType()->getTypeStr()); + $this->assertTrue($property->getType()->isScalar()); $this->assertTrue($property->hasSetMutator()); - $this->assertFalse($property->isEnum()); $this->assertEquals('email', $property->getName()); $property = new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'color')); - $this->assertTrue($property->isEnum()); - $this->assertEquals(ColorEnum::class, $property->getType()); + + $this->assertInstanceOf(EnumType::class,$property->getType()); + $this->assertEquals(ColorEnum::class,$property->getType()->getTypeStr()); } } From 20922b150fb62dfe6ae56f4c07be4a015f63eeb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 14 Jun 2023 11:59:47 +0300 Subject: [PATCH 082/100] optimize --- src/ArgumentsRepository.php | 8 +- src/CacheGenerator/CacheGenerator.php | 71 ++-- src/ClassTransformerConfig.php | 20 -- src/Contracts/ReflectionProperty.php | 29 +- src/Enums/TypeEnums.php | 2 + src/Hydrator.php | 5 +- src/HydratorConfig.php | 8 +- src/InstanceBuilder.php | 4 +- src/Reflection/CacheReflectionClass.php | 2 - src/Reflection/CacheReflectionProperty.php | 37 +- src/Reflection/RuntimeReflectionProperty.php | 47 +-- src/Reflection/Types/ArrayType.php | 20 +- src/Reflection/Types/PropertyType.php | 27 +- src/Reflection/Types/PropertyTypeFactory.php | 34 +- src/Reflection/Types/ScalarType.php | 2 + src/Reflection/Types/TransformableType.php | 2 + src/ValueCasting.php | 25 +- tests/Benchmark/Bid/ManyCheckBench.php | 335 ++++++++++++++++++ .../ClassTransformerFromCacheTest.php | 12 +- tests/Units/CacheGeneratorTest.php | 43 +-- tests/Units/CacheReflectionClassTest.php | 58 --- tests/Units/RuntimeReflectionPropertyTest.php | 10 +- 22 files changed, 505 insertions(+), 296 deletions(-) delete mode 100644 src/ClassTransformerConfig.php create mode 100644 tests/Benchmark/Bid/ManyCheckBench.php delete mode 100644 tests/Units/CacheReflectionClassTest.php diff --git a/src/ArgumentsRepository.php b/src/ArgumentsRepository.php index 59d9c0e..3da6161 100644 --- a/src/ArgumentsRepository.php +++ b/src/ArgumentsRepository.php @@ -48,8 +48,8 @@ public function __construct(...$args) */ public function getValue(ReflectionProperty $genericProperty): mixed { - if (array_key_exists($genericProperty->getName(), $this->args)) { - return $this->args[$genericProperty->getName()]; + if (array_key_exists($genericProperty->name, $this->args)) { + return $this->args[$genericProperty->name]; } $aliases = $genericProperty->getAliases(); @@ -68,12 +68,12 @@ public function getValue(ReflectionProperty $genericProperty): mixed throw new ValueNotFoundException(); } - $snakeCase = TransformUtils::attributeToSnakeCase($genericProperty->getName()); + $snakeCase = TransformUtils::attributeToSnakeCase($genericProperty->name); if (array_key_exists($snakeCase, $this->args) && sizeof(array_intersect([WritingStyle::STYLE_SNAKE_CASE, WritingStyle::STYLE_ALL], $styles)) > 0) { return $this->args[$snakeCase]; } - $camelCase = TransformUtils::attributeToCamelCase($genericProperty->getName()); + $camelCase = TransformUtils::attributeToCamelCase($genericProperty->name); if (array_key_exists($camelCase, $this->args) && sizeof(array_intersect([WritingStyle::STYLE_CAMEL_CASE, WritingStyle::STYLE_ALL], $styles)) > 0) { return $this->args[$camelCase]; } diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index f7b641b..dc6b898 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -4,6 +4,8 @@ namespace ClassTransformer\CacheGenerator; +use ClassTransformer\Attributes\FieldAlias; +use ClassTransformer\Attributes\WritingStyle; use ReflectionClass; use RuntimeException; use ReflectionException; @@ -34,14 +36,26 @@ class CacheGenerator /** @psalm-param class-string $class */ private string $class; + private string $cacheFile; + private string $path; /** * @param class-string $class */ public function __construct(string $class, HydratorConfig $config = null) { - $this->class = $class; $this->config = $config ?? new HydratorConfig(); + $this->class = $class; + $this->cacheFile = str_replace('\\', '_', $this->class); + $this->path = $this->config->cachePath . DIRECTORY_SEPARATOR . $this->cacheFile . '.cache.php'; + } + + /** + * @param class-string $class + */ + public static function create(string $class, HydratorConfig $config = null): CacheGenerator + { + return new self($class, $config); } @@ -68,8 +82,7 @@ public function getClass(): CacheReflectionClass */ public function generate(): array { - $this->makeCacheDir($this->config->cachePath); - $class = str_replace('\\', '_', $this->class); + $this->makeCacheDir(); $refInstance = new ReflectionClass($this->class); @@ -78,9 +91,8 @@ public function generate(): array $cache = [ 'properties' => array_map(fn($el) => $this->convertToCacheProperty(new RuntimeReflectionProperty($el)), $properties) ]; - - $path = $this->config->cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'; - file_put_contents($path, serialize($cache)); + + file_put_contents($this->path, serialize($cache)); return $cache; } @@ -91,17 +103,38 @@ public function generate(): array */ private function convertToCacheProperty(RuntimeReflectionProperty $property): CacheReflectionProperty { + $args = $this->getArguments($property); + return new CacheReflectionProperty( $property->class, $property->name, - $property->getType(), + $property->type, $property->hasSetMutator(), $property->notTransform(), $property->getDocComment(), - $this->getArguments($property), + $args, + $this->getAliases($args), ); } + /** + * @return array + */ + public function getAliases( $args): array + { + $aliases = $args[FieldAlias::class] ?? null; + + if (empty($aliases)) { + return []; + } + + $aliases = $aliases[0]; + if (is_string($aliases)) { + $aliases = [$aliases]; + } + return $aliases; + } + /** * @param RuntimeReflectionProperty $property * @@ -109,7 +142,7 @@ private function convertToCacheProperty(RuntimeReflectionProperty $property): Ca */ private function getArguments(RuntimeReflectionProperty $property): array { - $attrs = $property->property->getAttributes(); + $attrs = $property->reflectionProperty->getAttributes(); $attributes = []; foreach ($attrs as $attr) { $attributes[$attr->getName()] = $attr->getArguments(); @@ -122,8 +155,7 @@ private function getArguments(RuntimeReflectionProperty $property): array */ public function get(): array { - $class = str_replace('\\', '_', $this->class); - return unserialize(file_get_contents($this->config->cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php')); + return unserialize(file_get_contents($this->path)); } /** @@ -131,8 +163,7 @@ public function get(): array */ public function cacheExists(): bool { - $class = str_replace('\\', '_', $this->class); - return file_exists('./.cache/' . $class . '.cache.php'); + return file_exists($this->path); } /** @@ -141,18 +172,14 @@ public function cacheExists(): bool * @return void * @throws RuntimeException */ - private function makeCacheDir(?string $path): void + private function makeCacheDir(): void { - $concurrentDirectory = $this->config->cachePath; if ( - empty($path) || - ( - !file_exists($concurrentDirectory) && - !mkdir($concurrentDirectory, self::DIR_PERMISSION, true) && - !is_dir($concurrentDirectory) - ) + !file_exists($this->config->cachePath) && + !mkdir($this->config->cachePath, self::DIR_PERMISSION, true) && + !is_dir($this->config->cachePath) ) { - throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); + throw new RuntimeException(sprintf('Directory "%s" was not created', $this->config->cachePath)); } } } diff --git a/src/ClassTransformerConfig.php b/src/ClassTransformerConfig.php deleted file mode 100644 index d548fb8..0000000 --- a/src/ClassTransformerConfig.php +++ /dev/null @@ -1,20 +0,0 @@ - */ - public function getAttributeArguments(string $name): ?array; + abstract public function getAttributeArguments(string $name): ?array; /** * @return string */ - public function getDocComment(): string; + abstract public function getDocComment(): string; - /** * @return bool */ - public function hasSetMutator(): bool; + abstract public function hasSetMutator(): bool; /** * @return bool */ - public function notTransform(): bool; + abstract public function notTransform(): bool; /** * @return array */ - public function getAliases(): array; + abstract public function getAliases(): array; } diff --git a/src/Enums/TypeEnums.php b/src/Enums/TypeEnums.php index d0a93ba..16a88b5 100644 --- a/src/Enums/TypeEnums.php +++ b/src/Enums/TypeEnums.php @@ -1,5 +1,7 @@ config->cacheEnabled) { - $cache = new CacheGenerator($class, $this->config); - $repository = $cache->getClass(); + $repository = CacheGenerator::create($class, $this->config)->getClass(); } else { $repository = new RuntimeReflectionClass($class); } diff --git a/src/HydratorConfig.php b/src/HydratorConfig.php index ad25959..bc187ff 100644 --- a/src/HydratorConfig.php +++ b/src/HydratorConfig.php @@ -12,17 +12,17 @@ final class HydratorConfig { /** @var bool Cache mode enabled */ - public bool $cacheEnabled = false; + public bool $cacheEnabled; /** @var string Path to the cache directory */ - public string $cachePath = __DIR__ . '/../.cache'; + public string $cachePath; public function __construct( ?bool $cacheEnabled = null, ?string $cachePath = null ) { - $this->cacheEnabled = $cacheEnabled ?? ClassTransformerConfig::$cacheEnabled; - $this->cachePath = $cachePath ?? ClassTransformerConfig::$cachePath; + $this->cacheEnabled = $cacheEnabled ?? false; + $this->cachePath = $cachePath ?? __DIR__ . '/../.cache'; } } diff --git a/src/InstanceBuilder.php b/src/InstanceBuilder.php index c0049df..6ae3e3c 100644 --- a/src/InstanceBuilder.php +++ b/src/InstanceBuilder.php @@ -54,12 +54,12 @@ public function build(): mixed } if ($property->hasSetMutator()) { - $genericInstance->{TransformUtils::mutationSetterToCamelCase($property->getName())}($value); + $genericInstance->{TransformUtils::mutationSetterToCamelCase($property->name)}($value); continue; } $caster = new ValueCasting($property, $this->config); - $genericInstance->{$property->getName()} = $caster->castAttribute($value); + $genericInstance->{$property->name} = $caster->castAttribute($value); } return $genericInstance; diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 562492e..226a057 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -6,8 +6,6 @@ use RuntimeException; use ReflectionException; -use ClassTransformer\CacheGenerator\CacheGenerator; -use ClassTransformer\Validators\ClassExistsValidator; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\Contracts\ReflectionClassRepository; diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 1aeffd1..f69aff8 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -4,17 +4,14 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Attributes\FieldAlias; - use ClassTransformer\Reflection\Types\PropertyType; -use function is_string; /** * Class GenericProperty * * @psalm-api */ -final class CacheReflectionProperty implements \ClassTransformer\Contracts\ReflectionProperty +final class CacheReflectionProperty extends \ClassTransformer\Contracts\ReflectionProperty { /** */ @@ -25,18 +22,11 @@ public function __construct( public bool $hasSetMutator, public bool $notTransform, public string $docComment, - public array $attributes + public array $attributes, + public array $aliases, ) { } - /** - * @return PropertyType - */ - public function getType(): PropertyType - { - return $this->type; - } - /** * @return bool */ @@ -45,14 +35,6 @@ public function hasSetMutator(): bool return $this->hasSetMutator; } - /** - * @return bool - */ - public function isEnum(): bool - { - return $this->isEnum; - } - /** * @return bool */ @@ -102,17 +84,6 @@ public function getDocComment(): string */ public function getAliases(): array { - $aliases = $this->getAttributeArguments(FieldAlias::class); - - if (empty($aliases)) { - return []; - } - - $aliases = $aliases[0]; - - if (is_string($aliases)) { - $aliases = [$aliases]; - } - return $aliases; + return $this->aliases; } } diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index 95be521..5688595 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -4,33 +4,26 @@ namespace ClassTransformer\Reflection; +use ReflectionProperty; +use ReflectionAttribute; +use ClassTransformer\TransformUtils; use ClassTransformer\Attributes\FieldAlias; use ClassTransformer\Attributes\NotTransform; -use ClassTransformer\Enums\TypeEnums; use ClassTransformer\Reflection\Types\PropertyType; use ClassTransformer\Reflection\Types\PropertyTypeFactory; -use ClassTransformer\TransformUtils; -use ReflectionAttribute; -use ReflectionNamedType; -use ReflectionProperty; + use function method_exists; /** * Class GenericProperty */ -final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\ReflectionProperty +final class RuntimeReflectionProperty extends \ClassTransformer\Contracts\ReflectionProperty { /** @var ReflectionProperty */ - public ReflectionProperty $property; + public ReflectionProperty $reflectionProperty; /** @var PropertyType */ - private PropertyType $type; - - /** @var class-string|string $propertyClass */ - public string $name; - - /** @var class-string */ - public string $class; + public PropertyType $type; /** @var array>> */ @@ -39,26 +32,20 @@ final class RuntimeReflectionProperty implements \ClassTransformer\Contracts\Ref /** * @param ReflectionProperty $property */ - public function __construct(ReflectionProperty $property) + public function __construct(ReflectionProperty $reflectionProperty) { - $this->property = $property; - $this->class = $property->class; - $this->name = $this->property->name; - + $this->reflectionProperty = $reflectionProperty; + $this->class = $reflectionProperty->class; + $this->name = $this->reflectionProperty->name; $this->type = PropertyTypeFactory::create($this); } - public function getType(): PropertyType - { - return $this->type; - } - /** * @return string */ public function getDocComment(): string { - $doc = $this->property->getDocComment(); + $doc = $this->reflectionProperty->getDocComment(); return $doc !== false ? $doc : ''; } @@ -82,7 +69,7 @@ public function getAttribute(string $name): ?ReflectionAttribute return self::$attributesCache[$this->class][$this->name][$name]; } - $attr = $this->property->getAttributes($name); + $attr = $this->reflectionProperty->getAttributes($name); if (!empty($attr)) { return self::$attributesCache[$this->class][$this->name][$name] = $attr[0]; } @@ -107,14 +94,6 @@ public function hasSetMutator(): bool return method_exists($this->class, TransformUtils::mutationSetterToCamelCase($this->name)); } - /** - * @return string - */ - public function getName(): string - { - return $this->name; - } - /** * @return array */ diff --git a/src/Reflection/Types/ArrayType.php b/src/Reflection/Types/ArrayType.php index 73f91a3..4b1c7b2 100644 --- a/src/Reflection/Types/ArrayType.php +++ b/src/Reflection/Types/ArrayType.php @@ -1,8 +1,8 @@ itemsType; - } - - /** - * @return bool - */ - public function isScalarItems(): bool - { - return $this->isScalarItems; - } } diff --git a/src/Reflection/Types/PropertyType.php b/src/Reflection/Types/PropertyType.php index b6ca718..1e2b0b4 100644 --- a/src/Reflection/Types/PropertyType.php +++ b/src/Reflection/Types/PropertyType.php @@ -1,10 +1,8 @@ nullable; - } - - public function isScalar(): bool - { - return $this->isScalar; - } - - public function getTypeStr() - { - return $this->typeStr; - } } diff --git a/src/Reflection/Types/PropertyTypeFactory.php b/src/Reflection/Types/PropertyTypeFactory.php index bc4f407..f07ac83 100644 --- a/src/Reflection/Types/PropertyTypeFactory.php +++ b/src/Reflection/Types/PropertyTypeFactory.php @@ -1,12 +1,14 @@ property->getType(); + $reflectionType = $property->reflectionProperty->getType(); + if ($reflectionType === null) { $type = TypeEnums::TYPE_MIXED; $isNullable = true; @@ -34,9 +42,9 @@ public static function create(RuntimeReflectionProperty $property) if (($isScalar && $type !== TypeEnums::TYPE_ARRAY) || $property->notTransform()) { return new ScalarType( - $isNullable, $type, - $isScalar + $isScalar, + $isNullable, ); } @@ -50,36 +58,36 @@ public static function create(RuntimeReflectionProperty $property) } $arrayType ??= TypeEnums::TYPE_MIXED; $type = new ArrayType( - $isNullable, $type, - $isScalar + $isScalar, + $isNullable, ); $type->itemsType = $arrayType ?? TypeEnums::TYPE_MIXED; $type->isScalarItems = in_array($arrayType, [TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_STRING, TypeEnums::TYPE_BOOLEAN, TypeEnums::TYPE_MIXED]); return $type; } - + if (function_exists('enum_exists') && !$isScalar && enum_exists($type)) { return new EnumType( - $isNullable, $type, - $isScalar + $isScalar, + $isNullable, ); } if (!$isScalar) { return new TransformableType( - $isNullable, $type, - $isScalar + $isScalar, + $isNullable, ); } return new PropertyType( - $isNullable, $type, - $isScalar + $isScalar, + $isNullable, ); } } diff --git a/src/Reflection/Types/ScalarType.php b/src/Reflection/Types/ScalarType.php index 6729748..98901b4 100644 --- a/src/Reflection/Types/ScalarType.php +++ b/src/Reflection/Types/ScalarType.php @@ -1,5 +1,7 @@ property->getType()->isScalar() && !$this->property->getType() instanceof ArrayType) || $this->property->notTransform()) { - return $this->castScalar($this->property->getType()->getTypeStr(), $value); + if (($this->property->type->isScalar && !$this->property->type instanceof ArrayType) || $this->property->notTransform()) { + return $this->castScalar($this->property->type->name, $value); } - if ($this->property->getType() instanceof ArrayType) { + if ($this->property->type instanceof ArrayType) { return $this->castArray($value); } - - if ((is_string($value) || is_int($value)) && $this->property->getType() instanceof EnumType) { + + if ((is_string($value) || is_int($value)) && $this->property->type instanceof EnumType) { return $this->castEnum($value); } - if ($this->property->getType() instanceof TransformableType) { + if ($this->property->type instanceof TransformableType) { return (new Hydrator($this->config)) - ->create($this->property->getType()->getTypeStr(), $value); + ->create($this->property->type->name, $value); } return $value; @@ -95,14 +94,14 @@ private function castScalar(string $type, mixed $value): mixed */ private function castArray($value): mixed { - if (!is_array($value) || $this->property->getType()->getTypeStr() === TypeEnums::TYPE_MIXED) { + if (!is_array($value) || $this->property->type->name === TypeEnums::TYPE_MIXED) { return $value; } - if (!$this->property->getType()->isScalarItems()) { - return array_map(fn($el) => (new Hydrator($this->config))->create($this->property->getType()->getItemsType(), $el), $value); + if (!$this->property->type->isScalarItems) { + return array_map(fn($el) => (new Hydrator($this->config))->create($this->property->type->itemsType, $el), $value); } - return array_map(fn($item) => $this->castScalar($this->property->getType()->getItemsType(), $item), $value); + return array_map(fn($item) => $this->castScalar($this->property->type->itemsType, $item), $value); } /** @@ -112,7 +111,7 @@ private function castArray($value): mixed */ private function castEnum(int|string $value): mixed { - $propertyClass = $this->property->getType()->getTypeStr(); + $propertyClass = $this->property->type->name; if ($propertyClass && method_exists($propertyClass, 'from')) { /** @var \BackedEnum $propertyClass */ return $propertyClass::from($value); diff --git a/tests/Benchmark/Bid/ManyCheckBench.php b/tests/Benchmark/Bid/ManyCheckBench.php new file mode 100644 index 0000000..0ca3253 --- /dev/null +++ b/tests/Benchmark/Bid/ManyCheckBench.php @@ -0,0 +1,335 @@ +getPurcheseObject(); + + for ($i = 0; $i < 10; ++$i) { + $purchase = new PurchaseDto(); + + $user = new UserDto(); + $user->id = $data['user']['id']; + $user->email = $data['user']['email'] ?? $data['user']['contact']; + $user->balance = $data['user']['balance']; + $user->real_address = $data['user']['real_address'] ?? $data['user']['realAddress']; + $user->type = UserTypeEnum::from($data['user']['type']); + $user->createdAt = new \DateTime($data['user']['createdAt']); + $purchase->user = $user; + + $address = new AddressClean(); + $address->source = $data['address']['source']; + $address->result = $data['address']['result']; + $address->postal_code = $data['address']['postal_code']; + $address->country = $data['address']['country']; + $address->country_iso_code = $data['address']['country_iso_code']; + $address->federal_district = $data['address']['federal_district']; + $address->region_fias_id = $data['address']['region_fias_id']; + $address->region_kladr_id = $data['address']['region_kladr_id']; + $address->region_iso_code = $data['address']['region_iso_code']; + $address->region_with_type = $data['address']['region_with_type']; + $address->region_type = $data['address']['region_type']; + $address->region_type_full = $data['address']['region_type_full']; + $address->region = $data['address']['region']; + $address->area_fias_id = $data['address']['area_fias_id']; + $address->area_kladr_id = $data['address']['area_kladr_id']; + $address->area_with_type = $data['address']['area_with_type']; + $address->area_type = $data['address']['area_type']; + $address->area_type_full = $data['address']['area_type_full']; + $address->area = $data['address']['area']; + $address->city_fias_id = $data['address']['city_fias_id']; + $address->city_kladr_id = $data['address']['city_kladr_id']; + $address->city_with_type = $data['address']['city_with_type']; + $address->city_type = $data['address']['city_type']; + $address->city_type_full = $data['address']['city_type_full']; + $address->city = $data['address']['city']; + $address->city_area = $data['address']['city_area']; + $address->city_district_fias_id = $data['address']['city_district_fias_id']; + $address->city_district_kladr_id = $data['address']['city_district_kladr_id']; + $address->city_district_with_type = $data['address']['city_district_with_type']; + $address->city_district_type = $data['address']['city_district_type']; + $address->city_district_type_full = $data['address']['city_district_type_full']; + $address->city_district = $data['address']['city_district']; + $address->settlement_fias_id = $data['address']['settlement_fias_id']; + $address->settlement_kladr_id = $data['address']['settlement_kladr_id']; + $address->settlement_with_type = $data['address']['settlement_with_type']; + $address->settlement_type = $data['address']['settlement_type']; + $address->settlement_type_full = $data['address']['settlement_type_full']; + $address->settlement = $data['address']['settlement']; + $address->street_fias_id = $data['address']['street_fias_id']; + $address->street_kladr_id = $data['address']['street_kladr_id']; + $address->street_with_type = $data['address']['street_with_type']; + $address->street_type = $data['address']['street_type']; + $address->street_type_full = $data['address']['street_type_full']; + $address->street = $data['address']['street']; + $address->house_fias_id = $data['address']['house_fias_id']; + $address->house_kladr_id = $data['address']['house_kladr_id']; + $address->house_type = $data['address']['house_type']; + $address->house_type_full = $data['address']['house_type_full']; + $address->house = $data['address']['house']; + $address->block_type = $data['address']['block_type']; + $address->block_type_full = $data['address']['block_type_full']; + $address->block = $data['address']['block']; + $address->flat_fias_id = $data['address']['flat_fias_id']; + $address->flat_type = $data['address']['flat_type']; + $address->flat_type_full = $data['address']['flat_type_full']; + $address->flat = $data['address']['flat']; + $address->flat_area = $data['address']['flat_area']; + $address->square_meter_price = $data['address']['square_meter_price']; + $address->flat_price = $data['address']['flat_price']; + $address->postal_box = $data['address']['postal_box']; + $address->fias_id = $data['address']['fias_id']; + $address->fias_code = $data['address']['fias_code']; + $address->fias_level = $data['address']['fias_level']; + $address->kladr_id = $data['address']['kladr_id']; + $address->capital_marker = $data['address']['capital_marker']; + $address->okato = $data['address']['okato']; + $address->oktmo = $data['address']['oktmo']; + $address->tax_office = $data['address']['tax_office']; + $address->tax_office_legal = $data['address']['tax_office_legal']; + $address->timezone = $data['address']['timezone']; + $address->geo_lat = $data['address']['geo_lat']; + $address->geo_lon = $data['address']['geo_lon']; + $address->beltway_hit = $data['address']['beltway_hit']; + $address->beltway_distance = $data['address']['beltway_distance']; + $address->qc_geo = $data['address']['qc_geo']; + $address->qc_complete = $data['address']['qc_complete']; + $address->qc_house = $data['address']['qc_house']; + $address->qc = $data['address']['qc']; + $address->unparsed_parts = $data['address']['unparsed_parts']; + + foreach ($data['address']['metro'] as $item) { + $metro = new MetroDto(); + $metro->distance = $item['distance']; + $metro->line = $item['line']; + $metro->name = $item['name']; + $address->metro [] = $metro; + } + + $purchase->address = $address; + + $purchase->createdAt = new \DateTime($data['createdAt']); + + foreach ($data['products'] as $product) { + $newProduct = new ProductDto(); + $newProduct->id = $product['id']; + $newProduct->name = $product['name']; + $newProduct->price = $product['price']; + $newProduct->count = $product['count']; + $purchase->products [] = $newProduct; + } + + $this->assertEquals($data['user']['id'], $purchase->user->id); + } + } + + /** + * @Revs(10000) + */ + public function benchTransformCacheReflection(): void + { + $data = $this->getPurcheseObject(); + + for ($i = 0; $i < 10; ++$i) { + $purchase = (new Hydrator(new HydratorConfig(true))) + ->create(PurchaseDto::class, $data); + + $this->assertEquals($data['user']['id'], $purchase->user->id); + } + } + + /** + * @Revs(10000) + */ + public function benchTransformReflection(): void + { + $data = $this->getPurcheseObject(); + + for ($i = 0; $i < 10; ++$i) { + $purchase = (new Hydrator()) + ->create(PurchaseDto::class, $data); + + $this->assertEquals($data['user']['id'], $purchase->user->id); + } + } + + public function getPurcheseObject(): array + { + return [ + 'createdAt' => '2023-04-10 12:30:23', + 'products' => [ + [ + 'id' => 1, + 'name' => 'phone', + 'price' => 43.03, + 'description' => 'test description for phone', + 'count' => 123 + ], + [ + 'id' => 2, + 'name' => 'bread', + 'price' => 10.56, + 'description' => 'test description for bread', + 'count' => 321 + ], + [ + 'id' => 3, + 'name' => 'book', + 'price' => 5.5, + 'description' => 'test description for book', + 'count' => 333 + ], + [ + 'id' => 4, + 'name' => 'PC', + 'price' => 100, + 'description' => 'test description for PC', + 'count' => 7 + ] + ], + 'user' => [ + 'id' => 1, + 'contact' => 'fake@mail.com', + 'balance' => 10012.23, + 'type' => 'admin', + 'realAddress' => 'test address', + 'createdAt' => '2023-04-10 12:30:23', + ], + 'address' => $this->getAddress() + ]; + } + + private function getAddress() + { + return [ + "source" => "мск сухонска 11/-89", + "result" => "г Москва, ул Сухонская, д 11, кв 89", + "postal_code" => "127642", + "country" => "Россия", + "country_iso_code" => "RU", + "federal_district" => "Центральный", + "region_fias_id" => "0c5b2444-70a0-4932-980c-b4dc0d3f02b5", + "region_kladr_id" => "7700000000000", + "region_iso_code" => "RU-MOW", + "region_with_type" => "г Москва", + "region_type" => "г", + "region_type_full" => "город", + "region" => "Москва", + "area_fias_id" => null, + "area_kladr_id" => null, + "area_with_type" => null, + "area_type" => null, + "area_type_full" => null, + "area" => null, + "city_fias_id" => null, + "city_kladr_id" => null, + "city_with_type" => null, + "city_type" => null, + "city_type_full" => null, + "city" => null, + "city_area" => "Северо-восточный", + "city_district_fias_id" => null, + "city_district_kladr_id" => null, + "city_district_with_type" => "р-н Северное Медведково", + "city_district_type" => "р-н", + "city_district_type_full" => "район", + "city_district" => "Северное Медведково", + "settlement_fias_id" => null, + "settlement_kladr_id" => null, + "settlement_with_type" => null, + "settlement_type" => null, + "settlement_type_full" => null, + "settlement" => null, + "street_fias_id" => "95dbf7fb-0dd4-4a04-8100-4f6c847564b5", + "street_kladr_id" => "77000000000283600", + "street_with_type" => "ул Сухонская", + "street_type" => "ул", + "street_type_full" => "улица", + "street" => "Сухонская", + "house_fias_id" => "5ee84ac0-eb9a-4b42-b814-2f5f7c27c255", + "house_kladr_id" => "7700000000028360004", + "house_type" => "д", + "house_type_full" => "дом", + "house" => "11", + "block_type" => null, + "block_type_full" => null, + "block" => null, + "entrance" => null, + "floor" => null, + "flat_fias_id" => "f26b876b-6857-4951-b060-ec6559f04a9a", + "flat_type" => "кв", + "flat_type_full" => "квартира", + "flat" => "89", + "flat_area" => "34.6", + "square_meter_price" => "239953", + "flat_price" => "8302374", + "postal_box" => null, + "fias_id" => "f26b876b-6857-4951-b060-ec6559f04a9a", + "fias_code" => "77000000000000028360004", + "fias_level" => "9", + "fias_actuality_state" => "0", + "kladr_id" => "7700000000028360004", + "capital_marker" => "0", + "okato" => "45280583000", + "oktmo" => "45362000", + "tax_office" => "7715", + "tax_office_legal" => "7715", + "timezone" => "UTC+3", + "geo_lat" => "55.8782557", + "geo_lon" => "37.65372", + "beltway_hit" => "IN_MKAD", + "beltway_distance" => null, + "qc_geo" => 0, + "qc_complete" => 0, + "qc_house" => 2, + "qc" => 0, + "unparsed_parts" => null, + "metro" => [ + [ + "distance" => 1.1, + "line" => "Калужско-Рижская", + "name" => "Бабушкинская" + ], + [ + "distance" => 1.2, + "line" => "Калужско-Рижская", + "name" => "Медведково" + ], + [ + "distance" => 2.5, + "line" => "Калужско-Рижская", + "name" => "Свиблово" + ] + ] + ]; + } +} diff --git a/tests/Integration/ClassTransformerFromCacheTest.php b/tests/Integration/ClassTransformerFromCacheTest.php index 0b1f4be..0b13b9d 100644 --- a/tests/Integration/ClassTransformerFromCacheTest.php +++ b/tests/Integration/ClassTransformerFromCacheTest.php @@ -4,13 +4,13 @@ namespace Tests\Integration; +use ClassTransformer\Hydrator; +use ClassTransformer\HydratorConfig; use PHPUnit\Framework\TestCase; use Tests\ClearCache; use Tests\Integration\DTO\UserDTO; use Tests\Integration\DTO\ProductDTO; use Tests\Integration\DTO\PurchaseDTO; -use ClassTransformer\ClassTransformer; -use ClassTransformer\ClassTransformerConfig; use ClassTransformer\Exceptions\ClassNotFoundException; /** @@ -27,13 +27,11 @@ class ClassTransformerFromCacheTest extends TestCase */ public function testRecursiveObject(): void { - //$this->markTestSkipped('cache'); - //return; $data = $this->getRecursiveObject(); $data->orders = $this->getArrayUsers(); - ClassTransformerConfig::$cacheEnabled = true; - $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); - ClassTransformerConfig::$cacheEnabled = false; + + $purchaseDTO = (new Hydrator(new HydratorConfig(true))) + ->create(PurchaseDto::class, $data); self::assertInstanceOf(PurchaseDTO::class, $purchaseDTO); self::assertInstanceOf(UserDTO::class, $purchaseDTO->user); diff --git a/tests/Units/CacheGeneratorTest.php b/tests/Units/CacheGeneratorTest.php index 7498aa4..2d7676c 100644 --- a/tests/Units/CacheGeneratorTest.php +++ b/tests/Units/CacheGeneratorTest.php @@ -2,35 +2,37 @@ namespace Tests\Units; -use ClassTransformer\Attributes\WritingStyle; -use ClassTransformer\CacheGenerator\CacheGenerator; -use ClassTransformer\ClassTransformerConfig; -use ClassTransformer\Reflection\CacheReflectionProperty; -use ClassTransformer\Reflection\Types\EnumType; -use PHPUnit\Framework\TestCase; -use ReflectionClass; use RuntimeException; use Tests\ClearCache; use Tests\Units\DTO\ColorEnum; +use PHPUnit\Framework\TestCase; +use ClassTransformer\HydratorConfig; use Tests\Units\DTO\UserCacheableDTO; +use ClassTransformer\Attributes\WritingStyle; +use ClassTransformer\Reflection\Types\EnumType; +use ClassTransformer\CacheGenerator\CacheGenerator; +use ClassTransformer\Reflection\CacheReflectionProperty; class CacheGeneratorTest extends TestCase { use ClearCache; + + private HydratorConfig $config; + protected function setUp(): void { parent::setUp(); - + $this->config = new HydratorConfig(true); if ( - !file_exists(ClassTransformerConfig::$cachePath) && - !mkdir($concurrentDirectory = ClassTransformerConfig::$cachePath, 0777, true) && + !file_exists($this->config->cachePath) && + !mkdir($concurrentDirectory = $this->config->cachePath, 0777, true) && !is_dir($concurrentDirectory) ) { throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); } $class = str_replace('\\', '_', UserCacheableDTO::class); - $path = __DIR__ . '/../../.cache' . DIRECTORY_SEPARATOR . $class . '.cache.php'; + $path = $this->config->cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'; if (file_exists($path)) { unlink($path); @@ -40,25 +42,26 @@ protected function setUp(): void /** * @throws \ReflectionException */ - public function testGenerateException(): void + /*public function testGenerateException(): void { $this->expectException(RuntimeException::class); $class = new ReflectionClass(CacheGenerator::class); + $class-> $method = $class->getMethod('makeCacheDir'); $method->setAccessible(true); - $method->invokeArgs(new CacheGenerator(UserCacheableDTO::class), [null]); - } + $method->invoke(new CacheGenerator(UserCacheableDTO::class),[]); + }*/ public function testGenerateCache(): void { - $cacheGenerator = new CacheGenerator(UserCacheableDTO::class, ); + $cacheGenerator = new CacheGenerator(UserCacheableDTO::class, $this->config); $class = str_replace('\\', '_', UserCacheableDTO::class); - $this->assertFileDoesNotExist(ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'); + $this->assertFileDoesNotExist($this->config->cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'); $this->assertFalse($cacheGenerator->cacheExists()); $cacheGenerator->generate(); - $this->assertFileExists(ClassTransformerConfig::$cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'); + $this->assertFileExists($this->config->cachePath . DIRECTORY_SEPARATOR . $class . '.cache.php'); $this->assertTrue($cacheGenerator->cacheExists()); $cache = $cacheGenerator->get(); $this->assertIsArray($cache); @@ -69,7 +72,7 @@ public function testGenerateCache(): void $this->assertEquals(UserCacheableDTO::class, $property->class); $this->assertEquals('id', $property->name); - $this->assertEquals('int', $property->getType()->getTypeStr()); + $this->assertEquals('int', $property->type->name); $this->assertFalse($property->hasSetMutator); $this->assertFalse($property->notTransform); $this->assertEmpty($property->docComment); @@ -84,8 +87,8 @@ public function testGenerateCache(): void /** @var CacheReflectionProperty $property */ $property = $cache['properties'][8]; - $this->assertInstanceOf(EnumType::class, $property->getType()); - $this->assertEquals(ColorEnum::class, $property->getType()->getTypeStr()); + $this->assertInstanceOf(EnumType::class, $property->type); + $this->assertEquals(ColorEnum::class, $property->type->name); $cacheGenerator->generate(); } diff --git a/tests/Units/CacheReflectionClassTest.php b/tests/Units/CacheReflectionClassTest.php deleted file mode 100644 index 7d46374..0000000 --- a/tests/Units/CacheReflectionClassTest.php +++ /dev/null @@ -1,58 +0,0 @@ -getProperties(); - - $this->assertEquals(UserCacheableDTO::class, $reflectionProperties[0]->class); - $this->assertEquals('id', $reflectionProperties[0]->name); - $this->assertEquals('int', $reflectionProperties[0]->type); - $this->assertTrue($reflectionProperties[0]->isScalar()); - $this->assertFalse($reflectionProperties[0]->hasSetMutator()); - $this->assertFalse($reflectionProperties[0]->isArray()); - $this->assertFalse($reflectionProperties[0]->isEnum()); - $this->assertFalse($reflectionProperties[0]->notTransform()); - $this->assertEmpty($reflectionProperties[0]->getDocComment()); - $this->assertEmpty($reflectionProperties[0]->getAttribute('addressThree')); - }*/ - - protected function tearDown(): void - { - $this->clearCache(); - } -} diff --git a/tests/Units/RuntimeReflectionPropertyTest.php b/tests/Units/RuntimeReflectionPropertyTest.php index a5bdce5..4133e32 100644 --- a/tests/Units/RuntimeReflectionPropertyTest.php +++ b/tests/Units/RuntimeReflectionPropertyTest.php @@ -14,14 +14,14 @@ class RuntimeReflectionPropertyTest extends TestCase public function testCreatePropery(): void { $property = new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'email')); - $this->assertEquals('string', $property->getType()->getTypeStr()); - $this->assertTrue($property->getType()->isScalar()); + $this->assertEquals('string', $property->type->name); + $this->assertTrue($property->type->isScalar); $this->assertTrue($property->hasSetMutator()); - $this->assertEquals('email', $property->getName()); + $this->assertEquals('email', $property->name); $property = new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'color')); - $this->assertInstanceOf(EnumType::class,$property->getType()); - $this->assertEquals(ColorEnum::class,$property->getType()->getTypeStr()); + $this->assertInstanceOf(EnumType::class,$property->type); + $this->assertEquals(ColorEnum::class,$property->type->name); } } From b3bb957946df7a0c6f5d8d6e66c77e4686d499ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 14 Jun 2023 13:44:14 +0300 Subject: [PATCH 083/100] up mutation tests --- composer.json | 9 +++++ src/CacheGenerator/CacheGenerator.php | 21 +++++++----- src/Exceptions/InstantiableClassException.php | 19 +++++++++++ src/Reflection/RuntimeReflectionClass.php | 3 +- src/Reflection/Types/PropertyTypeFactory.php | 20 ++++------- .../ClassTransformerFromArrayTest.php | 34 ++++++++++++++----- tests/Units/CacheGeneratorTest.php | 22 +++++++----- tests/Units/DTO/AbstractClass.php | 13 +++++++ tests/Units/DTO/ExtendedDto.php | 4 +++ tests/Units/DTO/UserCacheableDTO.php | 3 ++ tests/Units/PropertyTypeFactoryTest.php | 32 +++++++++++++++++ tests/Units/RuntimeReflectionClassTest.php | 19 +++++++++++ tests/Units/ValueCastingTest.php | 6 ++-- 13 files changed, 163 insertions(+), 42 deletions(-) create mode 100644 src/Exceptions/InstantiableClassException.php create mode 100644 tests/Units/DTO/AbstractClass.php create mode 100644 tests/Units/PropertyTypeFactoryTest.php create mode 100644 tests/Units/RuntimeReflectionClassTest.php diff --git a/composer.json b/composer.json index 88d185b..29e886b 100644 --- a/composer.json +++ b/composer.json @@ -56,5 +56,14 @@ "phpcbf": "./vendor/bin/phpcbf --standard=./phpcs.xml -n --no-cache -s", "coverage": "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml --coverage-text --colors=never --coverage-clover coverage.xml", "coverage-html": "./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml --coverage-html public/coverage --coverage-text" + }, + "scripts-descriptions": { + "phpunit": "Run tests", + "infection": "Run mutation tests", + "infection-html": "Generate mutation tests report", + "psalm": "Run static analyze", + "phpcs": "Checking codestyle", + "coverage": "Checking code coverage", + "coverage-html": "Generate code coverage report" } } diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index dc6b898..ef61d36 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -68,6 +68,7 @@ public static function create(string $class, HydratorConfig $config = null): Cac */ public function getClass(): CacheReflectionClass { + /** @infection-ignore-all */ if (!$this->cacheExists()) { $classCache = $this->generate(); } else { @@ -91,7 +92,7 @@ public function generate(): array $cache = [ 'properties' => array_map(fn($el) => $this->convertToCacheProperty(new RuntimeReflectionProperty($el)), $properties) ]; - + file_put_contents($this->path, serialize($cache)); return $cache; } @@ -104,7 +105,7 @@ public function generate(): array private function convertToCacheProperty(RuntimeReflectionProperty $property): CacheReflectionProperty { $args = $this->getArguments($property); - + return new CacheReflectionProperty( $property->class, $property->name, @@ -116,14 +117,15 @@ private function convertToCacheProperty(RuntimeReflectionProperty $property): Ca $this->getAliases($args), ); } + /** * @return array */ - public function getAliases( $args): array + public function getAliases($args): array { $aliases = $args[FieldAlias::class] ?? null; - - if (empty($aliases)) { + + if (empty($aliases) || !is_array($aliases)) { return []; } @@ -134,7 +136,7 @@ public function getAliases( $args): array } return $aliases; } - + /** * @param RuntimeReflectionProperty $property * @@ -175,8 +177,11 @@ public function cacheExists(): bool private function makeCacheDir(): void { if ( - !file_exists($this->config->cachePath) && - !mkdir($this->config->cachePath, self::DIR_PERMISSION, true) && + ( + !file_exists($this->config->cachePath) && + !mkdir($this->config->cachePath, self::DIR_PERMISSION, true) + ) + || !is_dir($this->config->cachePath) ) { throw new RuntimeException(sprintf('Directory "%s" was not created', $this->config->cachePath)); diff --git a/src/Exceptions/InstantiableClassException.php b/src/Exceptions/InstantiableClassException.php new file mode 100644 index 0000000..34f5698 --- /dev/null +++ b/src/Exceptions/InstantiableClassException.php @@ -0,0 +1,19 @@ +class); if (!$refInstance->isInstantiable()) { - throw new InvalidArgumentException('Class ' . $this->class . ' is not instantiable.'); + throw new InstantiableClassException($this->class); } $properties = $refInstance->getProperties(); diff --git a/src/Reflection/Types/PropertyTypeFactory.php b/src/Reflection/Types/PropertyTypeFactory.php index f07ac83..b001ff8 100644 --- a/src/Reflection/Types/PropertyTypeFactory.php +++ b/src/Reflection/Types/PropertyTypeFactory.php @@ -35,19 +35,11 @@ public static function create(RuntimeReflectionProperty $property) $isNullable = $reflectionType->allowsNull(); $isScalar = $reflectionType->isBuiltin(); } else { - $type = (string)$reflectionType; + $type = $reflectionType; $isScalar = $reflectionType->isBuiltin(); $isNullable = $reflectionType->allowsNull(); } - if (($isScalar && $type !== TypeEnums::TYPE_ARRAY) || $property->notTransform()) { - return new ScalarType( - $type, - $isScalar, - $isNullable, - ); - } - if ($type === TypeEnums::TYPE_ARRAY) { $arrayTypeAttr = $property->getAttributeArguments(ConvertArray::class); @@ -68,23 +60,23 @@ public static function create(RuntimeReflectionProperty $property) return $type; } - if (function_exists('enum_exists') && !$isScalar && enum_exists($type)) { - return new EnumType( + if ($isScalar || $property->notTransform()) { + return new ScalarType( $type, $isScalar, $isNullable, ); } - if (!$isScalar) { - return new TransformableType( + if (function_exists('enum_exists') && !$isScalar && enum_exists($type)) { + return new EnumType( $type, $isScalar, $isNullable, ); } - return new PropertyType( + return new TransformableType( $type, $isScalar, $isNullable, diff --git a/tests/Integration/ClassTransformerFromArrayTest.php b/tests/Integration/ClassTransformerFromArrayTest.php index 59b3636..c0583fa 100644 --- a/tests/Integration/ClassTransformerFromArrayTest.php +++ b/tests/Integration/ClassTransformerFromArrayTest.php @@ -4,6 +4,7 @@ namespace Tests\Integration; +use ClassTransformer\Hydrator; use ReflectionException; use PHPUnit\Framework\TestCase; use Tests\Integration\DTO\ConstructDto; @@ -34,7 +35,9 @@ class ClassTransformerFromArrayTest extends TestCase public function testBaseArray(): void { $data = $this->getBaseArrayData(); - $userDTO = ClassTransformer::transform(UserDTO::class, $data); + + $userDTO = Hydrator::init()->create(UserDTO::class, $data); + self::assertInstanceOf(UserDTO::class, $userDTO); self::assertEquals($data['id'], $userDTO->id); self::assertEquals($data['email'], $userDTO->email); @@ -64,7 +67,7 @@ public function testBaseArray(): void public function testEmptyClass(): void { $data = $this->getBaseArrayData(); - $instance = ClassTransformer::transform(EmptyClassDto::class, $data); + $instance = Hydrator::init()->create(EmptyClassDto::class, $data); self::assertInstanceOf(EmptyClassDto::class, $instance); } @@ -77,7 +80,7 @@ public function testScalarArray(): void 'stringList' => [100, 200, 300], 'intList' => [400, 500, 600] ]; - $dto = ClassTransformer::transform(ArrayScalarDTO::class, $data); + $dto = Hydrator::init()->create(ArrayScalarDTO::class, $data); self::assertInstanceOf(ArrayScalarDTO::class, $dto); self::assertIsString($dto->stringList[0]); self::assertEquals($dto->stringList[0], '100'); @@ -94,7 +97,9 @@ public function testNullArray(): void 'id' => 1, 'products' => null ]; - $userDTO = ClassTransformer::transform(ArrayScalarDTO::class, $data); + + $userDTO = Hydrator::init()->create(ArrayScalarDTO::class, $data); + self::assertInstanceOf(ArrayScalarDTO::class, $userDTO); } @@ -104,8 +109,11 @@ public function testNullArray(): void public function testTransformCollection(): void { $data = $this->getArrayUsers(); - + $users = ClassTransformer::transformCollection(UserDTO::class, $data); + self::assertCount(count($data), $users); + + $users = Hydrator::init()->createCollection(UserDTO::class, $data); self::assertCount(count($data), $users); foreach ($users as $key => $user) { @@ -125,6 +133,11 @@ public function testTransformMultiple(): void $purchaseData = $this->getRecursiveArrayData(); $result = ClassTransformer::transformMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); + [$user, $purchase] = $result; + self::assertInstanceOf(UserDTO::class, $user); + self::assertInstanceOf(PurchaseDTO::class, $purchase); + + $result = Hydrator::init()->createMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); [$user, $purchase] = $result; @@ -142,7 +155,8 @@ public function testTransformMultiple(): void public function testRecursiveArray(): void { $data = $this->getRecursiveArrayData(); - $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); + $purchaseDTO = Hydrator::init()->create(PurchaseDTO::class, $data); + self::assertInstanceOf(PurchaseDTO::class, $purchaseDTO); self::assertInstanceOf(UserDTO::class, $purchaseDTO->user); self::assertEquals($data['user']['id'], $purchaseDTO->user->id); @@ -169,7 +183,9 @@ public function testRecursiveArray(): void public function testTripleRecursiveArray(): void { $data = $this->getTripleRecursiveArray(); - $basketDTO = ClassTransformer::transform(BasketDTO::class, $data); + + $basketDTO = Hydrator::init()->create(BasketDTO::class, $data); + foreach ($basketDTO->orders as $key => $purchase) { self::assertInstanceOf(PurchaseDTO::class, $purchase); self::assertInstanceOf(UserDTO::class, $purchase->user); @@ -197,7 +213,9 @@ public function testTripleRecursiveArray(): void public function testEmptyTypeObject(): void { $data = $this->getBaseArrayData(); - $userDTO = ClassTransformer::transform(UserEmptyTypeDTO::class, $data); + + $userDTO = Hydrator::init()->create(UserEmptyTypeDTO::class, $data); + self::assertInstanceOf(UserEmptyTypeDTO::class, $userDTO); self::assertEquals($data['id'], $userDTO->id); self::assertEquals($data['email'], $userDTO->email); diff --git a/tests/Units/CacheGeneratorTest.php b/tests/Units/CacheGeneratorTest.php index 2d7676c..54adf34 100644 --- a/tests/Units/CacheGeneratorTest.php +++ b/tests/Units/CacheGeneratorTest.php @@ -2,6 +2,7 @@ namespace Tests\Units; +use ClassTransformer\Enums\TypeEnums; use RuntimeException; use Tests\ClearCache; use Tests\Units\DTO\ColorEnum; @@ -18,7 +19,7 @@ class CacheGeneratorTest extends TestCase use ClearCache; private HydratorConfig $config; - + protected function setUp(): void { parent::setUp(); @@ -42,15 +43,13 @@ protected function setUp(): void /** * @throws \ReflectionException */ - /*public function testGenerateException(): void + public function testGenerateException(): void { $this->expectException(RuntimeException::class); - $class = new ReflectionClass(CacheGenerator::class); - $class-> - $method = $class->getMethod('makeCacheDir'); - $method->setAccessible(true); - $method->invoke(new CacheGenerator(UserCacheableDTO::class),[]); - }*/ + $dir = __DIR__ . '/CacheGeneratorTest.php'; + $generator = new CacheGenerator(UserCacheableDTO::class, new HydratorConfig(true, $dir)); + $dto = $generator->generate(); + } public function testGenerateCache(): void { @@ -79,6 +78,13 @@ public function testGenerateCache(): void $this->assertIsArray($property->attributes); $this->assertEmpty($property->attributes); + $property = $cache['properties'][2]; + + $this->assertEquals(UserCacheableDTO::class, $property->class); + $this->assertEquals('phone', $property->name); + $this->assertEquals(TypeEnums::TYPE_MIXED, $property->type->name); + $this->assertCount(1, $property->aliases); + $property = $cache['properties'][5]; $this->assertIsArray($property->attributes); $this->assertCount(1, $property->attributes); diff --git a/tests/Units/DTO/AbstractClass.php b/tests/Units/DTO/AbstractClass.php new file mode 100644 index 0000000..4677ca2 --- /dev/null +++ b/tests/Units/DTO/AbstractClass.php @@ -0,0 +1,13 @@ + + */ +abstract class AbstractClass +{ + +} diff --git a/tests/Units/DTO/ExtendedDto.php b/tests/Units/DTO/ExtendedDto.php index 5526b23..e253dc3 100644 --- a/tests/Units/DTO/ExtendedDto.php +++ b/tests/Units/DTO/ExtendedDto.php @@ -8,6 +8,8 @@ class ExtendedDto { + public AbstractClass $mixed; + public int $id; public ?string $email; @@ -45,6 +47,8 @@ class ExtendedDto /** @var array */ public array $mixedItems; + + public UserDto $user; public function setEmailAttribute($value) { diff --git a/tests/Units/DTO/UserCacheableDTO.php b/tests/Units/DTO/UserCacheableDTO.php index efe3a5a..92aacd7 100644 --- a/tests/Units/DTO/UserCacheableDTO.php +++ b/tests/Units/DTO/UserCacheableDTO.php @@ -4,12 +4,15 @@ namespace Tests\Units\DTO; +use ClassTransformer\Attributes\FieldAlias; use ClassTransformer\Attributes\WritingStyle; class UserCacheableDTO { public int $id; public ?string $email; + + #[FieldAlias('contact')] public $phone; /** @var array $orders Order list */ diff --git a/tests/Units/PropertyTypeFactoryTest.php b/tests/Units/PropertyTypeFactoryTest.php new file mode 100644 index 0000000..abbdd34 --- /dev/null +++ b/tests/Units/PropertyTypeFactoryTest.php @@ -0,0 +1,32 @@ +assertInstanceOf(ScalarType::class, $type); + + $type = PropertyTypeFactory::create(new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'color'))); + $this->assertInstanceOf(EnumType::class, $type); + + $type = PropertyTypeFactory::create(new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'stringItems'))); + $this->assertInstanceOf(ArrayType::class, $type); + + $type = PropertyTypeFactory::create(new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'user'))); + $this->assertInstanceOf(TransformableType::class, $type); + } +} diff --git a/tests/Units/RuntimeReflectionClassTest.php b/tests/Units/RuntimeReflectionClassTest.php new file mode 100644 index 0000000..8a453ba --- /dev/null +++ b/tests/Units/RuntimeReflectionClassTest.php @@ -0,0 +1,19 @@ +expectException(InstantiableClassException::class); + $instance = new RuntimeReflectionClass(AbstractClass::class); + $instance->getProperties(); + } +} diff --git a/tests/Units/ValueCastingTest.php b/tests/Units/ValueCastingTest.php index e8034b2..90dcd87 100644 --- a/tests/Units/ValueCastingTest.php +++ b/tests/Units/ValueCastingTest.php @@ -10,7 +10,7 @@ class ValueCastingTest extends TestCase { - public function testCreatePropery(): void + public function testCreateProperty(): void { $caster = new ValueCasting( new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'color')) @@ -48,7 +48,7 @@ public function testCreatePropery(): void $this->assertEquals(1, $value); } - public function testCreateArrayPropery(): void + public function testCreateArrayProperty(): void { // Array check @@ -86,7 +86,7 @@ public function testCreateArrayPropery(): void $value = $caster->castAttribute([0]); $this->assertIsBool($value[0]); $this->assertFalse($value[0]); - + $caster = new ValueCasting( new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'mixedItems')) ); From 40a63dea74f9c9c0faf9d50263c91afd47c55d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 15 Jun 2023 11:10:43 +0300 Subject: [PATCH 084/100] Upd doc --- bin/plain-to-class-clear | 11 ++--- composer.json | 2 +- composer.lock | 18 +++---- src/CacheGenerator/CacheGenerator.php | 49 ++++++++++--------- src/ClassTransformer.php | 3 +- src/Contracts/ReflectionProperty.php | 2 +- src/Exceptions/InstantiableClassException.php | 3 ++ src/Hydrator.php | 32 +++++++----- src/HydratorConfig.php | 11 +++-- src/InstanceBuilder.php | 1 - src/Reflection/CacheReflectionClass.php | 6 +-- src/Reflection/RuntimeReflectionClass.php | 9 ++-- src/Reflection/RuntimeReflectionProperty.php | 2 +- src/Reflection/Types/EnumType.php | 1 - src/Reflection/Types/PropertyType.php | 7 ++- src/Reflection/Types/PropertyTypeFactory.php | 34 ++++++------- src/ValueCasting.php | 17 +++---- tests/ClearCache.php | 28 ++++++----- 18 files changed, 126 insertions(+), 110 deletions(-) diff --git a/bin/plain-to-class-clear b/bin/plain-to-class-clear index 4af5062..a7b0f0d 100755 --- a/bin/plain-to-class-clear +++ b/bin/plain-to-class-clear @@ -3,10 +3,7 @@ function deleteDir($dirPath) { - if (!is_dir($dirPath)) { - throw new InvalidArgumentException("$dirPath must be a directory"); - } - if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') { + if (!str_ends_with($dirPath, '/')) { $dirPath .= '/'; } $files = glob($dirPath . '*', GLOB_MARK); @@ -24,9 +21,7 @@ function deleteDir($dirPath) if (file_exists(__DIR__ . '/../.cache')) { deleteDir(__DIR__ . '/../.cache'); -} else { - if (file_exists(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache')) { - deleteDir(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache'); - } +} elseif (file_exists(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache')) { + deleteDir(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache'); } exit(0); diff --git a/composer.json b/composer.json index 29e886b..0e2fcdf 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "2.0", + "version": "3.0", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index f263662..fcbad27 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6d209336cd7129a8e9377f0bd731e229", + "content-hash": "f2cb0affe4eb397254b1fda809fa1aa7", "packages": [], "packages-dev": [ { @@ -2891,16 +2891,16 @@ }, { "name": "sanmai/pipeline", - "version": "v6.7", + "version": "v6.8", "source": { "type": "git", "url": "https://github.com/sanmai/pipeline.git", - "reference": "0e5c45c8046298212347a0bfb659126af8e75d2e" + "reference": "d1f6deb47d90a57acd1774f3f51315ef941a946c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/pipeline/zipball/0e5c45c8046298212347a0bfb659126af8e75d2e", - "reference": "0e5c45c8046298212347a0bfb659126af8e75d2e", + "url": "https://api.github.com/repos/sanmai/pipeline/zipball/d1f6deb47d90a57acd1774f3f51315ef941a946c", + "reference": "d1f6deb47d90a57acd1774f3f51315ef941a946c", "shasum": "" }, "require": { @@ -2908,7 +2908,7 @@ }, "require-dev": { "ergebnis/composer-normalize": "^2.8", - "friendsofphp/php-cs-fixer": "^3", + "friendsofphp/php-cs-fixer": "^3.17", "infection/infection": ">=0.10.5", "league/pipeline": "^0.3 || ^1.0", "phan/phan": ">=1.1", @@ -2944,7 +2944,7 @@ "description": "General-purpose collections pipeline", "support": { "issues": "https://github.com/sanmai/pipeline/issues", - "source": "https://github.com/sanmai/pipeline/tree/v6.7" + "source": "https://github.com/sanmai/pipeline/tree/v6.8" }, "funding": [ { @@ -2952,7 +2952,7 @@ "type": "github" } ], - "time": "2023-04-29T11:21:51+00:00" + "time": "2023-06-13T02:51:40+00:00" }, { "name": "sebastian/cli-parser", @@ -5448,7 +5448,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.0" + "php": "^8.1" }, "platform-dev": [], "plugin-api-version": "2.3.0" diff --git a/src/CacheGenerator/CacheGenerator.php b/src/CacheGenerator/CacheGenerator.php index ef61d36..bb9dbef 100644 --- a/src/CacheGenerator/CacheGenerator.php +++ b/src/CacheGenerator/CacheGenerator.php @@ -4,13 +4,10 @@ namespace ClassTransformer\CacheGenerator; -use ClassTransformer\Attributes\FieldAlias; -use ClassTransformer\Attributes\WritingStyle; use ReflectionClass; use RuntimeException; -use ReflectionException; -use ReflectionNamedType; use ClassTransformer\HydratorConfig; +use ClassTransformer\Attributes\FieldAlias; use ClassTransformer\Reflection\CacheReflectionClass; use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\Reflection\CacheReflectionProperty; @@ -28,15 +25,22 @@ * * @template TClass */ -class CacheGenerator +final class CacheGenerator { + /** + * + */ private const DIR_PERMISSION = 0777; + /** + * @var HydratorConfig + */ private HydratorConfig $config; /** @psalm-param class-string $class */ private string $class; - private string $cacheFile; + + /** @var string Path to cache file */ private string $path; /** @@ -46,12 +50,14 @@ public function __construct(string $class, HydratorConfig $config = null) { $this->config = $config ?? new HydratorConfig(); $this->class = $class; - $this->cacheFile = str_replace('\\', '_', $this->class); - $this->path = $this->config->cachePath . DIRECTORY_SEPARATOR . $this->cacheFile . '.cache.php'; + $cacheFile = str_replace('\\', '_', $this->class); + $this->path = $this->config->cachePath . DIRECTORY_SEPARATOR . $cacheFile . '.cache.php'; } /** - * @param class-string $class + * @template T + * + * @param class-string $class */ public static function create(string $class, HydratorConfig $config = null): CacheGenerator { @@ -60,11 +66,8 @@ public static function create(string $class, HydratorConfig $config = null): Cac /** - * @param string $class - * * @return CacheReflectionClass - * @throws ReflectionException - * @throws ClassNotFoundException + * @throws ClassNotFoundException|RuntimeException */ public function getClass(): CacheReflectionClass { @@ -79,12 +82,16 @@ public function getClass(): CacheReflectionClass /** * @return array{properties: array} - * @throws ReflectionException|RuntimeException + * @throws ClassNotFoundException|RuntimeException */ public function generate(): array { $this->makeCacheDir(); + if (!class_exists($this->class)) { + throw new ClassNotFoundException("Class $this->class not found. Please check the class path you specified."); + } + $refInstance = new ReflectionClass($this->class); $properties = $refInstance->getProperties(); @@ -94,6 +101,7 @@ public function generate(): array ]; file_put_contents($this->path, serialize($cache)); + return $cache; } @@ -119,9 +127,11 @@ private function convertToCacheProperty(RuntimeReflectionProperty $property): Ca } /** - * @return array + * @param array $args + * + * @return array */ - public function getAliases($args): array + public function getAliases(array $args = []): array { $aliases = $args[FieldAlias::class] ?? null; @@ -169,18 +179,13 @@ public function cacheExists(): bool } /** - * @param string|null $path - * * @return void * @throws RuntimeException */ private function makeCacheDir(): void { if ( - ( - !file_exists($this->config->cachePath) && - !mkdir($this->config->cachePath, self::DIR_PERMISSION, true) - ) + (!file_exists($this->config->cachePath) && !mkdir($concurrentDirectory = $this->config->cachePath, self::DIR_PERMISSION, true) && !is_dir($concurrentDirectory)) || !is_dir($this->config->cachePath) ) { diff --git a/src/ClassTransformer.php b/src/ClassTransformer.php index 9282586..21142e5 100644 --- a/src/ClassTransformer.php +++ b/src/ClassTransformer.php @@ -5,6 +5,7 @@ namespace ClassTransformer; use ClassTransformer\Exceptions\ClassNotFoundException; +use RuntimeException; /** * Class ClassTransformer @@ -23,7 +24,7 @@ final class ClassTransformer * @param iterable|object ...$args * * @return null|T - * @throws ClassNotFoundException + * @throws ClassNotFoundException|RuntimeException */ public static function transform(string $className, ...$args): mixed { diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index 7eeb79a..660e4ea 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -36,7 +36,7 @@ abstract public function getAttributeArguments(string $name): ?array; * @return string */ abstract public function getDocComment(): string; - + /** * @return bool */ diff --git a/src/Exceptions/InstantiableClassException.php b/src/Exceptions/InstantiableClassException.php index 34f5698..4038aa2 100644 --- a/src/Exceptions/InstantiableClassException.php +++ b/src/Exceptions/InstantiableClassException.php @@ -1,10 +1,13 @@ */ -class Hydrator +final class Hydrator { + /** + * @var HydratorConfig + */ private HydratorConfig $config; /** - * @var array + * @var array */ private static array $classRepositoryCache = []; @@ -33,8 +38,11 @@ public function __construct(HydratorConfig $config = null) } /** + * @param HydratorConfig|null $config + * + * @return Hydrator */ - public static function init(HydratorConfig $config = null) + public static function init(HydratorConfig $config = null): self { return new self($config); } @@ -46,9 +54,9 @@ public static function init(HydratorConfig $config = null) * @param iterable|object ...$args * * @return null|T - * @throws ClassNotFoundException + * @throws ClassNotFoundException|RuntimeException */ - public function create(string $class, ...$args) + public function create(string $class, ...$args): mixed { new ClassExistsValidator($class); @@ -66,7 +74,7 @@ public function create(string $class, ...$args) * @param array> $args * * @return null|array|array - * @throws ClassNotFoundException + * @throws ClassNotFoundException|ReflectionException */ public function createCollection(string $class, array $args): ?array { @@ -82,7 +90,7 @@ public function createCollection(string $class, array $args): ?array * @param array> $args * * @return null|array|array - * @throws ClassNotFoundException + * @throws ClassNotFoundException|ReflectionException */ public function createMultiple(array $classes, array $args): ?array { @@ -95,12 +103,12 @@ public function createMultiple(array $classes, array $args): ?array /** * @param class-string $class - * @param ...$args + * @param iterable|object ...$args * * @return mixed - * @throws ClassNotFoundException + * @throws ClassNotFoundException|RuntimeException */ - private function getInstance(string $class, ...$args) + private function getInstance(string $class, ...$args): mixed { if (method_exists($class, 'transform')) { $instance = new $class(); @@ -120,7 +128,7 @@ private function getInstance(string $class, ...$args) * @param class-string $class * * @return ClassRepository - * @throws ClassNotFoundException|ReflectionException + * @throws ClassNotFoundException|RuntimeException */ private function createClassRepository(string $class): ClassRepository { diff --git a/src/HydratorConfig.php b/src/HydratorConfig.php index bc187ff..2711906 100644 --- a/src/HydratorConfig.php +++ b/src/HydratorConfig.php @@ -13,15 +13,18 @@ final class HydratorConfig { /** @var bool Cache mode enabled */ public bool $cacheEnabled; - + /** @var string Path to the cache directory */ public string $cachePath; - + + /** + * @param bool|null $cacheEnabled + * @param string|null $cachePath + */ public function __construct( ?bool $cacheEnabled = null, ?string $cachePath = null - ) - { + ) { $this->cacheEnabled = $cacheEnabled ?? false; $this->cachePath = $cachePath ?? __DIR__ . '/../.cache'; } diff --git a/src/InstanceBuilder.php b/src/InstanceBuilder.php index 6ae3e3c..aa2ac4e 100644 --- a/src/InstanceBuilder.php +++ b/src/InstanceBuilder.php @@ -60,7 +60,6 @@ public function build(): mixed $caster = new ValueCasting($property, $this->config); $genericInstance->{$property->name} = $caster->castAttribute($value); - } return $genericInstance; } diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index 226a057..fbbc976 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -19,13 +19,12 @@ final class CacheReflectionClass implements ReflectionClassRepository { /** @var class-string $class */ private string $class; - + + /** @var array CacheReflectionProperty[] */ private array $properties; /** * @param class-string $class - * - * @throws ClassNotFoundException */ public function __construct(string $class, array $properties) { @@ -35,7 +34,6 @@ public function __construct(string $class, array $properties) /** * @return CacheReflectionProperty[] - * @throws ReflectionException|RuntimeException */ public function getProperties(): array { diff --git a/src/Reflection/RuntimeReflectionClass.php b/src/Reflection/RuntimeReflectionClass.php index 0f53691..c29f4fa 100644 --- a/src/Reflection/RuntimeReflectionClass.php +++ b/src/Reflection/RuntimeReflectionClass.php @@ -4,11 +4,11 @@ namespace ClassTransformer\Reflection; -use ClassTransformer\Exceptions\InstantiableClassException; +use ClassTransformer\Exceptions\ClassNotFoundException; use ReflectionClass; use ReflectionException; -use InvalidArgumentException; use ClassTransformer\Contracts\ReflectionClassRepository; +use ClassTransformer\Exceptions\InstantiableClassException; use function array_map; @@ -33,10 +33,13 @@ public function __construct(string $class) /** * @return RuntimeReflectionProperty[] - * @throws ReflectionException|InvalidArgumentException + * @throws InstantiableClassException|ClassNotFoundException */ public function getProperties(): array { + if (!class_exists($this->class)) { + throw new ClassNotFoundException("Class $this->class not found. Please check the class path you specified."); + } $refInstance = new ReflectionClass($this->class); if (!$refInstance->isInstantiable()) { diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index 5688595..9738174 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -30,7 +30,7 @@ final class RuntimeReflectionProperty extends \ClassTransformer\Contracts\Reflec private static array $attributesCache = []; /** - * @param ReflectionProperty $property + * @param ReflectionProperty $reflectionProperty */ public function __construct(ReflectionProperty $reflectionProperty) { diff --git a/src/Reflection/Types/EnumType.php b/src/Reflection/Types/EnumType.php index b5f39b3..d8087e3 100644 --- a/src/Reflection/Types/EnumType.php +++ b/src/Reflection/Types/EnumType.php @@ -9,5 +9,4 @@ */ class EnumType extends PropertyType { - } diff --git a/src/Reflection/Types/PropertyType.php b/src/Reflection/Types/PropertyType.php index 1e2b0b4..41edc63 100644 --- a/src/Reflection/Types/PropertyType.php +++ b/src/Reflection/Types/PropertyType.php @@ -11,10 +11,13 @@ */ class PropertyType { + /** + * @param string $name Name of type + * @param bool $isScalar + */ public function __construct( public string $name, - public bool $isScalar, - public bool $nullable, + public bool $isScalar ) { } } diff --git a/src/Reflection/Types/PropertyTypeFactory.php b/src/Reflection/Types/PropertyTypeFactory.php index b001ff8..aec57c6 100644 --- a/src/Reflection/Types/PropertyTypeFactory.php +++ b/src/Reflection/Types/PropertyTypeFactory.php @@ -9,6 +9,7 @@ use ClassTransformer\Enums\TypeEnums; use ClassTransformer\Attributes\ConvertArray; use ClassTransformer\Reflection\RuntimeReflectionProperty; +use ReflectionType; /** * Class PropertyTypeFactory @@ -25,19 +26,16 @@ class PropertyTypeFactory public static function create(RuntimeReflectionProperty $property) { $reflectionType = $property->reflectionProperty->getType(); - - if ($reflectionType === null) { - $type = TypeEnums::TYPE_MIXED; - $isNullable = true; - $isScalar = true; - } elseif ($reflectionType instanceof ReflectionNamedType) { - $type = $reflectionType->getName(); - $isNullable = $reflectionType->allowsNull(); - $isScalar = $reflectionType->isBuiltin(); - } else { + + $type = TypeEnums::TYPE_MIXED; + $isScalar = true; + + if ($reflectionType instanceof ReflectionType) { $type = $reflectionType; + } + if ($reflectionType instanceof ReflectionNamedType) { + $type = $reflectionType->getName(); $isScalar = $reflectionType->isBuiltin(); - $isNullable = $reflectionType->allowsNull(); } if ($type === TypeEnums::TYPE_ARRAY) { @@ -51,8 +49,7 @@ public static function create(RuntimeReflectionProperty $property) $arrayType ??= TypeEnums::TYPE_MIXED; $type = new ArrayType( $type, - $isScalar, - $isNullable, + $isScalar ); $type->itemsType = $arrayType ?? TypeEnums::TYPE_MIXED; $type->isScalarItems = in_array($arrayType, [TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_STRING, TypeEnums::TYPE_BOOLEAN, TypeEnums::TYPE_MIXED]); @@ -63,23 +60,20 @@ public static function create(RuntimeReflectionProperty $property) if ($isScalar || $property->notTransform()) { return new ScalarType( $type, - $isScalar, - $isNullable, + $isScalar ); } - if (function_exists('enum_exists') && !$isScalar && enum_exists($type)) { + if (function_exists('enum_exists') && enum_exists($type)) { return new EnumType( $type, - $isScalar, - $isNullable, + $isScalar ); } return new TransformableType( $type, - $isScalar, - $isNullable, + $isScalar ); } } diff --git a/src/ValueCasting.php b/src/ValueCasting.php index e9f5dc0..88ea929 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -4,15 +4,15 @@ namespace ClassTransformer; -use ClassTransformer\Attributes\ConvertArray; -use ClassTransformer\Contracts\ReflectionProperty; +use RuntimeException; use ClassTransformer\Enums\TypeEnums; -use ClassTransformer\Exceptions\ClassNotFoundException; -use ClassTransformer\Reflection\Types\ArrayType; use ClassTransformer\Reflection\Types\EnumType; +use ClassTransformer\Reflection\Types\ArrayType; +use ClassTransformer\Contracts\ReflectionProperty; +use ClassTransformer\Exceptions\ClassNotFoundException; use ClassTransformer\Reflection\Types\TransformableType; + use function array_map; -use function in_array; use function is_array; use function method_exists; @@ -21,7 +21,6 @@ */ final class ValueCasting { - /** * @var HydratorConfig */ @@ -44,7 +43,7 @@ public function __construct(ReflectionProperty $property, HydratorConfig $config * @param mixed $value * * @return mixed - * @throws ClassNotFoundException + * @throws ClassNotFoundException|RuntimeException */ public function castAttribute(mixed $value): mixed { @@ -94,7 +93,7 @@ private function castScalar(string $type, mixed $value): mixed */ private function castArray($value): mixed { - if (!is_array($value) || $this->property->type->name === TypeEnums::TYPE_MIXED) { + if (!is_array($value) || $this->property->type->name === TypeEnums::TYPE_MIXED || !$this->property->type instanceof ArrayType) { return $value; } if (!$this->property->type->isScalarItems) { @@ -116,7 +115,7 @@ private function castEnum(int|string $value): mixed /** @var \BackedEnum $propertyClass */ return $propertyClass::from($value); } - if (is_string($propertyClass) && is_string($value)) { + if (is_string($value)) { return constant($propertyClass . '::' . $value); } return $value; diff --git a/tests/ClearCache.php b/tests/ClearCache.php index 5eb57a0..d066577 100644 --- a/tests/ClearCache.php +++ b/tests/ClearCache.php @@ -1,27 +1,33 @@ deleteDir(__DIR__ . '/../.cache'); - } else { - if (file_exists(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache')) { - $this->deleteDir(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache'); - } + } elseif (file_exists(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache')) { + $this->deleteDir(__DIR__ . '/../vendor/yzen.dev/plain-to-class/.cache'); } } - private function deleteDir($dirPath) + /** + * @param string $dirPath + * + * @return void + */ + private function deleteDir(string $dirPath): void { - if (!is_dir($dirPath)) { - throw new InvalidArgumentException("$dirPath must be a directory"); - } if (!str_ends_with($dirPath, '/')) { $dirPath .= '/'; } From 59d96eaceabfa120c8fbe7b37ab1866fdbc29d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 15 Jun 2023 14:27:52 +0300 Subject: [PATCH 085/100] update doc --- README.md | 29 ++++++++++++++++++++++------- docs/usage.rst | 33 +++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f0c58e9..e216c7a 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ This is where this package comes to the rescue, which takes care of all the work - [After Transform](#after-transform) - [Custom transform](#custom-transform) - [Comparison](#Comparison) + - [Cache](#Cache) ## **Installation** @@ -73,7 +74,10 @@ $data = [ 'email' => 'test@mail.com', 'balance' => 128.41, ]; -$dto = ClassTransformer::transform(CreateUserDTO::class, $data); + +$dto = (new Hydrator())->create(CreateUserDTO::class, $data); +// or static init +$dto = Hydrator::init()->create(CreateUserDTO::class, $data); var_dump($dto); ``` @@ -88,7 +92,7 @@ object(\LoginDTO) Also for php 8 you can pass named arguments: ```php -$dto = ClassTransformer::transform(CreateUserDTO::class, +$dto = Hydrator::init()->create(CreateUserDTO::class, email: 'test@mail.com', balance: 128.41 ); @@ -114,7 +118,7 @@ $data = [ 'cost' => 10012.23, ]; -$purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); +$purchaseDTO = Hydrator::init()->create(PurchaseDTO::class, $data); var_dump($purchaseDTO); ``` @@ -158,7 +162,7 @@ $data = [ ['id' => 2, 'name' => 'bread',], ], ]; -$purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); +$purchaseDTO = Hydrator::init()->create(PurchaseDTO::class, $data); ``` ### **Anonymous array** @@ -171,7 +175,7 @@ $data = [ ['id' => 1, 'name' => 'phone'], ['id' => 2, 'name' => 'bread'], ]; -$products = ClassTransformer::transformCollection(ProductDTO::class, $data); +$products = Hydrator::init()->createCollection(ProductDTO::class, $data); ``` As a result of this execution, you will get an array of ProductDTO objects @@ -204,7 +208,7 @@ which can then be easily unpacked. 'user' => ['id' => 3, 'email' => 'fake@mail.com', 'balance' => 10012.23,], ]; - $result = ClassTransformer::transformMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); + $result = Hydrator::init()->createMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); [$user, $purchase] = $result; var_dump($user); @@ -262,7 +266,7 @@ class WritingStyleSnakeCaseDTO 'contactFio' => 'yzen.dev', 'contactEmail' => 'test@mail.com', ]; -$model = ClassTransformer::transform(WritingStyleSnakeCaseDTO::class, $data); +$model = Hydrator::init()->create(WritingStyleSnakeCaseDTO::class, $data); var_dump($model); ``` @@ -345,6 +349,17 @@ class CustomTransformUserDTOArray } ``` +### **Cache** + +The package supports a class caching mechanism to avoid the cost of reflection. This functionality is recommended to be used only if you have very voluminous classes, or there is a cyclic transformation of multiple entities. On ordinary lightweight DTO, there will be only 5-10%, and this will be unnecessary access in the file system. + +You can enable caching by passing the config to the hydrator constructor: + +```php +(new Hydrator(new HydratorConfig(true))) + ->create(PurchaseDto::class, $data); +``` + ### Comparison I also made a comparison with current analogues and here are the main disadvantages - Works only for a specific framework diff --git a/docs/usage.rst b/docs/usage.rst index f765237..33131a9 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -20,7 +20,12 @@ Common use case: 'email' => 'test@mail.com', 'balance' => 128.41, ]; - $dto = ClassTransformer::transform(CreateUserDTO::class, $data); + $dto = (new Hydrator())->create(CreateUserDTO::class, $data); + + // or static init + + $dto = Hydrator::init()->create(CreateUserDTO::class, $data); + var_dump($dto); Output: @@ -34,7 +39,7 @@ Also for php 8 you can pass named arguments: .. code-block:: php - $dto = ClassTransformer::transform(CreateUserDTO::class, + $dto = Hydrator::init()->create(CreateUserDTO::class, email: 'test@mail.com', balance: 128.41 ); @@ -60,7 +65,7 @@ If the property is not of a scalar type, but a class of another DTO is allowed, 'cost' => 10012.23, ]; - $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); + $purchaseDTO = Hydrator::init()->create(PurchaseDTO::class, $data); var_dump($purchaseDTO); .. code-block:: bash @@ -101,14 +106,14 @@ Example: ['id' => 2, 'name' => 'bread',], ], ]; - $purchaseDTO = ClassTransformer::transform(PurchaseDTO::class, $data); + $purchaseDTO = Hydrator::init()->create(PurchaseDTO::class, $data); Anonymous array --------------- In case you need to convert an array of data into an array of class objects, you can implement this using -the `transformCollection` method. +the `createCollection` method. .. code-block:: php @@ -116,7 +121,7 @@ the `transformCollection` method. ['id' => 1, 'name' => 'phone'], ['id' => 2, 'name' => 'bread'], ]; - $products = ClassTransformer::transformCollection(ProductDTO::class, $data); + $products = Hydrator::init()->createCollection(ProductDTO::class, $data); As a result of this execution, you will get an array of ProductDTO objects @@ -150,7 +155,7 @@ which can then be easily unpacked. 'user' => ['id' => 3, 'email' => 'fake@mail.com', 'balance' => 10012.23,], ]; - $result = ClassTransformer::transformMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); + $result = Hydrator::init()->createMultiple([UserDTO::class, PurchaseDTO::class], [$userData, $purchaseData]); [$user, $purchase] = $result; var_dump($user); @@ -209,7 +214,7 @@ A constant problem with the style of writing, for example, in the database it is 'contactFio' => 'yzen.dev', 'contactEmail' => 'test@mail.com', ]; - $model = ClassTransformer::transform(WritingStyleSnakeCaseDTO::class, $data); + $model = Hydrator::init()->create(WritingStyleSnakeCaseDTO::class, $data); var_dump($model); Output: @@ -294,3 +299,15 @@ If you need to completely transform yourself, then you can create a transform me $this->username = $args['fio']; } } + +Cache +---------------- + +The package supports a class caching mechanism to avoid the cost of reflection. This functionality is recommended to be used only if you have very voluminous classes, or there is a cyclic transformation of multiple entities. On ordinary lightweight DTO, there will be only 5-10%, and this will be unnecessary access in the file system. + +You can enable caching by passing the config to the hydrator constructor: + +.. code-block:: php + + (new Hydrator(new HydratorConfig(true))) + ->create(PurchaseDto::class, $data); From 2b865a4688ca3b14fedebe8e6af3cf0648d73bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 15 Jun 2023 14:43:06 +0300 Subject: [PATCH 086/100] issue badge infection --- infection.json.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infection.json.dist b/infection.json.dist index 3c8f8cf..55412af 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -7,7 +7,7 @@ "logs": { "text": "php:\/\/stderr", "stryker": { - "badge": "main" + "report": "master" } }, "mutators": { From c073a554c3b43d96abb0312552a1b19d53837060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 15 Jun 2023 15:24:12 +0300 Subject: [PATCH 087/100] add tests --- composer.json | 2 +- composer.lock | 16 ++++++------ src/Reflection/CacheReflectionProperty.php | 10 +------ src/TransformUtils.php | 9 +++---- src/ValueCasting.php | 8 ++---- .../ClassTransformerFromCacheTest.php | 5 ++-- tests/Integration/DTO/PurchaseForCacheDto.php | 19 ++++++++++++++ tests/Units/CacheGeneratorTest.php | 26 +++++++++++++------ tests/Units/ClassExistsValidatorTest.php | 20 ++++++++++++++ tests/Units/RuntimeReflectionClassTest.php | 15 +++++++++++ tests/Units/ValueCastingTest.php | 10 +++++-- 11 files changed, 98 insertions(+), 42 deletions(-) create mode 100644 tests/Integration/DTO/PurchaseForCacheDto.php create mode 100644 tests/Units/ClassExistsValidatorTest.php diff --git a/composer.json b/composer.json index 0e2fcdf..3045226 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ } }, "require": { - "php": "^8.1" + "php": "^8.0" }, "require-dev": { "mockery/mockery": "1.5.1", diff --git a/composer.lock b/composer.lock index fcbad27..b14070c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f2cb0affe4eb397254b1fda809fa1aa7", + "content-hash": "7a70eb78b820a4f6c6cb039d69c5fe71", "packages": [], "packages-dev": [ { @@ -2891,16 +2891,16 @@ }, { "name": "sanmai/pipeline", - "version": "v6.8", + "version": "v6.8.1", "source": { "type": "git", "url": "https://github.com/sanmai/pipeline.git", - "reference": "d1f6deb47d90a57acd1774f3f51315ef941a946c" + "reference": "2e88e466dd49f20c10a15330b3953d4d49c326e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/pipeline/zipball/d1f6deb47d90a57acd1774f3f51315ef941a946c", - "reference": "d1f6deb47d90a57acd1774f3f51315ef941a946c", + "url": "https://api.github.com/repos/sanmai/pipeline/zipball/2e88e466dd49f20c10a15330b3953d4d49c326e3", + "reference": "2e88e466dd49f20c10a15330b3953d4d49c326e3", "shasum": "" }, "require": { @@ -2944,7 +2944,7 @@ "description": "General-purpose collections pipeline", "support": { "issues": "https://github.com/sanmai/pipeline/issues", - "source": "https://github.com/sanmai/pipeline/tree/v6.8" + "source": "https://github.com/sanmai/pipeline/tree/v6.8.1" }, "funding": [ { @@ -2952,7 +2952,7 @@ "type": "github" } ], - "time": "2023-06-13T02:51:40+00:00" + "time": "2023-06-15T09:14:47+00:00" }, { "name": "sebastian/cli-parser", @@ -5448,7 +5448,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.1" + "php": "^8.0" }, "platform-dev": [], "plugin-api-version": "2.3.0" diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index f69aff8..6f2c342 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -42,15 +42,7 @@ public function notTransform(): bool { return $this->notTransform; } - - /** - * @return string - */ - public function getName(): string - { - return $this->name; - } - + /** * @param string $name * diff --git a/src/TransformUtils.php b/src/TransformUtils.php index 83cca17..43ebef1 100644 --- a/src/TransformUtils.php +++ b/src/TransformUtils.php @@ -74,12 +74,9 @@ public static function mutationSetterToCamelCase(string $key): string * * @return string|null */ - public static function getClassFromPhpDoc($phpDoc): ?string + public static function getClassFromPhpDoc(string $phpDoc): ?string { - if (is_string($phpDoc)) { - preg_match('/array<([a-zA-Z\d\\\]+)>/', $phpDoc, $arrayType); - return $arrayType[1] ?? null; - } - return null; + preg_match('/array<([a-zA-Z\d\\\]+)>/', $phpDoc, $arrayType); + return $arrayType[1] ?? null; } } diff --git a/src/ValueCasting.php b/src/ValueCasting.php index 88ea929..4b20ad8 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -59,12 +59,8 @@ public function castAttribute(mixed $value): mixed return $this->castEnum($value); } - if ($this->property->type instanceof TransformableType) { - return (new Hydrator($this->config)) - ->create($this->property->type->name, $value); - } - - return $value; + return (new Hydrator($this->config)) + ->create($this->property->type->name, $value); } diff --git a/tests/Integration/ClassTransformerFromCacheTest.php b/tests/Integration/ClassTransformerFromCacheTest.php index 0b13b9d..db9bf21 100644 --- a/tests/Integration/ClassTransformerFromCacheTest.php +++ b/tests/Integration/ClassTransformerFromCacheTest.php @@ -8,6 +8,7 @@ use ClassTransformer\HydratorConfig; use PHPUnit\Framework\TestCase; use Tests\ClearCache; +use Tests\Integration\DTO\PurchaseForCacheDto; use Tests\Integration\DTO\UserDTO; use Tests\Integration\DTO\ProductDTO; use Tests\Integration\DTO\PurchaseDTO; @@ -31,9 +32,9 @@ public function testRecursiveObject(): void $data->orders = $this->getArrayUsers(); $purchaseDTO = (new Hydrator(new HydratorConfig(true))) - ->create(PurchaseDto::class, $data); + ->create(PurchaseForCacheDto::class, $data); - self::assertInstanceOf(PurchaseDTO::class, $purchaseDTO); + self::assertInstanceOf(PurchaseForCacheDto::class, $purchaseDTO); self::assertInstanceOf(UserDTO::class, $purchaseDTO->user); self::assertEquals($data->user->id, $purchaseDTO->user->id); self::assertEquals($data->user->email, $purchaseDTO->user->email); diff --git a/tests/Integration/DTO/PurchaseForCacheDto.php b/tests/Integration/DTO/PurchaseForCacheDto.php new file mode 100644 index 0000000..bd6746b --- /dev/null +++ b/tests/Integration/DTO/PurchaseForCacheDto.php @@ -0,0 +1,19 @@ + $orders Order list */ + public array $clients; +} diff --git a/tests/Units/CacheGeneratorTest.php b/tests/Units/CacheGeneratorTest.php index 54adf34..1645ca6 100644 --- a/tests/Units/CacheGeneratorTest.php +++ b/tests/Units/CacheGeneratorTest.php @@ -3,6 +3,7 @@ namespace Tests\Units; use ClassTransformer\Enums\TypeEnums; +use ClassTransformer\Exceptions\ClassNotFoundException; use RuntimeException; use Tests\ClearCache; use Tests\Units\DTO\ColorEnum; @@ -43,13 +44,20 @@ protected function setUp(): void /** * @throws \ReflectionException */ - public function testGenerateException(): void + public function testGenerateRuntimeException(): void { $this->expectException(RuntimeException::class); $dir = __DIR__ . '/CacheGeneratorTest.php'; $generator = new CacheGenerator(UserCacheableDTO::class, new HydratorConfig(true, $dir)); $dto = $generator->generate(); } + + public function testGenerateClassNotFoundException(): void + { + $this->expectException(ClassNotFoundException::class); + $generator = new CacheGenerator('FakeTestClass'); + $dto = $generator->generate(); + } public function testGenerateCache(): void { @@ -72,31 +80,33 @@ public function testGenerateCache(): void $this->assertEquals(UserCacheableDTO::class, $property->class); $this->assertEquals('id', $property->name); $this->assertEquals('int', $property->type->name); - $this->assertFalse($property->hasSetMutator); - $this->assertFalse($property->notTransform); - $this->assertEmpty($property->docComment); - $this->assertIsArray($property->attributes); - $this->assertEmpty($property->attributes); + $this->assertFalse($property->hasSetMutator()); + $this->assertFalse($property->notTransform()); + $this->assertEmpty($property->getDocComment()); $property = $cache['properties'][2]; $this->assertEquals(UserCacheableDTO::class, $property->class); $this->assertEquals('phone', $property->name); $this->assertEquals(TypeEnums::TYPE_MIXED, $property->type->name); - $this->assertCount(1, $property->aliases); + $this->assertCount(1, $property->getAliases()); $property = $cache['properties'][5]; $this->assertIsArray($property->attributes); $this->assertCount(1, $property->attributes); $this->assertIsArray($property->attributes[WritingStyle::class]); $this->assertEquals(WritingStyle::STYLE_CAMEL_CASE, $property->attributes[WritingStyle::class][0]); + $this->assertIsArray($property->getAttribute(WritingStyle::class)); + $this->assertIsArray($property->getAttributeArguments(WritingStyle::class)); /** @var CacheReflectionProperty $property */ $property = $cache['properties'][8]; $this->assertInstanceOf(EnumType::class, $property->type); $this->assertEquals(ColorEnum::class, $property->type->name); - $cacheGenerator->generate(); + $cacheClass = $cacheGenerator->getClass(); + $this->assertEquals(UserCacheableDTO::class, $cacheClass->getClass()); + } protected function tearDown(): void diff --git a/tests/Units/ClassExistsValidatorTest.php b/tests/Units/ClassExistsValidatorTest.php new file mode 100644 index 0000000..4ed01a3 --- /dev/null +++ b/tests/Units/ClassExistsValidatorTest.php @@ -0,0 +1,20 @@ +expectException(ClassNotFoundException::class); + new ClassExistsValidator('TestClass'); + } +} diff --git a/tests/Units/RuntimeReflectionClassTest.php b/tests/Units/RuntimeReflectionClassTest.php index 8a453ba..fb25b90 100644 --- a/tests/Units/RuntimeReflectionClassTest.php +++ b/tests/Units/RuntimeReflectionClassTest.php @@ -2,7 +2,9 @@ namespace Tests\Units; +use ClassTransformer\Exceptions\ClassNotFoundException; use PHPUnit\Framework\TestCase; +use Tests\Integration\DTO\PurchaseDTO; use Tests\Units\DTO\AbstractClass; use ClassTransformer\Reflection\RuntimeReflectionClass; use ClassTransformer\Exceptions\InstantiableClassException; @@ -16,4 +18,17 @@ public function testInstantiable(): void $instance = new RuntimeReflectionClass(AbstractClass::class); $instance->getProperties(); } + + public function testGetClass(): void + { + $instance = new RuntimeReflectionClass(PurchaseDTO::class); + $this->assertEquals(PurchaseDTO::class, $instance->getClass()); + } + + public function testGenerateClassNotFoundException(): void + { + $this->expectException(ClassNotFoundException::class); + $instance = new RuntimeReflectionClass('FakeTestClass'); + $instance->getProperties(); + } } diff --git a/tests/Units/ValueCastingTest.php b/tests/Units/ValueCastingTest.php index 90dcd87..5cec68f 100644 --- a/tests/Units/ValueCastingTest.php +++ b/tests/Units/ValueCastingTest.php @@ -50,7 +50,13 @@ public function testCreateProperty(): void public function testCreateArrayProperty(): void { - // Array check + + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'intItems')) + ); + $value = $caster->castAttribute('1'); + $this->assertIsString($value); + $this->assertEquals('1', $value); $caster = new ValueCasting( new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'intItems')) @@ -72,7 +78,7 @@ public function testCreateArrayProperty(): void $value = $caster->castAttribute([10]); $this->assertIsString($value[0]); $this->assertEquals('10', $value[0]); - + $caster = new ValueCasting( new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'boolItems')) ); From 180394c203d043944c11a11562f7a7629d1c8ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 15 Jun 2023 15:45:02 +0300 Subject: [PATCH 088/100] fix psalm --- src/Reflection/CacheReflectionProperty.php | 2 +- src/TransformUtils.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index 6f2c342..dee7514 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -42,7 +42,7 @@ public function notTransform(): bool { return $this->notTransform; } - + /** * @param string $name * diff --git a/src/TransformUtils.php b/src/TransformUtils.php index 43ebef1..e8cd16c 100644 --- a/src/TransformUtils.php +++ b/src/TransformUtils.php @@ -70,7 +70,7 @@ public static function mutationSetterToCamelCase(string $key): string } /** - * @param string|bool $phpDoc + * @param string $phpDoc * * @return string|null */ From b12ba5ceb86fb3d501a5c2d057f2506954cdfb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Thu, 15 Jun 2023 15:50:03 +0300 Subject: [PATCH 089/100] update version --- composer.json | 2 +- composer.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 3045226..f59619a 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "3.0", + "version": "3.0.1", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index b14070c..7155e47 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7a70eb78b820a4f6c6cb039d69c5fe71", + "content-hash": "e6392f91ae3712acc4a4fab8d25ef602", "packages": [], "packages-dev": [ { From 5f78b33ed81e468fa8982a6f6e6650ef6d2cc0a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 11 Jul 2023 14:55:45 +0300 Subject: [PATCH 090/100] Fix: cast nullable property --- composer.json | 2 +- composer.lock | 38 ++++++++++---------- src/Reflection/Types/PropertyType.php | 4 ++- src/Reflection/Types/PropertyTypeFactory.php | 15 +++++--- src/ValueCasting.php | 4 +++ tests/Units/DTO/TypesDto.php | 17 +++++++++ tests/Units/ValueCastingTest.php | 18 ++++++++++ 7 files changed, 73 insertions(+), 25 deletions(-) create mode 100644 tests/Units/DTO/TypesDto.php diff --git a/composer.json b/composer.json index f59619a..3d6874e 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "3.0.1", + "version": "3.0.2", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index 7155e47..43ff397 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e6392f91ae3712acc4a4fab8d25ef602", + "content-hash": "5a0b6ab9a620c712b6b5c2c8cc3bc364", "packages": [], "packages-dev": [ { @@ -1495,16 +1495,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.5", + "version": "v4.16.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" + "reference": "19526a33fb561ef417e822e85f08a00db4059c17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", + "reference": "19526a33fb561ef417e822e85f08a00db4059c17", "shasum": "" }, "require": { @@ -1545,9 +1545,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" }, - "time": "2023-05-19T20:20:00+00:00" + "time": "2023-06-25T14:52:30+00:00" }, { "name": "ocramius/package-versions", @@ -2162,16 +2162,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.22.0", + "version": "1.22.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c" + "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", - "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0", + "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0", "shasum": "" }, "require": { @@ -2203,9 +2203,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1" }, - "time": "2023-06-01T12:35:21+00:00" + "time": "2023-06-29T20:46:06+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4206,16 +4206,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.3.0", + "version": "v6.3.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "97b698e1d77d356304def77a8d0cd73090b359ea" + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/97b698e1d77d356304def77a8d0cd73090b359ea", - "reference": "97b698e1d77d356304def77a8d0cd73090b359ea", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", "shasum": "" }, "require": { @@ -4249,7 +4249,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.3.0" + "source": "https://github.com/symfony/filesystem/tree/v6.3.1" }, "funding": [ { @@ -4265,7 +4265,7 @@ "type": "tidelift" } ], - "time": "2023-05-30T17:12:32+00:00" + "time": "2023-06-01T08:30:39+00:00" }, { "name": "symfony/finder", diff --git a/src/Reflection/Types/PropertyType.php b/src/Reflection/Types/PropertyType.php index 41edc63..e608e0b 100644 --- a/src/Reflection/Types/PropertyType.php +++ b/src/Reflection/Types/PropertyType.php @@ -14,10 +14,12 @@ class PropertyType /** * @param string $name Name of type * @param bool $isScalar + * @param bool $isNullable */ public function __construct( public string $name, - public bool $isScalar + public bool $isScalar, + public bool $isNullable ) { } } diff --git a/src/Reflection/Types/PropertyTypeFactory.php b/src/Reflection/Types/PropertyTypeFactory.php index aec57c6..245a112 100644 --- a/src/Reflection/Types/PropertyTypeFactory.php +++ b/src/Reflection/Types/PropertyTypeFactory.php @@ -29,13 +29,16 @@ public static function create(RuntimeReflectionProperty $property) $type = TypeEnums::TYPE_MIXED; $isScalar = true; + $isNullable = true; if ($reflectionType instanceof ReflectionType) { $type = $reflectionType; + $isNullable = $reflectionType->allowsNull(); } if ($reflectionType instanceof ReflectionNamedType) { $type = $reflectionType->getName(); $isScalar = $reflectionType->isBuiltin(); + $isNullable = $reflectionType->allowsNull(); } if ($type === TypeEnums::TYPE_ARRAY) { @@ -49,7 +52,8 @@ public static function create(RuntimeReflectionProperty $property) $arrayType ??= TypeEnums::TYPE_MIXED; $type = new ArrayType( $type, - $isScalar + $isScalar, + $isNullable ); $type->itemsType = $arrayType ?? TypeEnums::TYPE_MIXED; $type->isScalarItems = in_array($arrayType, [TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_STRING, TypeEnums::TYPE_BOOLEAN, TypeEnums::TYPE_MIXED]); @@ -60,20 +64,23 @@ public static function create(RuntimeReflectionProperty $property) if ($isScalar || $property->notTransform()) { return new ScalarType( $type, - $isScalar + $isScalar, + $isNullable ); } if (function_exists('enum_exists') && enum_exists($type)) { return new EnumType( $type, - $isScalar + $isScalar, + $isNullable ); } return new TransformableType( $type, - $isScalar + $isScalar, + $isNullable ); } } diff --git a/src/ValueCasting.php b/src/ValueCasting.php index 4b20ad8..add0973 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -47,6 +47,10 @@ public function __construct(ReflectionProperty $property, HydratorConfig $config */ public function castAttribute(mixed $value): mixed { + if ($this->property->type->isNullable && empty($value)) { + return null; + } + if (($this->property->type->isScalar && !$this->property->type instanceof ArrayType) || $this->property->notTransform()) { return $this->castScalar($this->property->type->name, $value); } diff --git a/tests/Units/DTO/TypesDto.php b/tests/Units/DTO/TypesDto.php new file mode 100644 index 0000000..4819397 --- /dev/null +++ b/tests/Units/DTO/TypesDto.php @@ -0,0 +1,17 @@ +castAttribute('1'); $this->assertIsInt($value); $this->assertEquals(1, $value); + + + $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableInt'))); + $value = $caster->castAttribute(''); + $this->assertNull($value); + + $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableString'))); + $value = $caster->castAttribute(''); + $this->assertNull($value); + + $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableFloat'))); + $value = $caster->castAttribute(''); + $this->assertNull($value); + + $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableBool'))); + $value = $caster->castAttribute(''); + $this->assertNull($value); } public function testCreateArrayProperty(): void From 1f01828dbba75fa1f422933d2d97f59b51ba084e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 12 Jul 2023 10:50:55 +0300 Subject: [PATCH 091/100] fix: nullable property --- composer.json | 2 +- composer.lock | 2 +- src/ValueCasting.php | 2 +- tests/Units/ValueCastingTest.php | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 3d6874e..3c12ac8 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "3.0.2", + "version": "3.0.3", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index 43ff397..bc4682e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5a0b6ab9a620c712b6b5c2c8cc3bc364", + "content-hash": "38393cf49708cbd2c627803180a174c8", "packages": [], "packages-dev": [ { diff --git a/src/ValueCasting.php b/src/ValueCasting.php index add0973..bebe006 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -47,7 +47,7 @@ public function __construct(ReflectionProperty $property, HydratorConfig $config */ public function castAttribute(mixed $value): mixed { - if ($this->property->type->isNullable && empty($value)) { + if ($this->property->type->isNullable && $value === null) { return null; } diff --git a/tests/Units/ValueCastingTest.php b/tests/Units/ValueCastingTest.php index ba563ac..320edd0 100644 --- a/tests/Units/ValueCastingTest.php +++ b/tests/Units/ValueCastingTest.php @@ -50,19 +50,19 @@ public function testCreateProperty(): void $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableInt'))); - $value = $caster->castAttribute(''); + $value = $caster->castAttribute(null); $this->assertNull($value); $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableString'))); - $value = $caster->castAttribute(''); + $value = $caster->castAttribute(null); $this->assertNull($value); $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableFloat'))); - $value = $caster->castAttribute(''); + $value = $caster->castAttribute(null); $this->assertNull($value); $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableBool'))); - $value = $caster->castAttribute(''); + $value = $caster->castAttribute(null); $this->assertNull($value); } From ddbb7e1a14f70d554ebf38b9d626c2ca1bd14641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Fri, 14 Jul 2023 18:54:59 +0300 Subject: [PATCH 092/100] Add: attr emptyToNull. InvalidArgumentException --- composer.json | 2 +- composer.lock | 2 +- src/ArgumentsRepository.php | 2 +- src/Attributes/EmptyToNull.php | 16 +++++++++++ src/CacheGenerator/CacheGenerator.php | 1 + src/ClassRepository.php | 11 +++----- src/Contracts/ReflectionClassRepository.php | 2 +- src/Contracts/ReflectionProperty.php | 9 +++++-- src/Reflection/CacheReflectionClass.php | 5 ++-- src/Reflection/CacheReflectionProperty.php | 18 +++++++++++++ src/Reflection/RuntimeReflectionProperty.php | 15 ++++++++--- src/Reflection/Types/ArrayType.php | 2 ++ src/Reflection/Types/EnumType.php | 1 + src/Reflection/Types/PropertyType.php | 3 ++- src/Reflection/Types/PropertyTypeFactory.php | 22 ++++++++++----- src/Reflection/Types/ScalarType.php | 1 + src/Reflection/Types/TransformableType.php | 10 +++++++ src/ValueCasting.php | 28 +++++++++++++++++--- tests/Units/DTO/TypesDto.php | 3 +++ tests/Units/ValueCastingTest.php | 15 +++++++++++ 20 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 src/Attributes/EmptyToNull.php diff --git a/composer.json b/composer.json index 3c12ac8..98984bd 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "3.0.3", + "version": "3.0.4", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index bc4682e..d23fbb9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "38393cf49708cbd2c627803180a174c8", + "content-hash": "fe8f42c900ac36a6eb10fd83777d6411", "packages": [], "packages-dev": [ { diff --git a/src/ArgumentsRepository.php b/src/ArgumentsRepository.php index 3da6161..efbcf2d 100644 --- a/src/ArgumentsRepository.php +++ b/src/ArgumentsRepository.php @@ -26,7 +26,7 @@ final class ArgumentsRepository /** * - * @param iterable|array ...$args + * @param iterable|object ...$args */ public function __construct(...$args) { diff --git a/src/Attributes/EmptyToNull.php b/src/Attributes/EmptyToNull.php new file mode 100644 index 0000000..7d64b79 --- /dev/null +++ b/src/Attributes/EmptyToNull.php @@ -0,0 +1,16 @@ +type, $property->hasSetMutator(), $property->notTransform(), + $property->convertEmptyToNull(), $property->getDocComment(), $args, $this->getAliases($args), diff --git a/src/ClassRepository.php b/src/ClassRepository.php index 6855b64..5ea9c06 100644 --- a/src/ClassRepository.php +++ b/src/ClassRepository.php @@ -3,8 +3,6 @@ namespace ClassTransformer; use ClassTransformer\Contracts\ReflectionProperty; -use ClassTransformer\Exceptions\ClassNotFoundException; -use ClassTransformer\Reflection\CacheReflectionProperty; use ClassTransformer\Contracts\ReflectionClassRepository; /** @@ -14,22 +12,21 @@ */ final class ClassRepository { - /** @var class-string $class */ + /** @var string $class */ private string $class; /** @var ReflectionClassRepository $class */ private ReflectionClassRepository $classRepository; /** - * @var array + * @var array */ private static array $propertiesTypesCache = []; /** - * @param class-string $class - * - * @throws ClassNotFoundException + * @param string $class + * @param ReflectionClassRepository $classRepository */ public function __construct( string $class, diff --git a/src/Contracts/ReflectionClassRepository.php b/src/Contracts/ReflectionClassRepository.php index 8ec6f8b..919f2fb 100644 --- a/src/Contracts/ReflectionClassRepository.php +++ b/src/Contracts/ReflectionClassRepository.php @@ -8,7 +8,7 @@ interface ReflectionClassRepository { /** - * @return array + * @return ReflectionProperty[] */ public function getProperties(): array; diff --git a/src/Contracts/ReflectionProperty.php b/src/Contracts/ReflectionProperty.php index 660e4ea..249f5f7 100644 --- a/src/Contracts/ReflectionProperty.php +++ b/src/Contracts/ReflectionProperty.php @@ -19,14 +19,14 @@ abstract class ReflectionProperty public PropertyType $type; /** - * @param string $name + * @param class-string $name * * @return mixed */ abstract public function getAttribute(string $name): mixed; /** - * @param string $name + * @param class-string $name * * @return null|array */ @@ -47,6 +47,11 @@ abstract public function hasSetMutator(): bool; */ abstract public function notTransform(): bool; + /** + * @return bool + */ + abstract public function convertEmptyToNull(): bool; + /** * @return array */ diff --git a/src/Reflection/CacheReflectionClass.php b/src/Reflection/CacheReflectionClass.php index fbbc976..e3d3b27 100644 --- a/src/Reflection/CacheReflectionClass.php +++ b/src/Reflection/CacheReflectionClass.php @@ -17,14 +17,15 @@ */ final class CacheReflectionClass implements ReflectionClassRepository { - /** @var class-string $class */ + /** @var string $class */ private string $class; /** @var array CacheReflectionProperty[] */ private array $properties; /** - * @param class-string $class + * @param string $class + * @param CacheReflectionProperty[] $properties */ public function __construct(string $class, array $properties) { diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index dee7514..c2ba740 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -14,6 +14,15 @@ final class CacheReflectionProperty extends \ClassTransformer\Contracts\ReflectionProperty { /** + * @param class-string $class + * @param class-string|string $name + * @param PropertyType $type + * @param bool $hasSetMutator + * @param bool $notTransform + * @param bool $convertEmptyToNull + * @param string $docComment + * @param array $attributes + * @param array $aliases */ public function __construct( public string $class, @@ -21,6 +30,7 @@ public function __construct( public PropertyType $type, public bool $hasSetMutator, public bool $notTransform, + public bool $convertEmptyToNull, public string $docComment, public array $attributes, public array $aliases, @@ -42,6 +52,14 @@ public function notTransform(): bool { return $this->notTransform; } + + /** + * @return bool + */ + public function convertEmptyToNull(): bool + { + return $this->convertEmptyToNull; + } /** * @param string $name diff --git a/src/Reflection/RuntimeReflectionProperty.php b/src/Reflection/RuntimeReflectionProperty.php index 9738174..e5f1709 100644 --- a/src/Reflection/RuntimeReflectionProperty.php +++ b/src/Reflection/RuntimeReflectionProperty.php @@ -4,6 +4,7 @@ namespace ClassTransformer\Reflection; +use ClassTransformer\Attributes\EmptyToNull; use ReflectionProperty; use ReflectionAttribute; use ClassTransformer\TransformUtils; @@ -58,7 +59,15 @@ public function notTransform(): bool } /** - * @param string $name + * @return bool + */ + public function convertEmptyToNull(): bool + { + return $this->getAttribute(EmptyToNull::class) !== null; + } + + /** + * @param class-string $name * * @template T * @return null|ReflectionAttribute @@ -77,11 +86,11 @@ public function getAttribute(string $name): ?ReflectionAttribute } /** - * @param string|null $name + * @param class-string $name * * @return null|array */ - public function getAttributeArguments(?string $name = null): ?array + public function getAttributeArguments(string $name): ?array { return $this->getAttribute($name)?->getArguments(); } diff --git a/src/Reflection/Types/ArrayType.php b/src/Reflection/Types/ArrayType.php index 4b1c7b2..a2bfe15 100644 --- a/src/Reflection/Types/ArrayType.php +++ b/src/Reflection/Types/ArrayType.php @@ -11,6 +11,8 @@ */ class ArrayType extends PropertyType { + /** @var string|class-string */ public string $itemsType; public bool $isScalarItems; + } diff --git a/src/Reflection/Types/EnumType.php b/src/Reflection/Types/EnumType.php index d8087e3..4c01eb3 100644 --- a/src/Reflection/Types/EnumType.php +++ b/src/Reflection/Types/EnumType.php @@ -5,6 +5,7 @@ /** * Class EnumType * + * @psalm-api * @author yzen.dev */ class EnumType extends PropertyType diff --git a/src/Reflection/Types/PropertyType.php b/src/Reflection/Types/PropertyType.php index e608e0b..05568ee 100644 --- a/src/Reflection/Types/PropertyType.php +++ b/src/Reflection/Types/PropertyType.php @@ -7,12 +7,13 @@ /** * Class PropertyType * + * @psalm-api * @author yzen.dev */ class PropertyType { /** - * @param string $name Name of type + * @param string|class-string $name Name of type * @param bool $isScalar * @param bool $isNullable */ diff --git a/src/Reflection/Types/PropertyTypeFactory.php b/src/Reflection/Types/PropertyTypeFactory.php index 245a112..2edd61c 100644 --- a/src/Reflection/Types/PropertyTypeFactory.php +++ b/src/Reflection/Types/PropertyTypeFactory.php @@ -32,7 +32,7 @@ public static function create(RuntimeReflectionProperty $property) $isNullable = true; if ($reflectionType instanceof ReflectionType) { - $type = $reflectionType; + $type = (string)$reflectionType; $isNullable = $reflectionType->allowsNull(); } if ($reflectionType instanceof ReflectionNamedType) { @@ -41,6 +41,14 @@ public static function create(RuntimeReflectionProperty $property) $isNullable = $reflectionType->allowsNull(); } + if ($property->notTransform()) { + return new ScalarType( + $type, + $isScalar, + $isNullable + ); + } + if ($type === TypeEnums::TYPE_ARRAY) { $arrayTypeAttr = $property->getAttributeArguments(ConvertArray::class); @@ -50,18 +58,19 @@ public static function create(RuntimeReflectionProperty $property) $arrayType = TransformUtils::getClassFromPhpDoc($property->getDocComment()); } $arrayType ??= TypeEnums::TYPE_MIXED; - $type = new ArrayType( + + $typeInstance = new ArrayType( $type, $isScalar, $isNullable ); - $type->itemsType = $arrayType ?? TypeEnums::TYPE_MIXED; - $type->isScalarItems = in_array($arrayType, [TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_STRING, TypeEnums::TYPE_BOOLEAN, TypeEnums::TYPE_MIXED]); + $typeInstance->itemsType = $arrayType ?? TypeEnums::TYPE_MIXED; + $typeInstance->isScalarItems = in_array($arrayType, [TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_STRING, TypeEnums::TYPE_BOOLEAN, TypeEnums::TYPE_MIXED]); - return $type; + return $typeInstance; } - if ($isScalar || $property->notTransform()) { + if ($isScalar) { return new ScalarType( $type, $isScalar, @@ -80,7 +89,6 @@ public static function create(RuntimeReflectionProperty $property) return new TransformableType( $type, $isScalar, - $isNullable ); } } diff --git a/src/Reflection/Types/ScalarType.php b/src/Reflection/Types/ScalarType.php index 98901b4..ea14aee 100644 --- a/src/Reflection/Types/ScalarType.php +++ b/src/Reflection/Types/ScalarType.php @@ -7,6 +7,7 @@ /** * Class ScalarType * + * @psalm-api * @author yzen.dev */ class ScalarType extends PropertyType diff --git a/src/Reflection/Types/TransformableType.php b/src/Reflection/Types/TransformableType.php index 244d3ca..eac1427 100644 --- a/src/Reflection/Types/TransformableType.php +++ b/src/Reflection/Types/TransformableType.php @@ -11,4 +11,14 @@ */ class TransformableType extends PropertyType { + /** + * @param class-string $name Name of type + * @param bool $isNullable + */ + public function __construct( + public string $name, + public bool $isNullable + ) { + parent::__construct($this->name, false, $isNullable); + } } diff --git a/src/ValueCasting.php b/src/ValueCasting.php index bebe006..5676c7e 100644 --- a/src/ValueCasting.php +++ b/src/ValueCasting.php @@ -10,7 +10,7 @@ use ClassTransformer\Reflection\Types\ArrayType; use ClassTransformer\Contracts\ReflectionProperty; use ClassTransformer\Exceptions\ClassNotFoundException; -use ClassTransformer\Reflection\Types\TransformableType; +use ClassTransformer\Exceptions\InvalidArgumentException; use function array_map; use function is_array; @@ -44,6 +44,7 @@ public function __construct(ReflectionProperty $property, HydratorConfig $config * * @return mixed * @throws ClassNotFoundException|RuntimeException + * @throws InvalidArgumentException */ public function castAttribute(mixed $value): mixed { @@ -51,7 +52,15 @@ public function castAttribute(mixed $value): mixed return null; } - if (($this->property->type->isScalar && !$this->property->type instanceof ArrayType) || $this->property->notTransform()) { + if (($value === '' || $value === []) && $this->property->convertEmptyToNull()) { + return null; + } + + if ($this->property->notTransform() || $this->property->type->name === TypeEnums::TYPE_MIXED) { + return $value; + } + + if (in_array($this->property->type->name, [TypeEnums::TYPE_STRING, TypeEnums::TYPE_INTEGER, TypeEnums::TYPE_FLOAT, TypeEnums::TYPE_BOOLEAN])) { return $this->castScalar($this->property->type->name, $value); } @@ -73,15 +82,23 @@ public function castAttribute(mixed $value): mixed * @param mixed $value * * @return mixed + * @throws InvalidArgumentException */ private function castScalar(string $type, mixed $value): mixed { + + $providedType = gettype($value); + + if ($this->property->type->name !== TypeEnums::TYPE_MIXED && !in_array($providedType, ['integer', 'string', 'boolean', 'double'])) { + throw new InvalidArgumentException('Parameter `' . $this->property->name . '` expected type `' . $type . '`, `' . $providedType . '` provided'); + } + return match ($type) { TypeEnums::TYPE_STRING => (string)$value, TypeEnums::TYPE_INTEGER => (int)$value, TypeEnums::TYPE_FLOAT => (float)$value, TypeEnums::TYPE_BOOLEAN => (bool)$value, - default => $value + TypeEnums::TYPE_MIXED => $value, }; } @@ -90,6 +107,7 @@ private function castScalar(string $type, mixed $value): mixed * * @return array|mixed * @throws ClassNotFoundException + * @throws InvalidArgumentException */ private function castArray($value): mixed { @@ -100,6 +118,10 @@ private function castArray($value): mixed return array_map(fn($el) => (new Hydrator($this->config))->create($this->property->type->itemsType, $el), $value); } + if ($this->property->type->itemsType === TypeEnums::TYPE_MIXED) { + return $value; + } + return array_map(fn($item) => $this->castScalar($this->property->type->itemsType, $item), $value); } diff --git a/tests/Units/DTO/TypesDto.php b/tests/Units/DTO/TypesDto.php index 4819397..96caeff 100644 --- a/tests/Units/DTO/TypesDto.php +++ b/tests/Units/DTO/TypesDto.php @@ -4,6 +4,7 @@ namespace Tests\Units\DTO; +use ClassTransformer\Attributes\EmptyToNull; use ClassTransformer\Attributes\WritingStyle; class TypesDto @@ -11,6 +12,8 @@ class TypesDto public ?int $nullableInt; public ?string $nullableString; + #[EmptyToNull] + public ?string $emptyString; public ?float $nullableFloat; public ?bool $nullableBool; diff --git a/tests/Units/ValueCastingTest.php b/tests/Units/ValueCastingTest.php index 320edd0..56eed06 100644 --- a/tests/Units/ValueCastingTest.php +++ b/tests/Units/ValueCastingTest.php @@ -2,6 +2,7 @@ namespace Tests\Units; +use ClassTransformer\Exceptions\InvalidArgumentException; use ClassTransformer\ValueCasting; use PHPUnit\Framework\TestCase; use Tests\Units\DTO\ExtendedDto; @@ -11,6 +12,15 @@ class ValueCastingTest extends TestCase { + public function testCreateNotValidProperty(): void + { + $this->expectException(InvalidArgumentException::class); + $caster = new ValueCasting( + new RuntimeReflectionProperty(new \ReflectionProperty(ExtendedDto::class, 'id')) + ); + $caster->castAttribute([1,2]); + } + public function testCreateProperty(): void { $caster = new ValueCasting( @@ -56,6 +66,10 @@ public function testCreateProperty(): void $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableString'))); $value = $caster->castAttribute(null); $this->assertNull($value); + + $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'emptyString'))); + $value = $caster->castAttribute(''); + $this->assertNull($value); $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableFloat'))); $value = $caster->castAttribute(null); @@ -64,6 +78,7 @@ public function testCreateProperty(): void $caster = new ValueCasting(new RuntimeReflectionProperty(new \ReflectionProperty(TypesDto::class, 'nullableBool'))); $value = $caster->castAttribute(null); $this->assertNull($value); + } public function testCreateArrayProperty(): void From 7ae15d52c4dc903dd25ba29e7e5871d809c5ea8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Fri, 14 Jul 2023 18:56:39 +0300 Subject: [PATCH 093/100] fix code style --- src/Reflection/CacheReflectionProperty.php | 2 +- src/Reflection/Types/ArrayType.php | 1 - src/Reflection/Types/PropertyTypeFactory.php | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Reflection/CacheReflectionProperty.php b/src/Reflection/CacheReflectionProperty.php index c2ba740..8de1b8f 100644 --- a/src/Reflection/CacheReflectionProperty.php +++ b/src/Reflection/CacheReflectionProperty.php @@ -52,7 +52,7 @@ public function notTransform(): bool { return $this->notTransform; } - + /** * @return bool */ diff --git a/src/Reflection/Types/ArrayType.php b/src/Reflection/Types/ArrayType.php index a2bfe15..f4c534d 100644 --- a/src/Reflection/Types/ArrayType.php +++ b/src/Reflection/Types/ArrayType.php @@ -14,5 +14,4 @@ class ArrayType extends PropertyType /** @var string|class-string */ public string $itemsType; public bool $isScalarItems; - } diff --git a/src/Reflection/Types/PropertyTypeFactory.php b/src/Reflection/Types/PropertyTypeFactory.php index 2edd61c..9b9931b 100644 --- a/src/Reflection/Types/PropertyTypeFactory.php +++ b/src/Reflection/Types/PropertyTypeFactory.php @@ -58,7 +58,7 @@ public static function create(RuntimeReflectionProperty $property) $arrayType = TransformUtils::getClassFromPhpDoc($property->getDocComment()); } $arrayType ??= TypeEnums::TYPE_MIXED; - + $typeInstance = new ArrayType( $type, $isScalar, From 593572f3ab6dcbaf2ae856dcabd19e896392c77c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tatarynowicz?= Date: Tue, 1 Aug 2023 13:30:56 +0200 Subject: [PATCH 094/100] Fix first example in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e216c7a..2fbbdf7 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ var_dump($dto); Result: ```php -object(\LoginDTO) +object(\CreateUserDTO) 'email' => string(13) "test@mail.com" 'balance' => float(128.41) ``` From add8ce1fe6a66cdd49aaa48b2d35704e8a415266 Mon Sep 17 00:00:00 2001 From: Volodymyr Hordiienko Date: Sat, 11 Nov 2023 18:48:37 +0200 Subject: [PATCH 095/100] Allow ConvertArray attribute target properties --- src/Attributes/ConvertArray.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Attributes/ConvertArray.php b/src/Attributes/ConvertArray.php index 15e5512..fe0ea8e 100644 --- a/src/Attributes/ConvertArray.php +++ b/src/Attributes/ConvertArray.php @@ -11,7 +11,7 @@ * * @psalm-api */ -#[Attribute(Attribute::TARGET_PARAMETER)] +#[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_PROPERTY)] final class ConvertArray { /** From 579fcb0fb839067e6d47cdc27b0860e4576ac5b7 Mon Sep 17 00:00:00 2001 From: Volodymyr Hordiienko Date: Sun, 12 Nov 2023 15:19:31 +0200 Subject: [PATCH 096/100] Update EmptyToNull.php --- src/Attributes/EmptyToNull.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Attributes/EmptyToNull.php b/src/Attributes/EmptyToNull.php index 7d64b79..ff7ba77 100644 --- a/src/Attributes/EmptyToNull.php +++ b/src/Attributes/EmptyToNull.php @@ -10,7 +10,7 @@ * * @psalm-api */ -#[Attribute(Attribute::TARGET_PARAMETER)] +#[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_PROPERTY)] final class EmptyToNull { } From 85469827982c4d113b01d6a461483caf2b7f6298 Mon Sep 17 00:00:00 2001 From: Volodymyr Hordiienko Date: Sun, 12 Nov 2023 15:19:41 +0200 Subject: [PATCH 097/100] Update FieldAlias.php --- src/Attributes/FieldAlias.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Attributes/FieldAlias.php b/src/Attributes/FieldAlias.php index fdd4abb..e2d7ae5 100644 --- a/src/Attributes/FieldAlias.php +++ b/src/Attributes/FieldAlias.php @@ -9,7 +9,7 @@ /** * @psalm-api */ -#[Attribute(Attribute::TARGET_PARAMETER)] +#[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_PROPERTY)] final class FieldAlias { /** From 1c8b38ec399d5603c0fda60736db92f1f1a671dd Mon Sep 17 00:00:00 2001 From: Volodymyr Hordiienko Date: Sun, 12 Nov 2023 15:19:51 +0200 Subject: [PATCH 098/100] Update NotTransform.php --- src/Attributes/NotTransform.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Attributes/NotTransform.php b/src/Attributes/NotTransform.php index 4e8f751..6d9d52e 100644 --- a/src/Attributes/NotTransform.php +++ b/src/Attributes/NotTransform.php @@ -11,7 +11,7 @@ * * @psalm-api */ -#[Attribute(Attribute::TARGET_PARAMETER)] +#[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_PROPERTY)] final class NotTransform { } From 26eafefce853c1d7d3b4371b3ec17ad43086c9b6 Mon Sep 17 00:00:00 2001 From: Volodymyr Hordiienko Date: Sun, 12 Nov 2023 15:20:02 +0200 Subject: [PATCH 099/100] Update WritingStyle.php --- src/Attributes/WritingStyle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Attributes/WritingStyle.php b/src/Attributes/WritingStyle.php index 4d42e40..60c84a0 100644 --- a/src/Attributes/WritingStyle.php +++ b/src/Attributes/WritingStyle.php @@ -11,7 +11,7 @@ * * @psalm-api */ -#[Attribute(Attribute::TARGET_PARAMETER)] +#[Attribute(Attribute::TARGET_PARAMETER | Attribute::TARGET_PROPERTY)] final class WritingStyle { /** Check all possible styles */ From c094916743802e2bf025bda4623ebc6dbcabb3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=AF=D1=86=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Wed, 15 Nov 2023 18:32:39 +0300 Subject: [PATCH 100/100] composer update --- composer.json | 2 +- composer.lock | 392 ++++++++++++++++++++++++++------------------------ 2 files changed, 203 insertions(+), 191 deletions(-) diff --git a/composer.json b/composer.json index 98984bd..d9e1407 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yzen.dev/plain-to-class", - "version": "3.0.4", + "version": "3.0.5", "description": "Class-transformer to transform your dataset into a structured object", "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index d23fbb9..8349b4f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fe8f42c900ac36a6eb10fd83777d6411", + "content-hash": "0c65ba8a4a6a03d51bd17edabacb6623", "packages": [], "packages-dev": [ { @@ -266,16 +266,16 @@ }, { "name": "composer/pcre", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", "shasum": "" }, "require": { @@ -317,7 +317,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.0" + "source": "https://github.com/composer/pcre/tree/3.1.1" }, "funding": [ { @@ -333,20 +333,20 @@ "type": "tidelift" } ], - "time": "2022-11-17T09:50:14+00:00" + "time": "2023-10-11T07:11:09+00:00" }, { "name": "composer/semver", - "version": "3.3.2", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", "shasum": "" }, "require": { @@ -396,9 +396,9 @@ "versioning" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" + "source": "https://github.com/composer/semver/tree/3.4.0" }, "funding": [ { @@ -414,7 +414,7 @@ "type": "tidelift" } ], - "time": "2022-04-01T19:23:25+00:00" + "time": "2023-08-31T09:50:34+00:00" }, { "name": "composer/xdebug-handler", @@ -597,16 +597,16 @@ }, { "name": "doctrine/deprecations", - "version": "v1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { @@ -638,9 +638,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" }, - "time": "2023-06-03T09:27:29+00:00" + "time": "2023-09-27T20:04:15+00:00" }, { "name": "doctrine/lexer", @@ -1243,16 +1243,16 @@ }, { "name": "justinrainbow/json-schema", - "version": "5.2.12", + "version": "v5.2.13", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60" + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", "shasum": "" }, "require": { @@ -1307,9 +1307,9 @@ ], "support": { "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12" + "source": "https://github.com/justinrainbow/json-schema/tree/v5.2.13" }, - "time": "2022-04-13T08:02:27+00:00" + "time": "2023-09-26T02:20:38+00:00" }, { "name": "mockery/mockery", @@ -1495,16 +1495,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.16.0", + "version": "v4.17.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17" + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "shasum": "" }, "require": { @@ -1545,38 +1545,38 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" }, - "time": "2023-06-25T14:52:30+00:00" + "time": "2023-08-13T19:53:39+00:00" }, { "name": "ocramius/package-versions", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/Ocramius/PackageVersions.git", - "reference": "065921ed7cb2a6861443d91138d0a4378316af8d" + "reference": "7b5821f854cf1e6753c4ed7ceb3b11ae83bbad4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/065921ed7cb2a6861443d91138d0a4378316af8d", - "reference": "065921ed7cb2a6861443d91138d0a4378316af8d", + "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/7b5821f854cf1e6753c4ed7ceb3b11ae83bbad4e", + "reference": "7b5821f854cf1e6753c4ed7ceb3b11ae83bbad4e", "shasum": "" }, "require": { "composer-runtime-api": "^2.2.0", - "php": "~8.1.0 || ~8.2.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "replace": { "composer/package-versions-deprecated": "*" }, "require-dev": { - "composer/composer": "^2.4.4", - "doctrine/coding-standard": "^10.0.0", + "composer/composer": "^2.6.3", + "doctrine/coding-standard": "^12.0.0", "ext-zip": "^1.15.0", - "phpunit/phpunit": "^9.5.26", - "roave/infection-static-analysis-plugin": "^1.25.0", - "vimeo/psalm": "^4.29.0" + "phpunit/phpunit": "^9.6.12", + "roave/infection-static-analysis-plugin": "^1.33", + "vimeo/psalm": "^5.15.0" }, "type": "library", "autoload": { @@ -1597,7 +1597,7 @@ "description": "Provides efficient querying for installed package versions (no runtime IO)", "support": { "issues": "https://github.com/Ocramius/PackageVersions/issues", - "source": "https://github.com/Ocramius/PackageVersions/tree/2.7.0" + "source": "https://github.com/Ocramius/PackageVersions/tree/2.8.0" }, "funding": [ { @@ -1609,7 +1609,7 @@ "type": "tidelift" } ], - "time": "2022-10-31T12:51:46+00:00" + "time": "2023-09-15T11:02:59+00:00" }, { "name": "ondram/ci-detector", @@ -1802,21 +1802,21 @@ }, { "name": "phpbench/container", - "version": "2.2.1", + "version": "2.2.2", "source": { "type": "git", "url": "https://github.com/phpbench/container.git", - "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392" + "reference": "a59b929e00b87b532ca6d0edd8eca0967655af33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/container/zipball/6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", - "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", + "url": "https://api.github.com/repos/phpbench/container/zipball/a59b929e00b87b532ca6d0edd8eca0967655af33", + "reference": "a59b929e00b87b532ca6d0edd8eca0967655af33", "shasum": "" }, "require": { "psr/container": "^1.0|^2.0", - "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0" + "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.16", @@ -1847,9 +1847,9 @@ "description": "Simple, configurable, service container.", "support": { "issues": "https://github.com/phpbench/container/issues", - "source": "https://github.com/phpbench/container/tree/2.2.1" + "source": "https://github.com/phpbench/container/tree/2.2.2" }, - "time": "2022-01-25T10:17:35+00:00" + "time": "2023-10-30T13:38:26+00:00" }, { "name": "phpbench/dom", @@ -2104,16 +2104,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.7.2", + "version": "1.7.3", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d" + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d", - "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", "shasum": "" }, "require": { @@ -2156,22 +2156,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" }, - "time": "2023-05-30T18:13:47+00:00" + "time": "2023-08-12T11:01:26+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.22.1", + "version": "1.24.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0" + "reference": "bcad8d995980440892759db0c32acae7c8e79442" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", + "reference": "bcad8d995980440892759db0c32acae7c8e79442", "shasum": "" }, "require": { @@ -2203,22 +2203,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" }, - "time": "2023-06-29T20:46:06+00:00" + "time": "2023-09-26T12:28:12+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.2", + "version": "10.1.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" + "reference": "84838eed9ded511f61dc3e8b5944a52d9017b297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/84838eed9ded511f61dc3e8b5944a52d9017b297", + "reference": "84838eed9ded511f61dc3e8b5944a52d9017b297", "shasum": "" }, "require": { @@ -2275,7 +2275,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.8" }, "funding": [ { @@ -2283,20 +2283,20 @@ "type": "github" } ], - "time": "2023-05-22T09:04:27+00:00" + "time": "2023-11-15T13:31:15+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.0.2", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "5647d65443818959172645e7ed999217360654b6" + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/5647d65443818959172645e7ed999217360654b6", - "reference": "5647d65443818959172645e7ed999217360654b6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, "require": { @@ -2336,7 +2336,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.2" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" }, "funding": [ { @@ -2344,7 +2344,7 @@ "type": "github" } ], - "time": "2023-05-07T09:13:23+00:00" + "time": "2023-08-31T06:24:48+00:00" }, { "name": "phpunit/php-invoker", @@ -2411,16 +2411,16 @@ }, { "name": "phpunit/php-text-template", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d" + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d", - "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, "require": { @@ -2458,7 +2458,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" }, "funding": [ { @@ -2466,7 +2467,7 @@ "type": "github" } ], - "time": "2023-02-03T06:56:46+00:00" + "time": "2023-08-31T14:07:24+00:00" }, { "name": "phpunit/php-timer", @@ -2833,31 +2834,37 @@ }, { "name": "sanmai/later", - "version": "0.1.2", + "version": "0.1.3", "source": { "type": "git", "url": "https://github.com/sanmai/later.git", - "reference": "9b659fecef2030193fd02402955bc39629d5606f" + "reference": "88a1d39965aa3659ceb96622e2801b9194d16e2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/later/zipball/9b659fecef2030193fd02402955bc39629d5606f", - "reference": "9b659fecef2030193fd02402955bc39629d5606f", + "url": "https://api.github.com/repos/sanmai/later/zipball/88a1d39965aa3659ceb96622e2801b9194d16e2c", + "reference": "88a1d39965aa3659ceb96622e2801b9194d16e2c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.4" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.13", - "infection/infection": ">=0.10.5", + "ergebnis/composer-normalize": "^2.8", + "friendsofphp/php-cs-fixer": "^3.35.1", + "infection/infection": ">=0.27.6", "phan/phan": ">=2", "php-coveralls/php-coveralls": "^2.0", - "phpstan/phpstan": ">=0.10", - "phpunit/phpunit": ">=7.4", + "phpstan/phpstan": ">=1.4.5", + "phpunit/phpunit": ">=9.5 <10", "vimeo/psalm": ">=2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, "autoload": { "files": [ "src/functions.php" @@ -2879,7 +2886,7 @@ "description": "Later: deferred wrapper object", "support": { "issues": "https://github.com/sanmai/later/issues", - "source": "https://github.com/sanmai/later/tree/0.1.2" + "source": "https://github.com/sanmai/later/tree/0.1.3" }, "funding": [ { @@ -2887,20 +2894,20 @@ "type": "github" } ], - "time": "2021-01-02T10:26:44+00:00" + "time": "2023-10-23T13:38:10+00:00" }, { "name": "sanmai/pipeline", - "version": "v6.8.1", + "version": "v6.9", "source": { "type": "git", "url": "https://github.com/sanmai/pipeline.git", - "reference": "2e88e466dd49f20c10a15330b3953d4d49c326e3" + "reference": "c48f45c22c3ce4140d071f7658fb151df1cc08ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/pipeline/zipball/2e88e466dd49f20c10a15330b3953d4d49c326e3", - "reference": "2e88e466dd49f20c10a15330b3953d4d49c326e3", + "url": "https://api.github.com/repos/sanmai/pipeline/zipball/c48f45c22c3ce4140d071f7658fb151df1cc08ea", + "reference": "c48f45c22c3ce4140d071f7658fb151df1cc08ea", "shasum": "" }, "require": { @@ -2944,7 +2951,7 @@ "description": "General-purpose collections pipeline", "support": { "issues": "https://github.com/sanmai/pipeline/issues", - "source": "https://github.com/sanmai/pipeline/tree/v6.8.1" + "source": "https://github.com/sanmai/pipeline/tree/v6.9" }, "funding": [ { @@ -2952,7 +2959,7 @@ "type": "github" } ], - "time": "2023-06-15T09:14:47+00:00" + "time": "2023-10-08T11:56:54+00:00" }, { "name": "sebastian/cli-parser", @@ -3123,16 +3130,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", "shasum": "" }, "require": { @@ -3143,7 +3150,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.3" }, "type": "library", "extra": { @@ -3187,7 +3194,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" }, "funding": [ { @@ -3195,20 +3203,20 @@ "type": "github" } ], - "time": "2023-02-03T07:07:16+00:00" + "time": "2023-08-14T13:18:12+00:00" }, { "name": "sebastian/complexity", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6" + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6", - "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", "shasum": "" }, "require": { @@ -3221,7 +3229,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.1-dev" } }, "autoload": { @@ -3244,7 +3252,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" }, "funding": [ { @@ -3252,7 +3261,7 @@ "type": "github" } ], - "time": "2023-02-03T06:59:47+00:00" + "time": "2023-09-28T11:50:59+00:00" }, { "name": "sebastian/diff", @@ -3387,16 +3396,16 @@ }, { "name": "sebastian/exporter", - "version": "5.0.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc", + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc", "shasum": "" }, "require": { @@ -3410,7 +3419,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -3452,7 +3461,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1" }, "funding": [ { @@ -3460,20 +3470,20 @@ "type": "github" } ], - "time": "2023-02-03T07:06:49+00:00" + "time": "2023-09-24T13:22:09+00:00" }, { "name": "sebastian/global-state", - "version": "6.0.0", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "aab257c712de87b90194febd52e4d184551c2d44" + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/aab257c712de87b90194febd52e4d184551c2d44", - "reference": "aab257c712de87b90194febd52e4d184551c2d44", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4", + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4", "shasum": "" }, "require": { @@ -3513,7 +3523,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.0" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1" }, "funding": [ { @@ -3521,20 +3532,20 @@ "type": "github" } ], - "time": "2023-02-03T07:07:38+00:00" + "time": "2023-07-19T07:19:23+00:00" }, { "name": "sebastian/lines-of-code", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130" + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130", - "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", "shasum": "" }, "require": { @@ -3570,7 +3581,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" }, "funding": [ { @@ -3578,7 +3590,7 @@ "type": "github" } ], - "time": "2023-02-03T07:08:02+00:00" + "time": "2023-08-31T09:25:50+00:00" }, { "name": "sebastian/object-enumerator", @@ -3930,16 +3942,16 @@ }, { "name": "spatie/array-to-xml", - "version": "3.1.6", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/spatie/array-to-xml.git", - "reference": "e210b98957987c755372465be105d32113f339a4" + "reference": "96be97e664c87613121d073ea39af4c74e57a7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/e210b98957987c755372465be105d32113f339a4", - "reference": "e210b98957987c755372465be105d32113f339a4", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/96be97e664c87613121d073ea39af4c74e57a7f8", + "reference": "96be97e664c87613121d073ea39af4c74e57a7f8", "shasum": "" }, "require": { @@ -3977,7 +3989,7 @@ "xml" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/3.1.6" + "source": "https://github.com/spatie/array-to-xml/tree/3.2.2" }, "funding": [ { @@ -3989,7 +4001,7 @@ "type": "github" } ], - "time": "2023-05-11T14:04:07+00:00" + "time": "2023-11-14T14:08:51+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -4049,16 +4061,16 @@ }, { "name": "symfony/console", - "version": "v6.3.0", + "version": "v6.3.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7" + "reference": "0d14a9f6d04d4ac38a8cea1171f4554e325dae92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", + "url": "https://api.github.com/repos/symfony/console/zipball/0d14a9f6d04d4ac38a8cea1171f4554e325dae92", + "reference": "0d14a9f6d04d4ac38a8cea1171f4554e325dae92", "shasum": "" }, "require": { @@ -4119,7 +4131,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.0" + "source": "https://github.com/symfony/console/tree/v6.3.8" }, "funding": [ { @@ -4135,7 +4147,7 @@ "type": "tidelift" } ], - "time": "2023-05-29T12:49:39+00:00" + "time": "2023-10-31T08:09:35+00:00" }, { "name": "symfony/deprecation-contracts", @@ -4269,16 +4281,16 @@ }, { "name": "symfony/finder", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2" + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d9b01ba073c44cef617c7907ce2419f8d00d75e2", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2", + "url": "https://api.github.com/repos/symfony/finder/zipball/a1b31d88c0e998168ca7792f222cbecee47428c4", + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4", "shasum": "" }, "require": { @@ -4313,7 +4325,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.0" + "source": "https://github.com/symfony/finder/tree/v6.3.5" }, "funding": [ { @@ -4329,7 +4341,7 @@ "type": "tidelift" } ], - "time": "2023-04-02T01:25:41+00:00" + "time": "2023-09-26T12:56:25+00:00" }, { "name": "symfony/options-resolver", @@ -4400,16 +4412,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -4424,7 +4436,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4462,7 +4474,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -4478,20 +4490,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "875e90aeea2777b6f135677f618529449334a612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", "shasum": "" }, "require": { @@ -4503,7 +4515,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4543,7 +4555,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" }, "funding": [ { @@ -4559,20 +4571,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", "shasum": "" }, "require": { @@ -4584,7 +4596,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4627,7 +4639,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" }, "funding": [ { @@ -4643,20 +4655,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -4671,7 +4683,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4710,7 +4722,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -4726,20 +4738,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/process", - "version": "v6.3.0", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628" + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8741e3ed7fe2e91ec099e02446fb86667a0f1628", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628", + "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", "shasum": "" }, "require": { @@ -4771,7 +4783,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.0" + "source": "https://github.com/symfony/process/tree/v6.3.4" }, "funding": [ { @@ -4787,7 +4799,7 @@ "type": "tidelift" } ], - "time": "2023-05-19T08:06:44+00:00" + "time": "2023-08-07T10:39:22+00:00" }, { "name": "symfony/service-contracts", @@ -4873,16 +4885,16 @@ }, { "name": "symfony/string", - "version": "v6.3.0", + "version": "v6.3.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f" + "reference": "13880a87790c76ef994c91e87efb96134522577a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f", + "url": "https://api.github.com/repos/symfony/string/zipball/13880a87790c76ef994c91e87efb96134522577a", + "reference": "13880a87790c76ef994c91e87efb96134522577a", "shasum": "" }, "require": { @@ -4939,7 +4951,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.0" + "source": "https://github.com/symfony/string/tree/v6.3.8" }, "funding": [ { @@ -4955,7 +4967,7 @@ "type": "tidelift" } ], - "time": "2023-03-21T21:06:29+00:00" + "time": "2023-11-09T08:28:21+00:00" }, { "name": "symfony/var-dumper", @@ -5451,5 +5463,5 @@ "php": "^8.0" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" }