cd07fa6df91c0c1316f4e01e81c49778e77805f6
[yaffs-website] / vendor / zendframework / zend-diactoros / src / AbstractSerializer.php
1 <?php
2 /**
3  * Zend Framework (http://framework.zend.com/)
4  *
5  * @see       http://github.com/zendframework/zend-diactoros for the canonical source repository
6  * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
7  * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
8  */
9
10 namespace Zend\Diactoros;
11
12 use Psr\Http\Message\StreamInterface;
13 use UnexpectedValueException;
14
15 use function array_pop;
16 use function implode;
17 use function ltrim;
18 use function preg_match;
19 use function sprintf;
20 use function str_replace;
21 use function ucwords;
22
23 /**
24  * Provides base functionality for request and response de/serialization
25  * strategies, including functionality for retrieving a line at a time from
26  * the message, splitting headers from the body, and serializing headers.
27  */
28 abstract class AbstractSerializer
29 {
30     const CR  = "\r";
31     const EOL = "\r\n";
32     const LF  = "\n";
33
34     /**
35      * Retrieve a single line from the stream.
36      *
37      * Retrieves a line from the stream; a line is defined as a sequence of
38      * characters ending in a CRLF sequence.
39      *
40      * @param StreamInterface $stream
41      * @return string
42      * @throws UnexpectedValueException if the sequence contains a CR or LF in
43      *     isolation, or ends in a CR.
44      */
45     protected static function getLine(StreamInterface $stream)
46     {
47         $line    = '';
48         $crFound = false;
49         while (! $stream->eof()) {
50             $char = $stream->read(1);
51
52             if ($crFound && $char === self::LF) {
53                 $crFound = false;
54                 break;
55             }
56
57             // CR NOT followed by LF
58             if ($crFound && $char !== self::LF) {
59                 throw new UnexpectedValueException('Unexpected carriage return detected');
60             }
61
62             // LF in isolation
63             if (! $crFound && $char === self::LF) {
64                 throw new UnexpectedValueException('Unexpected line feed detected');
65             }
66
67             // CR found; do not append
68             if ($char === self::CR) {
69                 $crFound = true;
70                 continue;
71             }
72
73             // Any other character: append
74             $line .= $char;
75         }
76
77         // CR found at end of stream
78         if ($crFound) {
79             throw new UnexpectedValueException("Unexpected end of headers");
80         }
81
82         return $line;
83     }
84
85     /**
86      * Split the stream into headers and body content.
87      *
88      * Returns an array containing two elements
89      *
90      * - The first is an array of headers
91      * - The second is a StreamInterface containing the body content
92      *
93      * @param StreamInterface $stream
94      * @return array
95      * @throws UnexpectedValueException For invalid headers.
96      */
97     protected static function splitStream(StreamInterface $stream)
98     {
99         $headers       = [];
100         $currentHeader = false;
101
102         while ($line = self::getLine($stream)) {
103             if (preg_match(';^(?P<name>[!#$%&\'*+.^_`\|~0-9a-zA-Z-]+):(?P<value>.*)$;', $line, $matches)) {
104                 $currentHeader = $matches['name'];
105                 if (! isset($headers[$currentHeader])) {
106                     $headers[$currentHeader] = [];
107                 }
108                 $headers[$currentHeader][] = ltrim($matches['value']);
109                 continue;
110             }
111
112             if (! $currentHeader) {
113                 throw new UnexpectedValueException('Invalid header detected');
114             }
115
116             if (! preg_match('#^[ \t]#', $line)) {
117                 throw new UnexpectedValueException('Invalid header continuation');
118             }
119
120             // Append continuation to last header value found
121             $value = array_pop($headers[$currentHeader]);
122             $headers[$currentHeader][] = $value . ltrim($line);
123         }
124
125         // use RelativeStream to avoid copying initial stream into memory
126         return [$headers, new RelativeStream($stream, $stream->tell())];
127     }
128
129     /**
130      * Serialize headers to string values.
131      *
132      * @param array $headers
133      * @return string
134      */
135     protected static function serializeHeaders(array $headers)
136     {
137         $lines = [];
138         foreach ($headers as $header => $values) {
139             $normalized = self::filterHeader($header);
140             foreach ($values as $value) {
141                 $lines[] = sprintf('%s: %s', $normalized, $value);
142             }
143         }
144
145         return implode("\r\n", $lines);
146     }
147
148     /**
149      * Filter a header name to wordcase
150      *
151      * @param string $header
152      * @return string
153      */
154     protected static function filterHeader($header)
155     {
156         $filtered = str_replace('-', ' ', $header);
157         $filtered = ucwords($filtered);
158         return str_replace(' ', '-', $filtered);
159     }
160 }