70 lines
2.2 KiB
PHP
70 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Sms;
|
|
|
|
class SmsMessageLength
|
|
{
|
|
private const GSM_BASIC = "@\u{00A3}$\u{00A5}\u{00E8}\u{00E9}\u{00F9}\u{00EC}\u{00F2}\u{00C7}\n\u{00D8}\u{00F8}\r\u{00C5}\u{00E5}\u{0394}_\u{03A6}\u{0393}\u{039B}\u{03A9}\u{03A0}\u{03A8}\u{03A3}\u{0398}\u{039E}\u{00C6}\u{00E6}\u{00DF}\u{00C9} !\"#\u{00A4}%&'()*+,-./0123456789:;<=>?\u{00A1}ABCDEFGHIJKLMNOPQRSTUVWXYZ\u{00C4}\u{00D6}\u{00D1}\u{00DC}\u{00A7}\u{00BF}abcdefghijklmnopqrstuvwxyz\u{00E4}\u{00F6}\u{00F1}\u{00FC}\u{00E0}";
|
|
|
|
private const GSM_EXTENDED = "^{}\\[~]|\u{20AC}";
|
|
|
|
public function fitsInOneSms(string $message): bool
|
|
{
|
|
return $this->units($message) <= $this->singleSmsCapacity($message);
|
|
}
|
|
|
|
public function units(string $message): int
|
|
{
|
|
if (! $this->usesGsmAlphabet($message)) {
|
|
return intdiv(strlen(mb_convert_encoding($message, 'UTF-16BE', 'UTF-8')), 2);
|
|
}
|
|
|
|
return array_sum(array_map(
|
|
fn (string $character): int => str_contains(self::GSM_EXTENDED, $character) ? 2 : 1,
|
|
mb_str_split($message),
|
|
));
|
|
}
|
|
|
|
public function singleSmsCapacity(string $message): int
|
|
{
|
|
return $this->usesGsmAlphabet($message) ? 160 : 70;
|
|
}
|
|
|
|
/**
|
|
* Truncate a value so that prefix + value + suffix still fits in one SMS.
|
|
*/
|
|
public function truncateBetween(string $prefix, string $value, string $suffix, string $marker = '[...]'): string
|
|
{
|
|
if ($this->fitsInOneSms($prefix.$value.$suffix)) {
|
|
return $value;
|
|
}
|
|
|
|
$truncated = '';
|
|
|
|
foreach (mb_str_split($value) as $character) {
|
|
if (! $this->fitsInOneSms($prefix.$truncated.$character.$marker.$suffix)) {
|
|
break;
|
|
}
|
|
|
|
$truncated .= $character;
|
|
}
|
|
|
|
if ($this->fitsInOneSms($prefix.$truncated.$marker.$suffix)) {
|
|
return rtrim($truncated).$marker;
|
|
}
|
|
|
|
return rtrim($truncated);
|
|
}
|
|
|
|
private function usesGsmAlphabet(string $message): bool
|
|
{
|
|
foreach (mb_str_split($message) as $character) {
|
|
if (! str_contains(self::GSM_BASIC, $character) && ! str_contains(self::GSM_EXTENDED, $character)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|