PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 27.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v27.5
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / vendor_prefixed / guzzlehttp / psr7 / src / MultipartStream.php

MultipartStream.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 27.5, at vendor_prefixed/guzzlehttp/psr7/src/MultipartStream.php

131 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare (strict_types=1);
4 namespace YoastSEO_Vendor\GuzzleHttp\Psr7;
5
6 use YoastSEO_Vendor\Psr\Http\Message\StreamInterface;
7 /**
8 * Stream that when read returns bytes for a streaming multipart or
9 * multipart/form-data stream.
10 */
11 final class MultipartStream implements \YoastSEO_Vendor\Psr\Http\Message\StreamInterface
12 {
13 use StreamDecoratorTrait;
14 /** @var string */
15 private $boundary;
16 /** @var StreamInterface */
17 private $stream;
18 /**
19 * @param array $elements Array of associative arrays, each containing a
20 * required "name" key mapping to the form field,
21 * name, a required "contents" key mapping to a
22 * StreamInterface/resource/string, an optional
23 * "headers" associative array of custom headers,
24 * and an optional "filename" key mapping to a
25 * string to send as the filename in the part.
26 * @param string $boundary You can optionally provide a specific boundary
27 *
28 * @throws \InvalidArgumentException
29 */
30 public function __construct(array $elements = [], ?string $boundary = null)
31 {
32 $this->boundary = $boundary ?: \bin2hex(\random_bytes(20));
33 $this->stream = $this->createStream($elements);
34 }
35 public function getBoundary() : string
36 {
37 return $this->boundary;
38 }
39 public function isWritable() : bool
40 {
41 return \false;
42 }
43 /**
44 * Get the headers needed before transferring the content of a POST file
45 *
46 * @param string[] $headers
47 */
48 private function getHeaders(array $headers) : string
49 {
50 $str = '';
51 foreach ($headers as $key => $value) {
52 $str .= "{$key}: {$value}\r\n";
53 }
54 return "--{$this->boundary}\r\n" . \trim($str) . "\r\n\r\n";
55 }
56 /**
57 * Create the aggregate stream that will be used to upload the POST data
58 */
59 protected function createStream(array $elements = []) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface
60 {
61 $stream = new \YoastSEO_Vendor\GuzzleHttp\Psr7\AppendStream();
62 foreach ($elements as $element) {
63 if (!\is_array($element)) {
64 throw new \UnexpectedValueException('An array is expected');
65 }
66 $this->addElement($stream, $element);
67 }
68 // Add the trailing boundary with CRLF
69 $stream->addStream(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor("--{$this->boundary}--\r\n"));
70 return $stream;
71 }
72 private function addElement(\YoastSEO_Vendor\GuzzleHttp\Psr7\AppendStream $stream, array $element) : void
73 {
74 foreach (['contents', 'name'] as $key) {
75 if (!\array_key_exists($key, $element)) {
76 throw new \InvalidArgumentException("A '{$key}' key is required");
77 }
78 }
79 $element['contents'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($element['contents']);
80 if (empty($element['filename'])) {
81 $uri = $element['contents']->getMetadata('uri');
82 if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') {
83 $element['filename'] = $uri;
84 }
85 }
86 [$body, $headers] = $this->createElement($element['name'], $element['contents'], $element['filename'] ?? null, $element['headers'] ?? []);
87 $stream->addStream(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($this->getHeaders($headers)));
88 $stream->addStream($body);
89 $stream->addStream(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor("\r\n"));
90 }
91 /**
92 * @param string[] $headers
93 *
94 * @return array{0: StreamInterface, 1: string[]}
95 */
96 private function createElement(string $name, \YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, ?string $filename, array $headers) : array
97 {
98 // Set a default content-disposition header if one was no provided
99 $disposition = self::getHeader($headers, 'content-disposition');
100 if (!$disposition) {
101 $headers['Content-Disposition'] = $filename === '0' || $filename ? \sprintf('form-data; name="%s"; filename="%s"', $name, \basename($filename)) : "form-data; name=\"{$name}\"";
102 }
103 // Set a default content-length header if one was no provided
104 $length = self::getHeader($headers, 'content-length');
105 if (!$length) {
106 if ($length = $stream->getSize()) {
107 $headers['Content-Length'] = (string) $length;
108 }
109 }
110 // Set a default Content-Type if one was not supplied
111 $type = self::getHeader($headers, 'content-type');
112 if (!$type && ($filename === '0' || $filename)) {
113 $headers['Content-Type'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\MimeType::fromFilename($filename) ?? 'application/octet-stream';
114 }
115 return [$stream, $headers];
116 }
117 /**
118 * @param string[] $headers
119 */
120 private static function getHeader(array $headers, string $key) : ?string
121 {
122 $lowercaseHeader = \strtolower($key);
123 foreach ($headers as $k => $v) {
124 if (\strtolower((string) $k) === $lowercaseHeader) {
125 return $v;
126 }
127 }
128 return null;
129 }
130 }
131