*/ class Socket implements SocketInterface { /** * @type resource */ protected $_resource; public static $_defaults = array( 'domain' => AF_INET , 'type' => SOCK_STREAM , 'protocol' => SOL_TCP ); /** * @param int Specifies the protocol family to be used by the socket. * @param int The type of communication to be used by the socket * @param int Sets the specific protocol within the specified domain to be used when communicating on the returned socket * @throws Ratchet\Exception */ public function __construct($domain = null, $type = null, $protocol = null) { list($domain, $type, $protocol) = static::getConfig($domain, $type, $protocol); $this->_resource = @socket_create($domain, $type, $protocol); if (!is_resource($this->_resource)) { throw new Exception; } } /** * @return resource (Socket) */ public function getResource() { return $this->_resource; } /** * Calls socket_accept, duplicating its self * @throws Exception */ public function __clone() { $this->_resource = @socket_accept($this->_resource); if (false === $this->_resource) { throw new Exception; } } /** * 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 * @param Iterator|array|NULL The sockets listed in the read array will be watched to see if characters become available for reading (more precisely, to see if a read will not block - in particular, a socket resource is also ready on end-of-file, in which case a socket_read() will return a zero length string). * @param Iterator|array|NULL The sockets listed in the write array will be watched to see if a write will not block. * @param Iterator|array|NULL The sockets listed in the except array will be watched for exceptions. * @param int The tv_sec and tv_usec together form the timeout parameter. The timeout is an upper bound on the amount of time elapsed before socket_select() return. tv_sec may be zero , causing socket_select() to return immediately. This is useful for polling. If tv_sec is NULL (no timeout), socket_select() can block indefinitely. * @param int * @throws \InvalidArgumentException * @throws Exception * @todo See if this crack-pot scheme works! */ public function select(&$read, &$write, &$except, $tv_sec, $tv_usec = 0) { $read = static::mungForSelect($read); $write = static::mungForSelect($write); $except = static::mungForSelect($except); $num = socket_select($read, $write, $except, $tv_sec, $tv_usec); if (false === $num) { throw new Exception; } return $num; } 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 * @param int * @param int * @return int */ public function recv(&$buf, $len, $flags) { return socket_recv($this->_resource, $buf, $len, $flags); } /** * @param Ratchet\Protocol\ProtocolInterface * @return Socket * @throws Exception */ public static function createFromConfig(ProtocolInterface $protocol) { $config = $protocol::getDefaultConfig(); $class = get_called_class(); $socket = new $class($config['domain'] ?: null, $config['type'] ?: null, $config['protocol'] ?: null); if (is_array($config['options'])) { foreach ($config['options'] as $level => $pair) { foreach ($pair as $optname => $optval) { $socket->set_option($level, $optname, $optval); } } } return $socket; } /** * @internal * @param int Specifies the protocol family to be used by the socket. * @param int The type of communication to be used by the socket * @param int Sets the specific protocol within the specified domain to be used when communicating on the returned socket * @return array */ protected static function getConfig($domain = null, $type = null, $protocol = null) { foreach (static::$_defaults as $key => $val) { if (null === $$key) { $$key = $val; } } return array($domain, $type, $protocol); } /** * @internal * @param Iterator|array|NULL * @return array|NULL * @throws \InvalidArgumentException */ protected static function mungForSelect($collection) { if (null === $collection || is_array($collection)) { return $collection; } if (!($collection instanceof \Traversable)) { throw new \InvalidArgumentException('Object pass is not traversable'); } $return = array(); foreach ($collection as $key => $socket) { $return[$key] = ($socket instanceof \Ratchet\Socket ? $socket->getResource() : $socket); } 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; } // onAfterMethod return $result; } throw new \BadMethodCallException("{$method} is not a valid socket function"); } }