Search code examples
phpmultithreadingdesign-patternspass-by-referenceratchet

Sharing an array of objects across classes in php


I have a scenario where an array of objects (Players) must be read and modified by objects of other classes. Those objects will work on the same array, that is, any change to the array is seen by all objects. Anything is likely to modify the array including threads and IO event functions.

Here are the specifics:

class Player {
    public $connection;
    public $x,$y,$velocity;

    public function __construct(ConnectionInterface $conn, $initx, $inity) 
    { /* ... */ }

    public function updateVelocity($newVelocity) { /* ... */ }
}

$players_arr = array();

class ConnectionClass extends Thread implements MessageComponentInterface {
    private $players;

    // A new player connected
    public function onOpen(ConnectionInterface $conn) { 
    // ...
        array_push($this->players, new Player($conn, x, y);
    }

    public function run() {
        while(true) {
            // ...
            foreach ($this->players as $player) 
                $player->connection->send(data);
            sleep(1);
        }
    }

}

class World extends Thread {
    private $players;

    public function run() {
        while(true) {
            // ...
            foreach ($this->players as $player) 
                $player->updateVelocity($vel);
            usleep(30000);
        }
    }

}

$players in World and in ConnectionClass should always be identical!!

The ConnectionInterface and MessageComponentInterface are part of "Ratchet" a Web-Socket library I'm using. Also, if it makes any difference, each class is in its own .php file.

How should I structure the code?

Should I make the shared array static in Player class itself? if so, how to access it from other classes?

Things I've tried:

Note: I'm aware that multi-threaded access to the array requires special measures and synchronization (Please elaborate), but the main issue is how to share the array across classes (not necessarily threads)


Solution

  • If the purpose that the World and ConnectionClass are extending the Thread class is to share same $players this won't work.

    As far as I can get your problem a new instance of ArrayObject should do the job, you just have pass it's instance to the World's and ConnectionClass's instances:

    $players = new ArrayObject();
    $connection = new ConnectionClass($players); //add it to the other arguments if any
    $world = new World($players); //add it to the other arguments if any
    
    //or you can with setter
    
    $players = new ArrayObject();
    $connection = new ConnectionClass();
    $connection->setPlayers($players);
    $world = new World($players);
    $world-> setPlayers($players);
    

    You should change the controllers of your World and ConnectionClass to initiate the private $players property with the new argument, or implement a setter.

    public function __construct($players)
    {
         $this->players = $players;
    }
    
    //or
    
    public function setPlayers($players)
    {
         $this->players = $players;
    }
    

    You should also change onOpen's implemention:

    public function onOpen(ConnectionInterface $conn) { 
    // ...
        $this->players->append(new Player($conn, x, y));
    }