审查视图

vendor/symfony/http-foundation/HeaderBag.php 8.9 KB
王智 authored
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\HttpFoundation;

/**
 * HeaderBag is a container for HTTP headers.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class HeaderBag implements \IteratorAggregate, \Countable
{
    protected $headers = [];
    protected $cacheControl = [];
王智 authored
24 25 26
    /**
     * @param array $headers An array of HTTP headers
     */
王智 authored
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
    public function __construct(array $headers = [])
    {
        foreach ($headers as $key => $values) {
            $this->set($key, $values);
        }
    }

    /**
     * Returns the headers as a string.
     *
     * @return string The headers
     */
    public function __toString()
    {
        if (!$headers = $this->all()) {
            return '';
        }

        ksort($headers);
        $max = max(array_map('strlen', array_keys($headers))) + 1;
        $content = '';
        foreach ($headers as $name => $values) {
王智 authored
49
            $name = implode('-', array_map('ucfirst', explode('-', $name)));
王智 authored
50 51 52 53 54 55 56 57 58 59 60 61 62
            foreach ($values as $value) {
                $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value);
            }
        }

        return $content;
    }

    /**
     * Returns the headers.
     *
     * @return array An array of headers
     */
王智 authored
63
    public function all()
王智 authored
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
    {
        return $this->headers;
    }

    /**
     * Returns the parameter keys.
     *
     * @return array An array of parameter keys
     */
    public function keys()
    {
        return array_keys($this->all());
    }

    /**
     * Replaces the current HTTP headers by a new set.
王智 authored
80 81
     *
     * @param array $headers An array of HTTP headers
王智 authored
82 83 84 85 86 87 88 89 90
     */
    public function replace(array $headers = [])
    {
        $this->headers = [];
        $this->add($headers);
    }

    /**
     * Adds new headers the current HTTP headers set.
王智 authored
91 92
     *
     * @param array $headers An array of HTTP headers
王智 authored
93 94 95 96 97 98 99 100 101 102 103 104 105
     */
    public function add(array $headers)
    {
        foreach ($headers as $key => $values) {
            $this->set($key, $values);
        }
    }

    /**
     * Returns a header value by name.
     *
     * @param string      $key     The header name
     * @param string|null $default The default value
王智 authored
106
     * @param bool        $first   Whether to return the first value or all header values
王智 authored
107
     *
王智 authored
108
     * @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise
王智 authored
109
     */
王智 authored
110
    public function get($key, $default = null, $first = true)
