Completed
Push — master ( 193986...cdf399 )
by Andrey
11:47
created

HttpCache::getLog()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
7
 *
8
 * This code is partially based on the Rack-Cache library by Ryan Tomayko,
9
 * which is released under the MIT license.
10
 * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801)
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
16
namespace Symfony\Component\HttpKernel\HttpCache;
17
18
use Symfony\Component\HttpKernel\HttpKernelInterface;
19
use Symfony\Component\HttpKernel\TerminableInterface;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\Response;
22
23
/**
24
 * Cache provides HTTP caching.
25
 *
26
 * @author Fabien Potencier <[email protected]>
27
 */
28
class HttpCache implements HttpKernelInterface, TerminableInterface
29
{
30
    private $kernel;
31
    private $store;
32
    private $request;
33
    private $surrogate;
34
    private $surrogateCacheStrategy;
35
    private $options = array();
36
    private $traces = array();
37
38
    /**
39
     * Constructor.
40
     *
41
     * The available options are:
42
     *
43
     *   * debug:                 If true, the traces are added as a HTTP header to ease debugging
44
     *
45
     *   * default_ttl            The number of seconds that a cache entry should be considered
46
     *                            fresh when no explicit freshness information is provided in
47
     *                            a response. Explicit Cache-Control or Expires headers
48
     *                            override this value. (default: 0)
49
     *
50
     *   * private_headers        Set of request headers that trigger "private" cache-control behavior
51
     *                            on responses that don't explicitly state whether the response is
52
     *                            public or private via a Cache-Control directive. (default: Authorization and Cookie)
53
     *
54
     *   * allow_reload           Specifies whether the client can force a cache reload by including a
55
     *                            Cache-Control "no-cache" directive in the request. Set it to ``true``
56
     *                            for compliance with RFC 2616. (default: false)
57
     *
58
     *   * allow_revalidate       Specifies whether the client can force a cache revalidate by including
59
     *                            a Cache-Control "max-age=0" directive in the request. Set it to ``true``
60
     *                            for compliance with RFC 2616. (default: false)
61
     *
62
     *   * stale_while_revalidate Specifies the default number of seconds (the granularity is the second as the
63
     *                            Response TTL precision is a second) during which the cache can immediately return
64
     *                            a stale response while it revalidates it in the background (default: 2).
65
     *                            This setting is overridden by the stale-while-revalidate HTTP Cache-Control
66
     *                            extension (see RFC 5861).
67
     *
68
     *   * stale_if_error         Specifies the default number of seconds (the granularity is the second) during which
69
     *                            the cache can serve a stale response when an error is encountered (default: 60).
70
     *                            This setting is overridden by the stale-if-error HTTP Cache-Control extension
71
     *                            (see RFC 5861).
72
     *
73
     * @param HttpKernelInterface $kernel    An HttpKernelInterface instance
74
     * @param StoreInterface      $store     A Store instance
75
     * @param SurrogateInterface  $surrogate A SurrogateInterface instance
76
     * @param array               $options   An array of options
77
     */
78
    public function __construct(HttpKernelInterface $kernel, StoreInterface $store, SurrogateInterface $surrogate = null, array $options = array())
79
    {
80
        $this->store = $store;
81
        $this->kernel = $kernel;
82
        $this->surrogate = $surrogate;
83
84
        // needed in case there is a fatal error because the backend is too slow to respond
85
        register_shutdown_function(array($this->store, 'cleanup'));
86
87
        $this->options = array_merge(array(
88
            'debug' => false,
89
            'default_ttl' => 0,
90
            'private_headers' => array('Authorization', 'Cookie'),
91
            'allow_reload' => false,
92
            'allow_revalidate' => false,
93
            'stale_while_revalidate' => 2,
94
            'stale_if_error' => 60,
95
        ), $options);
96
    }
97
98
    /**
99
     * Gets the current store.
100
     *
101
     * @return StoreInterface $store A StoreInterface instance
102
     */
103
    public function getStore()
104
    {
105
        return $this->store;
106
    }
107
108
    /**
109
     * Returns an array of events that took place during processing of the last request.
110
     *
111
     * @return array An array of events
112
     */
113
    public function getTraces()
114
    {
115
        return $this->traces;
116
    }
117
118
    /**
119
     * Returns a log message for the events of the last request processing.
120
     *
121
     * @return string A log message
122
     */
123
    public function getLog()
124
    {
125
        $log = array();
126
        foreach ($this->traces as $request => $traces) {
127
            $log[] = sprintf('%s: %s', $request, implode(', ', $traces));
128
        }
129
130
        return implode('; ', $log);
131
    }
132
133
    /**
134
     * Gets the Request instance associated with the master request.
135
     *
136
     * @return Request A Request instance
137
     */
138
    public function getRequest()
139
    {
140
        return $this->request;
141
    }
142
143
    /**
144
     * Gets the Kernel instance.
145
     *
146
     * @return HttpKernelInterface An HttpKernelInterface instance
147
     */
148
    public function getKernel()
149
    {
150
        return $this->kernel;
151
    }
152
153
    /**
154
     * Gets the Surrogate instance.
155
     *
156
     * @return SurrogateInterface A Surrogate instance
157
     *
158
     * @throws \LogicException
159
     */
160
    public function getSurrogate()
161
    {
162
        if (!$this->surrogate instanceof Esi) {
163
            throw new \LogicException('This instance of HttpCache was not set up to use ESI as surrogate handler. You must overwrite and use createSurrogate');
164
        }
165
166
        return $this->surrogate;
167
    }
168
169
    /**
170
     * Gets the Esi instance.
171
     *
172
     * @return Esi An Esi instance
173
     *
174
     * @throws \LogicException
175
     *
176
     * @deprecated since version 2.6, to be removed in 3.0. Use getSurrogate() instead
177
     */
178
    public function getEsi()
179
    {
180
        @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the getSurrogate() method instead.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
181
182
        return $this->getSurrogate();
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
189
    {
190
        // FIXME: catch exceptions and implement a 500 error page here? -> in Varnish, there is a built-in error page mechanism
191
        if (HttpKernelInterface::MASTER_REQUEST === $type) {
192
            $this->traces = array();
193
            $this->request = $request;
194
            if (null !== $this->surrogate) {
195
                $this->surrogateCacheStrategy = $this->surrogate->createCacheStrategy();
196
            }
197
        }
198
199
        $path = $request->getPathInfo();
200
        if ($qs = $request->getQueryString()) {
201
            $path .= '?'.$qs;
202
        }
203
        $this->traces[$request->getMethod().' '.$path] = array();
204
205
        if (!$request->isMethodSafe(false)) {
206
            $response = $this->invalidate($request, $catch);
207
        } elseif ($request->headers->has('expect') || !$request->isMethodCacheable()) {
208
            $response = $this->pass($request, $catch);
209
        } else {
210
            $response = $this->lookup($request, $catch);
211
        }
212
213
        $this->restoreResponseBody($request, $response);
214
215
        $response->setDate(\DateTime::createFromFormat('U', time(), new \DateTimeZone('UTC')));
0 ignored issues
show
Security Bug introduced by
It seems like \DateTime::createFromFor...w \DateTimeZone('UTC')) targeting DateTime::createFromFormat() can also be of type false; however, Symfony\Component\HttpFo...ion\Response::setDate() does only seem to accept object<DateTime>, did you maybe forget to handle an error condition?
Loading history...
216
217
        if (HttpKernelInterface::MASTER_REQUEST === $type && $this->options['debug']) {
218
            $response->headers->set('X-Symfony-Cache', $this->getLog());
219
        }
220
221
        if (null !== $this->surrogate) {
222
            if (HttpKernelInterface::MASTER_REQUEST === $type) {
223
                $this->surrogateCacheStrategy->update($response);
224
            } else {
225
                $this->surrogateCacheStrategy->add($response);
226
            }
227
        }
228
229
        $response->prepare($request);
230
231
        $response->isNotModified($request);
232
233
        return $response;
234
    }
235
236
    /**
237
     * {@inheritdoc}
238
     */
239
    public function terminate(Request $request, Response $response)
240
    {
241
        if ($this->getKernel() instanceof TerminableInterface) {
242
            $this->getKernel()->terminate($request, $response);
243
        }
244
    }
245
246
    /**
247
     * Forwards the Request to the backend without storing the Response in the cache.
248
     *
249
     * @param Request $request A Request instance
250
     * @param bool    $catch   Whether to process exceptions
251
     *
252
     * @return Response A Response instance
253
     */
254
    protected function pass(Request $request, $catch = false)
255
    {
256
        $this->record($request, 'pass');
257
258
        return $this->forward($request, $catch);
259
    }
260
261
    /**
262
     * Invalidates non-safe methods (like POST, PUT, and DELETE).
263
     *
264
     * @param Request $request A Request instance
265
     * @param bool    $catch   Whether to process exceptions
266
     *
267
     * @return Response A Response instance
268
     *
269
     * @throws \Exception
270
     *
271
     * @see RFC2616 13.10
272
     */
273
    protected function invalidate(Request $request, $catch = false)
274
    {
275
        $response = $this->pass($request, $catch);
276
277
        // invalidate only when the response is successful
278
        if ($response->isSuccessful() || $response->isRedirect()) {
279
            try {
280
                $this->store->invalidate($request);
281
282
                // As per the RFC, invalidate Location and Content-Location URLs if present
283
                foreach (array('Location', 'Content-Location') as $header) {
284
                    if ($uri = $response->headers->get($header)) {
285
                        $subRequest = Request::create($uri, 'get', array(), array(), array(), $request->server->all());
0 ignored issues
show
Bug introduced by
It seems like $uri defined by $response->headers->get($header) on line 284 can also be of type array; however, Symfony\Component\HttpFoundation\Request::create() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
286
287
                        $this->store->invalidate($subRequest);
288
                    }
289
                }
290
291
                $this->record($request, 'invalidate');
292
            } catch (\Exception $e) {
293
                $this->record($request, 'invalidate-failed');
294
295
                if ($this->options['debug']) {
296
                    throw $e;
297
                }
298
            }
299
        }
300
301
        return $response;
302
    }
303
304
    /**
305
     * Lookups a Response from the cache for the given Request.
306
     *
307
     * When a matching cache entry is found and is fresh, it uses it as the
308
     * response without forwarding any request to the backend. When a matching
309
     * cache entry is found but is stale, it attempts to "validate" the entry with
310
     * the backend using conditional GET. When no matching cache entry is found,
311
     * it triggers "miss" processing.
312
     *
313
     * @param Request $request A Request instance
314
     * @param bool    $catch   whether to process exceptions
315
     *
316
     * @return Response A Response instance
317
     *
318
     * @throws \Exception
319
     */
320
    protected function lookup(Request $request, $catch = false)
321
    {
322
        // if allow_reload and no-cache Cache-Control, allow a cache reload
323
        if ($this->options['allow_reload'] && $request->isNoCache()) {
324
            $this->record($request, 'reload');
325
326
            return $this->fetch($request, $catch);
327
        }
328
329
        try {
330
            $entry = $this->store->lookup($request);
331
        } catch (\Exception $e) {
332
            $this->record($request, 'lookup-failed');
333
334
            if ($this->options['debug']) {
335
                throw $e;
336
            }
337
338
            return $this->pass($request, $catch);
339
        }
340
341
        if (null === $entry) {
342
            $this->record($request, 'miss');
343
344
            return $this->fetch($request, $catch);
345
        }
346
347
        if (!$this->isFreshEnough($request, $entry)) {
348
            $this->record($request, 'stale');
349
350
            return $this->validate($request, $entry, $catch);
351
        }
352
353
        $this->record($request, 'fresh');
354
355
        $entry->headers->set('Age', $entry->getAge());
356
357
        return $entry;
358
    }
359
360
    /**
361
     * Validates that a cache entry is fresh.
362
     *
363
     * The original request is used as a template for a conditional
364
     * GET request with the backend.
365
     *
366
     * @param Request  $request A Request instance
367
     * @param Response $entry   A Response instance to validate
368
     * @param bool     $catch   Whether to process exceptions
369
     *
370
     * @return Response A Response instance
371
     */
372
    protected function validate(Request $request, Response $entry, $catch = false)
373
    {
374
        $subRequest = clone $request;
375
376
        // send no head requests because we want content
377
        if ('HEAD' === $request->getMethod()) {
378
            $subRequest->setMethod('GET');
379
        }
380
381
        // add our cached last-modified validator
382
        $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified'));
383
384
        // Add our cached etag validator to the environment.
385
        // We keep the etags from the client to handle the case when the client
386
        // has a different private valid entry which is not cached here.
387
        $cachedEtags = $entry->getEtag() ? array($entry->getEtag()) : array();
388
        $requestEtags = $request->getETags();
389
        if ($etags = array_unique(array_merge($cachedEtags, $requestEtags))) {
390
            $subRequest->headers->set('if_none_match', implode(', ', $etags));
391
        }
392
393
        $response = $this->forward($subRequest, $catch, $entry);
394
395
        if (304 == $response->getStatusCode()) {
396
            $this->record($request, 'valid');
397
398
            // return the response and not the cache entry if the response is valid but not cached
399
            $etag = $response->getEtag();
400
            if ($etag && in_array($etag, $requestEtags) && !in_array($etag, $cachedEtags)) {
401
                return $response;
402
            }
403
404
            $entry = clone $entry;
405
            $entry->headers->remove('Date');
406
407
            foreach (array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified') as $name) {
408
                if ($response->headers->has($name)) {
409
                    $entry->headers->set($name, $response->headers->get($name));
410
                }
411
            }
412
413
            $response = $entry;
414
        } else {
415
            $this->record($request, 'invalid');
416
        }
417
418
        if ($response->isCacheable()) {
419
            $this->store($request, $response);
420
        }
421
422
        return $response;
423
    }
424
425
    /**
426
     * Forwards the Request to the backend and determines whether the response should be stored.
427
     *
428
     * This methods is triggered when the cache missed or a reload is required.
429
     *
430
     * @param Request $request A Request instance
431
     * @param bool    $catch   whether to process exceptions
432
     *
433
     * @return Response A Response instance
434
     */
435
    protected function fetch(Request $request, $catch = false)
436
    {
437
        $subRequest = clone $request;
438
439
        // send no head requests because we want content
440
        if ('HEAD' === $request->getMethod()) {
441
            $subRequest->setMethod('GET');
442
        }
443
444
        // avoid that the backend sends no content
445
        $subRequest->headers->remove('if_modified_since');
446
        $subRequest->headers->remove('if_none_match');
447
448
        $response = $this->forward($subRequest, $catch);
449
450
        if ($response->isCacheable()) {
451
            $this->store($request, $response);
452
        }
453
454
        return $response;
455
    }
456
457
    /**
458
     * Forwards the Request to the backend and returns the Response.
459
     *
460
     * @param Request  $request A Request instance
461
     * @param bool     $catch   Whether to catch exceptions or not
462
     * @param Response $entry   A Response instance (the stale entry if present, null otherwise)
463
     *
464
     * @return Response A Response instance
465
     */
466
    protected function forward(Request $request, $catch = false, Response $entry = null)
467
    {
468
        if ($this->surrogate) {
469
            $this->surrogate->addSurrogateCapability($request);
470
        }
471
472
        // modify the X-Forwarded-For header if needed
473
        $forwardedFor = $request->headers->get('X-Forwarded-For');
474
        if ($forwardedFor) {
475
            $request->headers->set('X-Forwarded-For', $forwardedFor.', '.$request->server->get('REMOTE_ADDR'));
476
        } else {
477
            $request->headers->set('X-Forwarded-For', $request->server->get('REMOTE_ADDR'));
478
        }
479
480
        // fix the client IP address by setting it to 127.0.0.1 as HttpCache
481
        // is always called from the same process as the backend.
482
        $request->server->set('REMOTE_ADDR', '127.0.0.1');
483
484
        // make sure HttpCache is a trusted proxy
485
        if (!in_array('127.0.0.1', $trustedProxies = Request::getTrustedProxies())) {
486
            $trustedProxies[] = '127.0.0.1';
487
            Request::setTrustedProxies($trustedProxies);
488
        }
489
490
        // always a "master" request (as the real master request can be in cache)
491
        $response = $this->kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, $catch);
492
        // FIXME: we probably need to also catch exceptions if raw === true
493
494
        // we don't implement the stale-if-error on Requests, which is nonetheless part of the RFC
495
        if (null !== $entry && in_array($response->getStatusCode(), array(500, 502, 503, 504))) {
496
            if (null === $age = $entry->headers->getCacheControlDirective('stale-if-error')) {
497
                $age = $this->options['stale_if_error'];
498
            }
499
500
            if (abs($entry->getTtl()) < $age) {
501
                $this->record($request, 'stale-if-error');
502
503
                return $entry;
504
            }
505
        }
506
507
        $this->processResponseBody($request, $response);
508
509
        if ($this->isPrivateRequest($request) && !$response->headers->hasCacheControlDirective('public')) {
510
            $response->setPrivate();
511
        } elseif ($this->options['default_ttl'] > 0 && null === $response->getTtl() && !$response->headers->getCacheControlDirective('must-revalidate')) {
512
            $response->setTtl($this->options['default_ttl']);
513
        }
514
515
        return $response;
516
    }
