nanoserv
[ class tree: nanoserv ] [ index: nanoserv ] [ all elements ]

Source for file nanoserv.php

Documentation is available at nanoserv.php

  1. <?php
  2.  
  3. /**
  4.  *
  5.  * nanoserv - a sockets daemon toolkit for PHP 5.1+
  6.  * 
  7.  * Copyright (C) 2004-2010 Vincent Negrier aka. sIX <six at aegis-corp.org>
  8.  * 
  9.  * This library is free software; you can redistribute it and/or
  10.  * modify it under the terms of the GNU Lesser General Public
  11.  * License as published by the Free Software Foundation; either
  12.  * version 2.1 of the License, or (at your option) any later version.
  13.  * 
  14.  * This library is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17.  * Lesser General Public License for more details.
  18.  * 
  19.  * You should have received a copy of the GNU Lesser General Public
  20.  * License along with this library; if not, write to the Free Software
  21.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  22.  *
  23.  * @package nanoserv
  24.  * @subpackage Core
  25.  */
  26.  
  27. /**
  28.  * nanoserv current version number
  29.  * @var string 
  30.  */
  31. define("NS_VERSION""2.1.1-dev");
  32.  
  33. /**
  34.  * Base exception class
  35.  *
  36.  * @package nanoserv
  37.  * @subpackage Core
  38.  * @since 2.0
  39.  */
  40. abstract class NS_Exception extends Exception {
  41.  
  42.     public $addr;
  43.  
  44.     public function __construct($errmsg$errno$addr{
  45.  
  46.         parent::__construct($errmsg$errno);
  47.  
  48.         $this->addr = $addr;
  49.  
  50.     }
  51.  
  52. }
  53.  
  54. /**
  55.  * Server exception class
  56.  *
  57.  * @package nanoserv
  58.  * @subpackage Core
  59.  * @since 2.0
  60.  */
  61. class NS_Server_Exception extends NS_Exception {
  62.  
  63.     public $listener;
  64.  
  65.     public function __construct($errmsg$errno$addrNS_Listener $listener NULL{
  66.  
  67.         parent::__construct($errmsg$errno$addr);
  68.  
  69.         $this->listener = $listener;
  70.     
  71.     }
  72.  
  73. }
  74.  
  75. /**
  76.  * Client exception class
  77.  *
  78.  * @package nanoserv
  79.  * @subpackage Core
  80.  * @since 2.0
  81.  */
  82. class NS_Client_Exception extends NS_Exception {
  83.  
  84.     public $handler;
  85.  
  86.     public function __construct($errmsg$errno$addrNS_Handler $handler NULL{
  87.  
  88.         parent::__construct($errmsg$errno$addr);
  89.  
  90.         $this->handler = $handler;
  91.     
  92.     }
  93.  
  94. }
  95.  
  96. /**
  97.  * Base socket class
  98.  *
  99.  * @package nanoserv
  100.  * @subpackage Core
  101.  * @since 0.9
  102.  */
  103. class NS_Socket {
  104.  
  105.     /**
  106.      * Maximum number of bytes read by Read()
  107.      * @var int 
  108.      */
  109.     const DEFAULT_READ_LENGTH = 16384;
  110.     
  111.     /**
  112.      * Internal Socket unique ID
  113.      * @var int 
  114.      */
  115.     public $id;
  116.     
  117.     /**
  118.      * Socket stream descriptor
  119.      * @var resource 
  120.      */
  121.     public $fd;
  122.  
  123.     /**
  124.      * Is the socket connected ?
  125.      * @var bool 
  126.      */
  127.     public $connected = false;
  128.     
  129.     /**
  130.      * Is the socket waiting to be connected ?
  131.      * @var bool 
  132.      */
  133.     public $pending_connect = false;
  134.     
  135.     /**
  136.      * Is the socket waiting for ssl/tls handshake ?
  137.      * @var bool 
  138.      */
  139.     public $pending_crypto = false;
  140.     
  141.     /**
  142.      * Is the socket blocked ?
  143.      * @var bool 
  144.      */
  145.     public $blocked = false;
  146.     
  147.     /**
  148.      * Should we block reading from this socket ?
  149.      * @var bool 
  150.      */
  151.     public $block_reads = false;
  152.     
  153.     /**
  154.      * Stream context
  155.      * @var resource 
  156.      */
  157.     protected $context;
  158.     
  159.     /**
  160.      * Crypto type
  161.      * @var int 
  162.      */
  163.     public $crypto_type;
  164.     
  165.     /**
  166.      * Attached handler
  167.      * @var NS_Connection_Handler 
  168.      */
  169.     public $handler;
  170.     
  171.     /**
  172.      * Static instance counter
  173.      * @var int 
  174.      */
  175.     private static $sck_cnt;
  176.     
  177.     /**
  178.      * NS_Socket contructor
  179.      *
  180.      * @param resource $fd 
  181.      */
  182.     public function __construct($fd false$crypto_type false{
  183.  
  184.         if ($fd === false{
  185.         
  186.             $this->context = stream_context_create();
  187.  
  188.         else {
  189.  
  190.             $this->fd = $fd;
  191.             $this->connected = true;
  192.             $this->Set_Blocking(false);
  193.             $this->Set_Timeout(0);
  194.  
  195.             if ($crypto_type$this->crypto_type = $crypto_type;
  196.         
  197.         }
  198.     
  199.         $this->id = ++NS_Socket::$sck_cnt;
  200.     
  201.     }
  202.     
  203.     /**
  204.      * Get stream options
  205.      *
  206.      * @return array 
  207.      * @since 0.9
  208.      */
  209.     public function Get_Options({
  210.  
  211.         if ($this->fd{
  212.  
  213.             return stream_context_get_options($this->fd);
  214.  
  215.         else {
  216.  
  217.             return stream_context_get_options($this->context);
  218.  
  219.         }
  220.  
  221.     }
  222.     
  223.     /**
  224.      * Set a stream context option
  225.      *
  226.      * @param string $wrapper 
  227.      * @param string $opt 
  228.      * @param mixed $val 
  229.      * @return bool 
  230.      * @since 0.9
  231.      */
  232.     public function Set_Option($wrapper$opt$val{
  233.  
  234.         if ($this->fd{
  235.  
  236.             return stream_context_set_option($this->fd$wrapper$opt$val);
  237.  
  238.         else {
  239.  
  240.             return stream_context_set_option($this->context$wrapper$opt$val);
  241.  
  242.         }
  243.     
  244.     }
  245.     
  246.     /**
  247.      * Set timeout
  248.      * 
  249.      * @param int $timeout 
  250.      * @return bool 
  251.      * @since 0.9
  252.      */
  253.     protected function Set_Timeout($timeout{
  254.  
  255.         return stream_set_timeout($this->fd$timeout);
  256.     
  257.     }
  258.     
  259.     /**
  260.      * Sets wether the socket is blocking or not
  261.      *
  262.      * @param bool $block 
  263.      * @return bool 
  264.      * @since 0.9
  265.      */
  266.     protected function Set_Blocking($block{
  267.  
  268.         return stream_set_blocking($this->fd$block);
  269.  
  270.     }
  271.  
  272.     /**
  273.      * Flag the socket so that the main loop won't read from it even if data is available.
  274.      *
  275.      * This can be used to implement flow control when proxying data between two asymetric connections for example.
  276.      *
  277.      * @param bool $block 
  278.      * @return bool the previous status
  279.      * @since 2.0.3
  280.      */
  281.     public function Block_Reads($block{
  282.  
  283.         $ret $this->block_reads;
  284.  
  285.         $this->block_reads = $block;
  286.  
  287.         return $ret;
  288.     
  289.     }
  290.     
  291.     /**
  292.      * Set the stream write buffer (PHP defaults to 8192 bytes)
  293.      *
  294.      * @param int $buffer_size 
  295.      * @return int 
  296.      * @since 2.0
  297.      */
  298.     public function Set_Write_Buffer($buffer_size{
  299.  
  300.         return stream_set_write_buffer($this->fd$buffer_size);
  301.     
  302.     }
  303.     
  304.     /**
  305.      * Enable or disable ssl/tls crypto on the socket
  306.      *
  307.      * @param bool $enable 
  308.      * @param int $type 
  309.      * @return mixed 
  310.      * @since 0.9
  311.      */
  312.     public function Enable_Crypto($enable true$type false{
  313.  
  314.         if ($type$this->crypto_type = $type;
  315.         
  316.         $ret @stream_socket_enable_crypto($this->fd$enable$this->crypto_type);
  317.         
  318.         $this->pending_crypto = $ret === 0;
  319.  
  320.         return $ret;
  321.         
  322.     }
  323.     
  324.     /**
  325.      * Setup crypto if needed
  326.      *
  327.      * @return bool 
  328.      * @since 0.9
  329.      */
  330.     public function Setup({
  331.  
  332.         if (isset($this->crypto_type)) return $this->Enable_Crypto();
  333.  
  334.         return true;
  335.         
  336.     }
  337.     
  338.     /**
  339.      * Get local socket name
  340.      *
  341.      * @return string 
  342.      * @since 0.9
  343.      */
  344.     public function Get_Name({
  345.  
  346.         return stream_socket_get_name($this->fdfalse);
  347.  
  348.     }
  349.     
  350.     /**
  351.      * Get remote socket name
  352.      *
  353.      * @return string 
  354.      * @since 0.9
  355.      */
  356.     public function Get_Peer_Name({
  357.  
  358.         return stream_socket_get_name($this->fdtrue);
  359.  
  360.     }
  361.     
  362.     /**
  363.      * Read data from the socket and return it
  364.      *
  365.      * @param int $length maximum read length
  366.      * @return string 
  367.      * @since 0.9
  368.      */
  369.     public function Read({
  370.  
  371.         return fread($this->fdself::DEFAULT_READ_LENGTH);
  372.  
  373.     }
  374.  
  375.     /**
  376.      * Read data from a non connected socket and return it
  377.      *
  378.      * @param string &$addr contains the message sender address upon return
  379.      * @param int $len maximum read length
  380.      * @return string 
  381.      * @since 0.9.61
  382.      */
  383.     public function Read_From(&$addr$len 16384{
  384.  
  385.         return stream_socket_recvfrom($this->fd$lenNULL$addr);
  386.  
  387.     }
  388.     
  389.     /**
  390.      * Write data to the socket
  391.      *
  392.      * write returns the number of bytes written to the socket
  393.      *
  394.      * @param string $data 
  395.      * @return int 
  396.      * @since 0.9
  397.      */
  398.     public function Write($data{
  399.  
  400.         $nb fwrite($this->fd$data);
  401.  
  402.         if (isset($data[$nb])) $this->blocked = true;
  403.  
  404.         return $nb;
  405.     
  406.     }
  407.     
  408.     /**
  409.      * Write data to a non connected socket
  410.      *
  411.      * @param string $to in the form of "<ip_address>:<port>"
  412.      * @param string $data 
  413.      * @return int 
  414.      * @since 0.9.61
  415.      */
  416.     public function Write_To($to$data{
  417.  
  418.         return stream_socket_sendto($this->fd$dataNULL$to);
  419.     
  420.     }
  421.     
  422.     /**
  423.      * Write data from stream to socket
  424.      *
  425.      * returns the number of bytes read from the stream and written to the socket
  426.      *
  427.      * @param resource $stream 
  428.      * @param int $len maximum length (bytes) to read/write
  429.      * @return int 
  430.      * @since 2.1
  431.      */
  432.     public function Write_From_Stream($stream$len 16384{
  433.         
  434.         return stream_copy_to_stream($stream$this->fd$len);
  435.     
  436.     }
  437.     
  438.     /**
  439.      * Query end of stream status
  440.      *
  441.      * @return bool 
  442.      * @since 0.9
  443.      */
  444.     public function Eof({
  445.  
  446.         $fd $this->fd;
  447.         
  448.         if (!is_resource($fd)) return true;
  449.  
  450.         stream_socket_recvfrom($fd1STREAM_PEEK);
  451.         
  452.         return feof($fd);
  453.  
  454.     }
  455.     
  456.     /**
  457.      * Close the socket
  458.      * @since 0.9
  459.      */
  460.     public function Close({
  461.  
  462.         @fclose($this->fd);
  463.  
  464.         $this->connected = $this->pending_connect = false;
  465.  
  466.     }
  467.  
  468.     /**
  469.      * NS_Socket destructor
  470.      */
  471.     public function __destruct({
  472.  
  473.         Nanoserv::Free_Write_Buffers($this->id);
  474.  
  475.         $this->Close();
  476.  
  477.     }
  478.  
  479. }
  480.  
  481. /**
  482.  * Server socket class
  483.  *
  484.  * @package nanoserv
  485.  * @subpackage Core
  486.  * @since 0.9
  487.  */
  488. class NS_Server_Socket extends NS_Socket {
  489.  
  490.     /**
  491.      * Listen address (format is 'proto://addr:port')
  492.      * @var string 
  493.      */
  494.     public $address;
  495.  
  496.     /**
  497.      * Real listen address (format is 'proto://addr:port')
  498.      * @var string 
  499.      */
  500.     private $real_address;
  501.  
  502.     /**
  503.      * NS_Server_Socket constructor
  504.      */
  505.     public function __construct($addr{
  506.  
  507.         parent::__construct();
  508.         
  509.         $this->address = $addr;
  510.  
  511.         $proto strtolower(strtok($addr":"));
  512.  
  513.         if (($proto === "udp"|| ($proto === "unix")) {
  514.  
  515.             $this->real_address $addr;
  516.         
  517.         else {
  518.         
  519.             $this->real_address "tcp:" strtok("");
  520.  
  521.             if ($proto !== "tcp"switch ($proto{
  522.  
  523.                 case "ssl":        $this->crypto_type = STREAM_CRYPTO_METHOD_SSLv23_SERVER;    break;
  524.                 case "tls":        $this->crypto_type = STREAM_CRYPTO_METHOD_TLS_SERVER;        break;
  525.                 case "sslv2":    $this->crypto_type = STREAM_CRYPTO_METHOD_SSLv2_SERVER;        break;
  526.                 case "sslv3":    $this->crypto_type = STREAM_CRYPTO_METHOD_SSLv3_SERVER;        break;
  527.  
  528.                 default:        
  529.                     
  530.                 if (defined($cname "STREAM_CRYPTO_METHOD_".strtoupper($proto)."_SERVER")) {
  531.                     
  532.                     $this->crypto_type = constant($cname);
  533.  
  534.                 else {
  535.  
  536.                     throw new NS_Server_Exception("unknown transport/crypto type '{$proto}'");
  537.                 
  538.                 }
  539.             
  540.             }
  541.  
  542.         }
  543.     
  544.     }
  545.  
  546.     /**
  547.      * Start listening and accepting connetions
  548.      *
  549.      * @return bool 
  550.      * @since 0.9
  551.      */
  552.     public function Listen($bind_only false{
  553.  
  554.         $errno $errstr false;
  555.         
  556.         $this->fd = @stream_socket_server($this->real_address$errno$errstrSTREAM_SERVER_BIND ($bind_only STREAM_SERVER_LISTEN)$this->context);
  557.  
  558.         if ($this->fd === false{
  559.  
  560.             throw new NS_Server_Exception("cannot listen to {$this->real_address}: {$errstr}", $errno, $this->real_address);
  561.         
  562.         }
  563.  
  564.         $this->Set_Blocking(false);
  565.         $this->Set_Timeout(0);
  566.         
  567.         return true;
  568.  
  569.     }
  570.  
  571.     /**
  572.      * Accept connection
  573.      *
  574.      * @return resource
  575.      * @since 0.9
  576.      */
  577.     public function Accept() {
  578.  
  579.         return @stream_socket_accept($this->fd, 0);
  580.  
  581.     }
  582.  
  583. }
  584.  
  585.  
  586. /**
  587.  * Client socket class
  588.  *
  589.  * @package nanoserv
  590.  * @subpackage Core
  591.  * @since 0.9
  592.  */
  593. class NS_Client_Socket extends NS_Socket {
  594.  
  595.     /**
  596.      * Connect timeout (seconds)
  597.      * @var int
  598.      */
  599.     const CONNECT_TIMEOUT = 10;
  600.     
  601.     /**
  602.      * Peer address (format is 'proto://addr:port')
  603.      * @var string
  604.      */
  605.     public $address;
  606.  
  607.     /**
  608.      * Connect timeout (timestamp)
  609.      * @var int
  610.      */
  611.     public $connect_timeout;
  612.     
  613.     /**
  614.      * NS_Client_Socket constructor
  615.      */
  616.     public function __construct($addr) {
  617.  
  618.         parent::__construct();
  619.         
  620.         $this->address = $addr;
  621.  
  622.         $proto = strtolower(strtok($addr, ":"));
  623.         $s = strtok("");
  624.  
  625.         if (($proto === "udp") || ($proto === "unix")) {
  626.  
  627.             $this->real_address = $addr;
  628.         
  629.         } else {
  630.         
  631.             $this->real_address = "tcp:" . $s;
  632.  
  633.             if ($proto != "tcp") switch ($proto) {
  634.  
  635.                 case "ssl":        $this->crypto_type = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;    break;
  636.                 case "tls":        $this->crypto_type = STREAM_CRYPTO_METHOD_TLS_CLIENT;        break;
  637.                 case "sslv2":    $this->crypto_type = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;        break;
  638.                 case "sslv3":    $this->crypto_type = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;        break;
  639.  
  640.                 default:        if (defined($cname = "STREAM_CRYPTO_METHOD_".strtoupper($proto)."_CLIENT")) $this->crypto_type = constant($cname);
  641.             
  642.             }
  643.  
  644.         }
  645.     
  646.     }
  647.  
  648.     /**
  649.      * Connect to the peer address
  650.      *
  651.      * @param int $timeout connection timeout in seconds
  652.      * @return bool
  653.      * @since 0.9
  654.      */
  655.     public function Connect($timeout = false) {
  656.  
  657.         $errno = $errstr = false;
  658.  
  659.         $this->fd = @stream_socket_client($this->real_address, $errno, $errstr, 3, STREAM_CLIENT_ASYNC_CONNECT | STREAM_CLIENT_CONNECT, $this->context);
  660.  
  661.         if ($this->fd === false) {
  662.  
  663.             throw new <a href="../nanoserv/Core/NS_Client_Exception.html">NS_Client_Exception</a>("cannot connect to {$this->real_address}: {$errstr}", $errno, $this->real_address);
  664.         
  665.         }
  666.  
  667.         if ($timeout === false) $timeout = self::CONNECT_TIMEOUT;
  668.         
  669.         $this->connect_timeout = microtime(true) + $timeout;
  670.         $this->pending_connect = true;
  671.         $this->connected = false;
  672.         $this->Set_Blocking(false);
  673.         $this->Set_Timeout(0);
  674.         
  675.         return true;
  676.  
  677.     }
  678.  
  679. }
  680.  
  681.  
  682. /**
  683.  * IPC Socket class
  684.  *
  685.  * @package nanoserv
  686.  * @subpackage Core
  687.  * @since 0.9
  688.  */
  689. class NS_IPC_Socket extends NS_Socket {
  690.  
  691.     /**
  692.      * Maximum size of inter process communication packets
  693.      * @var int
  694.      */
  695.     const IPC_MAX_PACKET_SIZE = 1048576;
  696.  
  697.     /**
  698.      * pid number of the remote forked process
  699.      * @var int
  700.      */
  701.     public $pid;
  702.     
  703.     /**
  704.      * IPC Socket constructor
  705.      *
  706.      * @param resource $fd
  707.      * @param int $pid
  708.      */
  709.     public function __construct($fd, $pid=false) {
  710.  
  711.         parent::__construct($fd);
  712.         
  713.         $this->Set_Write_Buffer(self::IPC_MAX_PACKET_SIZE);
  714.         
  715.         $this->pid = $pid;
  716.  
  717.     }
  718.  
  719.     /**
  720.      * Read data from IPC socket
  721.      *
  722.      * @return string
  723.      * @since 0.9
  724.      */
  725.     public function Read() {
  726.  
  727.         return fread($this->fd, self::IPC_MAX_PACKET_SIZE);
  728.     
  729.     }
  730.  
  731.     /**
  732.      * Creates a pair of connected, indistinguishable pipes
  733.      *
  734.      * Returns an array of two NS_IPC_Socket objects
  735.      *
  736.      * @param int $domain
  737.      * @param int $type
  738.      * @param int $proto
  739.      * @return array
  740.      * @since 0.9
  741.      */
  742.     static public function Pair($domain = STREAM_PF_UNIX, $type = STREAM_SOCK_DGRAM, $proto = 0) {
  743.  
  744.         list($s1, $s2) = stream_socket_pair($domain, $type, $proto);
  745.  
  746.         return array(new NS_IPC_Socket($s1), new NS_IPC_Socket($s2));
  747.     
  748.     }
  749.     
  750.     /**
  751.      * Ask the master process for object data
  752.      *
  753.      * @param array $request
  754.      * @param bool $need_response
  755.      * @return mixed
  756.      * @since 0.9
  757.      */
  758.     public function Ask_Master($request, $need_response = true) {
  759.  
  760.         $this->Write(serialize($request));
  761.  
  762.         if (!$need_response) return;
  763.         
  764.         $rfd = array($this->fd);
  765.         $dfd = array();
  766.         
  767.         if (@stream_select($rfd, $dfd, $dfd, 600)) return unserialize($this->Read());
  768.  
  769.     }
  770.  
  771. }
  772.  
  773. /**
  774.  * Timer class
  775.  *
  776.  * Do not instanciate NS_Timer but use the Nanoserv::New_Timer() method instead
  777.  *
  778.  * @package nanoserv
  779.  * @subpackage Core
  780.  * @since 0.9
  781.  */
  782. class NS_Timer {
  783.  
  784.     /**
  785.      * System time for timer activation
  786.      * @var float
  787.      */
  788.     public $microtime;
  789.  
  790.     /**
  791.      * Timer callback
  792.      * @var mixed
  793.      */
  794.     public $callback;
  795.  
  796.     /**
  797.      * Timer status
  798.      * @var bool
  799.      */
  800.     public $active = true;
  801.     
  802.     /**
  803.      * NS_Timer constructor
  804.      *
  805.      * @param float $time
  806.      * @param mixed $callback
  807.      * @since 0.9
  808.      * @see Nanoserv::New_Timer()
  809.      */
  810.     public function __construct($time, $callback) {
  811.  
  812.         $this->microtime = $time;
  813.         $this->callback = $callback;
  814.     
  815.     }
  816.  
  817.     /**
  818.      * Activate timer
  819.      *
  820.      * Timers are activated by default, and Activate should only be used after a call do Deactivate()
  821.      *
  822.      * @see NS_Timer::Deactivate()
  823.      */
  824.     public function Activate() {
  825.  
  826.         $this->active = true;
  827.  
  828.     }
  829.  
  830.     /**
  831.      * Deactivate timer
  832.      */
  833.     public function Deactivate() {
  834.  
  835.         $this->active = false;
  836.  
  837.     }
  838.  
  839. }
  840.  
  841. /**
  842.  * Write buffer interface
  843.  */
  844. interface NS_I_Write_Buffer {
  845.  
  846.     /**
  847.      * Setup a new write buffer
  848.      *
  849.      * @param NS_Socket $socket
  850.      * @param mixed $data
  851.      * @param mixed $callback
  852.      */
  853.     public function __construct(NS_Socket $socket, $data, $callback = false);
  854.  
  855.     /**
  856.      * Get availability of data
  857.      *
  858.      * @return bool
  859.      * @since 0.9
  860.      */
  861.     public function Waiting_Data();
  862.  
  863.     /**
  864.      * Write data to socket and advance buffer pointer
  865.      *
  866.      * @param int $length
  867.      */
  868.     public function Write($length = NULL);
  869.  
  870. }
  871.  
  872. /**
  873.  * Write buffer base class
  874.  *
  875.  * @package nanoserv
  876.  * @subpackage Core
  877.  * @since 0.9
  878.  */
  879. abstract class NS_Write_Buffer {
  880.  
  881.     /**
  882.      * Attached socket
  883.      * @var NS_Socket
  884.      */
  885.     public $socket;
  886.     
  887.     /**
  888.      * Buffered data
  889.      * @var string
  890.      */
  891.     protected $data;
  892.  
  893.     /**
  894.      * End-of-write Callback
  895.      * @var mixed
  896.      */
  897.     protected $callback = false;
  898.  
  899.     /**
  900.      * NS_Write_Buffer constructor
  901.      *
  902.      * @param NS_Socket $socket
  903.      * @param mixed $data
  904.      * @param mixed $callback
  905.      */
  906.     public function __construct(NS_Socket $socket, $data, $callback = false) {
  907.  
  908.         $this->socket = $socket;
  909.         $this->data = $data;
  910.         $this->callback = $callback;
  911.     
  912.     }
  913.     
  914.     /**
  915.      * NS_Write_Buffer destructor
  916.      */
  917.     public function __destruct() {
  918.  
  919.         if ($this->callback) call_user_func($this->callback, $this->Waiting_Data());
  920.     
  921.     }
  922.  
  923. }
  924.  
  925.  
  926. /**
  927.  * Static write buffer class
  928.  *
  929.  * @package nanoserv
  930.  * @subpackage Core
  931.  * @since 0.9
  932.  */
  933. class NS_Static_Write_Buffer extends NS_Write_Buffer implements NS_I_Write_Buffer {
  934.  
  935.     /**
  936.      * Buffered data pointer
  937.      * @var int
  938.      */
  939.     private $pointer = 0;
  940.     
  941.     /**
  942.      * Get availability of data
  943.      *
  944.      * @return bool
  945.      * @since 0.9
  946.      */
  947.     public function Waiting_Data() {
  948.  
  949.         return isset($this->data[$this->pointer]);
  950.         
  951.     }
  952.  
  953.     /**
  954.      * Write data to socket and advance buffer pointer
  955.      *
  956.      * @param int $length
  957.      * @since 1.1
  958.      */
  959.     public function Write($length = 16384) {
  960.  
  961.         $this->pointer += $this->socket->Write(substr($this->data, $this->pointer, $length));
  962.     
  963.     }
  964.     
  965. }
  966.  
  967.  
  968. /**
  969.  * Stream write buffer class
  970.  *
  971.  * @package nanoserv
  972.  * @subpackage Core
  973.  * @since 2.1
  974.  */
  975. class NS_Stream_Write_Buffer extends NS_Write_Buffer implements NS_I_Write_Buffer {
  976.  
  977.     /**
  978.      * Get availability of data from stream
  979.      *
  980.      * @return bool
  981.      * @since 0.9
  982.      */
  983.     public function Waiting_Data() {
  984.  
  985.         return !@feof($this->data);
  986.         
  987.     }
  988.  
  989.     /**
  990.      * Read data from stream and write it to socket
  991.      *
  992.      * @param int $length
  993.      * @since 1.1
  994.      */
  995.     public function Write($length = 16384) {
  996.  
  997.         return $this->socket->Write_From_Stream($this->data, $length);
  998.     
  999.     }
  1000.  
  1001. }
  1002.  
  1003. /**
  1004.  * Base handler class
  1005.  *
  1006.  * @package nanoserv
  1007.  * @subpackage Core
  1008.  * @since 0.9
  1009.  */
  1010. abstract class NS_Handler {
  1011.  
  1012.     /**
  1013.      * Attached socket
  1014.      * @var NS_Socket
  1015.      */
  1016.     public $socket;
  1017.  
  1018.     /**
  1019.      * Set a stream context option
  1020.      *
  1021.      * @param string $wrapper
  1022.      * @param string $opt
  1023.      * @param mixed $val
  1024.      * @return bool
  1025.      * @since 0.9
  1026.      */
  1027.     public function Set_Option($wrapper, $opt, $val) {
  1028.  
  1029.         return $this->socket->Set_Option($wrapper, $opt, $val);
  1030.     
  1031.     }
  1032.  
  1033. }
  1034.  
  1035.  
  1036. /**
  1037.  * Datagram listener / handler class
  1038.  *
  1039.  * @package nanoserv
  1040.  * @subpackage Core
  1041.  * @since 0.9.61
  1042.  */
  1043. abstract class NS_Datagram_Handler extends NS_Handler {
  1044.  
  1045.     /**
  1046.      * Is the listener active ?
  1047.      * @var bool
  1048.      */
  1049.     public $active = false;
  1050.  
  1051.     /**
  1052.      * NS_Datagram_Handler constructor
  1053.      *
  1054.      * @param string $addr
  1055.      * @param string $handler_classname
  1056.      * @param mixed $handler_options
  1057.      */
  1058.     public function __construct($addr) {
  1059.  
  1060.         $this->socket = new NS_Server_Socket($addr);
  1061.     
  1062.     }
  1063.     
  1064.     /**
  1065.      * Activate the listener
  1066.      *
  1067.      * @return bool
  1068.      * @since 0.9.61
  1069.      */
  1070.     public function Activate() {
  1071.  
  1072.         try {
  1073.         
  1074.             if ($ret = $this->socket->Listen(true)) $this->active = true;
  1075.             
  1076.             return $ret;
  1077.     
  1078.         } catch (NS_Server_Exception $e) {
  1079.  
  1080.             throw new NS_Server_Exception($e->getMessage(), $e->getCode(), $e->addr, $this);
  1081.         
  1082.         }
  1083.     
  1084.     }
  1085.  
  1086.     /**
  1087.      * Deactivate the listener
  1088.      * @since 0.9.61
  1089.      */
  1090.     public function Deactivate($close_socket = true) {
  1091.  
  1092.         if ($close_socket) {
  1093.             
  1094.             $this->socket->Close();
  1095.  
  1096.         }
  1097.  
  1098.         $this->active = false;
  1099.     
  1100.     }
  1101.  
  1102.     /**
  1103.      * Send data over the connection
  1104.      *
  1105.      * @param string $to in the form of "<ip_address>:<port>"
  1106.      * @param string $data
  1107.      * @return int
  1108.      * @since 0.9.61
  1109.      */
  1110.     public function Write($to, $data) {
  1111.  
  1112.         return $this->socket->Write_To($to, $data);
  1113.     
  1114.     }
  1115.  
  1116.     /**
  1117.      * Event called on data reception
  1118.      *
  1119.      * @param string $from
  1120.      * @param string $data
  1121.      * @since 0.9.61
  1122.      */
  1123.     public function on_Read($from, $data) {
  1124.  
  1125.     }
  1126.     
  1127.     /**
  1128.      * NS_Datagram_Handler destructor
  1129.      */
  1130.     public function __destruct() {
  1131.  
  1132.         $this->Deactivate();
  1133.  
  1134.     }
  1135.     
  1136. }
  1137.  
  1138.  
  1139. /**
  1140.  * Connection handler class
  1141.  *
  1142.  * @package nanoserv
  1143.  * @subpackage Core
  1144.  * @since 0.9
  1145.  */
  1146. abstract class NS_Connection_Handler extends NS_Handler {
  1147.  
  1148.     /**#@+
  1149.      * Cause of connection failure
  1150.      * @var int
  1151.      */
  1152.     const FAIL_CONNREFUSED = 1;
  1153.     const FAIL_TIMEOUT = 2;
  1154.     const FAIL_CRYPTO = 3;
  1155.     /**#@-*/
  1156.     
  1157.     /**
  1158.      * Send data over the connection
  1159.      *
  1160.      * @param string $data
  1161.      * @param mixed $callback
  1162.      * @return NS_Static_Write_Buffer
  1163.      * @since 0.9
  1164.      */
  1165.     public function Write($data, $callback=false) {
  1166.  
  1167.         return Nanoserv::New_Static_Write_Buffer($this->socket, $data, $callback);
  1168.  
  1169.     }
  1170.  
  1171.     /**
  1172.      * Send open stream over the connection
  1173.      *
  1174.      * @param resource $stream
  1175.      * @param mixed $callback
  1176.      * @return NS_Stream_Write_Buffer
  1177.      * @since 2.1
  1178.      */
  1179.     public function Write_Stream($stream, $callback=false) {
  1180.  
  1181.         return Nanoserv::New_Stream_Write_Buffer($this->socket, $stream, $callback);
  1182.  
  1183.     }
  1184.     
  1185.     /**
  1186.      * Connect
  1187.      *
  1188.      * @param int $timeout timeout in seconds
  1189.      * @since 0.9
  1190.      */
  1191.     public function Connect($timeout=false) {
  1192.  
  1193.         try {
  1194.         
  1195.             $this->socket->Connect($timeout);
  1196.  
  1197.         } catch (NS_Client_Exception $e) {
  1198.  
  1199.             Nanoserv::Free_Connection($this);
  1200.             
  1201.             throw new NS_Client_Exception($e->getMessage(), $e->getCode(), $e->addr, $this);
  1202.         
  1203.         }
  1204.     
  1205.     }
  1206.     
  1207.     /**
  1208.      * Disconnect
  1209.      */
  1210.     public function Disconnect() {
  1211.  
  1212.         $this->socket->Close();
  1213.  
  1214.         Nanoserv::Free_Connection($this);
  1215.     
  1216.     }
  1217.     
  1218.     /**
  1219.      * Event called on received connection
  1220.      * @since 0.9
  1221.      */
  1222.     public function on_Accept() {
  1223.  
  1224.     }
  1225.  
  1226.     /**
  1227.      * Event called on established connection
  1228.      * @since 0.9
  1229.      */
  1230.     public function on_Connect() {
  1231.         
  1232.     }
  1233.  
  1234.     /**
  1235.      * Event called on failed connection
  1236.      *
  1237.      * @param int $failcode see NS_Connection_Handler::FAIL_* constants
  1238.      * @since 0.9
  1239.      */
  1240.     public function on_Connect_Fail($failcode) {
  1241.         
  1242.     }
  1243.     
  1244.     /**
  1245.      * Event called on disconnection
  1246.      * @since 0.9
  1247.      */
  1248.     public function on_Disconnect() {
  1249.  
  1250.     }
  1251.  
  1252.     /**
  1253.      * Event called on data reception
  1254.      *
  1255.      * @param string $data
  1256.      * @since 0.9
  1257.      */
  1258.     public function on_Read($data) {
  1259.  
  1260.     }
  1261.  
  1262.     /**
  1263.      * Event called before forking
  1264.      *
  1265.      * @since 2.0
  1266.      */
  1267.     public function on_Fork_Prepare() {
  1268.  
  1269.     }
  1270.  
  1271.     /**
  1272.      * Event called after forking, both on master and child processes
  1273.      *
  1274.      * @since 2.0
  1275.      */
  1276.     public function on_Fork_Done() {
  1277.  
  1278.     }
  1279.  
  1280. }
  1281.  
  1282.  
  1283. /**
  1284.  * Listener class
  1285.  *
  1286.  * @package nanoserv
  1287.  * @subpackage Core
  1288.  * @since 0.9
  1289.  */
  1290. class NS_Listener {
  1291.  
  1292.     /**
  1293.      * Attached socket
  1294.      * @var NS_Server_Socket
  1295.      */
  1296.     public $socket;
  1297.  
  1298.     /**
  1299.      * Name of the handler class
  1300.      * @var string
  1301.      * @see NS_Connetion_Handler
  1302.      */
  1303.     public $handler_classname;
  1304.  
  1305.     /**
  1306.      * Handler options
  1307.      *
  1308.      * this is passed as the first constructor parameter of each spawned connection handlers
  1309.      *
  1310.      * @var mixed
  1311.      */
  1312.     public $handler_options;
  1313.  
  1314.     /**
  1315.      * Is the listener active ?
  1316.      * @var bool
  1317.      */
  1318.     public $active = false;
  1319.     
  1320.     /**
  1321.      * If set the listener will fork() a new process for each accepted connection
  1322.      * @var bool
  1323.      */
  1324.     public $forking = false;
  1325.     
  1326.     /**
  1327.      * NS_Listener constructor
  1328.      *
  1329.      * @param string $addr
  1330.      * @param string $handler_classname
  1331.      * @param mixed $handler_options
  1332.      */
  1333.     public function __construct($addr, $handler_classname, $handler_options=false, $forking=false) {
  1334.  
  1335.         $this->socket = new NS_Server_Socket($addr);
  1336.         $this->handler_classname = $handler_classname;
  1337.         $this->handler_options = $handler_options;
  1338.         $this->forking = ($forking && is_callable("pcntl_fork"));
  1339.     
  1340.     }
  1341.  
  1342.     /**
  1343.      * Set a stream context option
  1344.      *
  1345.      * @param string $wrapper
  1346.      * @param string $opt
  1347.      * @param mixed $val
  1348.      * @return bool
  1349.      * @since 0.9
  1350.      */
  1351.     public function Set_Option($wrapper, $opt, $val) {
  1352.  
  1353.         return $this->socket->Set_Option($wrapper, $opt, $val);
  1354.     
  1355.     }
  1356.     
  1357.     /**
  1358.      * Sets wether the listener should fork() a new process for each accepted connection
  1359.      *
  1360.      * @param bool $forking
  1361.      * @return bool
  1362.      * @since 0.9
  1363.      */
  1364.     public function Set_Forking($forking=true) {
  1365.  
  1366.         if ($forking && !is_callable("pcntl_fork")) return false;
  1367.         
  1368.         $this->forking = $forking;
  1369.  
  1370.         return true;
  1371.     
  1372.     }
  1373.     
  1374.     /**
  1375.      * Activate the listener
  1376.      *
  1377.      * @return bool
  1378.      * @since 0.9
  1379.      */
  1380.     public function Activate() {
  1381.  
  1382.         try {
  1383.         
  1384.             if ($ret = $this->socket->Listen()) $this->active = true;
  1385.             
  1386.             return $ret;
  1387.     
  1388.         } catch (<a href="../nanoserv/Core/NS_Server_Exception.html">NS_Server_Exception</a> $e) {
  1389.  
  1390.             throw new <a href="../nanoserv/Core/NS_Server_Exception.html">NS_Server_Exception</a>($e->getMessage(), $e->getCode(), $e->addr, $this);
  1391.  
  1392.         }
  1393.     
  1394.     }
  1395.  
  1396.     /**
  1397.      * Deactivate the listener
  1398.      * @since 0.9
  1399.      */
  1400.     public function Deactivate() {
  1401.  
  1402.         $this->socket->Close();
  1403.         $this->active = false;
  1404.     
  1405.     }
  1406.  
  1407.     /**
  1408.      * NS_Listener destructor
  1409.      */
  1410.     public function __destruct() {
  1411.  
  1412.         $this->Deactivate();
  1413.  
  1414.     }
  1415.  
  1416. }
  1417.  
  1418.  
  1419. /**
  1420.  * Shared object class for inter-process communications
  1421.  *
  1422.  * @package nanoserv
  1423.  * @subpackage Core
  1424.  * @since 0.9
  1425.  */
  1426. class <a href="../nanoserv/Core/NS_Shared_Object.html">NS_Shared_Object</a> {
  1427.  
  1428.     /**
  1429.      * caller process pid
  1430.      * @var int
  1431.      */
  1432.     static public $caller_pid;
  1433.     
  1434.     /**
  1435.      * shared object unique identifier
  1436.      * @var int
  1437.      */
  1438.     public $_oid;
  1439.     
  1440.     /**
  1441.      * wrapped object
  1442.      * @var object
  1443.      */
  1444.     private $wrapped;
  1445.  
  1446.     /**
  1447.      * static instance counter
  1448.      * @var int
  1449.      */
  1450.     static public $shared_count = 0;
  1451.     
  1452.     /**
  1453. /**
  1454.      * NS_Shared_Object constructor
  1455.      *
  1456.      * If $o is omited, a new StdClass object will be created and wrapped
  1457.      *
  1458.      * @param object $o 
  1459.      */
  1460.     public function __construct($o=false) {
  1461.  
  1462.         if ($o === false) $o = new StdClass();
  1463.  
  1464.         $this->_oid = ++self::$shared_count;
  1465.         $this->wrapped = $o;
  1466.     
  1467.     }
  1468.     
  1469.     public function __get($k) {
  1470.  
  1471.         if (Nanoserv::$child_process) {
  1472.  
  1473.             return Nanoserv::$master_pipe->Ask_Master(array("oid" => $this->_oid, "action" => "G", "var" => $k));
  1474.             
  1475.         } else {
  1476.         
  1477.             return $this->wrapped->$k;
  1478.  
  1479.         }
  1480.  
  1481.     }
  1482.  
  1483.     public function __set($k, $v) {
  1484.  
  1485.         if (<a href="../nanoserv/Core/Nanoserv.html">Nanoserv</a>::$child_process) {
  1486.  
  1487.             Nanoserv::$master_pipe->Ask_Master(array("oid" => $this->_oid, "action" => "S", "var" => $k, "val" => $v), false);
  1488.         
  1489.         } else {
  1490.         
  1491.             $this->wrapped->$k = $v;
  1492.  
  1493.         }
  1494.     
  1495.     }
  1496.  
  1497.     public function __call($m, $a) {
  1498.  
  1499.         if (<a href="../nanoserv/Core/Nanoserv.html">Nanoserv</a>::$child_process) {
  1500.  
  1501.             return Nanoserv::$master_pipe->Ask_Master(array("oid" => $this->_oid, "action" => "C", "func" => $m, "args" => $a));
  1502.  
  1503.         } else {
  1504.         
  1505.             return call_user_func_array(array($this->wrapped, $m), $a);
  1506.  
  1507.         }
  1508.     
  1509.     }
  1510.  
  1511. }
  1512.  
  1513.  
  1514. /**
  1515.  * Server / multiplexer class
  1516.  *
  1517.  * @package nanoserv
  1518.  * @subpackage Core
  1519.  * @since 0.9
  1520.  */
  1521. final class Nanoserv {
  1522.  
  1523. /**    
  1524.      * nanoserv current version number
  1525.      * @var string 
  1526.      */
  1527.     const VERSION = "2.1.1-dev";
  1528.     
  1529. /**    
  1530.      * Registered listeners
  1531.      * @var array 
  1532.      */
  1533.     static private $listeners = array();
  1534.  
  1535. /**    
  1536.      * Write buffers
  1537.      * @var array 
  1538.      */
  1539.     static private $write_buffers = array();
  1540.     
  1541. /**    
  1542.      * Active connections
  1543.      * @var array 
  1544.      */
  1545.     static private $connections = array();
  1546.     
  1547. /**    
  1548.      * Active datagram handlers
  1549.      * @var array 
  1550.      */
  1551.     static private $dgram_handlers = array();
  1552.     
  1553. /**    
  1554.      * Shared objects
  1555.      * @var array 
  1556.      */
  1557.     static private $shared_objects = array();
  1558.  
  1559. /**    
  1560.      * Forked process pipes
  1561.      * @var array 
  1562.      */
  1563.     static private $forked_pipes = array();
  1564.     
  1565. /**    
  1566.      * Timers
  1567.      * @var array 
  1568.      */
  1569.     static private $timers = array();
  1570.     
  1571. /**    
  1572.      * Timers updated
  1573.      * @var bool 
  1574.      */
  1575.     static private $timers_updated = false;
  1576.     
  1577. /**    
  1578.      * Number of active connection handler processes
  1579.      * @var int 
  1580.      */
  1581.     static public $nb_forked_processes = 0;
  1582.     
  1583. /**    
  1584.      * Maximum number of active children before incoming connections get delayed
  1585.      * @var int 
  1586.      */
  1587.     static public $max_forked_processes = 64;
  1588.     
  1589. /**    
  1590.      * Are we master or child process ?
  1591.      * @var bool 
  1592.      */
  1593.     static public $child_process = false;
  1594.     
  1595. /**    
  1596.      * Forked server handled connection
  1597.      * @var NS_Connection_Handler 
  1598.      */
  1599.     static private $forked_connection;
  1600.     
  1601. /**    
  1602.      * Forked server pipe to the master process
  1603.      * @var NS_Socket 
  1604.      */
  1605.     static public $master_pipe;
  1606.     
  1607. /**    
  1608.      * Class Nanoserv should not be instanciated but used statically
  1609.      */
  1610.     private function __construct() {
  1611.  
  1612.     }
  1613.     
  1614. /**    
  1615.      * Register a new listener
  1616.      *
  1617.      * For consistency New_Listener() will also wrap Nanoserv::New_Datagram_Handler() if the given addr is of type "udp"
  1618.      *
  1619.      * @param string $addr 
  1620.      * @param string $handler_classname 
  1621.      * @param mixed $handler_options 
  1622.      * @return NS_Listener 
  1623.      * @see NS_Listener
  1624.      * @see NS_Datagram_Handler
  1625.      * @since 0.9
  1626.      */
  1627.     static public function New_Listener($addr, $handler_classname, $handler_options=false) {
  1628.  
  1629.         if (strtolower(strtok($addr, ":")) == "udp") {
  1630.  
  1631.             $l = self::New_Datagram_Handler($addr, $handler_classname);
  1632.         
  1633.         } else {
  1634.         
  1635.             $l = new <a href="../nanoserv/Core/NS_Listener.html">NS_Listener</a>($addr, $handler_classname, $handler_options);
  1636.             self::$listeners[] = $l;
  1637.  
  1638.         }
  1639.         
  1640.         return $l;
  1641.  
  1642.     }
  1643.  
  1644. /**    
  1645.      * Deactivate and free a previously registered listener
  1646.      *
  1647.      * For consistency Free_Listener() will also wrap Nanoserv::Free_Datagram_Handler() if the given object is an instance of NS_Datagram_Handler
  1648.      *
  1649.      * @param NS_Listener $l 
  1650.      * @return bool 
  1651.      * @see NS_Listener
  1652.      * @see NS_Datagram_Handler
  1653.      * @since 0.9
  1654.      */
  1655.     static public function Free_Listener($l) {
  1656.  
  1657.         if ($l instanceof NS_Listener) {
  1658.         
  1659.             foreach (self::$listeners as $k => $v) if ($v === $l) {
  1660.  
  1661.                 unset(self::$listeners[$k]);
  1662.                 return true;
  1663.             
  1664.             }
  1665.  
  1666.         } else if ($l instanceof <a href="../nanoserv/Core/NS_Datagram_Handler.html">NS_Datagram_Handler</a>) {
  1667.  
  1668.             return self::Free_Datagram_Handler($l);
  1669.         
  1670.         }
  1671.         
  1672.         return false;
  1673.     
  1674.     }
  1675.  
  1676. /**    
  1677.      * Register a new static write buffer
  1678.      *
  1679.      * This method is used by NS_Connection_Handler::Write() and should not be
  1680.      * called unless you really know what you are doing
  1681.      *
  1682.      * @param NS_Socket $socket 
  1683.      * @param string $data 
  1684.      * @param mixed $callback 
  1685.      * @return NS_Static_Write_Buffer 
  1686.      * @see NS_Connection_Handler::Write()
  1687.      * @since 0.9
  1688.      */
  1689.     static public function New_Static_Write_Buffer(NS_Socket $socket, $data, $callback=false) {
  1690.  
  1691.         $wb = new <a href="../nanoserv/Core/NS_Static_Write_Buffer.html">NS_Static_Write_Buffer</a>($socket, $data, $callback);
  1692.  
  1693.         $wb->Write();
  1694.  
  1695.         if ($wb->Waiting_Data()) {
  1696.         
  1697.             self::$write_buffers[$socket->id][] = $wb;
  1698.  
  1699.         }
  1700.  
  1701.         return $wb;
  1702.     
  1703.     }
  1704.  
  1705. /**    
  1706.      * Register a new static write buffer
  1707.      *
  1708.      * This method is used by NS_Connection_Handler::Write_Stream() and should not be
  1709.      * called unless you really know what you are doing
  1710.      *
  1711.      * @param NS_Socket $socket 
  1712.      * @param resource $stream 
  1713.      * @param mixed $callback 
  1714.      * @return NS_Stream_Write_Buffer 
  1715.      * @see NS_Connection_Handler::Write_Stream()
  1716.      * @since 0.9
  1717.      */
  1718.     static public function New_Stream_Write_Buffer(NS_Socket $socket, $data, $callback=false) {
  1719.  
  1720.         $wb = new NS_Stream_Write_Buffer($socket, $data, $callback);
  1721.  
  1722.         $wb->Write();
  1723.  
  1724.         if ($wb->Waiting_Data()) {
  1725.         
  1726.             self::$write_buffers[$socket->id][] = $wb;
  1727.  
  1728.         }
  1729.  
  1730.         return $wb;
  1731.     
  1732.     }
  1733.     
  1734. /**    
  1735.      * Free a registered write buffer
  1736.      *
  1737.      * @param int $sid socket id
  1738.      * @since 0.9
  1739.      */
  1740.     static public function Free_Write_Buffers($sid) {
  1741.  
  1742.         unset(self::$write_buffers[$sid]);
  1743.     
  1744.     }
  1745.     
  1746. /**    
  1747.      * Register a new outgoing connection
  1748.      * 
  1749.      * @param string $addr 
  1750.      * @param string $handler_classname 
  1751.      * @param mixed $handler_options 
  1752.      * @return NS_Connection_Handler 
  1753.      * @see NS_Connection_Handler
  1754.      * @since 0.9
  1755.      */
  1756.     static public function New_Connection($addr, $handler_classname, $handler_options=false) {
  1757.  
  1758.         $sck = new NS_Client_Socket($addr);
  1759.         $h = new $handler_classname($handler_options);
  1760.  
  1761.         $h->socket = $sck;
  1762.  
  1763.         self::$connections[$sck->id] = $h;
  1764.         
  1765.         return $h;
  1766.     
  1767.     }
  1768.     
  1769. /**    
  1770.      * Free an allocated connection
  1771.      *
  1772.      * @param NS_Connection_Handler $h 
  1773.      * @return bool 
  1774.      * @since 0.9
  1775.      */
  1776.     static public function Free_Connection(NS_Connection_Handler $h) {
  1777.  
  1778.         $so = $h->socket;
  1779.         
  1780.         unset(self::$connections[$so->id]);
  1781.         self::Free_Write_Buffers($so->id);
  1782.  
  1783.         $so->pending_connect = $so->pending_crypto = $so->connected = false;
  1784.  
  1785.         if (self::$child_process && (self::$forked_connection === $h)) exit();
  1786.  
  1787.         return true;
  1788.     
  1789.     }
  1790.  
  1791. /**    
  1792.      * Register a new datagram (udp) handler
  1793.      *
  1794.      * @param string $addr 
  1795.      * @param string $handler_classname 
  1796.      * @return NS_Datagram_Handler 
  1797.      * @see NS_Datagram_Handler
  1798.      * @since 0.9.61
  1799.      */
  1800.     static public function New_Datagram_Handler($addr, $handler_classname) {
  1801.  
  1802.         $h = new $handler_classname($addr);
  1803.         self::$dgram_handlers[$h->socket->id] = $h;
  1804.  
  1805.         return $h;
  1806.     
  1807.     }
  1808.     
  1809. /**    
  1810.      * Deactivate and free a datagram handler
  1811.      *
  1812.      * @param NS_Datagram_Handler $h 
  1813.      * @return bool 
  1814.      * @since 0.9.61
  1815.      */
  1816.     static public function Free_Datagram_Handler(NS_Datagram_Handler $h) {
  1817.  
  1818.         unset(self::$dgram_handlers[$h->socket->id]);
  1819.  
  1820.         return true;
  1821.  
  1822.     }
  1823.     
  1824. /**    
  1825.      * Register a new shared object
  1826.      *
  1827.      * shared objects allow forked processes to use objects stored on the master process
  1828.      * if $o is ommited, a new StdClass empty object is created
  1829.      *
  1830.      * @param object $o 
  1831.      * @return NS_Shared_Object 
  1832.      * @since 0.9
  1833.      */
  1834.     static public function New_Shared_Object($o = false) {
  1835.  
  1836.         $shr = new <a href="../nanoserv/Core/NS_Shared_Object.html">NS_Shared_Object</a>($o);
  1837.  
  1838.         self::$shared_objects[$shr->_oid] = $shr;
  1839.  
  1840.         return $shr;
  1841.     
  1842.     }
  1843.     
  1844. /**    
  1845.      * Free a shared object
  1846.      *
  1847.      * @param NS_Shared_Object $o 
  1848.      * @since 0.9
  1849.      */
  1850.     static public function Free_Shared_Object(NS_Shared_Object $o) {
  1851.  
  1852.         unset(self::$shared_objects[$o->_oid]);
  1853.     
  1854.     }
  1855.     
  1856. /**    
  1857.      * Register a new timer callback
  1858.      *
  1859.      * @param float $delay specified in seconds
  1860.      * @param mixed $callback may be "function" or array($obj, "method")
  1861.      * @return NS_Timer 
  1862.      * @since 0.9
  1863.      */
  1864.     static public function New_Timer($delay, $callback) {
  1865.  
  1866.         $t = new NS_Timer(microtime(true) + $delay, $callback);
  1867.         
  1868.         self::$timers[] = $t;
  1869.         self::$timers_updated = true;
  1870.  
  1871.         return $t;
  1872.     
  1873.     }
  1874.     
  1875. /**    
  1876.      * Clear all existing timers
  1877.      *
  1878.      * @return int number of timers cleared
  1879.      * @since 2.0
  1880.      */
  1881.     static public function Clear_Timers() {
  1882.  
  1883.         $ret = count(self::$timers);
  1884.         
  1885.         self::$timers = array();
  1886.  
  1887.         return $ret;
  1888.     
  1889.     }
  1890.     
  1891. /**    
  1892.      * Get all registered NS_Connection_Handler objects
  1893.      *
  1894.      * Note: connections created by fork()ing listeners can not be retreived this way
  1895.      *
  1896.      * @param bool $include_pending_connect 
  1897.      * @return array 
  1898.      * @since 0.9
  1899.      */
  1900.     static public function Get_Connections($include_pending_connect=false) {
  1901.  
  1902.         $ret = array();
  1903.         
  1904.         foreach (self::$connections as $c) if ($c->socket->connected || $include_pending_connect) $ret[] = $c;
  1905.  
  1906.         return $ret;
  1907.     
  1908.     }
  1909.     
  1910. /**    
  1911.      * Get all registered NS_Listener objects
  1912.      *
  1913.      * @param bool $include_inactive 
  1914.      * @return array 
  1915.      * @since 0.9
  1916.      */
  1917.     static public function Get_Listeners($include_inactive=false) {
  1918.  
  1919.         $ret = array();
  1920.         
  1921.         foreach (self::$listeners as $l) if ($l->active || $include_inactive) $ret[] = $l;
  1922.  
  1923.         return $ret;
  1924.     
  1925.     }
  1926.     
  1927. /**    
  1928.      * Get all registered NS_Timer objects
  1929.      *
  1930.      * @param bool $include_inactive 
  1931.      * @return array 
  1932.      * @since 2.0.1
  1933.      */
  1934.     static public function Get_Timers($include_inactive=false) {
  1935.  
  1936.         $ret = array();
  1937.  
  1938.         foreach (self::$timers as $t) if ($t->active || $include_inactive) $ret[] = $t;
  1939.  
  1940.         return $ret;
  1941.  
  1942.     }
  1943.     
  1944. /**    
  1945.      * Set the maximum number of allowed children processes before delaying incoming connections
  1946.      *
  1947.      * Note: this setting only affect and applies to forking listeners
  1948.      *
  1949.      * @param int $i 
  1950.      * @since 2.0
  1951.      */
  1952.     static public function Set_Max_Children($i) {
  1953.  
  1954.         self::$max_forked_processes = $i;
  1955.  
  1956.     }
  1957.     
  1958. /**    
  1959.      * Flush all write buffers
  1960.      *
  1961.      * @since 2.0
  1962.      */
  1963.     static public function Flush_Write_Buffers() {
  1964.  
  1965.         while (self::$write_buffers) {
  1966.  
  1967.             self::Run(1);
  1968.  
  1969.         }
  1970.     
  1971.     }
  1972.     
  1973. /**    
  1974.      * Fork and setup IPC sockets
  1975.      *
  1976.      * @return int the pid of the created process, 0 if child process
  1977.      * @since 0.9.63
  1978.      */
  1979.     static public function Fork() {
  1980.  
  1981.         if ($has_shared = (<a href="../nanoserv/Core/NS_Shared_Object.html">NS_Shared_Object</a>::$shared_count > 0)) {
  1982.  
  1983.             list($s1, $s2) = NS_IPC_Socket::Pair();
  1984.         
  1985.         }
  1986.         
  1987.         $pid = pcntl_fork();
  1988.  
  1989.         if ($pid === 0) {
  1990.  
  1991.             self::$child_process = true;
  1992.  
  1993.             if ($has_shared) {
  1994.             
  1995.                 self::$master_pipe = $s2;
  1996.  
  1997.             }
  1998.             
  1999.         } else if ($pid > 0) {
  2000.  
  2001.             ++self::$nb_forked_processes;
  2002.  
  2003.             if ($has_shared) { 
  2004.  
  2005.                 $s1->pid = $pid;
  2006.                 self::$forked_pipes[$pid] = $s1;
  2007.             
  2008.             }
  2009.         
  2010.         }
  2011.  
  2012.         return $pid;
  2013.     
  2014.     }
  2015.     
  2016. /**    
  2017.      * Enter main loop
  2018.      *
  2019.      * The <var>$time</var> parameter can have different meanings:
  2020.      * <ul>
  2021.      * <li>int or float > 0 : the main loop will run once and will wait for activity for a maximum of <var>$time</var> seconds</li>
  2022.      * <li>0 : the main loop will run once and will not wait for activity when polling, only handling waiting packets and timers</li>
  2023.      * <li>int or float < 0 : the main loop will run for -<var>$time</var> seconds exactly, whatever may happen</li>
  2024.      * <li>NULL : the main loop will run forever</li>
  2025.      * </ul>
  2026.      *
  2027.      * @param float $time how much time should we run, if omited nanoserv will enter an endless loop
  2028.      * @param array $user_streams if specified, user streams will be polled along with internal streams
  2029.      * @return array the user streams with pending data
  2030.      * @since 0.9
  2031.      */
  2032.     static public function Run($time = NULL, array $user_streams = NULL) {
  2033.  
  2034.         $tmp = 0;
  2035.         
  2036.         $ret = array();
  2037.         
  2038.         if (isset($time)) {
  2039.  
  2040.             if ($time < 0) {
  2041.             
  2042.                 $poll_max_wait = -$time;
  2043.                 $exit_mt = microtime(true) - $time;
  2044.  
  2045.             } else {
  2046.  
  2047.                 $poll_max_wait = $time;
  2048.                 $exit = true;
  2049.             
  2050.             }
  2051.  
  2052.         } else {
  2053.  
  2054.             $poll_max_wait = 60;
  2055.             $exit = false;
  2056.  
  2057.         }
  2058.         
  2059.         do {
  2060.         
  2061.             $t = microtime(true);
  2062.  
  2063.             // Timers
  2064.             if (self::$timers_updated) {
  2065.  
  2066.                 usort(self::$timers, function(NS_Timer $a, NS_Timer $b) { return $a->microtime > $b->microtime; });
  2067.                 self::$timers_updated = false;
  2068.  
  2069.             }
  2070.             
  2071.             $next_timer_md = NULL;
  2072.             
  2073.             if (self::$timers) foreach (self::$timers as $k => $tmr) {
  2074.  
  2075.                 if ($tmr->microtime > $t) {
  2076.                     
  2077.                     $next_timer_md = $tmr->microtime - $t;
  2078.                     break;
  2079.  
  2080.                 } else if ($tmr->active) {
  2081.  
  2082.                     $tmr->Deactivate();
  2083.                     call_user_func($tmr->callback);
  2084.  
  2085.                 }
  2086.  
  2087.                 unset(self::$timers[$k]);
  2088.  
  2089.             }
  2090.             
  2091.             if (self::$timers_updated) {
  2092.  
  2093.                 $t = microtime(true);
  2094.  
  2095.                 usort(self::$timers, function(NS_Timer $a, NS_Timer $b) { return $a->microtime > $b->microtime; });
  2096.                 
  2097.                 foreach (self::$timers as $tmr) {
  2098.  
  2099.                     if ($tmr->microtime > $t) {
  2100.                     
  2101.                         $next_timer_md = $tmr->microtime - $t;
  2102.                         break;
  2103.  
  2104.                     }
  2105.  
  2106.                 }
  2107.                 
  2108.                 self::$timers_updated = false;
  2109.             
  2110.             }
  2111.             
  2112.             // Write buffers to non blocked sockets
  2113.             foreach (self::$write_buffers as $write_buffers) {
  2114.  
  2115.                 if (!$write_buffers || $write_buffers[0]->socket->blocked || !$write_buffers[0]->socket->connected) continue;
  2116.  
  2117.                 foreach ($write_buffers as $wb) {
  2118.  
  2119.                     while ($wb->Waiting_Data() && !$wb->socket->blocked) {
  2120.                             
  2121.                         $wb->Write();
  2122.                         
  2123.                         if (!$wb->Waiting_Data()) {
  2124.                                 
  2125.                             array_shift(self::$write_buffers[$wb->socket->id]);
  2126.                             if (!self::$write_buffers[$wb->socket->id]) self::Free_Write_Buffers($wb->socket->id);
  2127.  
  2128.                             break;
  2129.  
  2130.                         }
  2131.  
  2132.                     }
  2133.                 
  2134.                 }
  2135.  
  2136.             }
  2137.         
  2138.             $handler = $so = $write_buffers = $l = $c = $wbs = $wb = $data = $so = NULL;
  2139.             
  2140.             // Prepare socket arrays
  2141.             $fd_lookup_r = $fd_lookup_w = $rfd = $wfd = $efd = array();
  2142.  
  2143.             foreach (self::$listeners as $l) if (($l->active) && ((!$l->forking) || (self::$nb_forked_processes <= self::$max_forked_processes))) {
  2144.                 
  2145.                 $fd = $l->socket->fd;
  2146.                 $rfd[] = $fd;
  2147.                 $fd_lookup_r[(int)$fd] = $l;
  2148.             
  2149.             }
  2150.  
  2151.             $next_conn_timeout_mt = NULL;
  2152.             
  2153.             foreach (self::$connections as $c) {
  2154.  
  2155.                 $so = $c->socket;
  2156.  
  2157.                 if ($so->pending_crypto) {
  2158.                     
  2159.                     $cr = $so->Enable_Crypto();
  2160.  
  2161.                     if ($cr === true) {
  2162.  
  2163.                         $c->on_Accept();
  2164.                     
  2165.                     } else if ($cr === false) {
  2166.  
  2167.                         $c->on_Connect_Fail(<a href="../nanoserv/Core/NS_Connection_Handler.html">NS_Connection_Handler</a>::FAIL_CRYPTO);
  2168.                         self::Free_Connection($c);
  2169.                     
  2170.                     } else {
  2171.  
  2172.                         $fd = $so->fd;
  2173.                         $rfd[] = $fd;
  2174.                         $fd_lookup_r[(int)$fd] = $c;
  2175.  
  2176.                     }
  2177.  
  2178.                 } else if ($so->connected) {
  2179.                 
  2180.                     if (!$so->block_reads) {
  2181.                     
  2182.                         $fd = $so->fd;
  2183.                         $rfd[] = $fd;
  2184.                         $fd_lookup_r[(int)$fd] = $c;
  2185.  
  2186.                     }
  2187.                 
  2188.                 } else if ($so->connect_timeout < $t) {
  2189.  
  2190.                     $c->on_Connect_Fail(<a href="../nanoserv/Core/NS_Connection_Handler.html">NS_Connection_Handler</a>::FAIL_TIMEOUT);
  2191.                     self::Free_Connection($c);
  2192.                 
  2193.                 } else if ($so->pending_connect) {
  2194.                 
  2195.                     $fd = $so->fd;
  2196.                     $wfd[] = $fd;
  2197.                     $fd_lookup_w[(int)$fd] = $c;
  2198.  
  2199.                     if (!$next_conn_timeout_mt || ($sc->connect_timeout < $next_conn_timeout_mt)) {
  2200.  
  2201.                         $next_conn_timeout_mt = $sc->connect_timeout;
  2202.  
  2203.                     }
  2204.  
  2205.                 }
  2206.                 
  2207.             }
  2208.  
  2209.             if (self::$dgram_handlers) foreach (self::$dgram_handlers as $l) if ($l->active) {
  2210.  
  2211.                 $fd = $l->socket->fd;
  2212.                 $rfd[] = $fd;
  2213.                 $fd_lookup_r[(int)$fd] = $l;
  2214.             
  2215.             }
  2216.             
  2217.             foreach (self::$write_buffers as $wbs) if ($wbs[0]->socket->blocked) {
  2218.  
  2219.                 $fd = $wbs[0]->socket->fd;
  2220.                 $wfd[] = $fd;
  2221.                 $fd_lookup_w[(int)$fd] = self::$connections[$wbs[0]->socket->id];
  2222.             
  2223.             }
  2224.  
  2225.             if (self::$forked_pipes) foreach (self::$forked_pipes as $fp) {
  2226.  
  2227.                 $fd = $fp->fd;
  2228.                 $rfd[] = $fd;
  2229.                 $fd_lookup_r[(int)$fd] = $fp;
  2230.             
  2231.             }
  2232.  
  2233.             if (isset($user_streams)) {
  2234.             
  2235.                 foreach ((array)$user_streams[0] as $tmp_r) $rfd[] = $tmp_r;
  2236.                 foreach ((array)$user_streams[1] as $tmp_w) $wfd[] = $tmp_w;
  2237.             
  2238.             }
  2239.             
  2240.             // Main select
  2241.             
  2242.             $wait_mds = array($poll_max_wait);
  2243.             if (isset($next_timer_md)) $wait_mds[] = $next_timer_md;
  2244.             if (isset($exit_mt)) $wait_mds[] = $exit_mt - $t;
  2245.             if (isset($next_conn_timeout_mt)) $wait_mds[] = $next_conn_timeout_mt - $t;
  2246.                 
  2247.             $wait_md = min($wait_mds);
  2248.                 
  2249.             $tv_sec = (int)$wait_md;
  2250.             $tv_usec = ($wait_md - $tv_sec) * 1000000;
  2251.  
  2252.             if (($rfd || $wfd) && (@stream_select($rfd, $wfd, $efd, $tv_sec, $tv_usec))) {
  2253.  
  2254.                 foreach ($rfd as $act_rfd) {
  2255.  
  2256.                     $handler = $fd_lookup_r[(int)$act_rfd];
  2257.                     $so = $handler->socket;
  2258.  
  2259.                     if ($handler instanceof <a href="../nanoserv/Core/NS_Connection_Handler.html">NS_Connection_Handler</a>) {
  2260.  
  2261.                         if ($so->pending_crypto) {
  2262.                             
  2263.                             $cr = $so->Enable_Crypto();
  2264.  
  2265.                             if ($cr === true) {
  2266.  
  2267.                                 $handler->on_Accept();
  2268.                             
  2269.                             } else if ($cr === false) {
  2270.  
  2271.                                 $handler->on_Connect_Fail(<a href="../nanoserv/Core/NS_Connection_Handler.html">NS_Connection_Handler</a>::FAIL_CRYPTO);
  2272.                                 self::Free_Connection($handler);
  2273.                             
  2274.                             }
  2275.  
  2276.                         } else if (!$so->connected) {
  2277.                             
  2278.                             continue;
  2279.  
  2280.                         }
  2281.                         
  2282.                         $data = $so->Read();
  2283.  
  2284.                         if (($data === "") || ($data === false)) {
  2285.  
  2286.                             if ($so->Eof()) {
  2287.                             
  2288.                                 // Disconnected socket
  2289.                                 
  2290.                                 $handler->on_Disconnect();
  2291.                                 self::Free_Connection($handler);
  2292.  
  2293.                             }
  2294.  
  2295.                         } else {
  2296.  
  2297.                             // Data available
  2298.                             
  2299.                             $handler->on_Read($data);
  2300.                         
  2301.                         }
  2302.                     
  2303.                     } else if ($handler instanceof <a href="../nanoserv/Core/NS_Datagram_Handler.html">NS_Datagram_Handler</a>) {
  2304.                         
  2305.                         $from = "";
  2306.                         $data = $so->Read_From($from);
  2307.  
  2308.                         $handler->on_Read($from, $data);
  2309.                     
  2310.                     } else if ($handler instanceof <a href="../nanoserv/Core/NS_Listener.html">NS_Listener</a>) {
  2311.  
  2312.                         while ($fd = $so->Accept()) {
  2313.  
  2314.                             // New connection accepted
  2315.                             
  2316.                             $sck = new <a href="../nanoserv/Core/NS_Socket.html">NS_Socket</a>($fd, $so->crypto_type);
  2317.  
  2318.                             $hnd = new $handler->handler_classname($handler->handler_options);
  2319.                             $hnd->socket = $sck;
  2320.  
  2321.                             if ($handler->forking) {
  2322.  
  2323.                                 $hnd->on_Fork_Prepare();
  2324.                                 
  2325.                                 if (self::Fork() === 0) {
  2326.  
  2327.                                     $hnd->on_Fork_Done();
  2328.                                     
  2329.                                     self::$write_buffers = self::$listeners = array();
  2330.                                     self::$connections = array($sck->id => $hnd);
  2331.                                     self::$forked_connection = $hnd;
  2332.  
  2333.                                     self::Clear_Timers();
  2334.                                     
  2335.                                     if ($sck->Setup()) {
  2336.                                         
  2337.                                         $hnd->on_Accept();
  2338.  
  2339.                                     }
  2340.  
  2341.                                     $handler = $hnd = $sck = $l = $c = $wbs = $wb = $fd_lookup_r = $fd_lookup_w = false;
  2342.  
  2343.                                     break;
  2344.                                     
  2345.                                 } 
  2346.  
  2347.                                 $hnd->on_Fork_Done();
  2348.  
  2349.                                 if (self::$nb_forked_processes >= self::$max_forked_processes) break;
  2350.                             
  2351.                             } else {
  2352.                             
  2353.                                 self::$connections[$sck->id] = $hnd;
  2354.  
  2355.                                 if ($sck->Setup()) {
  2356.  
  2357.                                     $hnd->on_Accept();
  2358.  
  2359.                                 }
  2360.  
  2361.                             }
  2362.                         
  2363.                             $sck = $hnd = NULL;
  2364.  
  2365.                         }
  2366.                         
  2367.                     } else if ($handler instanceof <a href="../nanoserv/Core/NS_IPC_Socket.html">NS_IPC_Socket</a>) {
  2368.  
  2369.                         while ($ipcm = $handler->Read()) {
  2370.                         
  2371.                             if ((!$ipcq = unserialize($ipcm)) || (!is_object($o = self::$shared_objects[$ipcq["oid"]]))) continue;
  2372.  
  2373.                             switch ($ipcq["action"]) {
  2374.  
  2375.                                 case "G":
  2376.                                 $handler->Write(serialize($o->$ipcq["var"]));
  2377.                                 break;
  2378.  
  2379.                                 case "S":
  2380.                                 $o->$ipcq["var"] = $ipcq["val"];
  2381.                                 break;
  2382.  
  2383.                                 case "C":
  2384.                                 <a href="../nanoserv/Core/NS_Shared_Object.html">NS_Shared_Object</a>::$caller_pid = $handler->pid;
  2385.                                 $handler->Write(serialize(call_user_func_array(array($o, $ipcq["func"]), $ipcq["args"])));
  2386.                                 break;
  2387.                             
  2388.                             }
  2389.  
  2390.                         }
  2391.                     
  2392.                         $o = $ipcq = $ipcm = NULL;
  2393.                         
  2394.                     } else if (!isset($handler)) {
  2395.  
  2396.                         // User stream
  2397.                         $ret[0][] = $act_rfd;
  2398.                     
  2399.                     }
  2400.  
  2401.                 }
  2402.  
  2403.                 foreach ($wfd as $act_wfd) {
  2404.                     
  2405.                     $handler = $fd_lookup_w[$act_wfd];
  2406.                     $so = $handler->socket;
  2407.                     
  2408.                     if (!isset($handler)) {
  2409.  
  2410.                         // User stream
  2411.                         $ret[1][] = $act_wfd;
  2412.                     
  2413.                     } else if ($so->connected) {
  2414.  
  2415.                         // Unblock buffered write
  2416.                         
  2417.                         if ($so->Eof()) {
  2418.  
  2419.                             $handler->on_Disconnect();
  2420.                             self::Free_Connection($handler);
  2421.                         
  2422.                         } else {
  2423.                         
  2424.                             $so->blocked = false;
  2425.  
  2426.                         }
  2427.  
  2428.                     } else if ($so->pending_connect) {
  2429.                     
  2430.                         // Pending connect
  2431.                         if ($so->Eof()) {
  2432.  
  2433.                             $handler->on_Connect_Fail(<a href="../nanoserv/Core/NS_Connection_Handler.html">NS_Connection_Handler</a>::FAIL_CONNREFUSED);
  2434.                             self::Free_Connection($handler);
  2435.                         
  2436.                         } else {
  2437.  
  2438.                             $so->Setup();
  2439.                             $so->connected = true;
  2440.                             $so->pending_connect = false;
  2441.                             $handler->on_Connect();
  2442.  
  2443.                         }
  2444.  
  2445.                     }
  2446.                 
  2447.                 }
  2448.                 
  2449.             }
  2450.  
  2451.             if (self::$nb_forked_processes && !self::$child_process) while ((($pid = pcntl_wait($tmp, WNOHANG)) > 0) && self::$nb_forked_processes--) unset(self::$forked_pipes[$pid]);
  2452.             
  2453.             if ($ret) {
  2454.  
  2455.                 return $ret;
  2456.             
  2457.             } else if (isset($exit_mt)) {
  2458.  
  2459.                 $exit = $exit_mt <= $t;
  2460.             
  2461.             }
  2462.         
  2463.         } while (!$exit);
  2464.     
  2465.     }
  2466.  
  2467. }
  2468.  

Documentation generated on Wed, 30 Nov 2011 22:03:23 +0100 by phpDocumentor 1.4.3