Jump to content

Get SplObjectStorage by key


Go to solution Solved by kicken,

Recommended Posts

How can I retrieve an object stored in SplObjectStorage based on a unique key?  Is it possible to do so the way I am attempting to show in my 2 example?

<?php


class Foo
{
    private $storage;
    private $map;


    public function __construct()
    {
        $storage = new SplObjectStorage();
        $map=[];
    }


    public function add(object $obj)
    {
        $this->storage->attach($obj, []);        
    }


    public function assign1($obj,$id)
    {
        $this->storage[$obj] = $id;
    }


    public function findByID1($id)
    {
        $this->storage->rewind();
        while($this->storage->valid()) {
            if($id == $this->storage->getInfo()) {
                return $this->storage->current();
            }
            $this->storage->next();
        }
        return false;
    }


    public function assign2($obj,$id)
    {
         $this->map[$id]=spl_object_hash($obj);
    }


    public function findByID2($id)
    {
        $hash=$this->map[$id];
        return getObjectBasedOnHash($hash);
    }


}

$obj=(object)['foo' => 'bar'];
$foo=new Foo();
$foo->add($obj);

//Now this object's id is known $id=11111;

 

$foo->assign1($obj,$id); $obj1=$foo->findByID1($id); $foo->assign2($obj,$id); $obj2=$foo->findByID2($id);

 

Link to comment
https://forums.phpfreaks.com/topic/302877-get-splobjectstorage-by-key/
Share on other sites

Sorry, just realized that I did not enclose all the script in script tags...

<?php


class Foo
{
    private $storage;
    private $map;


    public function __construct()
    {
        $this->storage = new SplObjectStorage();
        $this->map=[];
    }


    public function add($obj)
    {
        $this->storage->attach($obj, []);        
    }


    public function assign1($obj,$id)
    {
        $this->storage[$obj] = $id;
    }


    public function findByID1($id)
    {
        $this->storage->rewind();
        while($this->storage->valid()) {
            if($id == $this->storage->getInfo()) {
                return $this->storage->current();
            }
            $this->storage->next();
        }
        return false;
    }


    public function assign2($obj,$id)
    {
         $this->map[$id]=spl_object_hash($obj);
    }




    public function findByID2($id)
    {
        $hash=$this->map[$id];
        //return getObjectBasedOnHash($hash); //How is this accomplished?
    }
}


$obj=(object)['foo' => 'bar'];
$foo=new Foo();
$foo->add($obj);




//Now this object's id is known $id=11111;
$id=123321;


//Option 1
$foo->assign1($obj,$id);
$obj1=$foo->findByID1($id);


//Option 2
$foo->assign2($obj,$id);
$obj2=$foo->findByID2($id);
Edited by NotionCommotion

SplObjectStorage is basically like an associative array that works with objects as keys. You are trying to use it as such, but you're also using these IDs as keys. Doing both doesn't make sense to me.

 

If the IDs are unique then why not use them as the keys? And if that then why use Foo at all and not just an array?

SplObjectStorage is basically like an associative array that works with objects as keys. You are trying to use it as such, but you're also using these IDs as keys. Doing both doesn't make sense to me.

 

If the IDs are unique then why not use them as the keys? And if that then why use Foo at all and not just an array?

 

Because I don't know the key when the objects are added to SplObjectStorage.
 
They are clients which make a connection to a server, and later communicate to the server and give the server its unique key.  Later, the server might be given a specific client's unique key and told to send something to that client.
 
I could iterate over the list of objects every time (as I showed in my option 1), but as a mater of principle, think I shouldn't.  Yes, they are like associate arrays, but my understanding is the key is not really the object but a hash of that object.  Why shouldn't I just store the hash which identifies the object?
 

Using the hash as the actual key is an implementation detail you shouldn't need to care about. If you use SplObjectStorage then just deal with the objects.

 

1. "Told to do something to that client" can't work until the client gives the key. Why does the client wait so long to give that? It should be giving that at the beginning of the process, as part of a sort of handshake protocol.

2. Since this code doesn't matter without the key being exchanged, you don't need to bother storing the client thing until that happens. Wait until you have the key and then store the object, which consequently removes the need for SplObjectStorage in the first place because you'll have the unique key (a scalar value) available.

Using the hash as the actual key is an implementation detail you shouldn't need to care about. If you use SplObjectStorage then just deal with the objects.

And then iterate over SplObjectStorage to find a specific object which has a unique property?

 

1. "Told to do something to that client" can't work until the client gives the key. Why does the client wait so long to give that? It should be giving that at the beginning of the process, as part of a sort of handshake protocol.

What handshake?

 

2. Since this code doesn't matter without the key being exchanged, you don't need to bother storing the client thing until that happens. Wait until you have the key and then store the object, which consequently removes the need for SplObjectStorage in the first place because you'll have the unique key (a scalar value) available.

Wouldn't this require passing the key on every exchange?