517
518
    /**
519
     * Checks whether the cache entry is "fresh enough" to satisfy the Request.
520
     *
521
     * @param Request  $request A Request instance
522
     * @param Response $entry   A Response instance
523
     *
524
     * @return bool true if the cache entry if fresh enough, false otherwise
525
     */
526
    protected function isFreshEnough(Request $request, Response $entry)
527
    {
528
        if (!$entry->isFresh()) {
529
            return $this->lock($request, $entry);
530
        }
531
532
        if ($this->options['allow_revalidate'] && null !== $maxAge = $request->headers->getCacheControlDirective('max-age')) {
533
            return $maxAge > 0 && $maxAge >= $entry->getAge();
534
        }
535
536
        return true;
537
    }
538
539
    /**
540
     * Locks a Request during the call to the backend.
541
     *
542
     * @param Request  $request A Request instance
543
     * @param Response $entry   A Response instance
544
     *
545
     * @return bool true if the cache entry can be returned even if it is staled, false otherwise
546
     */
547
    protected function lock(Request $request, Response $entry)
548
    {
549
        // try to acquire a lock to call the backend
550
        $lock = $this->store->lock($request);
551
552
        // there is already another process calling the backend
553
        if (true !== $lock) {
554
            // check if we can serve the stale entry
555
            if (null === $age = $entry->headers->getCacheControlDirective('stale-while-revalidate')) {
556
                $age = $this->options['stale_while_revalidate'];
557
            }
558
559
            if (abs($entry->getTtl()) < $age) {
560
                $this->record($request, 'stale-while-revalidate');
561
562
                // server the stale response while there is a revalidation
563
                return true;
564
            }
565
566
            // wait for the lock to be released
567
            $wait = 0;
568
            while ($this->store->isLocked($request) && $wait < 5000000) {
569
                usleep(50000);
570
                $wait += 50000;
571
            }
572
573
            if ($wait < 5000000) {
574
                // replace the current entry with the fresh one
575
                $new = $this->lookup($request);
576
                $entry->headers = $new->headers;
577
                $entry->setContent($new->getContent());
578
                $entry->setStatusCode($new->getStatusCode());
579
                $entry->setProtocolVersion($new->getProtocolVersion());
580
                foreach ($new->headers->getCookies() as $cookie) {
581
                    $entry->headers->setCookie($cookie);
582
                }
583
            } else {
584
                // backend is slow as hell, send a 503 response (to avoid the dog pile effect)
585
                $entry->setStatusCode(503);
586
                $entry->setContent('503 Service Unavailable');
587
                $entry->headers->set('Retry-After', 10);
588
            }
589
590
            return true;
591
        }
592
593
        // we have the lock, call the backend
594
        return false;
595
    }
