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

Open in your IDE?
  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 $request, Response $response, Throwable $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. }