Skip to content

Commit 8ebab9c

Browse files
committed
cleanup event dispatching and add debug subscriber
1 parent 34619f1 commit 8ebab9c

File tree

6 files changed

+118
-69
lines changed

6 files changed

+118
-69
lines changed

src/SymfonyCache/DebugSubscriber.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCache 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\HttpCache\SymfonyCache;
13+
14+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\RequestMatcher;
17+
use Symfony\Component\HttpFoundation\Response;
18+
use Symfony\Component\HttpKernel\HttpKernelInterface;
19+
use Symfony\Component\OptionsResolver\OptionsResolver;
20+
21+
/**
22+
* Debug handler for the symfony built-in HttpCache.
23+
*
24+
* Add debug information to the response for use in cache tests.
25+
*
26+
* @author David Buchmann <[email protected]>
27+
*
28+
* {@inheritdoc}
29+
*/
30+
class DebugSubscriber implements EventSubscriberInterface
31+
{
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public static function getSubscribedEvents()
36+
{
37+
return [
38+
Events::POST_HANDLE => 'handleDebug',
39+
];
40+
}
41+
42+
/**
43+
* Extract the cache HIT/MISS information from the X-Symfony-Cache header.
44+
*
45+
* For this header to be present, the HttpCache must be created with the
46+
* debug option set to true.
47+
*
48+
* @param CacheEvent $event
49+
*/
50+
public function handleDebug(CacheEvent $event)
51+
{
52+
$response = $event->getResponse();
53+
if ($response->headers->has('X-Symfony-Cache')) {
54+
if (false !== strpos($response->headers->get('X-Symfony-Cache'), 'miss')) {
55+
$state = 'MISS';
56+
} elseif (false !== strpos($response->headers->get('X-Symfony-Cache'), 'fresh')) {
57+
$state = 'HIT';
58+
} else {
59+
$state = 'UNDETERMINED';
60+
}
61+
$response->headers->set('X-Cache', $state);
62+
}
63+
}
64+
}

src/SymfonyCache/EventDispatchingHttpCache.php

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,21 @@
1919
use Symfony\Component\HttpKernel\HttpKernelInterface;
2020

