Refactoring

Refactored Command/Composite pattern, now as expected
Server recursively executes commands
Above changes fixed issues of server/client not being notified on forced disconnects
This commit is contained in:
Chris Boden 2011-11-07 16:02:01 -05:00
parent 66e656ec63
commit d880d29729
12 changed files with 89 additions and 35 deletions

View File

@ -1,12 +1,14 @@
<?php <?php
namespace Ratchet\Command\Action; namespace Ratchet\Command\Action;
use Ratchet\Command\CommandInterface; use Ratchet\Command\ActionInterface;
use Ratchet\SocketInterface; use Ratchet\SocketInterface;
use Ratchet\SocketObserver;
/** /**
* Close the connection to the sockets passed in the constructor * Close the connection to the sockets passed in the constructor
* @todo The server does not seem to be notified when a resource is closed by this class...
*/ */
class CloseConnection implements CommandInterface { class CloseConnection implements ActionInterface {
/** /**
* @var SocketInterface * @var SocketInterface
*/ */
@ -16,7 +18,10 @@ class CloseConnection implements CommandInterface {
$this->_socket = $socket; $this->_socket = $socket;
} }
function execute() { function execute(SocketObserver $scope = null) {
$ret = $scope->onClose($this->_socket);
$this->_socket->close(); $this->_socket->close();
return $ret;
} }
} }

View File

@ -1,15 +1,16 @@
<?php <?php
namespace Ratchet\Command\Action; namespace Ratchet\Command\Action;
use Ratchet\Command\CommandInterface; use Ratchet\Command\ActionInterface;
use Ratchet\SocketInterface; use Ratchet\SocketInterface;
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 CommandInterface { class Null implements ActionInterface {
public function __construct(SocketInterface $socket) { public function __construct(SocketInterface $socket) {
} }
public function execute() { public function execute(SocketObserver $scope = null) {
} }
} }

View File

@ -1,9 +1,10 @@
<?php <?php
namespace Ratchet\Command\Action; namespace Ratchet\Command\Action;
use Ratchet\Command\CommandInterface; use Ratchet\Command\ActionInterface;
use Ratchet\SocketInterface; use Ratchet\SocketInterface;
use Ratchet\SocketObserver;
class Runtime implements CommandInterface { class Runtime implements ActionInterface {
/** /**
* @var SocketInterface * @var SocketInterface
*/ */
@ -26,7 +27,7 @@ class Runtime implements CommandInterface {
$this->_command = $callback; $this->_command = $callback;
} }
public function execute() { public function execute(SocketObserver $scope = null) {
return call_user_func($this->_command, $socket); return call_user_func($this->_command, $socket);
} }
} }

View File

