Current File : //home/strato/chroot/opt/RZphp80/includes/Net/Server/Driver.php
<?PHP
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
 * Base class for all drivers
 *
 * PHP versions 4 and 5
 *
 * @category Networking
 * @package  Net_Server
 * @author   Stephan Schmidt <schst@php.net>
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.php.net/license PHP License
 * @link     http://pear.php.net/package/Net_Server
 */

/**
 * uses PEAR's error handling and the destructors
 */
require_once 'PEAR.php';

/**
 * Base class for all drivers
 *
 * This class provides methods, can be used by all
 * server implementations.
 *
 * @category Networking
 * @package  Net_Server
 * @author   Stephan Schmidt <schst@php.net>
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.php.net/license PHP License
 * @link     http://pear.php.net/package/Net_Server
 */
class Net_Server_Driver extends PEAR
{
    /**
     * Port to listen on
     *
     * @access private
     * @var integer
     */
    var $port = 10000;

    /**
     * Domain to bind to
     *
     * @access private
     * @var string
     */
    var $domain = "localhost";

    /**
     * The connection protocol:
     * AF_INET, AF_INET6, AF_UNIX
     *
     * @access private
     * @var integer
     */
    var $protocol = AF_INET;

    /**
     * All file descriptors are stored here
     *
     * @access private
     * @var array
     */
    var $clientFD = array();

    /**
     * Maximum amount of clients
     *
     * @access private
     * @var integer
     */
    var $maxClients = -1;

    /**
     * buffer size for socket_read
     * @access private
     * @var integer
     */
    var $readBufferSize = 128;

    /**
     * Wnd character for socket_read
     *
     * @access private
     * @var integer
     */
    var $readEndCharacter = "\n";

    /**
     * Maximum of backlog in queue
     *
     * @access private
     * @var integer
     */
    var $maxQueue = 500;

    /**
     * Debug mode
     *
     * @access private
     * @var boolean
     */
    var $_debug = true;

    /**
     * Debug mode, normally only text is needed,
     * as servers should not be run in a browser
     *
     * @access private
     * @var string
     */
    var $_debugMode = "text";

    /**
     * Debug destination (filename or stdout)
     *
     * @access private
     * @var string
     */
    var $_debugDest = "stdout";

    /**
     * Empty array, used for socket_select
     *
     * @access private
     * @var array
     */
    var $null = array();

    /**
     * Needed to store client information
     *
     * @access private
     * @var array
     */
    var $clientInfo = array();

    /**
     * Part of the string that has been read via socket_read
     * but came after the readEndCharacter char.
     *
     * This can happen if the server gets hammered with > 100 requests
     * per second - multiple requests are delivered on the same connection
     * then. We need to supply a way to split the single packages, and
     * _readLeftOver is the solution.
     *
     * @access private
     * @var string
     */
    var $_readLeftOver = null;



    /**
     * constructor _MUST_ not be called directly
     *
     * Please use the Net_Server::create() method instead
     * that can be called statically and will return a server
     * of the specified type.
     *
     * @param string  $domain   Domain to bind to
     * @param integer $port     Port to listen to
     * @param integer $protocol Protocol type: AF_INET (default), AF_INET6, AF_UNIX
     *
     * @see Net_Server::create()
     *
     * @access   private
     */
    function Net_Server_Driver($domain = "localhost", $port = 10000, $protocol = AF_INET)
    {
        $this->PEAR();

        $this->domain   = $domain;
        $this->port     = $port;
        $this->protocol = (int)$protocol;
    }



    /**
     * destructor
     *
     * Will shutdown the server if the script is abborted.
     *
     * @return void
     *
     * @access private
     */
    function _Net_Server_Driver()
    {
        $this->shutdown();
    }



    /**
     * Set debug mode
     *
     * Debug information can either be written
     * to a logfile or standard out (= the console).
     *
     * To disable debugging, pass false as first parameter.
     *
     * @param mixed  $debug [text|html|false]
     * @param string $dest  Destination of debug message ("stdout" to output or
     *                       filename if log should be written)
     *
     * @return void
     *
     * @access public
     */
    function setDebugMode($debug, $dest = "stdout")
    {
        if ($debug === false) {
            $this->_debug = false;
            return true;
        }

        $this->_debug     = true;
        $this->_debugMode = $debug;
        $this->_debugDest = $dest;
    }



