Completed
Push — master ( 56566c...f8ca6f )
by Muhammad
06:54
created

DbDriver::parseLongMessage()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 38
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 1
Metric Value
c 4
b 1
f 1
dl 0
loc 38
rs 8.8571
cc 3
eloc 22
nc 3
nop 1
1
<?php
2
3
namespace NotificationChannels\Gammu\Drivers;
4
5
use Illuminate\Contracts\Config\Repository;
6
use NotificationChannels\Gammu\Models\Outbox;
7
use NotificationChannels\Gammu\Models\OutboxMultipart;
8
use NotificationChannels\Gammu\Models\Phone;
9
use NotificationChannels\Gammu\Exceptions\CouldNotSendNotification;
10
use Exception;
11
12
class DbDriver extends DriverAbstract
13
{
14
    protected $config;
15
16
    protected $outbox;
17
18
    protected $multipart;
19
20
    protected $phone;
21
22
    protected $data = [];
23
24
    protected $chunks = [];
25
26
    public $isLongSms = false;
27
28
    public $sender;
29
30
    private $minLongSmsChar = 160;
31
32
    public function __construct(
33
        Repository $config, Outbox $outbox, OutboxMultipart $multipart, Phone $phone
34
    ) {
35
        $this->config = $config;
36
        $this->outbox = $outbox;
37
        $this->multipart = $multipart;
38
        $this->phone = $phone;
39
40
        $this->data['CreatorID'] = $this->getSignature();
41
    }
42
43
    public function send($phoneNumber, $content, $sender = null)
44
    {
45
        $this->setDestination($phoneNumber);
46
        $this->setContent($content);
47
        $this->setSender($sender);
48
49
        // Check Destination
50
        $this->getDestination();
51
52
        $outbox = $this->outbox->create($this->data);
53
54
        if (! empty($this->chunks) && ! empty($outbox->ID)) {
0 ignored issues
show
Documentation introduced by
The property ID does not exist on object<NotificationChannels\Gammu\Models\Outbox>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
55
            foreach ($this->chunks as $chunk) {
56
                $chunk['ID'] = $outbox->ID;
0 ignored issues
show
Documentation introduced by
The property ID does not exist on object<NotificationChannels\Gammu\Models\Outbox>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
57
                $this->multipart->create($chunk);
58
            }
59
        }
60
    }
61
62
    public function setDestination($phoneNumber)
63
    {
64
        if (empty($phoneNumber)) {
65
            throw CouldNotSendNotification::destinationNotProvided();
66
        }
67
68
        $this->data['DestinationNumber'] = $this->destination = trim($phoneNumber);
69
70
        return $this;
71
    }
72
73
    public function getDestination()
74
    {
75
        if (empty($this->data['DestinationNumber'])) {
76
            throw CouldNotSendNotification::destinationNotProvided();
77
        }
78
79
        return $this->destination;
80
    }
81
82
    public function setContent($content)
83
    {
84
        if (empty($content)) {
85
            throw CouldNotSendNotification::contentNotProvided();
86
        }
87
88
        $this->content = $content;
89
90
        if (strlen($content) > $this->minLongSmsChar) {
91
            $this->parseLongMessage($content);
92
        } else {
93
            $this->data['TextDecoded'] = $content;
94
        }
95
96
        return $this;
97
    }
98
99
    public function getContent()
100
    {
101
        if (empty($this->content)) {
102
            throw CouldNotSendNotification::contentNotProvided();
103
        }
104
105
        $content = $this->content;
0 ignored issues
show
Unused Code introduced by
$content is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
106
107
        return $this->content;
108
    }
109
110
    public function setSender($sender = null)
111
    {
112
        if (empty($sender)) {
113
            $sender = $this->getDefaultSender();
114
        }
115
116
        $senders = $this->getSendersArray();
117
118
        if (! in_array($sender, $senders)) {
119
            return $this->getSender();
120
        }
121
122
        $this->data['SenderID'] = $this->sender = $sender;
123
124
        return $this;
125
    }
126
127
    public function getSender()
128
    {
129
        if (empty($this->sender)) {
130
            $this->sender = $this->getDefaultSender();
131
        }
132
133
        return $this->sender;
134
    }
135
136
    private function getDefaultSender()
137
    {
138
        $sender = $this->config->get('services.gammu.sender');
139
140
        $senders = $this->getSendersArray();
141
142
        if (in_array($sender, $senders)) {
143
            $this->sender = $sender;
144
145
            return $this->sender;
146
        }
147
148
        try {
149
            return $this->phone->where('Send', 'yes')->firstOrFail()->ID;
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<NotificationChannels\Gammu\Models\Phone>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
150
        } catch (Exception $e) {
151
            throw CouldNotSendNotification::senderNotProvided();
152
        }
153
    }
154
155
    private function getSendersArray()
156
    {
157
        $senders = $this->phone->where('Send', 'yes')->get()->pluck('ID')->toArray();
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<NotificationChannels\Gammu\Models\Phone>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
158
159
        if (empty($senders)) {
160
            throw CouldNotSendNotification::senderNotProvided();
161
        }
162
163
        return $senders;
164
    }
165
166
    /**
167
     * Generate UDH part for long SMS.
168
     *
169
     * @link https://en.wikipedia.org/wiki/Concatenated_SMS#Sending_a_concatenated_SMS_using_a_User_Data_Header
170
     *
171
     * @return string
172
     */
173
    private function generateUDH($total = 2, $sequence = 2, $ref = 0)
174
    {
175
        // Length of User Data Header, in this case 05
176
        $octet1 = '05';
177
178
        // Information Element Identifier, equal to 00 (Concatenated short messages, 8-bit reference number)
179
        $octet2 = '00';
180
181
        // Length of the header, excluding the first two fields; equal to 03
182
        $octet3 = '03';
183
184
        // CSMS reference number, must be same for all the SMS parts in the CSMS
185
        $octet4 = str_pad(dechex($ref), 2, '0', STR_PAD_LEFT);
186
187
        // Total number of parts
188
        $octet5 = str_pad(dechex($total), 2, '0', STR_PAD_LEFT);
189
190
        // Part sequence
191
        $octet6 = str_pad(dechex($sequence), 2, '0', STR_PAD_LEFT);
192
193
        $udh = collect([
194
            $octet1, $octet2, $octet3, $octet4, $octet5, $octet6,
195
        ])->implode('');
196
197
        return strtoupper($udh);
198
    }
199
200
    protected function parseLongMessage($content)
201
    {
202
        if (strlen($content) <= $this->minLongSmsChar) {
203
            return $this;
204
        }
205
206
        // Parse message to chunks
207
        // @ref: http://www.nowsms.com/long-sms-text-messages-and-the-160-character-limit
208
        $messages = str_split($content, 153);
209
        $messages = collect($messages);
210
        $messagesCount = $messages->count();
211
212
        // Get first message
213
        $firstChunk = $messages->shift();
214
215
        // Generate UDH
216
        $ref = mt_rand(0, 255);
217
        $i = 1;
218
        $firstUDH = $this->generateUDH($messagesCount, $i, $ref);
219
        ++$i;
220
221
        $this->data['TextDecoded'] = $firstChunk;
222
        $this->data['UDH'] = $firstUDH;
223
        $this->data['MultiPart'] = 'true';
224
225
        foreach ($messages as $chunk) {
226
            array_push($this->chunks, [
227
                'UDH' => $this->generateUDH($messagesCount, $i, $ref),
228
                'TextDecoded' => $chunk,
229
                'SequencePosition' => $i,
230
            ]);
231
            ++$i;
232
        }
233
234
        $this->isLongSms = true;
235
236
        return $this;
237
    }
238
}
239