diff --git a/src/Console/Commands/AnalyzeDocumentation.php b/src/Console/Commands/AnalyzeDocumentation.php index c3b78c0b3..942dc7f48 100644 --- a/src/Console/Commands/AnalyzeDocumentation.php +++ b/src/Console/Commands/AnalyzeDocumentation.php @@ -27,13 +27,15 @@ public function handle(Generator $generator): int $generator(Scramble::getGeneratorConfig($this->option('api'))); + $exceptions = $generator->context->diagnostics->toExceptions(); + $i = 1; - $this->groupExceptions($generator->exceptions)->each(function (Collection $exceptions, string $group) use (&$i) { + $this->groupExceptions($exceptions)->each(function (Collection $exceptions, string $group) use (&$i) { $this->renderExceptionsGroup($exceptions, $group, $i); }); - if (count($generator->exceptions)) { - $this->error('[ERROR] Found '.count($generator->exceptions).' errors.'); + if (count($exceptions)) { + $this->error('[ERROR] Found '.count($exceptions).' errors.'); return static::FAILURE; } diff --git a/src/Diagnostics/Diagnostic.php b/src/Diagnostics/Diagnostic.php new file mode 100644 index 000000000..da898e92b --- /dev/null +++ b/src/Diagnostics/Diagnostic.php @@ -0,0 +1,19 @@ +withRoute($this->route); + + $this->diagnostics->push($diagnostic); + + if ($this->throwOnError && $diagnostic->severity() === DiagnosticSeverity::Error) { + throw $diagnostic->toException(); + } + } + + public function reportQuietly(GenericDiagnostic $diagnostic): void + { + $diagnostic = $diagnostic->withRoute($this->route); + + $this->diagnostics->push($diagnostic); + } + + public function toExceptions(): array + { + return array_map(fn (Diagnostic $d) => $d->toException(), $this->diagnostics->all()); + } + + public function forRoute(Route $route): self + { + return new self($this->diagnostics, $this->throwOnError, $route); + } +} diff --git a/src/Diagnostics/GenericDiagnostic.php b/src/Diagnostics/GenericDiagnostic.php new file mode 100644 index 000000000..f51641118 --- /dev/null +++ b/src/Diagnostics/GenericDiagnostic.php @@ -0,0 +1,58 @@ +message; + } + + public function severity(): DiagnosticSeverity + { + return $this->severity; + } + + public function toException(): Throwable + { + $exception = $this->originException ?? new Exception($this->message); + + if ($this->route) { + $exception = $exception instanceof RouteAware ? $exception->setRoute($this->route) : $exception; + } + + return $exception; + } + + public function withRoute(?Route $route): self + { + return new self($this->message, $this->severity, $this->originException, $route); + } + + public function withSeverity(DiagnosticSeverity $severity): self + { + return new self($this->message, $severity, $this->originException, $this->route); + } + + public static function fromException(Throwable $exception): self + { + return new self( + $exception->getMessage(), + DiagnosticSeverity::Error, + $exception, + ); + } +} diff --git a/src/Exceptions/InvalidSchema.php b/src/Exceptions/InvalidSchema.php index 98c3375da..696054d17 100644 --- a/src/Exceptions/InvalidSchema.php +++ b/src/Exceptions/InvalidSchema.php @@ -6,7 +6,6 @@ use Dedoc\Scramble\Support\Generator\Types\Type; use Exception; use Illuminate\Console\OutputStyle; -use Illuminate\Routing\Route; class InvalidSchema extends Exception implements ConsoleRenderable, RouteAware { @@ -42,14 +41,6 @@ public static function createForSchema(string $message, string $path, Type $sche return $exception; } - public function getRouteAwareMessage(Route $route, string $msg): string - { - $method = $route->methods()[0]; - $action = $route->getAction('uses'); - - return "'$method $route->uri' ($action): ".$msg; - } - public function renderInConsole(OutputStyle $outputStyle): void { $codeSample = null; diff --git a/src/Exceptions/RouteAwareTrait.php b/src/Exceptions/RouteAwareTrait.php index 271212019..2f3a800b5 100644 --- a/src/Exceptions/RouteAwareTrait.php +++ b/src/Exceptions/RouteAwareTrait.php @@ -27,4 +27,12 @@ public function getRoute(): ?Route { return $this->route; } + + public function getRouteAwareMessage(Route $route, string $msg): string + { + $method = $route->methods()[0]; + $action = $route->getAction('uses'); + + return "'$method $route->uri' ($action): ".$msg; + } } diff --git a/src/Exceptions/RulesEvaluationException.php b/src/Exceptions/RulesEvaluationException.php index 9d2d4bdc1..11e37d6c0 100644 --- a/src/Exceptions/RulesEvaluationException.php +++ b/src/Exceptions/RulesEvaluationException.php @@ -6,8 +6,10 @@ use Illuminate\Support\Arr; use Throwable; -class RulesEvaluationException extends Exception +class RulesEvaluationException extends Exception implements RouteAware { + use RouteAwareTrait; + /** @var array */ public array $exceptions = []; diff --git a/src/Generator.php b/src/Generator.php index c8393d664..f50436535 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -6,6 +6,7 @@ use Dedoc\Scramble\Attributes\ExcludeAllRoutesFromDocs; use Dedoc\Scramble\Attributes\ExcludeRouteFromDocs; use Dedoc\Scramble\Contracts\DocumentTransformer; +use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; use Dedoc\Scramble\Exceptions\RouteAware; use Dedoc\Scramble\OpenApiVisitor\SchemaEnforceVisitor; use Dedoc\Scramble\Support\ContainerUtils; @@ -20,10 +21,8 @@ use Dedoc\Scramble\Support\Generator\UniqueNameOptions; use Dedoc\Scramble\Support\Generator\UniqueNamesOptionsCollection; use Dedoc\Scramble\Support\OperationBuilder; -use Dedoc\Scramble\Support\RouteInfo; use Dedoc\Scramble\Support\ServerFactory; use Illuminate\Routing\Route; -use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Route as RouteFacade; use Illuminate\Support\Str; @@ -34,10 +33,10 @@ class Generator { - public array $exceptions = []; - protected bool $throwExceptions = true; + public ?OpenApiContext $context = null; + public function __construct( private OperationBuilder $operationBuilder, ) {} @@ -54,13 +53,32 @@ public function __invoke(?GeneratorConfig $config = null) $config ??= Scramble::getGeneratorConfig(Scramble::DEFAULT_API); $openApi = $this->makeOpenApi($config); - $context = new OpenApiContext($openApi, $config); + $context = $this->context = new OpenApiContext($openApi, $config, diagnostics: new DiagnosticsCollector(throwOnError: $this->throwExceptions)); $typeTransformer = $this->buildTypeTransformer($context); - $this->getRoutes($config) - ->flatMap(function (Route $route, int $index) use ($openApi, $config, $typeTransformer) { + $operations = $this->generateOperations($context, $typeTransformer); + + $this->setUniqueOperationId($operations); + $operations->each(fn (Operation $operation) => $openApi->addPath( + Path::make( + (string) Str::of($operation->path) + ->replaceStart($config->get('api_path', 'api'), '') + ->trim('/') + )->addOperation($operation) + )); + $this->moveSameAlternativeServersToPath($openApi); + + $this->applyDocumentTransformers($context, $typeTransformer); + + return $openApi->toArray(); + } + + private function generateOperations(OpenApiContext $context, TypeTransformer $typeTransformer): Collection + { + return $this->getRoutes($context->config) + ->flatMap(function (Route $route, int $index) use ($context, $typeTransformer) { try { - $operations = $this->routeToOperations($openApi, $route, $config, $typeTransformer); + $operations = $this->routeToOperations($context, $route, $typeTransformer); foreach ($operations as $i => $operation) { if ($route->getAction('uses') instanceof Closure) { @@ -91,44 +109,7 @@ public function __invoke(?GeneratorConfig $config = null) } }) ->filter() - ->sortBy($this->createOperationsSorter()) - ->each(fn (Operation $operation) => $openApi->addPath( - Path::make( - (string) Str::of($operation->path) - ->replaceStart($config->get('api_path', 'api'), '') - ->trim('/') - )->addOperation($operation) - )) - ->toArray(); - - $this->setUniqueOperationId($openApi); - - $this->moveSameAlternativeServersToPath($openApi); - - foreach ($config->documentTransformers->all() as $openApiTransformer) { - $openApiTransformer = is_callable($openApiTransformer) - ? $openApiTransformer - : ContainerUtils::makeContextable($openApiTransformer, [ - TypeTransformer::class => $typeTransformer, - ]); - - if (is_callable($openApiTransformer)) { - $openApiTransformer($openApi, $context); - - continue; - } - - if ($openApiTransformer instanceof DocumentTransformer) { - $openApiTransformer->handle($openApi, $context); - - continue; - } - - // @phpstan-ignore deadCode.unreachable - throw new InvalidArgumentException('(callable(OpenApi, OpenApiContext): void)|DocumentTransformer type for document transformer expected, received '.$openApiTransformer::class); - } - - return $openApi->toArray(); + ->sortBy($this->createOperationsSorter()); } private function createOperationsSorter(): array @@ -168,6 +149,32 @@ private function makeOpenApi(GeneratorConfig $config) return $openApi; } + private function applyDocumentTransformers(OpenApiContext $context, TypeTransformer $typeTransformer): void + { + foreach ($context->config->documentTransformers->all() as $openApiTransformer) { + $openApiTransformer = is_callable($openApiTransformer) + ? $openApiTransformer + : ContainerUtils::makeContextable($openApiTransformer, [ + TypeTransformer::class => $typeTransformer, + ]); + + if (is_callable($openApiTransformer)) { + $openApiTransformer($context->openApi, $context); + + continue; + } + + if ($openApiTransformer instanceof DocumentTransformer) { + $openApiTransformer->handle($context->openApi, $context); + + continue; + } + + // @phpstan-ignore deadCode.unreachable + throw new InvalidArgumentException('(callable(OpenApi, OpenApiContext): void)|DocumentTransformer type for document transformer expected, received '.$openApiTransformer::class); + } + } + /** * @return Collection */ @@ -239,31 +246,24 @@ private function buildTypeTransformer(OpenApiContext $context): TypeTransformer } /** @return Operation[] */ - private function routeToOperations(OpenApi $openApi, Route $route, GeneratorConfig $config, TypeTransformer $typeTransformer): array + private function routeToOperations(OpenApiContext $context, Route $route, TypeTransformer $typeTransformer): array { - $methods = array_map('strtolower', Arr::wrap(($config->operationMethodsResolver)($route))); - - $operations = []; - foreach ($methods as $method) { - $routeInfo = new RouteInfo($route, $method); + $operations = $this->operationBuilder->buildAll($context, $route, $typeTransformer); - $operation = $this->operationBuilder->build($routeInfo, $openApi, $config, $typeTransformer); - - $this->ensureSchemaTypes($route, $operation); - - $operations[] = $operation; + foreach ($operations as $operation) { + $this->ensureSchemaTypes($context, $route, $operation); } return $operations; } - private function ensureSchemaTypes(Route $route, Operation $operation): void + private function ensureSchemaTypes(OpenApiContext $context, Route $route, Operation $operation): void { if (! Scramble::getSchemaValidator()->hasRules()) { return; } - [$traverser, $visitor] = $this->createSchemaEnforceTraverser($route); + [$traverser, $visitor] = $this->createSchemaEnforceTraverser($route, $context); $traverser->traverse($operation, ['', 'paths', $operation->path, $operation->method]); $references = $visitor->popReferences(); @@ -276,14 +276,17 @@ private function ensureSchemaTypes(Route $route, Operation $operation): void } } - private function createSchemaEnforceTraverser(Route $route) + /** + * @return array{OpenApiTraverser, SchemaEnforceVisitor} + */ + private function createSchemaEnforceTraverser(Route $route, OpenApiContext $context): array { - $traverser = new OpenApiTraverser([$visitor = new SchemaEnforceVisitor($route, $this->throwExceptions, $this->exceptions)]); + $traverser = new OpenApiTraverser([$visitor = new SchemaEnforceVisitor($route, $context->diagnostics)]); return [$traverser, $visitor]; } - private function moveSameAlternativeServersToPath(OpenApi $openApi) + private function moveSameAlternativeServersToPath(OpenApi $openApi): void { foreach (collect($openApi->paths)->groupBy('path') as $pathsGroup) { if ($pathsGroup->isEmpty()) { @@ -310,11 +313,14 @@ private function moveSameAlternativeServersToPath(OpenApi $openApi) } } - private function setUniqueOperationId(OpenApi $openApi) + /** + * @param Collection $operations + */ + private function setUniqueOperationId(Collection $operations): void { $names = new UniqueNamesOptionsCollection; - $this->foreachOperation($openApi, function (Operation $operation) use ($names) { + $operations->each(function (Operation $operation) use ($names) { if ($operation->operationId) { return; } @@ -322,7 +328,7 @@ private function setUniqueOperationId(OpenApi $openApi) $names->push($operation->getAttribute('operationId')); // @phpstan-ignore argument.type }); - $this->foreachOperation($openApi, function (Operation $operation, $index) use ($names) { + $operations->each(function (Operation $operation, $index) use ($names) { if ($operation->operationId) { return; } @@ -341,19 +347,4 @@ private function setUniqueOperationId(OpenApi $openApi) })); }); } - - private function foreachOperation(OpenApi $openApi, callable $callback) - { - foreach (collect($openApi->paths)->groupBy('path') as $pathsGroup) { - if ($pathsGroup->isEmpty()) { - continue; - } - - $operations = collect($pathsGroup->pluck('operations')->flatten()); - - foreach ($operations as $index => $operation) { - $callback($operation, $index); - } - } - } } diff --git a/src/OpenApiContext.php b/src/OpenApiContext.php index 085d98030..3ee87b7f8 100644 --- a/src/OpenApiContext.php +++ b/src/OpenApiContext.php @@ -3,6 +3,7 @@ namespace Dedoc\Scramble; use Dedoc\Scramble\Attributes\Group; +use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; use Dedoc\Scramble\Support\Generator\OpenApi; use Illuminate\Support\Collection; use ReflectionAttribute; @@ -17,5 +18,6 @@ public function __construct( * @var Collection> */ public Collection $groups = new Collection, + public DiagnosticsCollector $diagnostics = new DiagnosticsCollector, ) {} } diff --git a/src/OpenApiVisitor/SchemaEnforceVisitor.php b/src/OpenApiVisitor/SchemaEnforceVisitor.php index d72781749..cd8f26de3 100644 --- a/src/OpenApiVisitor/SchemaEnforceVisitor.php +++ b/src/OpenApiVisitor/SchemaEnforceVisitor.php @@ -3,6 +3,8 @@ namespace Dedoc\Scramble\OpenApiVisitor; use Dedoc\Scramble\AbstractOpenApiVisitor; +use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; +use Dedoc\Scramble\Diagnostics\GenericDiagnostic; use Dedoc\Scramble\Exceptions\InvalidSchema; use Dedoc\Scramble\Exceptions\RouteAware; use Dedoc\Scramble\OpenApiTraverser; @@ -19,8 +21,7 @@ class SchemaEnforceVisitor extends AbstractOpenApiVisitor public function __construct( private Route $route, - private bool $throwExceptions = true, - protected array &$exceptions = [], + private DiagnosticsCollector $diagnostics, ) {} public function popReferences() @@ -28,7 +29,7 @@ public function popReferences() return tap($this->operationReferences, fn () => $this->operationReferences = []); } - public function enter($object, array $path = []) + public function enter($object, array $path = []): void { if ($object instanceof Reference) { if (array_key_exists($object->fullName, static::$handledReferences)) { @@ -43,7 +44,7 @@ public function enter($object, array $path = []) } } - protected function validateSchema($object, $path) + protected function validateSchema($object, $path): void { $exceptions = []; try { @@ -54,17 +55,15 @@ protected function validateSchema($object, $path) } catch (InvalidSchema $e) { $e->setRoute($this->route); - if ($this->throwExceptions) { - throw $e; - } - - $this->exceptions[] = $e; + $this->diagnostics->report(GenericDiagnostic::fromException($e)); } + foreach ($exceptions as $exception) { if ($exception instanceof RouteAware) { $exception->setRoute($this->route); } + + $this->diagnostics->reportQuietly(GenericDiagnostic::fromException($exception)); } - $this->exceptions = array_merge($this->exceptions, $exceptions); } } diff --git a/src/Support/OperationBuilder.php b/src/Support/OperationBuilder.php index dbe78bf1c..eb7481ca1 100644 --- a/src/Support/OperationBuilder.php +++ b/src/Support/OperationBuilder.php @@ -3,17 +3,39 @@ namespace Dedoc\Scramble\Support; use Dedoc\Scramble\Contracts\OperationTransformer; +use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; use Dedoc\Scramble\GeneratorConfig; use Dedoc\Scramble\OpenApiContext; use Dedoc\Scramble\Support\Generator\OpenApi; use Dedoc\Scramble\Support\Generator\Operation; use Dedoc\Scramble\Support\Generator\TypeTransformer; +use Illuminate\Routing\Route; +use Illuminate\Support\Arr; use InvalidArgumentException; /** @internal */ class OperationBuilder { - public function build(RouteInfo $routeInfo, OpenApi $openApi, GeneratorConfig $config, TypeTransformer $typeTransformer) + /** + * @return Operation[] + */ + public function buildAll(OpenApiContext $context, Route $route, TypeTransformer $typeTransformer): array + { + $methods = array_map('strtolower', Arr::wrap(($context->config->operationMethodsResolver)($route))); + + $operations = []; + foreach ($methods as $method) { + $routeInfo = new RouteInfo($route, $method); + + $operation = $this->build($routeInfo, $context->openApi, $context->config, $typeTransformer); + + $operations[] = $operation; + } + + return $operations; + } + + public function build(RouteInfo $routeInfo, OpenApi $openApi, GeneratorConfig $config, TypeTransformer $typeTransformer): Operation { $operation = new Operation('get'); @@ -25,6 +47,7 @@ public function build(RouteInfo $routeInfo, OpenApi $openApi, GeneratorConfig $c OpenApiContext::class => $typeTransformer->context, GeneratorConfig::class => $config, TypeTransformer::class => $typeTransformer, + DiagnosticsCollector::class => $typeTransformer->context->diagnostics->forRoute($routeInfo->route), ]); if (is_callable($instance)) { diff --git a/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php b/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php index ab407f3c6..23c839093 100644 --- a/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php +++ b/src/Support/OperationExtensions/ParameterExtractor/FormRequestParametersExtractor.php @@ -2,6 +2,7 @@ namespace Dedoc\Scramble\Support\OperationExtensions\ParameterExtractor; +use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; use Dedoc\Scramble\Infer; use Dedoc\Scramble\Support\Generator\TypeTransformer; use Dedoc\Scramble\Support\OperationExtensions\RequestBodyExtension; @@ -30,6 +31,7 @@ class FormRequestParametersExtractor implements ParameterExtractor public function __construct( private PrettyPrinter $printer, private TypeTransformer $openApiTransformer, + private DiagnosticsCollector $diagnostics, ) {} public function handle(RouteInfo $routeInfo, array $parameterExtractionResults): array @@ -119,7 +121,7 @@ public function extractFormRequestParameters(string $requestClassName, RouteInfo return new ParametersExtractionResult( parameters: $this->makeParameters( - rules: (new ComposedFormRequestRulesEvaluator($this->printer, $classReflector, $routeInfo->method))->handle(), + rules: (new ComposedFormRequestRulesEvaluator($this->printer, $classReflector, $routeInfo->method, $this->diagnostics))->handle(), typeTransformer: $this->openApiTransformer, rulesDocsRetriever: new TypeBasedRulesDocumentationRetriever( $routeInfo->getScope(), diff --git a/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php b/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php index 982f4fe6e..26f8bc3ca 100644 --- a/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php +++ b/src/Support/OperationExtensions/ParameterExtractor/ValidateCallParametersExtractor.php @@ -2,6 +2,7 @@ namespace Dedoc\Scramble\Support\OperationExtensions\ParameterExtractor; +use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; use Dedoc\Scramble\Support\Generator\TypeTransformer; use Dedoc\Scramble\Support\OperationExtensions\RequestBodyExtension; use Dedoc\Scramble\Support\OperationExtensions\RulesEvaluator\NodeRulesEvaluator; @@ -23,6 +24,7 @@ class ValidateCallParametersExtractor implements ParameterExtractor public function __construct( private PrettyPrinter $printer, private TypeTransformer $openApiTransformer, + private DiagnosticsCollector $diagnostics, ) {} public function handle(RouteInfo $routeInfo, array $parameterExtractionResults): array @@ -43,7 +45,7 @@ public function handle(RouteInfo $routeInfo, array $parameterExtractionResults): $parameterExtractionResults[] = new ParametersExtractionResult( parameters: $this->makeParameters( - rules: (new NodeRulesEvaluator($this->printer, $astNode, $validationRulesNode, $routeInfo->method, $routeInfo->className(), $routeInfo->getScope()))->handle(), + rules: (new NodeRulesEvaluator($this->printer, $astNode, $validationRulesNode, $routeInfo->method, $routeInfo->className(), $routeInfo->getScope(), $this->diagnostics))->handle(), typeTransformer: $this->openApiTransformer, rulesDocsRetriever: new TypeBasedRulesDocumentationRetriever( $routeInfo->getScope(), diff --git a/src/Support/OperationExtensions/RequestBodyExtension.php b/src/Support/OperationExtensions/RequestBodyExtension.php index 10b069473..e4ca62fad 100644 --- a/src/Support/OperationExtensions/RequestBodyExtension.php +++ b/src/Support/OperationExtensions/RequestBodyExtension.php @@ -2,8 +2,10 @@ namespace Dedoc\Scramble\Support\OperationExtensions; -use Dedoc\Scramble\Extensions\OperationExtension; -use Dedoc\Scramble\Scramble; +use Dedoc\Scramble\Contracts\OperationTransformer; +use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; +use Dedoc\Scramble\Diagnostics\GenericDiagnostic; +use Dedoc\Scramble\GeneratorConfig; use Dedoc\Scramble\Support\ContainerUtils; use Dedoc\Scramble\Support\Generator\Combined\AllOf; use Dedoc\Scramble\Support\Generator\Operation; @@ -24,10 +26,16 @@ use Illuminate\Support\Str; use Throwable; -class RequestBodyExtension extends OperationExtension +class RequestBodyExtension implements OperationTransformer { const HTTP_METHODS_WITHOUT_REQUEST_BODY = ['get', 'delete', 'head']; + public function __construct( + protected TypeTransformer $openApiTransformer, + protected GeneratorConfig $config, + protected DiagnosticsCollector $diagnostics, + ) {} + public function handle(Operation $operation, RouteInfo $routeInfo): void { $description = Str::of($routeInfo->phpDoc()->getAttribute('description')); // @phpstan-ignore argument.type @@ -38,9 +46,8 @@ public function handle(Operation $operation, RouteInfo $routeInfo): void try { $rulesResults = collect($this->extractParameters($operation, $routeInfo)); } catch (Throwable $exception) { - if (Scramble::shouldThrowOnError()) { - throw $exception; - } + $this->diagnostics->report(GenericDiagnostic::fromException($exception)); + $description = $description->append('⚠️ Cannot generate request documentation: '.$exception->getMessage()); } @@ -258,6 +265,7 @@ private function extractParameters(Operation $operation, RouteInfo $routeInfo): $extractor = ContainerUtils::makeContextable($extractorClass, [ TypeTransformer::class => $this->openApiTransformer, Operation::class => $operation, + DiagnosticsCollector::class => $this->diagnostics, ]); $result = $extractor->handle($routeInfo, $result); diff --git a/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php b/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php index fc2e88955..417d72bf3 100644 --- a/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php +++ b/src/Support/OperationExtensions/RulesEvaluator/ComposedFormRequestRulesEvaluator.php @@ -2,6 +2,7 @@ namespace Dedoc\Scramble\Support\OperationExtensions\RulesEvaluator; +use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; use Dedoc\Scramble\Exceptions\RulesEvaluationException; use Dedoc\Scramble\Infer\Reflector\ClassReflector; use PhpParser\Node\Expr\Array_; @@ -15,6 +16,7 @@ public function __construct( private PrettyPrinter $printer, private ClassReflector $classReflector, private string $method, + private DiagnosticsCollector $diagnostics, ) {} public function handle(): array @@ -31,7 +33,7 @@ public function handle(): array $evaluators = [ new FormRequestRulesEvaluator($this->classReflector, $this->method), - new NodeRulesEvaluator($this->printer, $rulesMethodNode, $returnNode, $this->method, $this->classReflector->className, $rulesMethod->getFunctionLikeDefinition()->getScope()), + new NodeRulesEvaluator($this->printer, $rulesMethodNode, $returnNode, $this->method, $this->classReflector->className, $rulesMethod->getFunctionLikeDefinition()->getScope(), $this->diagnostics), ]; $exceptions = []; diff --git a/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php b/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php index 59f062a5c..98ac2cde1 100644 --- a/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php +++ b/src/Support/OperationExtensions/RulesEvaluator/NodeRulesEvaluator.php @@ -2,6 +2,9 @@ namespace Dedoc\Scramble\Support\OperationExtensions\RulesEvaluator; +use Dedoc\Scramble\Diagnostics\DiagnosticsCollector; +use Dedoc\Scramble\Diagnostics\DiagnosticSeverity; +use Dedoc\Scramble\Diagnostics\GenericDiagnostic; use Dedoc\Scramble\Exceptions\RulesEvaluationException; use Dedoc\Scramble\Infer\Scope\Scope; use Dedoc\Scramble\Infer\Services\ReferenceTypeResolver; @@ -32,6 +35,7 @@ public function __construct( private string $method, private ?string $className, private Scope $scope, + private DiagnosticsCollector $diagnostics, ) {} public function handle(): array @@ -198,6 +202,10 @@ private function doEvaluateExpression(?Node\Expr $expression, array $variables): try { return eval("return $code;"); } catch (Throwable $e) { + $this->diagnostics->report( + GenericDiagnostic::fromException($e)->withSeverity(DiagnosticSeverity::Warning) + ); + $this->lastEvaluationException = $e; }