├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json └── src ├── ApacheMimetypeHelper.php ├── CustomMimetypeHelper.php ├── MimetypeHelper.php └── MultipartStreamBuilder.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 1.4.2 - 2024-09-04 4 | 5 | - Fix phpdoc syntax. 6 | 7 | ## 1.4.1 - 2024-09-04 8 | 9 | - Fix phpdoc for addResource options to make the options not required. 10 | 11 | ## 1.4.0 - 2024-09-01 12 | 13 | - No longer automatically add a `Content-Length` header for each part in MultipartStreamBuilder class to comply with RFC 7578 section 4.8. 14 | 15 | ## 1.3.1 - 2024-06-10 16 | 17 | - Added missing mimetype for `.webp` images. 18 | 19 | ## 1.3.0 - 2023-04-28 20 | 21 | - Removed unnecessary dependency on deprecated `php-http/message-factory` 22 | - Allow `psr/http-message` 2.* 23 | - Also skip setting filename if URI starts with `data://` 24 | 25 | ## 1.2.0 - 2021-05-21 26 | 27 | - Refactored MultipartStreamBuilder to clean up and allow injecting data without a filename 28 | - Dynamically use memory or temp file to buffer the stream content. 29 | 30 | ## 1.1.2 - 2020-07-13 31 | 32 | - Support PHP 8.0 33 | 34 | ## 1.1.1 - 2020-07-04 35 | 36 | - Fixed mistake in PHPDoc type. 37 | 38 | ## 1.1.0 - 2019-08-22 39 | 40 | - Added support for PSR-17 factories. 41 | - Dropped support for PHP < 7.1 42 | 43 | ## 1.0.0 - 2017-05-21 44 | 45 | No changes from 0.2.0. 46 | 47 | ## 0.2.0 - 2017-02-20 48 | 49 | You may do a BC update to version 0.2.0 if you are sure that you are not adding 50 | multiple resources with the same name to the Builder. 51 | 52 | ### Fixed 53 | 54 | - Make sure one can add resources with same name without overwrite. 55 | 56 | ## 0.1.6 - 2017-02-16 57 | 58 | ### Fixed 59 | 60 | - Performance improvements by avoid using `uniqid()`. 61 | 62 | ## 0.1.5 - 2017-02-14 63 | 64 | ### Fixed 65 | 66 | - Support for non-readable streams. This fix was needed because flaws in Guzzle, Zend and Slims implementations of PSR-7. 67 | 68 | ## 0.1.4 - 2016-12-31 69 | 70 | ### Added 71 | 72 | - Added support for resetting the builder 73 | 74 | ## 0.1.3 - 2016-12-22 75 | 76 | ### Added 77 | 78 | - Added `CustomMimetypeHelper` to allow you to configure custom mimetypes. 79 | 80 | ### Changed 81 | 82 | - Using regular expression instead of `basename($filename)` because basename is depending on locale. 83 | 84 | ## 0.1.2 - 2016-08-31 85 | 86 | ### Added 87 | 88 | - Support for Outlook msg files. 89 | 90 | ## 0.1.1 - 2016-08-10 91 | 92 | ### Added 93 | 94 | - Support for Apple passbook. 95 | 96 | ## 0.1.0 - 2016-07-19 97 | 98 | ### Added 99 | 100 | - Initial release 101 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 PHP HTTP Team 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSR-7 Multipart Stream Builder 2 | 3 | [![Latest Version](https://img.shields.io/github/release/php-http/multipart-stream-builder.svg?style=flat-square)](https://github.com/php-http/multipart-stream-builder/releases) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) 5 | [![Build Status](https://github.com/php-http/multipart-stream-builder/actions/workflows/tests.yml/badge.svg)](https://github.com/php-http/multipart-stream-builder/actions/workflows/tests.yml) 6 | [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/multipart-stream-builder.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/multipart-stream-builder) 7 | [![Quality Score](https://img.shields.io/scrutinizer/g/php-http/multipart-stream-builder.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/multipart-stream-builder) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/php-http/multipart-stream-builder.svg?style=flat-square)](https://packagist.org/packages/php-http/multipart-stream-builder) 9 | 10 | **A builder for Multipart PSR-7 Streams. The builder create streams independenly form any PSR-7 implementation.** 11 | 12 | 13 | ## Install 14 | 15 | Via Composer 16 | 17 | ``` bash 18 | $ composer require php-http/multipart-stream-builder 19 | ``` 20 | 21 | ## Documentation 22 | 23 | Please see the [official documentation](http://php-http.readthedocs.org/en/latest/components/multipart-stream-builder.html). 24 | 25 | 26 | ## Contributing 27 | 28 | Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details. 29 | 30 | 31 | ## Security 32 | 33 | If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org). 34 | 35 | 36 | ## License 37 | 38 | The MIT License (MIT). Please see [License File](LICENSE) for more information. 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-http/multipart-stream-builder", 3 | "description": "A builder class that help you create a multipart stream", 4 | "license": "MIT", 5 | "keywords": ["http", "factory", "message", "stream", "multipart stream"], 6 | "homepage": "http://php-http.org", 7 | "authors": [ 8 | { 9 | "name": "Tobias Nyholm", 10 | "email": "tobias.nyholm@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": "^7.1 || ^8.0", 15 | "php-http/discovery": "^1.15", 16 | "psr/http-factory-implementation": "^1.0" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3", 20 | "php-http/message": "^1.5", 21 | "php-http/message-factory": "^1.0.2", 22 | "nyholm/psr7": "^1.0" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Http\\Message\\MultipartStream\\": "src/" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "tests\\Http\\Message\\MultipartStream\\": "tests/" 32 | } 33 | }, 34 | "scripts": { 35 | "test": "vendor/bin/phpunit", 36 | "test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml" 37 | }, 38 | "config": { 39 | "allow-plugins": { 40 | "php-http/discovery": false 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ApacheMimetypeHelper.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class ApacheMimetypeHelper implements MimetypeHelper 13 | { 14 | /** 15 | * {@inheritdoc} 16 | * 17 | * @see http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types 18 | */ 19 | public function getMimetypeFromFilename($filename) 20 | { 21 | return $this->getMimetypeFromExtension(pathinfo($filename, PATHINFO_EXTENSION)); 22 | } 23 | 24 | /** 25 | * {@inheritdoc} 26 | * 27 | * @see http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types 28 | */ 29 | public function getMimetypeFromExtension($extension) 30 | { 31 | static $mimetypes = [ 32 | '7z' => 'application/x-7z-compressed', 33 | 'aac' => 'audio/x-aac', 34 | 'ai' => 'application/postscript', 35 | 'aif' => 'audio/x-aiff', 36 | 'asc' => 'text/plain', 37 | 'asf' => 'video/x-ms-asf', 38 | 'atom' => 'application/atom+xml', 39 | 'avi' => 'video/x-msvideo', 40 | 'bmp' => 'image/bmp', 41 | 'bz2' => 'application/x-bzip2', 42 | 'cer' => 'application/pkix-cert', 43 | 'crl' => 'application/pkix-crl', 44 | 'crt' => 'application/x-x509-ca-cert', 45 | 'css' => 'text/css', 46 | 'csv' => 'text/csv', 47 | 'cu' => 'application/cu-seeme', 48 | 'deb' => 'application/x-debian-package', 49 | 'doc' => 'application/msword', 50 | 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 51 | 'dvi' => 'application/x-dvi', 52 | 'eot' => 'application/vnd.ms-fontobject', 53 | 'eps' => 'application/postscript', 54 | 'epub' => 'application/epub+zip', 55 | 'etx' => 'text/x-setext', 56 | 'flac' => 'audio/flac', 57 | 'flv' => 'video/x-flv', 58 | 'gif' => 'image/gif', 59 | 'gz' => 'application/gzip', 60 | 'htm' => 'text/html', 61 | 'html' => 'text/html', 62 | 'ico' => 'image/x-icon', 63 | 'ics' => 'text/calendar', 64 | 'ini' => 'text/plain', 65 | 'iso' => 'application/x-iso9660-image', 66 | 'jar' => 'application/java-archive', 67 | 'jpe' => 'image/jpeg', 68 | 'jpeg' => 'image/jpeg', 69 | 'jpg' => 'image/jpeg', 70 | 'js' => 'text/javascript', 71 | 'json' => 'application/json', 72 | 'latex' => 'application/x-latex', 73 | 'log' => 'text/plain', 74 | 'm4a' => 'audio/mp4', 75 | 'm4v' => 'video/mp4', 76 | 'mid' => 'audio/midi', 77 | 'midi' => 'audio/midi', 78 | 'mov' => 'video/quicktime', 79 | 'mp3' => 'audio/mpeg', 80 | 'mp4' => 'video/mp4', 81 | 'mp4a' => 'audio/mp4', 82 | 'mp4v' => 'video/mp4', 83 | 'mpe' => 'video/mpeg', 84 | 'mpeg' => 'video/mpeg', 85 | 'mpg' => 'video/mpeg', 86 | 'mpg4' => 'video/mp4', 87 | 'oga' => 'audio/ogg', 88 | 'ogg' => 'audio/ogg', 89 | 'ogv' => 'video/ogg', 90 | 'ogx' => 'application/ogg', 91 | 'pbm' => 'image/x-portable-bitmap', 92 | 'pdf' => 'application/pdf', 93 | 'pgm' => 'image/x-portable-graymap', 94 | 'png' => 'image/png', 95 | 'pnm' => 'image/x-portable-anymap', 96 | 'ppm' => 'image/x-portable-pixmap', 97 | 'ppt' => 'application/vnd.ms-powerpoint', 98 | 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 99 | 'ps' => 'application/postscript', 100 | 'qt' => 'video/quicktime', 101 | 'rar' => 'application/x-rar-compressed', 102 | 'ras' => 'image/x-cmu-raster', 103 | 'rss' => 'application/rss+xml', 104 | 'rtf' => 'application/rtf', 105 | 'sgm' => 'text/sgml', 106 | 'sgml' => 'text/sgml', 107 | 'svg' => 'image/svg+xml', 108 | 'swf' => 'application/x-shockwave-flash', 109 | 'tar' => 'application/x-tar', 110 | 'tif' => 'image/tiff', 111 | 'tiff' => 'image/tiff', 112 | 'torrent' => 'application/x-bittorrent', 113 | 'ttf' => 'application/x-font-ttf', 114 | 'txt' => 'text/plain', 115 | 'wav' => 'audio/x-wav', 116 | 'webp' => 'image/webp', 117 | 'webm' => 'video/webm', 118 | 'wma' => 'audio/x-ms-wma', 119 | 'wmv' => 'video/x-ms-wmv', 120 | 'woff' => 'application/x-font-woff', 121 | 'wsdl' => 'application/wsdl+xml', 122 | 'xbm' => 'image/x-xbitmap', 123 | 'xls' => 'application/vnd.ms-excel', 124 | 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 125 | 'xml' => 'application/xml', 126 | 'xpm' => 'image/x-xpixmap', 127 | 'xwd' => 'image/x-xwindowdump', 128 | 'yaml' => 'text/yaml', 129 | 'yml' => 'text/yaml', 130 | 'zip' => 'application/zip', 131 | 132 | // Non-Apache standard 133 | 'pkpass' => 'application/vnd.apple.pkpass', 134 | 'msg' => 'application/vnd.ms-outlook', 135 | ]; 136 | 137 | $extension = strtolower($extension); 138 | 139 | return isset($mimetypes[$extension]) 140 | ? $mimetypes[$extension] 141 | : null; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/CustomMimetypeHelper.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class CustomMimetypeHelper extends ApacheMimetypeHelper 11 | { 12 | /** 13 | * @var array 14 | */ 15 | private $mimetypes = []; 16 | 17 | /** 18 | * @param array $mimetypes should be of type extension => mimetype 19 | */ 20 | public function __construct(array $mimetypes = []) 21 | { 22 | $this->mimetypes = $mimetypes; 23 | } 24 | 25 | /** 26 | * @param string $extension 27 | * @param string $mimetype 28 | * 29 | * @return $this 30 | */ 31 | public function addMimetype($extension, $mimetype) 32 | { 33 | $this->mimetypes[$extension] = $mimetype; 34 | 35 | return $this; 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | * 41 | * Check if we have any defined mimetypes and of not fallback to ApacheMimetypeHelper 42 | */ 43 | public function getMimetypeFromExtension($extension) 44 | { 45 | $extension = strtolower($extension); 46 | 47 | return isset($this->mimetypes[$extension]) 48 | ? $this->mimetypes[$extension] 49 | : parent::getMimetypeFromExtension($extension); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/MimetypeHelper.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | interface MimetypeHelper 9 | { 10 | /** 11 | * Determines the mimetype of a file by looking at its extension. 12 | * 13 | * @param string $filename 14 | * 15 | * @return string|null 16 | */ 17 | public function getMimetypeFromFilename($filename); 18 | 19 | /** 20 | * Maps a file extensions to a mimetype. 21 | * 22 | * @param string $extension The file extension 23 | * 24 | * @return string|null 25 | */ 26 | public function getMimetypeFromExtension($extension); 27 | } 28 | -------------------------------------------------------------------------------- /src/MultipartStreamBuilder.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class MultipartStreamBuilder 20 | { 21 | /** 22 | * @var HttplugStreamFactory|StreamFactoryInterface 23 | */ 24 | private $streamFactory; 25 | 26 | /** 27 | * @var MimetypeHelper 28 | */ 29 | private $mimetypeHelper; 30 | 31 | /** 32 | * @var string 33 | */ 34 | private $boundary; 35 | 36 | /** 37 | * @var array Element where each Element is an array with keys ['contents', 'headers'] 38 | */ 39 | private $data = []; 40 | 41 | /** 42 | * @param HttplugStreamFactory|StreamFactoryInterface|null $streamFactory 43 | */ 44 | public function __construct($streamFactory = null) 45 | { 46 | if ($streamFactory instanceof StreamFactoryInterface || $streamFactory instanceof HttplugStreamFactory) { 47 | $this->streamFactory = $streamFactory; 48 | 49 | return; 50 | } 51 | 52 | if (null !== $streamFactory) { 53 | throw new \LogicException(sprintf( 54 | 'First arguemnt to the constructor of "%s" must be of type "%s", "%s" or null. Got %s', 55 | __CLASS__, 56 | StreamFactoryInterface::class, 57 | HttplugStreamFactory::class, 58 | \is_object($streamFactory) ? \get_class($streamFactory) : \gettype($streamFactory) 59 | )); 60 | } 61 | 62 | // Try to find a stream factory. 63 | try { 64 | $this->streamFactory = Psr17FactoryDiscovery::findStreamFactory(); 65 | } catch (NotFoundException $psr17Exception) { 66 | try { 67 | $this->streamFactory = StreamFactoryDiscovery::find(); 68 | } catch (NotFoundException $httplugException) { 69 | // we could not find any factory. 70 | throw $psr17Exception; 71 | } 72 | } 73 | } 74 | 75 | /** 76 | * Add a resource to the Multipart Stream. 77 | * 78 | * @param string|resource|\Psr\Http\Message\StreamInterface $resource the filepath, resource or StreamInterface of the data 79 | * @param array $headers additional headers array: ['header-name' => 'header-value'] 80 | * 81 | * @return MultipartStreamBuilder 82 | */ 83 | public function addData($resource, array $headers = []) 84 | { 85 | $stream = $this->createStream($resource); 86 | $this->data[] = ['contents' => $stream, 'headers' => $headers]; 87 | 88 | return $this; 89 | } 90 | 91 | /** 92 | * Add a resource to the Multipart Stream. 93 | * 94 | * @param string $name the formpost name 95 | * @param string|resource|StreamInterface $resource 96 | * @param array{headers?: array, filename?: string} $options 97 | * 98 | * Options: 99 | * - headers: additional headers as hashmap ['header-name' => 'header-value'] 100 | * - filename: used to determine the mime type 101 | * 102 | * @return MultipartStreamBuilder 103 | */ 104 | public function addResource($name, $resource, array $options = []) 105 | { 106 | $stream = $this->createStream($resource); 107 | 108 | // validate options['headers'] exists 109 | if (!isset($options['headers'])) { 110 | $options['headers'] = []; 111 | } 112 | 113 | // Try to add filename if it is missing 114 | if (empty($options['filename'])) { 115 | $options['filename'] = null; 116 | $uri = $stream->getMetadata('uri'); 117 | if ('php://' !== substr($uri, 0, 6) && 'data://' !== substr($uri, 0, 7)) { 118 | $options['filename'] = $uri; 119 | } 120 | } 121 | 122 | $this->prepareHeaders($name, $stream, $options['filename'], $options['headers']); 123 | 124 | return $this->addData($stream, $options['headers']); 125 | } 126 | 127 | /** 128 | * Build the stream. 129 | * 130 | * @return StreamInterface 131 | */ 132 | public function build() 133 | { 134 | // Open a temporary read-write stream as buffer. 135 | // If the size is less than predefined limit, things will stay in memory. 136 | // If the size is more than that, things will be stored in temp file. 137 | $buffer = fopen('php://temp', 'r+'); 138 | foreach ($this->data as $data) { 139 | // Add start and headers 140 | fwrite($buffer, "--{$this->getBoundary()}\r\n". 141 | $this->getHeaders($data['headers'])."\r\n"); 142 | 143 | /** @var $contentStream StreamInterface */ 144 | $contentStream = $data['contents']; 145 | 146 | // Read stream into buffer 147 | if ($contentStream->isSeekable()) { 148 | $contentStream->rewind(); // rewind to beginning. 149 | } 150 | if ($contentStream->isReadable()) { 151 | while (!$contentStream->eof()) { 152 | // Read 1MB chunk into buffer until reached EOF. 153 | fwrite($buffer, $contentStream->read(1048576)); 154 | } 155 | } else { 156 | fwrite($buffer, $contentStream->__toString()); 157 | } 158 | fwrite($buffer, "\r\n"); 159 | } 160 | 161 | // Append end 162 | fwrite($buffer, "--{$this->getBoundary()}--\r\n"); 163 | 164 | // Rewind to starting position for reading. 165 | fseek($buffer, 0); 166 | 167 | return $this->createStream($buffer); 168 | } 169 | 170 | /** 171 | * Add extra headers if they are missing. 172 | * 173 | * @param string $name 174 | * @param string $filename 175 | */ 176 | private function prepareHeaders($name, StreamInterface $stream, $filename, array &$headers) 177 | { 178 | $hasFilename = '0' === $filename || $filename; 179 | 180 | // Set a default content-disposition header if one was not provided 181 | if (!$this->hasHeader($headers, 'content-disposition')) { 182 | $headers['Content-Disposition'] = sprintf('form-data; name="%s"', $name); 183 | if ($hasFilename) { 184 | $headers['Content-Disposition'] .= sprintf('; filename="%s"', $this->basename($filename)); 185 | } 186 | } 187 | 188 | // Set a default Content-Type if one was not provided 189 | if (!$this->hasHeader($headers, 'content-type') && $hasFilename) { 190 | if ($type = $this->getMimetypeHelper()->getMimetypeFromFilename($filename)) { 191 | $headers['Content-Type'] = $type; 192 | } 193 | } 194 | } 195 | 196 | /** 197 | * Get the headers formatted for the HTTP message. 198 | * 199 | * @return string 200 | */ 201 | private function getHeaders(array $headers) 202 | { 203 | $str = ''; 204 | foreach ($headers as $key => $value) { 205 | $str .= sprintf("%s: %s\r\n", $key, $value); 206 | } 207 | 208 | return $str; 209 | } 210 | 211 | /** 212 | * Check if header exist. 213 | * 214 | * @param string $key case insensitive 215 | * 216 | * @return bool 217 | */ 218 | private function hasHeader(array $headers, $key) 219 | { 220 | $lowercaseHeader = strtolower($key); 221 | foreach ($headers as $k => $v) { 222 | if (strtolower($k) === $lowercaseHeader) { 223 | return true; 224 | } 225 | } 226 | 227 | return false; 228 | } 229 | 230 | /** 231 | * Get the boundary that separates the streams. 232 | * 233 | * @return string 234 | */ 235 | public function getBoundary() 236 | { 237 | if (null === $this->boundary) { 238 | $this->boundary = uniqid('', true); 239 | } 240 | 241 | return $this->boundary; 242 | } 243 | 244 | /** 245 | * @param string $boundary 246 | * 247 | * @return MultipartStreamBuilder 248 | */ 249 | public function setBoundary($boundary) 250 | { 251 | $this->boundary = $boundary; 252 | 253 | return $this; 254 | } 255 | 256 | /** 257 | * @return MimetypeHelper 258 | */ 259 | private function getMimetypeHelper() 260 | { 261 | if (null === $this->mimetypeHelper) { 262 | $this->mimetypeHelper = new ApacheMimetypeHelper(); 263 | } 264 | 265 | return $this->mimetypeHelper; 266 | } 267 | 268 | /** 269 | * If you have custom file extension you may overwrite the default MimetypeHelper with your own. 270 | * 271 | * @return MultipartStreamBuilder 272 | */ 273 | public function setMimetypeHelper(MimetypeHelper $mimetypeHelper) 274 | { 275 | $this->mimetypeHelper = $mimetypeHelper; 276 | 277 | return $this; 278 | } 279 | 280 | /** 281 | * Reset and clear all stored data. This allows you to use builder for a subsequent request. 282 | * 283 | * @return MultipartStreamBuilder 284 | */ 285 | public function reset() 286 | { 287 | $this->data = []; 288 | $this->boundary = null; 289 | 290 | return $this; 291 | } 292 | 293 | /** 294 | * Gets the filename from a given path. 295 | * 296 | * PHP's basename() does not properly support streams or filenames beginning with a non-US-ASCII character. 297 | * 298 | * @author Drupal 8.2 299 | * 300 | * @param string $path 301 | * 302 | * @return string 303 | */ 304 | private function basename($path) 305 | { 306 | $separators = '/'; 307 | if (DIRECTORY_SEPARATOR != '/') { 308 | // For Windows OS add special separator. 309 | $separators .= DIRECTORY_SEPARATOR; 310 | } 311 | 312 | // Remove right-most slashes when $path points to directory. 313 | $path = rtrim($path, $separators); 314 | 315 | // Returns the trailing part of the $path starting after one of the directory separators. 316 | $filename = preg_match('@[^'.preg_quote($separators, '@').']+$@', $path, $matches) ? $matches[0] : ''; 317 | 318 | return $filename; 319 | } 320 | 321 | /** 322 | * @param string|resource|StreamInterface $resource 323 | * 324 | * @return StreamInterface 325 | */ 326 | private function createStream($resource) 327 | { 328 | if ($resource instanceof StreamInterface) { 329 | return $resource; 330 | } 331 | 332 | if ($this->streamFactory instanceof HttplugStreamFactory) { 333 | return $this->streamFactory->createStream($resource); 334 | } 335 | 336 | // Assert: We are using a PSR17 stream factory. 337 | if (\is_string($resource)) { 338 | return $this->streamFactory->createStream($resource); 339 | } 340 | 341 | if (\is_resource($resource)) { 342 | return $this->streamFactory->createStreamFromResource($resource); 343 | } 344 | 345 | throw new \InvalidArgumentException(sprintf('First argument to "%s::createStream()" must be a string, resource or StreamInterface.', __CLASS__)); 346 | } 347 | } 348 | --------------------------------------------------------------------------------