Reference
Counting, numbers and sender IDs
Message
use Devix\Sms\Message;
$cost = Message::of('It’s ready — now');
$cost->encoding; // 'GSM_7BIT' | 'GSM_7BIT_EX' | 'UTF16'
$cost->length; // septets for GSM-7, UTF-16 code units for UCS-2
$cost->parts; // what you are billed for
$cost->remaining; // how much room is left in this part
$cost->perPart; // 160, 153, 70 or 67
$cost->offenders; // ['’' => 1, '—' => 1]
$cost->isUnicode();
$cost->ifSanitised(); // the same message, cleaned up — or null if it would not help
The numbers, and why they are those numbers
| Single | Concatenated | |
|---|---|---|
| GSM-7 | 160 | 153 |
| UCS-2 | 70 | 67 |
A multipart message spends seven bytes of each part on the header that joins them, which is six septets — so 153, not 160. Counting long messages at 160 is the commonest way to under-estimate a bill.
An emoji counts as two, because UCS-2 counts code units and anything outside the basic multilingual plane takes a surrogate pair. Thirty-five emoji is a full message.
These figures are checked against instasent/sms-counter-php — 1.5 million
downloads, and the de facto answer — on ten cases including every boundary. They
agree, and the vectors are a test here so ours cannot drift.
Gsm
use Devix\Sms\Gsm;
Gsm::fits('Plain text'); // true
Gsm::fits('رمز'); // false
Gsm::isExtended('{'); // true — two septets
[$clean, $changed] = Gsm::sanitise('It’s “here” — now…');
// "It's \"here\" - now..."
// ['’' => "'", '“' => '"', '”' => '"', '—' => '-', '…' => '...']
sanitise() returns what it changed, so you can show somebody "we replaced your
curly quotes" rather than silently altering their message. It only ever touches
characters with an unambiguous GSM-7 equivalent; Arabic is left alone, because
there is nothing honest to turn it into.
Number
use Devix\Sms\Number;
Number::e164('0300-1234567', 'PK'); // '+923001234567'
Number::e164('050 123 4567', 'AE'); // '+971501234567'
Number::e164('00923001234567'); // '+923001234567'
Number::e164('٠٥٠١٢٣٤٥٦٧', 'AE'); // '+971501234567'
Number::isE164('+923001234567'); // true
It throws rather than guessing when it cannot make sense of something, because a malformed number costs the same as a good one and delivers nothing.
SenderId
use Devix\Sms\SenderId;
SenderId::problems($sender, $country); // reason codes, or []
SenderId::explain('needs-registration'); // the sentence
empty · too-long · bad-characters · all-digits · needs-registration
A numeric sender is a phone number and none of the alphanumeric rules apply to it.
Sender
$sender->send(string $to, string $text, ?string $from = null, array $options = []);
$sender->cost(string $text): Message;
$sender->driver(): Driver;
Configuration it reads: country, from, sanitise, max_parts,
check_registration.
max_parts refuses a message longer than you are willing to pay for, and when
the length is due to Unicode it says which characters did it.
Result
$result->sent; // bool
$result->to; // the E.164 number it went to
$result->id; // the gateway's own id, when it gave one
$result->parts; // what you were billed
$result->error; // the gateway's own words, not a paraphrase
$result->raw; // everything it returned
A gateway that is down produces a failed Result, not an exception. A single
message failing should not take down a queue worker.