1 |
cb15593b
|
Cajova-Houba
|
<?php
|
2 |
|
|
|
3 |
|
|
/*
|
4 |
|
|
* This file is part of the Symfony package.
|
5 |
|
|
*
|
6 |
|
|
* (c) Fabien Potencier <fabien@symfony.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 Symfony\Component\HttpFoundation;
|
13 |
|
|
|
14 |
|
|
/**
|
15 |
|
|
* ResponseHeaderBag is a container for Response HTTP headers.
|
16 |
|
|
*
|
17 |
|
|
* @author Fabien Potencier <fabien@symfony.com>
|
18 |
|
|
*/
|
19 |
|
|
class ResponseHeaderBag extends HeaderBag
|
20 |
|
|
{
|
21 |
|
|
const COOKIES_FLAT = 'flat';
|
22 |
|
|
const COOKIES_ARRAY = 'array';
|
23 |
|
|
|
24 |
|
|
const DISPOSITION_ATTACHMENT = 'attachment';
|
25 |
|
|
const DISPOSITION_INLINE = 'inline';
|
26 |
|
|
|
27 |
|
|
/**
|
28 |
|
|
* @var array
|
29 |
|
|
*/
|
30 |
|
|
protected $computedCacheControl = array();
|
31 |
|
|
|
32 |
|
|
/**
|
33 |
|
|
* @var array
|
34 |
|
|
*/
|
35 |
|
|
protected $cookies = array();
|
36 |
|
|
|
37 |
|
|
/**
|
38 |
|
|
* @var array
|
39 |
|
|
*/
|
40 |
|
|
protected $headerNames = array();
|
41 |
|
|
|
42 |
|
|
/**
|
43 |
|
|
* Constructor.
|
44 |
|
|
*
|
45 |
|
|
* @param array $headers An array of HTTP headers
|
46 |
|
|
*/
|
47 |
|
|
public function __construct(array $headers = array())
|
48 |
|
|
{
|
49 |
|
|
parent::__construct($headers);
|
50 |
|
|
|
51 |
|
|
if (!isset($this->headers['cache-control'])) {
|
52 |
|
|
$this->set('Cache-Control', '');
|
53 |
|
|
}
|
54 |
|
|
}
|
55 |
|
|
|
56 |
|
|
/**
|
57 |
|
|
* {@inheritdoc}
|
58 |
|
|
*/
|
59 |
|
|
public function __toString()
|
60 |
|
|
{
|
61 |
|
|
$cookies = '';
|
62 |
|
|
foreach ($this->getCookies() as $cookie) {
|
63 |
|
|
$cookies .= 'Set-Cookie: '.$cookie."\r\n";
|
64 |
|
|
}
|
65 |
|
|
|
66 |
|
|
ksort($this->headerNames);
|
67 |
|
|
|
68 |
|
|
return parent::__toString().$cookies;
|
69 |
|
|
}
|
70 |
|
|
|
71 |
|
|
/**
|
72 |
|
|
* Returns the headers, with original capitalizations.
|
73 |
|
|
*
|
74 |
|
|
* @return array An array of headers
|
75 |
|
|
*/
|
76 |
|
|
public function allPreserveCase()
|
77 |
|
|
{
|
78 |
|
|
return array_combine($this->headerNames, $this->headers);
|
79 |
|
|
}
|
80 |
|
|
|
81 |
|
|
/**
|
82 |
|
|
* {@inheritdoc}
|
83 |
|
|
*/
|
84 |
|
|
public function replace(array $headers = array())
|
85 |
|
|
{
|
86 |
|
|
$this->headerNames = array();
|
87 |
|
|
|
88 |
|
|
parent::replace($headers);
|
89 |
|
|
|
90 |
|
|
if (!isset($this->headers['cache-control'])) {
|
91 |
|
|
$this->set('Cache-Control', '');
|
92 |
|
|
}
|
93 |
|
|
}
|
94 |
|
|
|
95 |
|
|
/**
|
96 |
|
|
* {@inheritdoc}
|
97 |
|
|
*/
|
98 |
|
|
public function set($key, $values, $replace = true)
|
99 |
|
|
{
|
100 |
|
|
parent::set($key, $values, $replace);
|
101 |
|
|
|
102 |
|
|
$uniqueKey = str_replace('_', '-', strtolower($key));
|
103 |
|
|
$this->headerNames[$uniqueKey] = $key;
|
104 |
|
|
|
105 |
|
|
// ensure the cache-control header has sensible defaults
|
106 |
|
|
if (in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'))) {
|
107 |
|
|
$computed = $this->computeCacheControlValue();
|
108 |
|
|
$this->headers['cache-control'] = array($computed);
|
109 |
|
|
$this->headerNames['cache-control'] = 'Cache-Control';
|
110 |
|
|
$this->computedCacheControl = $this->parseCacheControl($computed);
|
111 |
|
|
}
|
112 |
|
|
}
|
113 |
|
|
|
114 |
|
|
/**
|
115 |
|
|
* {@inheritdoc}
|
116 |
|
|
*/
|
117 |
|
|
public function remove($key)
|
118 |
|
|
{
|
119 |
|
|
parent::remove($key);
|
120 |
|
|
|
121 |
|
|
$uniqueKey = str_replace('_', '-', strtolower($key));
|
122 |
|
|
unset($this->headerNames[$uniqueKey]);
|
123 |
|
|
|
124 |
|
|
if ('cache-control' === $uniqueKey) {
|
125 |
|
|
$this->computedCacheControl = array();
|
126 |
|
|
}
|
127 |
|
|
}
|
128 |
|
|
|
129 |
|
|
/**
|
130 |
|
|
* {@inheritdoc}
|
131 |
|
|
*/
|
132 |
|
|
public function hasCacheControlDirective($key)
|
133 |
|
|
{
|
134 |
|
|
return array_key_exists($key, $this->computedCacheControl);
|
135 |
|
|
}
|
136 |
|
|
|
137 |
|
|
/**
|
138 |
|
|
* {@inheritdoc}
|
139 |
|
|
*/
|
140 |
|
|
public function getCacheControlDirective($key)
|
141 |
|
|
{
|
142 |
|
|
return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
|
143 |
|
|
}
|
144 |
|
|
|
145 |
|
|
/**
|
146 |
|
|
* Sets a cookie.
|
147 |
|
|
*
|
148 |
|
|
* @param Cookie $cookie
|
149 |
|
|
*/
|
150 |
|
|
public function setCookie(Cookie $cookie)
|
151 |
|
|
{
|
152 |
|
|
$this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
|
153 |
|
|
}
|
154 |
|
|
|
155 |
|
|
/**
|
156 |
|
|
* Removes a cookie from the array, but does not unset it in the browser.
|
157 |
|
|
*
|
158 |
|
|
* @param string $name
|
159 |
|
|
* @param string $path
|
160 |
|
|
* @param string $domain
|
161 |
|
|
*/
|
162 |
|
|
public function removeCookie($name, $path = '/', $domain = null)
|
163 |
|
|
{
|
164 |
|
|
if (null === $path) {
|
165 |
|
|
$path = '/';
|
166 |
|
|
}
|
167 |
|
|
|
168 |
|
|
unset($this->cookies[$domain][$path][$name]);
|
169 |
|
|
|
170 |
|
|
if (empty($this->cookies[$domain][$path])) {
|
171 |
|
|
unset($this->cookies[$domain][$path]);
|
172 |
|
|
|
173 |
|
|
if (empty($this->cookies[$domain])) {
|
174 |
|
|
unset($this->cookies[$domain]);
|
175 |
|
|
}
|
176 |
|
|
}
|
177 |
|
|
}
|
178 |
|
|
|
179 |
|
|
/**
|
180 |
|
|
* Returns an array with all cookies.
|
181 |
|
|
*
|
182 |
|
|
* @param string $format
|
183 |
|
|
*
|
184 |
|
|
* @return array
|
185 |
|
|
*
|
186 |
|
|
* @throws \InvalidArgumentException When the $format is invalid
|
187 |
|
|
*/
|
188 |
|
|
public function getCookies($format = self::COOKIES_FLAT)
|
189 |
|
|
{
|
190 |
|
|
if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) {
|
191 |
|
|
throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY))));
|
192 |
|
|
}
|
193 |
|
|
|
194 |
|
|
if (self::COOKIES_ARRAY === $format) {
|
195 |
|
|
return $this->cookies;
|
196 |
|
|
}
|
197 |
|
|
|
198 |
|
|
$flattenedCookies = array();
|
199 |
|
|
foreach ($this->cookies as $path) {
|
200 |
|
|
foreach ($path as $cookies) {
|
201 |
|
|
foreach ($cookies as $cookie) {
|
202 |
|
|
$flattenedCookies[] = $cookie;
|
203 |
|
|
}
|
204 |
|
|
}
|
205 |
|
|
}
|
206 |
|
|
|
207 |
|
|
return $flattenedCookies;
|
208 |
|
|
}
|
209 |
|
|
|
210 |
|
|
/**
|
211 |
|
|
* Clears a cookie in the browser.
|
212 |
|
|
*
|
213 |
|
|
* @param string $name
|
214 |
|
|
* @param string $path
|
215 |
|
|
* @param string $domain
|
216 |
|
|
* @param bool $secure
|
217 |
|
|
* @param bool $httpOnly
|
218 |
|
|
*/
|
219 |
|
|
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
|
220 |
|
|
{
|
221 |
|
|
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
|
222 |
|
|
}
|
223 |
|
|
|
224 |
|
|
/**
|
225 |
|
|
* Generates a HTTP Content-Disposition field-value.
|
226 |
|
|
*
|
227 |
|
|
* @param string $disposition One of "inline" or "attachment"
|
228 |
|
|
* @param string $filename A unicode string
|
229 |
|
|
* @param string $filenameFallback A string containing only ASCII characters that
|
230 |
|
|
* is semantically equivalent to $filename. If the filename is already ASCII,
|
231 |
|
|
* it can be omitted, or just copied from $filename
|
232 |
|
|
*
|
233 |
|
|
* @return string A string suitable for use as a Content-Disposition field-value
|
234 |
|
|
*
|
235 |
|
|
* @throws \InvalidArgumentException
|
236 |
|
|
*
|
237 |
|
|
* @see RFC 6266
|
238 |
|
|
*/
|
239 |
|
|
public function makeDisposition($disposition, $filename, $filenameFallback = '')
|
240 |
|
|
{
|
241 |
|
|
if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
|
242 |
|
|
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
|
243 |
|
|
}
|
244 |
|
|
|
245 |
|
|
if ('' == $filenameFallback) {
|
246 |
|
|
$filenameFallback = $filename;
|
247 |
|
|
}
|
248 |
|
|
|
249 |
|
|
// filenameFallback is not ASCII.
|
250 |
|
|
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
|
251 |
|
|
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
|
252 |
|
|
}
|
253 |
|
|
|
254 |
|
|
// percent characters aren't safe in fallback.
|
255 |
|
|
if (false !== strpos($filenameFallback, '%')) {
|
256 |
|
|
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
|
257 |
|
|
}
|
258 |
|
|
|
259 |
|
|
// path separators aren't allowed in either.
|
260 |
|
|
if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
|
261 |
|
|
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
|
262 |
|
|
}
|
263 |
|
|
|
264 |
|
|
$output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback));
|
265 |
|
|
|
266 |
|
|
if ($filename !== $filenameFallback) {
|
267 |
|
|
$output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
|
268 |
|
|
}
|
269 |
|
|
|
270 |
|
|
return $output;
|
271 |
|
|
}
|
272 |
|
|
|
273 |
|
|
/**
|
274 |
|
|
* Returns the calculated value of the cache-control header.
|
275 |
|
|
*
|
276 |
|
|
* This considers several other headers and calculates or modifies the
|
277 |
|
|
* cache-control header to a sensible, conservative value.
|
278 |
|
|
*
|
279 |
|
|
* @return string
|
280 |
|
|
*/
|
281 |
|
|
protected function computeCacheControlValue()
|
282 |
|
|
{
|
283 |
|
|
if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
|
284 |
|
|
return 'no-cache';
|
285 |
|
|
}
|
286 |
|
|
|
287 |
|
|
if (!$this->cacheControl) {
|
288 |
|
|
// conservative by default
|
289 |
|
|
return 'private, must-revalidate';
|
290 |
|
|
}
|
291 |
|
|
|
292 |
|
|
$header = $this->getCacheControlHeader();
|
293 |
|
|
if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
|
294 |
|
|
return $header;
|
295 |
|
|
}
|
296 |
|
|
|
297 |
|
|
// public if s-maxage is defined, private otherwise
|
298 |
|
|
if (!isset($this->cacheControl['s-maxage'])) {
|
299 |
|
|
return $header.', private';
|
300 |
|
|
}
|
301 |
|
|
|
302 |
|
|
return $header;
|
303 |
|
|
}
|
304 |
|
|
}
|