vendor/friendsofsymfony/http-cache-bundle/src/DependencyInjection/Configuration.php line 198

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the FOSHttpCacheBundle package.
  4.  *
  5.  * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace FOS\HttpCacheBundle\DependencyInjection;
  11. use FOS\HttpCache\ProxyClient\Varnish;
  12. use FOS\HttpCache\SymfonyCache\PurgeListener;
  13. use FOS\HttpCache\SymfonyCache\PurgeTagsListener;
  14. use FOS\HttpCache\TagHeaderFormatter\TagHeaderFormatter;
  15. use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
  16. use Symfony\Component\Config\Definition\Builder\NodeBuilder;
  17. use Symfony\Component\Config\Definition\Builder\NodeDefinition;
  18. use Symfony\Component\Config\Definition\Builder\TreeBuilder;
  19. use Symfony\Component\Config\Definition\ConfigurationInterface;
  20. use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
  21. use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
  22. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  23. /**
  24.  * This class contains the configuration information for the bundle.
  25.  *
  26.  * This information is solely responsible for how the different configuration
  27.  * sections are normalized, and merged.
  28.  *
  29.  * @author David de Boer <david@driebit.nl>
  30.  * @author David Buchmann <mail@davidbu.ch>
  31.  */
  32. class Configuration implements ConfigurationInterface
  33. {
  34.     /**
  35.      * @var bool
  36.      */
  37.     private $debug;
  38.     /**
  39.      * @param bool $debug Whether to use the debug mode
  40.      */
  41.     public function __construct($debug)
  42.     {
  43.         $this->debug $debug;
  44.     }
  45.     /**
  46.      * {@inheritdoc}
  47.      */
  48.     public function getConfigTreeBuilder()
  49.     {
  50.         $treeBuilder = new TreeBuilder('fos_http_cache');
  51.         // Keep compatibility with symfony/config < 4.2
  52.         if (!method_exists($treeBuilder'getRootNode')) {
  53.             $rootNode $treeBuilder->root('fos_http_cache');
  54.         } else {
  55.             $rootNode $treeBuilder->getRootNode();
  56.         }
  57.         $rootNode
  58.             ->validate()
  59.                 ->ifTrue(function ($v) {
  60.                     return $v['cache_manager']['enabled']
  61.                         && !isset($v['proxy_client'])
  62.                         && !isset($v['cache_manager']['custom_proxy_client'])
  63.                     ;
  64.                 })
  65.                 ->then(function ($v) {
  66.                     if ('auto' === $v['cache_manager']['enabled']) {
  67.                         $v['cache_manager']['enabled'] = false;
  68.                         return $v;
  69.                     }
  70.                     throw new InvalidConfigurationException('You need to configure a proxy_client or specify a custom_proxy_client to use the cache_manager.');
  71.                 })
  72.             ->end()
  73.             ->validate()
  74.                 ->ifTrue(function ($v) {
  75.                     return $v['tags']['enabled'] && !$v['cache_manager']['enabled'];
  76.                 })
  77.                 ->then(function ($v) {
  78.                     if ('auto' === $v['tags']['enabled']) {
  79.                         $v['tags']['enabled'] = false;
  80.                         return $v;
  81.                     }
  82.                     throw new InvalidConfigurationException('You need to configure a proxy_client to get the cache_manager needed for tag handling.');
  83.                 })
  84.             ->end()
  85.             ->validate()
  86.                 ->ifTrue(function ($v) {
  87.                     return $v['invalidation']['enabled'] && !$v['cache_manager']['enabled'];
  88.                 })
  89.                 ->then(function ($v) {
  90.                     if ('auto' === $v['invalidation']['enabled']) {
  91.                         $v['invalidation']['enabled'] = false;
  92.                         return $v;
  93.                     }
  94.                     throw new InvalidConfigurationException('You need to configure a proxy_client to get the cache_manager needed for invalidation handling.');
  95.                 })
  96.             ->end()
  97.             ->validate()
  98.                 ->ifTrue(
  99.                     function ($v) {
  100.                         return false !== $v['user_context']['logout_handler']['enabled'];
  101.                     }
  102.                 )
  103.                 ->then(function ($v) {
  104.                     if (isset($v['cache_manager']['custom_proxy_client'])) {
  105.                         $v['user_context']['logout_handler']['enabled'] = true;
  106.                         return $v;
  107.                     }
  108.                     if (isset($v['proxy_client']['default'])
  109.                         && in_array($v['proxy_client']['default'], ['varnish''symfony''noop'])
  110.                     ) {
  111.                         $v['user_context']['logout_handler']['enabled'] = true;
  112.                         return $v;
  113.                     }
  114.                     if (isset($v['proxy_client']['varnish'])
  115.                         || isset($v['proxy_client']['symfony'])
  116.                         || isset($v['proxy_client']['noop'])
  117.                     ) {
  118.                         $v['user_context']['logout_handler']['enabled'] = true;
  119.                         return $v;
  120.                     }
  121.                     if ('auto' === $v['user_context']['logout_handler']['enabled']) {
  122.                         $v['user_context']['logout_handler']['enabled'] = false;
  123.                         return $v;
  124.                     }
  125.                     throw new InvalidConfigurationException('To enable the user context logout handler, you need to configure a tag capable proxy_client (varnish, symfony, noop or custom_proxy_client).');
  126.                 })
  127.             ->end()
  128.             // Determine the default tags header for the varnish client, depending on whether we use BAN or xkey
  129.             ->validate()
  130.                 ->ifTrue(
  131.                     function ($v) {
  132.                         return
  133.                             array_key_exists('proxy_client'$v)
  134.                             && array_key_exists('varnish'$v['proxy_client'])
  135.                             && empty($v['proxy_client']['varnish']['tags_header'])
  136.                         ;
  137.                     }
  138.                 )
  139.                 ->then(function ($v) {
  140.                     $v['proxy_client']['varnish']['tags_header'] =
  141.                         (Varnish::TAG_XKEY === $v['proxy_client']['varnish']['tag_mode'])
  142.                         ? Varnish::DEFAULT_HTTP_HEADER_CACHE_XKEY
  143.                         Varnish::DEFAULT_HTTP_HEADER_CACHE_TAGS;
  144.                     return $v;
  145.                 })
  146.             ->end()
  147.             // Determine the default tag response header, depending on whether we use BAN or xkey
  148.             ->validate()
  149.                 ->ifTrue(
  150.                     function ($v) {
  151.                         return empty($v['tags']['response_header']);
  152.                     }
  153.                 )
  154.                 ->then(function ($v) {
  155.                     $v['tags']['response_header'] = $this->isVarnishXkey($v) ? 'xkey' TagHeaderFormatter::DEFAULT_HEADER_NAME;
  156.                     return $v;
  157.                 })
  158.             ->end()
  159.             // Determine the default separator for the tags header, depending on whether we use BAN or xkey
  160.             ->validate()
  161.                 ->ifTrue(
  162.                     function ($v) {
  163.                         return empty($v['tags']['separator']);
  164.                     }
  165.                 )
  166.                 ->then(function ($v) {
  167.                     $v['tags']['separator'] = $this->isVarnishXkey($v) ? ' ' ',';
  168.                     return $v;
  169.                 })
  170.         ;
  171.         $this->addCacheableResponseSection($rootNode);
  172.         $this->addCacheControlSection($rootNode);
  173.         $this->addProxyClientSection($rootNode);
  174.         $this->addCacheManagerSection($rootNode);
  175.         $this->addTagSection($rootNode);
  176.         $this->addInvalidationSection($rootNode);
  177.         $this->addUserContextListenerSection($rootNode);
  178.         $this->addFlashMessageSection($rootNode);
  179.         $this->addTestSection($rootNode);
  180.         $this->addDebugSection($rootNode);
  181.         return $treeBuilder;
  182.     }
  183.     private function isVarnishXkey(array $v): bool
  184.     {
  185.         return array_key_exists('proxy_client'$v)
  186.             && array_key_exists('varnish'$v['proxy_client'])
  187.             && Varnish::TAG_XKEY === $v['proxy_client']['varnish']['tag_mode']
  188.         ;
  189.     }
  190.     private function addCacheableResponseSection(ArrayNodeDefinition $rootNode)
  191.     {
  192.         $rootNode
  193.             ->children()
  194.                 ->arrayNode('cacheable')
  195.                     ->addDefaultsIfNotSet()
  196.                     ->children()
  197.                         ->arrayNode('response')
  198.                             ->addDefaultsIfNotSet()
  199.                             ->children()
  200.                                 ->arrayNode('additional_status')
  201.                                     ->prototype('scalar')->end()
  202.                                     ->info('Additional response HTTP status codes that will be considered cacheable.')
  203.                                 ->end()
  204.                                 ->scalarNode('expression')
  205.                                     ->defaultNull()
  206.                                     ->info('Expression to decide whether response is cacheable. Replaces the default status codes.')
  207.                             ->end()
  208.                         ->end()
  209.                         ->validate()
  210.                             ->ifTrue(function ($v) {
  211.                                 return !empty($v['additional_status']) && !empty($v['expression']);
  212.                             })
  213.                             ->thenInvalid('You may not set both additional_status and expression.')
  214.                             ->ifTrue(function ($v) {
  215.                                 return !empty($v['expression']) && !class_exists(ExpressionLanguage::class);
  216.                             })
  217.                             ->thenInvalid('Configured a response.expression but ExpressionLanguage is not available')
  218.                         ->end()
  219.                     ->end()
  220.                 ->end()
  221.             ->end();
  222.     }
  223.     /**
  224.      * Cache header control main section.
  225.      */
  226.     private function addCacheControlSection(ArrayNodeDefinition $rootNode)
  227.     {
  228.         $rules $rootNode
  229.             ->children()
  230.                 ->arrayNode('cache_control')
  231.                     ->fixXmlConfig('rule')
  232.                     ->children()
  233.                         ->arrayNode('defaults')
  234.                             ->addDefaultsIfNotSet()
  235.                             ->children()
  236.                                 ->booleanNode('overwrite')
  237.                                     ->info('Whether to overwrite existing cache headers')
  238.                                     ->defaultFalse()
  239.                                 ->end()
  240.                             ->end()
  241.                         ->end()
  242.                         ->arrayNode('rules')
  243.                             ->prototype('array')
  244.                                 ->children();
  245.         $this->addMatch($rulestrue);
  246.         $rules
  247.             ->arrayNode('headers')
  248.                 ->isRequired()
  249.                 // todo validate there is some header defined
  250.                 ->children()
  251.                     ->enumNode('overwrite')
  252.                         ->info('Whether to overwrite cache headers for this rule, defaults to the cache_control.defaults.overwrite setting')
  253.                         ->values(['default'truefalse])
  254.                         ->defaultValue('default')
  255.                     ->end()
  256.                     ->arrayNode('cache_control')
  257.                         ->info('Add the specified cache control directives.')
  258.                         ->children()
  259.                             ->scalarNode('max_age')->end()
  260.                             ->scalarNode('s_maxage')->end()
  261.                             ->booleanNode('private')->end()
  262.                             ->booleanNode('public')->end()
  263.                             ->booleanNode('must_revalidate')->end()
  264.                             ->booleanNode('proxy_revalidate')->end()
  265.                             ->booleanNode('no_transform')->end()
  266.                             ->booleanNode('no_cache')->end()
  267.                             ->booleanNode('no_store')->end()
  268.                             ->scalarNode('stale_if_error')->end()
  269.                             ->scalarNode('stale_while_revalidate')->end()
  270.                         ->end()
  271.                     ->end()
  272.                     ->enumNode('etag')
  273.                         ->defaultValue(false)
  274.                         ->treatTrueLike('strong')
  275.                         ->info('Set a simple ETag which is just the md5 hash of the response body. '.
  276.                                'You can specify which type of ETag you want by passing "strong" or "weak".')
  277.                         ->values(['weak''strong'false])
  278.                     ->end()
  279.                     ->scalarNode('last_modified')
  280.                         ->validate()
  281.                             ->ifTrue(function ($v) {
  282.                                 if (is_string($v)) {
  283.                                     new \DateTime($v);
  284.                                 }
  285.                                 return false;
  286.                             })
  287.                             ->thenInvalid(''// this will never happen as new DateTime will throw an exception if $v is no date
  288.                         ->end()
  289.                         ->info('Set a default last modified timestamp if none is set yet. Value must be parseable by DateTime')
  290.                     ->end()
  291.                     ->scalarNode('reverse_proxy_ttl')
  292.                         ->defaultNull()
  293.                         ->info('Specify an X-Reverse-Proxy-TTL header with a time in seconds for a caching proxy under your control.')
  294.                     ->end()
  295.                     ->arrayNode('vary')
  296.                         ->beforeNormalization()->ifString()->then(function ($v) {
  297.                             return preg_split('/\s*,\s*/'$v);
  298.                         })->end()
  299.                         ->prototype('scalar')->end()
  300.                         ->info('Define a list of additional headers on which the response varies.')
  301.                     ->end()
  302.                 ->end()
  303.             ->end()
  304.         ;
  305.     }
  306.     /**
  307.      * Shared configuration between cache control, tags and invalidation.
  308.      *
  309.      * @param bool $matchResponse whether to also add fields to match response
  310.      */
  311.     private function addMatch(NodeBuilder $rules$matchResponse false)
  312.     {
  313.         $match $rules
  314.             ->arrayNode('match')
  315.                 ->cannotBeOverwritten()
  316.                 ->isRequired()
  317.                 ->fixXmlConfig('method')
  318.                 ->fixXmlConfig('ip')
  319.                 ->fixXmlConfig('attribute')
  320.                 ->validate()
  321.                     ->ifTrue(function ($v) {
  322.                         return !empty($v['additional_response_status']) && !empty($v['match_response']);
  323.                     })
  324.                     ->thenInvalid('You may not set both additional_response_status and match_response.')
  325.                 ->end()
  326.                 ->children()
  327.                     ->scalarNode('path')
  328.                         ->defaultNull()
  329.                         ->info('Request path.')
  330.                     ->end()
  331.                     ->scalarNode('query_string')
  332.                         ->defaultNull()
  333.                         ->info('Request query string.')
  334.                     ->end()
  335.                     ->scalarNode('host')
  336.                         ->defaultNull()
  337.                         ->info('Request host name.')
  338.                     ->end()
  339.                     ->arrayNode('methods')
  340.                         ->beforeNormalization()->ifString()->then(function ($v) {
  341.                             return preg_split('/\s*,\s*/'$v);
  342.                         })->end()
  343.                         ->useAttributeAsKey('name')
  344.                         ->prototype('scalar')->end()
  345.                         ->info('Request HTTP methods.')
  346.                     ->end()
  347.                     ->arrayNode('ips')
  348.                         ->beforeNormalization()->ifString()->then(function ($v) {
  349.                             return preg_split('/\s*,\s*/'$v);
  350.                         })->end()
  351.                         ->useAttributeAsKey('name')
  352.                         ->prototype('scalar')->end()
  353.                         ->info('List of client IPs.')
  354.                     ->end()
  355.                     ->arrayNode('attributes')
  356.                         ->useAttributeAsKey('name')
  357.                         ->prototype('scalar')->end()
  358.                         ->info('Regular expressions on request attributes.')
  359.                     ->end()
  360.         ;
  361.         if ($matchResponse) {
  362.             $match
  363.                 ->arrayNode('additional_response_status')
  364.                     ->prototype('scalar')->end()
  365.                     ->info('Additional response HTTP status codes that will match. Replaces cacheable configuration.')
  366.                 ->end()
  367.                 ->scalarNode('match_response')
  368.                     ->defaultNull()
  369.                     ->info('Expression to decide whether response should be matched. Replaces cacheable configuration.')
  370.                 ->end()
  371.             ;
  372.         }
  373.     }
  374.     private function addProxyClientSection(ArrayNodeDefinition $rootNode)
  375.     {
  376.         $rootNode
  377.             ->children()
  378.                 ->arrayNode('proxy_client')
  379.                     ->children()
  380.                         ->enumNode('default')
  381.                             ->values(['varnish''nginx''symfony''noop'])
  382.                             ->info('If you configure more than one proxy client, you need to specify which client is the default.')
  383.                         ->end()
  384.                         ->arrayNode('varnish')
  385.                             ->fixXmlConfig('default_ban_header')
  386.                             ->validate()
  387.                                 ->always(function ($v) {
  388.                                     if (!count($v['default_ban_headers'])) {
  389.                                         unset($v['default_ban_headers']);
  390.                                     }
  391.                                     return $v;
  392.                                 })
  393.                             ->end()
  394.                             ->children()
  395.                                 ->scalarNode('tags_header')
  396.                                     ->info('HTTP header to use when sending tag invalidation requests to Varnish')
  397.                                 ->end()
  398.                                 ->scalarNode('header_length')
  399.                                     ->info('Maximum header length when invalidating tags. If there are more tags to invalidate than fit into the header, the invalidation request is split into several requests.')
  400.                                 ->end()
  401.                                 ->arrayNode('default_ban_headers')
  402.                                     ->useAttributeAsKey('name')
  403.                                     ->info('Map of additional headers to include in each ban request.')
  404.                                     ->prototype('scalar')->end()
  405.                                 ->end()
  406.                                 ->enumNode('tag_mode')
  407.                                     ->info('If you can enable the xkey module in Varnish, use the purgekeys mode for more efficient tag handling')
  408.                                     ->values(['ban''purgekeys'])
  409.                                     ->defaultValue('ban')
  410.                                 ->end()
  411.                                 ->append($this->getHttpDispatcherNode())
  412.                             ->end()
  413.                         ->end()
  414.                         ->arrayNode('nginx')
  415.                             ->children()
  416.                                 ->scalarNode('purge_location')
  417.                                     ->defaultValue(false)
  418.                                     ->info('Path to trigger the purge on Nginx for different location purge.')
  419.                                 ->end()
  420.                                 ->append($this->getHttpDispatcherNode())
  421.                             ->end()
  422.                         ->end()
  423.                         ->arrayNode('symfony')
  424.                             ->children()
  425.                                 ->scalarNode('tags_header')
  426.                                     ->defaultValue(PurgeTagsListener::DEFAULT_TAGS_HEADER)
  427.                                     ->info('HTTP header to use when sending tag invalidation requests to Symfony HttpCache')
  428.                                 ->end()
  429.                                 ->scalarNode('tags_method')
  430.                                     ->defaultValue(PurgeTagsListener::DEFAULT_TAGS_METHOD)
  431.                                     ->info('HTTP method for sending tag invalidation requests to Symfony HttpCache')
  432.                                 ->end()
  433.                                 ->scalarNode('header_length')
  434.                                     ->info('Maximum header length when invalidating tags. If there are more tags to invalidate than fit into the header, the invalidation request is split into several requests.')
  435.                                 ->end()
  436.                                 ->scalarNode('purge_method')
  437.                                     ->defaultValue(PurgeListener::DEFAULT_PURGE_METHOD)
  438.                                     ->info('HTTP method to use when sending purge requests to Symfony HttpCache')
  439.                                 ->end()
  440.                                 ->booleanNode('use_kernel_dispatcher')
  441.                                     ->defaultFalse()
  442.                                     ->info('Dispatches invalidation requests to the kernel directly instead of executing real HTTP requests. Requires special kernel setup! Refer to the documentation for more information.')
  443.                                 ->end()
  444.                                 ->append($this->getHttpDispatcherNode())
  445.                             ->end()
  446.                         ->end()
  447.                         ->booleanNode('noop')->end()
  448.                     ->end()
  449.                     ->validate()
  450.                         ->always()
  451.                         ->then(function ($config) {
  452.                             foreach ($config as $proxyName => $proxyConfig) {
  453.                                 // we only want either the servers config or the servers_from_jsonenv config
  454.                                 if (isset($proxyConfig['http']['servers']) && !count($proxyConfig['http']['servers'])) {
  455.                                     unset($proxyConfig['http']['servers'], $config[$proxyName]['http']['servers']);
  456.                                 }
  457.                                 $arrayServersConfigured = isset($proxyConfig['http']['servers']) && \is_array($proxyConfig['http']['servers']);
  458.                                 $jsonServersConfigured = isset($proxyConfig['http']['servers_from_jsonenv']) && \is_string($proxyConfig['http']['servers_from_jsonenv']);
  459.                                 if ($arrayServersConfigured && $jsonServersConfigured) {
  460.                                     throw new InvalidConfigurationException(sprintf('You can only set one of "http.servers" or "http.servers_from_jsonenv" but not both to avoid ambiguity for the proxy "%s"'$proxyName));
  461.                                 }
  462.                                 if (!\in_array($proxyName, ['noop''default''symfony'])) {
  463.                                     if (!$arrayServersConfigured && !$jsonServersConfigured) {
  464.                                         throw new InvalidConfigurationException(sprintf('The "http.servers" or "http.servers_from_jsonenv" section must be defined for the proxy "%s"'$proxyName));
  465.                                     }
  466.                                     return $config;
  467.                                 }
  468.                                 if ('symfony' === $proxyName) {
  469.                                     if (!$arrayServersConfigured && !$jsonServersConfigured && false === $proxyConfig['use_kernel_dispatcher']) {
  470.                                         throw new InvalidConfigurationException('Either configure the "http.servers" or "http.servers_from_jsonenv" section or enable "proxy_client.symfony.use_kernel_dispatcher"');
  471.                                     }
  472.                                 }
  473.                             }
  474.                             return $config;
  475.                         })
  476.                     ->end()
  477.                 ->end()
  478.             ->end();
  479.     }
  480.     /**
  481.      * Get the configuration node for a HTTP dispatcher in a proxy client.
  482.      *
  483.      * @return NodeDefinition
  484.      */
  485.     private function getHttpDispatcherNode()
  486.     {
  487.         $treeBuilder = new TreeBuilder('http');
  488.         // Keep compatibility with symfony/config < 4.2
  489.         if (!method_exists($treeBuilder'getRootNode')) {
  490.             $node $treeBuilder->root('http');
  491.         } else {
  492.             $node $treeBuilder->getRootNode();
  493.         }
  494.         $node
  495.             ->fixXmlConfig('server')
  496.             ->children()
  497.                 ->arrayNode('servers')
  498.                     ->info('Addresses of the hosts the caching proxy is running on. The values may be hostnames or ips, and with :port if not the default port 80.')
  499.                     ->useAttributeAsKey('name')
  500.                     ->requiresAtLeastOneElement()
  501.                     ->prototype('scalar')->end()
  502.                 ->end()
  503.                 ->variableNode('servers_from_jsonenv')
  504.                     ->info('Addresses of the hosts the caching proxy is running on (env var that contains a json array as a string). The values may be hostnames or ips, and with :port if not the default port 80.')
  505.                 ->end()
  506.                 ->scalarNode('base_url')
  507.                     ->defaultNull()
  508.                     ->info('Default host name and optional path for path based invalidation.')
  509.                 ->end()
  510.                 ->scalarNode('http_client')
  511.                     ->defaultNull()
  512.                     ->info('Httplug async client service name to use for sending the requests.')
  513.                 ->end()
  514.             ->end()
  515.         ;
  516.         return $node;
  517.     }
  518.     private function addTestSection(ArrayNodeDefinition $rootNode)
  519.     {
  520.         $rootNode
  521.             ->children()
  522.                 ->arrayNode('test')
  523.                     ->children()
  524.                         ->scalarNode('cache_header')
  525.                             ->defaultValue('X-Cache')
  526.                             ->info('HTTP cache hit/miss header')
  527.                         ->end()
  528.                         ->arrayNode('proxy_server')
  529.                             ->info('Configure how caching proxy will be run in your tests')
  530.                             ->children()
  531.                                 ->enumNode('default')
  532.                                     ->values(['varnish''nginx'])
  533.                                     ->info('If you configure more than one proxy server, specify which client is the default.')
  534.                                 ->end()
  535.                                 ->arrayNode('varnish')
  536.                                     ->children()
  537.                                         ->scalarNode('config_file')->isRequired()->end()
  538.                                         ->scalarNode('binary')->defaultValue('varnishd')->end()
  539.                                         ->integerNode('port')->defaultValue(6181)->end()
  540.                                         ->scalarNode('ip')->defaultValue('127.0.0.1')->end()
  541.                                     ->end()
  542.                                 ->end()
  543.                                 ->arrayNode('nginx')
  544.                                     ->children()
  545.                                         ->scalarNode('config_file')->isRequired()->end()
  546.                                         ->scalarNode('binary')->defaultValue('nginx')->end()
  547.                                         ->integerNode('port')->defaultValue(8080)->end()
  548.                                         ->scalarNode('ip')->defaultValue('127.0.0.1')->end()
  549.                                     ->end()
  550.                                 ->end()
  551.                             ->end()
  552.                         ->end()
  553.                     ->end()
  554.                 ->end()
  555.             ->end();
  556.     }
  557.     /**
  558.      * Cache manager main section.
  559.      */
  560.     private function addCacheManagerSection(ArrayNodeDefinition $rootNode)
  561.     {
  562.         $rootNode
  563.             ->children()
  564.                 ->arrayNode('cache_manager')
  565.                     ->addDefaultsIfNotSet()
  566.                     ->beforeNormalization()
  567.                         ->ifArray()
  568.                         ->then(function ($v) {
  569.                             $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true;
  570.                             return $v;
  571.                         })
  572.                     ->end()
  573.                     ->info('Configure the cache manager. Needs a proxy_client to be configured.')
  574.                     ->children()
  575.                         ->enumNode('enabled')
  576.                             ->values([truefalse'auto'])
  577.                             ->defaultValue('auto')
  578.                             ->info('Allows to disable the invalidation manager. Enabled by default if you configure a proxy client.')
  579.                         ->end()
  580.                         ->scalarNode('custom_proxy_client')
  581.                             ->info('Service name of a custom proxy client to use. With a custom client, generate_url_type defaults to ABSOLUTE_URL and tag support needs to be explicitly enabled. If no custom proxy client is specified, the first proxy client you configured is used.')
  582.                             ->cannotBeEmpty()
  583.                         ->end()
  584.                         ->enumNode('generate_url_type')
  585.                             ->values([
  586.                                 'auto',
  587.                                 UrlGeneratorInterface::ABSOLUTE_PATH,
  588.                                 UrlGeneratorInterface::ABSOLUTE_URL,
  589.                                 UrlGeneratorInterface::NETWORK_PATH,
  590.                                 UrlGeneratorInterface::RELATIVE_PATH,
  591.                             ])
  592.                             ->defaultValue('auto')
  593.                             ->info('Set what URLs to generate on invalidate/refresh Route. Auto means path if base_url is set on the default proxy client, full URL otherwise.')
  594.                         ->end()
  595.                     ->end()
  596.         ;
  597.     }
  598.     private function addTagSection(ArrayNodeDefinition $rootNode)
  599.     {
  600.         $rules $rootNode
  601.             ->children()
  602.                 ->arrayNode('tags')
  603.                     ->addDefaultsIfNotSet()
  604.                     ->fixXmlConfig('rule')
  605.                     ->children()
  606.                         ->enumNode('enabled')
  607.                             ->values([truefalse'auto'])
  608.                             ->defaultValue('auto')
  609.                             ->info('Allows to disable tag support. Enabled by default if you configured the cache manager and have a proxy client that supports tagging.')
  610.                         ->end()
  611.                         ->arrayNode('annotations')
  612.                             ->info('Annotations require the FrameworkExtraBundle. Because we can not detect whether annotations are used when the FrameworkExtraBundle is not available, this option must be set to false explicitly if the application does not use annotations.')
  613.                             ->canBeDisabled()
  614.                         ->end()
  615.                         ->booleanNode('strict')->defaultFalse()->end()
  616.                         ->scalarNode('expression_language')
  617.                             ->defaultNull()
  618.                             ->info('Service name of a custom ExpressionLanugage to use.')
  619.                         ->end()
  620.                         ->scalarNode('response_header')
  621.                             ->defaultNull()
  622.                             ->info('HTTP header that contains cache tags. Defaults to xkey-softpurge for Varnish xkey or X-Cache-Tags otherwise')
  623.                         ->end()
  624.                         ->scalarNode('separator')
  625.                             ->defaultNull()
  626.                             ->info('Character(s) to use to separate multiple tags. Defaults to " " for Varnish xkey or "," otherwise')
  627.                         ->end()
  628.                         ->scalarNode('max_header_value_length')
  629.                             ->defaultNull()
  630.                             ->info('If configured the tag header value will be split into multiple response headers of the same name (see "response_header" configuration key) that all do not exceed the configured "max_header_value_length" (recommended is 4KB = 4096) - configure in bytes.')
  631.                         ->end()
  632.                         ->arrayNode('rules')
  633.                             ->prototype('array')
  634.                                 ->fixXmlConfig('tag')
  635.                                 ->fixXmlConfig('tag_expression')
  636.                                 ->validate()
  637.                                     ->ifTrue(function ($v) {
  638.                                         return !empty($v['tag_expressions']) && !class_exists(ExpressionLanguage::class);
  639.                                     })
  640.                                     ->thenInvalid('Configured a tag_expression but ExpressionLanugage is not available')
  641.                                 ->end()
  642.                                 ->children()
  643.                         ;
  644.         $this->addMatch($rules);
  645.         $rules
  646.             ->arrayNode('tags')
  647.                 ->prototype('scalar')
  648.                 ->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
  649.             ->end()->end()
  650.             ->arrayNode('tag_expressions')
  651.                 ->prototype('scalar')
  652.                 ->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
  653.             ->end()
  654.         ;
  655.     }
  656.     private function addInvalidationSection(ArrayNodeDefinition $rootNode)
  657.     {
  658.         $rules $rootNode
  659.             ->children()
  660.                 ->arrayNode('invalidation')
  661.                     ->fixXmlConfig('rule')
  662.                     ->addDefaultsIfNotSet()
  663.                     ->children()
  664.                         ->enumNode('enabled')
  665.                             ->values([truefalse'auto'])
  666.                             ->defaultValue('auto')
  667.                             ->info('Allows to disable the listener for invalidation. Enabled by default if the cache manager is configured. When disabled, the cache manager is no longer flushed automatically.')
  668.                         ->end()
  669.                         ->scalarNode('expression_language')
  670.                             ->defaultNull()
  671.                             ->info('Service name of a custom ExpressionLanugage to use.')
  672.                         ->end()
  673.                         ->arrayNode('rules')
  674.                             ->info('Set what requests should invalidate which target routes.')
  675.                             ->prototype('array')
  676.                                 ->fixXmlConfig('route')
  677.                                 ->children();
  678.         $this->addMatch($rules);
  679.         $rules
  680.             ->arrayNode('routes')
  681.                 ->isRequired()
  682.                 ->requiresAtLeastOneElement()
  683.                 ->useAttributeAsKey('name')
  684.                 ->info('Target routes to invalidate when request is matched')
  685.                 ->prototype('array')
  686.                     ->children()
  687.                         ->booleanNode('ignore_extra_params')->defaultTrue()->end()
  688.                     ->end()
  689.                 ->end()
  690.             ->end();
  691.     }
  692.     /**
  693.      * User context main section.
  694.      */
  695.     private function addUserContextListenerSection(ArrayNodeDefinition $rootNode)
  696.     {
  697.         $rootNode
  698.             ->children()
  699.                 ->arrayNode('user_context')
  700.                     ->info('Listener that returns the request for the user context hash as early as possible.')
  701.                     ->addDefaultsIfNotSet()
  702.                     ->canBeEnabled()
  703.                     ->fixXmlConfig('user_identifier_header')
  704.                     ->children()
  705.                         ->arrayNode('match')
  706.                             ->addDefaultsIfNotSet()
  707.                             ->children()
  708.                                 ->scalarNode('matcher_service')
  709.                                     ->defaultValue('fos_http_cache.user_context.request_matcher')
  710.                                     ->info('Service id of a request matcher that tells whether the request is a context hash request.')
  711.                                 ->end()
  712.                                 ->scalarNode('accept')
  713.                                     ->defaultValue('application/vnd.fos.user-context-hash')
  714.                                     ->info('Specify the accept HTTP header used for context hash requests.')
  715.                                 ->end()
  716.                                 ->scalarNode('method')
  717.                                     ->defaultNull()
  718.                                     ->info('Specify the HTTP method used for context hash requests.')
  719.                                 ->end()
  720.                             ->end()
  721.                         ->end()
  722.                         ->scalarNode('hash_cache_ttl')
  723.                             ->defaultValue(0)
  724.                             ->info('Cache the response for the hash for the specified number of seconds. Setting this to 0 will not cache those responses at all.')
  725.                         ->end()
  726.                         ->booleanNode('always_vary_on_context_hash')
  727.                             ->defaultTrue()
  728.                             ->info('Whether to always add the user context hash header name in the response Vary header.')
  729.                         ->end()
  730.                         ->arrayNode('user_identifier_headers')
  731.                             ->prototype('scalar')->end()
  732.                             ->defaultValue(['Cookie''Authorization'])
  733.                             ->info('List of headers that contain the unique identifier for the user in the hash request.')
  734.                         ->end()
  735.                         ->scalarNode('session_name_prefix')
  736.                             ->defaultValue(false)
  737.                             ->info('Prefix for session cookies. Must match your PHP session configuration. Set to false to ignore the session in user context.')
  738.                         ->end()
  739.                         ->scalarNode('user_hash_header')
  740.                             ->defaultValue('X-User-Context-Hash')
  741.                             ->info('Name of the header that contains the hash information for the context.')
  742.                         ->end()
  743.                         ->booleanNode('role_provider')
  744.                             ->defaultFalse()
  745.                             ->info('Whether to enable a provider that automatically adds all roles of the current user to the context.')
  746.                         ->end()
  747.                         ->arrayNode('logout_handler')
  748.                             ->addDefaultsIfNotSet()
  749.                             ->canBeEnabled()
  750.                             ->children()
  751.                                 ->enumNode('enabled')
  752.                                     ->values([truefalse'auto'])
  753.                                     ->defaultValue('auto')
  754.                                     ->info('Whether to enable the user context logout handler.')
  755.                                 ->end()
  756.                             ->end()
  757.                         ->end()
  758.                     ->end()
  759.                 ->end()
  760.             ->end()
  761.         ;
  762.     }
  763.     private function addFlashMessageSection(ArrayNodeDefinition $rootNode)
  764.     {
  765.         $rootNode
  766.             ->children()
  767.                 ->arrayNode('flash_message')
  768.                     ->canBeUnset()
  769.                     ->canBeEnabled()
  770.                     ->info('Activate the flash message listener that puts flash messages into a cookie.')
  771.                     ->children()
  772.                         ->scalarNode('name')
  773.                             ->defaultValue('flashes')
  774.                             ->info('Name of the cookie to set for flashes.')
  775.                         ->end()
  776.                         ->scalarNode('path')
  777.                             ->defaultValue('/')
  778.                             ->info('Cookie path validity.')
  779.                         ->end()
  780.                         ->scalarNode('host')
  781.                             ->defaultNull()
  782.                             ->info('Cookie host name validity.')
  783.                         ->end()
  784.                         ->scalarNode('secure')
  785.                             ->defaultFalse()
  786.                             ->info('Whether the cookie should only be transmitted over a secure HTTPS connection from the client.')
  787.                         ->end()
  788.                     ->end()
  789.                 ->end()
  790.             ->end();
  791.     }
  792.     private function addDebugSection(ArrayNodeDefinition $rootNode)
  793.     {
  794.         $rootNode
  795.             ->children()
  796.                 ->arrayNode('debug')
  797.                 ->addDefaultsIfNotSet()
  798.                 ->canBeEnabled()
  799.                 ->children()
  800.                     ->booleanNode('enabled')
  801.                         ->defaultValue($this->debug)
  802.                         ->info('Whether to send a debug header with the response to trigger a caching proxy to send debug information. If not set, defaults to kernel.debug.')
  803.                     ->end()
  804.                     ->scalarNode('header')
  805.                         ->defaultValue('X-Cache-Debug')
  806.                         ->info('The header to send if debug is true.')
  807.                     ->end()
  808.                 ->end()
  809.             ->end()
  810.         ->end();
  811.     }
  812. }