Passed
Push — master ( 595d53...ee8888 )
by Nikita
02:22
created

Gdaemon   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 326
Duplicated Lines 0 %

Test Coverage

Coverage 29.2%

Importance

Changes 0
Metric Value
eloc 120
dl 0
loc 326
ccs 33
cts 113
cp 0.292
rs 10
c 0
b 0
f 0
wmc 27

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __destruct() 0 3 1
A __construct() 0 3 1
A setConfig() 0 17 5
A login() 0 27 3
A connect() 0 36 2
A getSocket() 0 3 1
A disconnect() 0 13 3
A getConnection() 0 9 2
A writeSocket() 0 9 2
A readSocket() 0 27 6
A writeAndReadSocket() 0 7 1
1
<?php
2
3
namespace Knik\Gameap;
4
5
use Knik\Binn\BinnList;
6
use RuntimeException;
7
8
abstract class Gdaemon
9
{
10
    const DAEMON_SERVER_MODE_NOAUTH = 0;
11
    const DAEMON_SERVER_MODE_AUTH   = 1;
12
    const DAEMON_SERVER_MODE_CMD    = 2;
13
    const DAEMON_SERVER_MODE_FILES  = 3;
14
15
    const STATUS_ERROR                = 1;
16
    const STATUS_CRITICAL_ERROR       = 2;
17
    const STATUS_UNKNOWN_COMMAND      = 3;
18
    const STATUS_OK                   = 100;
19
20
    /**
21
     * @var string
22
     */
23
    const SOCKET_MSG_ENDL = "\xFF\xFF\xFF\xFF";
24
25
    /**
26
     * @var resource
27
     */
28
    private $_connection;
29
30
    /**
31
     * @var resource
32
     */
33
    protected $_socket;
34
35
    /**
36
     * @var string
37
     */
38
    protected $host;
39
40
    /**
41
     * @var int
42
     */
43
    protected $port = 31717;
44
45
    /**
46
     * @var string
47
     */
48
    protected $username = '';
49
50
    /**
51
     * @var string
52
     */
53
    protected $password = '';
54
55
    /**
56
     * @var int
57
     */
58
    protected $timeout = 10;
59
60
    /**
61
     * @var string
62
     */
63
    private $serverCertificate;
64
65
    /**
66
     * @var string
67
     */
68
    private $localCertificate;
69
70
    /**
71
     * @var string
72
     */
73
    private $privateKey;
74
75
    /**
76
     * @var string
77
     */
78
    private $privateKeyPass;
79
80
    /**
81
     * @var array
82
     */
83
    protected $configurable = [
84
        'host',
85
        'port',
86
        'username',
87
        'password',
88
        'serverCertificate',
89
        'localCertificate',
90
        'privateKey',
91
        'privateKeyPass',
92
        'timeout',
93
    ];
94
95
    /**
96
     * @var int
97
     */
98
    protected $maxBufsize = 20480;
99
100
    /**
101
     * @var int
102
     */
103
    protected $mode = self::DAEMON_SERVER_MODE_NOAUTH;
104
105
    /**
106
     * @var bool
107
     */
108
    private $_auth = false;
109
110
    /**
111
     * Constructor.
112
     *
113
     * @param array $config
114
     */
115
    public function __construct(array $config = [])
116
    {
117
        $this->setConfig($config);
118
    }
119
120
    /**
121
     * Disconnect on destruction.
122
     */
123
    public function __destruct()
124
    {
125
        $this->disconnect();
126
    }
127
128
    /**
129
     * Set the config.
130
     *
131
     * @param array $config
132
     *
133
     * @return $this
134
     */
135 6
    public function setConfig(array $config)
136
    {
137 6
        if (empty($config)) {
138 3
            return $this;
139
        }
140
141 6
        foreach ($this->configurable as $setting) {
142 6
            if ( ! isset($config[$setting])) {
143 6
                continue;
144
            }
145
146 6
            if (property_exists($this, $setting)) {
147 6
                $this->$setting = $config[$setting];
148 2
            }
149 2
        }
150
151 6
        return $this;
152
    }
153
154
    /**
155
     * Connect to the server.
156
     */
157
    public function connect()
158
    {
159
        $sslContext = stream_context_create([
160
            'ssl' => [
161
                'allow_self_signed' => true,
162
                'verify_peer'       => true,
163
                'verify_peer_name'  => false,
164
                'cafile'            => $this->serverCertificate,
165
                'local_cert'        => $this->localCertificate,
166
                'local_pk'          => $this->privateKey,
167
                'passphrase'        => $this->privateKeyPass,
168
                'crypto_method'     => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
169
            ]
170
        ]);
171
172
        set_error_handler(function ($err_severity, $err_msg) {
173
            throw new RuntimeException($err_msg);
174
        });
175
176
        $this->_connection = stream_socket_client("tls://{$this->host}:{$this->port}",
0 ignored issues
show
Documentation Bug introduced by
It seems like stream_socket_client('tl...T_CONNECT, $sslContext) can also be of type false. However, the property $_connection is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
177
            $errno,
178
            $errstr,
179
            30,
180
            STREAM_CLIENT_CONNECT,
181
            $sslContext
182
        );
183
        restore_error_handler();
184
185
        if ( ! $this->_connection) {
186
            throw new RuntimeException('Could not connect to host: '
187
                . $this->host
188
                . ', port:' . $this->port
189
                . "(Error $errno: $errstr)");
190
        }
191
192
        stream_set_blocking($this->_connection, true);
193
    }
194
195
    /**
196
     * @return mixed
197
     */
198
    protected function getConnection()
199
    {
200
        if (! is_resource($this->_connection)) {
201
            $this->disconnect();
202
            $this->connect();
203
            $this->login();
204
        }
205
206
        return $this->_connection;
207
    }
208
209
    /**
210
     * @param $username
211
     * @param $password
212
     * @param $privateKey
213
     * @param $privateKeyPass
214
     *
215
     * @return bool
216
     */
217 6
    protected function login()
218
    {
219 6
        if ($this->_auth) {
220 3
            return $this->_auth;
221
        }
222
223 6
        $writeBinn= new BinnList;
224
225 6
        $writeBinn->addInt16(self::DAEMON_SERVER_MODE_AUTH);
226 6
        $writeBinn->addStr($this->username);
227 6
        $writeBinn->addStr($this->password);
228 6
        $writeBinn->addInt16($this->mode);
229
230 6
        $read = $this->writeAndReadSocket($writeBinn->serialize());
231
232 6
        $readBinn = new BinnList;
233 6
        $readBinn->binnOpen($read);
234 6
        $results = $readBinn->unserialize();
235
236 6
        if ($results[0] == self::STATUS_OK) {
237 3
            $this->_auth = true;
238 1
        } else {
239 3
            throw new RuntimeException('Could not login with connection: ' . $this->host . ':' . $this->port
240 3
                . ', username: ' . $this->username);
241
        }
242
243 3
        return $this->_auth;
244
    }
245
246
    /**
247
     * Disconnect
248
     */
249
    public function disconnect()
250
    {
251
        if (is_resource($this->_socket)) {
252
            socket_close($this->_socket);
253
            $this->_socket = null;
254
        }
255
256
        if (is_resource($this->_connection)) {
257
            fclose($this->_connection);
258
            $this->_connection = null;
259
        }
260
261
        $this->_auth = false;
262
    }
263
264
    /**
265
     * @return bool|null|resource
266
     */
267
    protected function getSocket()
268
    {
269
        return $this->getConnection();
270
    }
271
272
    /**
273
     * @param integer
274
     * @param bool
275
     * @return bool|string
276
     */
277
    protected function readSocket($len = 0, $notTrimEndSymbols = false)
278
    {
279
        if ($len == 0) {
280
            $len = $this->maxBufsize;
281
        }
282
283
        if (!$notTrimEndSymbols) {
284
            $read = '';
285
            while (!feof($this->_connection))
286
            {
287
                $part = fread($this->_connection, $len);
288
289
                $read .= $part;
290
291
                $offset = (strlen($read) > strlen(self::SOCKET_MSG_ENDL))
292
                    ? strlen($read) - strlen(self::SOCKET_MSG_ENDL)
293
                    : 0;
294
295
                if (strpos($read, self::SOCKET_MSG_ENDL, $offset) !== false) {
296
                    break;
297
                }
298
            }
299
        } else {
300
            $read = stream_get_contents($this->_connection, $len);
301
        }
302
303
        return $read;
304
    }
305
306
    /**
307
     * @param $buffer
308
     * @return int
309
     */
310
    protected function writeSocket($buffer)
311
    {
312
        $result = fwrite($this->getConnection(), $buffer);
0 ignored issues
show
Bug introduced by
It seems like $this->getConnection() can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

312
        $result = fwrite(/** @scrutinizer ignore-type */ $this->getConnection(), $buffer);
Loading history...
313
314
        if ($result === false) {
315
            throw new RuntimeException('Socket read failed');
316
        }
317
318
        return $result;
319
    }
320
321
    /**
322
     * Write data to socket and read
323
     *
324
     * @param string $buffer
325
     * @return bool|string
326
     */
327 9
    protected function writeAndReadSocket($buffer)
328
    {
329 9
        $this->writeSocket($buffer . self::SOCKET_MSG_ENDL);
330
331 9
        $read = $this->readSocket();
332
333 9
        return $read;
334
    }
335
}