From acf8cd0f36fe5ddcde93368ec033e1f703c19454 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Fri, 25 Oct 2024 16:48:25 -0300 Subject: [PATCH] refactor: remove and migrate processor configuration components BREAKING CHANGE: Extracted processor configuration related components to a separate package - Removed attribute handling functionality: * Deleted AttributeHandler and its tests * Removed benchmark tests - Migrated processor configuration components: * Moved ProcessorConfigBuilder (contract and implementation) * Moved ProcessorNameNormalizer * Moved ProcessorValidator - Updated composer dependencies --- composer.json | 4 +- composer.lock | 257 +-------- src/AttributeHandler.php | 129 ----- src/Contract/ProcessorConfigBuilder.php | 19 - src/Processor/ProcessorConfigBuilder.php | 55 -- src/Processor/ProcessorNameNormalizer.php | 25 - src/Processor/ProcessorValidator.php | 26 - tests/AttributeHandlerTest.php | 236 -------- .../Processor/ProcessorConfigBuilderTest.php | 63 -- .../Processor/ProcessorNameNormalizerTest.php | 39 -- tests/Processor/ProcessorValidatorTest.php | 71 --- ...erphp => benchmark_attribute_analyzer.php} | 51 +- tests/benchmark_attribute_handler.php | 536 ------------------ 13 files changed, 26 insertions(+), 1485 deletions(-) delete mode 100644 src/AttributeHandler.php delete mode 100644 src/Contract/ProcessorConfigBuilder.php delete mode 100644 src/Processor/ProcessorConfigBuilder.php delete mode 100644 src/Processor/ProcessorNameNormalizer.php delete mode 100644 src/Processor/ProcessorValidator.php delete mode 100644 tests/AttributeHandlerTest.php delete mode 100644 tests/Processor/ProcessorConfigBuilderTest.php delete mode 100644 tests/Processor/ProcessorNameNormalizerTest.php delete mode 100644 tests/Processor/ProcessorValidatorTest.php rename tests/{benchmark_attribute_analyzerphp => benchmark_attribute_analyzer.php} (89%) delete mode 100644 tests/benchmark_attribute_handler.php diff --git a/composer.json b/composer.json index d29c6f5..2647889 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,6 @@ ], "require": { "php": "^8.3", - "kariricode/processor-pipeline": "^1.1", "kariricode/contract": "^2.7" }, "autoload": { @@ -44,8 +43,7 @@ "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^11.0", "squizlabs/php_codesniffer": "^3.9", - "enlightn/security-checker": "^2.0", - "kariricode/validator": "^1.0" + "enlightn/security-checker": "^2.0" }, "support": { "issues": "https://github.com/KaririCode-Framework/kariricode-property-inspector/issues", diff --git a/composer.lock b/composer.lock index e275927..0c9327c 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": "789260e11a472e31aeb935652229ca59", + "content-hash": "378b4a9811895ac189627748a3af127c", "packages": [ { "name": "kariricode/contract", @@ -67,198 +67,6 @@ "source": "https://github.com/KaririCode-Framework/kariricode-contract" }, "time": "2024-10-25T17:45:25+00:00" - }, - { - "name": "kariricode/data-structure", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/KaririCode-Framework/kariricode-data-structure.git", - "reference": "91c9e7ef36143b5e3d449f1a132169580945a4c8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-data-structure/zipball/91c9e7ef36143b5e3d449f1a132169580945a4c8", - "reference": "91c9e7ef36143b5e3d449f1a132169580945a4c8", - "shasum": "" - }, - "require": { - "kariricode/contract": "^2.0", - "php": "^8.3" - }, - "require-dev": { - "enlightn/security-checker": "^2.0", - "friendsofphp/php-cs-fixer": "^3.58", - "mockery/mockery": "^1.6", - "nunomaduro/phpinsights": "^2.11", - "phpunit/phpunit": "^10.5", - "squizlabs/php_codesniffer": "^3.10" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "KaririCode\\DataStructure\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Walmir Silva", - "email": "walmir.silva@kariricode.org" - } - ], - "description": "The KaririCode DataStructure component offers advanced PHP data structures, including lists, stacks, queues, maps, and sets. It features efficient, strongly-typed, object-oriented implementations like ArrayList, LinkedList, BinaryHeap, and TreeMap.", - "homepage": "https://kariricode.org/", - "keywords": [ - "KaririCode", - "LinkedList", - "OOP", - "algorithms", - "arraylist", - "binary heap", - "collections", - "data structures", - "dynamic array", - "heap", - "php", - "queue", - "red-black tree", - "set", - "stack", - "strong typing", - "treemap" - ], - "support": { - "issues": "https://github.com/KaririCode-Framework/kariricode-data-structure/issues", - "source": "https://github.com/KaririCode-Framework/kariricode-data-structure" - }, - "time": "2024-10-10T22:37:23+00:00" - }, - { - "name": "kariricode/exception", - "version": "v1.2.2", - "source": { - "type": "git", - "url": "https://github.com/KaririCode-Framework/kariricode-exception.git", - "reference": "ec5d9be5bda95e7d35ff3a230ec9afbf6f53c44d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-exception/zipball/ec5d9be5bda95e7d35ff3a230ec9afbf6f53c44d", - "reference": "ec5d9be5bda95e7d35ff3a230ec9afbf6f53c44d", - "shasum": "" - }, - "require": { - "php": "^8.3" - }, - "require-dev": { - "enlightn/security-checker": "^2.0", - "friendsofphp/php-cs-fixer": "^3.51", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^11.0", - "squizlabs/php_codesniffer": "^3.9" - }, - "type": "library", - "autoload": { - "psr-4": { - "KaririCode\\Exception\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Walmir Silva", - "email": "community@kariricode.org" - } - ], - "description": "KaririCode Exception provides a robust and modular exception handling system for the KaririCode Framework, enabling seamless error management across various application domains.", - "homepage": "https://kariricode.org", - "keywords": [ - "error-management", - "exception-handling", - "framework", - "kariri-code", - "modular-exceptions", - "php-exceptions", - "php-framework" - ], - "support": { - "issues": "https://github.com/KaririCode-Framework/kariricode-exception/issues", - "source": "https://github.com/KaririCode-Framework/kariricode-exception" - }, - "time": "2024-10-25T18:13:01+00:00" - }, - { - "name": "kariricode/processor-pipeline", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline.git", - "reference": "cb916d9fdb3193d895167748efa04f0833782ab9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-processor-pipeline/zipball/cb916d9fdb3193d895167748efa04f0833782ab9", - "reference": "cb916d9fdb3193d895167748efa04f0833782ab9", - "shasum": "" - }, - "require": { - "kariricode/contract": "^2.7", - "kariricode/data-structure": "^1.1", - "kariricode/exception": "^1.2", - "kariricode/property-inspector": "^1.0", - "php": "^8.3" - }, - "require-dev": { - "enlightn/security-checker": "^2.0", - "friendsofphp/php-cs-fixer": "^3.51", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^11.0", - "squizlabs/php_codesniffer": "^3.9" - }, - "type": "library", - "autoload": { - "psr-4": { - "KaririCode\\ProcessorPipeline\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Walmir Silva", - "email": "community@kariricode.org" - } - ], - "description": "A flexible and extensible processor pipeline component for the KaririCode framework. Enables the creation of modular, configurable processing chains for data transformation, validation, and sanitization tasks", - "homepage": "https://kariricode.org", - "keywords": [ - "KaririCode", - "configurable", - "data processing", - "modular", - "php", - "pipeline", - "processor" - ], - "support": { - "issues": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline/issues", - "source": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline" - }, - "time": "2024-10-25T18:44:51+00:00" } ], "packages-dev": [ @@ -1154,69 +962,6 @@ ], "time": "2024-07-18T11:15:46+00:00" }, - { - "name": "kariricode/validator", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/KaririCode-Framework/kariricode-validator.git", - "reference": "885b4b157983bf601f99efda85ddd4052256db97" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-validator/zipball/885b4b157983bf601f99efda85ddd4052256db97", - "reference": "885b4b157983bf601f99efda85ddd4052256db97", - "shasum": "" - }, - "require": { - "kariricode/contract": "^2.7", - "kariricode/exception": "^1.0", - "kariricode/processor-pipeline": "^1.1", - "kariricode/property-inspector": "^1.0", - "php": "^8.3" - }, - "require-dev": { - "enlightn/security-checker": "^2.0", - "friendsofphp/php-cs-fixer": "^3.51", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^11.0", - "squizlabs/php_codesniffer": "^3.9" - }, - "type": "library", - "autoload": { - "psr-4": { - "KaririCode\\Validator\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Walmir Silva", - "email": "community@kariricode.org" - } - ], - "description": "A robust and flexible data sanitization component for PHP, part of the KaririCode Framework, utilizing configurable processors and native functions.", - "homepage": "https://kariricode.org", - "keywords": [ - "KaririCode", - "attribute", - "data", - "entity", - "php", - "pipeline", - "processor", - "validation", - "validator" - ], - "support": { - "issues": "https://github.com/KaririCode-Framework/kariricode-validator/issues", - "source": "https://github.com/KaririCode-Framework/kariricode-validator" - }, - "time": "2024-10-23T22:10:44+00:00" - }, { "name": "myclabs/deep-copy", "version": "1.12.0", diff --git a/src/AttributeHandler.php b/src/AttributeHandler.php deleted file mode 100644 index 297e101..0000000 --- a/src/AttributeHandler.php +++ /dev/null @@ -1,129 +0,0 @@ -processAttribute($propertyName, $attribute, $value); - } catch (\Exception $e) { - $this->processingResultErrors[$propertyName][] = $e->getMessage(); - - return $value; - } - } - - private function processAttribute(string $propertyName, ProcessableAttribute $attribute, mixed $value): mixed - { - $config = $this->configBuilder->build($attribute); - $messages = []; - - if ($attribute instanceof CustomizableMessageAttribute) { - foreach ($config as $processorName => &$processorConfig) { - if ($message = $attribute->getMessage($processorName)) { - $processorConfig['customMessage'] = $message; - $messages[$processorName] = $message; - } - } - } - - $processedValue = $this->processValue($value, $config); - - if ($errors = $this->validateProcessors($config, $messages)) { - $this->processingResultErrors[$propertyName] = $errors; - } - - $this->processedPropertyValues[$propertyName] = [ - 'value' => $processedValue, - 'messages' => $messages, - ]; - - $this->processingResultMessages[$propertyName] = $messages; - - return $processedValue; - } - - private function validateProcessors(array $processorsConfig, array $messages): array - { - $errors = []; - foreach ($processorsConfig as $processorName => $config) { - // Simplify cache key to processor name - if (!isset($this->processorCache[$processorName])) { - $this->processorCache[$processorName] = $this->builder->build( - $this->processorType, - $processorName, - $config - ); - } - - $processor = $this->processorCache[$processorName]; - - if ($error = $this->validator->validate($processor, $processorName, $messages)) { - $errors[$processorName] = $error; - } - } - - return $errors; - } - - private function processValue(mixed $value, array $config): mixed - { - return $this->builder - ->buildPipeline($this->processorType, $config) - ->process($value); - } - - public function applyChanges(object $entity): void - { - foreach ($this->processedPropertyValues as $propertyName => $data) { - (new PropertyAccessor($entity, $propertyName))->setValue($data['value']); - } - } - - public function getProcessedPropertyValues(): array - { - return $this->processedPropertyValues; - } - - public function getProcessingResultErrors(): array - { - return $this->processingResultErrors; - } - - public function getProcessingResultMessages(): array - { - return $this->processingResultMessages; - } -} diff --git a/src/Contract/ProcessorConfigBuilder.php b/src/Contract/ProcessorConfigBuilder.php deleted file mode 100644 index cc61196..0000000 --- a/src/Contract/ProcessorConfigBuilder.php +++ /dev/null @@ -1,19 +0,0 @@ -getProcessors(); - $processorsConfig = []; - - foreach ($processors as $key => $processor) { - if ($this->isSimpleProcessor($processor)) { - $processorsConfig[$processor] = $this->getDefaultProcessorConfig(); - } elseif ($this->isConfigurableProcessor($processor)) { - $processorName = $this->determineProcessorName($key, $processor); - $processorsConfig[$processorName] = $this->getProcessorConfig($processor); - } - } - - return $processorsConfig; - } - - private function isSimpleProcessor(mixed $processor): bool - { - return is_string($processor); - } - - private function isConfigurableProcessor(mixed $processor): bool - { - return is_array($processor); - } - - private function getDefaultProcessorConfig(): array - { - return []; - } - - private function determineProcessorName(string|int $key, array $processor): string - { - $nameNormalizer = new ProcessorNameNormalizer(); - - return $nameNormalizer->normalize($key, $processor); - } - - private function getProcessorConfig(array $processor): array - { - return $processor; - } -} diff --git a/src/Processor/ProcessorNameNormalizer.php b/src/Processor/ProcessorNameNormalizer.php deleted file mode 100644 index 2b1f2b5..0000000 --- a/src/Processor/ProcessorNameNormalizer.php +++ /dev/null @@ -1,25 +0,0 @@ -isNamedProcessor($key) ? (string) $key : $this->extractProcessorName($processor); - } - - private function isNamedProcessor(string|int $key): bool - { - return is_string($key); - } - - private function extractProcessorName(array $processor): string - { - $firstKey = array_key_first($processor); - - return is_string($firstKey) ? $firstKey : ''; - } -} diff --git a/src/Processor/ProcessorValidator.php b/src/Processor/ProcessorValidator.php deleted file mode 100644 index 461a0a2..0000000 --- a/src/Processor/ProcessorValidator.php +++ /dev/null @@ -1,26 +0,0 @@ -isValid()) { - $errorKey = $processor->getErrorKey(); - - return [ - 'errorKey' => $errorKey, - 'message' => $messages[$processorName] ?? "Validation failed for $processorName", - ]; - } - - return null; - } -} diff --git a/tests/AttributeHandlerTest.php b/tests/AttributeHandlerTest.php deleted file mode 100644 index 3c8cfdc..0000000 --- a/tests/AttributeHandlerTest.php +++ /dev/null @@ -1,236 +0,0 @@ -processorBuilder = $this->createMock(ProcessorBuilder::class); - $this->processorValidator = $this->createMock(ProcessorValidator::class); - $this->configBuilder = $this->createMock(ProcessorConfigBuilder::class); - $this->attributeHandler = new AttributeHandler( - 'testProcessor', - $this->processorBuilder, - $this->processorValidator, - $this->configBuilder - ); - } - - public function testHandleAttributeProcessesValue(): void - { - $mockAttribute = $this->createMock(ProcessableAttribute::class); - $mockPipeline = $this->createMock(Pipeline::class); - - $this->configBuilder->expects($this->once()) - ->method('build') - ->willReturn(['processor1' => []]); - - $mockPipeline->expects($this->once()) - ->method('process') - ->with('initialValue') - ->willReturn('processedValue'); - - $this->processorBuilder->expects($this->once()) - ->method('buildPipeline') - ->with($this->equalTo('testProcessor'), $this->equalTo(['processor1' => []])) - ->willReturn($mockPipeline); - - $this->processorValidator->expects($this->once()) - ->method('validate') - ->willReturn(null); - - $result = $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); - - $this->assertSame('processedValue', $result); - } - - public function testHandleAttributeWithValidationError(): void - { - $mockAttribute = $this->createMock(ProcessableAttribute::class); - $mockPipeline = $this->createMock(Pipeline::class); - - $this->configBuilder->expects($this->once()) - ->method('build') - ->willReturn(['processor1' => []]); - - $mockPipeline->expects($this->once()) - ->method('process') - ->willReturn('processedValue'); - - $this->processorBuilder->expects($this->once()) - ->method('buildPipeline') - ->willReturn($mockPipeline); - - $this->processorValidator->expects($this->once()) - ->method('validate') - ->willReturn(['errorKey' => 'testError', 'message' => 'Test error message']); - - $result = $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); - - $this->assertSame('processedValue', $result); - - $errors = $this->attributeHandler->getProcessingResultErrors(); - $this->assertArrayHasKey('testProperty', $errors); - $this->assertArrayHasKey('processor1', $errors['testProperty']); - $this->assertEquals('testError', $errors['testProperty']['processor1']['errorKey']); - $this->assertEquals('Test error message', $errors['testProperty']['processor1']['message']); - } - - public function testHandleAttributeReturnsNullWhenAttributeNotProcessable(): void - { - $nonProcessableAttribute = new \stdClass(); - $result = $this->attributeHandler->handleAttribute('testProperty', $nonProcessableAttribute, 'initialValue'); - $this->assertNull($result); - } - - public function testApplyChangesSetsProcessedValues(): void - { - $mockEntity = new class { - public string $testProperty = 'originalValue'; - }; - - $mockAttribute = $this->createMock(ProcessableAttribute::class); - $mockPipeline = $this->createMock(Pipeline::class); - - $this->configBuilder->expects($this->once()) - ->method('build') - ->willReturn(['processor1' => []]); - - $mockPipeline->expects($this->once()) - ->method('process') - ->with('initialValue') - ->willReturn('processedValue'); - - $this->processorBuilder->expects($this->once()) - ->method('buildPipeline') - ->willReturn($mockPipeline); - - $this->processorValidator->expects($this->once()) - ->method('validate') - ->willReturn(null); - - $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); - $this->attributeHandler->applyChanges($mockEntity); - - $this->assertSame('processedValue', $mockEntity->testProperty); - } - - public function testGetProcessedPropertyValuesReturnsProcessedData(): void - { - $mockAttribute = $this->createMock(ProcessableAttribute::class); - $mockPipeline = $this->createMock(Pipeline::class); - - $this->configBuilder->expects($this->once()) - ->method('build') - ->willReturn(['processor1' => []]); - - $mockPipeline->expects($this->once()) - ->method('process') - ->with('initialValue') - ->willReturn('processedValue'); - - $this->processorBuilder->expects($this->once()) - ->method('buildPipeline') - ->willReturn($mockPipeline); - - $this->processorValidator->expects($this->once()) - ->method('validate') - ->willReturn(null); - - $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); - $processedValues = $this->attributeHandler->getProcessedPropertyValues(); - - $this->assertArrayHasKey('testProperty', $processedValues); - $this->assertIsArray($processedValues['testProperty']); - $this->assertArrayHasKey('value', $processedValues['testProperty']); - $this->assertArrayHasKey('messages', $processedValues['testProperty']); - $this->assertSame('processedValue', $processedValues['testProperty']['value']); - $this->assertIsArray($processedValues['testProperty']['messages']); - } - - public function testHandleAttributeWithCustomizableMessageAttribute(): void - { - $mockAttribute = $this->createMock(CombinedAttribute::class); - $mockPipeline = $this->createMock(Pipeline::class); - - $this->configBuilder->expects($this->once()) - ->method('build') - ->willReturn(['processor1' => ['option' => 'value']]); - - $mockAttribute->expects($this->once()) - ->method('getMessage') - ->with('processor1') - ->willReturn('Custom message'); - - $mockPipeline->method('process')->willReturn('processedValue'); - - $this->processorBuilder->expects($this->once()) - ->method('buildPipeline') - ->willReturn($mockPipeline); - - $this->processorValidator->expects($this->once()) - ->method('validate') - ->willReturn(null); - - $result = $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); - $this->assertSame('processedValue', $result); - - $processedValues = $this->attributeHandler->getProcessedPropertyValues(); - $this->assertArrayHasKey('testProperty', $processedValues); - $this->assertArrayHasKey('messages', $processedValues['testProperty']); - $this->assertArrayHasKey('processor1', $processedValues['testProperty']['messages']); - $this->assertEquals('Custom message', $processedValues['testProperty']['messages']['processor1']); - } - - public function testHandleAttributeWithProcessingException(): void - { - $mockAttribute = $this->createMock(ProcessableAttribute::class); - $mockPipeline = $this->createMock(Pipeline::class); - - $this->configBuilder->expects($this->once()) - ->method('build') - ->willReturn(['processor1' => []]); - - $mockPipeline->expects($this->once()) - ->method('process') - ->willThrowException( - ProcessorRuntimeException::contextNotFound('payment') - ); - - $this->processorBuilder->expects($this->once()) - ->method('buildPipeline') - ->willReturn($mockPipeline); - - $result = $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue'); - $this->assertSame('initialValue', $result); - - $errors = $this->attributeHandler->getProcessingResultErrors(); - - $this->assertStringContainsString("Processor context 'payment' not found", $errors['testProperty'][0]); - } -} diff --git a/tests/Processor/ProcessorConfigBuilderTest.php b/tests/Processor/ProcessorConfigBuilderTest.php deleted file mode 100644 index 81bcbce..0000000 --- a/tests/Processor/ProcessorConfigBuilderTest.php +++ /dev/null @@ -1,63 +0,0 @@ -configBuilder = new ProcessorConfigBuilder(); - } - - public function testBuildWithSimpleProcessors(): void - { - $attribute = $this->createMock(ProcessableAttribute::class); - $attribute->method('getProcessors')->willReturn(['processor1', 'processor2']); - - $result = $this->configBuilder->build($attribute); - - $this->assertEquals(['processor1' => [], 'processor2' => []], $result); - } - - public function testBuildWithConfigurableProcessors(): void - { - $attribute = $this->createMock(ProcessableAttribute::class); - $attribute->method('getProcessors')->willReturn([ - 'processor1' => ['option' => 'value'], - 'processor2' => ['another_option' => 'another_value'], - ]); - - $result = $this->configBuilder->build($attribute); - - $this->assertEquals([ - 'processor1' => ['option' => 'value'], - 'processor2' => ['another_option' => 'another_value'], - ], $result); - } - - public function testBuildWithMixedProcessors(): void - { - $attribute = $this->createMock(ProcessableAttribute::class); - $attribute->method('getProcessors')->willReturn([ - 'processor1', - 'processor2' => ['option' => 'value'], - ['unnamed_processor' => []], - ]); - - $result = $this->configBuilder->build($attribute); - - $this->assertEquals([ - 'processor1' => [], - 'processor2' => ['option' => 'value'], - 'unnamed_processor' => ['unnamed_processor' => []], - ], $result); - } -} diff --git a/tests/Processor/ProcessorNameNormalizerTest.php b/tests/Processor/ProcessorNameNormalizerTest.php deleted file mode 100644 index 9937ab8..0000000 --- a/tests/Processor/ProcessorNameNormalizerTest.php +++ /dev/null @@ -1,39 +0,0 @@ -normalizer = new ProcessorNameNormalizer(); - } - - public function testNormalizeWithStringKey(): void - { - $result = $this->normalizer->normalize('processor_name', []); - - $this->assertEquals('processor_name', $result); - } - - public function testNormalizeWithIntegerKey(): void - { - $result = $this->normalizer->normalize(0, ['processor_name' => []]); - - $this->assertEquals('processor_name', $result); - } - - public function testNormalizeWithEmptyProcessor(): void - { - $result = $this->normalizer->normalize(0, []); - - $this->assertEquals('', $result); - } -} diff --git a/tests/Processor/ProcessorValidatorTest.php b/tests/Processor/ProcessorValidatorTest.php deleted file mode 100644 index a07bd4c..0000000 --- a/tests/Processor/ProcessorValidatorTest.php +++ /dev/null @@ -1,71 +0,0 @@ -processorValidator = new ProcessorValidator(); - } - - public function testValidateWithNonValidatableProcessor(): void - { - $processor = $this->createMock(Processor::class); - - $result = $this->processorValidator->validate($processor, 'testProcessor', []); - - $this->assertNull($result); - } - - public function testValidateWithValidValidatableProcessor(): void - { - $processor = $this->createMock(ValidatableProcessor::class); - $processor->method('isValid')->willReturn(true); - - $result = $this->processorValidator->validate($processor, 'testProcessor', []); - - $this->assertNull($result); - } - - public function testValidateWithInvalidValidatableProcessor(): void - { - $processor = $this->createMock(ValidatableProcessor::class); - $processor->method('isValid')->willReturn(false); - $processor->method('getErrorKey')->willReturn('testError'); - - $result = $this->processorValidator->validate($processor, 'testProcessor', []); - - $this->assertIsArray($result); - $this->assertArrayHasKey('errorKey', $result); - $this->assertArrayHasKey('message', $result); - $this->assertEquals('testError', $result['errorKey']); - $this->assertEquals('Validation failed for testProcessor', $result['message']); - } - - public function testValidateWithInvalidValidatableProcessorAndCustomMessage(): void - { - $processor = $this->createMock(ValidatableProcessor::class); - $processor->method('isValid')->willReturn(false); - $processor->method('getErrorKey')->willReturn('testError'); - - $messages = ['testProcessor' => 'Custom error message']; - - $result = $this->processorValidator->validate($processor, 'testProcessor', $messages); - - $this->assertIsArray($result); - $this->assertArrayHasKey('errorKey', $result); - $this->assertArrayHasKey('message', $result); - $this->assertEquals('testError', $result['errorKey']); - $this->assertEquals('Custom error message', $result['message']); - } -} diff --git a/tests/benchmark_attribute_analyzerphp b/tests/benchmark_attribute_analyzer.php similarity index 89% rename from tests/benchmark_attribute_analyzerphp rename to tests/benchmark_attribute_analyzer.php index 39020de..52aad91 100644 --- a/tests/benchmark_attribute_analyzerphp +++ b/tests/benchmark_attribute_analyzer.php @@ -7,8 +7,8 @@ require_once __DIR__ . '/../vendor/autoload.php'; use KaririCode\PropertyInspector\AttributeAnalyzer; -use KaririCode\PropertyInspector\Exception\PropertyInspectionException; use KaririCode\PropertyInspector\Contract\AttributeAnalyzer as AttributeAnalyzerContract; +use KaririCode\PropertyInspector\Exception\PropertyInspectionException; #[\Attribute] class TestAttribute @@ -20,14 +20,14 @@ public function __construct(public string $value = '') class TestClass { - #[TestAttribute("test1")] - private string $prop1 = "value1"; + #[TestAttribute('test1')] + private string $prop1 = 'value1'; - #[TestAttribute("test2")] + #[TestAttribute('test2')] private int $prop2 = 42; - #[TestAttribute("test3")] - private array $prop3 = ["test"]; + #[TestAttribute('test3')] + private array $prop3 = ['test']; } class AttributeAnalyzerBenchmark @@ -63,8 +63,8 @@ public function runBenchmark(): void private function calculateDifference(float $original, float $optimized): float { - if ($original === 0.0) { - return $optimized === 0.0 ? 0.0 : 100.0; + if (0.0 === $original) { + return 0.0 === $optimized ? 0.0 : 100.0; } return (($original - $optimized) / $original) * 100; @@ -98,7 +98,7 @@ private function benchmarkMemoryUsage(): array 'Original' => number_format($originalMemory / 1024, 2), 'Optimized' => number_format($optimizedMemory / 1024, 2), 'Difference' => number_format(abs($difference), 2), - 'Improvement' => $difference > 0 ? 'Yes' : 'No' + 'Improvement' => $difference > 0 ? 'Yes' : 'No', ]; } @@ -130,7 +130,7 @@ private function benchmarkProcessingTime(): array 'Original' => number_format($originalTime * 1000, 4), 'Optimized' => number_format($optimizedTime * 1000, 4), 'Difference' => number_format(abs($difference), 2), - 'Improvement' => $difference > 0 ? 'Yes' : 'No' + 'Improvement' => $difference > 0 ? 'Yes' : 'No', ]; } @@ -139,21 +139,21 @@ private function benchmarkPropertyAccess(): array $object = new TestClass(); // Aquecimento - for ($i = 0; $i < 1000; $i++) { + for ($i = 0; $i < 1000; ++$i) { $this->originalAnalyzer->analyzeObject($object); $this->optimizedAnalyzer->analyzeObject($object); } // Original Version $start = microtime(true); - for ($i = 0; $i < self::ITERATIONS; $i++) { + for ($i = 0; $i < self::ITERATIONS; ++$i) { $this->originalAnalyzer->analyzeObject($object); } $originalTime = microtime(true) - $start; // Optimized Version $start = microtime(true); - for ($i = 0; $i < self::ITERATIONS; $i++) { + for ($i = 0; $i < self::ITERATIONS; ++$i) { $this->optimizedAnalyzer->analyzeObject($object); } $optimizedTime = microtime(true) - $start; @@ -164,7 +164,7 @@ private function benchmarkPropertyAccess(): array 'Original' => number_format($originalTime * 1000, 4), 'Optimized' => number_format($optimizedTime * 1000, 4), 'Difference' => number_format(abs($difference), 2), - 'Improvement' => $difference > 0 ? 'Yes' : 'No' + 'Improvement' => $difference > 0 ? 'Yes' : 'No', ]; } @@ -178,11 +178,11 @@ private function printResults(array $results): void echo str_repeat('-', 30) . "\n"; foreach ($data as $metric => $value) { - if ($metric === 'Improvement') { + if ('Improvement' === $metric) { continue; } - $unit = $metric === 'Difference' ? '%' : ($testName === 'Memory Usage' ? ' KB' : ' ms'); + $unit = 'Difference' === $metric ? '%' : ('Memory Usage' === $testName ? ' KB' : ' ms'); $color = $this->getMetricColor($metric, $data); echo sprintf( @@ -197,8 +197,8 @@ private function printResults(array $results): void echo sprintf( "\n%s%s%s\n\n", - $data['Improvement'] === 'Yes' ? self::ANSI_GREEN : self::ANSI_RED, - "Improvement: " . ($data['Improvement'] === 'Yes' ? 'Yes' : 'No'), + 'Yes' === $data['Improvement'] ? self::ANSI_GREEN : self::ANSI_RED, + 'Improvement: ' . ('Yes' === $data['Improvement'] ? 'Yes' : 'No'), self::ANSI_RESET ); } @@ -208,19 +208,18 @@ private function printResults(array $results): void private function getMetricColor(string $metric, array $data): string { - if ($metric === 'Difference') { + if ('Difference' === $metric) { return self::ANSI_YELLOW; } - if ($data['Improvement'] === 'Yes') { - return $metric === 'Optimized' ? self::ANSI_GREEN : self::ANSI_RED; + if ('Yes' === $data['Improvement']) { + return 'Optimized' === $metric ? self::ANSI_GREEN : self::ANSI_RED; } - return $metric === 'Original' ? self::ANSI_GREEN : self::ANSI_RED; + return 'Original' === $metric ? self::ANSI_GREEN : self::ANSI_RED; } } - final class OptimizedAttributeAnalyzer implements AttributeAnalyzerContract { private array $cache = []; @@ -265,7 +264,7 @@ private function cacheObjectMetadata(object $object): void $cachedProperties[$property->getName()] = [ 'attributes' => $attributeInstances, - 'property' => $property + 'property' => $property, ]; } } @@ -281,7 +280,7 @@ private function extractValues(object $object): array foreach ($this->cache[$className] as $propertyName => $data) { $results[$propertyName] = [ 'value' => $data['property']->getValue($object), - 'attributes' => $data['attributes'] + 'attributes' => $data['attributes'], ]; } @@ -344,8 +343,6 @@ public function clearCache(): void // } // } - - // Executar o benchmark $benchmark = new AttributeAnalyzerBenchmark(); $benchmark->runBenchmark(); diff --git a/tests/benchmark_attribute_handler.php b/tests/benchmark_attribute_handler.php deleted file mode 100644 index d819142..0000000 --- a/tests/benchmark_attribute_handler.php +++ /dev/null @@ -1,536 +0,0 @@ -builder = new MockProcessorBuilder(); - } - - public function run(): void - { - echo self::ANSI_BOLD . "\nATTRIBUTE HANDLER PERFORMANCE BENCHMARK\n" . self::ANSI_RESET; - echo str_repeat('=', 60) . "\n"; - echo self::ANSI_BLUE . 'Running benchmark with ' . self::ITERATIONS . ' iterations...' . self::ANSI_RESET . "\n\n"; - - // Warm up phase - $this->warmUp(); - - // Test original handler - $originalStats = $this->benchmarkOriginalHandler(); - - // Test optimized handler - $optimizedStats = $this->benchmarkOptimizedHandler(); - - // Display results - $this->displayResults($originalStats, $optimizedStats); - } - - private function warmUp(): void - { - echo self::ANSI_YELLOW . 'Warming up JIT compiler...' . self::ANSI_RESET . "\n"; - - for ($i = 0; $i < 1000; ++$i) { - $handler = new AttributeHandler('validator', $this->builder); - $this->runTestCase($handler); - - $handler = new AttributeHandlerOtimized('validator', $this->builder); - $this->runTestCase($handler); - } - - // Clear any accumulated memory - gc_collect_cycles(); - echo self::ANSI_GREEN . "Warm-up complete!\n\n" . self::ANSI_RESET; - } - - private function benchmarkOriginalHandler(): array - { - // Reset memory state - gc_collect_cycles(); - $startMemory = memory_get_usage(true); - - $handler = new AttributeHandler('validator', $this->builder); - $start = hrtime(true); - - for ($i = 0; $i < self::ITERATIONS; ++$i) { - $this->runTestCase($handler); - } - - $time = (hrtime(true) - $start) / 1e+9; - $memoryUsed = memory_get_usage(true) - $startMemory; - - return [ - 'time' => $time, - 'memory' => $memoryUsed, - 'peak' => memory_get_peak_usage(true), - ]; - } - - private function benchmarkOptimizedHandler(): array - { - // Reset memory state - gc_collect_cycles(); - $startMemory = memory_get_usage(true); - - $handler = new AttributeHandlerOtimized('validator', $this->builder); - $start = hrtime(true); - - for ($i = 0; $i < self::ITERATIONS; ++$i) { - $this->runTestCase($handler); - } - - $time = (hrtime(true) - $start) / 1e+9; - $memoryUsed = memory_get_usage(true) - $startMemory; - - return [ - 'time' => $time, - 'memory' => $memoryUsed, - 'peak' => memory_get_peak_usage(true), - ]; - } - - private function runTestCase($handler): void - { - $attribute = new class implements ProcessableAttribute { - public function getProcessors(): array - { - return [ - 'required', - 'email' => ['pattern' => '/.+@.+/'], - 'length' => ['min' => 5, 'max' => 50], - 'trim' => true, - 'lowercase' => true, - ]; - } - }; - - $testCases = [ - ['email', 'test@example.com'], - ['name', 'John Doe'], - ['age', 25], - ['description', str_repeat('a', 100)], - ['date', new \DateTime()], - ['empty', null], - ['whitespace', ' trimmed '], - ['special', '!@#$%^&*()'], - ['unicode', 'αβγδε'], - ['number_string', '12345'], - ]; - - foreach ($testCases as [$property, $value]) { - $handler->handleAttribute($property, $attribute, $value); - } - - $handler->getProcessingResultMessages(); - $handler->getProcessedPropertyValues(); - $handler->getProcessingResultErrors(); - } - - private function displayResults(array $originalStats, array $optimizedStats): void - { - echo self::ANSI_BOLD . "Performance Results\n" . self::ANSI_RESET; - echo str_repeat('=', 60) . "\n"; - - // Time Performance - $timeDiff = $this->calculatePercentageDiff($originalStats['time'], $optimizedStats['time']); - $timeColor = $timeDiff > 0 ? self::ANSI_GREEN : self::ANSI_RED; - - echo self::ANSI_BOLD . "Execution Time\n" . self::ANSI_RESET; - echo str_repeat('-', 40) . "\n"; - echo sprintf("%sOriginal Handler: %.6f seconds%s\n", self::ANSI_YELLOW, $originalStats['time'], self::ANSI_RESET); - echo sprintf("%sOptimized Handler: %.6f seconds%s\n", self::ANSI_YELLOW, $optimizedStats['time'], self::ANSI_RESET); - echo sprintf( - "%sTime Difference: %.2f%% %s%s\n\n", - $timeColor, - abs($timeDiff), - $timeDiff > 0 ? 'faster' : 'slower', - self::ANSI_RESET - ); - - // Memory Usage - echo self::ANSI_BOLD . "Memory Usage\n" . self::ANSI_RESET; - echo str_repeat('-', 40) . "\n"; - - $originalMemoryMB = $originalStats['memory'] / 1024 / 1024; - $optimizedMemoryMB = $optimizedStats['memory'] / 1024 / 1024; - $memoryDiff = $this->calculatePercentageDiff($originalStats['memory'], $optimizedStats['memory']); - $memoryColor = $memoryDiff > 0 ? self::ANSI_GREEN : self::ANSI_RED; - - echo sprintf("%sOriginal Handler: %.2f MB%s\n", self::ANSI_YELLOW, $originalMemoryMB, self::ANSI_RESET); - echo sprintf("%sOptimized Handler: %.2f MB%s\n", self::ANSI_YELLOW, $optimizedMemoryMB, self::ANSI_RESET); - echo sprintf( - "%sMemory Difference: %.2f%% %s%s\n\n", - $memoryColor, - abs($memoryDiff), - $memoryDiff > 0 ? 'less' : 'more', - self::ANSI_RESET - ); - - // Peak Memory - echo self::ANSI_BOLD . "Peak Memory Usage\n" . self::ANSI_RESET; - echo str_repeat('-', 40) . "\n"; - - $originalPeakMB = $originalStats['peak'] / 1024 / 1024; - $optimizedPeakMB = $optimizedStats['peak'] / 1024 / 1024; - $peakDiff = $this->calculatePercentageDiff($originalStats['peak'], $optimizedStats['peak']); - $peakColor = $peakDiff > 0 ? self::ANSI_GREEN : self::ANSI_RED; - - echo sprintf("%sOriginal Peak: %.2f MB%s\n", self::ANSI_YELLOW, $originalPeakMB, self::ANSI_RESET); - echo sprintf("%sOptimized Peak: %.2f MB%s\n", self::ANSI_YELLOW, $optimizedPeakMB, self::ANSI_RESET); - echo sprintf( - "%sPeak Difference: %.2f%% %s%s\n\n", - $peakColor, - abs($peakDiff), - $peakDiff > 0 ? 'less' : 'more', - self::ANSI_RESET - ); - - // Per Iteration Stats - echo self::ANSI_BOLD . "Per Iteration Stats\n" . self::ANSI_RESET; - echo str_repeat('-', 40) . "\n"; - - $originalTimePerIteration = ($originalStats['time'] * 1000) / self::ITERATIONS; - $optimizedTimePerIteration = ($optimizedStats['time'] * 1000) / self::ITERATIONS; - - echo sprintf( - "%sOriginal Time per Iteration: %.6f ms%s\n", - self::ANSI_YELLOW, - $originalTimePerIteration, - self::ANSI_RESET - ); - echo sprintf( - "%sOptimized Time per Iteration: %.6f ms%s\n", - self::ANSI_YELLOW, - $optimizedTimePerIteration, - self::ANSI_RESET - ); - - echo "\n" . str_repeat('=', 60) . "\n"; - } - - private function calculatePercentageDiff(float $original, float $optimized): float - { - if ($original <= 0) { - return 0; - } - - return (($original - $optimized) / $original) * 100; - } -} - -final class AttributeHandlerOtimized implements PropertyAttributeHandler, PropertyChangeApplier -{ - private array $processedPropertyValues = []; - private array $processingResultErrors = []; - private array $processingResultMessages = []; - private array $processorCache = []; - - public function __construct( - private readonly string $processorType, - private readonly ProcessorBuilder $builder, - private readonly ProcessorProcessorContract $validator = new ProcessorValidator(), - private readonly ProcessorConfigBuilderContract $configBuilder = new ProcessorConfigBuilder() - ) { - } - - public function handleAttribute(string $propertyName, object $attribute, mixed $value): mixed - { - if (!$attribute instanceof ProcessableAttribute) { - return null; - } - - try { - return $this->processAttribute($propertyName, $attribute, $value); - } catch (\Exception $e) { - $this->processingResultErrors[$propertyName][] = $e->getMessage(); - - return $value; - } - } - - private function processAttribute(string $propertyName, ProcessableAttribute $attribute, mixed $value): mixed - { - $config = $this->configBuilder->build($attribute); - $messages = []; - - if ($attribute instanceof CustomizableMessageAttribute) { - foreach ($config as $processorName => &$processorConfig) { - if ($message = $attribute->getMessage($processorName)) { - $processorConfig['customMessage'] = $message; - $messages[$processorName] = $message; - } - } - } - - $processedValue = $this->processValue($value, $config); - - if ($errors = $this->validateProcessors($config, $messages)) { - $this->processingResultErrors[$propertyName] = $errors; - } - - $this->processedPropertyValues[$propertyName] = [ - 'value' => $processedValue, - 'messages' => $messages, - ]; - - $this->processingResultMessages[$propertyName] = $messages; - - return $processedValue; - } - - private function validateProcessors(array $processorsConfig, array $messages): array - { - $errors = []; - foreach ($processorsConfig as $processorName => $config) { - // Simplify cache key to processor name - if (!isset($this->processorCache[$processorName])) { - $this->processorCache[$processorName] = $this->builder->build( - $this->processorType, - $processorName, - $config - ); - } - - $processor = $this->processorCache[$processorName]; - - if ($error = $this->validator->validate($processor, $processorName, $messages)) { - $errors[$processorName] = $error; - } - } - - return $errors; - } - - private function processValue(mixed $value, array $config): mixed - { - return $this->builder - ->buildPipeline($this->processorType, $config) - ->process($value); - } - - public function applyChanges(object $entity): void - { - foreach ($this->processedPropertyValues as $propertyName => $data) { - (new PropertyAccessor($entity, $propertyName))->setValue($data['value']); - } - } - - public function getProcessedPropertyValues(): array - { - return $this->processedPropertyValues; - } - - public function getProcessingResultErrors(): array - { - return $this->processingResultErrors; - } - - public function getProcessingResultMessages(): array - { - return $this->processingResultMessages; - } -} - -// Before -// class AttributeHandler implements PropertyAttributeHandler, PropertyChangeApplier -// { -// private array $processedPropertyValues = []; -// private array $processingResultErrors = []; -// private array $processingResultMessages = []; - -// public function __construct( -// private readonly string $processorType, -// private readonly ProcessorBuilder $builder, -// private readonly ProcessorProcessorContract $validator = new ProcessorValidator(), -// private readonly ProcessorConfigBuilderContract $configBuilder = new ProcessorConfigBuilder() -// ) { -// } - -// public function handleAttribute(string $propertyName, object $attribute, mixed $value): mixed -// { -// if (!$attribute instanceof ProcessableAttribute) { -// return null; -// } - -// $processorsConfig = $this->configBuilder->build($attribute); -// $messages = $this->extractCustomMessages($attribute, $processorsConfig); - -// try { -// $processedValue = $this->processValue($value, $processorsConfig); -// $errors = $this->validateProcessors($processorsConfig, $messages); - -// $this->storeProcessedPropertyValue($propertyName, $processedValue, $messages); - -// if (!empty($errors)) { -// $this->storeProcessingResultErrors($propertyName, $errors); -// } - -// return $processedValue; -// } catch (\Exception $e) { -// $this->storeProcessingResultError($propertyName, $e->getMessage()); - -// return $value; -// } -// } - -// private function validateProcessors(array $processorsConfig, array $messages): array -// { -// $errors = []; -// foreach ($processorsConfig as $processorName => $config) { -// $processor = $this->builder->build($this->processorType, $processorName, $config); -// $validationError = $this->validator->validate( -// $processor, -// $processorName, -// $messages -// ); - -// if ($this->shouldAddValidationError($validationError, $errors, $processorName)) { -// $errors[$processorName] = $validationError; -// } -// } - -// return $errors; -// } - -// private function shouldAddValidationError(?array $validationError, array $errors, string $processorName): bool -// { -// return null !== $validationError && !isset($errors[$processorName]); -// } - -// private function storeProcessingResultErrors(string $propertyName, array $errors): void -// { -// $this->processingResultErrors[$propertyName] = $errors; -// } - -// private function extractCustomMessages(ProcessableAttribute $attribute, array &$processorsConfig): array -// { -// $messages = []; -// if ($attribute instanceof CustomizableMessageAttribute) { -// foreach ($processorsConfig as $processorName => &$config) { -// $customMessage = $attribute->getMessage($processorName); -// if (null !== $customMessage) { -// $config['customMessage'] = $customMessage; -// $messages[$processorName] = $customMessage; -// } -// } -// } - -// return $messages; -// } - -// private function processValue(mixed $value, array $processorsConfig): mixed -// { -// $pipeline = $this->builder->buildPipeline( -// $this->processorType, -// $processorsConfig -// ); - -// return $pipeline->process($value); -// } - -// private function storeProcessedPropertyValue(string $propertyName, mixed $processedValue, array $messages): void -// { -// $this->processedPropertyValues[$propertyName] = [ -// 'value' => $processedValue, -// 'messages' => $messages, -// ]; -// $this->processingResultMessages[$propertyName] = $messages; -// } - -// private function storeProcessingResultError(string $propertyName, string $errorMessage): void -// { -// $this->processingResultErrors[$propertyName][] = $errorMessage; -// } - -// public function applyChanges(object $entity): void -// { -// foreach ($this->processedPropertyValues as $propertyName => $data) { -// (new PropertyAccessor($entity, $propertyName))->setValue($data['value']); -// } -// } - -// public function getProcessedPropertyValues(): array -// { -// return $this->processedPropertyValues; -// } - -// public function getProcessingResultErrors(): array -// { -// return $this->processingResultErrors; -// } - -// public function getProcessingResultMessages(): array -// { -// return $this->processingResultMessages; -// } -// } - -$benchmark = new BenchmarkRunner(); -$benchmark->run();