Connection::onCommand()   C
last analyzed

Complexity

Conditions 17
Paths 20

Size

Total Lines 57

Duplication

Lines 8
Ratio 14.04 %

Importance

Changes 0
Metric Value
cc 17
dl 8
loc 57
rs 5.2166
c 0
b 0
f 0
nc 20
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace PHPDaemon\Servers\IRCBouncer;
3
4
use PHPDaemon\Core\Daemon;
5
use PHPDaemon\Core\Timer;
6
use PHPDaemon\Utils\IRC;
7
8
/**
9
 * @package    NetworkServers
10
 * @subpackage IRCBouncer
11
 * @author     Vasily Zorin <[email protected]>
12
 */
13
class Connection extends \PHPDaemon\Network\Connection
14
{
15
16
    /**
17
     * @var string
18
     */
19
    public $EOL = "\r\n";
20
21
    /**
22
     * @var object
23
     */
24
    public $attachedServer;
25
26
    /**
27
     * @var string
28
     */
29
    public $usermask;
30
31
    /**
32
     * @var integer
33
     */
34
    public $latency;
35
36
    /**
37
     * @var integer
38
     */
39
    public $lastPingTS;
40
41
    /**
42
     * @var integer
43
     */
44
    public $timeout = 180;
45
46
    /**
47
     * @var boolean
48
     */
49
    public $protologging = false;
50
51
    /**
52
     * Called when the connection is handshaked (at low-level), and peer is ready to recv. data
53
     * @return void
54
     */
55
    public function onReady()
56
    {
57
        $conn = $this;
58
        $this->keepaliveTimer = setTimeout(function ($timer) use ($conn) {
0 ignored issues
show
Bug introduced by
The property keepaliveTimer does not seem to exist. Did you mean keepalive?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Unused Code introduced by
The parameter $timer is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
59
            $conn->ping();
60
        }, 10e6);
61
    }
62
63
    /**
64
     * @TODO DESCR
65
     */
66
    public function onFinish()
67
    {
68
        if ($this->attachedServer) {
69
            $this->attachedServer->attachedClients->detach($this);
70
        }
71
        Timer::remove($this->keepaliveTimer);
0 ignored issues
show
Bug introduced by
The property keepaliveTimer does not seem to exist. Did you mean keepalive?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
72
        parent::onFinish();
73
    }
74
75
    /**
76
     * @TODO DESCR
77
     */
78
    public function ping()
79
    {
80
        $this->lastPingTS = microtime(true);
0 ignored issues
show
Documentation Bug introduced by
The property $lastPingTS was declared of type integer, but microtime(true) is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
81
        $this->writeln('PING :' . $this->usermask);
82
        Timer::setTimeout($this->keepaliveTimer);
0 ignored issues
show
Bug introduced by
The property keepaliveTimer does not seem to exist. Did you mean keepalive?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
83
    }
84
85
    /**
86
     * @TODO DESCR
87
     * @param  string $from From
88
     * @param  string $cmd Command
89
     * @param  mixed ...$args Arguments
90
     */
91
    public function command($from, $cmd)
92
    {
93
        if ($from === null) {
94
            $from = $this->pool->config->servername->value;
95
        }
96
        $cmd = IRC::getCodeByCommand($cmd);
97
        $line = ':' . $from . ' ' . $cmd;
98 View Code Duplication
        for ($i = 2, $s = func_num_args(); $i < $s; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
99
            $arg = func_get_arg($i);
100
            if (($i + 1 === $s) && (mb_orig_strpos($arg, "\x20") !== false)) {
101
                $line .= ' :';
102
            } else {
103
                $line .= ' ';
104
            }
105
            $line .= $arg;
106
        }
107
        $this->writeln($line);
108
        if ($this->pool->protologging && $cmd !== 'PONG') {
109
            Daemon::log('=>=>=>=> ' . json_encode($line));
110
        }
111
    }
112
113
    /**
114
     * @TODO
115
     * @param  string $from From
116
     * @param  string $cmd Command
117
     * @param  array $args Arguments
118
     */
119
    public function commandArr($from, $cmd, $args)
120
    {
121
        if ($from === null) {
122
            $from = $this->pool->config->servername->value;
123
        }
124
        if (is_string($args)) {
125
            Daemon::log(get_class($this) . '->commandArr: args is string');
126
            return;
127
        }
128
        $cmd = IRC::getCodeByCommand($cmd);
129
        $line = ':' . $from . ' ' . $cmd;
130 View Code Duplication
        for ($i = 0, $s = sizeof($args); $i < $s; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
131
            if (($i + 1 === $s) && (mb_orig_strpos($args[$i], "\x20") !== false)) {
132
                $line .= ' :';
133
            } else {
134
                $line .= ' ';
135
            }
136
            $line .= $args[$i];
137
        }
138
        $this->writeln($line);
139
        if ($this->pool->protologging && $cmd !== 'PONG') {
140
            Daemon::log('=>=>=>=> ' . json_encode($line));
141
        }
142
    }
143
144
    /**
145
     * @TODO DESCR
146
     */
147
    public function detach()
148
    {
149
        if ($this->attachedServer) {
150
            $this->attachedServer->attachedClients->detach($this);
151
            $this->attachedServer = null;
152
        }
153
    }
154
155
    /**
156
     * @TODO DESCR
157
     */
158
    public function attachTo()
159
    {
160
        if ($this->pool->conn) {
161
            $this->attachedServer = $this->pool->conn;
162
            $this->attachedServer->attachedClients->attach($this);
163
        } else {
164
            return;
165
        }
166
        $this->msgFromBNC('Attached to ' . $this->attachedServer->url);
167
        $this->usermask = $this->attachedServer->nick . '!' . $this->attachedServer->user . '@' . $this->pool->config->servername->value;
168
        $this->command(null, 'RPL_WELCOME', $this->attachedServer->nick,
169
            'Welcome to phpDaemon bouncer -- ' . $this->pool->config->servername->value);
170
        foreach ($this->attachedServer->channels as $chan) {
171
            $this->exportChannel($chan);
172
        }
173
    }
174
175
    /**
176
     * @TODO DESCR
177
     * @param object $chan
178
     */
179
    public function exportChannel($chan)
180
    {
181
        $this->command($this->usermask, 'JOIN', $chan->name);
182
        $this->command($this->usermask, 'RPL_TOPIC', $chan->irc->nick, $chan->name, $chan->topic);
183
        $names = $chan->exportNicksArray();
184
        $packet = '';
185
        $maxlen = 510 - 7 - mb_orig_strlen($this->pool->config->servername->value) - $chan->irc->nick - 1;
186
        for ($i = 0, $s = sizeof($names); $i < $s; ++$i) {
187
            $packet .= ($packet !== '' ? ' ' : '') . $names[$i];
188
            if (!isset($names[$i + 1]) || (mb_orig_strlen($packet) + mb_orig_strlen($names[$i + 1]) + 1 > $maxlen)) {
189
                $this->command(null, 'RPL_NAMREPLY', $chan->irc->nick, $chan->type, $chan->name, $packet);
190
                $packet = '';
191
            }
192
        }
193
        $this->command(null, 'RPL_ENDOFNAMES', $chan->irc->nick, $chan->name, 'End of /NAMES list');
194
    }
195
196
    /**
197
     * @TODO DESCR
198
     * @param string $cmd Command
199
     * @param array $args Arguments
200
     */
201
    public function onCommand($cmd, $args)
202
    {
203
        if ($cmd === 'USER') {
204
            //list ($nick) = $args;
205
            $this->attachTo();
206
            return;
207
        } elseif ($cmd === 'QUIT') {
208
            $this->finish();
209
            return;
210
        } elseif ($cmd === 'PING') {
211
            $this->writeln(isset($args[0]) ? 'PONG :' . $args[0] : 'PONG');
212
            return;
213 View Code Duplication
        } elseif ($cmd === 'PONG') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
214
            if ($this->lastPingTS) {
215
                $this->latency = microtime(true) - $this->lastPingTS;
0 ignored issues
show
Documentation Bug introduced by
The property $latency was declared of type integer, but microtime(true) - $this->lastPingTS is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
216
                $this->lastPingTS = null;
217
                $this->event('lantency');
218
            }
219
            return;
220
        } elseif ($cmd === 'NICK') {
221
            return;
222
        } elseif ($cmd === 'PRIVMSG') {
223
            list($target, $msg) = $args;
224
            if ($target === '$') {
225
                if (preg_match('~^\s*(NICK\s+\S+|DETACH|ATTACH|BYE)\s*$~i', $msg, $m)) {
226
                    $clientCmd = strtoupper($m[1]);
227
                    if ($clientCmd === 'NICK') {
228
                    } elseif ($clientCmd === 'DETACH') {
229
                        $this->detach();
230
                        $this->msgFromBNC('Detached.');
231
                    } elseif ($clientCmd === 'ATTACH') {
232
                        $this->attachTo();
233
                    } elseif ($clientCmd === 'BYE') {
234
                        $this->detach();
235
                        $this->msgFromBNC('Bye-bye.');
236
                        $this->finish();
237
                    }
238
                } else {
239
                    $this->msgFromBNC('Unknown command: ' . $msg);
240
                }
241
                return;
242
            }
243
            $this->pool->messages->insert([
244
                'from' => $this->usermask,
245
                'to' => $target,
246
                'body' => $msg,
247
                'ts' => microtime(true),
248
                'dir' => 'o',
249
            ]);
250
        }
251
        if ($this->attachedServer) {
252
            $this->attachedServer->commandArr($cmd, $args);
253
        }
254
        if ($this->protologging) {
255
            Daemon::$process->log('<=<=<=< ' . $cmd . ': ' . json_encode($args));
256
        }
257
    }
