@@ -38,6 +38,13 @@ class MultipartStreamBuilder
38
38
*/
39
39
private $ data = [];
40
40
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
+
41
48
/**
42
49
* @param HttplugStreamFactory|StreamFactoryInterface|null $streamFactory
43
50
*/
@@ -116,10 +123,18 @@ public function addResource($name, $resource, array $options = [])
116
123
*/
117
124
public function build ()
118
125
{
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
+
119
134
// Open a temporary read-write stream as buffer.
120
135
// If the size is less than predefined limit, things will stay in memory.
121
136
// 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+ ' );
123
138
foreach ($ this ->data as $ data ) {
124
139
// Add start and headers
125
140
fwrite ($ buffer , "-- {$ this ->getBoundary ()}\r\n" .
@@ -347,4 +362,46 @@ private function createStream($resource)
347
362
348
363
throw new \InvalidArgumentException (sprintf ('First argument to "%s::createStream()" must be a string, resource or StreamInterface. ' , __CLASS__ ));
349
364
}
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
+ }
350
407
}
0 commit comments