From 5bab92bf3b9bdb0a4650db6b3f83ff69dbbd6bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Barto=C5=A1?= Date: Sat, 22 Feb 2025 23:00:18 +0100 Subject: [PATCH] Remove runtime reflection from FieldRutimeMeta --- CHANGELOG.md | 4 +- src/Meta/MetaResolver.php | 5 +- src/Meta/Runtime/FieldRuntimeMeta.php | 10 ++-- src/Meta/Runtime/PhpPropertyMeta.php | 55 +++++++++++++++++++ src/Processing/DefaultProcessor.php | 26 ++++----- .../Meta/Runtime/FieldRuntimeMetaTest.php | 4 +- .../Unit/Meta/Runtime/PhpPropertyMetaTest.php | 48 ++++++++++++++++ tests/Unit/Meta/Runtime/RuntimeMetaTest.php | 4 +- 8 files changed, 126 insertions(+), 30 deletions(-) create mode 100644 src/Meta/Runtime/PhpPropertyMeta.php create mode 100644 tests/Unit/Meta/Runtime/PhpPropertyMetaTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a4ecaf2..1767661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `DefaultProcessor` - Simplify finding missing fields (performance optimization) - Reduce calls needed to find field names for "did you mean" error helper (performance optimization) - - Call `unset()` only for initialized properties (performance optimization) - Create functions for setting and unsetting non public-set properties only once (performance optimization) - Callbacks - renamed @@ -62,9 +61,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `Options` - `withProcessedClass()`, `getProcessedClass()` - no longer used - Runtime reflection (performance optimization), from: - - `PropertyContext` - `CallbackRuntimeMeta` - `Callback` + - `PropertyContext` + - `FieldRuntimeMeta` ## [0.3.0](https://github.com/orisai/object-mapper/compare/0.2.0...0.3.0) - 2025-01-21 diff --git a/src/Meta/MetaResolver.php b/src/Meta/MetaResolver.php index accffe2..551d991 100644 --- a/src/Meta/MetaResolver.php +++ b/src/Meta/MetaResolver.php @@ -21,6 +21,7 @@ use Orisai\ObjectMapper\Meta\Runtime\ClassRuntimeMeta; use Orisai\ObjectMapper\Meta\Runtime\FieldRuntimeMeta; use Orisai\ObjectMapper\Meta\Runtime\ModifierRuntimeMeta; +use Orisai\ObjectMapper\Meta\Runtime\PhpPropertyMeta; use Orisai\ObjectMapper\Meta\Runtime\RuleRuntimeMeta; use Orisai\ObjectMapper\Meta\Runtime\RuntimeMeta; use Orisai\ObjectMapper\Meta\Shared\DefaultValueMeta; @@ -217,7 +218,7 @@ private function propertyNameToFieldName(FieldRuntimeMeta $fieldMeta) return $modifier->args->name; } - return $fieldMeta->property->getName(); + return $fieldMeta->property->name; } /** @@ -260,7 +261,7 @@ private function resolveFieldMeta( $context, ), $defaultValue, - $reflector, + PhpPropertyMeta::from($reflector), ); } diff --git a/src/Meta/Runtime/FieldRuntimeMeta.php b/src/Meta/Runtime/FieldRuntimeMeta.php index a3a133f..524ef62 100644 --- a/src/Meta/Runtime/FieldRuntimeMeta.php +++ b/src/Meta/Runtime/FieldRuntimeMeta.php @@ -5,7 +5,6 @@ use Orisai\ObjectMapper\Args\Args; use Orisai\ObjectMapper\Meta\Shared\DefaultValueMeta; use Orisai\ObjectMapper\Modifiers\Modifier; -use ReflectionProperty; final class FieldRuntimeMeta extends NodeRuntimeMeta { @@ -15,7 +14,7 @@ final class FieldRuntimeMeta extends NodeRuntimeMeta public DefaultValueMeta $default; - public ReflectionProperty $property; + public PhpPropertyMeta $property; /** @var array>, ModifierRuntimeMeta> */ public array $modifiers; @@ -31,7 +30,7 @@ public function __construct( array $modifiers, RuleRuntimeMeta $rule, DefaultValueMeta $default, - ReflectionProperty $property + PhpPropertyMeta $property ) { parent::__construct($callbacks, $docs); @@ -60,8 +59,7 @@ public function __serialize(): array 'parent' => parent::__serialize(), 'rule' => $this->rule, 'default' => $this->default, - 'class' => $this->property->getDeclaringClass()->getName(), - 'property' => $this->property->getName(), + 'property' => $this->property, 'modifiers' => $this->modifiers, ]; } @@ -74,7 +72,7 @@ public function __unserialize(array $data): void parent::__unserialize($data['parent']); $this->rule = $data['rule']; $this->default = $data['default']; - $this->property = new ReflectionProperty($data['class'], $data['property']); + $this->property = $data['property']; $this->modifiers = $data['modifiers']; } diff --git a/src/Meta/Runtime/PhpPropertyMeta.php b/src/Meta/Runtime/PhpPropertyMeta.php new file mode 100644 index 0000000..400ae00 --- /dev/null +++ b/src/Meta/Runtime/PhpPropertyMeta.php @@ -0,0 +1,55 @@ + */ + public string $declaringClass; + + public string $name; + + public bool $isPublicSet; + + /** + * @param class-string $declaringClass + */ + public function __construct( + string $declaringClass, + string $name, + bool $isPublicSet + ) + { + $this->declaringClass = $declaringClass; + $this->name = $name; + $this->isPublicSet = $isPublicSet; + } + + public static function from(ReflectionProperty $property): self + { + /** @var ReflectionClass $class */ + $class = $property->getDeclaringClass(); + + return new self( + $class->getName(), + $property->getName(), + self::isPublicSet($property), + ); + } + + private static function isPublicSet(ReflectionProperty $property): bool + { + return $property->isPublic() + && (PHP_VERSION_ID < 8_01_00 || !$property->isReadOnly()); + } + +} diff --git a/src/Processing/DefaultProcessor.php b/src/Processing/DefaultProcessor.php index bb22fe8..7edc704 100644 --- a/src/Processing/DefaultProcessor.php +++ b/src/Processing/DefaultProcessor.php @@ -34,7 +34,6 @@ use function array_map; use function assert; use function is_array; -use const PHP_VERSION_ID; final class DefaultProcessor implements Processor { @@ -328,13 +327,13 @@ private function handleSentFields( } $property = $fieldMeta->property; - $className = $property->getDeclaringClass()->getName(); - $propertyName = $property->getName(); + $className = $property->declaringClass; + $propertyName = $property->name; $propertyContext = $this->propertyContextCache[$className][$propertyName] ?? ( $this->propertyContextCache[$className][$propertyName] = new PropertyContext( $fieldMeta->default, - $fieldMeta->property->getName(), + $fieldMeta->property->name, $fieldName, )); @@ -581,18 +580,13 @@ private function fillObject( // Reset mapped properties state foreach ($fieldsMeta as $fieldMeta) { $property = $fieldMeta->property; - $declaringClass = $property->getDeclaringClass(); - $name = $property->getName(); - - if ( - $property->isInitialized($object) - && $property->isPublic() - && (PHP_VERSION_ID < 8_01_00 || !$property->isReadOnly()) - ) { + $name = $property->name; + + if ($property->isPublicSet) { unset($object->$name); } else { // phpcs:disable SlevomatCodingStandard.Functions.StaticClosure - $unsetter->bindTo($object, $declaringClass->getName())($object, $name); + $unsetter->bindTo($object, $property->declaringClass)($object, $name); // phpcs:enable } } @@ -603,14 +597,14 @@ private function fillObject( // Set processed data foreach ($data as $fieldName => $value) { $property = $fieldsMeta[$fieldName]->property; - $name = $property->getName(); + $name = $property->name; - if ($property->isPublic() && (PHP_VERSION_ID < 8_01_00 || !$property->isReadOnly())) { + if ($property->isPublicSet) { $object->$name = $value; } else { $setter->bindTo( $object, - $property->getDeclaringClass()->getName(), + $property->declaringClass, )($object, $name, $value); } } diff --git a/tests/Unit/Meta/Runtime/FieldRuntimeMetaTest.php b/tests/Unit/Meta/Runtime/FieldRuntimeMetaTest.php index 5ed0310..2a24b82 100644 --- a/tests/Unit/Meta/Runtime/FieldRuntimeMetaTest.php +++ b/tests/Unit/Meta/Runtime/FieldRuntimeMetaTest.php @@ -12,6 +12,7 @@ use Orisai\ObjectMapper\Meta\Runtime\FieldRuntimeMeta; use Orisai\ObjectMapper\Meta\Runtime\ModifierRuntimeMeta; use Orisai\ObjectMapper\Meta\Runtime\PhpMethodMeta; +use Orisai\ObjectMapper\Meta\Runtime\PhpPropertyMeta; use Orisai\ObjectMapper\Meta\Runtime\RuleRuntimeMeta; use Orisai\ObjectMapper\Meta\Shared\DefaultValueMeta; use Orisai\ObjectMapper\Meta\Shared\DocMeta; @@ -19,7 +20,6 @@ use Orisai\ObjectMapper\Modifiers\FieldNameModifier; use Orisai\ObjectMapper\Rules\MixedRule; use PHPUnit\Framework\TestCase; -use ReflectionProperty; use Tests\Orisai\ObjectMapper\Doubles\NoDefaultsVO; use function serialize; use function unserialize; @@ -29,7 +29,7 @@ final class FieldRuntimeMetaTest extends TestCase public function test(): void { - $property = new ReflectionProperty(NoDefaultsVO::class, 'string'); + $property = new PhpPropertyMeta(NoDefaultsVO::class, 'property', true); $beforeCallbacks = [ new CallbackRuntimeMeta( diff --git a/tests/Unit/Meta/Runtime/PhpPropertyMetaTest.php b/tests/Unit/Meta/Runtime/PhpPropertyMetaTest.php new file mode 100644 index 0000000..2be74ae --- /dev/null +++ b/tests/Unit/Meta/Runtime/PhpPropertyMetaTest.php @@ -0,0 +1,48 @@ + $declaringClass + * + * @dataProvider provide + */ + public function test( + string $declaringClass, + string $name, + bool $isPublicSet + ): void + { + $meta = new PhpPropertyMeta($declaringClass, $name, $isPublicSet); + + self::assertSame($declaringClass, $meta->declaringClass); + self::assertSame($name, $meta->name); + self::assertSame($isPublicSet, $meta->isPublicSet); + } + + public function provide(): Generator + { + yield [ + NoDefaultsVO::class, + 'property', + true, + ]; + + yield [ + DefaultsVO::class, + 'anotherProperty', + false, + ]; + } + +} diff --git a/tests/Unit/Meta/Runtime/RuntimeMetaTest.php b/tests/Unit/Meta/Runtime/RuntimeMetaTest.php index 9f850aa..dddc71d 100644 --- a/tests/Unit/Meta/Runtime/RuntimeMetaTest.php +++ b/tests/Unit/Meta/Runtime/RuntimeMetaTest.php @@ -5,12 +5,12 @@ use Orisai\ObjectMapper\Args\EmptyArgs; use Orisai\ObjectMapper\Meta\Runtime\ClassRuntimeMeta; use Orisai\ObjectMapper\Meta\Runtime\FieldRuntimeMeta; +use Orisai\ObjectMapper\Meta\Runtime\PhpPropertyMeta; use Orisai\ObjectMapper\Meta\Runtime\RuleRuntimeMeta; use Orisai\ObjectMapper\Meta\Runtime\RuntimeMeta; use Orisai\ObjectMapper\Meta\Shared\DefaultValueMeta; use Orisai\ObjectMapper\Rules\MixedRule; use PHPUnit\Framework\TestCase; -use ReflectionProperty; use Tests\Orisai\ObjectMapper\Doubles\NoDefaultsVO; use function serialize; use function unserialize; @@ -28,7 +28,7 @@ public function test(): void [], new RuleRuntimeMeta(MixedRule::class, new EmptyArgs()), DefaultValueMeta::fromNothing(), - new ReflectionProperty(NoDefaultsVO::class, 'string'), + new PhpPropertyMeta(NoDefaultsVO::class, 'property', true), ), ];