forked from PocketDock/PocketDockConsole
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathServer.php
More file actions
300 lines (266 loc) · 7.42 KB
/
Server.php
File metadata and controls
300 lines (266 loc) · 7.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
<?php
namespace Wrench;
use Wrench\Util\Configurable;
use \Closure;
use \InvalidArgumentException;
/**
* WebSocket server
*
* The server extends socket, which provides the master socket resource. This
* resource is listened to, and an array of clients managed.
*
* @author Nico Kaiser <[email protected]>
* @author Simon Samtleben <[email protected]>
* @author Dominic Scheirlinck <[email protected]>
*/
class Server extends Configurable
{
/**#@+
* Events
*
* @var string
*/
const EVENT_SOCKET_CONNECT = 'socket_connect';
const EVENT_SOCKET_DISCONNECT = 'socket_disconnect';
const EVENT_HANDSHAKE_REQUEST = 'handshake_request';
const EVENT_HANDSHAKE_SUCCESSFUL = 'handshake_successful';
const EVENT_CLIENT_DATA = 'client_data';
/**#@-*/
/**
* The URI of the server
*
* @var string
*/
protected $uri;
/**
* Options
*
* @var array
*/
protected $options = array();
/**
* A logging callback
*
* The default callback simply prints to stdout. You can pass your own logger
* in the options array. It should take a string message and string priority
* as parameters.
*
* @var Closure
*/
protected $logger;
/**
* Event listeners
*
* Add listeners using the addListener() method.
*
* @var array<string => array<Closure>>
*/
protected $listeners = array();
/**
* Connection manager
*
* @var ConnectionManager
*/
protected $connectionManager;
/**
* Applications
*
* @var array<string => Application>
*/
protected $applications = array();
/**
* Constructor
*
* @param string $uri Websocket URI, e.g. ws://localhost:8000/, path will
* be ignored
* @param array $options (optional) See configure
*/
public function __construct($uri, array $options = array(), $thread)
{
$this->uri = $uri;
$this->thread = $thread;
parent::__construct($options);
$this->log('Server initialized', 'info');
}
/**
* Configure options
*
* Options include
* - socket_class => The socket class to use, defaults to ServerSocket
* - socket_options => An array of socket options
* - logger => Closure($message, $priority = 'info'), used
* for logging
*
* @param array $options
* @return void
*/
protected function configure(array $options)
{
$options = array_merge(array(
'connection_manager_class' => 'Wrench\ConnectionManager',
'connection_manager_options' => array()
), $options);
parent::configure($options);
$this->configureConnectionManager();
$this->configureLogger();
}
/**
* Configures the logger
*
* @return void
*/
protected function configureLogger()
{
// Default logger
if (!isset($this->options['logger'])) {
$this->options['logger'] = function ($message, $priority = 'info') {
printf("%s: %s%s", $priority, $message, PHP_EOL);
};
}
$this->setLogger($this->options['logger']);
}
/**
* Configures the connection manager
*
* @return void
*/
protected function configureConnectionManager()
{
$class = $this->options['connection_manager_class'];
$options = $this->options['connection_manager_options'];
$this->connectionManager = new $class($this, $options);
}
/**
* Gets the connection manager
*
* @return \Wrench\ConnectionManager
*/
public function getConnectionManager()
{
return $this->connectionManager;
}
/**
* @return string
*/
public function getUri()
{
return $this->uri;
}
/**
* Sets a logger
*
* @param Closure $logger
* @return void
*/
public function setLogger($logger)
{
if (!is_callable($logger)) {
throw new \InvalidArgumentException('Logger must be callable');
}
$this->logger = $logger;
}
/**
* Main server loop
*
* @return void This method does not return!
*/
public function run()
{
$this->connectionManager->listen();
while ($this->thread->stop !== true) {
/*
* If there's nothing changed on any of the sockets, the server
* will sleep and other processes will have a change to run. Control
* this behaviour with the timeout options.
*/
$this->connectionManager->selectAndProcess();
/*
* If the application wants to perform periodic operations or queries and push updates to clients based on the result then that logic can be implemented in the 'onUpdate' method.
*/
foreach($this->applications as $application) {
if(method_exists($application, 'onUpdate')) {
$application->onUpdate();
}
}
}
}
/**
* Logs a message to the server log
*
* The default logger simply prints the message to stdout. You can provide
* a logging closure. This is useful, for instance, if you've daemonized
* and closed STDOUT.
*
* @param string $message Message to display.
* @return void
*/
public function log($message, $priority = 'info')
{
call_user_func($this->logger, $message, $priority);
}
/**
* Notifies listeners of an event
*
* @param string $event
* @param array $arguments Event arguments
* @return void
*/
public function notify($event, array $arguments = array())
{
if (!isset($this->listeners[$event])) {
return;
}
foreach ($this->listeners[$event] as $listener) {
call_user_func_array($listener, $arguments);
}
}
/**
* Adds a listener
*
* Provide an event (see the Server::EVENT_* constants) and a callback
* closure. Some arguments may be provided to your callback, such as the
* connection the caused the event.
*
* @param string $event
* @param Closure $callback
* @return void
* @throws InvalidArgumentException
*/
public function addListener($event, $callback)
{
if (!isset($this->listeners[$event])) {
$this->listeners[$event] = array();
}
if (!is_callable($callback)) {
throw new InvalidArgumentException('Invalid listener');
}
$this->listeners[$event][] = $callback;
}
/**
* Returns a server application.
*
* @param string $key Name of application.
* @return Application The application object.
*/
public function getApplication($key)
{
if (empty($key)) {
return false;
}
if (array_key_exists($key, $this->applications)) {
return $this->applications[$key];
}
return false;
}
/**
* Adds a new application object to the application storage.
*
* @param string $key Name of application.
* @param object $application The application object
* @return void
*/
public function registerApplication($key, $application)
{
$this->applications[$key] = $application;
}
}