Completed
Push — master ( 0a6e40...a44d55 )
by Camilo
06:48
created

constructBodyForMultipart()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 0
loc 14
ccs 8
cts 8
cp 1
crap 3
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace unreal4u\TelegramAPI\InternalFunctionality;
6
7
use MultipartBuilder\Builder;
8
use MultipartBuilder\MultipartData;
9
use Psr\Log\LoggerInterface;
10
use function strstr;
11
use unreal4u\Dummy\Logger;
12
use unreal4u\TelegramAPI\Abstracts\TelegramMethods;
13
use unreal4u\TelegramAPI\Exceptions\MissingMandatoryField;
14
use unreal4u\TelegramAPI\Telegram\Types\Custom\InputFile;
15
use function pathinfo;
16
use function stream_get_contents;
17
use function strlen;
18
use const PATHINFO_BASENAME;
19
20
class PostOptionsConstructor
21
{
22
    /**
23
     * With this flag we'll know what type of request to send to Telegram
24
     *
25
     * 'application/x-www-form-urlencoded' is the "normal" one, which is simpler and quicker.
26
     * 'multipart/form-data' should be used only when you upload documents, photos, etc.
27
     *
28
     * @var string
29
     */
30
    public $formType = 'application/x-www-form-urlencoded';
31
32
    /**
33
     * This is a flag intended to temporarily store the number of local files
34
     *
35
     * @var int
36
     */
37
    private $numberOfLocalFiles = 0;
38
39
    /**
40
     * @var LoggerInterface
41
     */
42
    protected $logger;
43
44 41
    public function __construct(LoggerInterface $logger = null)
45
    {
46 41
        if ($logger === null) {
47 41
            $logger = new Logger();
48
        }
49 41
        $this->logger = $logger;
50 41
    }
51
52
    /**
53
     * Builds up the form elements to be sent to Telegram
54
     *
55
     * @param TelegramMethods $method
56
     * @return array
57
     * @throws MissingMandatoryField
58
     */
59 32
    public function constructOptions(TelegramMethods $method): array
60
    {
61 32
        $result = $this->checkIsMultipart($method);
62
63 32
        if (!empty($result)) {
64 4
            return $this->constructMultipartOptions(
65 4
                $method->export(),
66 4
                $result
67
            );
68
        }
69
70 28
        $body = http_build_query($method->export(), '', '&');
71
72
        return [
73
            'headers' => [
74 26
                'Content-Type' => 'application/x-www-form-urlencoded',
75 26
                'Content-Length' => strlen($body),
76 26
                'User-Agent' => 'PHP7+ Bot API',
77
            ],
78 26
            'body' => $body
79
        ];
80
    }
81
82
    /**
83
     * Check if the given TelegramMethod should be handled as a multipart.
84
     *
85
     * @param TelegramMethods $method
86
     * @return array
87
     */
88 32
    private function checkIsMultipart(TelegramMethods $method): array
89
    {
90 32
        $this->logger->debug('Checking whether to apply special conditions to this request');
91 32
        $method->performSpecialConditions();
92
93 32
        $return = [];
94
95 32
        foreach ($method as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $method of type object<unreal4u\Telegram...tracts\TelegramMethods> is not traversable.
Loading history...
96 28
            if ($method->hasLocalFiles() === true) {
97 4
                $this->logger->debug('About to send a file, so changing request to use multi-part instead');
98
                // If we are about to send a file, we must use the multipart/form-data way
99 4
                $this->formType = 'multipart/form-data';
100 4
                foreach ($method->getLocalFiles() as $identifier => $localFile) {
101 4
                    $return[$identifier . '~' . $this->numberOfLocalFiles] = [
102 4
                        'id' => $this->numberOfLocalFiles,
103 4
                        'filename' => basename($localFile->path),
104 4
                        'stream' => $localFile->getStream(),
105
                    ];
106 28
                    $this->numberOfLocalFiles++;
107
                }
108
            }
109
        }
110
111 32
        return $return;
112
    }
113
114
    /**
115
     * Builds up a multipart form-like array for the HTTP client
116
     *
117
     * @param array $data The original object in array form
118
     * @param array $multipartData
119
     * @return array Returns the actual formdata to be sent
120
     */
121 4
    public function constructMultipartOptions(array $data, array $multipartData): array
122
    {
123 4
        $builder = $this->constructBodyForMultipart($data, $multipartData);
124 4
        $body = $builder->buildAll();
125
        $completeRequest = [
126
            'headers' => [
127 4
                'Content-Type' => 'multipart/form-data; boundary="' . $builder->getBoundary() . '"',
128 4
                'Content-Length' => strlen($body)
129
            ],
130 4
            'body' => $body
131
        ];
132
133 4
        return $completeRequest;
134
    }
135
136
    /**
137
     * Is able to construct the entire body for a multipart form data
138
     *
139
     * @param array $data
140
     * @param array $multipartData
141
     * @return Builder
142
     */
143 4
    private function constructBodyForMultipart(array $data, array $multipartData): Builder
144
    {
145 4
        $builder = new Builder();
146 4
        $this->logger->debug('Creating multi-part form array data (complex and expensive)');
147
148 4
        foreach ($data as $id => $value) {
149 4
            if (!$value instanceof InputFile) {
150 4
                $appendingData = new MultipartData((string)$id, (string)$value);
151 4
                $builder->append($appendingData);
152
            }
153
        }
154
155 4
        return $this->appendMediaStream($builder, $multipartData);
156
    }
157
158
    /**
159
     * Scans the actual media and adds it to the multipart form data
160
     *
161
     * @param Builder $builder
162
     * @param array $multipartData
163
     * @return Builder
164
     */
165 4
    private function appendMediaStream(Builder $builder, array $multipartData): Builder
166
    {
167 4
        foreach ($multipartData as $identifier => $mediaStream) {
168 4
            $appendingData = new MultipartData(
169 4
                strstr($identifier, '~', true),
170 4
                stream_get_contents($mediaStream['stream']),
171 4
                pathinfo($mediaStream['filename'], PATHINFO_BASENAME)
172
            );
173
174 4
            $builder->append($appendingData);
175
        }
176
177 4
        return $builder;
178
    }
179
}
180