Passed
Push — master ( 304f84...5965ff )
by Nikita
02:10
created

Gdaemon::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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
    const DAEMON_SERVER_MODE_STATUS = 4;
15
16
    const STATUS_ERROR                = 1;
17
    const STATUS_CRITICAL_ERROR       = 2;
18
    const STATUS_UNKNOWN_COMMAND      = 3;
19
    const STATUS_OK                   = 100;
20
21
    /**
22
     * @var string
23
     */
24
    const SOCKET_MSG_ENDL = "\xFF\xFF\xFF\xFF";
25
26
    /**
27
     * @var resource
28
     */
29
    private $_connection;
30
31
    /**
32
     * @var resource
33
     */
34
    protected $_socket;
35
36
    /**
37
     * @var string
38
     */
39
    protected $host;
40
41
    /**
42
     * @var int
43
     */
44
    protected $port = 31717;
45
46
    /**
47
     * @var string
48
     */
49
    protected $username = '';
50
51
    /**
52
     * @var string
53
     */
54
    protected $password = '';
55
56
    /**
57
     * @var int
58
     */
59
    protected $timeout = 10;
60
61
    /**
62
     * @var string
63
     */
64
    private $serverCertificate;
65
66
    /**
67
     * @var string
68
     */
69
    private $localCertificate;
70
71
    /**
72
     * @var string
73
     */
74
    private $privateKey;
75
76
    /**
77
     * @var string
78
     */
79
    private $privateKeyPass;
80
81
    /**
82
     * @var array
83
     */
84
    protected $configurable = [
85
        'host',
86
        'port',
87
        'username',
88
        'password',
89
        'serverCertificate',
90
        'localCertificate',
91
        'privateKey',
92
        'privateKeyPass',
93
        'timeout',
94
    ];
95
96
    /**
97
     * @var int
98
     */
99
    protected $maxBufsize = 20480;
100
101
    /**
102
     * @var int
103
     */
104
    protected $mode = self::DAEMON_SERVER_MODE_NOAUTH;
105
106
    /**
107
     * @var bool
108
     */
109
    private $_auth = false;
110
111
    /**
112
     * Constructor.
113
     *
114
     * @param array $config
115
     */
116 3
    public function __construct(array $config = [])
117
    {
118 3
        $this->setConfig($config);
119 3
    }
120
121
    /**
122
     * Disconnect on destruction.
123
     */
124 3
    public function __destruct()
125
    {
126 3
        $this->disconnect();
127 3
    }
128
129
    /**
130
     * Set the config.
131
     *
132
     * @param array $config
133
     *
134
     * @return $this
135
     */
136 9
    public function setConfig(array $config)
137
    {
138 9
        if (empty($config)) {
139 3
            return $this;
140
        }
141
142 9
        foreach ($this->configurable as $setting) {
143 9
            if ( ! isset($config[$setting])) {
144 9
                continue;
145
            }
146
147 9
            if (property_exists($this, $setting)) {
148 9
                $this->$setting = $config[$setting];
149 3
            }
150 3
        }
151
152 9
        return $this;
153
    }
154
155
    /**
156
     * Connect to the server.
157
     */
158 96
    public function connect()
159
    {
160 96
        $sslContext = stream_context_create([
161 64
            'ssl' => [
162 32
                'allow_self_signed' => true,
163 32
                'verify_peer'       => true,
164 32
                'verify_peer_name'  => false,
165 96
                'cafile'            => $this->serverCertificate,
166 96
                'local_cert'        => $this->localCertificate,
167 96
                'local_pk'          => $this->privateKey,
168 96
                'passphrase'        => $this->privateKeyPass,
169 64
                'crypto_method'     => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
170 32
            ]
171 32
        ]);
172
173 96
        set_error_handler(function ($err_severity, $err_msg) {
174
            throw new RuntimeException($err_msg);
175 96
        });
176
177 96
        $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...
178 96
            $errno,
179 96
            $errstr,
180 96
            30,
181 96
            STREAM_CLIENT_CONNECT,
182 64
            $sslContext
183 32
        );
184 96
        restore_error_handler();
