Completed
Push — master ( 508d23...6552fb )
by David
02:48
created

MaxHeaderValueLengthFormatter::splitTagsInHalves()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
/*
4
 * This file is part of the FOSHttpCache package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.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 FOS\HttpCache\TagHeaderFormatter;
13
14
use FOS\HttpCache\Exception\InvalidTagException;
15
16
/**
17
 * A header formatter that splits the value(s) from a given
18
 * other header formatter (e.g. the CommaSeparatedTagHeaderFormatter)
19
 * into multiple headers making sure none of the header values
20
 * exceeds the configured limit.
21
 *
22
 * @author Yanick Witschi <[email protected]>
23
 */
24
class MaxHeaderValueLengthFormatter implements TagHeaderFormatter
25
{
26
    /**
27
     * @var TagHeaderFormatter
28
     */
29
    private $inner;
30
31
    /**
32
     * @var int
33
     */
34
    private $maxHeaderValueLength;
35
36
    /**
37
     * The default value of the maximum header length is 4096 because most
38
     * servers limit header values to 4kb.
39
     * HTTP messages cannot carry characters outside the ISO-8859-1 standard so they all
40
     * use up just one byte.
41
     *
42
     * @param TagHeaderFormatter $inner
43
     * @param int                $maxHeaderValueLength
44
     */
45
    public function __construct(TagHeaderFormatter $inner, $maxHeaderValueLength = 4096)
46
    {
47
        $this->inner = $inner;
48
        $this->maxHeaderValueLength = $maxHeaderValueLength;
49
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54
    public function getTagsHeaderName()
55
    {
56
        $this->inner->getTagsHeaderName();
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function getTagsHeaderValue(array $tags)
63
    {
64
        $values = (array) $this->inner->getTagsHeaderValue($tags);
65
        $newValues = [[]];
66
67
        foreach ($values as $value) {
68
            if ($this->isHeaderTooLong($value)) {
69
                list($firstTags, $secondTags) = $this->splitTagsInHalves($tags);
70
71
                $newValues[] = (array) $this->getTagsHeaderValue($firstTags);
72
                $newValues[] = (array) $this->getTagsHeaderValue($secondTags);
73
            } else {
74
                $newValues[] = [$value];
75
            }
76
        }
77
78
        $newValues = array_merge(...$newValues);
79
80
        if (1 === count($newValues)) {
81
            return $newValues[0];
82
        }
83
84
        return $newValues;
85
    }
86
87
    /**
88
     * @param string $value
89
     *
90
     * @return bool
91
     */
92
    private function isHeaderTooLong($value)
93
    {
94
        return mb_strlen($value) > $this->maxHeaderValueLength;
95
    }
96
97
    /**
98
     * Split an array of tags in two more or less equal sized arrays.
99
     *
100
     * @param array $tags
101
     *
102
     * @return array
103
     *
104
     * @throws InvalidTagException
105
     */
106
    private function splitTagsInHalves(array $tags)
107
    {
108
        if (1 === count($tags)) {
109
            throw new InvalidTagException(sprintf(
110
                'You configured a maximum header length of %d but the tag "%s" is too long.',
111
                $this->maxHeaderValueLength,
112
                $tags[0]
113
            ));
114
        }
115
116
        $size = ceil(count($tags) / 2);
117
118
        return array_chunk($tags, $size);
119
    }
120
}
121