1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Symfony\Component\HttpFoundation;
13:
14: 15: 16: 17: 18: 19: 20:
21: class ResponseHeaderBag extends HeaderBag
22: {
23: const COOKIES_FLAT = 'flat';
24: const COOKIES_ARRAY = 'array';
25:
26: const DISPOSITION_ATTACHMENT = 'attachment';
27: const DISPOSITION_INLINE = 'inline';
28:
29: 30: 31:
32: protected $computedCacheControl = array();
33:
34: 35: 36:
37: protected $cookies = array();
38:
39: 40: 41:
42: protected $headerNames = array();
43:
44: 45: 46: 47: 48: 49: 50:
51: public function __construct(array $headers = array())
52: {
53: parent::__construct($headers);
54:
55: if (!isset($this->headers['cache-control'])) {
56: $this->set('Cache-Control', '');
57: }
58: }
59:
60: 61: 62:
63: public function __toString()
64: {
65: $cookies = '';
66: foreach ($this->getCookies() as $cookie) {
67: $cookies .= 'Set-Cookie: '.$cookie."\r\n";
68: }
69:
70: ksort($this->headerNames);
71:
72: return parent::__toString().$cookies;
73: }
74:
75: 76: 77: 78: 79:
80: public function allPreserveCase()
81: {
82: return array_combine($this->headerNames, $this->headers);
83: }
84:
85: 86: 87: 88: 89:
90: public function replace(array $headers = array())
91: {
92: $this->headerNames = array();
93:
94: parent::replace($headers);
95:
96: if (!isset($this->headers['cache-control'])) {
97: $this->set('Cache-Control', '');
98: }
99: }
100:
101: 102: 103: 104: 105:
106: public function set($key, $values, $replace = true)
107: {
108: parent::set($key, $values, $replace);
109:
110: $uniqueKey = strtr(strtolower($key), '_', '-');
111: $this->headerNames[$uniqueKey] = $key;
112:
113:
114: if (in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'))) {
115: $computed = $this->computeCacheControlValue();
116: $this->headers['cache-control'] = array($computed);
117: $this->headerNames['cache-control'] = 'Cache-Control';
118: $this->computedCacheControl = $this->parseCacheControl($computed);
119: }
120: }
121:
122: 123: 124: 125: 126:
127: public function remove($key)
128: {
129: parent::remove($key);
130:
131: $uniqueKey = strtr(strtolower($key), '_', '-');
132: unset($this->headerNames[$uniqueKey]);
133:
134: if ('cache-control' === $uniqueKey) {
135: $this->computedCacheControl = array();
136: }
137: }
138:
139: 140: 141:
142: public function hasCacheControlDirective($key)
143: {
144: return array_key_exists($key, $this->computedCacheControl);
145: }
146:
147: 148: 149:
150: public function getCacheControlDirective($key)
151: {
152: return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
153: }
154:
155: 156: 157: 158: 159: 160: 161:
162: public function setCookie(Cookie $cookie)
163: {
164: $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
165: }
166:
167: 168: 169: 170: 171: 172: 173: 174: 175:
176: public function removeCookie($name, $path = '/', $domain = null)
177: {
178: if (null === $path) {
179: $path = '/';
180: }
181:
182: unset($this->cookies[$domain][$path][$name]);
183:
184: if (empty($this->cookies[$domain][$path])) {
185: unset($this->cookies[$domain][$path]);
186:
187: if (empty($this->cookies[$domain])) {
188: unset($this->cookies[$domain]);
189: }
190: }
191: }
192:
193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203:
204: public function getCookies($format = self::COOKIES_FLAT)
205: {
206: if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) {
207: throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY))));
208: }
209:
210: if (self::COOKIES_ARRAY === $format) {
211: return $this->cookies;
212: }
213:
214: $flattenedCookies = array();
215: foreach ($this->cookies as $path) {
216: foreach ($path as $cookies) {
217: foreach ($cookies as $cookie) {
218: $flattenedCookies[] = $cookie;
219: }
220: }
221: }
222:
223: return $flattenedCookies;
224: }
225:
226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236:
237: public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
238: {
239: $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
240: }
241:
242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256:
257: public function makeDisposition($disposition, $filename, $filenameFallback = '')
258: {
259: if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
260: throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
261: }
262:
263: if ('' == $filenameFallback) {
264: $filenameFallback = $filename;
265: }
266:
267:
268: if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
269: throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
270: }
271:
272:
273: if (false !== strpos($filenameFallback, '%')) {
274: throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
275: }
276:
277:
278: if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
279: throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
280: }
281:
282: $output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback));
283:
284: if ($filename !== $filenameFallback) {
285: $output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
286: }
287:
288: return $output;
289: }
290:
291: 292: 293: 294: 295: 296: 297: 298:
299: protected function computeCacheControlValue()
300: {
301: if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
302: return 'no-cache';
303: }
304:
305: if (!$this->cacheControl) {
306:
307: return 'private, must-revalidate';
308: }
309:
310: $header = $this->getCacheControlHeader();
311: if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
312: return $header;
313: }
314:
315:
316: if (!isset($this->cacheControl['s-maxage'])) {
317: return $header.', private';
318: }
319:
320: return $header;
321: }
322: }
323: