diff --git a/src/SmsLength.php b/src/SmsLength.php index 93da5a1..6a3ec35 100644 --- a/src/SmsLength.php +++ b/src/SmsLength.php @@ -82,11 +82,22 @@ class SmsLength */ private $messageCount; + /** + * @var string + */ + private $messageContent; + + /** + * @var null|array + */ + private $messageParts; + /** * @param string $messageContent SMS message content (UTF-8) */ public function __construct(string $messageContent) { + $this->messageContent = $messageContent; $this->inspect($messageContent); } @@ -199,4 +210,74 @@ private function inspect(string $messageContent): void $this->messageCount = (int)ceil($this->size / $concatSize); } } + + /** + * Divide message to SMS parts indexed from 1 + * @return array|string[] + */ + public function getMessageParts(): array + { + if ($this->messageParts === null) { + if ($this->getMessageCount() === 1) { + $this->messageParts = [1 => $this->messageContent]; + } elseif ($this->getEncoding() === '7-bit') { + $this->messageParts = $this->divide7bitMessage(); + } else { + $this->messageParts = $this->divideUcs2Message(); + } + } + + return $this->messageParts; + } + + private function divide7bitMessage(): array + { + $size = 0; + $actualPart = 1; + $parts[$actualPart] = ''; // Init first part + + $mbLength = mb_strlen($this->messageContent, 'UTF-8'); + for ($i = 0; $i < $mbLength; $i++) { + $char = mb_substr($this->messageContent, $i, 1, 'UTF-8'); + if (in_array($char, self::GSM0338_BASIC, true)) { + $size++; + } else { + $size += 2; + } + + // Init next part + if ($actualPart * self::MAXIMUM_CHARACTERS_7BIT_CONCATENATED < $size) { + $actualPart++; + $parts[$actualPart] = ''; + } + + $parts[$actualPart] .= $char; + } + return $parts; + } + + private function divideUcs2Message(): array + { + $size = 0; + $actualPart = 1; + $parts[$actualPart] = ''; // Init first part + + $mbLength = mb_strlen($this->messageContent, 'UTF-8'); + for ($i = 0; $i < $mbLength; $i++) { + $char = mb_substr($this->messageContent, $i, 1, 'UTF-8'); + $utf16Hex = bin2hex(mb_convert_encoding($char, 'UTF-16', 'UTF-8')); + $charSize = strlen($utf16Hex) / 4; + $size += $charSize; + + // Init next part + if ($actualPart * self::MAXIMUM_CHARACTERS_UCS2_CONCATENATED < $size) { + $actualPart++; + $parts[$actualPart] = ''; + } + + $parts[$actualPart] .= $char; + } + + return $parts; + } } diff --git a/tests/SmsLengthTest.php b/tests/SmsLengthTest.php index 011bf83..040c1d1 100644 --- a/tests/SmsLengthTest.php +++ b/tests/SmsLengthTest.php @@ -91,6 +91,58 @@ public function providerSize(): array ]; } + /** + * @dataProvider providerMessageParts + */ + public function testDivideMessage(array $parts, int $characters): void { + $message = implode('', $parts); + + $size = new SmsLength($message); + + static::assertSame($parts, $size->getMessageParts()); + static::assertCount(count($parts), $size->getMessageParts()); + static::assertSame($characters, $size->getSize()); + } + + public function providerMessageParts(): array + { + return [ + // 7-bit + 'one-part-basic' => [[ + 1 => $this->getChars(self::GSM0338_BASIC, 160) + ], 160], + 'two-parts-basic' => [[ + 1 => $this->getChars(self::GSM0338_BASIC, 153), + 2 => $this->getChars(self::GSM0338_BASIC, 153) + ], 306], + + // 7-bit extended + 'one-part-extended' => [[ + 1 => $this->getChars(self::GSM0338_EXTENDED, 80) + ], 160], + 'two-part-extended' => [[ + 1 => $this->getChars(self::GSM0338_BASIC, 153), + 2 => $this->getChars(self::GSM0338_EXTENDED, 76) + ], 305], + + // ucs-2 + 'one-part-ucs' => [[ + 1 => $this->getChars('simple msg plus •', 70) + ], 70], + 'two-part-ucs' => [[ + 1 => $this->getChars('simple msg plus •', 67), + 2 => $this->getChars('simple msg plus •', 67), + ], 134], + 'one-part-ucs-double' => [[ + 1 => $this->getChars('simple msg plus ', 68) . "\xf0\x9f\x93\xb1", + ], 70], + 'two-part-ucs-double' => [[ + 1 => $this->getChars('simple msg plus ', 65) . "\xf0\x9f\x93\xb1", + 2 => $this->getChars('simple msg plus ', 65) . "\xf0\x9f\x93\xb1", + ], 134], + ]; + } + /** * @dataProvider providerTooLarge * @medium Expect tests to take >1 but <10 @@ -133,4 +185,17 @@ public function providerTooLarge(): array 'ucs-2' => [str_repeat("simple msg plus \xf0\x9f\x93\xb1", 950), 'ucs-2', 17100, 256, 17152], ]; } + + /** + * Trims or extends the character set to the exact length + */ + private function getChars(string $charSet, int $size): string + { + $chars = $charSet; + while (mb_strlen($chars, 'UTF-8') < $size) { + $chars .= $charSet; + } + + return mb_substr($chars, 0, $size); + } }