王智 authored
111
    {
王智 authored
112 113
        $key = str_replace('_', '-', strtolower($key));
        $headers = $this->all();
王智 authored
114
王智 authored
115 116 117
        if (!\array_key_exists($key, $headers)) {
            if (null === $default) {
                return $first ? null : [];
王智 authored
118 119
            }
王智 authored
120
            return $first ? $default : [$default];
王智 authored
121 122
        }
王智 authored
123 124 125 126 127 128 129 130 131 132
        if ($first) {
            if (!$headers[$key]) {
                return $default;
            }

            if (null === $headers[$key][0]) {
                return null;
            }

            return (string) $headers[$key][0];
王智 authored
133 134
        }
王智 authored
135
        return $headers[$key];
王智 authored
136 137 138 139 140 141 142 143 144 145 146
    }

    /**
     * Sets a header by name.
     *
     * @param string          $key     The key
     * @param string|string[] $values  The value or an array of values
     * @param bool            $replace Whether to replace the actual value or not (true by default)
     */
    public function set($key, $values, $replace = true)
    {
王智 authored
147
        $key = str_replace('_', '-', strtolower($key));
王智 authored
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

        if (\is_array($values)) {
            $values = array_values($values);

            if (true === $replace || !isset($this->headers[$key])) {
                $this->headers[$key] = $values;
            } else {
                $this->headers[$key] = array_merge($this->headers[$key], $values);
            }
        } else {
            if (true === $replace || !isset($this->headers[$key])) {
                $this->headers[$key] = [$values];
            } else {
                $this->headers[$key][] = $values;
            }
        }

        if ('cache-control' === $key) {
            $this->cacheControl = $this->parseCacheControl(implode(', ', $this->headers[$key]));
        }
    }

    /**
     * Returns true if the HTTP header is defined.
     *
     * @param string $key The HTTP header
     *
     * @return bool true if the parameter exists, false otherwise
     */
    public function has($key)
    {
王智 authored
179
        return \array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
王智 authored
180 181 182 183 184 185 186 187 188 189 190 191
    }

    /**
     * Returns true if the given HTTP header contains the given value.
     *
     * @param string $key   The HTTP header name
     * @param string $value The HTTP value
     *
     * @return bool true if the value is contained in the header, false otherwise
     */
    public function contains($key, $value)
    {
王智 authored
192
        return \in_array($value, $this->get($key, null, false));
王智 authored
193 194 195 196 197 198 199 200 201
    }

    /**
     * Removes a header.
     *
     * @param string $key The HTTP header name
     */
    public function remove($key)
    {
王智 authored
202
        $key = str_replace('_', '-', strtolower($key));
王智 authored
203 204 205 206 207 208 209 210 211 212 213

        unset($this->headers[$key]);

        if ('cache-control' === $key) {
            $this->cacheControl = [];
        }
    }

    /**
     * Returns the HTTP header value converted to a date.
     *
王智 authored
214 215
     * @param string    $key     The parameter key
     * @param \DateTime $default The default value
王智 authored
216
     *
王智 authored
217
     * @return \DateTime|null The parsed DateTime or the default value if the header does not exist
王智 authored
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
     *
     * @throws \RuntimeException When the HTTP header is not parseable
     */
    public function getDate($key, \DateTime $default = null)
    {
        if (null === $value = $this->get($key)) {
            return $default;
        }

        if (false === $date = \DateTime::createFromFormat(\DATE_RFC2822, $value)) {
            throw new \RuntimeException(sprintf('The "%s" HTTP header is not parseable (%s).', $key, $value));
        }

        return $date;
    }

    /**
     * Adds a custom Cache-Control directive.
     *
     * @param string $key   The Cache-Control directive name
     * @param mixed  $value The Cache-Control directive value
     */
    public function addCacheControlDirective($key, $value = true)
    {
        $this->cacheControl[$key] = $value;

        $this->set('Cache-Control', $this->getCacheControlHeader());
    }

    /**
     * Returns true if the Cache-Control directive is defined.
     *
     * @param string $key The Cache-Control directive
     *
     * @return bool true if the directive exists, false otherwise
     */
    public function hasCacheControlDirective($key)
    {
        return \array_key_exists($key, $this->cacheControl);
    }

    /**
     * Returns a Cache-Control directive value by name.
     *
     * @param string $key The directive name
     *
     * @return mixed|null The directive value if defined, null otherwise
     */
    public function getCacheControlDirective($key)
    {
        return \array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
    }

    /**
     * Removes a Cache-Control directive.
     *
     * @param string $key The Cache-Control directive
     */
    public function removeCacheControlDirective($key)
    {
        unset($this->cacheControl[$key]);

        $this->set('Cache-Control', $this->getCacheControlHeader());
    }

    /**
     * Returns an iterator for headers.
     *
     * @return \ArrayIterator An \ArrayIterator instance
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->headers);
    }

    /**
     * Returns the number of headers.
     *
     * @return int The number of headers
     */
    public function count()
    {
        return \count($this->headers);
    }

    protected function getCacheControlHeader()
    {
王智 authored
305
        $parts = [];
王智 authored
306
        ksort($this->cacheControl);
王智 authored
307 308 309 310 311 312 313
        foreach ($this->cacheControl as $key => $value) {
            if (true === $value) {
                $parts[] = $key;
            } else {
                if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
                    $value = '"'.$value.'"';
                }
王智 authored
314
王智 authored
315 316 317 318 319
                $parts[] = "$key=$value";
            }
        }

        return implode(', ', $parts);
王智 authored
320 321 322 323 324 325 326 327 328 329 330
    }

    /**
     * Parses a Cache-Control HTTP header.
     *
     * @param string $header The value of the Cache-Control HTTP header
     *
     * @return array An array representing the attribute values
     */
    protected function parseCacheControl($header)
    {
王智 authored
331 332 333 334 335
        $cacheControl = [];
        preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, \PREG_SET_ORDER);
        foreach ($matches as $match) {
            $cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true);
        }
王智 authored
336
王智 authored
337
        return $cacheControl;
王智 authored
338 339
    }
}