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
← All changes | includes/sdk/google/guzzlehttp/psr7/src/MultipartStream.php +55 -22 1.2.0 → 1.4.2 View file →
@@ -1,10 +1,10 @@
1 1 <?php
2 2
3 3 declare (strict_types=1);
4 -namespace Dudlewebs\WPMCS\GuzzleHttp\Psr7;
4 +namespace Dudlewebs\WPMCS\GCP\GuzzleHttp\Psr7;
5 5
6 -use Dudlewebs\WPMCS\Psr\Http\Message\StreamInterface;
6 +use Dudlewebs\WPMCS\GCP\Psr\Http\Message\StreamInterface;
7 7 /**
8 8 * Stream that when read returns bytes for a streaming multipart or
9 9 * multipart/form-data stream.
10 10 */
@@ -17,13 +17,20 @@
17 17 private $stream;
18 18 /**
19 19 * @param array $elements Array of associative arrays, each containing a
20 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.
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.
26 33 * @param string $boundary You can optionally provide a specific boundary
27 34 *
28 35 * @throws \InvalidArgumentException
29 36 */
@@ -28,16 +35,16 @@
28 35 * @throws \InvalidArgumentException
29 36 */
30 37 public function __construct(array $elements = [], ?string $boundary = null)
31 38 {
32 - $this->boundary = $boundary ?: bin2hex(random_bytes(20));
39 + $this->boundary = $boundary ?: \bin2hex(\random_bytes(20));
33 40 $this->stream = $this->createStream($elements);
34 41 }
35 - public function getBoundary(): string
42 + public function getBoundary() : string
36 43 {
37 44 return $this->boundary;
38 45 }
39 - public function isWritable(): bool
46 + public function isWritable() : bool
40 47 {
41 48 return \false;
42 49 }
43 50 /**
@@ -44,24 +51,24 @@
44 51 * Get the headers needed before transferring the content of a POST file
45 52 *
46 53 * @param string[] $headers
47 54 */
48 - private function getHeaders(array $headers): string
55 + private function getHeaders(array $headers) : string
49 56 {
50 57 $str = '';
51 58 foreach ($headers as $key => $value) {
52 59 $str .= "{$key}: {$value}\r\n";
53 60 }
54 - return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
61 + return "--{$this->boundary}\r\n" . \trim($str) . "\r\n\r\n";
55 62 }
56 63 /**
57 64 * Create the aggregate stream that will be used to upload the POST data
58 65 */
59 - protected function createStream(array $elements = []): StreamInterface
66 + protected function createStream(array $elements = []) : StreamInterface
60 67 {
61 68 $stream = new AppendStream();
62 69 foreach ($elements as $element) {
63 - if (!is_array($element)) {
70 + if (!\is_array($element)) {
64 71 throw new \UnexpectedValueException('An array is expected');
65 72 }
66 73 $this->addElement($stream, $element);
67 74 }
@@ -68,15 +75,25 @@
68 75 // Add the trailing boundary with CRLF
69 76 $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n"));
70 77 return $stream;
71 78 }
72 - private function addElement(AppendStream $stream, array $element): void
79 + private function addElement(AppendStream $stream, array $element) : void
73 80 {
74 81 foreach (['contents', 'name'] as $key) {
75 - if (!array_key_exists($key, $element)) {
82 + if (!\array_key_exists($key, $element)) {
76 83 throw new \InvalidArgumentException("A '{$key}' key is required");
77 84 }
78 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 + }
79 96 $element['contents'] = Utils::streamFor($element['contents']);
80 97 if (empty($element['filename'])) {
81 98 $uri = $element['contents']->getMetadata('uri');
82 99 if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') {
@@ -82,24 +99,40 @@
82 99 if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') {
83 100 $element['filename'] = $uri;
84 101 }
85 102 }
86 - [$body, $headers] = $this->createElement($element['name'], $element['contents'], $element['filename'] ?? null, $element['headers'] ?? []);
103 + [$body, $headers] = $this->createElement((string) $element['name'], $element['contents'], $element['filename'] ?? null, $element['headers'] ?? []);
87 104 $stream->addStream(Utils::streamFor($this->getHeaders($headers)));
88 105 $stream->addStream($body);
89 106 $stream->addStream(Utils::streamFor("\r\n"));
90 107 }
91 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 + /**
92 125 * @param string[] $headers
93 126 *
94 127 * @return array{0: StreamInterface, 1: string[]}
95 128 */
96 - private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array
129 + private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers) : array
97 130 {
98 131 // Set a default content-disposition header if one was no provided
99 132 $disposition = self::getHeader($headers, 'content-disposition');
100 133 if (!$disposition) {
101 - $headers['Content-Disposition'] = $filename === '0' || $filename ? sprintf('form-data; name="%s"; filename="%s"', $name, basename($filename)) : "form-data; name=\"{$name}\"";
134 + $headers['Content-Disposition'] = $filename === '0' || $filename ? \sprintf('form-data; name="%s"; filename="%s"', $name, \basename($filename)) : "form-data; name=\"{$name}\"";
102 135 }
103 136 // Set a default content-length header if one was no provided
104 137 $length = self::getHeader($headers, 'content-length');
105 138 if (!$length) {
@@ -116,13 +149,13 @@
116 149 }
117 150 /**
118 151 * @param string[] $headers
119 152 */
120 - private static function getHeader(array $headers, string $key): ?string
153 + private static function getHeader(array $headers, string $key) : ?string
121 154 {
122 - $lowercaseHeader = strtolower($key);
155 + $lowercaseHeader = \strtolower($key);
123 156 foreach ($headers as $k => $v) {
124 - if (strtolower((string) $k) === $lowercaseHeader) {
157 + if (\strtolower((string) $k) === $lowercaseHeader) {
125 158 return $v;
126 159 }
127 160 }
128 161 return null;