Completed
Push — master ( d9a3d5...4dcabc )
by Camilo
10:43 queued 10:43
created

TelegramMethods::formatReplyMarkup()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 10
ccs 5
cts 6
cp 0.8333
crap 3.0416
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace unreal4u\TelegramAPI\Abstracts;
6
7
use Generator;
8
use Psr\Log\LoggerInterface;
9
use unreal4u\TelegramAPI\Exceptions\MissingMandatoryField;
10
use unreal4u\TelegramAPI\Interfaces\TelegramMethodDefinitions;
11
use unreal4u\TelegramAPI\InternalFunctionality\TelegramResponse;
12
use unreal4u\TelegramAPI\Telegram\Types\Custom\InputFile;
13
use unreal4u\TelegramAPI\Telegram\Types\Inline\Keyboard\Markup;
14
use unreal4u\TelegramAPI\Telegram\Types\Message;
15
use unreal4u\TelegramAPI\Telegram\Types\ReplyKeyboardMarkup;
16
use function get_class;
17
use function is_object;
18
use function json_encode;
19
20
/**
21
 * Contains methods that all Telegram methods should implement
22
 */
23
abstract class TelegramMethods implements TelegramMethodDefinitions
24
{
25
    /**
26
     * @var TelegramResponse
27
     */
28
    protected $response;
29
30
    /**
31
     * Most of the methods will return a Message object on success, so set that as the default.
32
     *
33
     * This function may however be overwritten if the method uses another object, there are many examples of this, so
34
     * just check out the rest of the code. A good place to start is GetUserProfilePhotos or LeaveChat
35
     *
36
     * @see \unreal4u\TelegramAPI\Telegram\Methods\GetUserProfilePhotos
37
     * @see \unreal4u\TelegramAPI\Telegram\Methods\LeaveChat
38
     *
39
     * @param TelegramResponse $data
40
     * @param LoggerInterface $logger
41
     *
42
     * @return TelegramTypes
43
     */
44 9
    public static function bindToObject(TelegramResponse $data, LoggerInterface $logger): TelegramTypes
45
    {
46 9
        return new Message($data->getResult(), $logger);
47
    }
48
49
    /**
50
     * Before making the actual request this method will be called
51
     *
52
     * It must be used to json_encode stuff, or do other changes in the internal class representation _before_ sending
53
     * it to the Telegram servers
54
     *
55
     * @return TelegramMethods
56
     */
57 32
    public function performSpecialConditions(): TelegramMethods
58
    {
59 32
        if (!empty($this->reply_markup)) {
60 1
            $this->reply_markup = json_encode($this->formatReplyMarkup($this->reply_markup));
0 ignored issues
show
Bug introduced by
The property reply_markup does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
61
        }
62
63 32
        return $this;
64
    }
65
66
    /**
67
     * Ensure we have a method we can always call in order to check if we have any local files
68
     *
69
     * @see \unreal4u\TelegramAPI\InternalFunctionality\PostOptionsConstructor::checkIsMultipart
70
     *
71
     * @return bool
72
     */
73 20
    public function hasLocalFiles(): bool
74
    {
75 20
        return false;
76
    }
77
78
    /**
79
     * Yields all local files (if present)
80
     *
81
     * @return Generator|InputFile[]
82
     */
83
    public function getLocalFiles(): Generator
84
    {
85
        yield;
86
    }
87
88
    /**
89
     * Exports the class to an array in order to send it to the Telegram servers without extra fields that we don't need
90
     *
91
     * @return array
92
     * @throws MissingMandatoryField
93
     */
94 38
    final public function export(): array
95
    {
96 38
        $finalArray = [];
97 38
        $mandatoryFields = $this->getMandatoryFields();
98
99 38
        $cleanObject = new $this();
100 38
        foreach ($cleanObject as $fieldId => $value) {
101 38
            if ($this->$fieldId === $cleanObject->$fieldId) {
102 38
                if (\in_array($fieldId, $mandatoryFields, true)) {
103 6
                    $missingMandatoryField = new MissingMandatoryField(sprintf(
104 6
                        'The field "%s" for class "%s" is mandatory and empty, please correct',
105
                        $fieldId,
106 6
                        get_class($cleanObject)
107
                    ));
108 6
                    $missingMandatoryField->method = get_class($cleanObject);
109 6
                    $missingMandatoryField->methodInstance = $this;
110 38
                    throw $missingMandatoryField;
111
                }
112
            } else {
113 25
                $finalArray[$fieldId] = $this->$fieldId;
114
            }
115
        }
116
117 32
        return $finalArray;
118
    }
119
120
    /**
121
     * Will resolve the dependency of a mandatory inline_message_id OR a chat_id + message_id
122
     *
123
     * NOTE: This will use pass by reference instead of copy on write as the use-case for this functions allows this
124
     *
125
     * @param array $return
126
     * @return array
127
     */
128 4
    final protected function mandatoryUserOrInlineMessageId(array &$return): array
129
    {
130 4
        if (empty($this->chat_id) && empty($this->message_id)) {
0 ignored issues
show
Bug introduced by
The property chat_id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property message_id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
131 3
            $return[] = 'inline_message_id';
132
        }
133
134
        // On the other hand, chat_id and message_id are mandatory if inline_message_id is not filled in
135 4
        if (empty($this->inline_message_id)) {
0 ignored issues
show
Bug introduced by
The property inline_message_id does not seem to exist. Did you mean message_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
136 3
            $return[] = 'chat_id';
137 3
            $return[] = 'message_id';
138
        }
139
140 4
        return $return;
141
    }
142
143
    /**
144
     * ReplyMarkup fields require a bit of work before sending them
145
     *
146
     * This happens because reply markup are a type thus they don't have an export mechanism to do the job
147
     *
148
     * @param TelegramTypes $replyMarkup
149
     * @return TelegramTypes
150
     */
151 1
    private function formatReplyMarkup(TelegramTypes $replyMarkup): TelegramTypes
152
    {
153 1
        if ($replyMarkup instanceof Markup) {
154
            $replyMarkup->inline_keyboard = $this->getArrayFromKeyboard($replyMarkup->inline_keyboard);
155 1
        } elseif ($replyMarkup instanceof ReplyKeyboardMarkup) {
156 1
            $replyMarkup->keyboard = $this->getArrayFromKeyboard($replyMarkup->keyboard);
157
        }
158
159 1
        return $replyMarkup;
160
    }
161
162 1
    private function getArrayFromKeyboard(array $keyboardArray): array
163
    {
164 1
        $finalCleanArray = [];
165
166
        // A keyboard is an array of an array of objects or strings
167 1
        foreach ($keyboardArray as $rowItems) {
168 1
            $elements = [];
169 1
            foreach ($rowItems as $rowItem) {
170 1
                if (is_object($rowItem)) {
171
                    // Button is effectively an object
172
                    $elements[] = $this->exportReplyMarkupItem($rowItem);
173
                } else {
174
                    // Add support for old style simple text buttons
175 1
                    $elements[] = $rowItem;
176
                }
177
            }
178
179 1
            $finalCleanArray[] = $elements;
180
        }
181
182 1
        return $finalCleanArray;
183
    }
184
185
    /**
186
     * Does the definitive export of those fields in a reply markup item that are filled in
187
     *
188
     * @param TelegramTypes $markupItem
189
     * @return array
190
     */
191
    private function exportReplyMarkupItem(TelegramTypes $markupItem): array
192
    {
193
        $finalArray = [];
194
        $cleanObject = new $markupItem;
195
        foreach ($markupItem as $fieldId => $value) {
0 ignored issues
show
Bug introduced by
The expression $markupItem of type object<unreal4u\Telegram...bstracts\TelegramTypes> is not traversable.
Loading history...
196
            if ($markupItem->$fieldId !== $cleanObject->$fieldId) {
197
                $finalArray[$fieldId] = $markupItem->$fieldId;
198
            }
199
        }
200
201
        return $finalArray;
202
    }
203
}
204