vendor/overblog/graphql-bundle/src/DataCollector/GraphQLCollector.php line 101

  1. <?php
  2. declare(strict_types=1);
  3. namespace Overblog\GraphQLBundle\DataCollector;
  4. use GraphQL\Error\SyntaxError;
  5. use GraphQL\Language\AST\DocumentNode;
  6. use GraphQL\Language\AST\FieldNode;
  7. use GraphQL\Language\AST\OperationDefinitionNode;
  8. use GraphQL\Language\Parser;
  9. use Overblog\GraphQLBundle\Event\ExecutorResultEvent;
  10. use Symfony\Component\HttpFoundation\Request;
  11. use Symfony\Component\HttpFoundation\Response;
  12. use Symfony\Component\HttpKernel\DataCollector\DataCollector;
  13. use Throwable;
  14. use function count;
  15. use function microtime;
  16. class GraphQLCollector extends DataCollector
  17. {
  18.     /**
  19.      * GraphQL Batches executed.
  20.      */
  21.     protected array $batches = [];
  22.     public function collect(Request $requestResponse $responseThrowable $exception null): void
  23.     {
  24.         $error false;
  25.         $count 0;
  26.         $schema null;
  27.         foreach ($this->batches as $batch) {
  28.             if (!$schema) {
  29.                 $schema $batch['schema'];
  30.             }
  31.             if (isset($batch['error'])) {
  32.                 $error true;
  33.             }
  34.             $count += $batch['count'];
  35.         }
  36.         $this->data = [
  37.             'schema' => $schema,
  38.             'batches' => $this->batches,
  39.             'count' => $count,
  40.             'error' => $error,
  41.         ];
  42.     }
  43.     /**
  44.      * Check if we have an error.
  45.      */
  46.     public function getError(): bool
  47.     {
  48.         return $this->data['error'] ?? false;
  49.     }
  50.     /**
  51.      * Count the number of executed queries.
  52.      */
  53.     public function getCount(): int
  54.     {
  55.         return $this->data['count'] ?? 0;
  56.     }
  57.     /**
  58.      * Return the targeted schema.
  59.      */
  60.     public function getSchema(): ?string
  61.     {
  62.         return $this->data['schema'];
  63.     }
  64.     /**
  65.      * Return the list of executed batch.
  66.      */
  67.     public function getBatches(): array
  68.     {
  69.         return $this->data['batches'] ?? [];
  70.     }
  71.     /**
  72.      * {@inheritdoc}
  73.      */
  74.     public function reset(): void
  75.     {
  76.         $this->data = [];
  77.     }
  78.     /**
  79.      * {@inheritdoc}
  80.      */
  81.     public function getName(): string
  82.     {
  83.         return 'graphql';
  84.     }
  85.     /**
  86.      * Hook into the GraphQL events to populate the collector.
  87.      */
  88.     public function onPostExecutor(ExecutorResultEvent $event): void
  89.     {
  90.         $executorArgument $event->getExecutorArguments();
  91.         $queryString $executorArgument->getRequestString();
  92.         $operationName $executorArgument->getOperationName();
  93.         $variables $executorArgument->getVariableValue();
  94.         $queryTime microtime(true) - $executorArgument->getStartTime();
  95.         $result $event->getResult()->toArray();
  96.         $batch = [
  97.             'schema' => $executorArgument->getSchemaName(),
  98.             'queryString' => $queryString,
  99.             'queryTime' => $queryTime,
  100.             'variables' => $this->cloneVar($variables),
  101.             'result' => $this->cloneVar($result),
  102.             'count' => 0,
  103.         ];
  104.         try {
  105.             $parsed Parser::parse($queryString);
  106.             $batch['graphql'] = $this->extractGraphql($parsed$operationName);
  107.             if (isset($batch['graphql']['fields'])) {
  108.                 $batch['count'] += count($batch['graphql']['fields']);
  109.             }
  110.             $error $result['errors'][0] ?? false;
  111.             if ($error) {
  112.                 $batch['error'] = [
  113.                     'message' => $error['message'],
  114.                     'location' => $error['locations'][0] ?? false,
  115.                 ];
  116.             }
  117.         } catch (SyntaxError $error) {
  118.             $location $error->getLocations()[0] ?? false;
  119.             $batch['error'] = ['message' => $error->getMessage(), 'location' => $location];
  120.         }
  121.         $this->batches[] = $batch;
  122.     }
  123.     /**
  124.      * Extract GraphQL Information from the documentNode.
  125.      */
  126.     protected function extractGraphql(DocumentNode $document, ?string $operationName): array
  127.     {
  128.         $operation null;
  129.         $fields = [];
  130.         foreach ($document->definitions as $definition) {
  131.             if ($definition instanceof OperationDefinitionNode) {
  132.                 $definitionOperation $definition->name->value ?? null;
  133.                 if ($operationName != $definitionOperation) {
  134.                     continue;
  135.                 }
  136.                 $operation $definition->operation;
  137.                 foreach ($definition->selectionSet->selections as $selection) {
  138.                     if ($selection instanceof FieldNode) {
  139.                         $name $selection->name->value;
  140.                         $alias $selection->alias $selection->alias->value null;
  141.                         $fields[] = [
  142.                             'name' => $name,
  143.                             'alias' => $alias,
  144.                         ];
  145.                     }
  146.                 }
  147.             }
  148.         }
  149.         return [
  150.             'operation' => $operation,
  151.             'operationName' => $operationName,
  152.             'fields' => $fields,
  153.         ];
  154.     }
  155. }