@@ -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
*/
@@ -133,10 +140,18 @@ public function addResource($name, $resource, array $options = [])
133
140
*/
134
141
public function build ()
135
142
{
143
+ // Assign maximimum 1/4 php's available memory
144
+ // to attempt buffering the stream content.
145
+ // If the stream content exceed this, will fallback
146
+ // to use temporary file.
147
+ $ maxmemory = ($ this ->bufferMaxMemory < 0 )
148
+ ? \floor (static ::getAvailableMemory () / 4 )
149
+ : $ this ->bufferMaxMemory ;
150
+
136
151
// Open a temporary read-write stream as buffer.
137
152
// If the size is less than predefined limit, things will stay in memory.
138
153
// If the size is more than that, things will be stored in temp file.
139
- $ buffer = fopen ('php://temp ' , 'r+ ' );
154
+ $ buffer = fopen ('php://temp/maxmemory: ' . $ maxmemory , 'r+ ' );
140
155
foreach ($ this ->data as $ data ) {
141
156
// Add start and headers
142
157
fwrite ($ buffer , "-- {$ this ->getBoundary ()}\r\n" .
@@ -358,4 +373,50 @@ private function createStream($resource)
358
373
359
374
throw new \InvalidArgumentException (sprintf ('First argument to "%s::createStream()" must be a string, resource or StreamInterface. ' , __CLASS__ ));
360
375
}
376
+
377
+ /**
378
+ * Setup the stream buffer size limit. PHP will allocate buffer
379
+ * in memory if the size of the stream is smaller than this size.
380
+ * Otherwise, PHP will store the stream data in a temporary file.
381
+ *
382
+ * @param integer $size
383
+ * Size of stream data buffered (in bytes) until using temporary
384
+ * file to buffer.
385
+ *
386
+ * @return MultipartStreamBuilder
387
+ */
388
+ public function setBufferMaxMemory (int $ size ): MultipartStreamBuilder
389
+ {
390
+ $ this ->bufferMaxMemory = $ size ;
391
+
392
+ return $ this ;
393
+ }
394
+
395
+ /**
396
+ * Get and parse memory_limit into bytes integer.
397
+ *
398
+ * @return integer
399
+ *
400
+ * @throws \Exception
401
+ * If the ini format does not match expectation.
402
+ */
403
+ protected static function getAvailableMemory (): int
404
+ {
405
+ $ memory_limit = ini_get ('memory_limit ' );
406
+ if ($ memory_limit === '-1 ' ) {
407
+ // If there is no memory limit, return 100MB by default.
408
+ return 100 * 1024 * 1024 ;
409
+ }
410
+ if (!preg_match ('/^(\d+)(G|M|K|)$/ ' , $ memory_limit , $ matches )) {
411
+ throw new \Exception ("Unknown memory_limit format: {$ memory_limit }" );
412
+ }
413
+ if ($ matches [2 ] == 'G ' ) {
414
+ $ memory_limit = $ matches [1 ] * 1024 * 1024 * 1024 ; // nnnG -> nnn GB
415
+ } else if ($ matches [2 ] == 'M ' ) {
416
+ $ memory_limit = $ matches [1 ] * 1024 * 1024 ; // nnnM -> nnn MB
417
+ } else if ($ matches [2 ] == 'K ' ) {
418
+ $ memory_limit = $ matches [1 ] * 1024 ; // nnnK -> nnn KB
419
+ }
420
+ return (int ) $ memory_limit - \memory_get_usage ();
421
+ }
361
422
}
0 commit comments