|
<?php |
|
|
|
/** |
|
* A genertor that yields multipart form-data fragments (without the ending EOL). |
|
* Would encode all files with base64 to make the request binary-safe. |
|
* |
|
* @param iterable $vars |
|
* Key-value iterable (e.g. assoc array) of string or integer. |
|
* Keys represents the field name. |
|
* @param iterable $files |
|
* Key-value iterable (e.g. assoc array) of file path string. |
|
* Keys represents the field name of file upload. |
|
* |
|
* @return \Generator |
|
* Generator of multipart form-data fragments (without the ending EOL) in assoc format, |
|
* always contains 2 key-values: |
|
* * header: An array of header for a key-value pair. |
|
* * content: A value string (can contain binary content) of the key-value pair. |
|
*/ |
|
function generate_multipart_data_parts(iterable $vars, iterable $files=[]): Generator { |
|
// handle normal variables |
|
foreach ($vars as $name => $value) { |
|
$name = urlencode($name); |
|
$value = urlencode($value); |
|
yield [ |
|
'header' => ["Content-Disposition: form-data; name=\"{$name}\""], |
|
'content' => $value, |
|
]; |
|
} |
|
|
|
// handle file contents |
|
foreach ($files as $file_fieldname => $file_path) { |
|
$file_fieldname = urlencode($file_fieldname); |
|
$file_data = file_get_contents($file_path); |
|
yield [ |
|
'header' => [ |
|
"Content-Disposition: form-data; name=\"{$file_fieldname}\"; filename=\"".basename($file_path)."\"", |
|
"Content-Type: application/octet-stream", // for binary safety |
|
], |
|
'content' => $file_data |
|
]; |
|
} |
|
} |
|
|
|
/** |
|
* Converts output of generate_multipart_data_parts() into form data. |
|
* |
|
* @param iterable $parts |
|
* An iterator of form fragment arrays. See return data of |
|
* generate_multipart_data_parts(). |
|
* @param string|null $boundary |
|
* An optional pre-generated boundary string to use for wrapping data. |
|
* Please reference section 7.2 "The Multipart Content-Type" in RFC1341. |
|
* |
|
* @return array |
|
* An assoc array with 2 items: |
|
* * boundary: the multipart boundary string |
|
* * data: the data string (can contain binary data) |
|
*/ |
|
function wrap_multipart_data(iterable $parts, ?string $boundary = null): array { |
|
if (empty($boundary)) { |
|
$boundary = 'boundary' . time(); |
|
} |
|
$data = ''; |
|
foreach ($parts as $part) { |
|
list('header' => $header, 'content' => $content) = $part; |
|
// Check content for boundary. |
|
// Note: Won't check header and expect the program makes sense there. |
|
if (strstr($content, "\r\n$boundary") !== false) { |
|
throw new \Exception('Error: data contains the multipart boundary'); |
|
} |
|
$data .= "--{$boundary}\r\n"; |
|
$data .= implode("\r\n", $header) . "\r\n\r\n" . $content . "\r\n"; |
|
} |
|
// signal end of request (note the trailing "--") |
|
$data .= "--{$boundary}--\r\n"; |
|
return ['boundary' => $boundary, 'data' => $data]; |
|
} |