| 
<?php
 /**
 * This file is part of the PHP Generics package.
 *
 * @package Generics
 */
 namespace Generics\Socket;
 
 use Generics\Streams\SocketStream;
 use Generics\ResetException;
 use Exception;
 
 /**
 * This abstract class provides basic socket functionality
 *
 * @author Maik Greubel <[email protected]>
 */
 abstract class Socket implements SocketStream
 {
 
 /**
 * The socket handle
 *
 * @var resource
 */
 protected $handle;
 
 /**
 * The socket endpoint
 *
 * @var Endpoint
 */
 protected $endpoint;
 
 /**
 * Create a new socket
 *
 * @param Endpoint $endpoint
 *            The endpoint for the socket
 */
 public function __construct(Endpoint $endpoint)
 {
 $this->endpoint = $endpoint;
 $this->open();
 }
 
 /**
 * Opens a socket
 *
 * @throws SocketException
 */
 private function open()
 {
 $this->handle = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
 
 if (! is_resource($this->handle)) {
 $code = socket_last_error();
 throw new SocketException(socket_strerror($code), array(), $code);
 }
 }
 
 /**
 * Clean up
 */
 public function __destruct()
 {
 $this->close();
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\Stream::close()
 */
 public function close()
 {
 if (is_resource($this->handle)) {
 socket_close($this->handle);
 $this->handle = null;
 }
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\Stream::ready()
 */
 public function ready(): bool
 {
 if (! is_resource($this->handle)) {
 return false;
 }
 
 $read = array(
 $this->handle
 );
 $write = null;
 $except = null;
 
 $num = @socket_select($read, $write, $except, 0);
 
 if ($num === false) {
 $code = socket_last_error($this->handle);
 throw new SocketException(socket_strerror($code), array(), $code);
 }
 
 if ($num < 1) {
 return false;
 }
 
 if (! in_array($this->handle, $read)) {
 return false;
 }
 
 return true;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\OutputStream::isWriteable()
 */
 public function isWriteable(): bool
 {
 if (! is_resource($this->handle)) {
 return false;
 }
 
 $read = null;
 $write = array(
 $this->handle
 );
 $except = null;
 
 $num = @socket_select($read, $write, $except, 0, 0);
 
 if ($num === false) {
 $code = socket_last_error($this->handle);
 throw new SocketException(socket_strerror($code), array(), $code);
 }
 
 if ($num < 1) {
 return false;
 }
 
 if (! in_array($this->handle, $write)) {
 return false;
 }
 
 return true;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Countable::count()
 */
 public function count()
 {
 throw new SocketException("Cannot count elements of socket");
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\InputStream::read()
 */
 public function read($length = 1, $offset = null): string
 {
 if (($buf = @socket_read($this->handle, $length)) === false) {
 $buf = "";
 $code = socket_last_error();
 if ($code != 0) {
 if ($code != 10053) {
 throw new SocketException(socket_strerror($code), array(), $code);
 } else {
 $this->handle = null;
 }
 }
 }
 
 return $buf;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\OutputStream::write()
 */
 public function write($buffer)
 {
 if (($written = @socket_write($this->handle, "{$buffer}")) === false) {
 $code = socket_last_error();
 throw new SocketException(socket_strerror($code), array(), $code);
 }
 
 if ($written != strlen($buffer)) {
 throw new SocketException("Could not write all {bytes} bytes to socket ({written} written)", array(
 'bytes' => strlen($buffer),
 'written' => $written
 ));
 }
 }
 
 /**
 * Get the socket endpoint
 *
 * @return \Generics\Socket\Endpoint
 */
 public function getEndpoint(): Endpoint
 {
 return $this->endpoint;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\OutputStream::flush()
 */
 public function flush()
 {
 // There is no function to flush a socket. This is only possible for file descriptors.
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\Stream::isOpen()
 */
 public function isOpen(): bool
 {
 return is_resource($this->handle);
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Resettable::reset()
 */
 public function reset()
 {
 try {
 $this->close();
 $this->open();
 } catch (Exception $ex) {
 throw new ResetException($ex->getMessage(), array(), $ex->getCode(), $ex);
 }
 }
 }
 
 |