如何将用户添加到websocket聊天

时间:2016-08-29 16:30:54

标签: php websocket ratchet

我已经让聊天工作了,但我已将用户的ID硬编码到Chat.php中。

我的登录设置在他们登录网站时将电子邮件发送到会话($_SESSION['email']=$email;)。

我可以在聊天表单中添加一个隐藏字段及其ID,并将其传递给Chat.php,但我认为这是更好的方式。

免责声明:我知道它编码不好,我只是想学习它,让它工作,然后更好地编码。

chat_box.php

<?php
echo '<div id="chatbox" class="nav chatbox">',
    '<div class="chatbox-1">',
    '</div>',
    '<div id="send_chat" class="send_chat">',
        '<input type="text" class="text" id="chatsay" name="chatsay" maxlength="200" autocomplete="off">',
        '<button class="submit-chatsend" id="chatsend">Send</button>',
    '</div>',
'</div>';

JS

window.connect = function () {
    window.ws = $.websocket("ws://domain.com:8080/", {
        open: function () {
        },
        close: function () {
        },
        events: {
            fetch: function (e) {
            },
            single: function (e) {
                var elem = e.data;

                if (elem.type == "text") {
                    var html = "<div class='msg' id='" + elem.id + "'><div class='name'>" + elem.name + "</div><div class='msgc'>" + elem.msg.linkify() + "<div class='posted'><time class='timeago' datetime='" + elem.posted + "'>" + elem.posted + "</time></div></div></div>";

                    if (typeof elem.append != "undefined") {
                        $(".msg:last").remove();
                    }

                    if (typeof elem.earlier_msg == "undefined") {
                        $(".chatbox .chatbox-1").append(html);
                        $.scrollToBottom();
                    }
                    else {
                        $(".chatbox .chatbox-1 #load_earlier_messages").remove();
                        $(".chatbox .chatbox-1 .msg:first").before(html);
                    }
                }
                else if (elem.type == "more_messages") {
                    $(".chatbox .chatbox-1 .msg:first").before("<a id='load_earlier_messages'>Load Earlier Messages...</a>");
                }
                $("time.timeago").timeago();
            }
        }
    });
};
$(document).ready(function () {
    connect();

    $(document).on("click", "#load_earlier_messages", function () {
        ws.send("fetch", {"id": $(".msg:first").attr("id")});
    });

    $('#chatsend').click(function () { //use clicks message send button
        var chatsay_val = $('#chatsay').val(); //get message text
        if (chatsay_val != "") {
            ws.send("send", {"type": "text", "msg": chatsay_val});
            $('#chatsay').val(''); //reset text
        }
    });
});

Chat.php

<?php
namespace MyApp;

