Skip to content
Open
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
20 changes: 12 additions & 8 deletions Command/GraphQLConfigureCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class GraphQLConfigureCommand extends Command
{
const PROJECT_NAMESPACE = 'App';
public const PROJECT_NAMESPACE = 'App';

/** @var Container */
protected $container;
Expand All @@ -39,7 +39,7 @@ protected function configure()
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$isComposerCall = $input->getOption('composer');

Expand All @@ -59,11 +59,13 @@ protected function execute(InputInterface $input, OutputInterface $output)
} else {
$question = new ConfirmationQuestion(sprintf('Confirm creating class at %s ? [Y/n]', $schemaNamespace . '\\' . $className), true);
if (!$inputHelper->ask($input, $output, $question)) {
return;
return Command::SUCCESS;
}

if (!is_dir($graphqlPath)) {
mkdir($graphqlPath, 0777, true);
if (!mkdir($graphqlPath, 0777, true) && !is_dir($graphqlPath)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $graphqlPath));
}
}
file_put_contents($classPath, $this->getSchemaClassTemplate($schemaNamespace, $className));

Expand All @@ -73,14 +75,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
if (!file_exists($configFile)) {
$question = new ConfirmationQuestion(sprintf('Config file not found (look at %s). Create it? [Y/n]', $configFile), true);
if (!$inputHelper->ask($input, $output, $question)) {
return;
return Command::SUCCESS;
}

touch($configFile);
}

$originalConfigData = file_get_contents($configFile);
if (strpos($originalConfigData, 'graphql') === false) {
if (!str_contains($originalConfigData, 'graphql')) {
$projectNameSpace = self::PROJECT_NAMESPACE;
$configData = <<<CONFIG
graphql:
Expand All @@ -107,6 +109,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
$output->writeln('GraphQL default route was found.');
}
}

return Command::SUCCESS;
}

