Skip to content

Commit 96bbaf7

Browse files
authored
Merge pull request #1411 from Inchie/bugfix/fix-recursion-in-tca-schema-validator
[BUGFIX] Prevent endless recursion in TcaSchemaFieldLengthValidator.
2 parents 9e7ac7a + 16d76c1 commit 96bbaf7

1 file changed

Lines changed: 24 additions & 22 deletions

File tree

Classes/Validation/Validator/TcaSchemaFieldLengthValidator.php

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,16 @@
2020
use TYPO3\CMS\Extbase\Error\Result;
2121
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory;
2222
use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator;
23-
use TYPO3\CMS\Extbase\Validation\Validator\ObjectValidatorInterface;
2423

2524
/**
26-
* Validates that all string properties of a domain model (and its nested relations) do not exceed
27-
* the maximum field length defined in the database schema.
25+
* Validates that all string properties of a domain model and its nested relations
26+
* do not exceed the maximum field length defined in the database schema.
2827
*
29-
* Recursively traverses 1:1 relations (AbstractDomainObject properties) and 1:n / m:n relations
30-
* (Traversable properties, e.g. ObjectStorage) and validates each related model as well.
31-
* Circular references are detected via a shared SplObjectStorage and skipped to prevent infinite loops.
32-
*
33-
* Implements ObjectValidatorInterface so that TYPO3's validation chain can inject the shared
34-
* validatedInstancesContainer when this validator is used with other object validators.
28+
* Recursively traverses related domain objects and validates each entity only once.
29+
* Processed entities are tracked using a stable identifier based on class name and uid
30+
* to prevent endless recursion caused by cyclic relations and lazy-loading proxies.
3531
*/
36-
final class TcaSchemaFieldLengthValidator extends AbstractValidator implements ObjectValidatorInterface
32+
final class TcaSchemaFieldLengthValidator extends AbstractValidator
3733
{
3834
private const SUPPORTED_TYPES = [
3935
TableColumnType::INPUT->value,
@@ -45,7 +41,7 @@ final class TcaSchemaFieldLengthValidator extends AbstractValidator implements O
4541
TableColumnType::TEXT->value,
4642
];
4743

48-
private ?\SplObjectStorage $validatedInstancesContainer = null;
44+
private array $processedEntityIdentifiers = [];
4945
private array $columnCache = [];
5046

5147
public function __construct(
@@ -55,32 +51,26 @@ public function __construct(
5551
) {
5652
}
5753

58-
public function setValidatedInstancesContainer(\SplObjectStorage $validatedInstancesContainer): void
59-
{
60-
$this->validatedInstancesContainer = $validatedInstancesContainer;
61-
}
62-
6354
public function isValid(mixed $value): void
6455
{
56+
$this->processedEntityIdentifiers = [];
57+
6558
if (!($value instanceof AbstractDomainObject)) {
6659
return;
6760
}
6861

69-
if ($this->validatedInstancesContainer === null) {
70-
$this->validatedInstancesContainer = new \SplObjectStorage();
71-
}
72-
7362
$this->result->merge($this->validateDomainObject($value));
7463
}
7564

7665
private function validateDomainObject(AbstractDomainObject $value): Result
7766
{
7867
$result = new Result();
7968

80-
if ($this->validatedInstancesContainer->contains($value)) {
69+
$identifier = $this->getValidationIdentifier($value);
70+
if (isset($this->processedEntityIdentifiers[$identifier])) {
8171
return $result;
8272
}
83-
$this->validatedInstancesContainer->attach($value);
73+
$this->processedEntityIdentifiers[$identifier] = true;
8474

8575
$dataMap = $this->dataMapFactory->buildDataMap(get_class($value));
8676
$tableName = $dataMap->getTableName();
@@ -139,4 +129,16 @@ private function validateDomainObject(AbstractDomainObject $value): Result
139129

140130
return $result;
141131
}
132+
133+
private function getValidationIdentifier(AbstractDomainObject $object): string
134+
{
135+
$className = get_parent_class($object);
136+
$uid = $object->getUid();
137+
138+
if ($uid > 0) {
139+
return sprintf('%s:%s', $className, $uid);
140+
}
141+
142+
return sprintf('%s:%s', $className, spl_object_id($object));
143+
}
142144
}

0 commit comments

Comments
 (0)