258
259
    /**
260
     * @TODO DESCR
261
     * @param string $msg
262
     */
263
    public function msgFromBNC($msg)
264
    {
265
        if ($this->usermask === null) {
266
            return;
267
        }
268
        $this->command('$!@' . $this->pool->config->servername->value, 'PRIVMSG', $this->usermask, $msg);
269
    }
270
271
    /**
272
     * Called when new data received
273
     * @return void
274
     */
275
    public function onRead()
276
    {
277
        Timer::setTimeout($this->keepaliveTimer);
0 ignored issues
show
Bug introduced by
The property keepaliveTimer does not seem to exist. Did you mean keepalive?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
278
        while (($line = $this->readline()) !== null) {
279
            if ($line === '') {
280
                continue;
281
            }
282
            if (mb_orig_strlen($line) > 512) {
283
                Daemon::$process->log('IRCBouncerConnection error: buffer overflow.');
284
                $this->finish();
285
                return;
286
            }
287
            $line = mb_orig_substr($line, 0, -mb_orig_strlen($this->EOL));
288
            $p = mb_orig_strpos($line, ':', 1);
289
            $max = $p ? substr_count($line, "\x20", 0, $p) + 1 : 18;
290
            $e = explode("\x20", $line, $max);
291
            $i = 0;
292
            $cmd = $e[$i++];
293
            $args = [];
294
295 View Code Duplication
            for ($s = min(sizeof($e), 14); $i < $s; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
296
                if ($e[$i][0] === ':') {
297
                    $args[] = mb_orig_substr($e[$i], 1);
298
                    break;
299
                }
300
                $args[] = $e[$i];
301
            }
302
303 View Code Duplication
            if (ctype_digit($cmd)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
304
                $code = (int)$cmd;
305
                $cmd = isset(IRC::$codes[$code]) ? IRC::$codes[$code] : 'UNKNOWN-' . $code;
306
            }
307
            $this->onCommand($cmd, $args);
308
        }
309
        if (mb_orig_strlen($this->buf) > 512) {
0 ignored issues
show
Documentation introduced by
The property buf does not exist on object<PHPDaemon\Servers\IRCBouncer\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
310
            Daemon::$process->log('IRCClientConnection error: buffer overflow.');
311
            $this->finish();
312
        }
313
    }
314
}
315