Skip to content

Commit cf4def5

Browse files
committed
[domain] outgoing MMS support
1 parent 2aa9667 commit cf4def5

8 files changed

Lines changed: 368 additions & 13 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66

77
## [Unreleased]
88

9+
### New Features
10+
11+
#### Outgoing MMS
12+
13+
- **Outgoing MMS support** — pass an `MmsMessage` (instead of a text string) to `Message` or `MessageBuilder` to send an MMS with an optional subject, body, and attachments:
14+
```php
15+
$mms = new MmsMessage('Subject', 'Body', [
16+
new MmsAttachment('image/png', base64_encode($bytes), 'photo.png'),
17+
]);
18+
$message = (new MessageBuilder($mms, ['+15550100']))->build();
19+
```
20+
Attachments carry a MIME type, base64-encoded data, and an optional filename; null subject, text, and filename are omitted from the request body. MMS fields are encrypted when an `Encryptor` is set, and an MMS message is mutually exclusive with an SMS text body on the wire.
21+
22+
## [2.6.1] - 2026-09-23
23+
924
### Bug Fixes
1025

1126
- **Message priority always serialized** — a message's `priority` is now always included in the JSON body (defaulting to `0` when unset), matching the wire format of the other official SDKs.

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ A modern PHP client for the [SMSGate](https://sms-gate.app) API: send SMS messag
1111

1212
## 📖 About
1313

14-
`capcom6/android-sms-gateway` is a type-safe PHP library for the SMSGate 3rd-party API. It covers messages (send, state, listing, cancellation), inbox refresh with individual or batch webhook delivery and attachment download, devices, webhooks, settings, logs, health checks, and the JWT token lifecycle, with a fluent `MessageBuilder` for message construction and an optional `Encryptor` for end-to-end encryption. Works with any PSR-18 HTTP client (Guzzle, curl, or others) and PHP 7.4+.
14+
`capcom6/android-sms-gateway` is a type-safe PHP library for the SMSGate 3rd-party API. It covers messages (send SMS and MMS, state, listing, cancellation), inbox refresh with individual or batch webhook delivery and attachment download, devices, webhooks, settings, logs, health checks, and the JWT token lifecycle, with a fluent `MessageBuilder` for message construction and an optional `Encryptor` for end-to-end encryption. Works with any PSR-18 HTTP client (Guzzle, curl, or others) and PHP 7.4+.
1515

1616
## 📚 Table of Contents
1717

@@ -33,7 +33,7 @@ A modern PHP client for the [SMSGate](https://sms-gate.app) API: send SMS messag
3333
## ⭐ Features
3434

3535
- Fluent `MessageBuilder` for messages and `SettingsBuilder` for settings
36-
- Messages: send, state, listing, and cancellation
36+
- Messages: send SMS text or MMS (subject, body, attachments), state, listing, and cancellation
3737
- PSR-18 HTTP client and PSR-17 factories, auto-discovered
3838
- Basic and JWT authentication with token generation and revocation
3939
- Inbox refresh with individual or batch webhook delivery and MMS attachment download

‎src/Domain/Message.php‎

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@ class Message implements SerializableInterface {
1313
* Message ID, will be generated automatically if not set
1414
*/
1515
private ?string $id;
16+
1617
/**
1718
* Message text
1819
* Long message will be divided into parts
1920
*/
2021
private string $message;
22+
/**
23+
* MMS message content (mutually exclusive with text/data messages)
24+
* @var MmsMessage|null
25+
*/
26+
private ?MmsMessage $mmsMessage;
27+
2128
/**
2229
* Time to live in seconds
2330
* If message is not received by device in this time, it will be failed
@@ -50,10 +57,11 @@ class Message implements SerializableInterface {
5057
private ?string $validUntil;
5158

5259
/**
60+
* @param mixed $message string or MmsMessage
5361
* @param array<string> $phoneNumbers
5462
*/
5563
public function __construct(
56-
string $message,
64+
$message,
5765
array $phoneNumbers,
5866
?string $id = null,
5967
?int $ttl = null,
@@ -62,19 +70,43 @@ public function __construct(
6270
?int $priority = null,
6371
?string $validUntil = null
6472
) {
73+
if (is_scalar($message)) {
74+
$message = (string) $message;
75+
}
76+
77+
if (!is_string($message) && !($message instanceof MmsMessage)) {
78+
throw new \InvalidArgumentException('message must be a string or MmsMessage');
79+
}
80+
6581
if ($ttl !== null && $validUntil !== null) {
6682
throw new \InvalidArgumentException('validUntil and ttl cannot be set at the same time');
6783
}
6884

6985
$this->id = $id;
70-
$this->message = $message;
7186
$this->ttl = $ttl;
7287
$this->simNumber = $simNumber;
7388
$this->withDeliveryReport = $withDeliveryReport;
7489
$this->phoneNumbers = $phoneNumbers;
7590
$this->isEncrypted = false;
7691
$this->priority = $priority;
7792
$this->validUntil = $validUntil;
93+
94+
if ($message instanceof MmsMessage) {
95+
$this->mmsMessage = $message;
96+
$this->message = '';
97+
} else {
98+
$this->message = $message;
99+
$this->mmsMessage = null;
100+
}
101+
}
102+
103+
104+
/**
105+
* Get MMS message content (null if not set)
106+
* @return MmsMessage|null
107+
*/
108+
public function MmsMessage(): ?MmsMessage {
109+
return $this->mmsMessage;
78110
}
79111

80112
public function Encrypt(Encryptor $encryptor): self {
@@ -88,19 +120,46 @@ public function Encrypt(Encryptor $encryptor): self {
88120
fn(string $phoneNumber) => $encryptor->Encrypt($phoneNumber),
89121
$this->phoneNumbers
90122
);
123+
124+
if ($this->mmsMessage !== null) {
125+
$this->mmsMessage = new MmsMessage(
126+
$this->mmsMessage->Subject() !== null
127+
? $encryptor->Encrypt($this->mmsMessage->Subject())
128+
: null,
129+
$this->mmsMessage->Text() !== null
130+
? $encryptor->Encrypt($this->mmsMessage->Text())
131+
: null,
132+
array_map(
133+
fn(MmsAttachment $attachment) => new MmsAttachment(
134+
$attachment->ContentType(),
135+
$encryptor->Encrypt($attachment->Data()),
136+
$attachment->Name() !== null
137+
? $encryptor->Encrypt($attachment->Name())
138+
: null
139+
),
140+
$this->mmsMessage->Attachments()
141+
)
142+
);
143+
}
144+
91145
return $this;
92146
}
93147

94148
public function ToObject(): object {
95149
$obj = (object) [
96150
'id' => $this->id,
97-
'message' => $this->message,
98151
'simNumber' => $this->simNumber,
99152
'withDeliveryReport' => $this->withDeliveryReport,
100153
'isEncrypted' => $this->isEncrypted,
101154
'phoneNumbers' => $this->phoneNumbers,
102155
];
103156

157+
if ($this->mmsMessage !== null) {
158+
$obj->mmsMessage = $this->mmsMessage->ToObject();
159+
} else {
160+
$obj->message = $this->message;
161+
}
162+
104163
if ($this->priority !== null) {
105164
$obj->priority = $this->priority;
106165
}

‎src/Domain/MessageBuilder.php‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ class MessageBuilder {
1414
private ?string $id = null;
1515

1616
/**
17-
* @var string
17+
* @var string|MmsMessage
1818
*/
19-
private string $message;
19+
private $message;
2020

2121
/**
2222
* @var int|null
@@ -50,10 +50,10 @@ class MessageBuilder {
5050
private ?string $validUntil = null;
5151

5252
/**
53-
* @param string $message
53+
* @param string|MmsMessage $message
5454
* @param array<string> $phoneNumbers
5555
*/
56-
public function __construct(string $message, array $phoneNumbers) {
56+
public function __construct($message, array $phoneNumbers) {
5757
$this->message = $message;
5858
$this->phoneNumbers = $phoneNumbers;
5959
}
@@ -72,10 +72,10 @@ public function setId(?string $id): self {
7272
/**
7373
* Set message text
7474
*
75-
* @param string $message
75+
* @param string|MmsMessage $message
7676
* @return $this
7777
*/
78-
public function setMessage(string $message): self {
78+
public function setMessage($message): self {
7979
$this->message = $message;
8080
return $this;
8181
}

‎src/Domain/MmsAttachment.php‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace AndroidSmsGateway\Domain;
4+
5+
/**
6+
* A single attachment of an MMS message.
7+
*/
8+
final class MmsAttachment {
9+
/**
10+
* MIME type of the attachment (e.g. image/png)
11+
* @var string
12+
*/
13+
private string $contentType;
14+
15+
/**
16+
* Base64-encoded attachment content
17+
* @var string
18+
*/
19+
private string $data;
20+
21+
/**
22+
* Optional file name of the attachment
23+
* @var string|null
24+
*/
25+
private ?string $name;
26+
27+
/**
28+
* @param string $contentType MIME type of the attachment (e.g. image/png)
29+
* @param string $data Base64-encoded attachment content
30+
* @param string|null $name Optional file name of the attachment
31+
*/
32+
public function __construct(string $contentType, string $data, ?string $name = null) {
33+
$this->contentType = $contentType;
34+
$this->data = $data;
35+
$this->name = $name;
36+
}
37+
38+
public function ContentType(): string {
39+
return $this->contentType;
40+
}
41+
42+
public function Data(): string {
43+
return $this->data;
44+
}
45+
46+
public function Name(): ?string {
47+
return $this->name;
48+
}
49+
50+
public function ToObject(): \stdClass {
51+
$obj = (object) [
52+
'contentType' => $this->contentType,
53+
'name' => $this->name,
54+
'data' => $this->data,
55+
];
56+
57+
if ($this->name === null) {
58+
unset($obj->name);
59+
}
60+
61+
return $obj;
62+
}
63+
}

‎src/Domain/MmsMessage.php‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace AndroidSmsGateway\Domain;
4+
5+
/**
6+
* An MMS message with optional subject, text and attachments.
7+
*/
8+
final class MmsMessage {
9+
/**
10+
* Optional subject of the MMS
11+
* @var string|null
12+
*/
13+
private ?string $subject;
14+
15+
/**
16+
* Optional text body of the MMS
17+
* @var string|null
18+
*/
19+
private ?string $text;
20+
21+
/**
22+
* List of attachments. Omitted entirely from the wire when empty.
23+
* @var array<MmsAttachment>
24+
*/
25+
private array $attachments;
26+
27+
/**
28+
* @param string|null $subject Optional subject of the MMS
29+
* @param string|null $text Optional text body of the MMS
30+
* @param array<MmsAttachment> $attachments List of attachments
31+
*/
32+
public function __construct(?string $subject = null, ?string $text = null, array $attachments = []) {
33+
$this->subject = $subject;
34+
$this->text = $text;
35+
$this->attachments = array_values($attachments);
36+
}
37+
38+
public function Subject(): ?string {
39+
return $this->subject;
40+
}
41+
42+
public function Text(): ?string {
43+
return $this->text;
44+
}
45+
46+
/**
47+
* @return array<MmsAttachment>
48+
*/
49+
public function Attachments(): array {
50+
return $this->attachments;
51+
}
52+
53+
public function ToObject(): \stdClass {
54+
$obj = (object) [
55+
'subject' => $this->subject,
56+
'text' => $this->text,
57+
'attachments' => array_map(
58+
static fn(MmsAttachment $attachment) => $attachment->ToObject(),
59+
$this->attachments
60+
),
61+
];
62+
63+
if ($this->subject === null) {
64+
unset($obj->subject);
65+
}
66+
67+
if ($this->text === null) {
68+
unset($obj->text);
69+
}
70+
71+
if (count($this->attachments) === 0) {
72+
unset($obj->attachments);
73+
}
74+
75+
return $obj;
76+
}
77+
}

‎tests/ClientTest.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public function testServer(): void {
113113
);
114114
$client = new Client($login, $password, Client::DEFAULT_URL, $curlClient);
115115

116-
$message = new Message(date('Y-m-d H:i:s'), [$phoneNumber]);
116+
$message = new Message('Test message from `client-php`' . PHP_EOL . date('Y-m-d H:i:s'), [$phoneNumber]);
117117

118118
$messageState = $client->SendMessage($message);
119119
$this->assertInstanceOf(MessageState::class, $messageState);

0 commit comments

Comments
 (0)