Completed
Push — master ( 57883c...b5af1c )
by Jeroen
01:44
created

IrcClient::connect()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 15
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 0
1
<?php
2
3
namespace Jerodev\PhpIrcClient;
4
5
use Exception;
6
use Jerodev\PhpIrcClient\Helpers\EventHandlerCollection;
7
use Jerodev\PhpIrcClient\Messages\IrcMessage;
8
9
class IrcClient
10
{
11
    /** @var IrcChannel[] */
12
    private $channels;
13
14
    /** @var IrcConnection */
15
    private $connection;
16
17
    /** @var bool */
18
    private $isAuthenticated;
19
20
    /** @var EventHandlerCollection */
21
    private $messageEventHandlers;
22
23
    /** @var IrcUser|null */
24
    private $user;
25
26
    /**
27
     *  Create a new IrcClient instance.
28
     *
29
     *  @param string $server The server address to connect to including the port: `address:port`.
30
     *  @param null|string $username The username to use on the server. Can be set in more detail using `setUser()`.
31
     *  @param null|string|string[] $channels The channels to join on connect.
32
     */
33
    public function __construct(string $server, $username = null, $channels = null)
34
    {
35
        $this->connection = new IrcConnection($server);
36
37
        $this->user = $username === null ? null : new IrcUser($username);
38
        $this->channels = [];
39
        $this->messageEventHandlers = new EventHandlerCollection();
40
41
        if (!empty($channels)) {
42
            if (is_string($channels)) {
43
                $channels = [$channels];
44
            }
45
46
            foreach ($channels as $channel) {
47
                $this->channels[$channel] = new IrcChannel($channel);
48
            }
49
        }
50
    }
51
52
    /**
53
     *  Set the user credentials for the connections.
54
     *  When a connection is already open, this function can be used to change the nickname of the client.
55
     *
56
     *  @param IrcUser|string $user The user information.
57
     */
58
    public function setUser($user): void
59
    {
60
        if (is_string($user)) {
61
            $user = new IrcUser($user);
62
        }
63
64
        if ($this->connection->isConnected() && $this->user->nickname !== $user->nickname) {
65
            $this->send("NICK :$user->nickname");
66
        }
67
68
        $this->user = $user;
69
    }
70
71
    /**
72
     *  Connect to the irc server and start listening for messages.
73
     *
74
     *  @throws Exception if no user information is provided before connecting.
75
     */
76
    public function connect(): void
77
    {
78
        if (!$this->user) {
79
            throw new Exception('A nickname must be set before connecting to an irc server.');
80
        }
81
82
        if ($this->connection->isConnected()) {
83
            return;
84
        }
85
86
        $this->isAuthenticated = false;
87
        $this->connection->onData(function ($msg) {
88
            $this->handleIrcMessage($msg);
89
        });
90
        $this->connection->open();
91
    }
92
93
    /**
94
     *  Close the current connection, if any.
95
     */
96
    public function disconnect(): void
97
    {
98
        $this->connection->close();
99
    }
100
101
    /**
102
     *  Register to an event callback.
103
     *
104
     *  @param string $event The event to register to.
105
     *  @param callable $callback The callback to be execute when the event is emitted.
106
     */
107
    public function on(string $event, callable $callback): void
108
    {
109
        $this->messageEventHandlers->addHandler($event, $callback);
110
    }
111
112
    /**
113
     *  Send a raw command string to the irc server.
114
     *
115
     *  @param string $command The full command string to send.
116
     */
117
    public function send(string $command): void
118
    {
119
        $this->connection->write($command);
120
    }
121
122
    /**
123
     *  Send a message to a channel or user.
124
     *  To send to a channel, make sure the `$target` starts with a `#`.
125
     *
126
     *  @param string $target The channel or user to message.
127
     *  @param string $message The message to send.
128
     */
129
    public function say(string $target, string $message): void
130
    {
131
        $this->send("PRIVMSG $target :$message");
132
    }
133
134
    /**
135
     *  Join an irc channel.
136
     *
137
     *  @param string $channel The name of the channel to join.
138
     */
139
    public function join(string $channel): void
140
    {
141
        $channel = $this->channelName($channel);
142
        $this->send("JOIN $channel");
143
        $this->getChannel($channel);
144
    }
145
146
    /**
147
     *  Part from an irc channel.
148
     *
149
     *  @param string $channel The name of the channel to leave.
150
     */
151
    public function part(string $channel): void
152
    {
153
        $channel = $this->channelName($channel);
154
155
        if (array_key_exists($channel, $this->channels)) {
156
            $this->send("PART $channel");
157
        }
158
    }
159
160
    /**
161
     *  Grab channel information by its name.
162
     *  This function makes sure the channel exists on this client first.
163
     *
164
     *  @param string $channel The name of this channel.
165
     *
166
     *  @return IrcChannel
167
     */
168
    public function getChannel(string $channel): IrcChannel
169
    {
170
        $channel = $this->channelName($channel);
171
172
        if (($this->channels[$channel] ?? null) === null) {
173
            $this->channels[$channel] = new IrcChannel($channel);
174
        }
175
176
        return $this->channels[$channel];
177
    }
178
179
    /**
180
     *  Return a list of all channels.
181
     *
182
     *  @return IrcChannel[]
183
     */
184
    public function getChannels(): array
185
    {
186
        return $this->channels;
187
    }
188
189
    /**
190
     *  Take actions required for received irc messages and invoke the correct event handlers.
191
     *
192
     *  @param IrcMessage $message The message object for the received line.
193
     */
194
    private function handleIrcMessage(IrcMessage $message): void
195
    {
196
        $message->handle($this);
197
198
        if (!$this->isAuthenticated && $this->user) {
199
            $this->send("USER {$this->user->nickname} * * :{$this->user->nickname}");
200
            $this->send("NICK {$this->user->nickname}");
201
            $this->isAuthenticated = true;
202
        }
203
204
        foreach ($message->getEventArgs() as $eventArgs) {
205
            $this->messageEventHandlers->invoke($eventArgs);
206
        }
207
    }
208
209
    /**
210
     *  Make sure all channel names have the same format.
211
     *
212
     *  @param string $channel The name of the channel to format.
213
     *
214
     *  @return string The formatted name.
215
     */
216
    private function channelName(string $channel): string
217
    {
218
        if ($channel[0] !== '#') {
219
            $channel = "#$channel";
220
        }
221
222
        return $channel;
223
    }
224
}
225