PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / trunk
Tutor LMS – eLearning and online course solution vtrunk
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / ecommerce / PaymentGateways / Paypal / vendor / guzzlehttp / psr7 / src / Message.php
tutor / ecommerce / PaymentGateways / Paypal / vendor / guzzlehttp / psr7 / src Last commit date
Exception 1 year ago AppendStream.php 2 months ago BufferStream.php 2 months ago CachingStream.php 2 months ago DroppingStream.php 2 months ago FnStream.php 2 months ago Header.php 2 months ago HttpFactory.php 1 year ago InflateStream.php 1 year ago LazyOpenStream.php 1 year ago LimitStream.php 2 months ago Message.php 2 months ago MessageTrait.php 2 months ago MimeType.php 2 months ago MultipartStream.php 2 months ago NoSeekStream.php 2 months ago PumpStream.php 2 months ago Query.php 1 year ago Request.php 2 months ago Response.php 2 months ago Rfc3986.php 2 months ago Rfc7230.php 2 months ago ServerRequest.php 2 months ago Stream.php 2 months ago StreamDecoratorTrait.php 2 months ago StreamWrapper.php 2 months ago UploadedFile.php 2 months ago Uri.php 2 months ago UriComparator.php 2 months ago UriNormalizer.php 2 months ago UriResolver.php 2 months ago Utils.php 2 months ago
Message.php
324 lines
1 <?php
2
3 declare(strict_types=1);
4
5 namespace GuzzleHttp\Psr7;
6
7 use Psr\Http\Message\MessageInterface;
8 use Psr\Http\Message\RequestInterface;
9 use Psr\Http\Message\ResponseInterface;
10
11 final class Message
12 {
13 /**
14 * Returns the string representation of an HTTP message.
15 *
16 * @param MessageInterface $message Message to convert to a string.
17 */
18 public static function toString(MessageInterface $message): string
19 {
20 if ($message instanceof RequestInterface) {
21 $msg = trim($message->getMethod().' '
22 .$message->getRequestTarget())
23 .' HTTP/'.$message->getProtocolVersion();
24 if (!$message->hasHeader('host')) {
25 $msg .= "\r\nHost: ".$message->getUri()->getHost();
26 }
27 } elseif ($message instanceof ResponseInterface) {
28 $msg = 'HTTP/'.$message->getProtocolVersion().' '
29 .$message->getStatusCode().' '
30 .$message->getReasonPhrase();
31 } else {
32 throw new \InvalidArgumentException('Unknown message type');
33 }
34
35 foreach ($message->getHeaders() as $name => $values) {
36 if (is_string($name) && strtolower($name) === 'set-cookie') {
37 foreach ($values as $value) {
38 $msg .= "\r\n{$name}: ".$value;
39 }
40 } else {
41 $msg .= "\r\n{$name}: ".implode(', ', $values);
42 }
43 }
44
45 return "{$msg}\r\n\r\n".$message->getBody();
46 }
47
48 /**
49 * Get a short summary of the message body.
50 *
51 * Will return `null` if the response is not printable.
52 *
53 * @param MessageInterface $message The message to get the body summary
54 * @param int $truncateAt The maximum allowed size of the summary
55 */
56 public static function bodySummary(MessageInterface $message, int $truncateAt = 120): ?string
57 {
58 $body = $message->getBody();
59
60 if (!$body->isSeekable() || !$body->isReadable()) {
61 return null;
62 }
63
64 $size = $body->getSize();
65
66 if ($size === 0) {
67 return null;
68 }
69
70 $body->rewind();
71 $summary = $body->read($truncateAt);
72
73 if ($size > $truncateAt) {
74 if (preg_match('//u', $summary) !== 1) {
75 $summary = self::trimTrailingIncompleteUtf8Character($summary, $body->read(3));
76 }
77
78 $summary .= ' (truncated...)';
79 }
80
81 $body->rewind();
82
83 // Matches any printable character, including unicode characters:
84 // letters, marks, numbers, punctuation, spacing, and separators.
85 if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary) !== 0) {
86 return null;
87 }
88
89 return $summary;
90 }
91
92 /**
93 * Trims a partial UTF-8 character from the end of a truncated string.
94 */
95 private static function trimTrailingIncompleteUtf8Character(string $summary, string $lookahead): string
96 {
97 $length = strlen($summary);
98
99 if ($length === 0) {
100 return $summary;
101 }
102
103 $start = $length - 1;
104
105 while ($start >= 0) {
106 $byte = ord($summary[$start]);
107
108 if ($byte < 0x80 || $byte > 0xBF) {
109 break;
110 }
111
112 --$start;
113 }
114
115 if ($start < 0) {
116 return $summary;
117 }
118
119 $lead = ord($summary[$start]);
120
121 if ($lead >= 0xC2 && $lead <= 0xDF) {
122 $expectedLength = 2;
123 } elseif ($lead >= 0xE0 && $lead <= 0xEF) {
124 $expectedLength = 3;
125 } elseif ($lead >= 0xF0 && $lead <= 0xF4) {
126 $expectedLength = 4;
127 } else {
128 return $summary;
129 }
130
131 $availableLength = $length - $start;
132
133 if ($availableLength >= $expectedLength) {
134 return $summary;
135 }
136
137 $sequence = substr($summary, $start).substr($lookahead, 0, $expectedLength - $availableLength);
138
139 if (strlen($sequence) !== $expectedLength || preg_match('//u', $sequence) !== 1) {
140 return $summary;
141 }
142
143 return substr($summary, 0, $start);
144 }
145
146 /**
147 * Attempts to rewind a message body and throws an exception on failure.
148 *
149 * The body of the message will only be rewound if a call to `tell()`
150 * returns a value other than `0`.
151 *
152 * @param MessageInterface $message Message to rewind
153 *
154 * @throws \RuntimeException
155 */
156 public static function rewindBody(MessageInterface $message): void
157 {
158 $body = $message->getBody();
159
160 if ($body->tell()) {
161 $body->rewind();
162 }
163 }
164
165 /**
166 * Parses an HTTP message into an associative array.
167 *
168 * The array contains the "start-line" key containing the start line of
169 * the message, "headers" key containing an associative array of header
170 * array values, and a "body" key containing the body of the message.
171 *
172 * @param string $message HTTP request or response to parse.
173 */
174 public static function parseMessage(string $message): array
175 {
176 if (!$message) {
177 throw new \InvalidArgumentException('Invalid message');
178 }
179
180 $message = ltrim($message, "\r\n");
181
182 $messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
183
184 if ($messageParts === false || count($messageParts) !== 2) {
185 throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
186 }
187
188 [$rawHeaders, $body] = $messageParts;
189 $rawHeaders .= "\r\n"; // Put back the delimiter we split previously
190 $headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
191
192 if ($headerParts === false || count($headerParts) !== 2) {
193 throw new \InvalidArgumentException('Invalid message: Missing status line');
194 }
195
196 [$startLine, $rawHeaders] = $headerParts;
197
198 if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
199 // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
200 $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
201 }
202
203 /** @var array[] $headerLines */
204 $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
205
206 // If these aren't the same, then one line didn't match and there's an invalid header.
207 if ($count !== substr_count($rawHeaders, "\n")) {
208 // Folding is deprecated, see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
209 if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
210 throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
211 }
212
213 throw new \InvalidArgumentException('Invalid header syntax');
214 }
215
216 $headers = [];
217
218 foreach ($headerLines as $headerLine) {
219 $headers[$headerLine[1]][] = $headerLine[2];
220 }
221
222 return [
223 'start-line' => $startLine,
224 'headers' => $headers,
225 'body' => $body,
226 ];
227 }
228
229 /**
230 * Constructs a URI for an HTTP request message.
231 *
232 * @param string $path Path from the start-line
233 * @param array $headers Array of headers (each value an array).
234 */
235 public static function parseRequestUri(string $path, array $headers): string
236 {
237 $host = self::getHostFromHeaders($headers);
238
239 // If no host is found, then a full URI cannot be constructed.
240 if ($host === null) {
241 return $path;
242 }
243
244 $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
245
246 return $scheme.'://'.$host.'/'.ltrim($path, '/');
247 }
248
249 /**
250 * @param array $headers Array of headers (each value an array).
251 */
252 private static function getHostFromHeaders(array $headers): ?string
253 {
254 $hostKey = array_filter(array_keys($headers), function ($k) {
255 // Numeric array keys are converted to int by PHP.
256 $k = (string) $k;
257
258 return strtolower($k) === 'host';
259 });
260
261 if (!$hostKey) {
262 return null;
263 }
264
265 $host = $headers[reset($hostKey)][0];
266 if (!is_string($host) || Rfc7230::parseHostHeader($host) === null) {
267 throw new \InvalidArgumentException('Invalid request string');
268 }
269
270 return $host;
271 }
272
273 /**
274 * Parses a request message string into a request object.
275 *
276 * @param string $message Request message string.
277 */
278 public static function parseRequest(string $message): RequestInterface
279 {
280 $data = self::parseMessage($message);
281 $matches = [];
282 if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
283 throw new \InvalidArgumentException('Invalid request string');
284 }
285 $parts = explode(' ', $data['start-line'], 3);
286 $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
287
288 $request = new Request(
289 $parts[0],
290 $matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1],
291 $data['headers'],
292 $data['body'],
293 $version
294 );
295
296 return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
297 }
298
299 /**
300 * Parses a response message string into a response object.
301 *
302 * @param string $message Response message string.
303 */
304 public static function parseResponse(string $message): ResponseInterface
305 {
306 $data = self::parseMessage($message);
307 // According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
308 // the space between status-code and reason-phrase is required. But
309 // browsers accept responses without space and reason as well.
310 if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
311 throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']);
312 }
313 $parts = explode(' ', $data['start-line'], 3);
314
315 return new Response(
316 (int) $parts[1],
317 $data['headers'],
318 $data['body'],
319 explode('/', $parts[0])[1],
320 $parts[2] ?? null
321 );
322 }
323 }
324