Passed
Pull Request — master (#12)
by
unknown
08:46
created

IrcConnection::handleMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jerodev\PhpIrcClient;
6
7
use Exception;
8
use Jerodev\PhpIrcClient\Helpers\Event;
9
use Jerodev\PhpIrcClient\Helpers\EventHandlerCollection;
10
use Jerodev\PhpIrcClient\Messages\IrcMessage;
11
use Jerodev\PhpIrcClient\Options\ConnectionOptions;
12
use React\EventLoop\LoopInterface;
13
use React\Socket\ConnectionInterface;
14
15
class IrcConnection
16
{
17
    private bool $connected = false;
18
    private ?ConnectionInterface $connection = null;
19
    private EventHandlerCollection $eventHandlerCollection;
20
    private bool $floodProtected;
21
    private LoopInterface $loop;
22
    private IrcMessageParser $messageParser;
23
24
    /** @var array<int, string> */
25
    private array $messageQueue = [];
26
27
    public function __construct(
28
        private string $server,
29
        ?ConnectionOptions $options = null
30
    ) {
31
        $options = $options ?? new ConnectionOptions();
32
33
        $this->eventHandlerCollection = new EventHandlerCollection();
34
        $this->floodProtected = $options->floodProtectionDelay > 0;
35
        $this->loop = \React\EventLoop\Factory::create();
0 ignored issues
show
Deprecated Code introduced by
The function React\EventLoop\Factory::create() has been deprecated: 1.2.0 See Loop::get() instead. ( Ignorable by Annotation )

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

35
        $this->loop = /** @scrutinizer ignore-deprecated */ \React\EventLoop\Factory::create();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
36
        $this->messageParser = new IrcMessageParser();
37
38
        if ($this->floodProtected) {
39
            $this->loop->addPeriodicTimer($options->floodProtectionDelay / 1000, function () {
40
                if ($msg = array_shift($this->messageQueue)) {
41
                    $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

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