use DateTime;
use DateTimeZone;
use Exception;
use PDO;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class Chat implements MessageComponentInterface
{
    protected $clients=array();
    private $dbh;

    /**
     * Chat constructor.
     */
    public function __construct()
    {
        global $db_host;
        global $db_username;
        global $db_password;
        global $db;
        require_once BASE_PATH.'modules'.DS.'Database'.DS.'Database.php';
        $database=new Database($db_host, $db, $db_username, $db_password);
        $this->dbh=$database;
    }

    /**
     * @param ConnectionInterface $conn
     */
    public function onOpen(ConnectionInterface $conn)
    {
        $this->clients[$conn->resourceId]=$conn;
        echo "New connection! ({$conn->resourceId})\n";
        $this->fetchMessages($conn);
    }

    /**
     * @param ConnectionInterface $conn
     * @param int /null $id
     */
    public function fetchMessages(ConnectionInterface $conn, $id=NULL)
    {
        $database=$this->dbh;

        if($id===NULL)
        {
            $database->query('SELECT * FROM `chat` ORDER BY `id` ASC', array());
            $msgs=$database->statement->fetchAll(PDO::FETCH_ASSOC);
            $msgCount=$database->count();

            if($msgCount>0)
            {
                # If more then 5 chat messages...
                if($msgCount>5)
                {
                    # Extract a slice of the array.
                    $msgs=array_slice($msgs, $msgCount-5, $msgCount);
                }

                foreach($msgs as $msg)
                {
                    $date=new DateTime($msg['posted']);
                    $date->setTimezone(new DateTimeZone(TIMEZONE));

                    $user=SearchUser($msg['user_id']);

                    $return=array(
                        "id"=>$msg['id'],
                        "name"=>$user['staffname'],
                        "type"=>$msg['type'],
                        "msg"=>$msg['msg'],
                        "posted"=>$date->format("Y-m-d H:i:sP")
                    );
                    $this->send($conn, "single", $return);
                }
                if($msgCount>5)
                {
                    $this->send($conn, "single", array(
                        "type"=>"more_messages"
                    ));
                }
            }
        }
        else
        {
            $database->query('SELECT * FROM `chat` WHERE `id` < :id ORDER BY `id` DESC LIMIT 10', array(':id'=>$id));
            $msgs=$database->statement->fetchAll(PDO::FETCH_ASSOC);
            $msgCount=$database->count();

            if($msgCount>0)
            {
                foreach($msgs as $msg)
                {
                    $date=new DateTime($msg['posted']);
                    $date->setTimezone(new DateTimeZone(TIMEZONE));

                    $user=SearchUser($msg['user_id']);

                    $return=array(
                        "id"=>$msg['id'],
                        "name"=>$user['staffname'],
                        "type"=>$msg['type'],
                        "msg"=>$msg['msg'],
                        "posted"=>$date->format("Y-m-d H:i:sP"),
                        "earlier_msg"=>TRUE
                    );
                    $this->send($conn, "single", $return);
                }

                sort($msgs);
                $firstID=$msgs[0]['id'];
                if($firstID!="1")
                {
                    $this->send($conn, "single", array(
                        "type"=>"more_messages"
                    ));
                }
            }
        }
    }

    /**
     * @param ConnectionInterface $client
     * @param $type
     * @param $data
     */
    public function send(ConnectionInterface $client, $type, $data)
    {
        $send=array(
            "type"=>$type,
            "data"=>$data
        );
        $send=json_encode($send, TRUE);
        $client->send($send);
    }

    /**
     * @param ConnectionInterface $conn
     * @param string $data
     */
    public function onMessage(ConnectionInterface $conn, $data)
    {
        $database=$this->dbh;

        $data=json_decode($data, TRUE);

        if(isset($data['data']) && count($data['data'])!=0)
        {
            $type=$data['type'];
            # How can I get the user's ID?
            $user_id=1;
            $user_name=SearchUser($user_id);

            $return=NULL;

            if($type=="send" && isset($data['data']['type']) && $user_name!=-1)
            {
                $msg=htmlspecialchars($data['data']['msg']);
                $date=new DateTime;
                $date->setTimezone(new DateTimeZone(TIMEZONE));

                if($data['data']['type']=='text')
                {
                    $database->query('SELECT `id`, `user_id`, `msg`, `type` FROM `chat` ORDER BY `id` DESC LIMIT 1', array());
                    $lastMsg=$database->statement->fetch(PDO::FETCH_OBJ);

                    if($lastMsg->user_id==$user_id && (strlen($lastMsg->msg)<=100 || strlen($lastMsg->msg)+strlen($msg)<=100))
                    {
                        # Append message.
                        $msg=$lastMsg->msg."<br/>".$msg;

                        $database->query('UPDATE `chat` SET `msg`=:msg, `posted`=NOW() WHERE `id`=:lastmsg', array(
                            ':msg'=>$msg,
                            ':lastmsg'=>$lastMsg->id
                        ));

                        $return=array(
                            "id"=>$lastMsg->id,
                            "name"=>$user_name['staffname'],
                            "type"=>"text",
                            "msg"=>$msg,
                            "posted"=>$date->format("Y-m-d H:i:sP"),
                            "append"=>TRUE
                        );
                    }
                    else
                    {
                        $database->query('INSERT INTO `chat` (`user_id`, `msg`, `type`, `posted`) VALUES (?, ?, "text", NOW())', array(
                            $user_id,
                            $msg
                        ));

                        # Get last insert ID.
                        $get_chat_id=$database->lastInsertId();
                        $return=array(
                            "id"=>$get_chat_id,
                            "name"=>$user_name['staffname'],
                            "type"=>"text",
                            "msg"=>$msg,
                            "posted"=>$date->format("Y-m-d H:i:sP")
                        );
                    }
                }

                foreach($this->clients as $client)
                {
                    $this->send($client, "single", $return);
                }
            }
            elseif($type=="fetch")
            {
                # Fetch previous messages.
                $this->fetchMessages($conn, $data['data']['id']);
            }
        }
    }

    /**
     * @param ConnectionInterface $conn
     */
    public function onClose(ConnectionInterface $conn)
    {
        # The connection is closed, remove it, as we can no longer send it messages.
        unset($this->clients[$conn->resourceId]);
        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    /**
     * @param ConnectionInterface $conn
     * @param Exception $e
     */
    public function onError(ConnectionInterface $conn, Exception $e)
    {
        echo "An error has occurred: {$e->getMessage()}\n";
        $conn->close();
    }
}

start_server.php (通过命令行启动服务器:php start_server.php)

<?php
# Need this for the database insert.
if(!defined('DOMAIN_NAME'))
{
    define('DOMAIN_NAME', 'domain.com');
}
require_once 'includes/config.php';
include_once BASE_PATH.'modules'.DS.'WS'.DS.'server.php';

server.php

<?php

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;

$ip="domain.com";
$port="8080";

# Need this for the database insert.
if(!defined('DOMAIN_NAME'))
{
    define('DOMAIN_NAME', $ip);
}

require_once '../../vendor/autoload.php';

$server = IoServer::factory(
    new HttpServer(
        new WsServer(
            new Chat()
        )
    ),
    $port,
    $ip
);

$server->run();

这是我的methodToGetSessionData()方法@Sherif建议

/**
 * @param $sessionId
 * @return mixed
 */
private function methodToGetSessionData($sessionId)
{
    if(file_exists(session_save_path().'/sess_'.$sessionId))
    {
        $file=session_save_path().'/sess_'.$sessionId;
    }
    else
    {
        $file=sys_get_temp_dir().'/sess_'.$sessionId;
    }
    $contents=file_get_contents($file);
    session_decode($contents);

    return $_SESSION;
}

1 个答案:

答案 0 :(得分:1)

只需使用您已经在做的会话。在onOpen方法中,$conn将初始HTTP请求作为GuzzleHttp对象。您可以从中提取会话cookie,并在会话中读取您的websocket服务器。

public function onOpen(ConnectionInterface $conn)
{
    // get the cookies
    $cookies = (string) $conn->WebSocket->request->getHeader('Cookie');

    // look at each cookie to find the one you expect
    $cookies = array_map('trim', explode(';', $cookies));
    $sessionId = null;

    foreach($cookies as $cookie) {
        // If the string is empty keep going
        if (!strlen($cookie)) {
            continue;
        }
        // Otherwise, let's get the cookie name and value
        list($cookieName, $cookieValue) = explode('=', $cookie, 2) + [null, null];
        // If either are empty, something went wrong, we'll fail silently here
        if (!strlen($cookieName) || !strlen($cookieValue)) {
            continue;
        }
        // If it's not the cookie we're looking for keep going
        if ($cookieName !== "PHPSESSID") {
            continue;
        }
        // If we've gotten this far we have the session id
        $sessionId = urldecode($cookieValue);
        break;
    }

    // If we got here and $sessionId is still null, then the user isn't logged in
    if (!$sessionId) {
        return $conn->close(); // close the connection - no session!
    }

    // Extract the session data using the session id from the cookie
    $conn->session = $this->methodToGetSessionData($sessionId);

    // now you have access to things in the session
    $this->clinets[] = $conn;
}

根据您使用PHP在整个站点中存储会话的方式,您需要实现methodToGetSessionData方法,以便能够从该会话存储读取并反序列化数据。从那里,您可以通过websocket服务器中的$conn->session$client->session访问会话中存储的任何内容。

通常,如果您在PHP中使用基于默认文件的存储,则只需从session.storage_path读取会话文件并对其进行反序列化即可。我认为Ratchet为Redis / Memcached之类的东西提供了一些会话组件,你可以轻松地将它们注入到websocket应用程序中。