• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

overblog / GraphQLBundle / 25302155942

13 Apr 2026 07:00AM UTC coverage: 98.547%. Remained the same
25302155942

push

github

web-flow
fix: make profiler configurable and disable it by default in non-debug mode (#1243)

The profiler.yaml was loaded unconditionally, causing GraphQLCollector's
event listener to run on every GraphQL request in production — parsing
queries and deep-cloning results for data that was never consumed.

Add an `enabled` option to the `profiler` configuration section,
defaulting to `kernel.debug`. This allows explicit control while
ensuring profiler services are only registered in dev/test by default.

6 of 6 new or added lines in 2 files covered. (100.0%)

2 existing lines in 1 file now uncovered.

4545 of 4612 relevant lines covered (98.55%)

76.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.54
/src/DependencyInjection/OverblogGraphQLExtension.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Overblog\GraphQLBundle\DependencyInjection;
6

7
use Closure;
8
use GraphQL\Error\UserError;
9
use GraphQL\Type\Definition\Type;
10
use GraphQL\Type\Schema;
11
use Overblog\GraphQLBundle\CacheWarmer\CompileCacheWarmer;
12
use Overblog\GraphQLBundle\Config\Processor\BuilderProcessor;
13
use Overblog\GraphQLBundle\Definition\Builder\SchemaBuilder;
14
use Overblog\GraphQLBundle\Definition\Resolver\MutationInterface;
15
use Overblog\GraphQLBundle\Definition\Resolver\QueryInterface;
16
use Overblog\GraphQLBundle\Error\ErrorHandler;
17
use Overblog\GraphQLBundle\Error\ExceptionConverter;
18
use Overblog\GraphQLBundle\Error\ExceptionConverterInterface;
19
use Overblog\GraphQLBundle\Error\UserWarning;
20
use Overblog\GraphQLBundle\Event\Events;
21
use Overblog\GraphQLBundle\EventListener\ClassLoaderListener;
22
use Overblog\GraphQLBundle\EventListener\DebugListener;
23
use Overblog\GraphQLBundle\EventListener\ErrorHandlerListener;
24
use Overblog\GraphQLBundle\EventListener\ErrorLoggerListener;
25
use Overblog\GraphQLBundle\Request\Executor;
26
use Symfony\Component\Config\FileLocator;
27
use Symfony\Component\DependencyInjection\ContainerBuilder;
28
use Symfony\Component\DependencyInjection\ContainerInterface;
29
use Symfony\Component\DependencyInjection\Extension\Extension;
30
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
31
use Symfony\Component\DependencyInjection\Reference;
32
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
33

34
use function realpath;
35
use function sprintf;
36

37
final class OverblogGraphQLExtension extends Extension
38
{
39
    public function load(array $configs, ContainerBuilder $container): void
40
    {
41
        $configuration = $this->getConfiguration($configs, $container);
98✔
42
        $config = $this->processConfiguration($configuration, $configs);
98✔
43

44
        $this->loadConfigFiles($config, $container);
98✔
45
        $this->setBatchingMethod($config, $container);
98✔
46
        $this->setServicesAliases($config, $container);
98✔
47
        $this->setSchemaBuilderArguments($config, $container);
98✔
48
        $this->setSchemaArguments($config, $container);
98✔
49
        $this->setErrorHandler($config, $container);
98✔
50
        $this->setSecurity($config, $container);
98✔
51
        $this->setConfigBuilders($config, $container);
98✔
52
        $this->setDebugListener($config, $container);
98✔
53
        $this->setDefinitionParameters($config, $container);
98✔
54
        $this->setProfilerParameters($config, $container);
98✔
55
        $this->setClassLoaderListener($config, $container);
98✔
56
        $this->setCompilerCacheWarmer($config, $container);
98✔
57
        $this->registerForAutoconfiguration($container);
98✔
58
        $this->setDefaultFieldResolver($config, $container);
98✔
59

60
        $container->setParameter($this->getAlias().'.schemas', array_keys($config['definitions']['schema']));
98✔
61
        $container->setParameter($this->getAlias().'.config', $config);
98✔
62
        $container->setParameter($this->getAlias().'.resources_dir', realpath(__DIR__.'/../Resources'));
98✔
63
    }
64

65
    public function getAlias(): string
66
    {
67
        return Configuration::NAME;
98✔
68
    }
69

70
    public function getConfiguration(array $config, ContainerBuilder $container): Configuration
71
    {
72
        return new Configuration(
98✔
73
            // @phpstan-ignore-next-line
74
            $container->getParameter('kernel.debug')
98✔
75
        );
98✔
76
    }
77

78
    private function loadConfigFiles(array $config, ContainerBuilder $container): void
79
    {
80
        $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
98✔
81
        $loader->load('services.yaml');
98✔
82
        $loader->load('commands.yaml');
98✔
83
        $loader->load('listeners.yaml');
98✔
84
        $loader->load('graphql_types.yaml');
98✔
85
        $loader->load('graphql_resolvers.yaml');
98✔
86
        $loader->load('expression_language_functions.yaml');
98✔
87
        $loader->load('definition_config_processors.yaml');
98✔
88
        $loader->load('aliases.yaml');
98✔
89

90
        if ($config['profiler']['enabled']) {
98✔
91
            $loader->load('profiler.yaml');
86✔
92
        }
93
    }
94

95
    private function registerForAutoconfiguration(ContainerBuilder $container): void
96
    {
97
        $container->registerForAutoconfiguration(MutationInterface::class)
98✔
98
            ->addTag('overblog_graphql.mutation');
98✔
99

100
        $container->registerForAutoconfiguration(QueryInterface::class)
98✔
101
            ->addTag('overblog_graphql.query');
98✔
102

103
        $container->registerForAutoconfiguration(Type::class)
98✔
104
            ->addTag('overblog_graphql.type');
98✔
105
    }
106

107
    private function setDefaultFieldResolver(array $config, ContainerBuilder $container): void
108
    {
109
        $container->setAlias($this->getAlias().'.default_field_resolver', $config['definitions']['default_field_resolver']);
98✔
110
    }
111

112
    private function setCompilerCacheWarmer(array $config, ContainerBuilder $container): void
113
    {
114
        $container->register(CompileCacheWarmer::class)
98✔
115
            ->setArguments([
98✔
116
                new Reference($this->getAlias().'.cache_compiler'),
98✔
117
                $config['definitions']['auto_compile'],
98✔
118
            ])
98✔
119
            ->addTag('kernel.cache_warmer', ['priority' => 50])
98✔
120
        ;
98✔
121
    }
122

123
    private function setClassLoaderListener(array $config, ContainerBuilder $container): void
124
    {
125
        $container->setParameter($this->getAlias().'.use_classloader_listener', $config['definitions']['use_classloader_listener']);
98✔
126
        if ($config['definitions']['use_classloader_listener']) {
98✔
127
            $definition = $container->register(
96✔
128
                $this->getAlias().'.event_listener.classloader_listener',
96✔
129
                ClassLoaderListener::class
96✔
130
            );
96✔
131
            $definition->setPublic(true);
96✔
132
            $definition->setArguments([new Reference($this->getAlias().'.cache_compiler')]);
96✔
133
            $definition->addTag('kernel.event_listener', ['event' => 'kernel.request', 'method' => 'load', 'priority' => 255]);
96✔
134
            $definition->addTag('kernel.event_listener', ['event' => 'console.command', 'method' => 'load', 'priority' => 255]);
96✔
135
        }
136
    }
137

138
    private function setDefinitionParameters(array $config, ContainerBuilder $container): void
139
    {
140
        // generator and config
141
        $container->setParameter($this->getAlias().'.class_namespace', $config['definitions']['class_namespace']);
98✔
142
        $container->setParameter($this->getAlias().'.cache_dir', $config['definitions']['cache_dir']);
98✔
143
        $container->setParameter($this->getAlias().'.cache_dir_permissions', $config['definitions']['cache_dir_permissions']);
98✔
144
        $container->setParameter($this->getAlias().'.argument_class', $config['definitions']['argument_class']);
98✔
145
    }
146

147
    private function setProfilerParameters(array $config, ContainerBuilder $container): void
148
    {
149
        if (!$config['profiler']['enabled']) {
98✔
150
            return;
12✔
151
        }
152

153
        $container->setParameter($this->getAlias().'.profiler.query_match', $config['profiler']['query_match']);
86✔
154
    }
155

156
    private function setBatchingMethod(array $config, ContainerBuilder $container): void
157
    {
158
        $container->setParameter($this->getAlias().'.batching_method', $config['batching_method']);
98✔
159
    }
160

161
    private function setDebugListener(array $config, ContainerBuilder $container): void
162
    {
163
        if ($config['definitions']['show_debug_info']) {
98✔
164
            $definition = $container->register(DebugListener::class);
3✔
165
            $definition->addTag('kernel.event_listener', ['event' => Events::PRE_EXECUTOR, 'method' => 'onPreExecutor']);
3✔
166
            $definition->addTag('kernel.event_listener', ['event' => Events::POST_EXECUTOR, 'method' => 'onPostExecutor']);
3✔
167
        }
168
    }
169

170
    private function setConfigBuilders(array $config, ContainerBuilder $container): void
171
    {
172
        foreach (BuilderProcessor::BUILDER_TYPES as $type) {
98✔
173
            if (!empty($config['definitions']['builders'][$type])) {
98✔
174
                foreach ($config['definitions']['builders'][$type] as $params) {
10✔
175
                    $container->addObjectResource($params['class']);
10✔
176
                    BuilderProcessor::addBuilderClass($params['alias'], $type, $params['class']);
10✔
177
                }
178
            }
179
        }
180
    }
181

182
    private function setSecurity(array $config, ContainerBuilder $container): void
183
    {
184
        $executorDefinition = $container->getDefinition(Executor::class);
98✔
185
        if ($config['security']['enable_introspection']) {
98✔
186
            $executorDefinition->addMethodCall('enableIntrospectionQuery');
97✔
187
        } else {
188
            $executorDefinition->addMethodCall('disableIntrospectionQuery');
3✔
189
        }
190

191
        foreach ($config['security'] as $key => $value) {
98✔
192
            $container->setParameter(sprintf('%s.%s', $this->getAlias(), $key), $value);
98✔
193
        }
194
    }
195

196
    private function setErrorHandler(array $config, ContainerBuilder $container): void
197
    {
198
        if (!$config['errors_handler']['enabled']) {
98✔
UNCOV
199
            return;
×
200
        }
201

202
        $container->register(ExceptionConverter::class)
98✔
203
            ->setArgument(0, $this->buildExceptionMap($config['errors_handler']['exceptions']))
98✔
204
            ->setArgument(1, $config['errors_handler']['map_exceptions_to_parent']);
98✔
205

206
        $container->register(ErrorHandler::class)
98✔
207
            ->setArgument(0, new Reference(EventDispatcherInterface::class))
98✔
208
            ->setArgument(1, new Reference(ExceptionConverterInterface::class))
98✔
209
            ->setArgument(2, $config['errors_handler']['internal_error_message']);
98✔
210

211
        $container->register(ErrorHandlerListener::class)
98✔
212
            ->setArgument(0, new Reference(ErrorHandler::class))
98✔
213
            ->setArgument(1, $config['errors_handler']['rethrow_internal_exceptions'])
98✔
214
            ->setArgument(2, $config['errors_handler']['debug'])
98✔
215
            ->addTag('kernel.event_listener', ['event' => Events::POST_EXECUTOR, 'method' => 'onPostExecutor']);
98✔
216

217
        $container->setAlias(ExceptionConverterInterface::class, ExceptionConverter::class);
98✔
218

219
        if ($config['errors_handler']['log']) {
98✔
220
            $loggerServiceId = $config['errors_handler']['logger_service'];
98✔
221
            $invalidBehavior = ErrorLoggerListener::DEFAULT_LOGGER_SERVICE === $loggerServiceId ? ContainerInterface::NULL_ON_INVALID_REFERENCE : ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
98✔
222

223
            $container->register(ErrorLoggerListener::class)
98✔
224
                ->setPublic(true)
98✔
225
                ->addArgument(new Reference($loggerServiceId, $invalidBehavior))
98✔
226
                ->addTag('kernel.event_listener', ['event' => Events::ERROR_FORMATTING, 'method' => 'onErrorFormatting']);
98✔
227
        }
228
    }
229

230
    private function setSchemaBuilderArguments(array $config, ContainerBuilder $container): void
231
    {
232
        $container->getDefinition(SchemaBuilder::class)
98✔
233
            ->replaceArgument(1, $config['definitions']['config_validation']);
98✔
234
    }
235

236
    private function setSchemaArguments(array $config, ContainerBuilder $container): void
237
    {
238
        if (!isset($config['definitions']['schema'])) {
98✔
UNCOV
239
            return;
×
240
        }
241

242
        $executorDefinition = $container->getDefinition(Executor::class);
98✔
243

244
        foreach ($config['definitions']['schema'] as $schemaName => $schemaConfig) {
98✔
245
            // builder
246
            $schemaBuilderID = sprintf('%s.schema_builder_%s', $this->getAlias(), $schemaName);
82✔
247
            $definition = $container->register($schemaBuilderID, Closure::class);
82✔
248
            $definition->setFactory([new Reference('overblog_graphql.schema_builder'), 'getBuilder']);
82✔
249
            $definition->setArguments([
82✔
250
                $schemaName,
82✔
251
                $schemaConfig['query'],
82✔
252
                $schemaConfig['mutation'],
82✔
253
                $schemaConfig['subscription'],
82✔
254
                $schemaConfig['types'],
82✔
255
            ]);
82✔
256
            // schema
257
            $schemaID = sprintf('%s.schema_%s', $this->getAlias(), $schemaName);
82✔
258
            $definition = $container->register($schemaID, Schema::class);
82✔
259
            $definition->setFactory([new Reference($schemaBuilderID), 'call']);
82✔
260

261
            $executorDefinition->addMethodCall('addSchemaBuilder', [$schemaName, new Reference($schemaBuilderID)]);
82✔
262
        }
263
    }
264

265
    private function setServicesAliases(array $config, ContainerBuilder $container): void
266
    {
267
        if (isset($config['services'])) {
98✔
268
            foreach ($config['services'] as $name => $id) {
98✔
269
                $alias = sprintf('%s.%s', $this->getAlias(), $name);
98✔
270
                $container->setAlias($alias, $id);
98✔
271
            }
272
        }
273
    }
274

275
    /**
276
     * Returns a list of custom exceptions mapped to error/warning classes.
277
     *
278
     * @param array<string, array<string>> $exceptionConfig
279
     *
280
     * @return array<string, string> Custom exception map, [exception => UserError/UserWarning]
281
     */
282
    private function buildExceptionMap(array $exceptionConfig): array
283
    {
284
        $exceptionMap = [];
98✔
285
        $errorsMapping = [
98✔
286
            'errors' => UserError::class,
98✔
287
            'warnings' => UserWarning::class,
98✔
288
        ];
98✔
289

290
        foreach ($exceptionConfig as $type => $exceptionList) {
98✔
291
            foreach ($exceptionList as $exception) {
98✔
292
                $exceptionMap[$exception] = $errorsMapping[$type];
5✔
293
            }
294
        }
295

296
        return $exceptionMap;
98✔
297
    }
298
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc