PluginProbe
Media Cloud Sync / 1.4.2
Media Cloud Sync v1.4.2
1.4.2 1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 All 36 releases
media-cloud-sync / includes / sdk / google / guzzlehttp / psr7 / src / MultipartStream.php

MultipartStream.php in Media Cloud Sync 1.4.2, at includes/sdk/google/guzzlehttp/psr7/src/MultipartStream.php

164 lines 6.7 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 Dudlewebs\WPMCS\GCP\GuzzleHttp\Psr7;
5
6 use Dudlewebs\WPMCS\GCP\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 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 any
22 * value accepted by Utils::streamFor() (scalar,
23 * null, resource, StreamInterface, Iterator, or
24 * callable), or an array for nested expansion.
25 * Optional keys include "headers" (associative
26 * array of custom headers) and "filename" (string
27 * to send as the filename in the part).
28 * When "contents" is an array, it is recursively
29 * expanded into multiple fields using bracket notation
30 * (e.g., name[0][key]). Empty arrays produce no fields.
31 * The "filename" and "headers" options cannot be used
32 * with array contents.
33 * @param string $boundary You can optionally provide a specific boundary
34 *
35 * @throws \InvalidArgumentException
36 */
37 public function __construct(array $elements = [], ?string $boundary = null)
38 {
39 $this->boundary = $boundary ?: \bin2hex(\random_bytes(20));
40 $this->stream = $this->createStream($elements);
41 }
42 public function getBoundary() : string
43 {
44 return $this->boundary;
45 }
46 public function isWritable() : bool
47 {
48 return \false;
49 }
50 /**
51 * Get the headers needed before transferring the content of a POST file
52 *
53 * @param string[] $headers
54 */
55 private function getHeaders(array $headers) : string
56 {
57 $str = '';
58 foreach ($headers as $key => $value) {
59 $str .= "{$key}: {$value}\r\n";
60 }
61 return "--{$this->boundary}\r\n" . \trim($str) . "\r\n\r\n";
62 }
63 /**
64 * Create the aggregate stream that will be used to upload the POST data
65 */
66 protected function createStream(array $elements = []) : StreamInterface
67 {
68 $stream = new AppendStream();
69 foreach ($elements as $element) {
70 if (!\is_array($element)) {
71 throw new \UnexpectedValueException('An array is expected');
72 }
73 $this->addElement($stream, $element);
74 }
75 // Add the trailing boundary with CRLF
76 $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n"));
77 return $stream;
78 }
79 private function addElement(AppendStream $stream, array $element) : void
80 {
81 foreach (['contents', 'name'] as $key) {
82 if (!\array_key_exists($key, $element)) {
83 throw new \InvalidArgumentException("A '{$key}' key is required");
84 }
85 }
86 if (!\is_string($element['name']) && !\is_int($element['name'])) {
87 throw new \InvalidArgumentException("The 'name' key must be a string or integer");
88 }
89 if (\is_array($element['contents'])) {
90 if (\array_key_exists('filename', $element) || \array_key_exists('headers', $element)) {
91 throw new \InvalidArgumentException("The 'filename' and 'headers' options cannot be used when 'contents' is an array");
92 }
93 $this->addNestedElements($stream, $element['contents'], (string) $element['name']);
94 return;
95 }
96 $element['contents'] = Utils::streamFor($element['contents']);
97 if (empty($element['filename'])) {
98 $uri = $element['contents']->getMetadata('uri');
99 if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') {
100 $element['filename'] = $uri;
101 }
102 }
103 [$body, $headers] = $this->createElement((string) $element['name'], $element['contents'], $element['filename'] ?? null, $element['headers'] ?? []);
104 $stream->addStream(Utils::streamFor($this->getHeaders($headers)));
105 $stream->addStream($body);
106 $stream->addStream(Utils::streamFor("\r\n"));
107 }
108 /**
109 * Recursively expand array contents into multiple form fields.
110 *
111 * @param array<array-key, mixed> $contents
112 */
113 private function addNestedElements(AppendStream $stream, array $contents, string $root) : void
114 {
115 foreach ($contents as $key => $value) {
116 $fieldName = $root === '' ? \sprintf('[%s]', (string) $key) : \sprintf('%s[%s]', $root, (string) $key);
117 if (\is_array($value)) {
118 $this->addNestedElements($stream, $value, $fieldName);
119 } else {
120 $this->addElement($stream, ['name' => $fieldName, 'contents' => $value]);
121 }
122 }
123 }
124 /**
125 * @param string[] $headers
126 *
127 * @return array{0: StreamInterface, 1: string[]}
128 */
129 private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers) : array
130 {
131 // Set a default content-disposition header if one was no provided
132 $disposition = self::getHeader($headers, 'content-disposition');
133 if (!$disposition) {
134 $headers['Content-Disposition'] = $filename === '0' || $filename ? \sprintf('form-data; name="%s"; filename="%s"', $name, \basename($filename)) : "form-data; name=\"{$name}\"";
135 }
136 // Set a default content-length header if one was no provided
137 $length = self::getHeader($headers, 'content-length');
138 if (!$length) {
139 if ($length = $stream->getSize()) {
140 $headers['Content-Length'] = (string) $length;
141 }
142 }
143 // Set a default Content-Type if one was not supplied
144 $type = self::getHeader($headers, 'content-type');
145 if (!$type && ($filename === '0' || $filename)) {
146 $headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream';
147 }
148 return [$stream, $headers];
149 }
150 /**
151 * @param string[] $headers
152 */
153 private static function getHeader(array $headers, string $key) : ?string
154 {
155 $lowercaseHeader = \strtolower($key);
156 foreach ($headers as $k => $v) {
157 if (\strtolower((string) $k) === $lowercaseHeader) {
158 return $v;
159 }
160 }
161 return null;
162 }
163 }
164