Command Refactoring

Refactored Command namespace; reusing more code, standardized interfaces
WebSocket handles wrapping messages better/properly now
This commit is contained in:
Chris Boden 2011-11-08 09:32:20 -05:00
parent c45962c7b4
commit 1d14119bb5
9 changed files with 75 additions and 86 deletions

View File

@ -1,25 +1,15 @@
<?php
namespace Ratchet\Command\Action;
use Ratchet\Command\ActionInterface;
use Ratchet\SocketInterface;
use Ratchet\Command\ActionTemplate;
use Ratchet\SocketObserver;
/**
* Close the connection to the sockets passed in the constructor
*/
class CloseConnection implements ActionInterface {
/**
* @var SocketInterface
*/
protected $_socket;
public function __construct(SocketInterface $socket) {
$this->_socket = $socket;
}
class CloseConnection extends ActionTemplate {
function execute(SocketObserver $scope = null) {
$ret = $scope->onClose($this->_socket);
$this->_socket->close();
$ret = $scope->onClose($this->getSocket());
$this->getSocket()->close();
return $ret;
}

View File

@ -1,16 +1,12 @@
<?php
namespace Ratchet\Command\Action;
use Ratchet\Command\ActionInterface;
use Ratchet\SocketInterface;
use Ratchet\Command\ActionTemplate;
use Ratchet\SocketObserver;
/**
* Null pattern - execution does nothing, something needs to be passed back though
*/
class Null implements ActionInterface {
public function __construct(SocketInterface $socket) {
}
class Null extends ActionTemplate {
public function execute(SocketObserver $scope = null) {
}
}

View File

@ -1,26 +1,16 @@
<?php
namespace Ratchet\Command\Action;
use Ratchet\Command\ActionInterface;
use Ratchet\SocketInterface;
use Ratchet\Command\ActionTemplate;
use Ratchet\SocketObserver;
class Runtime implements ActionInterface {
/**
* @var SocketInterface
*/
protected $_socket;
class Runtime extends ActionTemplate {
/**
* @var Closure
*/
protected $_command = null;
public function __construct(SocketInterface $socket) {
$this->_socket = $socket;
}
/**
* Your closure should accept a single \Ratchet\Socket parameter
* Your closure should accept a single \Ratchet\SocketInterface parameter and return a CommandInterface or NULL
* @param Closure Your closure/lambda to execute when the time comes
*/
public function setCommand(\Closure $callback) {
@ -28,6 +18,6 @@ class Runtime implements ActionInterface {
}
public function execute(SocketObserver $scope = null) {
return call_user_func($this->_command, $socket);
return call_user_func($this->_command, $this->getSocket());
}
}

View File

@ -1,27 +1,17 @@
<?php
namespace Ratchet\Command\Action;
use Ratchet\Command\ActionInterface;
use Ratchet\SocketInterface;
use Ratchet\Command\ActionTemplate;
use Ratchet\SocketObserver;
/**
* Send text back to the client end of the socket(s)
*/
class SendMessage implements ActionInterface {
/**
* @var SocketInterface
*/
public $_socket;
class SendMessage extends ActionTemplate {
/**
* @var string
*/
protected $_message = '';
public function __construct(SocketInterface $socket) {
$this->_socket = $socket;
}
/**
* The message to send to the socket(s)
* @param string
@ -48,6 +38,6 @@ class SendMessage implements ActionInterface {
throw new \UnexpectedValueException("Message is empty");
}
$this->_socket->write($this->_message, strlen($this->_message));
$this->getSocket()->write($this->_message, strlen($this->_message));
}
}

View File

@ -8,4 +8,9 @@ interface ActionInterface extends CommandInterface {
* @param Ratchet\SocketInterface
*/
function __construct(SocketInterface $socket);
/**
* @return Ratchet\SocketInterface
*/
function getSocket();
}

View File

@ -0,0 +1,18 @@
<?php
namespace Ratchet\Command;
use Ratchet\SocketInterface;
abstract class ActionTemplate implements ActionInterface {
/**
* @var Ratchet\SocketInterface
*/
protected $_socket;
public function __construct(SocketInterface $socket) {
$this->_socket = $socket;
}
public function getSocket() {
return $this->_socket;
}
}

View File

@ -9,6 +9,8 @@ use Ratchet\SocketObserver;
interface CommandInterface {
/**
* The Server class will call the execution
* @param Ratchet\SocketObserver Scope to execute the command under
* @return CommandInterface|NULL
*/
function execute(SocketObserver $scope = null);
}

View File

@ -3,8 +3,13 @@ namespace Ratchet\Command;
use Ratchet\SocketObserver;
class Composite extends \SplQueue implements CommandInterface {
public function enqueue(CommandInterface $command) {
if ($command instanceof Composite) {
/**
* Add another Command to the stack
* Unlike a true composite the enqueue flattens a composite parameter into leafs
* @param CommandInterface
*/
public function enqueue(CommandInterface $command = null) {
if ($command instanceof self) {
foreach ($command as $cmd) {
$this->enqueue($cmd);
}
@ -12,7 +17,9 @@ class Composite extends \SplQueue implements CommandInterface {
return;
}
parent::enqueue($command);
if (null !== $command) {
parent::enqueue($command);
}
}
public function execute(SocketObserver $scope = null) {
@ -21,11 +28,7 @@ class Composite extends \SplQueue implements CommandInterface {
$recursive = new self;
foreach ($this as $command) {
$ret = $command->execute($scope);
if ($ret instanceof CommandInterface) {
$recursive->enqueue($ret);
}
$recursive->enqueue($command->execute($scope));
}
if (count($recursive) > 0) {

View File

@ -4,9 +4,9 @@ use Ratchet\Protocol\WebSocket\Client;
use Ratchet\Protocol\WebSocket\VersionInterface;
use Ratchet\SocketInterface;
use Ratchet\SocketObserver;
use Ratchet\Command\Factory;
use Ratchet\Command\CommandInterface;
use Ratchet\Command\Action\SendMessage;
use Ratchet\Command\Composite;
use Ratchet\Protocol\WebSocket\Util\HTTP;
/**
@ -15,8 +15,14 @@ use Ratchet\Protocol\WebSocket\Util\HTTP;
* @link http://ca.php.net/manual/en/ref.http.php
* @todo Make sure this works both ways (client/server) as stack needs to exist on client for framing
* @todo Make sure all SendMessage Commands are framed, not just ones received from onRecv
* @todo Learn about closing the socket. A message has to be sent prior to closing - does the message get sent onClose event or CloseConnection command?
*/
class WebSocket implements ProtocolInterface {
/**
* @var Ratchet\Command\Factory
*/
protected $_factory;
/**
* Lookup for connected clients
* @type SplObjectStorage
@ -40,6 +46,7 @@ class WebSocket implements ProtocolInterface {
public function __construct(SocketObserver $application) {
$this->_clients = new \SplObjectStorage;
$this->_app = $application;
$this->_factory = new Factory;
}
/**
@ -83,12 +90,8 @@ class WebSocket implements ProtocolInterface {
$header = $response;
}
$cmds = new Composite;
$mess = new SendMessage($from);
$mess->setMessage($header);
$cmds->enqueue($mess);
return $cmds;
// here, need to send headers/handshake to application, let it have the cookies, etc
return $this->_factory->newCommand('SendMessage', $from)->setMessage($header);
}
try {
@ -97,26 +100,11 @@ class WebSocket implements ProtocolInterface {
$msg = $msg['payload'];
}
} catch (\UnexpectedValueException $e) {
$cmd = new Composite;
$close = new \Ratchet\Command\Action\CloseConnection($from); // This is to change to Disconnect (proper protocol close)
$cmd->enqueue($close);
return $cmd;
return $this->_factory->newCommand('CloseConnection', $from);
}
$cmds = $this->_app->onRecv($from, $msg);
if ($cmds instanceof Composite) {
foreach ($cmds as $cmd) {
if ($cmd instanceof SendMessage) {
$sock = $cmd->_socket; // bad
$clnt = $this->_clients[$sock];
$cmd->setMessage($clnt->getVersion()->frame($cmd->getMessage()));
}
}
}
return $cmds;
return $this->prepareCommand($cmds);
}
/**
@ -135,16 +123,23 @@ class WebSocket implements ProtocolInterface {
}
/**
* @param \Ratchet\Command\CommandInterface
* @param Version\VersionInterface
* @return \Ratchet\Command\CommandInterface
* Checks if a return Command from your application is a message, if so encode it/them
* @param Ratchet\Command\CommandInterface|NULL
* @return Ratchet\Command\CommandInterface|NULL
*/
protected function prepareCommand(CommandInterface $cmd, VersionInterface $version) {
if ($cmd instanceof SendMessage) {
$cmd->setMessage($version->frame($cmd->getMessage()));
protected function prepareCommand(CommandInterface $command = null) {
if ($command instanceof SendMessage) {
$version = $this->_clients[$command->getSocket()]->getVersion();
return $command->setMessage($version->frame($command->getMessage()));
}
return $cmd;
if ($command instanceof \Traversable) {
foreach ($command as $cmd) {
$cmd = $this->prepareCommand($cmd);
}
}
return $command;
}
/**