THRIFT-778. php: PHP socket listening server

This patch which adds TServerTransport/TServerSocket, along with a generic TServer and TSimpleServer implementation.

Patch: Nick Jones

git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@984864 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/php/src/transport/TServerSocket.php b/lib/php/src/transport/TServerSocket.php
new file mode 100644
index 0000000..cac12ab
--- /dev/null
+++ b/lib/php/src/transport/TServerSocket.php
@@ -0,0 +1,96 @@
+<?php
+
+include_once $GLOBALS['THRIFT_ROOT'].'/transport/TServerTransport.php';
+include_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php';
+
+/**
+ * Socket implementation of a server agent.
+ *
+ * @package thrift.transport
+ */
+class TServerSocket extends TServerTransport {
+
+  /**
+   * Handle for the listener socket
+   *
+   * @var resource
+   */
+  private $listener_;
+
+  /**
+   * Port for the listener to listen on
+   *
+   * @var int
+   */
+  private $port_;
+
+  /**
+   * Timeout when listening for a new client
+   *
+   * @var int
+   */
+  private $acceptTimeout_ = 30000;
+
+  /**
+   * Host to listen on
+   *
+   * @var string
+   */
+  private $host_;
+
+  /**
+   * ServerSocket constructor
+   *
+   * @param string $host        Host to listen on
+   * @param int $port           Port to listen on
+   * @return void
+   */
+  public function __construct($host = 'localhost', $port = 9090) {
+    $this->host_ = $host;
+    $this->port_ = $port;
+  }
+
+  /**
+   * Sets the accept timeout
+   *
+   * @param int $acceptTimeout
+   * @return void
+   */
+  public function setAcceptTimeout($acceptTimeout) {
+    $this->acceptTimeout_ = $acceptTimeout;
+  }
+
+  /**
+   * Opens a new socket server handle
+   *
+   * @return void
+   */
+  public function listen() {
+    $this->listener_ = stream_socket_server('tcp://' . $this->host_ . ':' . $this->port_);
+  }
+
+  /**
+   * Closes the socket server handle
+   *
+   * @return void
+   */
+  public function close() {
+    @fclose($this->listener_);
+    $this->listener_ = null;
+  }
+
+  /**
+   * Implementation of accept. If not client is accepted in the given time
+   *
+   * @return TSocket
+   */
+  protected function acceptImpl() {
+    $handle = @stream_socket_accept($this->listener_, $this->acceptTimeout_ / 1000.0);
+    if(!$handle) return null;
+
+    $socket = new TSocket();
+    $socket->setHandle($handle);
+    
+    return $socket;
+  }
+}
diff --git a/lib/php/src/transport/TServerTransport.php b/lib/php/src/transport/TServerTransport.php
new file mode 100644
index 0000000..e92ca77
--- /dev/null
+++ b/lib/php/src/transport/TServerTransport.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Generic class for Server agent.
+ *
+ * @package thrift.transport
+ */
+abstract class TServerTransport {
+  /**
+   * List for new clients
+   *
+   * @abstract
+   * @return void
+   */
+  abstract public function listen();
+
+  /**
+   * Close the server
+   *
+   * @abstract
+   * @return void
+   */
+  abstract public function close();
+
+  /**
+   * Subclasses should use this to implement
+   * accept.
+   *
+   * @abstract
+   * @return TTransport
+   */
+  protected abstract function acceptImpl();
+
+  /**
+   * Uses the accept implemtation. If null is returned, an
+   * exception is thrown.
+   *
+   * @throws TTransportException
+   * @return TTransport
+   */
+  public function accept() {
+    $transport = $this->acceptImpl();
+
+    if ($transport == null) {
+      throw new TTransportException("accept() may not return NULL");
+    }
+    
+    return $transport;
+  }
+}
diff --git a/lib/php/src/transport/TSocket.php b/lib/php/src/transport/TSocket.php
index a3000f7..bbe1987 100644
--- a/lib/php/src/transport/TSocket.php
+++ b/lib/php/src/transport/TSocket.php
@@ -110,6 +110,14 @@
   }
 
   /**
+   * @param resource $handle
+   * @return void
+   */
+  public function setHandle($handle) {
+    $this->handle_ = $handle;
+  }
+
+  /**
    * Sets the send timeout.
    *
    * @param int $timeout  Timeout in milliseconds.
@@ -167,6 +175,17 @@
    * Connects the socket.
    */
   public function open() {
+    if ($this->isOpen()) {
+      throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN);
+    }
+
+    if (empty($this->host_)) {
+      throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN);
+    }
+
+    if ($this->port_ <= 0) {
+      throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN);
+    }
 
     if ($this->persist_) {
       $this->handle_ = @pfsockopen($this->host_,
@@ -225,16 +244,16 @@
       if ($buf === FALSE || $buf === '') {
         $md = stream_get_meta_data($this->handle_);
         if ($md['timed_out']) {
-          throw new TException('TSocket: timed out reading '.$len.' bytes from '.
+          throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '.
                                $this->host_.':'.$this->port_);
         } else {
-          throw new TException('TSocket: Could not read '.$len.' bytes from '.
+          throw new TTransportException('TSocket: Could not read '.$len.' bytes from '.
                                $this->host_.':'.$this->port_);
         }
       } else if (($sz = strlen($buf)) < $len) {
         $md = stream_get_meta_data($this->handle_);
         if ($md['timed_out']) {
-          throw new TException('TSocket: timed out reading '.$len.' bytes from '.
+          throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '.
                                $this->host_.':'.$this->port_);
         } else {
           $pre .= $buf;
@@ -261,10 +280,10 @@
     if ($data === FALSE || $data === '') {
       $md = stream_get_meta_data($this->handle_);
       if ($md['timed_out']) {
-        throw new TException('TSocket: timed out reading '.$len.' bytes from '.
+        throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '.
                              $this->host_.':'.$this->port_);
       } else {
-        throw new TException('TSocket: Could not read '.$len.' bytes from '.
+        throw new TTransportException('TSocket: Could not read '.$len.' bytes from '.
                              $this->host_.':'.$this->port_);
       }
     }
@@ -286,10 +305,10 @@
       if ($got === 0 || $got === FALSE) {
         $md = stream_get_meta_data($this->handle_);
         if ($md['timed_out']) {
-          throw new TException('TSocket: timed out writing '.strlen($buf).' bytes from '.
+          throw new TTransportException('TSocket: timed out writing '.strlen($buf).' bytes from '.
                                $this->host_.':'.$this->port_);
         } else {
-            throw new TException('TSocket: Could not write '.strlen($buf).' bytes '.
+            throw new TTransportException('TSocket: Could not write '.strlen($buf).' bytes '.
                                  $this->host_.':'.$this->port_);
         }
       }
diff --git a/lib/php/src/transport/TTransportFactory.php b/lib/php/src/transport/TTransportFactory.php
new file mode 100644
index 0000000..ac0a65a
--- /dev/null
+++ b/lib/php/src/transport/TTransportFactory.php
@@ -0,0 +1,12 @@
+<?php
+
+class TTransportFactory {
+  /**
+   * @static
+   * @param TTransport $transport
+   * @return TTransport
+   */
+  public static function getTransport(TTransport $transport) {
+    return $transport;
+  }
+}