Skip to content

Instantly share code, notes, and snippets.

@Mevrael
Created March 14, 2017 10:40
Show Gist options
  • Star 34 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save Mevrael/6855dd47d45fa34ee7161c8e0d2d0e88 to your computer and use it in GitHub Desktop.
Save Mevrael/6855dd47d45fa34ee7161c8e0d2d0e88 to your computer and use it in GitHub Desktop.
Laravel + WebSocket (Ratchet/ReactPHP) integration
<?php
// add this to web routes
Route::get('/websocket/open', 'WebSocketController@onOpen');
Route::get('/websocket/message', 'WebSocketController@onMessage');
Route::get('/websocket/close', 'WebSocketController@onClose');
Route::get('/websocket/error', 'WebSocketController@onError');
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Ratchet\WebSocket\Version\RFC6455\Connection;
class WebSocketController
{
/**
* @var Connection
*/
protected $connection = null;
/**
* @var array|mixed
*/
protected $data = [];
/**
* @var Connection
*/
protected $currentClient = null;
/**
* @var Connection[]
*/
protected $otherClients = [];
public function __construct(Request $request)
{
$this->connection = $request->get('connection');
$this->data = $request->get('data');
$this->currentClient = $request->get('current_client');
$this->otherClients = $request->get('other_clients');
}
public function sendToOthers(array $data) {
foreach ($this->otherClients as $client) {
$client->send(json_encode($data));
}
}
public function onOpen(Request $request)
{
if (!Auth::check()) {
$this->currentClient->close();
return;
}
echo 'Opened' . PHP_EOL;
echo $request->ip() . PHP_EOL;
$this->sendToOthers([
'type' => 'USER_CONNECTED',
]);
}
public function onMessage(Request $request)
{
if (!Auth::check()) {
$this->currentClient->close();
return;
}
echo 'New message' . PHP_EOL;
echo $request->ip() . PHP_EOL;
$this->sendToOthers([
'type' => 'MESSAGE_RECEIVED',
'data' => [
'user' => Auth::user()->name,
'message' => $this->data->message,
]
]);
}
public function onClose(Request $request)
{
echo 'Closed' . PHP_EOL;
echo $request->ip() . PHP_EOL;
$this->sendToOthers([
'type' => 'USER_DISCONNECTED',
]);
}
public function onError(Request $request)
{
echo 'Error' . PHP_EOL;
}
}
<?php
// in Laravel project root create also wsserver.php
// use 'php wsserver' to launch Web Socket server
// Make sure composer dependencies have been installed
require __DIR__ . '/vendor/autoload.php';
use App\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
/**
* chat.php
* Send any incoming messages to all connected clients (except sender)
*/
class WebSocketLaravelServer implements MessageComponentInterface
{
protected $clients;
public function __construct() {
echo 'Creating app...' . PHP_EOL;
$this->clients = new \SplObjectStorage;
}
protected function handleLaravelRequest(ConnectionInterface $con, $route, $data = null)
{
/**
* @var \Ratchet\WebSocket\Version\RFC6455\Connection $con
* @var \Guzzle\Http\Message\Request $wsrequest
* @var \Illuminate\Http\Response $response
*/
$params = [
'connection' => $con,
'other_clients' => [],
];
if ($data !== null) {
if (is_string($data)) {
$params = ['data' => json_decode($data)];
} else {
$params = ['data' => $data];
}
}
foreach ($this->clients as $client) {
if ($con != $client) {
$params['other_clients'][] = $client;
} else {
$params['current_client'] = $client;
}
}
$wsrequest = $con->WebSocket->request;
$app = require __DIR__.'/bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::create($route, 'GET', $params, $wsrequest->getCookies())
);
//var_dump(Auth::id());
$controllerResult = $response->getContent();
$kernel->terminate($request, $response);
return json_encode($controllerResult);
}
public function onOpen(ConnectionInterface $con)
{
$this->clients->attach($con);
$this->handleLaravelRequest($con, '/websocket/open');
}
public function onMessage(ConnectionInterface $con, $msg)
{
$this->handleLaravelRequest($con, '/websocket/message', $msg);
}
public function onClose(ConnectionInterface $con)
{
$this->handleLaravelRequest($con, '/websocket/close');
$this->clients->detach($con);
}
public function onError(ConnectionInterface $con, \Exception $e)
{
$this->handleLaravelRequest($con, '/websocket/error');
echo 'Error: ' . $e->getMessage() . PHP_EOL;
$con->close();
}
}
// Run the server application through the WebSocket protocol on port 8080
$app = new Ratchet\App('localhost', 8080);
$app->route('/echo', new WebSocketLaravelServer);
$app->run();
@grabson101
Copy link

grabson101 commented Jul 21, 2020

I've used this code and added few changes, but whatever I do, code
$response = $kernel->handle( $request );
is causing memory leak.

Yes, it causes memory leak.
Does anyone has solution?

Maybe this code will help you

<?php
use \League\OAuth2\Server\ResourceServer;
use \Laravel\Passport\TokenRepository;
use \Laravel\Passport\Guards\TokenGuard;
use \Laravel\Passport\ClientRepository;
use \Illuminate\Support\Facades\Auth;
use \Illuminate\Http\Request;

class UserGetter 
{
    public static function getUser($bearerToken) 
    {
        $tokenguard = new TokenGuard(
            \App::make(ResourceServer::class), 
            \Auth::createUserProvider('users'), 
            \App::make(TokenRepository::class), 
            \App::make(ClientRepository::class), 
            \App::make('encrypter')
        );
        $request = Request::create('/');
        $request->headers->set('Authorization', $bearerToken);
        
        return $tokenguard->user($request);
    }
}

Also you have to use garbage collector like:
gc_collect_cycles();
after all because it still takes a lot of memory

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment