Skip to content

Commit 0d97db8

Browse files
authored
Merge pull request #9 from KaririCode-Framework/develop
feat(Sanitizer): enhance sanitization with ProcessorConfigBuilder and…
2 parents 26f4af4 + 9d80852 commit 0d97db8

File tree

6 files changed

+247
-26
lines changed

6 files changed

+247
-26
lines changed

src/AttributeHandler.php

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,21 @@
77
use KaririCode\Contract\Processor\Attribute\CustomizableMessageAttribute;
88
use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;
99
use KaririCode\Contract\Processor\ProcessorBuilder;
10-
use KaririCode\ProcessorPipeline\Exception\ProcessingException;
1110
use KaririCode\PropertyInspector\Contract\PropertyAttributeHandler;
1211
use KaririCode\PropertyInspector\Contract\PropertyChangeApplier;
12+
use KaririCode\PropertyInspector\Processor\ProcessorConfigBuilder;
1313
use KaririCode\PropertyInspector\Utility\PropertyAccessor;
1414

1515
class AttributeHandler implements PropertyAttributeHandler, PropertyChangeApplier
1616
{
1717
private array $processedValues = [];
1818
private array $processingErrors = [];
19+
private array $processingMessages = [];
1920

2021
public function __construct(
2122
private readonly string $processorType,
2223
private readonly ProcessorBuilder $builder,
24+
private readonly ProcessorConfigBuilder $configBuilder = new ProcessorConfigBuilder()
2325
) {
2426
}
2527

@@ -29,38 +31,65 @@ public function handleAttribute(string $propertyName, object $attribute, mixed $
2931
return null;
3032
}
3133

32-
$processors = $attribute->getProcessors();
34+
$processorsConfig = $this->configBuilder->build($attribute);
35+
$messages = $this->extractCustomMessages($attribute, $processorsConfig);
36+
37+
try {
38+
$processedValue = $this->processValue($value, $processorsConfig);
39+
$this->storeProcessedValue($propertyName, $processedValue, $messages);
40+
41+
return $processedValue; // Return the processed value, not the original
42+
} catch (\Exception $e) {
43+
$this->storeProcessingError($propertyName, $e->getMessage());
44+
45+
return $value;
46+
}
47+
}
48+
49+
private function extractCustomMessages(ProcessableAttribute $attribute, array &$processorsConfig): array
50+
{
51+
$messages = [];
3352

3453
if ($attribute instanceof CustomizableMessageAttribute) {
35-
foreach ($processors as $processorName => &$config) {
54+
foreach ($processorsConfig as $processorName => &$config) {
3655
$customMessage = $attribute->getMessage($processorName);
3756
if (null !== $customMessage) {
3857
$config['customMessage'] = $customMessage;
58+
$messages[$processorName] = $customMessage;
3959
}
4060
}
4161
}
4262

43-
$pipeline = $this->builder->buildPipeline($this->processorType, $processors);
63+
return $messages;
64+
}
4465

45-
try {
46-
$processedValue = $pipeline->process($value);
47-
$this->processedValues[$propertyName] = $processedValue;
66+
private function processValue(mixed $value, array $processorsConfig): mixed
67+
{
68+
$pipeline = $this->builder->buildPipeline($this->processorType, $processorsConfig);
4869

49-
return $processedValue;
50-
} catch (ProcessingException $e) {
51-
$this->processingErrors[$propertyName][] = $e->getMessage();
70+
return $pipeline->process($value);
71+
}
5272

53-
return $value; // Return original value in case of processing error
54-
}
73+
private function storeProcessedValue(string $propertyName, mixed $processedValue, array $messages): void
74+
{
75+
$this->processedValues[$propertyName] = [
76+
'value' => $processedValue,
77+
'messages' => $messages,
78+
];
79+
$this->processingMessages[$propertyName] = $messages;
80+
}
81+
82+
private function storeProcessingError(string $propertyName, string $errorMessage): void
83+
{
84+
$this->processingErrors[$propertyName][] = $errorMessage;
5585
}
5686

5787
public function applyChanges(object $entity): void
5888
{
59-
foreach ($this->processedValues as $propertyName => $value) {
89+
foreach ($this->processedValues as $propertyName => $data) {
6090
$accessor = new PropertyAccessor($entity, $propertyName);
61-
$accessor->setValue($value);
91+
$accessor->setValue($data['value']);
6292
}
63-
$this->processedValues = []; // Clear the processed values after applying
6493
}
6594

6695
public function getProcessedValues(): array
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\PropertyInspector\Processor;
6+
7+
use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;
8+
9+
readonly class ProcessorConfigBuilder
10+
{
11+
public function build(ProcessableAttribute $attribute): array
12+
{
13+
$processors = $attribute->getProcessors();
14+
$processorsConfig = [];
15+
16+
foreach ($processors as $key => $processor) {
17+
if ($this->isSimpleProcessor($processor)) {
18+
$processorsConfig[$processor] = $this->getDefaultProcessorConfig();
19+
} elseif ($this->isConfigurableProcessor($processor)) {
20+
$processorName = $this->determineProcessorName($key, $processor);
21+
$processorsConfig[$processorName] = $this->getProcessorConfig($processor);
22+
}
23+
}
24+
25+
return $processorsConfig;
26+
}
27+
28+
private function isSimpleProcessor(mixed $processor): bool
29+
{
30+
return is_string($processor);
31+
}
32+
33+
private function isConfigurableProcessor(mixed $processor): bool
34+
{
35+
return is_array($processor);
36+
}
37+
38+
private function getDefaultProcessorConfig(): array
39+
{
40+
return [];
41+
}
42+
43+
private function determineProcessorName(string|int $key, array $processor): string
44+
{
45+
$nameNormalizer = new ProcessorNameNormalizer();
46+
47+
return $nameNormalizer->normalize($key, $processor);
48+
}
49+
50+
private function getProcessorConfig(array $processor): array
51+
{
52+
return $processor;
53+
}
54+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\PropertyInspector\Processor;
6+
7+
final readonly class ProcessorNameNormalizer
8+
{
9+
public function normalize(string|int $key, array $processor): string
10+
{
11+
return $this->isNamedProcessor($key) ? (string) $key : $this->extractProcessorName($processor);
12+
}
13+
14+
private function isNamedProcessor(string|int $key): bool
15+
{
16+
return is_string($key);
17+
}
18+
19+
private function extractProcessorName(array $processor): string
20+
{
21+
$firstKey = array_key_first($processor);
22+
23+
return is_string($firstKey) ? $firstKey : '';
24+
}
25+
}

tests/AttributeHandlerTest.php

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use KaririCode\Contract\Processor\ProcessorBuilder;
1111
use KaririCode\ProcessorPipeline\Exception\ProcessingException;
1212
use KaririCode\PropertyInspector\AttributeHandler;
13+
use KaririCode\PropertyInspector\Processor\ProcessorConfigBuilder;
1314
use PHPUnit\Framework\MockObject\MockObject;
1415
use PHPUnit\Framework\TestCase;
1516

@@ -34,6 +35,11 @@ public function testHandleAttributeProcessesValue(): void
3435
{
3536
$mockAttribute = $this->createMock(ProcessableAttribute::class);
3637
$mockPipeline = $this->createMock(Pipeline::class);
38+
$mockConfigBuilder = $this->createMock(ProcessorConfigBuilder::class);
39+
40+
$mockConfigBuilder->expects($this->once())
41+
->method('build')
42+
->willReturn(['processor1' => []]);
3743

3844
$mockPipeline->expects($this->once())
3945
->method('process')
@@ -42,14 +48,13 @@ public function testHandleAttributeProcessesValue(): void
4248

4349
$this->processorBuilder->expects($this->once())
4450
->method('buildPipeline')
45-
->with('testProcessor', ['processor1'])
51+
->with($this->equalTo('testProcessor'), $this->equalTo(['processor1' => []]))
4652
->willReturn($mockPipeline);
4753

48-
$mockAttribute->expects($this->once())
49-
->method('getProcessors')
50-
->willReturn(['processor1']);
54+
$attributeHandler = new AttributeHandler('testProcessor', $this->processorBuilder, $mockConfigBuilder);
55+
56+
$result = $attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue');
5157

52-
$result = $this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue');
5358
$this->assertSame('processedValue', $result);
5459
}
5560

@@ -117,6 +122,7 @@ public function testGetProcessedValuesReturnsProcessedData(): void
117122
{
118123
$mockAttribute = $this->createMock(ProcessableAttribute::class);
119124
$mockPipeline = $this->createMock(Pipeline::class);
125+
$mockConfigBuilder = $this->createMock(ProcessorConfigBuilder::class);
120126

121127
$mockPipeline->expects($this->once())
122128
->method('process')
@@ -127,15 +133,20 @@ public function testGetProcessedValuesReturnsProcessedData(): void
127133
->method('buildPipeline')
128134
->willReturn($mockPipeline);
129135

130-
$mockAttribute->expects($this->once())
131-
->method('getProcessors')
132-
->willReturn(['processor1']);
136+
$mockConfigBuilder->expects($this->once())
137+
->method('build')
138+
->willReturn(['processor1' => []]);
133139

134-
$this->attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue');
135-
$processedValues = $this->attributeHandler->getProcessedValues();
140+
$attributeHandler = new AttributeHandler('testProcessor', $this->processorBuilder, $mockConfigBuilder);
141+
$attributeHandler->handleAttribute('testProperty', $mockAttribute, 'initialValue');
142+
$processedValues = $attributeHandler->getProcessedValues();
136143

137144
$this->assertArrayHasKey('testProperty', $processedValues);
138-
$this->assertSame('processedValue', $processedValues['testProperty']);
145+
$this->assertIsArray($processedValues['testProperty']);
146+
$this->assertArrayHasKey('value', $processedValues['testProperty']);
147+
$this->assertArrayHasKey('messages', $processedValues['testProperty']);
148+
$this->assertSame('processedValue', $processedValues['testProperty']['value']);
149+
$this->assertIsArray($processedValues['testProperty']['messages']);
139150
}
140151

141152
public function testHandleAttributeWithCustomizableMessageAttribute(): void
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\PropertyInspector\Tests\Processor;
6+
7+
use KaririCode\Contract\Processor\Attribute\ProcessableAttribute;
8+
use KaririCode\PropertyInspector\Processor\ProcessorConfigBuilder;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class ProcessorConfigBuilderTest extends TestCase
12+
{
13+
private ProcessorConfigBuilder $configBuilder;
14+
15+
protected function setUp(): void
16+
{
17+
$this->configBuilder = new ProcessorConfigBuilder();
18+
}
19+
20+
public function testBuildWithSimpleProcessors(): void
21+
{
22+
$attribute = $this->createMock(ProcessableAttribute::class);
23+
$attribute->method('getProcessors')->willReturn(['processor1', 'processor2']);
24+
25+
$result = $this->configBuilder->build($attribute);
26+
27+
$this->assertEquals(['processor1' => [], 'processor2' => []], $result);
28+
}
29+
30+
public function testBuildWithConfigurableProcessors(): void
31+
{
32+
$attribute = $this->createMock(ProcessableAttribute::class);
33+
$attribute->method('getProcessors')->willReturn([
34+
'processor1' => ['option' => 'value'],
35+
'processor2' => ['another_option' => 'another_value'],
36+
]);
37+
38+
$result = $this->configBuilder->build($attribute);
39+
40+
$this->assertEquals([
41+
'processor1' => ['option' => 'value'],
42+
'processor2' => ['another_option' => 'another_value'],
43+
], $result);
44+
}
45+
46+
public function testBuildWithMixedProcessors(): void
47+
{
48+
$attribute = $this->createMock(ProcessableAttribute::class);
49+
$attribute->method('getProcessors')->willReturn([
50+
'processor1',
51+
'processor2' => ['option' => 'value'],
52+
['unnamed_processor' => []],
53+
]);
54+
55+
$result = $this->configBuilder->build($attribute);
56+
57+
$this->assertEquals([
58+
'processor1' => [],
59+
'processor2' => ['option' => 'value'],
60+
'unnamed_processor' => ['unnamed_processor' => []],
61+
], $result);
62+
}
63+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\PropertyInspector\Tests\Processor;
6+
7+
use KaririCode\PropertyInspector\Processor\ProcessorNameNormalizer;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class ProcessorNameNormalizerTest extends TestCase
11+
{
12+
private ProcessorNameNormalizer $normalizer;
13+
14+
protected function setUp(): void
15+
{
16+
$this->normalizer = new ProcessorNameNormalizer();
17+
}
18+
19+
public function testNormalizeWithStringKey(): void
20+
{
21+
$result = $this->normalizer->normalize('processor_name', []);
22+
23+
$this->assertEquals('processor_name', $result);
24+
}
25+
26+
public function testNormalizeWithIntegerKey(): void
27+
{
28+
$result = $this->normalizer->normalize(0, ['processor_name' => []]);
29+
30+
$this->assertEquals('processor_name', $result);
31+
}
32+
33+
public function testNormalizeWithEmptyProcessor(): void
34+
{
35+
$result = $this->normalizer->normalize(0, []);
36+
37+
$this->assertEquals('', $result);
38+
}
39+
}

0 commit comments

Comments
 (0)