135 lines
4.1 KiB
PHP
135 lines
4.1 KiB
PHP
![]() |
<?php
|
||
|
|
||
|
namespace PayPalHttp\Serializer;
|
||
|
|
||
|
use finfo;
|
||
|
use PayPalHttp\HttpRequest;
|
||
|
use PayPalHttp\Serializer;
|
||
|
use PayPalHttp\Encoder;
|
||
|
use PayPalHttp\Serializer\FormPart;
|
||
|
|
||
|
/**
|
||
|
* Class Multipart
|
||
|
* @package PayPalHttp\Serializer
|
||
|
*
|
||
|
* Serializer for multipart.
|
||
|
*/
|
||
|
class Multipart implements Serializer
|
||
|
{
|
||
|
const LINEFEED = "\r\n";
|
||
|
|
||
|
public function contentType()
|
||
|
{
|
||
|
return "/^multipart\/.*$/";
|
||
|
}
|
||
|
|
||
|
public function encode(HttpRequest $request)
|
||
|
{
|
||
|
if (!is_array($request->body) || !$this->isAssociative($request->body))
|
||
|
{
|
||
|
throw new \Exception("HttpRequest body must be an associative array when Content-Type is: " . $request->headers["content-type"]);
|
||
|
}
|
||
|
$boundary = "---------------------" . md5(mt_rand() . microtime());
|
||
|
$contentTypeHeader = $request->headers["content-type"];
|
||
|
$request->headers["content-type"] = "{$contentTypeHeader}; boundary={$boundary}";
|
||
|
|
||
|
$value_params = [];
|
||
|
$file_params = [];
|
||
|
|
||
|
$disallow = ["\0", "\"", "\r", "\n"];
|
||
|
|
||
|
$body = [];
|
||
|
|
||
|
foreach ($request->body as $k => $v) {
|
||
|
$k = str_replace($disallow, "_", $k);
|
||
|
if (is_resource($v)) {
|
||
|
$file_params[] = $this->prepareFilePart($k, $v, $boundary);
|
||
|
} else if ($v instanceof FormPart) {
|
||
|
$value_params[] = $this->prepareFormPart($k, $v, $boundary);
|
||
|
} else {
|
||
|
$value_params[] = $this->prepareFormField($k, $v, $boundary);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$body = array_merge($value_params, $file_params);
|
||
|
|
||
|
// add boundary for each parameters
|
||
|
array_walk($body, function (&$part) use ($boundary) {
|
||
|
$part = "--{$boundary}" . self::LINEFEED . "{$part}";
|
||
|
});
|
||
|
|
||
|
// add final boundary
|
||
|
$body[] = "--{$boundary}--";
|
||
|
$body[] = "";
|
||
|
|
||
|
return implode(self::LINEFEED, $body);
|
||
|
}
|
||
|
|
||
|
public function decode($data)
|
||
|
{
|
||
|
throw new \Exception("Multipart does not support deserialization");
|
||
|
}
|
||
|
|
||
|
private function isAssociative(array $array)
|
||
|
{
|
||
|
return array_values($array) !== $array;
|
||
|
}
|
||
|
|
||
|
private function prepareFormField($partName, $value, $boundary)
|
||
|
{
|
||
|
return implode(self::LINEFEED, [
|
||
|
"Content-Disposition: form-data; name=\"{$partName}\"",
|
||
|
"",
|
||
|
filter_var($value),
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
private function prepareFilePart($partName, $file, $boundary)
|
||
|
{
|
||
|
$fileInfo = new finfo(FILEINFO_MIME_TYPE);
|
||
|
$filePath = stream_get_meta_data($file)['uri'];
|
||
|
$data = file_get_contents($filePath);
|
||
|
$mimeType = $fileInfo->buffer($data);
|
||
|
|
||
|
$splitFilePath = explode(DIRECTORY_SEPARATOR, $filePath);
|
||
|
$filePath = end($splitFilePath);
|
||
|
$disallow = ["\0", "\"", "\r", "\n"];
|
||
|
$filePath = str_replace($disallow, "_", $filePath);
|
||
|
return implode(self::LINEFEED, [
|
||
|
"Content-Disposition: form-data; name=\"{$partName}\"; filename=\"{$filePath}\"",
|
||
|
"Content-Type: {$mimeType}",
|
||
|
"",
|
||
|
$data,
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
private function prepareFormPart($partName, $formPart, $boundary)
|
||
|
{
|
||
|
$contentDisposition = "Content-Disposition: form-data; name=\"{$partName}\"";
|
||
|
|
||
|
$partHeaders = $formPart->getHeaders();
|
||
|
$formattedheaders = array_change_key_case($partHeaders);
|
||
|
if (array_key_exists("content-type", $formattedheaders)) {
|
||
|
if ($formattedheaders["content-type"] === "application/json") {
|
||
|
$contentDisposition .= "; filename=\"{$partName}.json\"";
|
||
|
}
|
||
|
$tempRequest = new HttpRequest('/', 'POST');
|
||
|
$tempRequest->headers = $formattedheaders;
|
||
|
$tempRequest->body = $formPart->getValue();
|
||
|
$encoder = new Encoder();
|
||
|
$partValue = $encoder->serializeRequest($tempRequest);
|
||
|
} else {
|
||
|
$partValue = $formPart->getValue();
|
||
|
}
|
||
|
|
||
|
$finalPartHeaders = [];
|
||
|
foreach ($partHeaders as $k => $v) {
|
||
|
$finalPartHeaders[] = "{$k}: {$v}";
|
||
|
}
|
||
|
|
||
|
$body = array_merge([$contentDisposition], $finalPartHeaders, [""], [$partValue]);
|
||
|
|
||
|
return implode(self::LINEFEED, $body);
|
||
|
}
|
||
|
}
|