]> jfr.im git - irc/unrealircd/unrealircd-rpc-php.git/blob - lib/Connection.php
Add $rpc->rpc()->add_timer(); and ->del_timer()
[irc/unrealircd/unrealircd-rpc-php.git] / lib / Connection.php
1 <?php
2
3 namespace UnrealIRCd;
4
5 use Exception;
6 use WebSocket;
7
8 class Connection
9 {
10 protected WebSocket\Client $connection;
11
12 public $errno = 0;
13 public $error = NULL;
14
15 public function __construct(string $uri, string $api_login, array $options = null)
16 {
17 $context = $options["context"] ?? stream_context_create();
18
19 if (isset($options["tls_verify"]) && !$options["tls_verify"]) {
20 stream_context_set_option($context, 'ssl', 'verify_peer', false);
21 stream_context_set_option($context, 'ssl', 'verify_peer_name', false);
22 }
23
24 $this->connection = new WebSocket\Client($uri, [
25 'context' => $context,
26 'headers' => [
27 'Authorization' => sprintf('Basic %s', base64_encode($api_login)),
28 ],
29 'timeout' => 10,
30 ]);
31
32 /* Start the connection now */
33 $this->connection->ping();
34 }
35
36 /**
37 * Encode and send a query to the RPC server.
38 *
39 * @note I'm not sure on the response type except that it may be either an object or array.
40 *
41 * @param string $method
42 * @param array|null $params
43 *
44 * @return object|array|bool
45 * @throws Exception
46 */
47 public function query(string $method, array|null $params = null): object|array|bool
48 {
49 $id = random_int(1, 99999);
50
51 $rpc = [
52 "jsonrpc" => "2.0",
53 "method" => $method,
54 "params" => $params,
55 "id" => $id
56 ];
57
58 $json_rpc = json_encode($rpc);
59 $this->connection->text($json_rpc);
60
61 do {
62 $reply = $this->connection->receive();
63
64 $reply = json_decode($reply);
65
66 if (property_exists($reply, 'result')) {
67 /* Possibly we are streaming log events, then ignore this event
68 * NOTE: This does mean that this event is "lost"
69 */
70 if ($id !== $reply->id)
71 continue;
72 $this->errno = 0;
73 $this->error = NULL;
74 return $reply->result;
75 }
76
77 if (property_exists($reply, 'error')) {
78 $this->errno = $reply->error->code;
79 $this->error = $reply->error->message;
80 return false;
81 }
82 } while(0);
83
84 /* This should never happen */
85 throw new Exception('Invalid JSON-RPC response from UnrealIRCd: not an error and not a result.');
86 }
87
88 /**
89 * Grab and/or wait for next event. Used for log streaming.
90 * @note This function will return NULL after a 10 second timeout,
91 * this so the function is not entirely blocking. You can safely
92 * retry the operation if the return value === NULL.
93 *
94 * @return object|array|bool|null
95 * @throws Exception
96 */
97 public function eventloop(): object|array|bool|null
98 {
99 $starttime = microtime(true);
100 try {
101 $reply = $this->connection->receive();
102 } catch (WebSocket\TimeoutException $e) {
103 if (microtime(true) - $starttime < 1)
104 {
105 /* There's some bug in the library: if we
106 * caught the timeout exception once (so
107 * harmless) and then later the server gets
108 * killed or closes the connection otherwise,
109 * then it will again throw WebSocket\TimeoutException
110 * even though it has nothing to do with timeouts.
111 * We detect this by checking if the timeout
112 * took less than 1 second, then we know for sure
113 * that it wasn't really a timeout (since the
114 * timeout is normally 10 seconds).
115 */
116 throw $e;
117 }
118 return NULL;
119 }
120
121 $reply = json_decode($reply);
122
123 if (property_exists($reply, 'result')) {
124 $this->errno = 0;
125 $this->error = NULL;
126 return $reply->result;
127 }
128
129 /* This would be weird */
130 if (property_exists($reply, 'error')) {
131 $this->errno = $reply->error->code;
132 $this->error = $reply->error->message;
133 return false;
134 }
135
136 /* This should never happen */
137 throw new Exception('Invalid JSON-RPC data from UnrealIRCd: not an error and not a result.');
138 }
139
140 public function rpc(): Rpc
141 {
142 return new Rpc($this);
143 }
144
145 public function stats(): Stats
146 {
147 return new Stats($this);
148 }
149
150 public function user(): User
151 {
152 return new User($this);
153 }
154
155 public function channel(): Channel
156 {
157 return new Channel($this);
158 }
159
160 public function serverban(): ServerBan
161 {
162 return new ServerBan($this);
163 }
164
165 public function spamfilter(): Spamfilter
166 {
167 return new Spamfilter($this);
168 }
169
170 public function nameban(): NameBan
171 {
172 return new NameBan($this);
173 }
174
175 public function server(): Server
176 {
177 return new Server($this);
178 }
179
180 public function serverbanexception(): ServerBanException
181 {
182 return new ServerBanException($this);
183 }
184
185 public function log(): Log
186 {
187 return new Log($this);
188 }
189 }