Passed
Push — master ( dc47ca...795870 )
by Nikita
02:13
created

Gdaemon::setConfig()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 5
nop 1
dl 0
loc 17
ccs 11
cts 11
cp 1
crap 5
rs 9.6111
c 0
b 0
f 0
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 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 () {});
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
     * @return bool
213
     */
214 6
    protected function login()
215
    {
216 6
        if ($this->_auth) {
217 3
            return $this->_auth;
218
        }
219
220 6
        $writeBinn= new BinnList;
221
222 6
        $writeBinn->addInt16(self::DAEMON_SERVER_MODE_AUTH);
223 6
        $writeBinn->addStr($this->username);
224 6
        $writeBinn->addStr($this->password);
225 6
        $writeBinn->addInt16($this->mode);
226
227 6
        $read = $this->writeAndReadSocket($writeBinn->serialize());
228
229 6
        $readBinn = new BinnList;
230 6
        $readBinn->binnOpen($read);
231 6
        $results = $readBinn->unserialize();
232
233 6
        if ($results[0] == self::STATUS_OK) {
234 3
            $this->_auth = true;
235 1
        } else {
236 3
            throw new RuntimeException('Could not login with connection: ' . $this->host . ':' . $this->port
237 3
                . ', username: ' . $this->username);
238
        }
239
240 3
        return $this->_auth;
241
    }
242
243
    /**
244
     * Disconnect
245
     */
246
    public function disconnect()
247
    {
248
        if (is_resource($this->_socket)) {
249
            socket_close($this->_socket);
250
            $this->_socket = null;
251
        }
252
253
        if (is_resource($this->_connection)) {
254
            fclose($this->_connection);
255
            $this->_connection = null;
256
        }
257
258
        $this->_auth = false;
259
    }
260
261
    /**
262
     * @return bool|null|resource
263
     */
264
    protected function getSocket()
265
    {
266
        return $this->getConnection();
267
    }
268
269
    /**
270
     * @param integer
271
     * @param bool
272
     * @return bool|string
273
     */
274
    protected function readSocket($len = 0, $notTrimEndSymbols = false)
275
    {
276
        if ($len == 0) {
277
            $len = $this->maxBufsize;
278
        }
279
280
        $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

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

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