Completed
Pull Request — master (#391)
by David
02:28
created

FOSHttpCacheExtension::parseResponseMatcher()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 24
Code Lines 16

Duplication

Lines 12
Ratio 50 %

Code Coverage

Tests 13
CRAP Score 5

Importance

Changes 0
Metric Value
dl 12
loc 24
ccs 13
cts 13
cp 1
rs 8.5125
c 0
b 0
f 0
cc 5
eloc 16
nc 5
nop 2
crap 5
1
<?php
2
3
/*
4
 * This file is part of the FOSHttpCacheBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\HttpCacheBundle\DependencyInjection;
13
14
use FOS\HttpCache\ProxyClient\HttpDispatcher;
15
use FOS\HttpCacheBundle\DependencyInjection\Compiler\HashGeneratorPass;
16
use FOS\HttpCacheBundle\Http\ResponseMatcher\ExpressionResponseMatcher;
17
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
18
use Symfony\Component\Config\FileLocator;
19
use Symfony\Component\DependencyInjection\ChildDefinition;
20
use Symfony\Component\DependencyInjection\ContainerBuilder;
21
use Symfony\Component\DependencyInjection\Definition;
22
use Symfony\Component\DependencyInjection\DefinitionDecorator;
23
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
24
use Symfony\Component\DependencyInjection\Reference;
25
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
26
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
27
28
/**
29
 * {@inheritdoc}
30
 */
31
class FOSHttpCacheExtension extends Extension
32
{
33
    /**
34
     * {@inheritdoc}
35
     */
36 26
    public function getConfiguration(array $config, ContainerBuilder $container)
37
    {
38 26
        return new Configuration($container->getParameter('kernel.debug'));
39
    }
40
41
    /**
42
     * {@inheritdoc}
43
     */
44 26
    public function load(array $configs, ContainerBuilder $container)
45
    {
46 26
        $configuration = $this->getConfiguration($configs, $container);
47 26
        $config = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 46 can be null; however, Symfony\Component\Depend...:processConfiguration() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
48
49 26
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
50 26
        $loader->load('matcher.xml');
51
52 26
        if ($config['debug']['enabled'] || (!empty($config['cache_control']))) {
53 6
            $debugHeader = $config['debug']['enabled'] ? $config['debug']['header'] : false;
54 6
            $container->setParameter($this->getAlias().'.debug_header', $debugHeader);
55 6
            $loader->load('cache_control_listener.xml');
56
        }
57
58 26
        $this->loadCacheable($container, $config['cacheable']);
59
60 26
        if (!empty($config['cache_control'])) {
61 6
            $this->loadCacheControl($container, $config['cache_control']);
62
        }
63
64 25
        if (isset($config['proxy_client'])) {
65 17
            $this->loadProxyClient($container, $loader, $config['proxy_client']);
66
        }
67
68 24
        if (isset($config['test'])) {
69 1
            $this->loadTest($container, $loader, $config['test']);
70
        }
71
72 24
        if ($config['cache_manager']['enabled']) {
73 17
            if (array_key_exists('custom_proxy_client', $config['cache_manager'])) {
74
                // overwrite the previously set alias, if a proxy client was also configured
75 1
                $container->setAlias(
76 1
                    $this->getAlias().'.default_proxy_client',
77 1
                    $config['cache_manager']['custom_proxy_client']
78
                );
79
            }
80 17
            if ('auto' === $config['cache_manager']['generate_url_type']) {
81 17
                if (array_key_exists('custom_proxy_client', $config['cache_manager'])) {
82 1
                    $generateUrlType = UrlGeneratorInterface::ABSOLUTE_URL;
83
                } else {
84 16
                    $defaultClient = $this->getDefaultProxyClient($config['proxy_client']);
85 16
                    if ($defaultClient !== 'noop'
86 16
                        && array_key_exists('base_url', $config['proxy_client'][$defaultClient])) {
87
                        $generateUrlType = UrlGeneratorInterface::ABSOLUTE_PATH;
88
                    } else {
89 17
                        $generateUrlType = UrlGeneratorInterface::ABSOLUTE_URL;
90
                    }
91
                }
92
            } else {
93
                $generateUrlType = $config['cache_manager']['generate_url_type'];
94
            }
95 17
            $container->setParameter($this->getAlias().'.cache_manager.generate_url_type', $generateUrlType);
96 17
            $loader->load('cache_manager.xml');
97
        }
98
99 24
        if ($config['tags']['enabled']) {
100 17
            $this->loadCacheTagging(
101 17
                $container,
102 17
                $loader,
103 17
                $config['tags'],
104 17
                array_key_exists('proxy_client', $config)
105 16
                    ? $this->getDefaultProxyClient($config['proxy_client'])
106 17
                    : 'custom'
107
            );
108
        } else {
109 7
            $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', false);
110
        }
111
112 23
        if ($config['invalidation']['enabled']) {
113 16
            $loader->load('invalidation_listener.xml');
114
115 16
            if (!empty($config['invalidation']['expression_language'])) {
116
                $container->setAlias(
117
                    $this->getAlias().'.invalidation.expression_language',
118
                    $config['invalidation']['expression_language']
119
                );
120
            }
121
122 16
            if (!empty($config['invalidation']['rules'])) {
123 2
                $this->loadInvalidatorRules($container, $config['invalidation']['rules']);
124
            }
125
        }
126
127 23
        if ($config['user_context']['enabled']) {
128 4
            $this->loadUserContext($container, $loader, $config['user_context']);
129
        }
130
131 23
        if (!empty($config['flash_message']) && $config['flash_message']['enabled']) {
132 2
            unset($config['flash_message']['enabled']);
133 2
            $container->setParameter($this->getAlias().'.event_listener.flash_message.options', $config['flash_message']);
134
135 2
            $loader->load('flash_message.xml');
136
        }
137 23
    }
138
139 26
    private function loadCacheable(ContainerBuilder $container, array $config)
140
    {
141 26
        $definition = $container->getDefinition($this->getAlias().'.response_matcher.cacheable');
142
143
        // Change CacheableResponseMatcher to ExpressionResponseMatcher
144 26
        if ($config['response']['expression']) {
145
            $definition->setClass(ExpressionResponseMatcher::class)
146
                ->setArguments([$config['response']['expression']]);
147
        } else {
148 26
            $container->setParameter(
149 26
                $this->getAlias().'.cacheable.response.additional_status',
150 26
                $config['response']['additional_status']
151
            );
152
        }
153 26
    }
154
155
    /**
156
     * @param ContainerBuilder $container
157
     * @param array            $config
158
     *
159
     * @throws InvalidConfigurationException
160
     */
161 6
    private function loadCacheControl(ContainerBuilder $container, array $config)
162
    {
163 6
        $controlDefinition = $container->getDefinition($this->getAlias().'.event_listener.cache_control');
164
165 6
        foreach ($config['rules'] as $rule) {
166 6
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
167
168 6
            if ('default' === $rule['headers']['overwrite']) {
169 6
                $rule['headers']['overwrite'] = $config['defaults']['overwrite'];
170
            }
171
172 6
            $controlDefinition->addMethodCall('addRule', [$ruleMatcher, $rule['headers']]);
173
        }
174 5
    }
175
176
    /**
177
     * Parse one cache control rule match configuration.
178
     *
179
     * @param ContainerBuilder $container
180
     * @param array            $match     Request and response match criteria
181
     *
182
     * @return Reference pointing to a rule matcher service
183
     */
184 6
    private function parseRuleMatcher(ContainerBuilder $container, array $match)
185
    {
186 6
        $requestMatcher = $this->parseRequestMatcher($container, $match);
187 6
        $responseMatcher = $this->parseResponseMatcher($container, $match);
188
189 6
        $signature = serialize([(string) $requestMatcher, (string) $responseMatcher]);
190 6
        $id = $this->getAlias().'.cache_control.rule_matcher.'.md5($signature);
191
192 6
        if ($container->hasDefinition($id)) {
193 1
            throw new InvalidConfigurationException('Duplicate match criteria. Would be hidden by a previous rule. match: '.json_encode($match));
194
        }
195
196
        $container
197 6
            ->setDefinition($id, $this->createChildDefinition($this->getAlias().'.rule_matcher'))
198 6
            ->replaceArgument(0, $requestMatcher)
199 6
            ->replaceArgument(1, $responseMatcher)
200
        ;
201
202 6
        return new Reference($id);
203
    }
204
205
    /**
206
     * Used for cache control, tag and invalidation rules.
207
     *
208
     * @param ContainerBuilder $container
209
     * @param array            $match
210
     *
211
     * @return Reference to the request matcher
212
     */
213 8
    private function parseRequestMatcher(ContainerBuilder $container, array $match)
214
    {
215 8
        $match['ips'] = (empty($match['ips'])) ? null : $match['ips'];
216
217
        $arguments = [
218 8
            $match['path'],
219 8
            $match['host'],
220 8
            $match['methods'],
221 8
            $match['ips'],
222 8
            $match['attributes'],
223
        ];
224 8
        $serialized = serialize($arguments);
225 8
        $id = $this->getAlias().'.request_matcher.'.md5($serialized).sha1($serialized);
226
227 8
        if (!$container->hasDefinition($id)) {
228
            $container
229 8
                ->setDefinition($id, $this->createChildDefinition($this->getAlias().'.request_matcher'))
230 8
                ->setArguments($arguments)
231
            ;
232
233 8
            if (!empty($match['query_string'])) {
234
                $container->getDefinition($id)->addMethodCall('setQueryString', [$match['query_string']]);
235
            }
236
        }
237
238 8
        return new Reference($id);
239
    }
240
241
    /**
242
     * Used only for cache control rules.
243
     *
244
     * @param ContainerBuilder $container
245
     * @param array            $config
246
     *
247
     * @return Reference to the correct response matcher service
248
     */
249 6
    private function parseResponseMatcher(ContainerBuilder $container, array $config)
250
    {
251 6
        if (!empty($config['additional_response_status'])) {
252 1
            $id = $this->getAlias().'cache_control.expression.'.md5(serialize($config['additional_response_status']));
253 1 View Code Duplication
            if (!$container->hasDefinition($id)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
                $container
255 1
                    ->setDefinition($id, $this->createChildDefinition($this->getAlias().'.response_matcher.cache_control.cacheable_response'))
256 1
                    ->setArguments([$config['additional_response_status']])
257
                ;
258
            }
259 5
        } elseif (!empty($config['match_response'])) {
260 2
            $id = $this->getAlias().'cache_control.match_response.'.md5($config['match_response']);
261 2 View Code Duplication
            if (!$container->hasDefinition($id)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
262
                $container
263 2
                    ->setDefinition($id, $this->createChildDefinition($this->getAlias().'.response_matcher.cache_control.expression'))
264 2
                    ->replaceArgument(0, $config['match_response'])
265
                ;
266
            }
267
        } else {
268 3
            $id = $this->getAlias().'.response_matcher.cacheable';
269
        }
270
271 6
        return new Reference($id);
272
    }
273
274 4
    private function loadUserContext(ContainerBuilder $container, XmlFileLoader $loader, array $config)
275
    {
276 4
        $loader->load('user_context.xml');
277
278 4
        $container->getDefinition($this->getAlias().'.user_context.request_matcher')
279 4
            ->replaceArgument(0, $config['match']['accept'])
280 4
            ->replaceArgument(1, $config['match']['method']);
281
282 4
        $container->setParameter($this->getAlias().'.event_listener.user_context.options', [
283 4
            'user_identifier_headers' => $config['user_identifier_headers'],
284 4
            'user_hash_header' => $config['user_hash_header'],
285 4
            'ttl' => $config['hash_cache_ttl'],
286 4
            'add_vary_on_hash' => $config['always_vary_on_context_hash'],
287
        ]);
288 4
        $container->getDefinition($this->getAlias().'.event_listener.user_context')
289 4
            ->replaceArgument(0, new Reference($config['match']['matcher_service']));
290
291 4
        $container->getDefinition($this->getAlias().'.user_context.anonymous_request_matcher')
292 4
            ->replaceArgument(0, $config['user_identifier_headers']);
293
294 4
        if ($config['logout_handler']['enabled']) {
295 4
            $container->getDefinition($this->getAlias().'.user_context.logout_handler')
296 4
                ->replaceArgument(1, $config['user_identifier_headers'])
297 4
                ->replaceArgument(2, $config['match']['accept']);
298
        } else {
299
            $container->removeDefinition($this->getAlias().'.user_context.logout_handler');
300
        }
301
302 4
        if ($config['role_provider']) {
303 2
            $container->getDefinition($this->getAlias().'.user_context.role_provider')
304 2
                ->addTag(HashGeneratorPass::TAG_NAME)
305 2
                ->setAbstract(false);
306
        }
307 4
    }
308
309 17
    private function loadProxyClient(ContainerBuilder $container, XmlFileLoader $loader, array $config)
310
    {
311 17
        if (isset($config['varnish'])) {
312 13
            $this->loadVarnish($container, $loader, $config['varnish']);
313
        }
314 16
        if (isset($config['nginx'])) {
315 2
            $this->loadNginx($container, $loader, $config['nginx']);
316
        }
317 16
        if (isset($config['symfony'])) {
318 1
            $this->loadSymfony($container, $loader, $config['symfony']);
319
        }
320 16
        if (isset($config['noop'])) {
321 1
            $loader->load('noop.xml');
322
        }
323
324 16
        $container->setAlias(
325 16
            $this->getAlias().'.default_proxy_client',
326 16
            $this->getAlias().'.proxy_client.'.$this->getDefaultProxyClient($config)
327
        );
328 16
    }
329
330
    /**
331
     * Define the http dispatcher service for the proxy client $name.
332
     *
333
     * @param ContainerBuilder $container
334
     * @param array            $config
335
     * @param string           $serviceName
336
     */
337 16
    private function createHttpDispatcherDefinition(ContainerBuilder $container, array $config, $serviceName)
338
    {
339 16
        foreach ($config['servers'] as $url) {
340 16
            $this->validateUrl($url, 'Not a valid Varnish server address: "%s"');
341
        }
342 16
        if (!empty($config['base_url'])) {
343 16
            $baseUrl = $this->prefixSchema($config['base_url']);
344 16
            $this->validateUrl($baseUrl, 'Not a valid base path: "%s"');
345
        } else {
346
            $baseUrl = null;
347
        }
348 15
        $httpClient = null;
349 15
        if ($config['http_client']) {
350 1
            $httpClient = new Reference($config['http_client']);
351
        }
352
353 15
        $definition = new Definition(HttpDispatcher::class, [
354 15
            $config['servers'],
355 15
            $baseUrl,
356 15
            $httpClient,
357
        ]);
358
359 15
        $container->setDefinition($serviceName, $definition);
360 15
    }
361
362 13
    private function loadVarnish(ContainerBuilder $container, XmlFileLoader $loader, array $config)
363
    {
364 13
        $this->createHttpDispatcherDefinition($container, $config['http'], $this->getAlias().'.proxy_client.varnish.http_dispatcher');
365
        $options = [
366 12
            'tags_header' => $config['tags_header'],
367
        ];
368 12
        if (!empty($config['header_length'])) {
369
            $options['header_length'] = $config['header_length'];
370
        }
371 12
        if (!empty($config['default_ban_headers'])) {
372
            $options['default_ban_headers'] = $config['default_ban_headers'];
373
        }
374 12
        $container->setParameter($this->getAlias().'.proxy_client.varnish.options', $options);
375
376 12
        $loader->load('varnish.xml');
377 12
    }
378
379 2
    private function loadNginx(ContainerBuilder $container, XmlFileLoader $loader, array $config)
380
    {
381 2
        $this->createHttpDispatcherDefinition($container, $config['http'], $this->getAlias().'.proxy_client.nginx.http_dispatcher');
382 2
        $container->setParameter($this->getAlias().'.proxy_client.nginx.options', [
383 2
            'purge_location' => $config['purge_location'],
384
        ]);
385 2
        $loader->load('nginx.xml');
386 2
    }
387
388 1
    private function loadSymfony(ContainerBuilder $container, XmlFileLoader $loader, array $config)
389
    {
390 1
        $this->createHttpDispatcherDefinition($container, $config['http'], $this->getAlias().'.proxy_client.symfony.http_dispatcher');
391 1
        $loader->load('symfony.xml');
392 1
    }
393
394
    /**
395
     * @param ContainerBuilder $container
396
     * @param XmlFileLoader    $loader
397
     * @param array            $config    Configuration section for the tags node
398
     * @param string           $client    Name of the client used with the cache manager,
399
     *                                    "custom" when a custom client is used
400
     */
401 17
    private function loadCacheTagging(ContainerBuilder $container, XmlFileLoader $loader, array $config, $client)
402
    {
403 17
        if ('auto' === $config['enabled'] && 'varnish' !== $client) {
404 4
            $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', false);
405
406 4
            return;
407
        }
408 13
        if (!in_array($client, ['varnish', 'custom', 'noop'])) {
409 1
            throw new InvalidConfigurationException(sprintf('You can not enable cache tagging with the %s client', $client));
410
        }
411
412 12
        $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', true);
413 12
        $container->setParameter($this->getAlias().'.tag_handler.response_header', $config['response_header']);
414 12
        $container->setParameter($this->getAlias().'.tag_handler.strict', $config['strict']);
415 12
        $loader->load('cache_tagging.xml');
416
417 12
        if (!empty($config['expression_language'])) {
418
            $container->setAlias(
419
                $this->getAlias().'.tag_handler.expression_language',
420
                $config['expression_language']
421
            );
422
        }
423
424 12
        if (!empty($config['rules'])) {
425 2
            $this->loadTagRules($container, $config['rules']);
426
        }
427 12
    }
428
429 1
    private function loadTest(ContainerBuilder $container, XmlFileLoader $loader, array $config)
430
    {
431 1
        $container->setParameter($this->getAlias().'.test.cache_header', $config['cache_header']);
432
433 1
        if ($config['proxy_server']) {
434 1
            $this->loadProxyServer($container, $loader, $config['proxy_server']);
435
        }
436 1
    }
437
438 1
    private function loadProxyServer(ContainerBuilder $container, XmlFileLoader $loader, array $config)
439
    {
440 1
        if (isset($config['varnish'])) {
441 1
            $this->loadVarnishProxyServer($container, $loader, $config['varnish']);
442
        }
443
444 1
        if (isset($config['nginx'])) {
445
            $this->loadNginxProxyServer($container, $loader, $config['varnish']);
446
        }
447
448 1
        $container->setAlias(
449 1
            $this->getAlias().'.test.default_proxy_server',
450 1
            $this->getAlias().'.test.proxy_server.'.$this->getDefaultProxyClient($config)
451
        );
452 1
    }
453
454 1
    private function loadVarnishProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
455
    {
456 1
        $loader->load('varnish_proxy.xml');
457 1
        foreach ($config as $key => $value) {
458 1
            $container->setParameter(
459 1
                $this->getAlias().'.test.proxy_server.varnish.'.$key,
460 1
                $value
461
            );
462
        }
463 1
    }
464
465
    private function loadNginxProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
466
    {
467
        $loader->load('nginx_proxy.xml');
468
        foreach ($config as $key => $value) {
469
            $container->setParameter(
470
                $this->getAlias().'.test.proxy_server.nginx.'.$key,
471
                $value
472
            );
473
        }
474
    }
475
476 2
    private function loadTagRules(ContainerBuilder $container, array $config)
477
    {
478 2
        $tagDefinition = $container->getDefinition($this->getAlias().'.event_listener.tag');
479
480 2
        foreach ($config as $rule) {
481 2
            $ruleMatcher = $this->parseRequestMatcher($container, $rule['match']);
482
483
            $tags = [
484 2
                'tags' => $rule['tags'],
485 2
                'expressions' => $rule['tag_expressions'],
486
            ];
487
488 2
            $tagDefinition->addMethodCall('addRule', [$ruleMatcher, $tags]);
489
        }
490 2
    }
491
492 2
    private function loadInvalidatorRules(ContainerBuilder $container, array $config)
493
    {
494 2
        $tagDefinition = $container->getDefinition($this->getAlias().'.event_listener.invalidation');
495
496 2
        foreach ($config as $rule) {
497 2
            $ruleMatcher = $this->parseRequestMatcher($container, $rule['match']);
498 2
            $tagDefinition->addMethodCall('addRule', [$ruleMatcher, $rule['routes']]);
499
        }
500 2
    }
501
502 16
    private function validateUrl($url, $msg)
503
    {
504 16
        $prefixed = $this->prefixSchema($url);
505
506 16
        if (!$parts = parse_url($prefixed)) {
507 1
            throw new InvalidConfigurationException(sprintf($msg, $url));
508
        }
509 16
    }
510
511 16
    private function prefixSchema($url)
512
    {
513 16
        if (false === strpos($url, '://')) {
514 16
            $url = sprintf('%s://%s', 'http', $url);
515
        }
516
517 16
        return $url;
518
    }
519
520 16
    private function getDefaultProxyClient(array $config)
521
    {
522 16
        if (isset($config['default'])) {
523
            return $config['default'];
524
        }
525
526 16
        if (isset($config['varnish'])) {
527 12
            return 'varnish';
528
        }
529
530 4
        if (isset($config['nginx'])) {
531 2
            return 'nginx';
532
        }
533
534 2
        if (isset($config['symfony'])) {
535 1
            return 'symfony';
536
        }
537
538 1
        if (isset($config['noop'])) {
539 1
            return 'noop';
540
        }
541
542
        throw new InvalidConfigurationException('No proxy client configured');
543
    }
544
545
    /**
546
     * Build the child definition with fallback for Symfony versions < 3.3.
547
     *
548
     * @param string $id Id of the service to extend
549
     *
550
     * @return ChildDefinition|DefinitionDecorator
551
     */
552 8
    private function createChildDefinition($id)
553
    {
554 8
        if (class_exists(ChildDefinition::class)) {
555
            return new ChildDefinition($id);
556
        }
557
558 8
        return new DefinitionDecorator($id);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
559
    }
560
}
561