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

View File

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

View File

@ -1,26 +1,16 @@
<?php <?php
namespace Ratchet\Command\Action; namespace Ratchet\Command\Action;
use Ratchet\Command\ActionInterface; use Ratchet\Command\ActionTemplate;
use Ratchet\SocketInterface;
use Ratchet\SocketObserver; use Ratchet\SocketObserver;
class Runtime implements ActionInterface { class Runtime extends ActionTemplate {
/**
* @var SocketInterface
*/
protected $_socket;
/** /**
* @var Closure * @var Closure
*/ */
protected $_command = null; 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 * @param Closure Your closure/lambda to execute when the time comes
*/ */
public function setCommand(\Closure $callback) { public function setCommand(\Closure $callback) {
@ -28,6 +18,6 @@ class Runtime implements ActionInterface {
} }
public function execute(SocketObserver $scope = null) { 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 <?php
namespace Ratchet\Command\Action; namespace Ratchet\Command\Action;
use Ratchet\Command\ActionInterface; use Ratchet\Command\ActionTemplate;
use Ratchet\SocketInterface;
use Ratchet\SocketObserver; use Ratchet\SocketObserver;
/** /**
* Send text back to the client end of the socket(s) * Send text back to the client end of the socket(s)
*/ */
class SendMessage implements ActionInterface { class SendMessage extends ActionTemplate {
/**
* @var SocketInterface
*/
public $_socket;
/** /**
* @var string * @var string
*/ */
protected $_message = ''; protected $_message = '';
public function __construct(SocketInterface $socket) {
$this->_socket = $socket;
}
/** /**
* The message to send to the socket(s) * The message to send to the socket(s)
* @param string * @param string
@ -48,6 +38,6 @@ class SendMessage implements ActionInterface {
throw new \UnexpectedValueException("Message is empty"); 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 * @param Ratchet\SocketInterface
*/ */
function __construct(SocketInterface $socket); 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 { interface CommandInterface {
/** /**
* The Server class will call the execution * The Server class will call the execution
* @param Ratchet\SocketObserver Scope to execute the command under
* @return CommandInterface|NULL
*/ */
function execute(SocketObserver $scope = null); function execute(SocketObserver $scope = null);
} }

View File

@ -3,8 +3,13 @@ namespace Ratchet\Command;
use Ratchet\SocketObserver; use Ratchet\SocketObserver;
class Composite extends \SplQueue implements CommandInterface { 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) { foreach ($command as $cmd) {
$this->enqueue($cmd); $this->enqueue($cmd);
} }
@ -12,7 +17,9 @@ class Composite extends \SplQueue implements CommandInterface {
return; return;
} }
parent::enqueue($command); if (null !== $command) {
parent::enqueue($command);
}
} }
public function execute(SocketObserver $scope = null) { public function execute(SocketObserver $scope = null) {
@ -21,11 +28,7 @@ class Composite extends \SplQueue implements CommandInterface {
$recursive = new self; $recursive = new self;
foreach ($this as $command) { foreach ($this as $command) {
$ret = $command->execute($scope); $recursive->enqueue($command->execute($scope));
if ($ret instanceof CommandInterface) {
$recursive->enqueue($ret);
}
} }
if (count($recursive) > 0) { if (count($recursive) > 0) {

View File

@ -4,9 +4,9 @@ use Ratchet\Protocol\WebSocket\Client;
use Ratchet\Protocol\WebSocket\VersionInterface; use Ratchet\Protocol\WebSocket\VersionInterface;
use Ratchet\SocketInterface; use Ratchet\SocketInterface;
use Ratchet\SocketObserver; use Ratchet\SocketObserver;
use Ratchet\Command\Factory;
use Ratchet\Command\CommandInterface; use Ratchet\Command\CommandInterface;
use Ratchet\Command\Action\SendMessage; use Ratchet\Command\Action\SendMessage;
use Ratchet\Command\Composite;
use Ratchet\Protocol\WebSocket\Util\HTTP; 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 * @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 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 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 { class WebSocket implements ProtocolInterface {
/**
* @var Ratchet\Command\Factory
*/
protected $_factory;
/** /**
* Lookup for connected clients * Lookup for connected clients
* @type SplObjectStorage * @type SplObjectStorage
@ -40,6 +46,7 @@ class WebSocket implements ProtocolInterface {
public function __construct(SocketObserver $application) { public function __construct(SocketObserver $application) {
$this->_clients = new \SplObjectStorage; $this->_clients = new \SplObjectStorage;
$this->_app = $application; $this->_app = $application;
$this->_factory = new Factory;
} }
/** /**
@ -83,12 +90,8 @@ class WebSocket implements ProtocolInterface {
$header = $response; $header = $response;
} }
$cmds = new Composite; // here, need to send headers/handshake to application, let it have the cookies, etc
$mess = new SendMessage($from); return $this->_factory->newCommand('SendMessage', $from)->setMessage($header);
$mess->setMessage($header);
$cmds->enqueue($mess);
return $cmds;
} }
try { try {
@ -97,26 +100,11 @@ class WebSocket implements ProtocolInterface {
$msg = $msg['payload']; $msg = $msg['payload'];
} }
} catch (\UnexpectedValueException $e) { } catch (\UnexpectedValueException $e) {
$cmd = new Composite; return $this->_factory->newCommand('CloseConnection', $from);
$close = new \Ratchet\Command\Action\CloseConnection($from); // This is to change to Disconnect (proper protocol close)
$cmd->enqueue($close);
return $cmd;
} }
$cmds = $this->_app->onRecv($from, $msg); $cmds = $this->_app->onRecv($from, $msg);
if ($cmds instanceof Composite) { return $this->prepareCommand($cmds);
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;
} }
/** /**
@ -135,16 +123,23 @@ class WebSocket implements ProtocolInterface {
} }
/** /**
* @param \Ratchet\Command\CommandInterface * Checks if a return Command from your application is a message, if so encode it/them
* @param Version\VersionInterface * @param Ratchet\Command\CommandInterface|NULL
* @return \Ratchet\Command\CommandInterface * @return Ratchet\Command\CommandInterface|NULL
*/ */
protected function prepareCommand(CommandInterface $cmd, VersionInterface $version) { protected function prepareCommand(CommandInterface $command = null) {
if ($cmd instanceof SendMessage) { if ($command instanceof SendMessage) {
$cmd->setMessage($version->frame($cmd->getMessage())); $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;
} }
/** /**