I would ask why the clients are the ones generating these keys rather than the server? If you're going to be using them to identify individual clients then they would need to be unique and to guarantee they are unique the server should be the one generating them and assigning them to the client. If the server generates the key then you'd have it from the beginning.

 

As for your option 2 method, why use a hash? You could just store the object into the map array.

    public function assign2($obj,$id)
    {
         $this->map[$id]=$obj;
    }

    public function findByID2($id)
    {
        return $this->map[$id];
    }
No need for SplObjectStorage or the hash for a simple id to object mapping like that.

I would ask why the clients are the ones generating these keys rather than the server? If you're going to be using them to identify individual clients then they would need to be unique and to guarantee they are unique the server should be the one generating them and assigning them to the client. If the server generates the key then you'd have it from the beginning.

 

The clients are not generating these keys, but rather have been manually assigned a key such as 3512e089-861e-427f-a4eb-c9187f03a7d4.  Care will be taken never to duplicate a key.

 

 

As for your option 2 method, why use a hash? You could just store the object into the map array.

    public function assign2($obj,$id)
    {
         $this->map[$id]=$obj;
    }

    public function findByID2($id)
    {
        return $this->map[$id];
    }
No need for SplObjectStorage or the hash for a simple id to object mapping like that.

 

 

Because I don't know the key when I first receive the object.  The object is created when a client makes a connection, and it is saved using $this->clientList->attach($client, []);.

 

Immediately after the client connects to the server, the client will send the server its GUID.  The reason I do so are twofold:

  1. For every subsequent data receipt, the client does not need to send its GUID again.
  2. More importantly, another script which only know's a given clients GUID needs to be able to send data to that client.  I am struggling on determining the best way to do this, however, it is outside of the scope of this post.

Does this make any sense?

<?php


namespace MyServer;


use React\EventLoop\Factory;
use React\Socket\Server as SocketServer;
use SplObjectStorage;
require 'JSONStream.php';


class Server
{
    private $app,       //The main application
    $url_sockets,       //Host and port for the socket
    $clientList;        //SplObjectStorage


    public function __construct($url_sockets,$app)
    {
        $this->app = $app;
        $this->url_sockets=$url_sockets;
        $this->clientList = new SplObjectStorage();
    }


    public function start() {
        $loop = Factory::create();
        $socket = new SocketServer($loop);
        $socket->on('connection', function (\React\Socket\ConnectionInterface $client){
            $client = new \Kicken\RLGL\JSONStream($client);
            $this->clientList->attach($client, []);


            $client->on('data', function($data) use ($client){
                // The server doesn't know the client's GUID until the client sends this data to the server   
                if($guid=$this->getConnectionID($client)) {
                    $this->app->process($data, $guid, $client);
                }
                elseif($guid=$this->app->getGUID($data)) {
                    $this->addConnection($client, $guid);
                }
            });


            $client->on('close', function($conn) use ($client) {
                if($this->socketClients->contains($client)) {
                    $this->socketClients->detach($client);
                }
            });


            $client->on('error', function($conn) use ($client) {
                $client->close();
            });


            echo "New connection accepted.\r\n";
        });
        
        $foo->on('connection', function ($guid, $data){
            /*
            I haven't really figured out this part.
            Another file such as index.php will receive $guid and $data via POST or GET.
            It then somehow needs to get it to this service so it could be sent to the approriate client.
            Options are:
            1) redis queue
            2) HTTP server located here and index.php sends it via cURL
            3) Use the existing Sockets server here and index.php needs to somehow need to send it
            4) ???
            */
            $client=$this->findConnectionByGuid($guid);
            $client->write($data);
        });
        
        $socket->listen($this->url_sockets['port'],$this->url_sockets['host']);


        $loop->addPeriodicTimer(60, function() {
            //Initiate communication to local SOAP servers and save data
            $this->app->comm->soapClient();
        });


        $loop->run();
    }


    private function addConnection($client, $guid)
    {
        $this->clientList[$client]['guid']=$guid;
    }
    private function getConnectionID($client)
    {
        return $this->clientList[$client]['guid'];
    }
    private function findConnectionByGuid($guid)
    {
        //There should be a better way to do this???
        $this->socketClients->rewind();
        while($this->socketClients->valid()) {
            if($guid == $this->socketClients->getInfo()) {
                return $this->socketClients->current();
            }
            $this->socketClients->next();
        }
        return false;
    }
}
  • Solution

You could still make a guid map if you wanted to after you know what the GUID is.

private function addConnection($client, $guid)
{
    $this->clientList[$client]['guid']=$guid;
    $this->guidMap[$guid] = $client;
}

private function findConnectionByGuid($guid)
{
    return isset($this->guidMap[$guid])?$this->guidMap[$guid]:null;
}

If there are not a lot of clients I'd probably just stick with the loop, but use a foreach to make it simpler.

private function findConnectionByGuid($guid)
{
    foreach ($this->socketClients as $client){
        if ($this->socketClients[$client]['guid'] == $guid){
	    return $client;
	}
    }

    return null;
}
That way things are kept simple with only one member variable tracking the list of clients.
This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.