Skip to content

Commit

Permalink
Use temp stream instead of string to buffer content (#52)
Browse files Browse the repository at this point in the history
* Use temp stream instead of string to buffer content

* Update MultipartStreamBuilder::build to buffer built content
  in a php://temp stream resource instead of (string) $contents.
  This way, the size of the stream built won't be limited by PHP
  runtime limit.

* Detect available memory for maxmemory in buffer

* Detect available memory (in bytes). Then config the buffer
  stream to stay in memory until reaching 1/4 of that.
* Added setBufferMaxMemory to manually override that limit.

* Fix code style

* Improve documentation to getAvailableMemory

* Improve documentation to MultipartStreamBuilder::getAvailableMemory

* Update CHANGELOG.md

* Remove the memory detection logic from build

* Remove the memory detection logic from MultipartStreamBuilder::build.

* Code style update

* Minor updates

* Improve readability of comment.
* Use the old __toString approach if the stream is not isReadable().

* Remove unnecessary comment
  • Loading branch information
yookoala committed Dec 23, 2020
1 parent 9addfcf commit 8819e79
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 23 deletions.
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 1.2.0 - (unreleased)

- Refactored MultipartStreamBuilder to clean up and allow injecting data without a filename
- Dynamically use memory or temp file to buffer the stream content.

## 1.1.2 - 2020-07-13

Expand All @@ -24,23 +25,23 @@ No changes from 0.2.0.
## 0.2.0 - 2017-02-20

You may do a BC update to version 0.2.0 if you are sure that you are not adding
multiple resources with the same name to the Builder.
multiple resources with the same name to the Builder.

### Fixed

- Make sure one can add resources with same name without overwrite.
- Make sure one can add resources with same name without overwrite.

## 0.1.6 - 2017-02-16

### Fixed

- Performance improvements by avoid using `uniqid()`.
- Performance improvements by avoid using `uniqid()`.

## 0.1.5 - 2017-02-14

### Fixed

- Support for non-readable streams. This fix was needed because flaws in Guzzle, Zend and Slims implementations of PSR-7.
- Support for non-readable streams. This fix was needed because flaws in Guzzle, Zend and Slims implementations of PSR-7.

## 0.1.4 - 2016-12-31

Expand All @@ -52,7 +53,7 @@ multiple resources with the same name to the Builder.

### Added

- Added `CustomMimetypeHelper` to allow you to configure custom mimetypes.
- Added `CustomMimetypeHelper` to allow you to configure custom mimetypes.

### Changed

Expand All @@ -62,13 +63,13 @@ multiple resources with the same name to the Builder.

### Added

- Support for Outlook msg files.
- Support for Outlook msg files.

## 0.1.1 - 2016-08-10

### Added

- Support for Apple passbook.
- Support for Apple passbook.

## 0.1.0 - 2016-07-19

Expand Down
42 changes: 26 additions & 16 deletions src/MultipartStreamBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,10 @@ public function __construct($streamFactory = null)
}

/**
* Add a resource to the Multipart Stream
* Add a resource to the Multipart Stream.
*
* @param string|resource|\Psr\Http\Message\StreamInterface $resource
* The filepath, resource or StreamInterface of the data.
* @param array $headers
* Additional headers array: ['header-name' => 'header-value'].
* @param string|resource|\Psr\Http\Message\StreamInterface $resource the filepath, resource or StreamInterface of the data
* @param array $headers additional headers array: ['header-name' => 'header-value']
*
* @return MultipartStreamBuilder
*/
Expand Down Expand Up @@ -133,28 +131,40 @@ public function addResource($name, $resource, array $options = [])
*/
public function build()
{
$streams = '';
// Open a temporary read-write stream as buffer.
// If the size is less than predefined limit, things will stay in memory.
// If the size is more than that, things will be stored in temp file.
$buffer = fopen('php://temp', 'r+');
foreach ($this->data as $data) {
// Add start and headers
$streams .= "--{$this->getBoundary()}\r\n".
$this->getHeaders($data['headers'])."\r\n";
fwrite($buffer, "--{$this->getBoundary()}\r\n".
$this->getHeaders($data['headers'])."\r\n");

// Convert the stream to string
/* @var $contentStream StreamInterface */
/** @var $contentStream StreamInterface */
$contentStream = $data['contents'];

// Read stream into buffer
if ($contentStream->isSeekable()) {
$streams .= $contentStream->__toString();
$contentStream->rewind(); // rewind to beginning.
}
if ($contentStream->isReadable()) {
while (!$contentStream->eof()) {
// Read 1MB chunk into buffer until reached EOF.
fwrite($buffer, $contentStream->read(1048576));
}
} else {
$streams .= $contentStream->getContents();
fwrite($buffer, $contentStream->__toString());
}

$streams .= "\r\n";
fwrite($buffer, "\r\n");
}

// Append end
$streams .= "--{$this->getBoundary()}--\r\n";
fwrite($buffer, "--{$this->getBoundary()}--\r\n");

// Rewind to starting position for reading.
fseek($buffer, 0);

return $this->createStream($streams);
return $this->createStream($buffer);
}

/**
Expand Down

0 comments on commit 8819e79

Please sign in to comment.