symfony/fake-sms-notifier
Symfony Notifier transport that fakes SMS delivery during development. Redirect SMS messages to email (with configurable to/from and optional custom mailer transport) or log them via a logger DSN, without sending real texts.
composer require symfony/notifier symfony/fake-sms-notifier
.env (choose one):
FAKE_SMS_DSN=fakesms+email://default?to=dev@example.com&from=TestSMS
FAKE_SMS_DSN=fakesms+logger://default
config/services.php:
'notifier' => [
'dsn' => env('FAKE_SMS_DSN'),
],
use Symfony\Component\Notifier\Notifier;
use Symfony\Component\Notifier\Message\SmsMessage;
$notifier = new Notifier();
$notifier->send(new SmsMessage('Hello from fake SMS!', '+1234567890'));
FAKE_SMS_DSN=fakesms+email://default?to=your-email@example.com.From: TestSMS)..env to toggle between fake and real SMS:
# .env.local (overrides .env)
FAKE_SMS_DSN=fakesms+email://default?to=dev@example.com
AppServiceProvider:
public function boot()
{
if (app()->environment('local')) {
config(['services.notifier.dsn' => 'fakesms+email://default?to=dev@example.com']);
}
}
FAKE_SMS_DSN=fakesms+logger://default
Configure Laravel’s logging in config/logging.php:
'channels' => [
'fakesms' => [
'driver' => 'single',
'path' => storage_path('logs/fake_sms.log'),
'level' => 'debug',
],
],
NotificationChannel to use Symfony Notifier:
// app/Channels/FakeSmsChannel.php
use Symfony\Component\Notifier\Notifier;
use Symfony\Component\Notifier\Message\SmsMessage;
class FakeSmsChannel
{
public function send($notifiable, Notification $notification)
{
$notifier = app(Notifier::class);
$notifier->send(new SmsMessage(
$notification->toSms($notifiable),
$notifiable->phone_number
));
}
}
// app/Notifications/SmsVerification.php
public function via($notifiable)
{
return [FakeSmsChannel::class];
}
public function test_sms_is_sent_via_fake()
{
$this->actingAs($user)
->post('/verify', ['phone' => '+1234567890']);
$this->assertEmailSent(function ($mail) {
return $mail->hasTo('dev@example.com')
&& $mail->subject === 'Fake SMS: Your code';
});
}
assertLogged for logger mode:
public function test_sms_logged_in_production()
{
config(['services.notifier.dsn' => 'fakesms+logger://default']);
$this->post('/verify', ['phone' => '+1234567890']);
$this->assertLogged('Your code: 123456');
}
Symfony Notifier Dependency:
symfony/notifier, which may conflict with existing Laravel packages (e.g., symfony/mailer).composer why-not symfony/notifier to check conflicts. Isolate dependencies in a custom package if needed.Email Sender Address:
from parameter in the DSN must be a valid email (e.g., from=test@example.com).from=+1234567890) will fail with:
[Symfony\Component\Notifier\Exception\LogicException]
The sender email address cannot be a phone number.
from.Logger Mode Visibility:
Laravel Notifications vs. Symfony Notifier:
Notification facade uses a different API than Symfony Notifier.Notification to Symfony Notifier will fail.FakeSmsChannel wrapper (see Implementation Patterns).Environment-Specific Behavior:
.env.local can lead to real SMS being sent in dev.AppServiceProvider:
if (app()->environment('local') && !str_starts_with(config('services.notifier.dsn'), 'fakesms')) {
throw new \RuntimeException('Fake SMS DSN not configured for local environment!');
}
Check DSN Parsing:
php artisan config:clear after changing .env to reload the DSN.php -r "use Symfony\Component\Notifier\Bridge\FakeSms\FakeSmsTransportFactory; echo (new FakeSmsTransportFactory())->supports('fakesms+email://default?to=test@example.com');"
Inspect Fake SMS:
to and from addresses in the DSN.[NOTICE] Fake SMS sent to "+1234567890": "Your message"
Common Errors:
InvalidArgumentException: Invalid DSN format. Use fakesms+email:// or fakesms+logger://.RuntimeException: Missing to parameter in email mode. Always include ?to=....Custom Transport:
Extend FakeSmsTransport to add a new output (e.g., Slack):
// app/Transports/CustomFakeSmsTransport.php
use Symfony\Component\Notifier\Transport\FakeSmsTransport;
class CustomFakeSmsTransport extends FakeSmsTransport
{
public function __construct(string $dsn)
{
parent::__construct($dsn);
}
protected function doSend(SmsMessage $message): void
{
// Custom logic (e.g., send to Slack)
\Log::info('Slack SMS: '.$message->getPhoneNumber().' - '.$message->getContent());
}
}
Register it in config/services.php:
'notifier' => [
'transports' => [
'custom_fakesms' => \App\Transports\CustomFakeSmsTransport::class,
],
],
Dynamic Recipients:
Override the to parameter per environment:
// In AppServiceProvider
$dsn = str_replace(
'?to=dev@example.com',
'?to='.config('services.fake_sms.recipient'),
env('FAKE_SMS_DSN')
);
config(['services.notifier.dsn' => $dsn]);
Testing Helpers: Create a helper to assert fake SMS:
// tests/TestHelpers/FakeSms.php
use Symfony\Component\Notifier\Notifier;
class FakeSms
{
public static function assertSent(string $phone, string $content)
{
$notifier = app
How can I help you explore Laravel packages today?