Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?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
    protected $computedCacheControl = [];
28
    protected $cookies = [];
29
    protected $headerNames = [];
30
 
31
    public function __construct(array $headers = [])
32
    {
33
        parent::__construct($headers);
34
 
35
        if (!isset($this->headers['cache-control'])) {
36
            $this->set('Cache-Control', '');
37
        }
38
 
39
        /* RFC2616 - 14.18 says all Responses need to have a Date */
40
        if (!isset($this->headers['date'])) {
41
            $this->initDate();
42
        }
43
    }
44
 
45
    /**
46
     * Returns the headers, with original capitalizations.
47
     *
48
     * @return array An array of headers
49
     */
50
    public function allPreserveCase()
51
    {
52
        $headers = [];
53
        foreach ($this->all() as $name => $value) {
54
            $headers[$this->headerNames[$name] ?? $name] = $value;
55
        }
56
 
57
        return $headers;
58
    }
59
 
60
    public function allPreserveCaseWithoutCookies()
61
    {
62
        $headers = $this->allPreserveCase();
63
        if (isset($this->headerNames['set-cookie'])) {
64
            unset($headers[$this->headerNames['set-cookie']]);
65
        }
66
 
67
        return $headers;
68
    }
69
 
70
    /**
71
     * {@inheritdoc}
72
     */
73
    public function replace(array $headers = [])
74
    {
75
        $this->headerNames = [];
76
 
77
        parent::replace($headers);
78
 
79
        if (!isset($this->headers['cache-control'])) {
80
            $this->set('Cache-Control', '');
81
        }
82
 
83
        if (!isset($this->headers['date'])) {
84
            $this->initDate();
85
        }
86
    }
87
 
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function all(string $key = null)
92
    {
93
        $headers = parent::all();
94
 
95
        if (null !== $key) {
96
            $key = strtr($key, self::UPPER, self::LOWER);
97
 
98
            return 'set-cookie' !== $key ? $headers[$key] ?? [] : array_map('strval', $this->getCookies());
99
        }
100
 
101
        foreach ($this->getCookies() as $cookie) {
102
            $headers['set-cookie'][] = (string) $cookie;
103
        }
104
 
105
        return $headers;
106
    }
107
 
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function set(string $key, $values, bool $replace = true)
112
    {
113
        $uniqueKey = strtr($key, self::UPPER, self::LOWER);
114
 
115
        if ('set-cookie' === $uniqueKey) {
116
            if ($replace) {
117
                $this->cookies = [];
118
            }
119
            foreach ((array) $values as $cookie) {
120
                $this->setCookie(Cookie::fromString($cookie));
121
            }
122
            $this->headerNames[$uniqueKey] = $key;
123
 
124
            return;
125
        }
126
 
127
        $this->headerNames[$uniqueKey] = $key;
128
 
129
        parent::set($key, $values, $replace);
130
 
131
        // ensure the cache-control header has sensible defaults
132
        if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true) && '' !== $computed = $this->computeCacheControlValue()) {
133
            $this->headers['cache-control'] = [$computed];
134
            $this->headerNames['cache-control'] = 'Cache-Control';
135
            $this->computedCacheControl = $this->parseCacheControl($computed);
136
        }
137
    }
138
 
139
    /**
140
     * {@inheritdoc}
141
     */
142
    public function remove(string $key)
143
    {
144
        $uniqueKey = strtr($key, self::UPPER, self::LOWER);
145
        unset($this->headerNames[$uniqueKey]);
146
 
147
        if ('set-cookie' === $uniqueKey) {
148
            $this->cookies = [];
149
 
150
            return;
151
        }
152
 
153
        parent::remove($key);
154
 
155
        if ('cache-control' === $uniqueKey) {
156
            $this->computedCacheControl = [];
157
        }
158
 
159
        if ('date' === $uniqueKey) {
160
            $this->initDate();
161
        }
162
    }
163
 
164
    /**
165
     * {@inheritdoc}
166
     */
167
    public function hasCacheControlDirective(string $key)
168
    {
169
        return \array_key_exists($key, $this->computedCacheControl);
170
    }
171
 
172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function getCacheControlDirective(string $key)
176
    {
177
        return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
178
    }
179
 
180
    public function setCookie(Cookie $cookie)
181
    {
182
        $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
183
        $this->headerNames['set-cookie'] = 'Set-Cookie';
184
    }
185
 
186
    /**
187
     * Removes a cookie from the array, but does not unset it in the browser.
188
     */
189
    public function removeCookie(string $name, ?string $path = '/', string $domain = null)
190
    {
191
        if (null === $path) {
192
            $path = '/';
193
        }
194
 
195
        unset($this->cookies[$domain][$path][$name]);
196
 
197
        if (empty($this->cookies[$domain][$path])) {
198
            unset($this->cookies[$domain][$path]);
199
 
200
            if (empty($this->cookies[$domain])) {
201
                unset($this->cookies[$domain]);
202
            }
203
        }
204
 
205
        if (empty($this->cookies)) {
206
            unset($this->headerNames['set-cookie']);
207
        }
208
    }
209
 
210
    /**
211
     * Returns an array with all cookies.
212
     *
213
     * @return Cookie[]
214
     *
215
     * @throws \InvalidArgumentException When the $format is invalid
216
     */
217
    public function getCookies(string $format = self::COOKIES_FLAT)
218
    {
219
        if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) {
220
            throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY])));
221
        }
222
 
223
        if (self::COOKIES_ARRAY === $format) {
224
            return $this->cookies;
225
        }
226
 
227
        $flattenedCookies = [];
228
        foreach ($this->cookies as $path) {
229
            foreach ($path as $cookies) {
230
                foreach ($cookies as $cookie) {
231
                    $flattenedCookies[] = $cookie;
232
                }
233
            }
234
        }
235
 
236
        return $flattenedCookies;
237
    }
238
 
239
    /**
240
     * Clears a cookie in the browser.
241
     */
242
    public function clearCookie(string $name, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true, string $sameSite = null)
243
    {
244
        $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, $sameSite));
245
    }
246
 
247
    /**
248
     * @see HeaderUtils::makeDisposition()
249
     */
250
    public function makeDisposition(string $disposition, string $filename, string $filenameFallback = '')
251
    {
252
        return HeaderUtils::makeDisposition($disposition, $filename, $filenameFallback);
253
    }
254
 
255
    /**
256
     * Returns the calculated value of the cache-control header.
257
     *
258
     * This considers several other headers and calculates or modifies the
259
     * cache-control header to a sensible, conservative value.
260
     *
261
     * @return string
262
     */
263
    protected function computeCacheControlValue()
264
    {
265
        if (!$this->cacheControl) {
266
            if ($this->has('Last-Modified') || $this->has('Expires')) {
267
                return 'private, must-revalidate'; // allows for heuristic expiration (RFC 7234 Section 4.2.2) in the case of "Last-Modified"
268
            }
269
 
270
            // conservative by default
271
            return 'no-cache, private';
272
        }
273
 
274
        $header = $this->getCacheControlHeader();
275
        if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
276
            return $header;
277
        }
278
 
279
        // public if s-maxage is defined, private otherwise
280
        if (!isset($this->cacheControl['s-maxage'])) {
281
            return $header.', private';
282
        }
283
 
284
        return $header;
285
    }
286
 
287
    private function initDate(): void
288
    {
289
        $this->set('Date', gmdate('D, d M Y H:i:s').' GMT');
290
    }
291
}