596
597
    /**
598
     * Writes the Response to the cache.
599
     *
600
     * @param Request  $request  A Request instance
601
     * @param Response $response A Response instance
602
     *
603
     * @throws \Exception
604
     */
605
    protected function store(Request $request, Response $response)
606
    {
607
        if (!$response->headers->has('Date')) {
608
            $response->setDate(\DateTime::createFromFormat('U', time()));
0 ignored issues
show
Security Bug introduced by
It seems like \DateTime::createFromFormat('U', time()) targeting DateTime::createFromFormat() can also be of type false; however, Symfony\Component\HttpFo...ion\Response::setDate() does only seem to accept object<DateTime>, did you maybe forget to handle an error condition?
Loading history...
609
        }
610
        try {
611
            $this->store->write($request, $response);
612
613
            $this->record($request, 'store');
614
615
            $response->headers->set('Age', $response->getAge());
616
        } catch (\Exception $e) {
617
            $this->record($request, 'store-failed');
618
619
            if ($this->options['debug']) {
620
                throw $e;
621
            }
622
        }
623
624
        // now that the response is cached, release the lock
625
        $this->store->unlock($request);
626
    }
627
628
    /**
629
     * Restores the Response body.
630
     *
631
     * @param Request  $request  A Request instance
632
     * @param Response $response A Response instance
633
     */
