Ratchet
Ratchet copied to clipboard
Example Needed (Async MySQL + Ratchet)
Hi. I need some examples of using Async MySQL with an IoServer. I'll be using https://github.com/friends-of-reactphp/mysql most likely, but I can't get it to work correctly with Ratchet.
My Scenario: I need to authenticate connections using tokens that are stored on a database. The user connects with a token. Then once connected, the server authenticates and allows the connection to continue.
I'm entirely new to Ratchet and using composer also, so i'm clearly a little lost. I've spent the last day trying to figure it out but to no avail.
Below is my working code (Non-Async) server.php:
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;
require dirname(__DIR__) . '/vendor/autoload.php';
$server = IoServer::factory(
new HttpServer(
new WsServer(
new Chat()
)
),
8080
);
$server->run();
Chat.php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use mysqli;
class Chat implements MessageComponentInterface {
protected $clients;
protected $publicKey;
protected $privateKey;
protected $db;
public function __construct() {
$this->publicKey = openssl_pkey_get_public(file_get_contents(dirname(__DIR__).'/certificates/webSocket/webSocket.pem'));
$this->privateKey = openssl_pkey_get_private(file_get_contents(dirname(__DIR__).'/certificates/webSocket/webSocket.pem'));
$this->clients = new \SplObjectStorage;
echo "Server Started.";
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})\n";
$authFail = false;
$this->db = new mysqli("localhost","root","","lewisjones");
if($this->db){
$querystring = $conn->httpRequest->getUri()->getQuery();
parse_str($querystring,$queryarray);
if(!isset($queryarray['token'])){
$authFail = true;
} else {
$getSession = $this->query("select token from wsSessions where userID = ? limit 1",array(1));
if(!is_array($getSession)){
$authFail = true;
} else {
$verify = openssl_verify($getSession[0]["token"], base64_decode(base64_decode($queryarray['token'])), $this->publicKey, OPENSSL_ALGO_SHA256);
if($verify != 1) {
$authFail = true;
}
}
$this->query("delete from wsSessions where userID = ?",array(1));
}
$this->db->close();
} else {
$authFail = true;
}
if($authFail){
foreach ($this->clients as $client) {
if ($conn->resourceId !== $client) {
$client->send("Authentication failed! Connection Closed.");
$conn->close();
}
}
}
}
public function onMessage(ConnectionInterface $from, $msg) {
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
, $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
foreach ($this->clients as $client) {
if ($from !== $client) {
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
private function query($sql,$params = null){
$types = null;
if(is_array($params)){
$types = "";
foreach($params as $key => $value){
if(is_int($value)){
$types .= "i";
} else {
if(is_float($value)){
$types .= "d";
} else {
$types .= "s";
}
}
}
}
if($stmt = $this->db->prepare($sql)){
if($types && $params){
$bind_names[] = $types;
for ($i=0; $i<count($params);$i++){
$bind_name = 'bind' . $i;
$$bind_name = $params[$i];
$bind_names[] = &$$bind_name;
}
call_user_func_array(array($stmt,'bind_param'),$bind_names);
}
$stmt->execute();
$result = $stmt->get_result();
if (!empty($result) && $result->num_rows > 0) {
$returnArray = array();
while ($row = $result->fetch_assoc()) {
$returnArray[] = $row;
}
return $returnArray;
}
if($stmt){
return true;
}
return false;
$stmt->free_result();
$stmt->close();
}
return false;
}
}
If anyone can point me in the right direction, that would be appreciated. Thanks.
OK. Update, I've managed to get it working but i've hit another wall.
Due to the nature of it being asynchronous, I cannot seem to manage variables correctly (Which I need in order to close a connection if the auth fails)
This is what I have so far:
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})\n";
$connection = $this->factory->createLazyConnection($this->uri);
$connection->query("select token from wsSessions where userID = ? limit 1",[1])->then(function(ConnectionInterface $conn,QueryResult $command) {
//logic here, if not authorized, close connection
$conn->close();
});
$connection->quit();
}
The problem I have, is that I cannot close the connection. $conn doesn't seem to parse correctly. And I can't update global variables and do the logic outside of the query function because it's asynchronous.
Any advice?
Hi,
I think you just need to add use($conn)
to use it in your anonymous function scope.
Like this
$connection->query("select token from wsSessions where userID = ? limit 1", [1])
->then(function(ConnectionInterface $conn,QueryResult $command) use($conn) { // <- here
//logic here, if not authorized, close connection
$conn->close();
});