Passed
Push — master ( 6e1454...dc47ca )
by Nikita
02:05
created

Gdaemon   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 305
Duplicated Lines 0 %

Test Coverage

Coverage 21%

Importance

Changes 0
Metric Value
eloc 110
dl 0
loc 305
ccs 21
cts 100
cp 0.21
rs 10
c 0
b 0
f 0
wmc 25

11 Methods

Rating   Name   Duplication   Size   Complexity  
A login() 0 24 3
A __destruct() 0 3 1
A connect() 0 33 2
A writeSocket() 0 9 2
A readSocket() 0 13 4
A getSocket() 0 3 1
A disconnect() 0 13 3
A getConnection() 0 9 2
A writeAndReadSocket() 0 7 1
A setConfig() 0 17 5
A __construct() 0 3 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 = 10240;
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
    public function setConfig(array $config)
136
    {
137
        if (empty($config)) {
138
            return $this;
139
        }
140
141
        foreach ($this->configurable as $setting) {
142
            if ( ! isset($config[$setting])) {
143
                continue;
144
            }
145
146
            if (property_exists($this, $setting)) {
147
                $this->$setting = $config[$setting];
148
            }
149
        }
150
151
        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 () {});
173
        $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...
174
            $errno,
175
            $errstr,
176
            30,
177
            STREAM_CLIENT_CONNECT,
178
            $sslContext
179
        );
180
        restore_error_handler();
181
182
        if ( ! $this->_connection) {
183
            throw new RuntimeException('Could not connect to host: '
184
                . $this->host
185
                . ', port:' . $this->port
186
                . "(Error $errno: $errstr)");
187
        }
188
189
        stream_set_blocking($this->_connection, true);
190
    }
191
192
    /**
193
     * @return mixed
194
     */
195
    protected function getConnection()
196
    {
197
        if (! is_resource($this->_connection)) {
198
            $this->disconnect();
199
            $this->connect();
200
            $this->login();
201
        }
202
203
        return $this->_connection;
204
    }
205
206
    /**
207
     * @param $username
208
     * @param $password
209
     * @param $privateKey
210
     * @param $privateKeyPass
211
     */
212 6
    protected function login()
213
    {
214 6
        if ($this->_auth) {
215
            return;
216
        }
217
218 6
        $writeBinn= new BinnList;
219
220 6
        $writeBinn->addInt16(self::DAEMON_SERVER_MODE_AUTH);
221 6
        $writeBinn->addStr($this->username);
222 6
        $writeBinn->addStr($this->password);
223 6
        $writeBinn->addInt16($this->mode);
224
225 6
        $read = $this->writeAndReadSocket($writeBinn->serialize());
226
227 6
        $readBinn = new BinnList;
228 6
        $readBinn->binnOpen($read);
229 6
        $results = $readBinn->unserialize();
230
231 6
        if ($results[0] == self::STATUS_OK) {
232 3
            $this->_auth = true;
233 1
        } else {
234 3
            throw new RuntimeException('Could not login with connection: ' . $this->host . ':' . $this->port
235 3
                . ', username: ' . $this->username);
236
        }
237 3
    }
238
239
    /**
240
     * Disconnect
241
     */
242
    public function disconnect()
243
    {
244
        if (is_resource($this->_socket)) {
245
            socket_close($this->_socket);
246
            $this->_socket = null;
247
        }
248
249
        if (is_resource($this->_connection)) {
250
            fclose($this->_connection);
251
            $this->_connection = null;
252
        }
253
254
        $this->_auth = false;
255
    }
256
257
    /**
258
     * @return bool|null|resource
259
     */
260
    protected function getSocket()
261
    {
262
        return $this->getConnection();
263
    }
264
265
    /**
266
     * @param integer
267
     * @param bool
268
     * @return bool|string
269
     */
270
    protected function readSocket($len = 0, $notTrimEndSymbols = false)
271
    {
272
        if ($len == 0) {
273
            $len = $this->maxBufsize;
274
        }
275
276
        $read = fread($this->getConnection(), $len);
0 ignored issues
show
Bug introduced by
It seems like $this->getConnection() can also be of type false; however, parameter $handle of fread() 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

276
        $read = fread(/** @scrutinizer ignore-type */ $this->getConnection(), $len);
Loading history...
277
278
        if ($read === false) {
279
            throw new RuntimeException('Socket read failed: ' );
280
        }
281
282
        return $notTrimEndSymbols ? $read : substr($read, 0, -4);
283
    }
284
285
    /**
286
     * @param $buffer
287
     * @return int
288
     */
289
    protected function writeSocket($buffer)
290
    {
291
        $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

291
        $result = fwrite(/** @scrutinizer ignore-type */ $this->getConnection(), $buffer);
Loading history...
292
293
        if ($result === false) {
294
            throw new RuntimeException('Socket read failed');
295
        }
296
297
        return $result;
298
    }
299
300
    /**
301
     * Write data to socket and read
302
     *
303
     * @param string $buffer
304
     * @return bool|string
305
     */
306 9
    protected function writeAndReadSocket($buffer)
307
    {
308 9
        $this->writeSocket($buffer . self::SOCKET_MSG_ENDL);
309
310 9
        $read = $this->readSocket();
311
312 9
        return $read;
313
    }
314
}