summaryrefslogtreecommitdiffstats
path: root/vendor/paypal/paypalhttp/lib/PayPalHttp/Serializer/Multipart.php
blob: b9142de684277826d4d06d3bfaea69914b27abb5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<?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);
    }
}