Php 按附加信息查找SplObjectStorage中的对象

Php 按附加信息查找SplObjectStorage中的对象,php,Php,我使用PHP Ratchet构建了一个聊天应用程序 我将所有连接存储在SplObjectStorage中 每个连接都将具有用户id,我将通过以下方式将其附加: public function __construct() { $this->clients = new \SplObjectStorage; } public function onOpen(ConnectionInterface $conn) { // Store t

我使用PHP Ratchet构建了一个聊天应用程序

我将所有连接存储在SplObjectStorage中

每个连接都将具有用户id,我将通过以下方式将其附加:

   public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

   public function onOpen(ConnectionInterface $conn)
    {
        // Store the new connection to send messages to later
        $querystring = $conn->WebSocket->request->getQuery();

        foreach ($querystring as $value)
        {
            if($key == "senderId")
                $senderId = $value;
        } 

        $this->clients->attach($conn, $senderId);

        echo "New connection! ({$conn->resourceId}) senderId({$senderId})\n";
    }
当消息到达时,我希望以最快的方式获取与特定用户id相关的
$conn
对象。 我可以像这样使用琐碎的foreach:

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->getInfo() !== $client->getInfo()) {

                   // do stuff
                }
    }
$conn = $this->clients->getClientWithInfo("WANTED-INFO");
我想知道是否有更快的方法。也许可以使用如下功能:

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->getInfo() !== $client->getInfo()) {

                   // do stuff
                }
    }
$conn = $this->clients->getClientWithInfo("WANTED-INFO");
想要的方法是使我的所有连接上的循环无效,以便向特定用户发送消息。
我希望获得与用户id关联的连接。

在我看来,只有一种解决方案可以让它正常工作,就像您预期的那样=>扩展SplObjectStorage类。但是你有两个选择

首先,您可以懒散地将getClientWithInfo方法添加到为您查找对象的类中:

class ConnectionStorageSimple extends SplObjectStorage
{
    public function getClientWithInfo($info)
    {
        $this->rewind();
        while ($this->valid()) {
            $object = $this->current(); // similar to current($s)
            $data = $this->getInfo();
            if ($info === $data) {
                $this->rewind();

                return $object;
            }
            $this->next();
        }

        return null;
    }
}

$conStorage = new ConnectionStorageSimple();

$con1 = new \stdClass();
$con1->id = 1;
$con2 = new \stdClass();
$con2->id = 2;


$conStorage->attach($con1, 1);
$conStorage->attach($con2, 2);

var_dump($conStorage->getClientWithInfo(1));

var_dump($conStorage->getClientWithInfo(2));

/**
 This will output something like that:
 class stdClass#2 (1) {
   public $id =>
   int(1)
 }
 class stdClass#3 (1) {
   public $id =>
   int(2)
 }
*/
另一个选项是,基于父函数构建一个信息对象映射。这有点复杂:

<?php

class ConnectionStorage extends SplObjectStorage
{

    private $objInfoMapping = array();

    public function attach($object, $data = null)
    {
        if (null !== $data) {
            $this->objInfoMapping[$data] = $object;
        }
        parent::attach($object, $data);
    }

    public function detach($object)
    {
        $this->detach($object);
        parent::detach($object);
    }

    public function addAll($storage)
    {
        $this->addStorage($storage);

        parent::addAll($storage);
    }

    public function removeAll($storage)
    {
        $this->objInfoMapping = array();
        parent::removeAll($storage);
    }

    public function removeAllExcept($storage)
    {
        $this->objInfoMapping = array();
        $this->addStorage($storage);
        parent::removeAllExcept($storage);
    }

    public function unserialize($serialized)
    {
        parent::unserialize($serialized);
        $this->addStorage($this);
    }

    public function offsetUnset($object)
    {
        $this->detach($object);
        parent::offsetUnset($object);
    }

    protected function detachObject($obj)
    {
        $info = $this[$obj];
        if (array_key_exists($info, $this->objInfoMapping)) {
            unset($this->objInfoMapping[$info]);
        }
    }

    protected function addStorage(SplObjectStorage $storage)
    {
        $storage->rewind();
        while ($storage->valid()) {
            $object = $storage->current(); // similar to current($s)
            $data = $storage->getInfo();
            $this->objInfoMapping[$data] = $object;
            $storage->next();
        }
    }

    public function getClientWithInfo($info)
    {
        if (array_key_exists($info, $this->objInfoMapping)) {
            return $this->objInfoMapping[$info];
        }
    }

}

$conStorage = new ConnectionStorage();

$con1 = new \stdClass();
$con1->id = 1;
$con2 = new \stdClass();
$con2->id = 2;


$conStorage->attach($con1, 1);
$conStorage->attach($con2, 2);

var_dump($conStorage->getClientWithInfo(1));

var_dump($conStorage->getClientWithInfo(2));

/**
 This will also output something like that:
 class stdClass#2 (1) {
   public $id =>
   int(1)
 }
 class stdClass#3 (1) {
   public $id =>
   int(2)
 }
*/

我就是这么做的,请注意它更简单、更快速

namespace mine;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class ws implements MessageComponentInterface {
    protected $clients;
    protected $clientids;

    public function __construct() {
        $this->clients = new \SplObjectStorage; 
        $this->clientids = array();
    }

    public function multicast($msg) {
        foreach ($this->clients as $client) $client->send($msg);
    }

    public function send_to($to,$msg) {
        if (array_key_exists($to, $this->clientids)) $this->clientids[$to]->send($msg);
    }

    public function onOpen(ConnectionInterface $conn) {
        $socket_name = "{$conn->resourceId}@{$conn->WebSocket->request->getHeader('X-Forwarded-For')}";
        $this->clients->attach($conn,$socket_name);
        $this->clientids[$socket_name] = $conn;
    }

    public function onMessage(ConnectionInterface $from, $msg) {

    }

    public function onClose(ConnectionInterface $conn) {
        unset($this->clientids[$this->clients[$conn]]);
        $this->clients->detach($conn);
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        $conn->close();
    }
}
这增加了两个函数,一个用于多播,另一个用于通过socket_名称向客户端发送消息,socket_名称是一个字符串ID(我选择了socket ID和ip的组合以停止可能的冲突)

因此,要发送到客户端:

$ws->send_to(socket_name,message);
显然,$ws是初始化时创建的websocket:

$ws = new mine\ws();
$ws_server = new Ratchet\Server\IoServer( new Ratchet\Http\HttpServer( new Ratchet\WebSocket\WsServer( $ws ) ), $socket );