@ -1,12 +1,13 @@
<?php <?php
namespace Ratchet\Command\Action; namespace Ratchet\Command\Action;
use Ratchet\Command\CommandInterface; use Ratchet\Command\ActionInterface;
use Ratchet\SocketInterface; use Ratchet\SocketInterface;
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 CommandInterface { class SendMessage implements ActionInterface {
/** /**
* @var SocketInterface * @var SocketInterface
*/ */
@ -42,7 +43,7 @@ class SendMessage implements CommandInterface {
/** /**
* @throws \UnexpectedValueException if a message was not set with setMessage() * @throws \UnexpectedValueException if a message was not set with setMessage()
*/ */
public function execute() { public function execute(SocketObserver $scope = null) {
if (empty($this->_message)) { if (empty($this->_message)) {
throw new \UnexpectedValueException("Message is empty"); throw new \UnexpectedValueException("Message is empty");
} }

View File

@ -0,0 +1,11 @@
<?php
namespace Ratchet\Command;
use Ratchet\SocketInterface;
interface ActionInterface extends CommandInterface {
/**
* Pass the Sockets to execute the command on
* @param Ratchet\SocketInterface
*/
function __construct(SocketInterface $socket);
}

View File

@ -1,20 +1,14 @@
<?php <?php
namespace Ratchet\Command; namespace Ratchet\Command;
use Ratchet\SocketInterface; use Ratchet\SocketObserver;
/** /**
* Socket implementation of the Command Pattern * Socket implementation of the Command Pattern
* User created applications are to return a Command to the server for execution * User created applications are to return a Command to the server for execution
*/ */
interface CommandInterface { interface CommandInterface {
/**
* Pass the Sockets to execute the command on
* @param Ratchet\SocketInterface
*/
function __construct(SocketInterface $socket);
/** /**
* The Server class will call the execution * The Server class will call the execution
*/ */
function execute(); function execute(SocketObserver $scope = null);
} }

View File

@ -1,17 +1,35 @@
<?php <?php
namespace Ratchet\Command; namespace Ratchet\Command;
use Ratchet\SocketInterface; use Ratchet\SocketObserver;
class Composite extends \SplQueue { class Composite extends \SplQueue implements CommandInterface {
public function enqueue(CommandInterface $command) { public function enqueue(CommandInterface $command) {
return parent::enqueue($command); if ($command instanceof Composite) {
foreach ($command as $cmd) {
$this->enqueue($cmd);
}
return;
}
parent::enqueue($command);
} }
public function execute() { public function execute(SocketObserver $scope = null) {
$this->setIteratorMode(static::IT_MODE_DELETE); $this->setIteratorMode(static::IT_MODE_DELETE);
$recursive = new self;
foreach ($this as $command) { foreach ($this as $command) {
$command->execute(); $ret = $command->execute($scope);
if ($ret instanceof CommandInterface) {
$recursive->enqueue($ret);
}
}
if (count($recursive) > 0) {
return $recursive;
} }
} }
} }

View File

@ -98,7 +98,7 @@ class WebSocket implements ProtocolInterface {
} }
} catch (\UnexpectedValueException $e) { } catch (\UnexpectedValueException $e) {
$cmd = new Composite; $cmd = new Composite;
$close = new \Ratchet\Command\Close($from); $close = new \Ratchet\Command\Action\CloseConnection($from); // This is to change to Disconnect (proper protocol close)
$cmd->enqueue($close); $cmd->enqueue($close);
return $cmd; return $cmd;

View File

@ -0,0 +1,20 @@
<?php
namespace Ratchet\Protocol\WebSocket\Command\Action;
use Ratchet\SocketInterface;
use Ratchet\Command\Action\SendMessage;
use Ratchet\SocketObserver;
class Disconnect extends SendMessage {
protected $_code = 1000;
public function setStatusCode($code) {
$this->_code = (int)$code;
// re-do message based on code
}
public function execute(SocketObserver $scope = null) {
parent::execute();
$this->_socket->close();
}
}

View File

@ -1,12 +1,13 @@
<?php <?php
namespace Ratchet\Command; namespace Ratchet\Command;
use Ratchet\SocketInterface; use Ratchet\SocketInterface;
use Ratchet\Command\CommandInterface; use Ratchet\Command\ActionInterface;
use Ratchet\SocketObserver;
class Ping implements CommandInterface { class Ping implements ActionInterface {
public function __construct(SocketInterface $socket) { public function __construct(SocketInterface $socket) {
} }
public function execute() { public function execute(SocketObserver $scope = null) {
} }
} }

View File

@ -1,12 +1,13 @@
<?php <?php
namespace Ratchet\Command; namespace Ratchet\Command;
use Ratchet\SocketInterface; use Ratchet\SocketInterface;
use Ratchet\Command\CommandInterface; use Ratchet\Command\ActionInterface;
use Ratchet\SocketObserver;
class Pong implements CommandInterface { class Pong implements ActionInterface {
public function __construct(SocketInterface $socket) { public function __construct(SocketInterface $socket) {
} }
public function execute() { public function execute(SocketObserver $scope = null) {
} }
} }

View File

@ -4,7 +4,7 @@ use Ratchet\Server\Aggregator;
use Ratchet\Protocol\ProtocolInterface; use Ratchet\Protocol\ProtocolInterface;
use Ratchet\Logging\LoggerInterface; use Ratchet\Logging\LoggerInterface;
use Ratchet\Logging\NullLogger; use Ratchet\Logging\NullLogger;
use Ratchet\Command\Composite; use Ratchet\Command\CommandInterface;
/** /**
* Creates an open-ended socket to listen on a port for incomming connections. Events are delegated through this to attached applications * Creates an open-ended socket to listen on a port for incomming connections. Events are delegated through this to attached applications
@ -72,6 +72,7 @@ class Server implements SocketObserver, \IteratorAggregate {
* @throws Exception * @throws Exception
* @todo Validate address. Use socket_get_option, if AF_INET must be IP, if AF_UNIX must be path * @todo Validate address. Use socket_get_option, if AF_INET must be IP, if AF_UNIX must be path
* @todo Should I make handling open/close/msg an application? * @todo Should I make handling open/close/msg an application?
* @todo Consider making the 4kb listener changable
*/ */
public function run($address = '127.0.0.1', $port = 1025) { public function run($address = '127.0.0.1', $port = 1025) {
set_time_limit(0); set_time_limit(0);
@ -107,8 +108,8 @@ class Server implements SocketObserver, \IteratorAggregate {
} }
} }
if ($res instanceof Composite) { while ($res instanceof CommandInterface) {
$res->execute(); $res = $res->execute($this);
} }
} }
} catch (Exception $se) { } catch (Exception $se) {