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