2121
/**
22-
* Trait for enhanced Symfony reverse proxy based on the symfony component.
22+
* Trait for enhanced Symfony reverse proxy based on the symfony kernel component.
2323
*
24-
* <b>When using FOSHttpCacheBundle, look at FOS\HttpCacheBundle\HttpCache instead.</b>
24+
* Your kernel needs to implement CacheInvalidatorInterface and redeclare the
25+
* fetch method as public. (The later is needed because the trait declaring it
26+
* public does not satisfy the interface for whatever reason. See also
27+
* http://stackoverflow.com/questions/31877844/php-trait-exposing-a-method-and-interfaces )
2528
*
26-
* This kernel supports event subscribers that can act on the events defined in
27-
* FOS\HttpCache\SymfonyCache\Events and may alter the request flow.
29+
* CacheInvalidator kernels support event subscribers that can act on the
30+
* events defined in FOS\HttpCache\SymfonyCache\Events and may alter the
31+
* request flow.
32+
*
33+
* If your kernel overwrites any of the methods defined in this trait, make
34+
* sure to also call the trait method. You might get into issues with the order
35+
* of events, in which case you will need to copy event triggering into your
36+
* kernel.
2837
*
2938
* @author Jérôme Vieilledent <[email protected]> (courtesy of eZ Systems AS)
3039
*
@@ -68,17 +77,13 @@ public function addSubscriber(EventSubscriberInterface $subscriber)
6877
*/
6978
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
7079
{
71-
if ($this->getEventDispatcher()->hasListeners(Events::PRE_HANDLE)) {
72-
$event = new CacheEvent($this, $request);
73-
$this->getEventDispatcher()->dispatch(Events::PRE_HANDLE, $event);
74-
if ($event->getResponse()) {
75-
return $this->dispatchPostHandle($request, $event->getResponse());
76-
}
80+
if ($response = $this->dispatch(Events::PRE_HANDLE, $request)) {
81+
return $this->dispatch(Events::POST_HANDLE, $request, $response);
7782
}
7883

7984
$response = parent::handle($request, $type, $catch);
8085

81-
return $this->dispatchPostHandle($request, $response);
86+
return $this->dispatch(Events::POST_HANDLE, $request, $response);
8287
}
8388

8489
/**
@@ -88,57 +93,40 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ
8893
*/
8994
protected function store(Request $request, Response $response)
9095
{
91-
if ($this->getEventDispatcher()->hasListeners(Events::PRE_STORE)) {
92-
$event = new CacheEvent($this, $request, $response);
93-
$this->getEventDispatcher()->dispatch(Events::PRE_STORE, $event);
94-
$response = $event->getResponse();
95-
}
96+
$response = $this->dispatch(Events::PRE_STORE, $request, $response);
9697

9798
parent::store($request, $response);
9899
}
99100

100101
/**
101-
* Dispatch the POST_HANDLE event if needed.
102+
* Dispatch an event if needed.
102103
*
103-
* @param Request $request
104-
* @param Response $response
104+
* @param string $name Name of the event to trigger. One of the constants in FOS\HttpCache\SymfonyCache\Events
105+
* @param Request $request
106+
* @param Response|null $response If already available
105107
*
106-
* @return Response The response to return which might be altered by a POST_HANDLE listener.
108+
* @return Response The response to return, which might be provided/altered by a listener.
107109
*/
108-
private function dispatchPostHandle(Request $request, Response $response)
110+
protected function dispatch($name, Request $request, Response $response = null)
109111
{
110-
if ($this->getEventDispatcher()->hasListeners(Events::POST_HANDLE)) {
112+
if ($this->getEventDispatcher()->hasListeners($name)) {
111113
$event = new CacheEvent($this, $request, $response);
112-
$this->getEventDispatcher()->dispatch(Events::POST_HANDLE, $event);
114+
$this->getEventDispatcher()->dispatch($name, $event);
113115
$response = $event->getResponse();
114116
}
115117

116118
return $response;
117119
}
118120

119-
/**
120-
* Made public to allow event subscribers to do refresh operations.
121-
*
122-
* {@inheritDoc}
123-
*/
124-
public function fetch(Request $request, $catch = false)
125-
{
126-
return parent::fetch($request, $catch);
127-
}
128-
129121
/**
130122
* {@inheritDoc}
131123
*
132124
* Adding the Events::PRE_INVALIDATE event.
133125
*/
134126
protected function invalidate(Request $request, $catch = false)
135127
{
136-
if ($this->getEventDispatcher()->hasListeners(Events::PRE_INVALIDATE)) {
137-
$event = new CacheEvent($this, $request);
138-
$this->getEventDispatcher()->dispatch(Events::PRE_INVALIDATE, $event);
139-
if ($event->getResponse()) {
140-
return $event->getResponse();
141-
}
128+
if ($response = $this->dispatch(Events::PRE_INVALIDATE, $request)) {
129+
return $response;
142130
}
143131

144132
return parent::invalidate($request, $catch);

src/Test/PHPUnit/AbstractCacheConstraint.php

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace FOS\HttpCache\Test\PHPUnit;
1313

14+
use GuzzleHttp\Psr7\Response;
15+
1416
/**
1517
* Abstract cache constraint
1618
*/
@@ -41,17 +43,25 @@ abstract public function getValue();
4143

4244
/**
4345
* {@inheritdoc}
46+
*
47+
* @param Response $other The guzzle response object
4448
*/
4549
protected function matches($other)
4650
{
51+
if (!$other instanceof Response) {
52+
throw new \RuntimeException('Expected a GuzzleHttp\Psr7\Response but got %s', get_class($other));
53+
}
4754
if (!$other->hasHeader($this->header)) {
48-
throw new \RuntimeException(
49-
sprintf(
50-
'Response has no "%s" header. Configure your caching proxy '
51-
. 'to set the header with cache hit/miss status.',
52-
$this->header
53-
)
55+
$message = sprintf(
56+
'Response has no "%s" header. Configure your caching proxy '
57+
. 'to set the header with cache hit/miss status.',
58+
$this->header
5459
);
60+
if (200 !== $other->getStatusCode()) {
61+
$message .= sprintf("\nStatus code of response is %s.", $other->getStatusCode());
62+
}
63+
64+
throw new \RuntimeException($message);
5565
}
5666

5767
return $this->getValue() === (string) $other->getHeaderLine($this->header);

tests/Functional/Fixtures/Symfony/AppCache.php

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,7 @@
1010

1111
class AppCache extends HttpCache implements CacheInvalidationInterface
1212
{
13-
// http://stackoverflow.com/questions/31877844/php-trait-exposing-a-method-and-interfaces
14-
use EventDispatchingHttpCache {fetch as public eventTriggeringFetch;}
15-
16-
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
17-
{
18-
$response = parent::handle($request, $type, $catch);
19-
20-
if ($response->headers->has('X-Symfony-Cache')) {
21-
if (false !== strpos($response->headers->get('X-Symfony-Cache'), 'miss')) {
22-
$state = 'MISS';
23-
} elseif (false !== strpos($response->headers->get('X-Symfony-Cache'), 'fresh')) {
24-
$state = 'HIT';
25-
} else {
26-
$state = 'UNDETERMINED';
27-
}
28-
$response->headers->set('X-Cache', $state);
29-
}
30-
31-
return $response;
32-
}
13+
use EventDispatchingHttpCache;
3314

3415
/**
3516
* Made public to allow event subscribers to do refresh operations.
@@ -38,6 +19,6 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ
3819
*/
3920
public function fetch(Request $request, $catch = false)
4021
{
41-
return $this->eventTriggeringFetch($request, $catch);
22+
return parent::fetch($request, $catch);
4223
}
4324
}

tests/Functional/Fixtures/web/symfony.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
$kernel->addSubscriber(new PurgeSubscriber(['purge_method' => 'NOTIFY']));
1919
$kernel->addSubscriber(new RefreshSubscriber());
2020
$kernel->addSubscriber(new UserContextSubscriber());
21+
$kernel->addSubscriber(new \FOS\HttpCache\SymfonyCache\DebugSubscriber());
2122
$request = Request::createFromGlobals();
2223
$response = $kernel->handle($request);
2324
$response->send();

tests/Unit/SymfonyCache/EventDispatchingHttpCacheTest.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,16 @@ public function testAbortInvalidate()
146146

147147
class AppCache extends HttpCache implements CacheInvalidationInterface
148148
{
149-
// http://stackoverflow.com/questions/31877844/php-trait-exposing-a-method-and-interfaces
150-
use EventDispatchingHttpCache {fetch as public eventTriggeringFetch;}
149+
use EventDispatchingHttpCache;
150+
151+
/**
152+
* Made public to allow event subscribers to do refresh operations.
153+
*
154+
* {@inheritDoc}
155+
*/
151156
public function fetch(Request $request, $catch = false)
152157
{
153-
return $this->eventTriggeringFetch($request, $catch);
158+
parent::fetch($request, $catch);
154159
}
155160
}
156161

0 commit comments

Comments
 (0)