Skip to content

Commit

Permalink
chore(definition-loader): faster/naive circular references detection
Browse files Browse the repository at this point in the history
  • Loading branch information
sidux committed Feb 20, 2024
1 parent 4cd3c46 commit 3240cca
Showing 1 changed file with 14 additions and 36 deletions.
50 changes: 14 additions & 36 deletions src/Definition/Loader/OpenApiDefinitionLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -411,12 +411,10 @@ private function getExamples(\cebe\openapi\spec\Operation $operation, array $par
try {
$example = (array) $this->extractDeepExamples(
$mediaType->schema,
path: 'requestBody.mediaType.schema'
path: $operation->operationId . '.requestBody.mediaType.schema'
);
} catch (InvalidExampleException $e) {
$this->logger->warning(
'Could not extract example for request body, error: ' . $e->getMessage()
);
$this->logger->warning($e->getMessage());
continue;
}
$operationExample = $this->getExample('properties', $examples);
Expand Down Expand Up @@ -459,12 +457,10 @@ private function getExamples(\cebe\openapi\spec\Operation $operation, array $par
try {
$example = $this->extractDeepExamples(
$mediaType->schema,
path: 'responseBody.mediaType.schema'
path: $operation->operationId . '.responseBody.mediaType.schema'
);
} catch (InvalidExampleException $e) {
$this->logger->warning(
'Could not extract example for response body, error: ' . $e->getMessage()
);
$this->logger->warning($e->getMessage());
continue;
}
$operationExample = $this->getExample('properties', $examples);
Expand Down Expand Up @@ -513,41 +509,23 @@ private function getExample(string $name, array &$examples, ?int $statusCode = n
}

/**
* @return array<string, int>
* naive check for circular references for performance reasons
*/
private function findRepeatingPatterns(string $string, int $occurrences = 2): array
private function hasRepeatingConsecutivePattern(string $s): bool
{
$length = mb_strlen($string);
$patternCounts = [];

$minPatternLength = 2;
$maxPatternLength = $length / 2;

for ($patternLength = $minPatternLength; $patternLength <= $maxPatternLength; $patternLength++) {
for ($start = 0; $start <= $length - $patternLength; $start++) {
$pattern = mb_substr($string, $start, $patternLength);
$patternQuoted = preg_quote($pattern, '/');
$matches = [];
preg_match_all("/{$patternQuoted}/", $string, $matches);

if (!empty($matches[0]) && count($matches[0]) > $occurrences) {
if (!array_key_exists($pattern, $patternCounts)) {
$patternCounts[$pattern] = count($matches[0]);
}
}
}
}
$parts = explode('.', $s);
$lastPart = $parts[count($parts) - 1];
$count = mb_substr_count($s, $lastPart);

return array_filter($patternCounts, static fn ($count) => $count > $occurrences);
return $count > 3;
}

/**
* @throws InvalidExampleException
*/
private function extractDeepExamples(Schema $schema, bool $optional = false, string $path = ''): mixed
{
$repeatPatterns = $this->findRepeatingPatterns($path, 3);
if (count($repeatPatterns) > 0) {
if ($this->hasRepeatingConsecutivePattern($path)) {
$this->logger->warning("Found circular reference in path: {$path}, using null as example");

return null;
Expand All @@ -556,7 +534,7 @@ private function extractDeepExamples(Schema $schema, bool $optional = false, str
if (isset($schema->type)) {
if ($schema->type === 'array' && $schema->items instanceof Schema) {
return [
$this->extractDeepExamples($schema->items, false, $path . 'items'),
$this->extractDeepExamples($schema->items, false, $path),
];
}

Expand All @@ -571,13 +549,13 @@ private function extractDeepExamples(Schema $schema, bool $optional = false, str
$example[$name] = $this->extractDeepExamples(
$property,
!$isRequired,
$path . '.properties.' . $name,
$path . '.' . $name,
);
} catch (InvalidExampleException $e) {
if ($optional) {
continue;
}
$this->logger->warning($e->getMessage());
throw $e;
}
}

Expand Down

0 comments on commit 3240cca

Please sign in to comment.