symfony/vonage-notifier
Symfony Notifier bridge for Vonage, enabling SMS notifications via a simple DSN configuration. Set VONAGE_DSN with your Vonage key/secret and sender (“from”) to route notifications through Vonage.
Install the package:
composer require symfony/vonage-notifier
Configure DSN in .env:
VONAGE_DSN=vonage://KEY:SECRET@default?from=FROM
Replace KEY, SECRET, and FROM with your Vonage credentials and sender ID.
First use case: Send an SMS
use Symfony\Component\Notifier\Notifier;
use Symfony\Component\Notifier\Message\SmsMessage;
$notifier = new Notifier([], [new VonageTransport($dsn)]);
$notifier->send(new SmsMessage('Hello, world!', 'TO_NUMBER'));
Verify in Vonage dashboard or check logs for delivery status.
SmsMessage, VoiceMessage, and EmailMessage (via Vonage’s unified API).Notifier or Messenger for async dispatch.// In a Laravel controller or Symfony command
$notifier->send(new SmsMessage(
'Your OTP is: ' . $otp,
$user->phone
));
$notifier = new Notifier([
new VonageTransport($dsn, 'sms'), // Primary
new VonageTransport($dsn, 'voice'), // Fallback
]);
Messenger with delay stamps.$message = new SmsMessage('Reminder: Meeting at 3 PM', 'TO_NUMBER');
$message->delay(3600); // Send in 1 hour
$bus->dispatch($message);
hash_equals() (post-v8.1.0-BETA2).use Symfony\Component\Notifier\Bridge\Vonage\Webhook\VonageWebhookValidator;
$validator = new VonageWebhookValidator('YOUR_SECRET');
if ($validator->isValidSignature(
$request->getContent(),
$request->headers->get('X-Vonage-Signature')
)) {
// Process webhook (e.g., delivery receipt)
}
Laravel Adaptation:
Create a custom notification channel by extending VonageChannel and integrating with Laravel’s Notification facade.
use Symfony\Component\Notifier\Bridge\Vonage\VonageTransport;
class VonageChannel extends Channel
{
public function __construct(VonageTransport $transport)
{
$this->transport = $transport;
}
}
Error Handling: Wrap notifications in try-catch blocks and log failures:
try {
$notifier->send($message);
} catch (\Exception $e) {
\Log::error('Vonage notification failed: ' . $e->getMessage());
}
Testing:
Use Symfony’s TransportFactoryTestCase (deprecated in v7.2+) or mock the VonageTransport:
$transport = $this->createMock(VonageTransport::class);
$transport->method('send')->willReturn(true);
$notifier = new Notifier([], [$transport]);
Webhook Signature Validation (Critical Fix in v8.1.0-BETA2)
strcmp() or === for signature validation is vulnerable to timing attacks.hash_equals():
// Old (vulnerable)
if (strcmp($expected, $received) === 0) { ... }
// New (secure)
if (hash_equals($expected, $received)) { ... }
/webhooks/vonage).DSN Configuration Quirks
from parameter in DSN causes InvalidArgumentException.?from=YOUR_SENDER_ID:
VONAGE_DSN=vonage://KEY:SECRET@default?from=YourApp
Rate Limiting
$retryDelay = 1000; // ms
while ($attempts < 3) {
try {
$notifier->send($message);
break;
} catch (RateLimitException $e) {
usleep($retryDelay);
$retryDelay *= 2;
$attempts++;
}
}
Phone Number Formatting
+12125551234). Invalid formats cause InvalidPhoneNumberException.$phone = preg_replace('/[^0-9]/', '', $user->phone);
if (strlen($phone) < 10) {
$phone = '+1' . $phone; // Example for US numbers
}
Async Processing Gaps
Notifier is synchronous by default. Async dispatch requires Messenger.Messenger with a queue transport:
$bus = $container->get('messenger');
$bus->dispatch($message); // Async
Enable Vonage Debug Mode:
Set VONAGE_DEBUG=1 in .env to log API requests/responses.
VONAGE_DSN=vonage://KEY:SECRET@default?from=YourApp&debug=1
Check Vonage Dashboard: Monitor Vonage Messages API for delivery stats, errors, or throttling.
Log Failed Notifications:
Extend VonageTransport to log exceptions:
class LoggingVonageTransport extends VonageTransport
{
public function send(MessageInterface $message): void
{
try {
parent::send($message);
} catch (\Exception $e) {
\Log::error('Vonage send failed', [
'message' => $message->getContent(),
'to' => $message->getRecipients(),
'error' => $e->getMessage(),
]);
throw $e;
}
}
}
Custom Message Templates
VonageTransport to preprocess messages:
class CustomVonageTransport extends VonageTransport
{
public function send(MessageInterface $message): void
{
$content = $this->applyTemplate($message->getContent());
$message = new SmsMessage($content, $message->getRecipients());
parent::send($message);
}
private function applyTemplate(string $content): string
{
return str_replace('{app}', 'MyApp', $content);
}
}
Webhook Extensions
VonageWebhookValidator for custom signature logic:
class CustomValidator extends VonageWebhookValidator
{
protected function generateSignature(array $data): string
{
// Custom signature generation
return hash_hmac('sha256', $data['payload'], $this->secret);
}
}
Fallback Transports
$notifier = new Notifier([
new VonageTransport($dsn), // Primary
new EmailTransport(new GmailTransport($gmailDsn)), // Fallback
]);
NOTIFIER_VONAGE_DSN for Symfony’s Notifier component:
NOTIFIER_VONAGE_DSN=vonage://KEY:SECRET@default?from=YourApp
How can I help you explore Laravel packages today?