Installation
composer require cspoo/symfony-sms-bundle
Register the bundle in config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 2/3):
return [
// ...
cspoo\SmsBundle\cspooSmsBundle::class => ['all' => true],
];
Configure the Bundle
Add a config/packages/cspoo_sms.yaml (Symfony 4+) or app/config/config.yml (Symfony 2/3):
cspoo_sms:
default_transport: twilio # or your provider name
transports:
- { name: twilio, type: twilio, account_sid: '...', auth_token: '...' }
First Use Case Send an SMS in a controller:
use Symfony\Component\HttpFoundation\Request;
public function sendSmsAction(Request $request)
{
$smsSender = $this->get('sms');
$sms = $smsSender->createSms('+1234567890', 'Your verification code is 12345');
$smsSender->sendSms($sms);
return new Response('SMS sent!');
}
Sending SMS
$smsSender = $this->get('sms');
$sms = $smsSender->createSms($phoneNumber, $message);
$result = $smsSender->sendSms($sms); // Returns provider-specific response (e.g., message ID)
Using Different Transports
# config/packages/cspoo_sms.yaml
cspoo_sms:
transports:
- { name: twilio, type: twilio, ... }
- { name: nexmo, type: nexmo, ... }
// Send via Nexmo
$smsSender->setTransport('nexmo');
$smsSender->sendSms($sms);
Dependency Injection
Inject the SmsSender service directly into services/controllers:
use cspoo\SmsBundle\Sms\SmsSender;
public function __construct(SmsSender $smsSender) {
$this->smsSender = $smsSender;
}
Batch Sending Loop through recipients and send SMS in bulk (handle rate limits):
foreach ($recipients as $phone) {
$sms = $this->smsSender->createSms($phone, $message);
$this->smsSender->sendSms($sms);
// Add delay if needed (e.g., sleep(1))
}
Logging and Error Handling Wrap SMS sending in a try-catch block and log failures:
try {
$this->smsSender->sendSms($sms);
} catch (\Exception $e) {
$this->logger->error('SMS failed: ' . $e->getMessage());
// Retry logic or fallback (e.g., email)
}
Laravel-Specific Setup
config/services.php to bind the service:
'sms' => \cspoo\SmsBundle\Sms\SmsSender::class,
$smsSender = app('sms');
Environment Variables
Store sensitive credentials (e.g., username, password) in .env:
SMS_WINIC_USERNAME=foo
SMS_WINIC_PASSWORD=bar
Reference them in config:
transports:
- { name: winic, type: winic, username: '%env(SMS_WINIC_USERNAME)%' }
Event Listeners Trigger actions post-SMS (e.g., update user record):
$this->eventDispatcher->dispatch(new SmsSentEvent($sms));
Testing
Mock the SmsSender in PHPUnit:
$mockSmsSender = $this->createMock(SmsSender::class);
$mockSmsSender->expects($this->once())->method('sendSms')->with($sms);
$this->app->instance('sms', $mockSmsSender);
Transport Class Naming
WinicTransport, but the config key is type: winic. Ensure the class name matches the type in config (e.g., TwilioTransport for type: twilio).BaseTransport Inheritance
BaseTransport (not Transport) to avoid missing methods like getUsername() or getPassword():
class MyTransport extends \cspoo\SmsBundle\Transport\BaseTransport { ... }
Configuration Overrides
imports in config/packages/cspoo_sms.yaml:
imports:
- { resource: cspoo_sms.yaml }
Deprecated Methods
createSms() with raw strings. Use the Sms model for structured data (e.g., recipients, parts):
$sms = new \cspoo\SmsBundle\Model\Sms();
$sms->setRecipient($phone)->setMessage($message);
Rate Limiting
dispatch(new SendSmsJob($phone, $message))->delay(now()->addSeconds(5));
Enable Debug Mode
Set debug: true in config to log transport interactions:
cspoo_sms:
debug: true
Check Transport Responses
Log the return value of sendSms() to verify provider-specific responses:
$response = $smsSender->sendSms($sms);
$this->logger->debug('SMS response:', ['response' => $response]);
Validate Phone Numbers
Use a library like libphonenumber to format numbers before sending:
use giggsey\LibPhonenumber\PhoneNumberUtil;
$phoneUtil = PhoneNumberUtil::getInstance();
$phone = $phoneUtil->parse($rawPhone, 'US');
$sms->setRecipient($phoneUtil->format($phone, PhoneNumberFormat::E164));
Custom Transport Logic
Override sendSms() in your transport class to add features like:
public function sendSms(Sms $sms) {
$this->scheduleMessage($sms->getMessage(), $sms->getRecipient(), $sms->getSentAt());
return 'scheduled';
}
$content = urlencode(mb_convert_encoding($sms->getMessage(), 'UTF-8'));
Add Metadata to SMS
Extend the Sms model to include custom fields (e.g., template_id, campaign_id):
class ExtendedSms extends \cspoo\SmsBundle\Model\Sms {
private $templateId;
public function setTemplateId($id) { $this->templateId = $id; }
public function getTemplateId() { return $this->templateId; }
}
Hook into the Factory
Modify SmsFactory.php to add pre-send validation or logging:
public function sendSms(Sms $sms) {
$this->logger->info('Sending SMS to ' . $sms->getRecipient());
return $this->getTransport()->sendSms($sms);
}
Support for Multi-Part SMS Extend the bundle to handle long messages by splitting them into parts:
public function sendSms(Sms $sms) {
$parts = $this->splitMessage($sms->getMessage());
foreach ($parts as $part) {
$this->sendPart($part, $sms->getRecipient());
}
}
AppServiceProviderHow can I help you explore Laravel packages today?