185
186 96
        if ( ! $this->_connection) {
187
            throw new RuntimeException('Could not connect to host: '
188
                . $this->host
189
                . ', port:' . $this->port
190
                . "(Error $errno: $errstr)");
191
        }
192
193 96
        stream_set_blocking($this->_connection, true);
194
195 96
        $this->login();
196 96
    }
197
198
    /**
199
     * @return mixed
200
     */
201 96
    protected function getConnection()
202
    {
203 96
        if (! is_resource($this->_connection)) {
204 96
            $this->disconnect();
205 96
            $this->connect();
206 32
        }
207
208 96
        return $this->_connection;
209
    }
210
211
    /**
212
     * Disconnect
213
     */
214 99
    public function disconnect()
215
    {
216 99
        if (is_resource($this->_socket)) {
217
            socket_close($this->_socket);
218
            $this->_socket = null;
219
        }
220
221 99
        if (is_resource($this->_connection)) {
222
            fclose($this->_connection);
223
            $this->_connection = null;
224
        }
225
226 99
        $this->_auth = false;
227 99
    }
228
229
    /**
230
     * @return bool|null|resource
231
     */
232
    protected function getSocket()
233
    {
234
        return $this->getConnection();
235
    }
236
237
    /**
238
     * @param integer
239
     * @param bool
240
     * @return bool|string
241
     */
242
    protected function readSocket($len = 0, $notTrimEndSymbols = false)
243
    {
244
        if ($len == 0) {
245
            $len = $this->maxBufsize;
246
        }
247
248
        if (!$notTrimEndSymbols) {
249
            $read = '';
250
            while (!feof($this->_connection))
251
            {
252
                $part = fread($this->_connection, $len);
253
254
                $read .= $part;
255
256
                $offset = (strlen($read) > strlen(self::SOCKET_MSG_ENDL))
257
                    ? strlen($read) - strlen(self::SOCKET_MSG_ENDL)
258
                    : 0;
259
260
                if (strpos($read, self::SOCKET_MSG_ENDL, $offset) !== false) {
261
                    break;
262
                }
263
            }
264
        } else {
265
            $read = stream_get_contents($this->_connection, $len);
266
        }
267
268
        return $read;
269
    }
270
271
    /**
272
     * @param $buffer
273
     * @return int
274
     */
275 96
    protected function writeSocket($buffer)
276
    {
277 96
        if (empty($buffer)) {
278
            throw new RuntimeException('Empty write string');
279
        }
280
        
281 96
        $result = fwrite($this->getConnection(), $buffer);
282
283 96
        if ($result === false) {
0 ignored issues
show
introduced by
The condition $result === false is always false.
Loading history...
284
            throw new RuntimeException('Socket read failed');
285
        }
286
287 96
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type true which is incompatible with the documented return type integer.
Loading history...
288
    }
289
290
    /**
291
     * Write data to socket and read
292
     *
293
     * @param string $buffer
294
     * @return bool|string
295
     */
296 96
    protected function writeAndReadSocket($buffer)
297
    {
298 96
        $this->writeSocket($buffer . self::SOCKET_MSG_ENDL);
299
300 96
        $read = $this->readSocket();
301
302 96
        return $read;
303
    }
304
305
    /**
306
     * @return bool
307
     */
308 96
    private function login()
309
    {
310 96
        if ($this->_auth) {
311
            return $this->_auth;
312
        }
313
314 96
        $writeBinn= new BinnList;
315
316 96
        $writeBinn->addInt16(self::DAEMON_SERVER_MODE_AUTH);
317 96
        $writeBinn->addStr($this->username);
318 96
        $writeBinn->addStr($this->password);
319 96
        $writeBinn->addInt16($this->mode);
320
321 96
        $read = $this->writeAndReadSocket($writeBinn->serialize());
322
323 96
        $readBinn = new BinnList;
324 96
        $readBinn->binnOpen($read);
325 96
        $results = $readBinn->unserialize();
326
327 96
        if ($results[0] == self::STATUS_OK) {
328 96
            $this->_auth = true;
329 32
        } else {
330
            throw new RuntimeException('Could not login with connection: ' . $this->host . ':' . $this->port
331
                . ', username: ' . $this->username);
332
        }
333
334 96
        return $this->_auth;
335
    }
336
}