/**
Expand All @@ -119,7 +123,7 @@ protected function getMainRouteConfig()
$routerResources = $this->container->get('router')->getRouteCollection()->getResources();
foreach ($routerResources as $resource) {
/** @var FileResource|DirectoryResource $resource */
if (method_exists($resource, 'getResource') && substr($resource->getResource(), -11) == 'routes.yaml') {
if (method_exists($resource, 'getResource') && str_ends_with($resource->getResource(), 'routes.yaml')) {
return $resource->getResource();
}
}
Expand All @@ -136,7 +140,7 @@ protected function graphQLRouteExists()
$routerResources = $this->container->get('router')->getRouteCollection()->getResources();
foreach ($routerResources as $resource) {
/** @var FileResource|DirectoryResource $resource */
if (method_exists($resource, 'getResource') && strpos($resource->getResource(), 'GraphQLController.php') !== false) {
if (method_exists($resource, 'getResource') && str_contains($resource->getResource(), 'GraphQLController.php')) {
return true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion Config/Rule/TypeValidationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function validate($data, $ruleInfo)

if (($ruleInfo == TypeService::TYPE_CALLABLE) && (
is_callable($data) ||
(is_array($data) && count($data) == 2 && substr($data[0], 0, 1) == '@'))
(is_array($data) && count($data) == 2 && str_starts_with((string) $data[0], '@')))
) {
return true;
}
Expand Down
55 changes: 21 additions & 34 deletions Controller/GraphQLController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,42 @@

namespace Youshido\GraphQLBundle\Controller;

use Psr\Container\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Youshido\GraphQLBundle\Exception\UnableToInitializeSchemaServiceException;
use Youshido\GraphQLBundle\Execution\Processor;

class GraphQLController extends AbstractController
{
protected $container;
protected $params;

public function __construct(ContainerInterface $container, ParameterBagInterface $params)
public function __construct(protected ParameterBagInterface $params)
{
$this->container = $container;
$this->params = $params;
}

/**
* @Route("/graphql")
*
* @throws \Exception
*
* @return JsonResponse
*/
public function defaultAction()
public function defaultAction(): JsonResponse
{
try {
$this->initializeSchemaService();
} catch (UnableToInitializeSchemaServiceException $e) {
} catch (UnableToInitializeSchemaServiceException) {
return new JsonResponse(
[['message' => 'Schema class ' . $this->getSchemaClass() . ' does not exist']],
200,
$this->getResponseHeaders()
);
}

if ($this->container->get('request_stack')->getCurrentRequest()->getMethod() == 'OPTIONS') {
if ($this->container->get('request_stack')->getCurrentRequest()->getMethod() === 'OPTIONS') {
return $this->createEmptyResponse();
}

list($queries, $isMultiQueryRequest) = $this->getPayload();
[$queries, $isMultiQueryRequest] = $this->getPayload();

$queryResponses = array_map(function ($queryData) {
return $this->executeQuery($queryData['query'], $queryData['variables']);
}, $queries);
$queryResponses = array_map(fn($queryData) => $this->executeQuery($queryData['query'], $queryData['variables']), $queries);

$response = new JsonResponse($isMultiQueryRequest ? $queryResponses : $queryResponses[0], 200, $this->getParam('graphql.response.headers'));

Expand All @@ -66,12 +54,12 @@ public function defaultAction()
return $response;
}

protected function createEmptyResponse()
protected function createEmptyResponse(): JsonResponse
{
return new JsonResponse([], 200, $this->getResponseHeaders());
}

protected function executeQuery($query, $variables)
protected function executeQuery($query, $variables): array
{
/** @var Processor $processor */
$processor = $this->container->get('graphql.processor');
Expand All @@ -85,25 +73,28 @@ protected function executeQuery($query, $variables)
*
* @throws \Exception
*/
protected function getPayload()
protected function getPayload(): array
{
$request = $this->container->get('request_stack')->getCurrentRequest();
$query = $request->get('query', null);
$variables = $request->get('variables', []);
$isMultiQueryRequest = false;
$queries = [];

$variables = is_string($variables) ? json_decode($variables, true) ?: [] : [];
if (is_string($variables)) {
$decoded = json_decode($variables, true);
$variables = $decoded ?: [];
}

$content = $request->getContent();
if (!empty($content)) {
if ($request->headers->has('Content-Type') && 'application/graphql' == $request->headers->get('Content-Type')) {
if ($request->headers->has('Content-Type') && 'application/graphql' === $request->headers->get('Content-Type')) {
$queries[] = [
'query' => $content,
'variables' => [],
];
} else {
$params = json_decode($content, true);
$params = json_decode((string) $content, true);

if ($params) {
// check for a list of queries
Expand All @@ -114,11 +105,12 @@ protected function getPayload()
}

foreach ($params as $queryParams) {
$query = isset($queryParams['query']) ? $queryParams['query'] : $query;
$query = $queryParams['query'] ?? $query;

if (isset($queryParams['variables'])) {
if (is_string($queryParams['variables'])) {
$variables = json_decode($queryParams['variables'], true) ?: $variables;
$decoded = json_decode($queryParams['variables'], true);
$variables = $decoded ?: $variables;
} else {
$variables = $queryParams['variables'];
}
Expand Down Expand Up @@ -175,12 +167,7 @@ protected function makeSchemaService()
return $this->container->get($schemaClass);
}

$schema = new $schemaClass();
if ($schema instanceof ContainerAwareInterface) {
$schema->setContainer($this->container);
}

return $schema;
return new $schemaClass();
}

/**
Expand All @@ -198,8 +185,8 @@ protected function getSchemaService()
{
$serviceName = $this->getParam('graphql.schema_service');

if (substr($serviceName ?: '', 0, 1) === '@') {
return substr($serviceName, 1, strlen($serviceName) - 1);
if (str_starts_with($serviceName ?: '', '@')) {
return substr($serviceName, 1);
}

return $serviceName;
Expand Down
33 changes: 33 additions & 0 deletions DependencyInjection/Compiler/GraphQLEventListenerPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Youshido\GraphQLBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class GraphQLEventListenerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->has('graphql.event_dispatcher')) {
return;
}
$dispatcher = $container->findDefinition('graphql.event_dispatcher');
foreach ($container->findTaggedServiceIds('graphql.event_listener') as $id => $tags) {
foreach ($tags as $attributes) {
$event = $attributes['event'] ?? null;
$method = $attributes['method'] ?? '__invoke';
if ($event) {
$dispatcher->addMethodCall('addListener', [
$event,
[new Reference($id), $method]
]);
}
}
}
foreach ($container->findTaggedServiceIds('graphql.event_subscriber') as $id => $tags) {
$dispatcher->addMethodCall('addSubscriber', [new Reference($id)]);
}
}
}
2 changes: 1 addition & 1 deletion DependencyInjection/Compiler/GraphQlCompilerPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class GraphQlCompilerPass implements CompilerPassInterface
public function process(ContainerBuilder $container)
{
if ($loggerAlias = $container->getParameter('graphql.logger')) {
if (strpos($loggerAlias, '@') === 0) {
if (str_starts_with($loggerAlias, '@')) {
$loggerAlias = substr($loggerAlias, 1);
}

Expand Down
2 changes: 1 addition & 1 deletion DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Configuration implements ConfigurationInterface
/**
* {@inheritdoc}
*/
public function getConfigTreeBuilder()
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('graphql');
$rootNode = $treeBuilder->getRootNode();
Expand Down
12 changes: 6 additions & 6 deletions DependencyInjection/GraphQLExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

/**
* This is the class that loads and manages your bundle configuration
Expand All @@ -19,13 +19,13 @@ class GraphQLExtension extends Extension
/**
* {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
public function load(array $configs, ContainerBuilder $container): void
{
$configuration = new Configuration();
$this->config = $this->processConfiguration($configuration, $configs);

$preparedHeaders = [];
$headers = $this->config['response']['headers'] ? $this->config['response']['headers'] : $this->getDefaultHeaders();
$headers = $this->config['response']['headers'] ?: $this->getDefaultHeaders();
foreach ($headers as $header) {
$preparedHeaders[$header['name']] = $header['value'];
}
Expand All @@ -46,8 +46,8 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('graphql.security.white_list', $this->config['security']['white_list']);


$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.xml');
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yaml');
}

private function getDefaultHeaders()
Expand Down
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM php:8.4
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
RUN apt update
RUN apt install git -y
RUN apt-get install -y \
libzip-dev \
zip \
&& docker-php-ext-install zip
RUN pecl install xdebug && docker-php-ext-enable xdebug
WORKDIR /var/www/html
ENTRYPOINT ["tail", "-f", "/dev/null"]
17 changes: 2 additions & 15 deletions Event/ResolveEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,16 @@

class ResolveEvent extends GenericEvent
{
/**
* @var Field */
private $field;

/** @var array */
private $astFields;

/** @var mixed|null */
private $resolvedValue;

/**
* Constructor.
*
* @param FieldInterface $field
* @param array $astFields
* @param mixed|null $resolvedValue
*/
public function __construct(FieldInterface $field, array $astFields, $resolvedValue = null)
public function __construct(private readonly FieldInterface $field, private readonly array $astFields, private $resolvedValue = null)
{
$this->field = $field;
$this->astFields = $astFields;
$this->resolvedValue = $resolvedValue;
parent::__construct('ResolveEvent', [$field, $astFields, $resolvedValue]);
parent::__construct('ResolveEvent', [$this->field, $this->astFields, $this->resolvedValue]);
}

/**
Expand Down
9 changes: 4 additions & 5 deletions Execution/Container/SymfonyContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@
namespace Youshido\GraphQLBundle\Execution\Container;


use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Youshido\GraphQL\Execution\Container\ContainerInterface;

class SymfonyContainer implements ContainerInterface, ContainerAwareInterface
class SymfonyContainer implements ContainerInterface
{
use ContainerAwareTrait;
public function __construct(private readonly ?\Symfony\Component\DependencyInjection\ContainerInterface $container = null)
{}

public function get($id)
{
Expand Down Expand Up @@ -68,4 +67,4 @@ public function getSymfonyContainer()
return $this->container;
}

}
}
Loading