    /**
     * Read from a socket
     *
     * @param integer $clientId Internal id of the client to read from
     *
     * @return string|boolean $data Data that was read
     *
     * @access   private
     */
    function readFromSocket($clientId = 0)
    {
        // start with empty string
        $data = '';

        // There are problems when readEndCharacter is longer than
        // readBufferSize - endings won't be detected.
        $lookInData = strlen($this->readEndCharacter) > $this->readBufferSize;
        if ($lookInData) {
            $extraDataLength = strlen($this->readEndCharacter) - $this->readBufferSize;
        }

        // read data from socket
        while (true) {
            if ($this->_readLeftOver == null) {
                $buf = socket_read(
                    $this->clientFD[$clientId], $this->readBufferSize
                );
            } else {
                $buf                 = $this->_readLeftOver;
                $this->_readLeftOver = null;
            }

            if ($this->readEndCharacter != null) {
                if (strlen($buf) == 0) {
                    break;
                }

                if (!$lookInData) {
                    $posEndChar = strpos($buf, $this->readEndCharacter);
                } else {
                    $posEndChar = strpos(substr($data, - $extraDataLength)
                        . $buf, $this->readEndCharacter);
                }
                if ($posEndChar === false) {
                    $data .= $buf;
                } else {
                    $posEndChar += strlen($this->readEndCharacter);
                    $data       .= substr($buf, 0, $posEndChar);
                    if ($posEndChar < strlen($buf)) {
                        $this->_readLeftOver = substr($buf, $posEndChar);
                    }
                    break;
                }
            } else {
                /**
                 * readEndCharacter is set to null => don't loop, just return
                 */
                $data .= $buf;
                break;
            }
        }

        if ($buf === false) {
            $this->_sendDebugMessage(
                'Could not read from client ' . $clientId . ' ('
                . $this->getLastSocketError($this->clientFD[$clientId])
                . ').');
            return false;
        }
        if ((string)$data === '') {
            return false;
        }
        return $data;
    }



    /**
     * send a debug message
     *
     * Debug messages will be either sent to standard out
     * or written to a logfile, depending on debug settings.
     *
     * @param string $msg Message to debug
     *
     * @return void
     *
     * @see  setDebugMode()
     * @todo remove underscore from method name as the method now is public.
     *
     * @access public
     */
    function _sendDebugMessage($msg)
    {
        if (!$this->_debug) {
            return false;
        }

        $msg = date('Y-m-d H:i:s', time()) . ' ' . $msg;

        switch ($this->_debugMode) {
        case 'text':
            $msg = $msg."\n";
            break;
        case 'html':
            $msg = htmlspecialchars($msg) . "<br />\n";
            break;
        }

        if ($this->_debugDest == 'stdout' || empty($this->_debugDest)) {
            echo $msg;
            flush();
            return true;
        }

        error_log($msg, 3, $this->_debugDest);
        return true;
    }



    /**
     * register a callback object
     *
     * The callback object is the actual server,
     * that contains the logic to process the events
     * triggered by the driver class.
     *
     * The best way to create a callback object is to
     * extend the Net_Server_Handler class.
     *
     * @param object &$object callback object
     *
     * @return void
     *
     * @see Net_Server_Handler
     *
     * @access public
     */
    function setCallbackObject(&$object)
    {
        $this->callbackObj = &$object;
        if (method_exists($this->callbackObj, 'setServerReference')) {
            $this->callbackObj->setServerReference($this);
        }
    }



    /**
     * return string for last socket error
     *
     * @param resource &$fd Socket to get last error from
     *
     * @return string $error Last error
     *
     * @access   public
     */
    function getLastSocketError(&$fd)
    {
        if (!is_resource($fd)) {
            return '';
        }
        $lastError = socket_last_error($fd);
        return 'Msg: ' . socket_strerror($lastError) . ' / Code: '.$lastError;
    }



    /**
     * Sets the readEndCharacter setting.
     *
     * @param string $readEndCharacter The new end character
     *
     * @return void
     *
     * @access public
     */
    function setEndCharacter($readEndCharacter)
    {
        $this->readEndCharacter = $readEndCharacter;
    }



    /**
     * Returns the readEndCharacter setting.
     *
     * @return string The readEndCharacter setting
     *
     * @access public
     */
    function getEndCharacter()
    {
        return $this->readEndCharacter;
    }

}
?>