|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Debril\RssAtomBundle\Controller; |
|
4
|
|
|
|
|
5
|
|
|
use Debril\RssAtomBundle\Protocol\FeedFormatter; |
|
6
|
|
|
use Debril\RssAtomBundle\Protocol\FeedOutInterface; |
|
7
|
|
|
use FeedIo\Feed; |
|
8
|
|
|
use Symfony\Component\HttpFoundation\Response; |
|
9
|
|
|
use Symfony\Component\HttpFoundation\Request; |
|
10
|
|
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
|
11
|
|
|
use Debril\RssAtomBundle\Provider\FeedContentProviderInterface; |
|
12
|
|
|
use Debril\RssAtomBundle\Exception\FeedException\FeedNotFoundException; |
|
13
|
|
|
|
|
14
|
|
|
/** |
|
15
|
|
|
* Class StreamController. |
|
16
|
|
|
*/ |
|
17
|
|
|
class StreamController extends Controller |
|
18
|
|
|
{ |
|
19
|
|
|
/** |
|
20
|
|
|
* default provider. |
|
21
|
|
|
*/ |
|
22
|
|
|
const DEFAULT_SOURCE = 'debril.provider.default'; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* parameter used to force refresh at every hit (skips 'If-Modified-Since' usage). |
|
26
|
|
|
* set it to true for debug purpose. |
|
27
|
|
|
*/ |
|
28
|
|
|
const FORCE_PARAM_NAME = 'force_refresh'; |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* @var \DateTime |
|
32
|
|
|
*/ |
|
33
|
|
|
protected $since; |
|
34
|
|
|
|
|
35
|
|
|
/** |
|
36
|
|
|
* @param Request $request |
|
37
|
|
|
* |
|
38
|
|
|
* @return Response |
|
39
|
|
|
*/ |
|
40
|
3 |
|
public function indexAction(Request $request) |
|
41
|
|
|
{ |
|
42
|
3 |
|
$options = $request->attributes->get('_route_params'); |
|
43
|
3 |
|
$this->setModifiedSince($request); |
|
44
|
3 |
|
$options['Since'] = $this->getModifiedSince(); |
|
45
|
|
|
|
|
46
|
3 |
|
return $this->createStreamResponse( |
|
47
|
3 |
|
$options, |
|
48
|
3 |
|
$request->get('format', 'rss'), |
|
49
|
3 |
|
$request->get('source', self::DEFAULT_SOURCE) |
|
50
|
3 |
|
); |
|
51
|
|
|
} |
|
52
|
|
|
|
|
53
|
|
|
/** |
|
54
|
|
|
* Extract the 'If-Modified-Since' value from the headers. |
|
55
|
|
|
* |
|
56
|
|
|
* @return \DateTime |
|
57
|
|
|
*/ |
|
58
|
3 |
|
protected function getModifiedSince() |
|
59
|
|
|
{ |
|
60
|
3 |
|
if (is_null($this->since)) { |
|
61
|
|
|
$this->since = new \DateTime('@0'); |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
3 |
|
return $this->since; |
|
65
|
|
|
} |
|
66
|
|
|
|
|
67
|
|
|
/** |
|
68
|
|
|
* @param Request $request |
|
69
|
|
|
* |
|
70
|
|
|
* @return $this |
|
71
|
|
|
*/ |
|
72
|
3 |
|
protected function setModifiedSince(Request $request) |
|
73
|
|
|
{ |
|
74
|
3 |
|
$this->since = new \DateTime(); |
|
75
|
3 |
|
if ($request->headers->has('If-Modified-Since')) { |
|
76
|
1 |
|
$string = $request->headers->get('If-Modified-Since'); |
|
77
|
1 |
|
$this->since = \DateTime::createFromFormat(\DateTime::RSS, $string); |
|
78
|
1 |
|
} else { |
|
79
|
3 |
|
$this->since->setTimestamp(1); |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
3 |
|
return $this; |
|
83
|
|
|
} |
|
84
|
|
|
|
|
85
|
|
|
/** |
|
86
|
|
|
* Generate the HTTP response |
|
87
|
|
|
* 200 : a full body containing the stream |
|
88
|
|
|
* 304 : Not modified. |
|
89
|
|
|
* |
|
90
|
|
|
* @param array $options |
|
91
|
|
|
* @param $format |
|
92
|
|
|
* @param string $source |
|
93
|
|
|
* |
|
94
|
|
|
* @return Response |
|
95
|
|
|
* |
|
96
|
|
|
* @throws \Exception |
|
97
|
|
|
*/ |
|
98
|
3 |
|
protected function createStreamResponse(array $options, $format, $source = self::DEFAULT_SOURCE) |
|
99
|
|
|
{ |
|
100
|
3 |
|
$content = $this->getContent($options, $source); |
|
101
|
|
|
|
|
102
|
1 |
|
if ($this->mustForceRefresh() || $content->getLastModified() > $this->getModifiedSince()) { |
|
|
|
|
|
|
103
|
1 |
|
$response = new Response($this->getStringOutput($content, $format)); |
|
104
|
1 |
|
$response->headers->set('Content-Type', 'application/xhtml+xml'); |
|
105
|
|
|
|
|
106
|
1 |
|
if (! $this->container->getParameter('debril_rss_atom.private_feeds')) { |
|
107
|
1 |
|
$response->setPublic(); |
|
108
|
1 |
|
} |
|
109
|
|
|
|
|
110
|
1 |
|
$response->setMaxAge(3600); |
|
111
|
1 |
|
$response->setLastModified($content->getLastModified()); |
|
|
|
|
|
|
112
|
1 |
|
} else { |
|
113
|
1 |
|
$response = new Response(); |
|
114
|
1 |
|
$response->setNotModified(); |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
1 |
|
return $response; |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
/** |
|
121
|
|
|
* @param $feed |
|
122
|
|
|
* @param $format |
|
123
|
|
|
* @return string |
|
124
|
|
|
* @throws \Exception |
|
125
|
|
|
*/ |
|
126
|
1 |
|
protected function getStringOutput($feed, $format) |
|
127
|
|
|
{ |
|
128
|
1 |
|
if ( $feed instanceof Feed ) { |
|
129
|
1 |
|
return $this->getFeedIo()->format($feed, $format)->saveXML(); |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
$formatter = $this->getFormatter($format); |
|
133
|
|
|
return $formatter->toString($feed); |
|
|
|
|
|
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
/** |
|
137
|
|
|
* Get the Stream's content using a FeedContentProviderInterface |
|
138
|
|
|
* The FeedContentProviderInterface instance is provided as a service |
|
139
|
|
|
* default : debril.provider.service. |
|
140
|
|
|
* |
|
141
|
|
|
* @param array $options |
|
142
|
|
|
* @param string $source |
|
143
|
|
|
* |
|
144
|
|
|
* @return FeedOutInterface |
|
145
|
|
|
* |
|
146
|
|
|
* @throws \Exception |
|
147
|
|
|
*/ |
|
148
|
3 |
|
protected function getContent(array $options, $source) |
|
149
|
|
|
{ |
|
150
|
3 |
|
$provider = $this->get($source); |
|
151
|
|
|
|
|
152
|
3 |
|
if (!$provider instanceof FeedContentProviderInterface) { |
|
153
|
1 |
|
throw new \Exception('Provider is not a FeedContentProviderInterface instance'); |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
try { |
|
157
|
2 |
|
return $provider->getFeedContent($options); |
|
158
|
1 |
|
} catch (FeedNotFoundException $e) { |
|
159
|
1 |
|
throw $this->createNotFoundException('feed not found'); |
|
160
|
|
|
} |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
/** |
|
164
|
|
|
* Returns true if the controller must ignore the last modified date. |
|
165
|
|
|
* |
|
166
|
|
|
* @return bool |
|
167
|
|
|
*/ |
|
168
|
1 |
|
protected function mustForceRefresh() |
|
169
|
|
|
{ |
|
170
|
1 |
|
if ($this->container->hasParameter(self::FORCE_PARAM_NAME)) { |
|
171
|
|
|
return $this->container->getParameter(self::FORCE_PARAM_NAME); |
|
172
|
|
|
} |
|
173
|
|
|
|
|
174
|
1 |
|
return false; |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
/** |
|
178
|
|
|
* Get the accurate formatter. |
|
179
|
|
|
* |
|
180
|
|
|
* @param string $format |
|
181
|
|
|
* |
|
182
|
|
|
* @throws \Exception |
|
183
|
|
|
* |
|
184
|
|
|
* @return FeedFormatter |
|
185
|
|
|
*/ |
|
186
|
|
|
protected function getFormatter($format) |
|
187
|
|
|
{ |
|
188
|
|
|
$services = array( |
|
189
|
|
|
'rss' => 'debril.formatter.rss', |
|
190
|
|
|
'atom' => 'debril.formatter.atom', |
|
191
|
|
|
); |
|
192
|
|
|
|
|
193
|
|
|
if (!array_key_exists($format, $services)) { |
|
194
|
|
|
throw new \Exception("Unsupported format {$format}"); |
|
195
|
|
|
} |
|
196
|
|
|
|
|
197
|
|
|
return $this->get($services[$format]); |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
/** |
|
201
|
|
|
* @return \FeedIo\FeedIo |
|
202
|
|
|
*/ |
|
203
|
1 |
|
protected function getFeedIo() |
|
204
|
|
|
{ |
|
205
|
1 |
|
return $this->container->get('feedio'); |
|
206
|
|
|
} |
|
207
|
|
|
} |
|
208
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.