IrcConnection::handleMessage()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Jerodev\PhpIrcClient;
4
5
use Exception;
6
use Jerodev\PhpIrcClient\Helpers\Event;
7
use Jerodev\PhpIrcClient\Helpers\EventHandlerCollection;
8
use Jerodev\PhpIrcClient\Messages\IrcMessage;
9
use Jerodev\PhpIrcClient\Options\ConnectionOptions;
10
use React\EventLoop\LoopInterface;
11
use React\Socket\ConnectionInterface;
12
13
class IrcConnection
14
{
15
    /** @var bool */
16
    private $connected;
17
18
    /** @var ConnectionInterface|null */
19
    private $connection;
20
21
    /** @var EventHandlerCollection */
22
    private $eventHandlerCollection;
23
24
    /** @var bool */
25
    private $floodProtected;
26
27
    /** @var LoopInterface */
28
    private $loop;
29
30
    /** @var IrcMessageParser */
31
    private $messageParser;
32
33
    /** @var string[] */
34
    private $messageQueue;
35
36
    /** @var string */
37
    private $server;
38
39
    public function __construct(string $server, ?ConnectionOptions $options = null)
40
    {
41
        $options = $options ?? new ConnectionOptions();
42
        $this->server = $server;
43
44
        $this->connected = false;
45
        $this->eventHandlerCollection = new EventHandlerCollection();
46
        $this->floodProtected = $options->floodProtectionDelay > 0;
47
        $this->loop = \React\EventLoop\Factory::create();
48
        $this->messageParser = new IrcMessageParser();
49
        $this->messageQueue = [];
50
51
        if ($this->floodProtected) {
52
            $this->loop->addPeriodicTimer($options->floodProtectionDelay / 1000, function () {
53
                if ($msg = array_shift($this->messageQueue)) {
54
                    $this->connection->write($msg);
0 ignored issues
show
Bug introduced by
The method write() does not exist on null. ( Ignorable by Annotation )

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

54
                    $this->connection->/** @scrutinizer ignore-call */ 
55
                                       write($msg);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
55
                }
56
            });
57
        }
58
    }
59
60
    /**
61
     *  Open a connection to the irc server.
62
     */
63
    public function open()
64
    {
65
        if ($this->isConnected()) {
66
            return;
67
        }
68
69
        $tcpConnector = new \React\Socket\TcpConnector($this->loop);
70
        $dnsResolverFactory = new \React\Dns\Resolver\Factory();
71
        $dns = $dnsResolverFactory->createCached('1.1.1.1', $this->loop);
72
        $dnsConnector = new \React\Socket\DnsConnector($tcpConnector, $dns);
73
74
        $dnsConnector->connect($this->server)->then(function (ConnectionInterface $connection) {
75
            $this->connection = $connection;
76
            $this->connected = true;
77
78
            $this->connection->on('data', function ($data) {
79
                foreach ($this->messageParser->parse($data) as $msg) {
80
                    $this->handleMessage($msg);
81
                }
82
            });
83
84
            $this->connection->on('close', function () {
85
                $this->connected = false;
86
            });
87
            $this->connection->on('end', function () {
88
                $this->connected = false;
89
            });
90
        });
91
92
        $this->loop->run();
93
    }
94
95
    /**
96
     *  Close the current irc server connection.
97
     */
98
    public function close(): void
99
    {
100
        if ($this->isConnected()) {
101
            $this->connection->close();
102
            $this->loop->stop();
103
        }
104
    }
105
106
    /**
107
     *  Test if there is an open connection to the irc server.
108
     */
109
    public function isConnected(): bool
110
    {
111
        return $this->connection && $this->connected;
112
    }
113
114
    /**
115
     *  Set a callback for received irc data
116
     *  An IrcMessage object will be passed to the callback.
117
     *
118
     *  @param callable $function The function to be called.
119
     */
120
    public function onData(callable $function): void
121
    {
122
        $this->eventHandlerCollection->addHandler('data', $function);
123
    }
124
125
    /**
126
     * Send a command to the irc server.
127
     *
128
     *  @param string $command The raw irc command.
129
     *
130
     *  @throws Exception if no open connection is available.
131
     */
132
    public function write(string $command): void
133
    {
134
        if (!$this->isConnected()) {
135
            throw new Exception('No open connection was found to write commands to.');
136
        }
137
138
        // Make sure the command ends in a newline character
139
        if (substr($command, -1) !== "\n") {
140
            $command .= "\n";
141
        }
142
143
        if ($this->floodProtected) {
144
            $this->messageQueue[] = $command;
145
        } else {
146
            $this->connection->write($command);
147
        }
148
    }
149
150
    /**
151
     *  Handle a single parsed IrcMessage.
152
     *
153
     *  @param IrcMessage $message The message received from the server.
154
     */
155
    private function handleMessage(IrcMessage $message): void
156
    {
157
        $this->eventHandlerCollection->invoke(new Event('data', [$message]));
158
    }
159
}
160