iguazoft / yii2-sse-notification
Yii2 Server-Sent Events & Web Push Notification Extension
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:yii2-extension
pkg:composer/iguazoft/yii2-sse-notification
Requires
- php: >=8.1
- minishlink/web-push: ^9.0
- yiisoft/yii2: ~2.0.14
Requires (Dev)
- yiisoft/yii2-mongodb: ^2.1
- yiisoft/yii2-redis: ^2.0
README
Real-time notifications for Yii2 applications using Server-Sent Events (SSE) and Web Push (VAPID).
Features
- ✅ Server-Sent Events (SSE) - Real-time notifications for online users
- ✅ Web Push Notifications (VAPID) - Browser notifications for offline users
- ✅ Automatic Routing - Intelligently routes notifications via SSE or Web Push
- ✅ Multiple Notification Types - Private, authenticated broadcast, and public
- ✅ Flexible Storage - Redis and ActiveRecord drivers included
- ✅ Service Worker - Pre-configured for Web Push
- ✅ Connection Management - Automatic online/offline detection
- ✅ Modern UI - Toast notifications with customizable styling
Requirements
- PHP >= 8.1
- Yii2 >= 2.0.14
- One of:
- Redis (recommended for production)
- MongoDB
- MySQL/PostgreSQL (via ActiveRecord)
Installation
Install via Composer:
composer require iguazoft/yii2-sse-notification
Quick Start
1. Configure the Module
Add the SSE module to your config/web.php:
'modules' => [ 'sse' => [ 'class' => 'iguazoft\sse\Module', 'vapid' => [ 'subject' => 'mailto:admin@yourdomain.com', 'publicKey' => 'YOUR_VAPID_PUBLIC_KEY', 'privateKey' => 'YOUR_VAPID_PRIVATE_KEY', ], ], ],
2. Generate VAPID Keys
Generate your VAPID keys for Web Push:
# Using web-push CLI npx web-push generate-vapid-keys # Or using PHP vendor/bin/web-push generate-vapid-keys
3. Configure Storage Driver
Choose your notification storage driver in config/web.php:
Option A: Redis (Recommended)
'components' => [ 'redis' => [ 'class' => 'yii\redis\Connection', 'hostname' => 'localhost', 'port' => 6379, 'database' => 0, ], ], 'container' => [ 'definitions' => [ 'iguazoft\sse\interfaces\NotificationSourceInterface' => [ 'class' => 'iguazoft\sse\drivers\RedisDriver', ], ], ],
Option B: ActiveRecord (MySQL/PostgreSQL)
'container' => [ 'definitions' => [ 'iguazoft\sse\interfaces\NotificationSourceInterface' => [ 'class' => 'iguazoft\sse\drivers\ActiveRecordDriver', 'notificationClass' => 'app\models\Notification', ], ], ],
4. Run Database Migration
Create the required tables:
php yii migrate --migrationPath=@vendor/iguazoft/yii2-sse/src/migrations
This creates:
notification- Stores notifications (if using ActiveRecord)push_subscription- Stores Web Push subscriptionsuser_connection- Tracks online/offline status
5. Copy Service Worker
Copy the Service Worker to your web root:
cp vendor/iguazoft/yii2-sse/src/assets/sw.js web/sw.js
6. Add to Layout
Include the SSE assets in your layout (views/layouts/main.php):
<?php use iguazoft\sse\assets\SseAsset; use yii\helpers\Url; SseAsset::register($this); $sseUrl = Url::to(['/sse/stream/index']); $pushConfig = json_encode([ 'publicKey' => Yii::$app->getModule('sse')->vapid['publicKey'], 'subscriptionUrl' => Url::to(['/sse/push/subscribe']), 'serviceWorkerUrl' => '/sw.js', ]); $this->registerJs(" const sseClient = new NotificationClient('$sseUrl'); const pushClient = new PushClient($pushConfig); // Bind Subscribe Button const btnSub = document.getElementById('btn-subscribe'); if (btnSub) { btnSub.addEventListener('click', () => { pushClient.subscribe().then(() => { alert('Successfully subscribed to notifications!'); }).catch(err => { console.error('Subscription failed', err); }); }); } "); ?> <!-- Notification container --> <div id="notification-container" style="position: fixed; top: 70px; right: 20px; z-index: 9999; width: 350px;"></div>
7. Add Subscribe Button (Optional)
Add a button to allow users to subscribe to Web Push:
<button id="btn-subscribe" class="btn btn-primary">
🔔 Subscribe to Notifications
</button>
Usage
Sending Notifications
Method 1: Using NotificationDispatcher
use iguazoft\sse\components\NotificationDispatcher; use Yii; $dispatcher = Yii::createObject(NotificationDispatcher::class); // Send private notification $dispatcher->send([ 'type' => 'private', 'user_id' => 123, 'message' => [ 'title' => 'New Message', 'body' => 'You have a new message from John', 'icon' => '/images/notification-icon.png', 'url' => '/messages/view/456', ], ]);
Method 2: Using HTTP Endpoint
curl -X POST http://yourapp.com/index.php?r=sse/notification/create \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "type=private" \ -d "user_id=123" \ -d "message[title]=New Message" \ -d "message[body]=You have a new message"
Method 3: Direct Driver Access
use iguazoft\sse\interfaces\NotificationSourceInterface; use Yii; $driver = Yii::createObject(NotificationSourceInterface::class); $driver->createNotification([ 'type' => 'private', 'user_id' => 123, 'message' => [ 'title' => 'Direct Notification', 'body' => 'Sent directly via driver', ], ]);
Notification Types
Private Notification - Sent to a specific user:
[
'type' => 'private',
'user_id' => 123,
'message' => [...],
]
Authenticated Broadcast - Sent to all authenticated users:
[
'type' => 'auth_broadcast',
'message' => [...],
]
Public Broadcast - Sent to all connected users (including guests):
[
'type' => 'public',
'message' => [...],
]
Message Format
'message' => [ 'title' => 'Notification Title', // Required 'body' => 'Message content', // Required 'icon' => '/path/to/icon.png', // Optional 'badge' => '/path/to/badge.png', // Optional 'url' => '/target/page', // Optional - where to redirect on click 'data' => [ // Optional - custom data 'id' => 123, 'type' => 'message', ], ]
How It Works
Intelligent Routing
The extension automatically routes notifications based on user status:
┌─────────────────────────┐
│ Notification Created │
└───────────┬─────────────┘
│
▼
┌───────────────┐
│ Is User │
│ Online? │
└───┬───────┬───┘
│ │
YES │ │ NO
│ │
▼ ▼
┌─────┐ ┌──────────┐
│ SSE │ │ Web Push │
└─────┘ └──────────┘
│ │
▼ ▼
Toast OS Notification
SSE Flow (Online Users)
- User opens your app
- SSE connection established to
/sse/stream/index - Notification created → stored in Redis/DB
- SSE stream picks up notification
- JavaScript displays toast notification
Latency: < 2 seconds
Web Push Flow (Offline Users)
- User subscribes via browser
- Subscription saved to database
- User goes offline/closes browser
- Notification created → user detected as offline
- Push sent via VAPID to FCM/Mozilla servers
- Browser receives push event
- Service Worker displays OS notification
Latency: 2-10 seconds
Configuration Reference
Module Configuration
'modules' => [ 'sse' => [ 'class' => 'iguazoft\sse\Module', // VAPID keys for Web Push (required) 'vapid' => [ 'subject' => 'mailto:admin@example.com', 'publicKey' => 'YOUR_PUBLIC_KEY', 'privateKey' => 'YOUR_PRIVATE_KEY', ], ], ],
Driver Configuration
Redis Driver (Recommended)
'container' => [ 'definitions' => [ 'iguazoft\sse\interfaces\NotificationSourceInterface' => [ 'class' => 'iguazoft\sse\drivers\RedisDriver', 'keyPrefix' => 'sse:', // Optional, default: 'sse:' ], ], ],
ActiveRecord Driver
'container' => [ 'definitions' => [ 'iguazoft\sse\interfaces\NotificationSourceInterface' => [ 'class' => 'iguazoft\sse\drivers\ActiveRecordDriver', 'notificationClass' => 'app\models\Notification', // Your AR model ], ], ],
Advanced Usage
Custom Notification Model
Create your own ActiveRecord model:
namespace app\models; use yii\db\ActiveRecord; class Notification extends ActiveRecord { public static function tableName() { return 'notification'; } public function rules() { return [ [['type', 'message'], 'required'], [['user_id'], 'integer'], [['message'], 'safe'], ]; } }
Customizing Toast Notifications
Override the default toast styling in your CSS:
.notification-toast { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); } .notification-toast .title { font-weight: 700; font-size: 16px; } .notification-toast .body { opacity: 0.9; }
Manually Managing Connections
use iguazoft\sse\components\ConnectionManager; use Yii; $cm = Yii::createObject(ConnectionManager::class); // Mark user as online $cm->connect(123); // Check if user is online if ($cm->isOnline(123)) { echo "User is connected"; } // Mark user as offline $cm->disconnect(123);
Direct PushService Usage
use iguazoft\sse\components\PushService; use Yii; $pushService = Yii::createObject(PushService::class); // Send to specific user $result = $pushService->sendToUser(123, [ 'title' => 'Direct Push', 'body' => 'Sent directly via PushService', 'data' => ['url' => '/dashboard'], ]); print_r($result); // Output: Array ( [0] => Sent to {subscription_id}: OK )
Troubleshooting
Notifications Not Appearing
SSE (Online Users)
- Check browser console for connection errors
- Verify SSE route is accessible:
/index.php?r=sse/stream/index - Check Redis/DB for stored notifications
- Ensure
NotificationClientis initialized - Verify CORS headers if using different domains
Web Push (Offline Users)
- Check browser permissions: Settings → Notifications → Allow
- Verify VAPID keys are correct
- Ensure Service Worker is registered:
chrome://serviceworker-internals - Check subscription exists in database
- On macOS: Chrome must be open (minimized is OK)
VAPID Errors
InvalidConfigException: VAPID keys are not configured
Solution: Ensure the sse module is loaded and VAPID keys are set in config/web.php.
Service Worker Not Found
GET /sw.js 404 (Not Found)
Solution: Copy sw.js to your web root:
cp vendor/iguazoft/yii2-sse/src/assets/sw.js web/sw.js
Subscription Fails for Guest Users
{status: "error", message: "You must be logged in"}
Solution: Ensure users are authenticated before subscribing to push notifications.
Architecture
Components
| Component | Purpose |
|---|---|
NotificationDispatcher |
Routes notifications to SSE or Web Push |
PushService |
Handles Web Push via VAPID |
ConnectionManager |
Tracks user online/offline status |
StreamController |
Serves SSE stream |
NotificationController |
Provides notification creation API |
PushController |
Handles push subscriptions |
Drivers
| Driver | Storage | Production Ready |
|---|---|---|
RedisDriver |
Redis Lists | ✅ Yes (Recommended) |
ActiveRecordDriver |
MySQL/PostgreSQL | ✅ Yes |
MongoDriver |
MongoDB | ⚠️ Experimental |
Assets
| File | Purpose |
|---|---|
sse.js |
SSE client (EventSource) |
push.js |
Web Push client |
sw.js |
Service Worker for push events |
Security Considerations
- CSRF Protection: Enabled by default on all endpoints
- Authentication: Web Push subscriptions require authenticated users
- VAPID Keys: Keep private key secret, never expose in client-side code
- HTTPS Required: Web Push only works over HTTPS (except localhost)
Performance
Recommended Limits
- Concurrent SSE Connections: Up to 10,000 per server
- Notification Rate: Up to 1,000/second (Redis driver)
- TTL for Stored Notifications: 24 hours (configurable)
- Web Push Queue: Up to 100 pending per user
Optimization Tips
- Use Redis driver for production
- Implement message queue for bulk notifications
- Add CDN for static assets (sw.js, icons)
- Use Redis Pub/Sub for multi-server deployments
- Implement notification batching for high-volume scenarios
License
This extension is available under a dual license model:
Option 1: AGPL-3.0 (Free for Open Source)
Use this extension for free if your project is open source and licensed under AGPL-3.0 or a compatible license. You must:
- ✅ Release your application source code under AGPL-3.0
- ✅ Disclose all source code (including for SaaS applications)
- ✅ License derivative works under AGPL-3.0
Option 2: Commercial License (For Proprietary Use)
If your application is closed-source, commercial, or proprietary, you must purchase a Commercial License. This includes:
- ✅ No requirement to open source your code
- ✅ Use in proprietary applications
- ✅ Priority support and updates
- ✅ Legal indemnification
📧 Purchase a Commercial License: licensing@iguazoft.com
See the LICENSE file for complete details.
Support
- Issues: GitHub Issues
- Documentation: Full Documentation
- Email: dev@iguazoft.com
Credits
Developed by Iguazoft
Powered by:
- Yii2 Framework
- web-push-php by Minishlink