recursivity check

master
Josha von Gizycki 2 weeks ago
parent 6537a2db81
commit 94f5bcb2a1

@ -37,6 +37,8 @@ readonly class EntityAnalyzer
*/ */
public function analyze(): EntityResult public function analyze(): EntityResult
{ {
(new RecursionCheck($this->entity))->check();
$refClass = new ReflectionClass($this->entity); $refClass = new ReflectionClass($this->entity);
$fields = $this->fields($refClass); $fields = $this->fields($refClass);

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Kartierung\Analyze;
use Kartierung\Analyze\Exception\InvalidEntity;
use Kartierung\Attribute\ListOf;
use ReflectionClass;
use ReflectionException;
readonly class RecursionCheck
{
/**
* @param class-string<object> $fqcn
*/
public function __construct(
private string $fqcn
) {}
public function check(): void
{
/** @var list<class-string<object>> $seenClasses */
$seenClasses = [$this->fqcn];
$toProcess = [...$this->declaredListTypes($this->fqcn)];
while (!empty($toProcess)) {
$current = array_pop($toProcess);
try {
// is there a better workaround?
new ReflectionClass($current);
if (in_array($current, $seenClasses)) {
throw new InvalidEntity(
"Entity $this->fqcn declares a recursive mapping. This is not supported."
);
}
$seenClasses[] = $current;
} catch (ReflectionException) {
// $current does not reference a class, skip
}
}
}
/**
* @param class-string<object> $fqcn
* @return list<class-string<object>>
*/
private function declaredListTypes(string $fqcn): array
{
$params = (new ReflectionClass($fqcn))->getConstructor()?->getParameters();
if ($params === null) {
return [];
}
$result = [];
foreach ($params as $param) {
$listAttr = $param->getAttributes(ListOf::class);
if (empty($listAttr)) {
continue;
}
/** @var ListOf $inst */
$inst = $listAttr[0]->newInstance();
$result[] = $inst->entitiyFqcn;
}
return $result;
}
}

@ -168,4 +168,17 @@ class EntityAnalyzerTest extends TestCase
$result->fields[1] $result->fields[1]
); );
} }
public function testInfiniteRecursionIsDetected(): void
{
// Given
$entity = EntityWithInfiniteRecursion::class;
// Then
$this->expectException(InvalidEntity::class);
$this->expectExceptionMessageMatches('/.*recursive.*/');
// When
(new EntityAnalyzer($entity))->analyze();
}
} }

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Kartierung\Analyze;
use Kartierung\Attribute\Id;
use Kartierung\Attribute\ListOf;
class EntityWithInfiniteRecursion
{
public function __construct(
#[Id]
public int $id,
#[ListOf(self::class)]
public array $children
) {}
}
Loading…
Cancel
Save