634
    private function restoreResponseBody(Request $request, Response $response)
635
    {
636
        if ($request->isMethod('HEAD') || 304 === $response->getStatusCode()) {
637
            $response->setContent(null);
638
            $response->headers->remove('X-Body-Eval');
639
            $response->headers->remove('X-Body-File');
640
641
            return;
642
        }
643
644
        if ($response->headers->has('X-Body-Eval')) {
645
            ob_start();
646
647
            if ($response->headers->has('X-Body-File')) {
648
                include $response->headers->get('X-Body-File');
649
            } else {
650
                eval('; ?>'.$response->getContent().'<?php ;');
651
            }
652
653
            $response->setContent(ob_get_clean());
654
            $response->headers->remove('X-Body-Eval');
655
            if (!$response->headers->has('Transfer-Encoding')) {
656
                $response->headers->set('Content-Length', strlen($response->getContent()));
657
            }
658
        } elseif ($response->headers->has('X-Body-File')) {
659
            $response->setContent(file_get_contents($response->headers->get('X-Body-File')));
660
        } else {
661
            return;
662
        }
663
664
        $response->headers->remove('X-Body-File');
665
    }
666
667
    protected function processResponseBody(Request $request, Response $response)
668
    {
669
        if (null !== $this->surrogate && $this->surrogate->needsParsing($response)) {
670
            $this->surrogate->process($request, $response);
671
        }
672
    }
673
674
    /**
675
     * Checks if the Request includes authorization or other sensitive information
676
     * that should cause the Response to be considered private by default.
677
     *
678
     * @param Request $request A Request instance
679
     *
680
     * @return bool true if the Request is private, false otherwise
681
     */
682
    private function isPrivateRequest(Request $request)
683
    {
684
        foreach ($this->options['private_headers'] as $key) {
685
            $key = strtolower(str_replace('HTTP_', '', $key));
686
687
            if ('cookie' === $key) {
688
                if (count($request->cookies->all())) {
689
                    return true;
690
                }
691
            } elseif ($request->headers->has($key)) {
692
                return true;
693
            }
694
        }
695
696
        return false;
697
    }
698
699
    /**
700
     * Records that an event took place.
701
     *
702
     * @param Request $request A Request instance
703
     * @param string  $event   The event name
704
     */
705
    private function record(Request $request, $event)
706
    {
707
        $path = $request->getPathInfo();
708
        if ($qs = $request->getQueryString()) {
709
            $path .= '?'.$qs;
710
        }
711
        $this->traces[$request->getMethod().' '.$path][] = $event;
712
    }
713
}
714