Completed
Push — master ( 28d5b3...5a7b92 )
by Camilo
18s
created

PostOptionsConstructor::checkIsMultipart()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 12
cts 12
cp 1
rs 8.9197
c 0
b 0
f 0
cc 4
eloc 13
nc 3
nop 1
crap 4
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 unreal4u\TelegramAPI\Abstracts\TelegramMethods;
11
use unreal4u\TelegramAPI\Telegram\Types\Custom\InputFile;
12
13
class PostOptionsConstructor
14
{
15
    /**
16
     * With this flag we'll know what type of request to send to Telegram
17
     *
18
     * 'application/x-www-form-urlencoded' is the "normal" one, which is simpler and quicker.
19
     * 'multipart/form-data' should be used only when you upload documents, photos, etc.
20
     *
21
     * @var string
22
     */
23
    public $formType = 'application/x-www-form-urlencoded';
24
25
    /**
26
     * @var LoggerInterface
27
     */
28
    protected $logger;
29
30 40
    public function __construct(LoggerInterface $logger = null)
31
    {
32 40
        if ($logger === null) {
33 40
            $logger = new DummyLogger();
34
        }
35 40
        $this->logger = $logger;
36 40
    }
37
38
    /**
39
     * Builds up the form elements to be sent to Telegram
40
     *
41
     * @TODO Move this to apart function
42
     *
43
     * @param TelegramMethods $method
44
     * @return array
45
     * @throws \unreal4u\TelegramAPI\Exceptions\MissingMandatoryField
46
     */
47 31
    public function constructOptions(TelegramMethods $method): array
48
    {
49 31
        $result = $this->checkIsMultipart($method);
50
51 31
        if (!empty($result)) {
52 4
            return $this->constructMultipartOptions(
53 4
                $method->export(),
54 4
                $result['id'],
55 4
                $result['stream'],
56 4
                $result['filename']
57
            );
58
        }
59
60 27
        $body = http_build_query($method->export(), '', '&');
61
        
62
        return [
63
            'headers' => [
64 25
                'Content-Type' => 'application/x-www-form-urlencoded',
65 25
                'Content-Length' => strlen($body),
66 25
                'User-Agent: PHP7+ Bot API',
67
            ],
68 25
            'body' => $body
69
        ];
70
    }
71
72
    /**
73
     * Check if the given TelegramMethod should be handled as a multipart.
74
     *
75
     * @param TelegramMethods $method
76
     * @return array
77
     */
78 31
    private function checkIsMultipart(TelegramMethods $method): array
79
    {
80 31
        $this->logger->debug('Checking whether to apply special conditions to this request');
81 31
        $method->performSpecialConditions();
82
83 31
        $return = [];
84
85 31
        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...
86 27
            if (is_object($value) && $value instanceof InputFile) {
87 4
                $this->logger->debug('About to send a file, so changing request to use multi-part instead');
88
                // If we are about to send a file, we must use the multipart/form-data way
89 4
                $this->formType = 'multipart/form-data';
90
                $return = [
91 4
                    'id' => $key,
92 4
                    'filename' => $value->path,
93 27
                    'stream' => $value->getStream()
94
                ];
95
            }
96
        }
97
98 31
        return $return;
99
    }
100
101
    /**
102
     * Builds up a multipart form-like array for Guzzle
103
     *
104
     * @param array $data The original object in array form
105
     * @param string $fileKeyName A file handler will be sent instead of a string, state here which field it is
106
     * @param resource $stream The actual file handler
107
     * @param string $filename
108
     * @return array Returns the actual formdata to be sent
109
     */
110 4
    public function constructMultipartOptions(array $data, string $fileKeyName, $stream, string $filename): array
111
    {
112 4
        $builder = new Builder();
113 4
        $this->logger->debug('Creating multi-part form array data (complex and expensive)');
114
115 4
        foreach ($data as $id => $value) {
0 ignored issues
show
Bug introduced by
The expression $data of type object<MultipartBuilder\MultipartData>|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
116 4
            if ($id === $fileKeyName) {
117 4
                $data = new MultipartData(
118 4
                    (string) $id,
119 4
                    stream_get_contents($stream),
120 4
                    pathinfo($filename, PATHINFO_BASENAME)
121
                );
122
            } else {
123 4
                $data = new MultipartData((string) $id, (string) $value);
124
            }
125
            
126 4
            $builder->append($data);
127
        }
128
129 4
        $body = $builder->buildAll();
130
        $array = [
131
            'headers' => [
132 4
                'Content-Type' => 'multipart/form-data; boundary="' . $builder->getBoundary() . '"',
133 4
                'Content-Length' => strlen($body)
134
            ],
135 4
            'body' => $body
136
        ];
137 4
        return $array;
138
    }
139
}
140