vendor/php-http/httplug-bundle/src/DependencyInjection/Configuration.php line 724

Open in your IDE?
  1. <?php
  2. namespace Http\HttplugBundle\DependencyInjection;
  3. use Http\Client\Common\Plugin\Cache\Generator\CacheKeyGenerator;
  4. use Http\Client\Common\Plugin\CachePlugin;
  5. use Http\Client\Common\Plugin\Journal;
  6. use Http\Message\CookieJar;
  7. use Http\Message\Formatter;
  8. use Http\Message\StreamFactory;
  9. use Psr\Cache\CacheItemPoolInterface;
  10. use Psr\Log\LoggerInterface;
  11. use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
  12. use Symfony\Component\Config\Definition\Builder\NodeDefinition;
  13. use Symfony\Component\Config\Definition\Builder\TreeBuilder;
  14. use Symfony\Component\Config\Definition\ConfigurationInterface;
  15. use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
  16. use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
  17. /**
  18.  * This class contains the configuration information for the bundle.
  19.  *
  20.  * This information is solely responsible for how the different configuration
  21.  * sections are normalized, and merged.
  22.  *
  23.  * @author David Buchmann <mail@davidbu.ch>
  24.  * @author Tobias Nyholm <tobias.nyholm@gmail.com>
  25.  */
  26. class Configuration implements ConfigurationInterface
  27. {
  28.     /**
  29.      * Whether to use the debug mode.
  30.      *
  31.      * @see https://github.com/doctrine/DoctrineBundle/blob/v1.5.2/DependencyInjection/Configuration.php#L31-L41
  32.      *
  33.      * @var bool
  34.      */
  35.     private $debug;
  36.     /**
  37.      * @param bool $debug
  38.      */
  39.     public function __construct($debug)
  40.     {
  41.         $this->debug = (bool) $debug;
  42.     }
  43.     /**
  44.      * {@inheritdoc}
  45.      */
  46.     public function getConfigTreeBuilder()
  47.     {
  48.         $treeBuilder = new TreeBuilder('httplug');
  49.         // Keep compatibility with symfony/config < 4.2
  50.         if (!method_exists($treeBuilder'getRootNode')) {
  51.             $rootNode $treeBuilder->root('httplug');
  52.         } else {
  53.             $rootNode $treeBuilder->getRootNode();
  54.         }
  55.         $this->configureClients($rootNode);
  56.         $this->configureSharedPlugins($rootNode);
  57.         $rootNode
  58.             ->validate()
  59.                 ->ifTrue(function ($v) {
  60.                     return !empty($v['classes']['client'])
  61.                         || !empty($v['classes']['message_factory'])
  62.                         || !empty($v['classes']['uri_factory'])
  63.                         || !empty($v['classes']['stream_factory']);
  64.                 })
  65.                 ->then(function ($v) {
  66.                     foreach ($v['classes'] as $key => $class) {
  67.                         if (null !== $class && !class_exists($class)) {
  68.                             throw new InvalidConfigurationException(sprintf(
  69.                                 'Class %s specified for httplug.classes.%s does not exist.',
  70.                                 $class,
  71.                                 $key
  72.                             ));
  73.                         }
  74.                     }
  75.                     return $v;
  76.                 })
  77.             ->end()
  78.             ->beforeNormalization()
  79.                 ->ifTrue(function ($v) {
  80.                     return is_array($v) && array_key_exists('toolbar'$v) && is_array($v['toolbar']);
  81.                 })
  82.                 ->then(function ($v) {
  83.                     if (array_key_exists('profiling'$v)) {
  84.                         throw new InvalidConfigurationException('Can\'t configure both "toolbar" and "profiling" section. The "toolbar" config is deprecated as of version 1.3.0, please only use "profiling".');
  85.                     }
  86.                     @trigger_error('"httplug.toolbar" config is deprecated since version 1.3 and will be removed in 2.0. Use "httplug.profiling" instead.'E_USER_DEPRECATED);
  87.                     if (array_key_exists('enabled'$v['toolbar']) && 'auto' === $v['toolbar']['enabled']) {
  88.                         @trigger_error('"auto" value in "httplug.toolbar" config is deprecated since version 1.3 and will be removed in 2.0. Use a boolean value instead.'E_USER_DEPRECATED);
  89.                         $v['toolbar']['enabled'] = $this->debug;
  90.                     }
  91.                     $v['profiling'] = $v['toolbar'];
  92.                     unset($v['toolbar']);
  93.                     return $v;
  94.                 })
  95.             ->end()
  96.             ->fixXmlConfig('client')
  97.             ->children()
  98.                 ->booleanNode('default_client_autowiring')
  99.                     ->defaultTrue()
  100.                     ->info('Set to false to not autowire HttpClient and HttpAsyncClient.')
  101.                 ->end()
  102.                 ->arrayNode('main_alias')
  103.                     ->addDefaultsIfNotSet()
  104.                     ->info('Configure which service the main alias point to.')
  105.                     ->children()
  106.                         ->scalarNode('client')->defaultValue('httplug.client.default')->end()
  107.                         ->scalarNode('message_factory')->defaultValue('httplug.message_factory.default')->end()
  108.                         ->scalarNode('uri_factory')->defaultValue('httplug.uri_factory.default')->end()
  109.                         ->scalarNode('stream_factory')->defaultValue('httplug.stream_factory.default')->end()
  110.                     ->end()
  111.                 ->end()
  112.                 ->arrayNode('classes')
  113.                     ->addDefaultsIfNotSet()
  114.                     ->info('Overwrite a service class instead of using the discovery mechanism.')
  115.                     ->children()
  116.                         ->scalarNode('client')->defaultNull()->end()
  117.                         ->scalarNode('message_factory')->defaultNull()->end()
  118.                         ->scalarNode('uri_factory')->defaultNull()->end()
  119.                         ->scalarNode('stream_factory')->defaultNull()->end()
  120.                     ->end()
  121.                 ->end()
  122.                 ->arrayNode('profiling')
  123.                     ->addDefaultsIfNotSet()
  124.                     ->treatFalseLike(['enabled' => false])
  125.                     ->treatTrueLike(['enabled' => true])
  126.                     ->treatNullLike(['enabled' => $this->debug])
  127.                     ->info('Extend the debug profiler with information about requests.')
  128.                     ->children()
  129.                         ->booleanNode('enabled')
  130.                             ->info('Turn the toolbar on or off. Defaults to kernel debug mode.')
  131.                             ->defaultValue($this->debug)
  132.                         ->end()
  133.                         ->scalarNode('formatter')->defaultNull()->end()
  134.                         ->scalarNode('captured_body_length')
  135.                             ->validate()
  136.                                 ->ifTrue(function ($v) {
  137.                                     return null !== $v && !is_int($v);
  138.                                 })
  139.                                 ->thenInvalid('The child node "captured_body_length" at path "httplug.profiling" must be an integer or null ("%s" given).')
  140.                             ->end()
  141.                             ->defaultValue(0)
  142.                             ->info('Limit long HTTP message bodies to x characters. If set to 0 we do not read the message body. If null the body will not be truncated. Only available with the default formatter (FullHttpMessageFormatter).')
  143.                         ->end()
  144.                     ->end()
  145.                 ->end()
  146.                 ->arrayNode('discovery')
  147.                     ->addDefaultsIfNotSet()
  148.                     ->info('Control what clients should be found by the discovery.')
  149.                     ->children()
  150.                         ->scalarNode('client')
  151.                             ->defaultValue('auto')
  152.                             ->info('Set to "auto" to see auto discovered client in the web profiler. If provided a service id for a client then this client will be found by auto discovery.')
  153.                         ->end()
  154.                         ->scalarNode('async_client')
  155.                             ->defaultNull()
  156.                             ->info('Set to "auto" to see auto discovered client in the web profiler. If provided a service id for a client then this client will be found by auto discovery.')
  157.                         ->end()
  158.                     ->end()
  159.                 ->end()
  160.             ->end();
  161.         return $treeBuilder;
  162.     }
  163.     private function configureClients(ArrayNodeDefinition $root)
  164.     {
  165.         $root->children()
  166.             ->arrayNode('clients')
  167.                 ->useAttributeAsKey('name')
  168.                 ->prototype('array')
  169.                 ->fixXmlConfig('plugin')
  170.                 ->validate()
  171.                     ->ifTrue(function ($config) {
  172.                         // Make sure we only allow one of these to be true
  173.                         return (bool) $config['flexible_client'] + (bool) $config['http_methods_client'] + (bool) $config['batch_client'] >= 2;
  174.                     })
  175.                     ->thenInvalid('A http client can\'t be decorated with several of FlexibleHttpClient, HttpMethodsClient and BatchClient. Only one of the following options can be true. ("flexible_client", "http_methods_client", "batch_client")')
  176.                 ->end()
  177.                 ->validate()
  178.                     ->ifTrue(function ($config) {
  179.                         return 'httplug.factory.auto' === $config['factory'] && !empty($config['config']);
  180.                     })
  181.                     ->thenInvalid('If you want to use the "config" key you must also specify a valid "factory".')
  182.                 ->end()
  183.                 ->validate()
  184.                     ->ifTrue(function ($config) {
  185.                         return !empty($config['service']) && ('httplug.factory.auto' !== $config['factory'] || !empty($config['config']));
  186.                     })
  187.                     ->thenInvalid('If you want to use the "service" key you cannot specify "factory" or "config".')
  188.                 ->end()
  189.                 ->children()
  190.                     ->scalarNode('factory')
  191.                         ->defaultValue('httplug.factory.auto')
  192.                         ->cannotBeEmpty()
  193.                         ->info('The service id of a factory to use when creating the adapter.')
  194.                     ->end()
  195.                     ->scalarNode('service')
  196.                         ->defaultNull()
  197.                         ->info('The service id of the client to use.')
  198.                     ->end()
  199.                     ->booleanNode('public')
  200.                         ->defaultNull()
  201.                         ->info('Set to true if you really cannot use dependency injection and need to make the client service public.')
  202.                     ->end()
  203.                     ->booleanNode('flexible_client')
  204.                         ->defaultFalse()
  205.                         ->info('Set to true to get the client wrapped in a FlexibleHttpClient which emulates async or sync behavior.')
  206.                     ->end()
  207.                     ->booleanNode('http_methods_client')
  208.                         ->defaultFalse()
  209.                         ->info('Set to true to get the client wrapped in a HttpMethodsClient which emulates provides functions for HTTP verbs.')
  210.                     ->end()
  211.                     ->booleanNode('batch_client')
  212.                         ->defaultFalse()
  213.                         ->info('Set to true to get the client wrapped in a BatchClient which allows you to send multiple request at the same time.')
  214.                     ->end()
  215.                     ->variableNode('config')->defaultValue([])->end()
  216.                     ->append($this->createClientPluginNode())
  217.                 ->end()
  218.             ->end()
  219.         ->end();
  220.     }
  221.     /**
  222.      * @param ArrayNodeDefinition $root
  223.      */
  224.     private function configureSharedPlugins(ArrayNodeDefinition $root)
  225.     {
  226.         $pluginsNode $root
  227.             ->children()
  228.                 ->arrayNode('plugins')
  229.                 ->info('Global plugin configuration. Plugins need to be explicitly added to clients.')
  230.                 ->addDefaultsIfNotSet()
  231.             // don't call end to get the plugins node
  232.         ;
  233.         $this->addSharedPluginNodes($pluginsNode);
  234.     }
  235.     /**
  236.      * Createplugins node of a client.
  237.      *
  238.      * @return ArrayNodeDefinition The plugin node
  239.      */
  240.     private function createClientPluginNode()
  241.     {
  242.         $treeBuilder = new TreeBuilder('plugins');
  243.         // Keep compatibility with symfony/config < 4.2
  244.         if (!method_exists($treeBuilder'getRootNode')) {
  245.             $node $treeBuilder->root('plugins');
  246.         } else {
  247.             $node $treeBuilder->getRootNode();
  248.         }
  249.         /** @var ArrayNodeDefinition $pluginList */
  250.         $pluginList $node
  251.             ->info('A list of plugin service ids and client specific plugin definitions. The order is important.')
  252.             ->prototype('array')
  253.         ;
  254.         $pluginList
  255.             // support having just a service id in the list
  256.             ->beforeNormalization()
  257.                 ->always(function ($plugin) {
  258.                     if (is_string($plugin)) {
  259.                         return [
  260.                             'reference' => [
  261.                                 'enabled' => true,
  262.                                 'id' => $plugin,
  263.                             ],
  264.                         ];
  265.                     }
  266.                     return $plugin;
  267.                 })
  268.             ->end()
  269.             ->validate()
  270.                 ->always(function ($plugins) {
  271.                     foreach ($plugins as $name => $definition) {
  272.                         if ('authentication' === $name) {
  273.                             if (!count($definition)) {
  274.                                 unset($plugins['authentication']);
  275.                             }
  276.                         } elseif (!$definition['enabled']) {
  277.                             unset($plugins[$name]);
  278.                         }
  279.                     }
  280.                     return $plugins;
  281.                 })
  282.             ->end()
  283.         ;
  284.         $this->addSharedPluginNodes($pluginListtrue);
  285.         $pluginList
  286.             ->children()
  287.                 ->arrayNode('reference')
  288.                     ->canBeEnabled()
  289.                     ->info('Reference to a plugin service')
  290.                     ->children()
  291.                         ->scalarNode('id')
  292.                             ->info('Service id of a plugin')
  293.                             ->isRequired()
  294.                             ->cannotBeEmpty()
  295.                         ->end()
  296.                     ->end()
  297.                 ->end()
  298.                 ->arrayNode('add_host')
  299.                     ->canBeEnabled()
  300.                     ->addDefaultsIfNotSet()
  301.                     ->info('Set scheme, host and port in the request URI.')
  302.                     ->children()
  303.                         ->scalarNode('host')
  304.                             ->info('Host name including protocol and optionally the port number, e.g. https://api.local:8000')
  305.                             ->isRequired()
  306.                             ->cannotBeEmpty()
  307.                         ->end()
  308.                         ->scalarNode('replace')
  309.                             ->info('Whether to replace the host if request already specifies one')
  310.                             ->defaultValue(false)
  311.                         ->end()
  312.                     ->end()
  313.                 ->end()
  314.                 ->arrayNode('add_path')
  315.                     ->canBeEnabled()
  316.                     ->addDefaultsIfNotSet()
  317.                     ->info('Add a base path to the request.')
  318.                     ->children()
  319.                         ->scalarNode('path')
  320.                             ->info('Path to be added, e.g. /api/v1')
  321.                             ->isRequired()
  322.                             ->cannotBeEmpty()
  323.                         ->end()
  324.                     ->end()
  325.                 ->end()
  326.                 ->arrayNode('base_uri')
  327.                     ->canBeEnabled()
  328.                     ->addDefaultsIfNotSet()
  329.                     ->info('Set a base URI to the request.')
  330.                     ->children()
  331.                         ->scalarNode('uri')
  332.                             ->info('Base Uri including protocol, optionally the port number and prepend path, e.g. https://api.local:8000/api')
  333.                             ->isRequired()
  334.                             ->cannotBeEmpty()
  335.                         ->end()
  336.                         ->scalarNode('replace')
  337.                             ->info('Whether to replace the host if request already specifies one')
  338.                             ->defaultValue(false)
  339.                         ->end()
  340.                     ->end()
  341.                 ->end()
  342.                 ->arrayNode('content_type')
  343.                     ->canBeEnabled()
  344.                     ->info('Detect the content type of a request body and set the Content-Type header if it is not already set.')
  345.                     ->children()
  346.                         ->booleanNode('skip_detection')
  347.                             ->info('Whether to skip detection when request body is larger than size_limit')
  348.                             ->defaultFalse()
  349.                         ->end()
  350.                         ->scalarNode('size_limit')
  351.                             ->info('Skip content type detection if request body is larger than size_limit bytes')
  352.                         ->end()
  353.                     ->end()
  354.                 ->end()
  355.                 ->arrayNode('header_append')
  356.                     ->canBeEnabled()
  357.                     ->info('Append headers to the request. If the header already exists the value will be appended to the current value.')
  358.                     ->fixXmlConfig('header')
  359.                     ->children()
  360.                         ->arrayNode('headers')
  361.                             ->info('Keys are the header names, values the header values')
  362.                             ->normalizeKeys(false)
  363.                             ->useAttributeAsKey('name')
  364.                             ->prototype('scalar')->end()
  365.                         ->end()
  366.                     ->end()
  367.                 ->end()
  368.                 ->arrayNode('header_defaults')
  369.                     ->canBeEnabled()
  370.                     ->info('Set header to default value if it does not exist.')
  371.                     ->fixXmlConfig('header')
  372.                     ->children()
  373.                         ->arrayNode('headers')
  374.                             ->info('Keys are the header names, values the header values')
  375.                             ->normalizeKeys(false)
  376.                             ->useAttributeAsKey('name')
  377.                             ->prototype('scalar')->end()
  378.                         ->end()
  379.                     ->end()
  380.                 ->end()
  381.                 ->arrayNode('header_set')
  382.                     ->canBeEnabled()
  383.                     ->info('Set headers to requests. If the header does not exist it wil be set, if the header already exists it will be replaced.')
  384.                     ->fixXmlConfig('header')
  385.                     ->children()
  386.                         ->arrayNode('headers')
  387.                             ->info('Keys are the header names, values the header values')
  388.                             ->normalizeKeys(false)
  389.                             ->useAttributeAsKey('name')
  390.                             ->prototype('scalar')->end()
  391.                         ->end()
  392.                     ->end()
  393.                 ->end()
  394.                 ->arrayNode('header_remove')
  395.                     ->canBeEnabled()
  396.                     ->info('Remove headers from requests.')
  397.                     ->fixXmlConfig('header')
  398.                     ->children()
  399.                         ->arrayNode('headers')
  400.                             ->info('List of header names to remove')
  401.                             ->prototype('scalar')->end()
  402.                         ->end()
  403.                     ->end()
  404.                 ->end()
  405.                 ->arrayNode('query_defaults')
  406.                     ->canBeEnabled()
  407.                     ->info('Sets query parameters to default value if they are not present in the request.')
  408.                     ->fixXmlConfig('parameter')
  409.                     ->children()
  410.                         ->arrayNode('parameters')
  411.                             ->info('List of query parameters. Names and values must not be url encoded as the plugin will encode them.')
  412.                             ->normalizeKeys(false)
  413.                             ->useAttributeAsKey('name')
  414.                             ->prototype('scalar')->end()
  415.                         ->end()
  416.                     ->end()
  417.                 ->end()
  418.             ->end()
  419.         ->end();
  420.         return $node;
  421.     }
  422.     /**
  423.      * Add the definitions for shared plugin configurations.
  424.      *
  425.      * @param ArrayNodeDefinition $pluginNode the node to add to
  426.      * @param bool                $disableAll Some shared plugins are enabled by default. On the client, all are disabled by default.
  427.      */
  428.     private function addSharedPluginNodes(ArrayNodeDefinition $pluginNode$disableAll false)
  429.     {
  430.         $children $pluginNode->children();
  431.         $children->append($this->createAuthenticationPluginNode());
  432.         $children->append($this->createCachePluginNode());
  433.         $children
  434.             ->arrayNode('cookie')
  435.                 ->canBeEnabled()
  436.                 ->children()
  437.                     ->scalarNode('cookie_jar')
  438.                         ->info('This must be a service id to a service implementing '.CookieJar::class)
  439.                         ->isRequired()
  440.                         ->cannotBeEmpty()
  441.                     ->end()
  442.                 ->end()
  443.             ->end();
  444.         // End cookie plugin
  445.         $children
  446.             ->arrayNode('history')
  447.                 ->canBeEnabled()
  448.                 ->children()
  449.                     ->scalarNode('journal')
  450.                         ->info('This must be a service id to a service implementing '.Journal::class)
  451.                         ->isRequired()
  452.                         ->cannotBeEmpty()
  453.                     ->end()
  454.                 ->end()
  455.             ->end();
  456.         // End history plugin
  457.         $decoder $children->arrayNode('decoder');
  458.         $disableAll $decoder->canBeEnabled() : $decoder->canBeDisabled();
  459.         $decoder->addDefaultsIfNotSet()
  460.             ->children()
  461.                 ->scalarNode('use_content_encoding')->defaultTrue()->end()
  462.             ->end()
  463.         ->end();
  464.         // End decoder plugin
  465.         $logger $children->arrayNode('logger');
  466.         $disableAll $logger->canBeEnabled() : $logger->canBeDisabled();
  467.         $logger->addDefaultsIfNotSet()
  468.             ->children()
  469.                 ->scalarNode('logger')
  470.                     ->info('This must be a service id to a service implementing '.LoggerInterface::class)
  471.                     ->defaultValue('logger')
  472.                     ->cannotBeEmpty()
  473.                 ->end()
  474.                 ->scalarNode('formatter')
  475.                     ->info('This must be a service id to a service implementing '.Formatter::class)
  476.                     ->defaultNull()
  477.                 ->end()
  478.             ->end()
  479.         ->end();
  480.         // End logger plugin
  481.         $redirect $children->arrayNode('redirect');
  482.         $disableAll $redirect->canBeEnabled() : $redirect->canBeDisabled();
  483.         $redirect->addDefaultsIfNotSet()
  484.             ->children()
  485.                 ->scalarNode('preserve_header')->defaultTrue()->end()
  486.                 ->scalarNode('use_default_for_multiple')->defaultTrue()->end()
  487.             ->end()
  488.         ->end();
  489.         // End redirect plugin
  490.         $retry $children->arrayNode('retry');
  491.         $disableAll $retry->canBeEnabled() : $retry->canBeDisabled();
  492.         $retry->addDefaultsIfNotSet()
  493.             ->children()
  494.                 ->scalarNode('retry')->defaultValue(1)->end() // TODO: should be called retries for consistency with the class
  495.             ->end()
  496.         ->end();
  497.         // End retry plugin
  498.         $stopwatch $children->arrayNode('stopwatch');
  499.         $disableAll $stopwatch->canBeEnabled() : $stopwatch->canBeDisabled();
  500.         $stopwatch->addDefaultsIfNotSet()
  501.             ->children()
  502.                 ->scalarNode('stopwatch')
  503.                     ->info('This must be a service id to a service extending Symfony\Component\Stopwatch\Stopwatch')
  504.                     ->defaultValue('debug.stopwatch')
  505.                     ->cannotBeEmpty()
  506.                 ->end()
  507.             ->end()
  508.         ->end();
  509.         // End stopwatch plugin
  510.     }
  511.     /**
  512.      * Create configuration for authentication plugin.
  513.      *
  514.      * @return NodeDefinition definition for the authentication node in the plugins list
  515.      */
  516.     private function createAuthenticationPluginNode()
  517.     {
  518.         $treeBuilder = new TreeBuilder('authentication');
  519.         // Keep compatibility with symfony/config < 4.2
  520.         if (!method_exists($treeBuilder'getRootNode')) {
  521.             $node $treeBuilder->root('authentication');
  522.         } else {
  523.             $node $treeBuilder->getRootNode();
  524.         }
  525.         $node
  526.             ->useAttributeAsKey('name')
  527.             ->prototype('array')
  528.                 ->validate()
  529.                     ->always()
  530.                     ->then(function ($config) {
  531.                         switch ($config['type']) {
  532.                             case 'basic':
  533.                                 $this->validateAuthenticationType(['username''password'], $config'basic');
  534.                                 break;
  535.                             case 'bearer':
  536.                                 $this->validateAuthenticationType(['token'], $config'bearer');
  537.                                 break;
  538.                             case 'service':
  539.                                 $this->validateAuthenticationType(['service'], $config'service');
  540.                                 break;
  541.                             case 'wsse':
  542.                                 $this->validateAuthenticationType(['username''password'], $config'wsse');
  543.                                 break;
  544.                             case 'query_param':
  545.                                 $this->validateAuthenticationType(['params'], $config'query_param');
  546.                                 break;
  547.                         }
  548.                         return $config;
  549.                     })
  550.                 ->end()
  551.                 ->children()
  552.                     ->enumNode('type')
  553.                         ->values(['basic''bearer''wsse''service''query_param'])
  554.                         ->isRequired()
  555.                         ->cannotBeEmpty()
  556.                     ->end()
  557.                     ->scalarNode('username')->end()
  558.                     ->scalarNode('password')->end()
  559.                     ->scalarNode('token')->end()
  560.                     ->scalarNode('service')->end()
  561.                     ->arrayNode('params')->prototype('scalar')->end()
  562.                     ->end()
  563.                 ->end()
  564.             ->end(); // End authentication plugin
  565.         return $node;
  566.     }
  567.     /**
  568.      * Validate that the configuration fragment has the specified keys and none other.
  569.      *
  570.      * @param array  $expected Fields that must exist
  571.      * @param array  $actual   Actual configuration hashmap
  572.      * @param string $authName Name of authentication method for error messages
  573.      *
  574.      * @throws InvalidConfigurationException If $actual does not have exactly the keys specified in $expected (plus 'type')
  575.      */
  576.     private function validateAuthenticationType(array $expected, array $actual$authName)
  577.     {
  578.         unset($actual['type']);
  579.         // Empty array is always provided, even if the config is not filled.
  580.         if (empty($actual['params'])) {
  581.             unset($actual['params']);
  582.         }
  583.         $actual array_keys($actual);
  584.         sort($actual);
  585.         sort($expected);
  586.         if ($expected === $actual) {
  587.             return;
  588.         }
  589.         throw new InvalidConfigurationException(sprintf(
  590.             'Authentication "%s" requires %s but got %s',
  591.             $authName,
  592.             implode(', '$expected),
  593.             implode(', '$actual)
  594.         ));
  595.     }
  596.     /**
  597.      * Create configuration for cache plugin.
  598.      *
  599.      * @return NodeDefinition definition for the cache node in the plugins list
  600.      */
  601.     private function createCachePluginNode()
  602.     {
  603.         $builder = new TreeBuilder('config');
  604.         // Keep compatibility with symfony/config < 4.2
  605.         if (!method_exists($builder'getRootNode')) {
  606.             $config $builder->root('config');
  607.         } else {
  608.             $config $builder->getRootNode();
  609.         }
  610.         $config
  611.             ->fixXmlConfig('method')
  612.             ->fixXmlConfig('respect_response_cache_directive')
  613.             ->addDefaultsIfNotSet()
  614.             ->validate()
  615.                 ->ifTrue(function ($config) {
  616.                     // Cannot set both respect_cache_headers and respect_response_cache_directives
  617.                     return isset($config['respect_cache_headers'], $config['respect_response_cache_directives']);
  618.                 })
  619.                 ->thenInvalid('You can\'t provide config option "respect_cache_headers" and "respect_response_cache_directives" simultaniously. Use "respect_response_cache_directives" instead.')
  620.             ->end()
  621.             ->children()
  622.                 ->scalarNode('cache_key_generator')
  623.                     ->info('This must be a service id to a service implementing '.CacheKeyGenerator::class)
  624.                 ->end()
  625.                 ->integerNode('cache_lifetime')
  626.                     ->info('The minimum time we should store a cache item')
  627.                 ->end()
  628.                 ->integerNode('default_ttl')
  629.                     ->info('The default max age of a Response')
  630.                 ->end()
  631.                 ->enumNode('hash_algo')
  632.                     ->info('Hashing algorithm to use')
  633.                     ->values(hash_algos())
  634.                     ->cannotBeEmpty()
  635.                 ->end()
  636.                 ->arrayNode('methods')
  637.                     ->info('Which request methods to cache')
  638.                     ->defaultValue(['GET''HEAD'])
  639.                     ->prototype('scalar')
  640.                         ->validate()
  641.                             ->ifTrue(function ($v) {
  642.                                 /* RFC7230 sections 3.1.1 and 3.2.6 except limited to uppercase characters. */
  643.                                 return preg_match('/[^A-Z0-9!#$%&\'*+\-.^_`|~]+/'$v);
  644.                             })
  645.                             ->thenInvalid('Invalid method: %s')
  646.                         ->end()
  647.                     ->end()
  648.                 ->end()
  649.                 ->scalarNode('respect_cache_headers')
  650.                     ->info('Whether we should care about cache headers or not [DEPRECATED]')
  651.                     ->beforeNormalization()
  652.                         ->always(function ($v) {
  653.                             @trigger_error('The option "respect_cache_headers" is deprecated since version 1.3 and will be removed in 2.0. Use "respect_response_cache_directives" instead.'E_USER_DEPRECATED);
  654.                             return $v;
  655.                         })
  656.                     ->end()
  657.                     ->validate()
  658.                         ->ifNotInArray([nulltruefalse])
  659.                         ->thenInvalid('Value for "respect_cache_headers" must be null or boolean')
  660.                     ->end()
  661.                 ->end()
  662.                 ->variableNode('respect_response_cache_directives')
  663.                     ->info('A list of cache directives to respect when caching responses')
  664.                     ->validate()
  665.                         ->always(function ($v) {
  666.                             if (is_null($v) || is_array($v)) {
  667.                                 return $v;
  668.                             }
  669.                             throw new InvalidTypeException();
  670.                         })
  671.                     ->end()
  672.                 ->end()
  673.             ->end()
  674.         ;
  675.         $cache $builder->root('cache');
  676.         $cache
  677.             ->canBeEnabled()
  678.             ->info('Configure HTTP caching, requires the php-http/cache-plugin package')
  679.             ->addDefaultsIfNotSet()
  680.             ->validate()
  681.                 ->ifTrue(function ($v) {
  682.                     return !empty($v['enabled']) && !class_exists(CachePlugin::class);
  683.                 })
  684.                 ->thenInvalid('To use the cache plugin, you need to require php-http/cache-plugin in your project')
  685.             ->end()
  686.             ->children()
  687.                 ->scalarNode('cache_pool')
  688.                     ->info('This must be a service id to a service implementing '.CacheItemPoolInterface::class)
  689.                     ->isRequired()
  690.                     ->cannotBeEmpty()
  691.                 ->end()
  692.                 ->scalarNode('stream_factory')
  693.                     ->info('This must be a service id to a service implementing '.StreamFactory::class)
  694.                     ->defaultValue('httplug.stream_factory')
  695.                     ->cannotBeEmpty()
  696.                 ->end()
  697.             ->end()
  698.             ->append($config)
  699.         ;
  700.         return $cache;
  701.     }
  702. }