/server/WebSocket/Server.php

https://github.com/GulDmitry/php-websocket-server · PHP · 254 lines · 136 code · 31 blank · 87 comment · 19 complexity · 0a0910e20990b69e3417560fba845ba1 MD5 · raw file

  1. <?php
  2. namespace WebSocket;
  3. /**
  4. * Simple WebSockets server
  5. *
  6. * @author Nico Kaiser <nico@kaiser.me>
  7. * @author Dmitru Gulyakevich
  8. */
  9. class Server extends Socket
  10. {
  11. // Contain: socket obj (key) => Connection obj (value)
  12. private $_clients = array();
  13. private $_applications = array();
  14. // For group ability
  15. private $_groups = array();
  16. private $_debug = false;
  17. public function __construct($host = 'localhost', $port = 8000, $max = 100)
  18. {
  19. parent::__construct($host, $port, $max);
  20. $this->log('Server created');
  21. }
  22. public function run()
  23. {
  24. while (true) {
  25. // open sockets. Contain 1 master socket
  26. $changed_sockets = $this->_allsockets;
  27. @socket_select($changed_sockets, $write = NULL, $except = NULL, 1);
  28. foreach ($this->_applications as $application) {
  29. $application->onTick();
  30. }
  31. foreach ($changed_sockets as $socket) {
  32. // client connect first time
  33. if ($socket == $this->_master) {
  34. $resource = socket_accept($this->_master);
  35. if ($resource < 0) {
  36. $this->log('Socket error: ' . socket_strerror(socket_last_error($resource)));
  37. continue;
  38. } else {
  39. $client = new Connection($this, $resource);
  40. $this->_clients[$resource] = $client;
  41. $this->_allsockets[] = $resource;
  42. }
  43. // client send message or disconnect
  44. } else {
  45. $client = $this->_clients[$socket];
  46. $bytes = socket_recv($socket, $data, 4096, 0);
  47. //client disconnected
  48. if ($bytes === 0) {
  49. $client->onDisconnect();
  50. unset($client);
  51. } else {
  52. // $data - all data with headers
  53. $client->onData($data);
  54. }
  55. }
  56. }
  57. }
  58. }
  59. /**
  60. * Enable\Disable debug mode
  61. *
  62. * @param bool $flag
  63. */
  64. public function setDebug($flag)
  65. {
  66. $this->_debug = (bool) $flag;
  67. }
  68. /**
  69. * Get debug mode
  70. *
  71. * @return bool
  72. */
  73. public function getDebug()
  74. {
  75. return (bool) $this->_debug;
  76. }
  77. /**
  78. * Get Application by register name.
  79. *
  80. * @param string $key
  81. * @return mixed Application object | false if app not exists
  82. */
  83. public function getApplication($key)
  84. {
  85. if (array_key_exists($key, $this->_applications)) {
  86. return $this->_applications[$key];
  87. } else {
  88. return false;
  89. }
  90. }
  91. /**
  92. * Register application
  93. *
  94. * @param string $key App name
  95. * @param object $application Class.
  96. */
  97. public function registerApplication($key, $application)
  98. {
  99. $this->_applications[$key] = $application;
  100. }
  101. /**
  102. * Console log
  103. *
  104. * @param string $message
  105. * @param string $type
  106. */
  107. public function log($message, $type = 'info')
  108. {
  109. echo date('Y-m-d H:i:s') . ' [' . ($type ? $type : 'error') . '] ' . $message . PHP_EOL;
  110. }
  111. /**
  112. * Sends data from the current connection to all other connections on the _server_
  113. *
  114. * @param string $message
  115. */
  116. public function send($message)
  117. {
  118. foreach ($this->_clients as $v) {
  119. $v->send($message);
  120. }
  121. }
  122. /**
  123. * Sends data from the current connection to all other connections on the _server_,
  124. * excluding the sending connection.
  125. *
  126. * @param Connection $excludeConnection
  127. * @param string $message
  128. */
  129. public function broadcast($excludeConnection, $message)
  130. {
  131. $clientKey = array_search($excludeConnection, $this->_clients);
  132. foreach ($this->_clients as $k => $v) {
  133. if ($k == $clientKey) {
  134. continue;
  135. }
  136. $v->send($message);
  137. }
  138. }
  139. /**
  140. * Delete connection from server
  141. *
  142. * @param Connection $connect
  143. */
  144. public function socketDisconnect($connect)
  145. {
  146. $socketKey = array_search($connect, $this->_clients);
  147. $sKey = array_search($socketKey, $this->_allsockets);
  148. if ($socketKey && $sKey) {
  149. unset($this->_clients[$socketKey]);
  150. unset($this->_allsockets[$sKey]);
  151. }
  152. // remove from group
  153. $groupKey = $connect->getGroup();
  154. if ($groupKey) {
  155. unset($this->_groups[$groupKey][array_search($connect, $this->_groups[$groupKey])]);
  156. }
  157. }
  158. /**
  159. * @param mixed $key
  160. * @param Connection $connection
  161. */
  162. public function addToGroup($key, $connection)
  163. {
  164. $this->_groups[$key][] = $connection;
  165. }
  166. /**
  167. * Get connections by group key
  168. *
  169. * @param mixed $key
  170. * @return mixed Connections in group or null
  171. */
  172. public function getGroupByKey($key)
  173. {
  174. return array_key_exists($key, $this->_groups) ? $this->_groups[$key] : false;
  175. }
  176. /**
  177. * Send to current connection group if $key = false
  178. *
  179. * @param mixed $key Group identifier
  180. * @param string $message
  181. */
  182. public function sendGroup($message, $key)
  183. {
  184. if ($key === false) {
  185. return;
  186. }
  187. foreach ($this->_groups[$key] as $v) {
  188. $v->send($message);
  189. }
  190. }
  191. /**
  192. * Broadcast to current connection group if $key = false
  193. *
  194. * @param string $message
  195. * @param Connection $excludeConnectio
  196. * @param mixed $key Group identifier
  197. */
  198. public function broadcastGroup($message, $excludeConnection, $key)
  199. {
  200. if ($key === false) {
  201. return;
  202. }
  203. $excludeKey = array_search($excludeConnection, $this->_groups[$key]);
  204. foreach ($this->_groups[$key] as $k => $v) {
  205. if ($k == $excludeKey) {
  206. continue;
  207. }
  208. $v->send($message);
  209. }
  210. }
  211. /**
  212. * Return connections and sockets (with master socket) count
  213. *
  214. * @return array ['cliets', 'sockets']
  215. */
  216. public function showSocketsInfoCount()
  217. {
  218. return array(
  219. 'clients' => count($this->_clients),
  220. 'sockets' => count($this->_allsockets)
  221. );
  222. }
  223. }