Skip to content

Commit 4932e97

Browse files
committed
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.
1 parent 6d7694c commit 4932e97

File tree

1 file changed

+58
-1
lines changed

1 file changed

+58
-1
lines changed

src/MultipartStreamBuilder.php

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ class MultipartStreamBuilder
3838
*/
3939
private $data = [];
4040

41+
/**
42+
* @var int Bytes of unallocated memory to use for stream buffer.
43+
* To have MultipartStreamBuilder manage it automatically, set to -1.
44+
* Default: -1
45+
*/
46+
private $bufferMaxMemory = -1;
47+
4148
/**
4249
* @param HttplugStreamFactory|StreamFactoryInterface|null $streamFactory
4350
*/
@@ -116,10 +123,18 @@ public function addResource($name, $resource, array $options = [])
116123
*/
117124
public function build()
118125
{
126+
// Assign maximimum 1/4 php's available memory
127+
// to attempt buffering the stream content.
128+
// If the stream content exceed this, will fallback
129+
// to use temporary file.
130+
$maxmemory = ($this->bufferMaxMemory < 0)
131+
? \floor(static::getAvailableMemory() / 4)
132+
: $this->bufferMaxMemory;
133+
119134
// Open a temporary read-write stream as buffer.
120135
// If the size is less than predefined limit, things will stay in memory.
121136
// If the size is more than that, things will be stored in temp file.
122-
$buffer = fopen('php://temp', 'r+');
137+
$buffer = fopen('php://temp/maxmemory:' . $maxmemory, 'r+');
123138
foreach ($this->data as $data) {
124139
// Add start and headers
125140
fwrite($buffer, "--{$this->getBoundary()}\r\n".
@@ -347,4 +362,46 @@ private function createStream($resource)
347362

348363
throw new \InvalidArgumentException(sprintf('First argument to "%s::createStream()" must be a string, resource or StreamInterface.', __CLASS__));
349364
}
365+
366+
/**
367+
* Setup the stream buffer size limit. PHP will allocate buffer
368+
* in memory if the size of the stream is smaller than this size.
369+
* Otherwise, PHP will store the stream data in a temporary file.
370+
*
371+
* @param integer $size
372+
* Size of stream data buffered (in bytes) until using temporary
373+
* file to buffer.
374+
*
375+
* @return MultipartStreamBuilder
376+
*/
377+
public function setBufferMaxMemory(int $size): MultipartStreamBuilder
378+
{
379+
$this->bufferMaxMemory = $size;
380+
381+
return $this;
382+
}
383+
384+
/**
385+
* Get and parse memory_limit into bytes integer.
386+
*
387+
* @return integer
388+
*
389+
* @throws \Exception
390+
* If the ini format does not match expectation.
391+
*/
392+
protected static function getAvailableMemory(): int
393+
{
394+
$memory_limit = ini_get('memory_limit');
395+
if (!preg_match('/^(\d+)(G|M|K|)$/', $memory_limit, $matches)) {
396+
throw new \Exception("Unknown memory_limit format: {$memory_limit}");
397+
}
398+
if ($matches[2] == 'G') {
399+
$memory_limit = $matches[1] * 1024 * 1024 * 1024; // nnnG -> nnn GB
400+
} else if ($matches[2] == 'M') {
401+
$memory_limit = $matches[1] * 1024 * 1024; // nnnM -> nnn MB
402+
} else if ($matches[2] == 'K') {
403+
$memory_limit = $matches[1] * 1024; // nnnK -> nnn KB
404+
}
405+
return (int) $memory_limit - \memory_get_usage();
406+
}
350407
}

0 commit comments

Comments
 (0)