annotate vendor/zendframework/zend-diactoros/src/AbstractSerializer.php @ 19:fa3358dc1485 tip

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