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