Advertisement
aaaaaa123456789

PHP socket server

Oct 17th, 2013
143
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <?php
  2.  
  3. /*
  4.    PHP socket server, by aaaaaa123456789, version 1.0
  5.    October 17, 2013
  6.    
  7.    This code is released to the public domain under the Creative Commons CC0 license.
  8.    You can find the full text at: http://creativecommons.org/publicdomain/zero/1.0/legalcode
  9. */
  10.  
  11. const SERVER_SPLIT_CHAR = 0; // split on the specified character
  12. const SERVER_SPLIT_LENGTH = 1; // split on the specified length
  13. const SERVER_SPLIT_BYTES_AFTER_BYTE = 2; // split after the amount of bytes specified by a certain value (8-bit)
  14. const SERVER_SPLIT_BYTES_AFTER_16LE = 3; // split after the amount of bytes specified by a certain value (16-bit, little endian)
  15. const SERVER_SPLIT_BYTES_AFTER_32LE = 4; // split after the amount of bytes specified by a certain value (32-bit, little endian)
  16. const SERVER_SPLIT_BYTES_AFTER_16BE = 5; // split after the amount of bytes specified by a certain value (16-bit, big endian)
  17. const SERVER_SPLIT_BYTES_AFTER_32BE = 6; // split after the amount of bytes specified by a certain value (32-bit, big endian)
  18. const SERVER_SPLIT_NEWLINES = 7; // split on any newline
  19. const SERVER_SPLIT_NEWLINES_NO_BLANKS = 8; // split on any sequence of newlines (i.e., split on newlines and don't send empty lines)
  20. const SERVER_SPLIT_PACKET = 9; // don't split, just consider the packet received (whatever size) as the appropriate size
  21.  
  22. const SERVER_ERROR_INVALID_PORT = -1;
  23. const SERVER_ERROR_CANNOT_CREATE_SOCKET = -2;
  24. const SERVER_ERROR_CANNOT_READ = -3;
  25. const SERVER_ERROR_LISTENING_SOCKET_KILLED = -4;
  26. const SERVER_ERROR_ALREADY_RUNNING = -5;
  27.  
  28. const SERVER_SEND_ALL = 0; // sends the buffer passed as is, without change
  29. const SERVER_SEND_LINE = -1;  // sends until the first newline, including it
  30. const SERVER_SEND_LINE_NO_NEWLINE = -2; // sends until the first newline, stripping it
  31. const SERVER_SEND_LINE_LF = -3; // sends until the first newline (not included), and adds a \n to it
  32. const SERVER_SEND_LINE_CR = -4; // sends until the first newline (not included), and adds a \r to it
  33. const SERVER_SEND_LINE_CRLF = -5; // sends until the first newline (not included), and adds a \r\n to it
  34.  
  35. class Server {
  36.  
  37.   private $_clients;
  38.   private $_groups;
  39.   private $_delim;
  40.   private $_split;
  41.   private $_callbacks;
  42.   private $_listening;
  43.   private $_running;
  44.   private $_clientstemp;
  45.   private $_packetlimit;
  46.  
  47.   private $_setters = array();
  48.   private $_getters = array(
  49.     'running',
  50.     'clients',
  51.     'groups',
  52.     'newGroupID'
  53.   );
  54.   private $_accessors = array(
  55.     'openCallback',
  56.     'closeCallback',
  57.     'receiveCallback',
  58.     'overflowCallback',
  59.     'delimiter',
  60.     'splitMode',
  61.     'packetLimit'
  62.   );
  63.  
  64.   private function setCallback ($event, $callback) {
  65.     if (isset($this -> _callbacks[$event]))
  66.       $current = $this -> _callbacks[$event];
  67.     else
  68.       $current = false;
  69.     if ($callback === null) return $current;
  70.     if ($callback === false) {
  71.       if ($current !== false) unset($this -> _callbacks[$event]);
  72.       return false;
  73.     }
  74.     if (!is_callable($callback)) return null;
  75.     $this -> _callbacks[$event] = $callback;
  76.     return $callback;
  77.   }
  78.  
  79.   private function createClient ($socket) {
  80.     $newclient = array();
  81.     $newclient['socket'] = $socket;
  82.     $newclient['buffer'] = "";
  83.     foreach ($this -> _groups as $group => $count)
  84.       $newclient[$group] = false;
  85.     do
  86.       $newID = mt_rand();
  87.     while (($newID <= 0) or ($newID > 2147483647));
  88.     while (isset($this -> _clients[$newID])){
  89.       $newID ++;
  90.       if (($newID <= 0) or ($newID > 2147483647)) $newID = 1;
  91.     }
  92.     $this -> _clients[$newID] = $newclient;
  93.     return $newID;
  94.   }
  95.  
  96.   private function removeClient ($client) {
  97.     if (!isset($this -> _clients[$client])) return false;
  98.     $clientdata = $this -> _clients[$client];
  99.     unset($this -> _clients[$client]);
  100.     socket_close($clientdata['socket']);
  101.     foreach ($this -> _groups as $group => $count)
  102.       if ($clientdata[$group]) {
  103.         $this -> _groups[$group] --;
  104.         if (!$this -> _groups[$group])
  105.           $this -> deleteGroup($group);
  106.       }
  107.     if (isset($this -> _callbacks[$client])) unset($this -> _callbacks[$client]);
  108.     return true;
  109.   }
  110.  
  111.   private function getSockets () {
  112.     $result = array($this -> _listening);
  113.     foreach ($this -> _clients as $client)
  114.       $result[] = $client['socket'];
  115.     return $result;
  116.   }
  117.  
  118.   private function closeEverything () {
  119.     foreach ($this -> _clients as $client => $info)
  120.       $this -> closeClient($client);
  121.     if ($this -> _listening !== null) {
  122.       socket_close($this -> _listening);
  123.       $this -> _listening = null;
  124.     }
  125.     $this -> _groups = null;
  126.     $this -> _clients = null;
  127.     $this -> _clientstemp = null;
  128.     $this -> _running = false;
  129.     return true;
  130.   }
  131.  
  132.   private function processIncoming () {
  133.     for ($retries = 3; $retries and (($socket = socket_accept($this -> _listening)) === false); $retries --);
  134.     if ($socket === false) return false;
  135.     $id = $this -> createClient($socket);
  136.     if (isset($this -> _callbacks['open']))
  137.       call_user_func($this -> _callbacks['open'], $id, $this);
  138.     return true;
  139.   }
  140.  
  141.   private function triggerClientOverflow ($client) {
  142.     if (isset($this -> _callbacks['overflow'])) {
  143.       call_user_func($this -> _callbacks['overflow'], $client, $this);
  144.       return;
  145.     }
  146.     $this -> removeClient($client);
  147.     if (isset($this -> _callbacks['close']))
  148.       call_user_func($this -> _callbacks['close'], $client, $this);
  149.   }
  150.  
  151.   private function checkBuffers ($size) {
  152.     $limit = $this -> _packetlimit;
  153.     if ($limit <= 0) return;
  154.     foreach ($this -> _clients as $client => $data) {
  155.       if (!isset($this -> _clients[$client]['buffer'])) continue;
  156.       if (strlen($this -> _clients[$client]['buffer']) > $limit)
  157.         $this -> triggerClientOverflow($client);
  158.     }
  159.   }
  160.  
  161.   private function getValue ($buffer, $position, $bitwidth, $bigendian = false) {
  162.     $count = (int) ($bitwidth / 8);
  163.     if (strlen($buffer) < ($count)) return null;
  164.     $partial = substr($buffer, $position, $count);
  165.     if (strlen($partial) < $count) return null;
  166.     if ($bigendian) return $this -> getValue(strrev($partial), 0, 8 * $count);
  167.     $result = 0;
  168.     while ($partial != "") {
  169.       $char = ord($partial);
  170.       $partial = substr($partial, 1);
  171.       $result <<= 8;
  172.       $result += $char;
  173.     }
  174.     return $result;
  175.   }
  176.  
  177.   private function processReceived ($client, $data) {
  178.     if (isset($this -> _callbacks[$client]))
  179.       call_user_func($this -> _callbacks[$client], $data, $this, $client);
  180.     else if (isset($this -> _callbacks['receive']))
  181.       call_user_func($this -> _callbacks['receive'], $data, $client, $this);
  182.   }
  183.  
  184.   private function splitReceived ($client, $buffer) {
  185.     $mode = $this -> _split;
  186.     if (isset($this -> _clients[$client]['buffer'])) {
  187.       $buffer = $this -> _clients[$client]['buffer'] . $buffer;
  188.       unset($this -> _clients[$client]['buffer']);
  189.     }
  190.     switch ($mode) {
  191.       case SERVER_SPLIT_PACKET:
  192.         $this -> processReceived($client, $buffer);
  193.         return;
  194.       case SERVER_SPLIT_NEWLINES:
  195.       case SERVER_SPLIT_NEWLINES_NO_BLANKS:
  196.         while ((($crpos = strpos($buffer, "\r")) !== false) | (($lfpos = strpos($buffer, "\n")) !== false)) {
  197.           if ($crpos === false) $crpos = 2147483647; // good ol' high value technique
  198.           if ($lfpos === false) $lfpos = 2147483647;
  199.           if ($crpos < $lfpos) {
  200.             $cut = $crpos;
  201.             $skip = ($lfpos == ($crpos + 1)) ? 2 : 1;
  202.           } else {
  203.             $cut = $lfpos;
  204.             $skip = 1;
  205.           }
  206.           $line = substr($buffer, 0, $cut);
  207.           $buffer = substr($buffer, $cut + $skip);
  208.           if (($mode == SERVER_SPLIT_NEWLINES_NO_BLANKS) and ($line == "")) continue;
  209.           $this -> processReceived($client, $line);
  210.         }
  211.         break;
  212.       case SERVER_SPLIT_CHAR:
  213.         while (($split = strpos($buffer, chr($this -> _delim))) !== false) {
  214.           $line = substr($buffer, 0, $split);
  215.           $buffer = substr($buffer, $split + 1);
  216.           $this -> processReceived($client, $line);
  217.         }
  218.         break;
  219.       case SERVER_SPLIT_LENGTH:
  220.         $length = strlen($buffer);
  221.         for ($pos = 0; $pos < $length; $pos += $this -> _delim)
  222.           $this -> processReceived($client, substr($buffer, $pos, $this -> _delim));
  223.         $buffer = substr($buffer, $pos, $length - $pos);
  224.         break;
  225.       case SERVER_SPLIT_BYTES_AFTER_BYTE:
  226.         $bytes = 1;
  227.         $be = false;
  228.         goto split_bytes;
  229.       case SERVER_SPLIT_BYTES_AFTER_16LE:
  230.         $bytes = 2;
  231.         $be = false;
  232.         goto split_bytes;
  233.       case SERVER_SPLIT_BYTES_AFTER_16BE:
  234.         $bytes = 2;
  235.         $be = true;
  236.         goto split_bytes;
  237.       case SERVER_SPLIT_BYTES_AFTER_32LE:
  238.         $bytes = 4;
  239.         $be = false;
  240.         goto split_bytes;
  241.       case SERVER_SPLIT_BYTES_AFTER_32BE:
  242.         $bytes = 4;
  243.         $be = true;
  244.       split_bytes:
  245.         while ($buffer != "") {
  246.           $length = strlen($buffer);
  247.           $base = $this -> _delim + $bytes;
  248.           if ($length < $base) break;
  249.           $offset = $this -> getValue($buffer, $this -> _delim, $bytes * 8, $be);
  250.           $bs = $base + $offset;
  251.           if ($length < $bs) break;
  252.           $block = substr($buffer, 0, $bs);
  253.           $buffer = substr($buffer, $bs);
  254.           $this -> processReceived($client, $block);
  255.         }
  256.     }
  257.     if (($this -> _packetlimit > 0) and (strlen($buffer) > $this -> _packetlimit))
  258.       $this -> triggerClientOverflow($client);
  259.     else if ($buffer != "")
  260.       $this -> _clients[$client]['buffer'] = $buffer;
  261.   }
  262.  
  263.   private function splitAgain () {
  264.     foreach ($this -> _clients as $client => $data) {
  265.       if (!isset($this -> _clients[$client]['buffer'])) continue;
  266.       $this -> splitReceived($client, "");
  267.     }
  268.   }
  269.  
  270.   private function processReceiving ($client) {
  271.     if (!isset($this -> _clients[$client])) return false;
  272.     $socket = $this -> _clients[$client]['socket'];
  273.     $length = (($split = $this -> _split) == SERVER_SPLIT_PACKET) ? $this -> _delim : 2048;
  274.     if ($length <= 0) $length = 2048; // default
  275.     $buffer = str_repeat("\0", $length);
  276.     $length = socket_recv($socket, $buffer, $length, 0);
  277.     if (!$length) return false;
  278.     $buffer = substr($buffer, 0, $length);
  279.     $this -> splitReceived($client, $buffer);
  280.     return true;
  281.   }
  282.  
  283.   private function searchClientBySocket ($socket) {
  284.     foreach ($this -> _clients as $client => $data)
  285.       if ($data['socket'] == $socket)
  286.         return $client;
  287.     return false;
  288.   }
  289.  
  290.   private function closeClient ($client) {
  291.     if (!isset($this -> _clients[$client])) return false;
  292.     if (isset($this -> _callbacks[$client]))
  293.       $callback = $this -> _callbacks[$client];
  294.     else
  295.       $callback = null;
  296.     $data = $this -> _clients[$client];
  297.     $this -> removeClient($client);
  298.     $this -> _clientstemp[$client] = $data;
  299.     if ($callback !== null)
  300.       call_user_func($callback, null, $this, $client);
  301.     else if (isset($this -> _callbacks['close']))
  302.       call_user_func($this -> _callbacks['close'], $client, $this);
  303.     unset($this -> _clientstemp[$client]);
  304.     return true;
  305.   }
  306.  
  307.   private function createGroup ($group) {
  308.     if (!is_int($group)) return false;
  309.     if ($group <= 0) return false;
  310.     if (isset($this -> _groups[$group])) return false;
  311.     $this -> _groups[$group] = 0;
  312.     foreach ($this -> _clients as $client => $data)
  313.       $this -> _clients[$client][$group] = false;
  314.     return true;
  315.   }
  316.  
  317.   private function parseData ($data, $mode) {
  318.     if (is_int($data)) {
  319.       if (($data >= 0) and ($data < 256))
  320.         return ord($data);
  321.       return false;
  322.     } else if (is_array($data)) {
  323.       $result = "";
  324.       foreach ($data as $item) {
  325.         if (!is_int($item)) return false;
  326.         if (($item < 0) or ($item >= 256)) return false;
  327.         $result .= chr($item);
  328.       }
  329.       return $result;
  330.     } else if (!is_string($data))
  331.       return false;
  332.     else {
  333.       if (is_float($mode)) $mode = (int) $mode;
  334.       if (($mode === SERVER_SEND_ALL) or ($mode === null)) return $data;
  335.       if (!is_int($mode)) return false;
  336.       if ($mode < -5) return false;
  337.       if ($mode > 0) {
  338.         $length = strlen($data);
  339.         if ($length == $mode) return $data;
  340.         if ($length > $mode) return substr($data, 0, $mode);
  341.         $data .= str_repeat("\0", $mode - $length);
  342.         return $data;
  343.       }
  344.       $pos1 = strpos($data, "\r");
  345.       $pos2 = strpos($data, "\n");
  346.       if (($pos1 === false) and ($pos2 === false)) {
  347.         $substring = $data;
  348.         $linebreak = "";
  349.       } else {
  350.         if ($pos1 < $pos2) {
  351.           $substring = substr($data, 0, $pos1);
  352.           if ($pos2 == ($pos1 + 1))
  353.             $linebreak = "\r\n";
  354.           else
  355.             $linebreak = "\r";
  356.         } else {
  357.           $substring = substr($data, 0, $pos2);
  358.           $linebreak = "\n";
  359.         }
  360.       }
  361.       switch ($mode) {
  362.         case SERVER_SEND_LINE: return $substring . $linebreak;
  363.         case SERVER_SEND_LINE_NO_NEWLINE: return $substring;
  364.         case SERVER_SEND_LINE_CR: return $substring . "\r";
  365.         case SERVER_SEND_LINE_LF: return $substring . "\n";
  366.         case SERVER_SEND_LINE_CRLF: return $substring . "\r\n";
  367.       }
  368.     }
  369.     return false;
  370.   }
  371.  
  372.   private function sendData ($socket, $data) {
  373.     if ($data === false) return false;
  374.     $remainder = strlen($data);
  375.     $attempts = 0;
  376.     while ($remainder and ($attempts < 3)) {
  377.       $sent = socket_send($socket, $data, $remainder, 0);
  378.       if (!$sent) {
  379.         $attempts ++;
  380.         continue;
  381.       }
  382.       $remainder -= $sent;
  383.       if ($remainder <= 0) return true;
  384.       $data = substr($data, $sent);
  385.     }
  386.     return false;
  387.   }
  388.  
  389.   public function __construct () {
  390.     $this -> _listening = null;
  391.     $this -> _split = SERVER_SPLIT_NEWLINES_NO_BLANKS;
  392.     $this -> _delim = 0;
  393.     $this -> _running = false;
  394.     $this -> _clients = array();
  395.     $this -> _clientstemp = array();
  396.     $this -> _callbacks = array();
  397.     $this -> _groups = array();
  398.     $this -> _packetlimit = 0;
  399.   }
  400.  
  401.   // returns the status of the server (running or not)
  402.   public function running () {
  403.     return $this -> _running;
  404.   }
  405.  
  406.   // opens the server, in the specified port; this function will return when the server dies
  407.   public function open ($port) {
  408.     if ($this -> _running) return SERVER_ERROR_ALREADY_RUNNING;
  409.     if (!is_int($port)) return SERVER_ERROR_INVALID_PORT;
  410.     if (($port < 1) or ($port > 65535)) return SERVER_ERROR_INVALID_PORT;
  411.     $this -> _listening = socket_create_listen($port, SOMAXCONN);
  412.     if ($this -> _listening === false) {
  413.       $this -> _listening = null;
  414.       return SERVER_ERROR_CANNOT_CREATE_SOCKET;
  415.     }
  416.     $this -> _clients = array();
  417.     $this -> _clientstemp = array();
  418.     $this -> _groups = array();
  419.     $this -> _running = true;
  420.     while ($this -> _running) {
  421.       $sockets = $this -> getSockets();
  422.       $w = $e = null;
  423.       $repetitions = 0;
  424.       do
  425.         $status = socket_select($sockets, $w, $e, null);
  426.       while (($status === false) and ((++ $repetitions) < 5));
  427.       if ($status === false) {
  428.         $this -> closeEverything();
  429.         return SERVER_ERROR_CANNOT_READ;
  430.       }
  431.       foreach ($sockets as $socket) {
  432.         if ($socket === $this -> _listening)
  433.           $status = $this -> processIncoming();
  434.         else {
  435.           $client = $this -> searchClientBySocket($socket);
  436.           $status = $this -> processReceiving($client);
  437.         }
  438.         if (!$status) {
  439.           if ($socket == $this -> _listening) {
  440.             $this -> closeEverything();
  441.             return SERVER_ERROR_LISTENING_SOCKET_KILLED;
  442.           } else
  443.             $this -> closeClient($client);
  444.         }
  445.       }
  446.     }
  447.     $this -> closeEverything();
  448.     return 0;
  449.   }
  450.  
  451.   // closes the server, killing all outstanding client connections
  452.   public function close () {
  453.     $x = $this -> _running;
  454.     $this -> _running = false;
  455.     return $x;
  456.   }
  457.  
  458.   // sets or gets the value of the delimiter between packets (a character for SERVER_SPLIT_CHAR, a length for SERVER_SPLIT_LENGTH, or a
  459.  
  460. position)
  461.   public function delimiter ($newdelim = null) {
  462.     if (is_float($newdelim)) $newdelim = (int) $newdelim;
  463.     if (is_int($newdelim) and ($newdelim >= 0))
  464.       $this -> _delim = $newdelim;
  465.     elseif (is_string($newdelim) and (strlen($newdelim) == 1))
  466.       $this -> _delim = ord($newdelim);
  467.     if ($newdelim !== null) $this -> splitAgain();
  468.     return ($this -> _split == SERVER_SPLIT_CHAR) ? @chr($this -> _delim) : $this -> _delim;
  469.   }
  470.  
  471.   // sets or gets the splitting mode according to the defined constants
  472.   public function splitMode ($newmode = null) {
  473.     if (is_int($newmode) and ($newmode >= 0) and ($newmode <= 9)) {
  474.       $this -> _split = $newmode;
  475.       $this -> splitAgain();
  476.     }
  477.     return $this -> _split;
  478.   }
  479.  
  480.   // sets or gets the packet size limit, in bytes (0 = no limit)
  481.   public function packetLimit ($newlimit = null) {
  482.     if (is_int($newlimit) and ($newlimit >= 0)) {
  483.       $this -> _packetlimit = $newlimit;
  484.       if ($newlimit > 0) $this -> checkBuffers($newlimit);
  485.     }
  486.     return $this -> _packetlimit;
  487.   }
  488.  
  489.   // sends a message, according to the send mode, to a certain client or an array of them
  490.   // the message can also be a single byte-long value or an array of them, in which case the mode is ignored
  491.   public function send ($client, $data, $mode = null) {
  492.     if (!$this -> _running) return false;
  493.     if (is_array($client)) {
  494.       $errors = array();
  495.       foreach ($client as $each)
  496.         if ($this -> send($each, $data, $mode) !== true)
  497.           $errors[] = $each;
  498.       if (!count($errors)) return true;
  499.       return $errors;
  500.     }
  501.     if (!isset($this -> _clients[$client])) return false;
  502.     return $this -> sendData($this -> _clients[$client]['socket'], $this -> parseData($data, $mode));
  503.   }
  504.  
  505.   // broadcasts a message to all clients (group = 0), all but a client (group = -client), or those who are in a certain group (group > 0)
  506.   public function broadcast ($data, $group = null, $mode = null) {
  507.     if (!$this -> _running) return false;
  508.     $errors = array();
  509.     if (($group === null) or ($group === 0))
  510.       foreach ($this -> _clients as $client => $info) {
  511.         if ($this -> send($client, $data, $mode) !== true)
  512.           $errors[] = $client;
  513.       }
  514.     elseif (!is_int($group))
  515.       return false;
  516.     elseif ($group < 0)
  517.       foreach ($this -> _clients as $client => $info) {
  518.         if ($client == (- $group)) continue;
  519.         if ($this -> send($client, $data, $mode) !== true)
  520.           $errors[] = $client;
  521.       }
  522.     else if (!isset($this -> _groups[$group]))
  523.       return false;
  524.     else
  525.       foreach ($this -> _clients as $client => $info) {
  526.         if (!$info[$group]) continue;
  527.         if ($this -> send($client, $data, $mode) !== true)
  528.           $errors[] = $client;
  529.       }
  530.     if (!count($errors)) return true;
  531.     return $errors;
  532.   }
  533.  
  534.   // closes the connection with a client (or an array of them), disconnecting it
  535.   public function disconnect ($client) {
  536.     if (!$this -> _running) return false;
  537.     if (is_array($client)) {
  538.       $errors = array();
  539.       foreach ($client as $each)
  540.         if ($this -> disconnect($each) !== true)
  541.           $errors[] = $each;
  542.       if (!count($errors)) return true;
  543.       return $errors;
  544.     }
  545.     $this -> removeClient($client);
  546.     return true;
  547.   }
  548.  
  549.   // closes the connection for multiple clients ($group has the same meaning as for the broadcast() method)
  550.   public function disconnectMultiple ($group = null) {
  551.     if (!$this -> _running) return false;
  552.     $errors = array();
  553.     if (($group === null) or ($group === 0))
  554.       foreach ($this -> _clients as $client => $data) {
  555.         if ($this -> disconnect($client) !== true)
  556.           $errors[] = $client;
  557.       }
  558.     else if (!is_int($group))
  559.       return false;
  560.     else if ($group < 0)
  561.       foreach ($this -> _clients as $client => $data) {
  562.         if (!($client + $group)) continue;
  563.         if ($this -> disconnect($client) !== true)
  564.           $errors[] = $client;
  565.       }
  566.     elseif (!isset($this -> _groups[$group]))
  567.       return false;
  568.     else
  569.       foreach ($this -> _clients as $client => $data) {
  570.         if (!$data[$group]) continue;
  571.         if ($this -> disconnect($client) !== true)
  572.           $errors[] = $client;
  573.       }
  574.     if (!count($errors)) return true;
  575.     return $errors;
  576.   }
  577.  
  578.   // returns all clients currently connected to the server
  579.   public function clients () {
  580.     if (!$this -> _running) return false;
  581.     return array_keys($this -> _clients);
  582.   }
  583.  
  584.   // returns the groups a client has joined
  585.   public function groupsOf ($client) {
  586.     if (!$this -> _running) return null;
  587.     if (isset($this -> _clientstemp[$client])) {
  588.       $joined = array();
  589.       foreach ($this -> _groups as $group => $count)
  590.         if ($this -> _clientstemp[$client][$group])
  591.           $joined[] = $group;
  592.       return $joined;
  593.     }
  594.     if (!isset($this -> _clients[$client])) return null;
  595.     $joined = array();
  596.     foreach ($this -> _groups as $group => $count)
  597.       if ($this -> _clients[$client][$group])
  598.         $joined[] = $group;
  599.     return $joined;
  600.   }
  601.  
  602.   // joins a client (or an array of them) to a group (or an array of them)
  603.   public function joinGroup ($client, $group) {
  604.     if (!$this -> _running) return false;
  605.     if (is_array($client)) {
  606.       $errors = array();
  607.       foreach ($client as $each)
  608.         if ($this -> joinGroup($each, $group) !== true)
  609.           $errors[] = $each;
  610.       if (!count($errors)) return true;
  611.       return $errors;
  612.     }
  613.     if (is_array($group)) {
  614.       $errors = array();
  615.       foreach ($group as $each)
  616.         if ($this -> joinGroup($client, $each) !== true)
  617.           $errors[] = $each;
  618.       if (!count($errors)) return true;
  619.       return $errors;
  620.     }
  621.     if (!isset($this -> _clients[$client])) return false;
  622.     if (isset($this -> _groups[$group])) {
  623.       $this -> _clients[$client][$group] = true;
  624.       $this -> _groups[$group] ++;
  625.       return true;
  626.     }
  627.     $status = $this -> createGroup($group);
  628.     if (!$status) return false;
  629.     $this -> _clients[$client][$group] = true;
  630.     $this -> _groups[$group] = 1;
  631.     return true;
  632.   }
  633.  
  634.   // removes a client (or an array of them) from a group (or an array of them)
  635.   public function leaveGroup ($client, $group) {
  636.     if (!$this -> _running) return false;
  637.     if (is_array($client)) {
  638.       $errors = array();
  639.       foreach ($client as $each)
  640.         if ($this -> leaveGroup($each, $group) !== true)
  641.           $errors[] = $each;
  642.       if (!count($errors)) return true;
  643.       return $errors;
  644.     }
  645.     if (is_array($group)) {
  646.       $errors = array();
  647.       foreach ($group as $each)
  648.         if ($this -> leaveGroup($client, $each) !== true)
  649.           $errors[] = $each;
  650.       if (!count($errors)) return true;
  651.       return $errors;
  652.     }
  653.     if (!isset($this -> _clients[$client])) return false;
  654.     if (!isset($this -> _groups[$group])) return false;
  655.     $this -> _clients[$client][$group] = false;
  656.     $this -> _groups[$group] --;
  657.     if ($this -> _groups[$group] == 0) $this -> deleteGroup($group);
  658.     return true;
  659.   }
  660.  
  661.   // deletes an entire group (or an array of them)
  662.   public function deleteGroup ($group) {
  663.     if (!$this -> _running) return false;
  664.     if (is_array($group)) {
  665.       $errors = array();
  666.       foreach ($group as $each)
  667.         if ($this -> deleteGroup($each) !== true)
  668.           $errors[] = $each;
  669.       if (!count($errors)) return true;
  670.       return $errors;
  671.     }
  672.     if (!isset($this -> _groups[$group])) return false;
  673.     foreach ($this -> _clients as $clientid => $client)
  674.       unset($this -> _clients[$clientid][$group]);
  675.     unset($this -> _groups[$group]);
  676.     return true;
  677.   }
  678.  
  679.   // returns all existing groups
  680.   public function groups () {
  681.     if (!$this -> _running) return null;
  682.     return array_keys($this -> _groups);
  683.   }
  684.  
  685.   // returns an available ID for a new group
  686.   public function newGroupID () {
  687.     if (!$this -> _running) return null;
  688.     do
  689.       $newID = mt_rand();
  690.     while (($newID <= 0) or ($newID > 2147483647));
  691.     while (isset($this -> _groups[$newID])){
  692.       $newID ++;
  693.       if (($newID <= 0) or ($newID > 2147483647)) $newID = 1;
  694.     }
  695.     return $newID;
  696.   }
  697.  
  698.   // sets or gets the callback for when a new connection is opened, the callback receives the new client's ID and the server object itself
  699.   // the callback is removed if it is set to false
  700.   public function openCallback ($callback = null) {
  701.     return $this -> setCallback('open', $callback);
  702.   }
  703.  
  704.   // sets or gets the callback for when a connection is closed, the callback receives the old client's ID and the server object itself
  705.   // the callback is removed if it is set to false
  706.   public function closeCallback ($callback = null) {
  707.     return $this -> setCallback('close', $callback);
  708.   }
  709.  
  710.   // sets or gets the callback for when a message is received, the callback receives the message, the client's ID and the server object
  711.  
  712. itself
  713.   // the callback is removed if it is set to false
  714.   public function receiveCallback ($callback = null) {
  715.     return $this -> setCallback('receive', $callback);
  716.   }
  717.  
  718.   // sets or gets the callback for when a message is received for a certain client (the callback is removed if it is set to false)
  719.   // the callback receives the message (null for a close), the server object itself and the client's ID
  720.   public function clientCallback ($client, $callback = null) {
  721.     if (!$this -> _running) return false;
  722.     if (!isset($this -> _clients[$client])) return false;
  723.     return $this -> setCallback($client, $callback);
  724.   }
  725.  
  726.   // sets or gets the callback for when a connection sends too much data and overflows the packet limit
  727.   // the callback receives the client's ID and the server object itself, and the data that is to be received can no longer be trusted to be
  728.   // properly split. If set to false, a default callback that closes the connection and calls the closing callback is used
  729.   public function overflowCallback ($client, $callback = null) {
  730.     return $this -> setCallback('overflow', $callback);
  731.   }
  732.  
  733.   public function __get ($property) {
  734.     if (!in_array($property, array_merge($this -> _getters, $this -> _accessors)))
  735.       trigger_error("Invalid property invoked: $property", E_USER_ERROR);
  736.     return $this -> $property();
  737.   }
  738.  
  739.   public function __set ($property, $value) {
  740.     if (!in_array($property, array_merge($this -> _setters, $this -> _accessors)))
  741.       trigger_error("Invalid or read-only property invoked: $property", E_USER_ERROR);
  742.     return $this -> $property($value);
  743.   }
  744.  
  745. }
  746.  
  747. ?>
Advertisement
RAW Paste Data Copied
Advertisement