Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .phpactor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "/phpactor.schema.json",
"language_server_phpstan.enabled": true,
"language_server_psalm.enabled": true,
"language_server_php_cs_fixer.enabled": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Duyler\OpenApi\Exception;
namespace Duyler\OpenApi\Validator\Exception;

use Exception;

Expand Down
154 changes: 106 additions & 48 deletions src/Validator/Schema/RefResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Duyler\OpenApi\Validator\Schema;

use Duyler\OpenApi\Exception\RefResolutionException;
use Duyler\OpenApi\Validator\Exception\RefResolutionException;
use Duyler\OpenApi\Schema\Model\Parameter;
use Duyler\OpenApi\Schema\Model\Response;
use Duyler\OpenApi\Schema\Model\Schema;
Expand Down Expand Up @@ -35,8 +35,10 @@ public function getBaseUri(OpenApiDocument $document): ?string
}

#[Override]
public function resolveRelativeRef(string $ref, OpenApiDocument $document): string
{
public function resolveRelativeRef(
string $ref,
OpenApiDocument $document,
): string {
$baseUri = $this->getBaseUri($document);

if (null === $baseUri) {
Expand All @@ -53,7 +55,7 @@ public function combineUris(string $baseUri, string $relativeRef): string
{
$basePath = dirname($baseUri);

return $basePath . '/' . $relativeRef;
return $basePath . "/" . $relativeRef;
}

#[Override]
Expand All @@ -66,50 +68,57 @@ public function resolve(string $ref, OpenApiDocument $document): Schema
if (false === $result instanceof Schema) {
throw new UnresolvableRefException(
$ref,
'Expected Schema but got ' . $result::class,
"Expected Schema but got " . $result::class,
);
}

return $result;
}

#[Override]
public function resolveParameter(string $ref, OpenApiDocument $document): Parameter
{
public function resolveParameter(
string $ref,
OpenApiDocument $document,
): Parameter {
/** @var array<string, bool> $visited */
$visited = [];
$result = $this->resolveRef($ref, $document, $visited);

if (false === $result instanceof Parameter) {
throw new UnresolvableRefException(
$ref,
'Expected Parameter but got ' . $result::class,
"Expected Parameter but got " . $result::class,
);
}

return $result;
}

#[Override]
public function resolveResponse(string $ref, OpenApiDocument $document): Response
{
public function resolveResponse(
string $ref,
OpenApiDocument $document,
): Response {
/** @var array<string, bool> $visited */
$visited = [];
$result = $this->resolveRef($ref, $document, $visited);

if (false === $result instanceof Response) {
throw new UnresolvableRefException(
$ref,
'Expected Response but got ' . $result::class,
"Expected Response but got " . $result::class,
);
}

return $result;
}

#[Override]
public function schemaHasDiscriminator(Schema $schema, OpenApiDocument $document, array &$visited = []): bool
{
public function schemaHasDiscriminator(
Schema $schema,
OpenApiDocument $document,
array &$visited = [],
): bool {
$schemaId = spl_object_id($schema);

if (isset($visited[$schemaId])) {
Expand All @@ -121,7 +130,11 @@ public function schemaHasDiscriminator(Schema $schema, OpenApiDocument $document
if (null !== $schema->ref) {
try {
$resolvedSchema = $this->resolve($schema->ref, $document);
return $this->schemaHasDiscriminator($resolvedSchema, $document, $visited);
return $this->schemaHasDiscriminator(
$resolvedSchema,
$document,
$visited,
);
} catch (UnresolvableRefException) {
return false;
}
Expand All @@ -133,27 +146,49 @@ public function schemaHasDiscriminator(Schema $schema, OpenApiDocument $document

if (null !== $schema->properties) {
foreach ($schema->properties as $property) {
if ($this->schemaHasDiscriminator($property, $document, $visited)) {
if (
$this->schemaHasDiscriminator(
$property,
$document,
$visited,
)
) {
return true;
}
}
}

if (null !== $schema->items) {
return $this->schemaHasDiscriminator($schema->items, $document, $visited);
return $this->schemaHasDiscriminator(
$schema->items,
$document,
$visited,
);
}

if (null !== $schema->oneOf) {
foreach ($schema->oneOf as $subSchema) {
if ($this->schemaHasDiscriminator($subSchema, $document, $visited)) {
if (
$this->schemaHasDiscriminator(
$subSchema,
$document,
$visited,
)
) {
return true;
}
}
}

if (null !== $schema->anyOf) {
foreach ($schema->anyOf as $subSchema) {
if ($this->schemaHasDiscriminator($subSchema, $document, $visited)) {
if (
$this->schemaHasDiscriminator(
$subSchema,
$document,
$visited,
)
) {
return true;
}
}
Expand Down Expand Up @@ -281,7 +316,10 @@ public function schemaHasRef(Schema $schema, array &$visited = []): bool
}
}

if (null !== $schema->additionalProperties && $schema->additionalProperties instanceof Schema) {
if (
null !== $schema->additionalProperties
&& $schema->additionalProperties instanceof Schema
) {
if ($this->schemaHasRef($schema->additionalProperties, $visited)) {
return true;
}
Expand All @@ -291,8 +329,10 @@ public function schemaHasRef(Schema $schema, array &$visited = []): bool
}

#[Override]
public function resolveSchemaWithOverride(Schema $schema, OpenApiDocument $document): Schema
{
public function resolveSchemaWithOverride(
Schema $schema,
OpenApiDocument $document,
): Schema {
if (null === $schema->ref) {
return $schema;
}
Expand Down Expand Up @@ -367,8 +407,10 @@ enum: $resolved->enum,
}

#[Override]
public function resolveParameterWithOverride(Parameter $parameter, OpenApiDocument $document): Parameter
{
public function resolveParameterWithOverride(
Parameter $parameter,
OpenApiDocument $document,
): Parameter {
if (null === $parameter->ref) {
return $parameter;
}
Expand Down Expand Up @@ -401,8 +443,10 @@ public function resolveParameterWithOverride(Parameter $parameter, OpenApiDocume
}

#[Override]
public function resolveResponseWithOverride(Response $response, OpenApiDocument $document): Response
{
public function resolveResponseWithOverride(
Response $response,
OpenApiDocument $document,
): Response {
if (null === $response->ref) {
return $response;
}
Expand Down Expand Up @@ -434,12 +478,16 @@ public function resolveResponseWithOverride(Response $response, OpenApiDocument
/**
* @param array<string, bool> $visited
*/
private function resolveRef(string $ref, OpenApiDocument $document, array &$visited): Schema|Parameter|Response
{
private function resolveRef(
string $ref,
OpenApiDocument $document,
array &$visited,
): Schema|Parameter|Response {
if (isset($visited[$ref])) {
throw new UnresolvableRefException(
$ref,
'Circular reference detected: ' . $this->formatCircularPath($visited, $ref),
"Circular reference detected: "
. $this->formatCircularPath($visited, $ref),
);
}

Expand All @@ -453,12 +501,15 @@ private function resolveRef(string $ref, OpenApiDocument $document, array &$visi
}
}

if (false === str_starts_with($ref, '#/')) {
throw new UnresolvableRefException($ref, 'Only local refs (#/...) are supported');
if (false === str_starts_with($ref, "#/")) {
throw new UnresolvableRefException(
$ref,
"Only local refs (#/...) are supported",
);
}

$path = substr($ref, 2);
$parts = explode('/', $path);
$parts = explode("/", $path);

try {
$result = $this->navigate($document, $parts);
Expand All @@ -481,18 +532,24 @@ private function resolveRef(string $ref, OpenApiDocument $document, array &$visi
/**
* @param array<int, string> $parts
*/
private function navigate(object|array $current, array $parts): Schema|Parameter|Response
{
private function navigate(
object|array $current,
array $parts,
): Schema|Parameter|Response {
$part = array_shift($parts);

if (null === $part) {
if ($current instanceof Schema || $current instanceof Parameter || $current instanceof Response) {
if (
$current instanceof Schema
|| $current instanceof Parameter
|| $current instanceof Response
) {
return $current;
}

throw new UnresolvableRefException(
'',
'Target is not a Schema, Parameter, or Response',
"",
"Target is not a Schema, Parameter, or Response",
);
}

Expand All @@ -501,13 +558,15 @@ private function navigate(object|array $current, array $parts): Schema|Parameter
return $this->navigate($next, $parts);
}

private function getProperty(object|array $container, string $property): object|array
{
private function getProperty(
object|array $container,
string $property,
): object|array {
if (is_array($container)) {
if (false === array_key_exists($property, $container)) {
throw new UnresolvableRefException(
$property,
'Array key does not exist',
"Array key does not exist",
);
}

Expand All @@ -516,24 +575,21 @@ private function getProperty(object|array $container, string $property): object|
if (false === property_exists($container, $property)) {
throw new UnresolvableRefException(
$property,
'Property does not exist',
"Property does not exist",
);
}

$value = $container->$property;
}

if (null === $value) {
throw new UnresolvableRefException(
$property,
'Value is null',
);
throw new UnresolvableRefException($property, "Value is null");
}

if (false === is_object($value) && false === is_array($value)) {
throw new UnresolvableRefException(
$property,
'Value is not an object or array',
"Value is not an object or array",
);
}

Expand All @@ -543,10 +599,12 @@ private function getProperty(object|array $container, string $property): object|
/**
* @param array<string, bool> $visited
*/
private function formatCircularPath(array $visited, string $circularRef): string
{
private function formatCircularPath(
array $visited,
string $circularRef,
): string {
$path = array_keys($visited);
$path[] = $circularRef;
return implode(' -> ', $path);
return implode(" -> ", $path);
}
}
Loading