diff --git a/lib/Ratchet/Application/Server/App.php b/lib/Ratchet/Application/Server/App.php index 831f425..cb80669 100644 --- a/lib/Ratchet/Application/Server/App.php +++ b/lib/Ratchet/Application/Server/App.php @@ -64,11 +64,9 @@ class App implements ApplicationInterface { set_time_limit(0); ob_implicit_flush(); - $host->set_nonblock(); declare(ticks = 1); - $host->bind($address, (int)$port); - $host->listen(); + $host->set_nonblock()->bind($address, (int)$port)->listen(); do { $this->loop($host); diff --git a/lib/Ratchet/Resource/Command/Action/SendMessage.php b/lib/Ratchet/Resource/Command/Action/SendMessage.php index 8cdf57a..74eb595 100644 --- a/lib/Ratchet/Resource/Command/Action/SendMessage.php +++ b/lib/Ratchet/Resource/Command/Action/SendMessage.php @@ -38,6 +38,6 @@ class SendMessage extends ActionTemplate { throw new \UnexpectedValueException("Message is empty"); } - $this->getConnection()->getSocket()->write($this->_message, strlen($this->_message)); + $this->getConnection()->getSocket()->deliver($this->_message); } } \ No newline at end of file diff --git a/lib/Ratchet/Socket.php b/lib/Ratchet/Socket.php index e9989ce..476cfbc 100644 --- a/lib/Ratchet/Socket.php +++ b/lib/Ratchet/Socket.php @@ -5,6 +5,7 @@ use Ratchet\Application\ProtocolInterface; /** * A wrapper for the PHP socket_ functions * @author Chris Boden + * @link http://ca2.php.net/manual/en/book.sockets.php * @todo Possibly move this into Ratchet\Resource - another concrete could use streams */ class Socket implements SocketInterface { @@ -51,10 +52,6 @@ class Socket implements SocketInterface { return $this->_resource; } - /** - * Calls socket_accept, duplicating its self - * @throws Exception - */ public function __clone() { $this->_resource = @socket_accept($this->_resource); @@ -63,6 +60,69 @@ class Socket implements SocketInterface { } } + public function deliver($message) { + $len = strlen($message); + + do { + $sent = $this->write($message, 4); + $len -= $sent; + $message = substr($message, $sent); + } while ($len > 0); + } + + public function bind($address, $port = 0) { + if (false === @socket_bind($this->getResource(), $address, $port)) { + throw new Exception; + } + + return $this; + } + + public function close() { + @socket_close($this->getResource()); + unset($this->_resource); + } + + public function connect($address, $port = 0) { + if (false === @socket_connect($this->getResource(), $address, $port)) { + throw new Exception; + } + + return $this; + } + + public function get_option($level, $optname) { + if (false === ($res = @socket_get_option($this->getResource(), $level, $optname))) { + throw new Exception; + } + + return $res; + } + + public function listen($backlog = 0) { + if (false === @socket_listen($this->getResource(), $backlog)) { + throw new Exception; + } + + return $this; + } + + /** + * @see http://ca3.php.net/manual/en/function.socket-recv.php + * @param string Variable to write data to + * @param int Number of bytes to read + * @param int + * @return int Number of bytes received + * @throws Exception + */ + public function recv(&$buf, $len, $flags) { + if (false === ($bytes = @socket_recv($this->_resource, $buf, $len, $flags))) { + throw new Exception($this); + } + + return $bytes; + } + /** * Since PHP is retarded and their golden hammer, the array, doesn't implement any interfaces I have to hackishly overload socket_select * @see http://ca3.php.net/manual/en/function.socket-select.php @@ -88,31 +148,44 @@ class Socket implements SocketInterface { return $num; } - /** - * @todo Do loop to make sure entire buffer is sent to client - */ - public function write($buffer, $length = 0) { - return $this->__call('write', array($buffer, $length)); - } - - public function close() { - return $this->__call('close', array()); - } - - /** - * @see http://ca3.php.net/manual/en/function.socket-recv.php - * @param string Variable to write data to - * @param int Number of bytes to read - * @param int - * @return int Number of bytes received - * @throws Exception - */ - public function recv(&$buf, $len, $flags) { - if (false === ($bytes = @socket_recv($this->_resource, $buf, $len, $flags))) { - throw new Exception($this); + public function set_block() { + if (false === @socket_set_block($this->getResource())) { + throw new Exception; } - return $bytes; + return $this; + } + + public function set_nonblock() { + if (false === @socket_set_nonblock($this->getResource())) { + throw new Exception; + } + + return $this; + } + + public function set_option($level, $optname, $optval) { + if (false === @socket_set_option($this->getResource(), $level, $optname, $optval)) { + throw new Exception; + } + + return $this; + } + + public function shutdown($how = 2) { + if (false === @socket_shutdown($this->getResource(), $how)) { + throw new Exception; + } + + return $this; + } + + public function write($buffer, $length = 0) { + if (false === ($res = @socket_write($this->getResource(), $buffer, $length))) { + throw new Exception; + } + + return $res; } /** @@ -176,32 +249,4 @@ class Socket implements SocketInterface { return $return; } - - /** - * Call all the socket_ functions (without passing the resource) through this - * @see http://ca3.php.net/manual/en/ref.sockets.php - * @param string - * @param array - * @return mixed - * @throws Exception - * @throws \BadMethodCallException - */ - public function __call($method, $arguments) { - if (function_exists('socket_' . $method)) { - // onBeforeMethod - - array_unshift($arguments, $this->_resource); - $result = @call_user_func_array('socket_' . $method, $arguments); - - if (false === $result) { - throw new Exception($this); - } - - // onAfterMethod - - return $result; - } - - throw new \BadMethodCallException("{$method} is not a valid socket function"); - } } \ No newline at end of file diff --git a/lib/Ratchet/SocketInterface.php b/lib/Ratchet/SocketInterface.php index e165853..e95e3a1 100644 --- a/lib/Ratchet/SocketInterface.php +++ b/lib/Ratchet/SocketInterface.php @@ -11,11 +11,63 @@ interface SocketInterface { function getResource(); /** - * Send text to the client on the other end of the socket + * Return the unique ID of this socket instance + */ + function __toString(); + + /** + * Calls socket_accept, duplicating its self + * @throws Exception + */ + function __clone(); + + /** + * Send a message through the socket. This writes to the buffer until the entire message is delivered + * @param string Your message to send to the socket + * @return null + * @throws Exception + * @see write + */ + function deliver($message); + + // Not sure if I'll implement this or leave it only in clone +// function accept(); + + /** * @param string * @param int + * @return SocketInterface + * @throws Exception */ - function write($buffer, $length = 0); + function bind($address, $port = 0); + + /** + * Close the open connection to the client/socket + */ + function close(); + + /** + * @param string + * @param int + * @return SocketInterface + * @throws Exception + */ + function connect($address, $port = 0); + + /** + * @param int + * @param int + * @return mixed + * @throws Exception + */ + function get_option($level, $optname); + + /** + * @param int + * @return SocketInterface + * @throws Exception + */ + function listen($backlog = 0); /** * Called when the client sends data to the server through the socket @@ -24,16 +76,44 @@ interface SocketInterface { * @param int * @return int Number of bytes received * @throws Exception + * @todo Change the pass by reference */ function recv(&$buf, $len, $flags); - /** - * Close the open connection to the client/socket - */ - function close(); + // @todo Figure out how to break this out to not do pass by reference +// function select(array &$read, array &$write, array &$except, $tv_sec, $tv_usec = 0); /** - * Return the unique ID of this socket instance + * @return SocketInterface + * @throws Exception */ - function __toString(); + function set_block(); + + /** + * @return SocketInterface + * @throws Exception + */ + function set_nonblock(); + + /** + * @param int + * @param int + * @param mixed + * @return SocketInterface + */ + function set_option($level, $optname, $optval); + + /** + * @param int + * @return SocketInterface + * @throws Exception + */ + function shutdown($how = 2); + + /** + * Send text to the client on the other end of the socket + * @param string + * @param int + */ + function write($buffer, $length = 0); } \ No newline at end of file diff --git a/tests/Ratchet/Tests/Mock/FakeSocket.php b/tests/Ratchet/Tests/Mock/FakeSocket.php index 87a3550..20cbcac 100644 --- a/tests/Ratchet/Tests/Mock/FakeSocket.php +++ b/tests/Ratchet/Tests/Mock/FakeSocket.php @@ -14,10 +14,7 @@ class FakeSocket extends RealSocket { return '1'; } - public function accept() { - } - - public function bind($address, $port) { + public function bind($address, $port = 0) { } public function close() { diff --git a/tests/Ratchet/Tests/SocketTest.php b/tests/Ratchet/Tests/SocketTest.php index 2176e13..9699b88 100644 --- a/tests/Ratchet/Tests/SocketTest.php +++ b/tests/Ratchet/Tests/SocketTest.php @@ -39,11 +39,6 @@ class SocketTest extends \PHPUnit_Framework_TestCase { $socket->close(); } - public function testInvalidSocketCall() { - $this->setExpectedException('\\BadMethodCallException'); - $this->_socket->fake_method(); - } - public function asArrayProvider() { return array( array(array('hello' => 'world'), array('hello' => 'world'))