Completed
Push — master ( 4b839d...94a773 )
by Valentin
02:14
created

src/Server/Connection.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * This file is a part of Woketo package.
5
 *
6
 * (c) Nekland <[email protected]>
7
 *
8
 * For the full license, take a look to the LICENSE file
9
 * on the root directory of this project
10
 */
11
12
namespace Nekland\Woketo\Server;
13
14
use Nekland\Woketo\Exception\RuntimeException;
15
use Nekland\Woketo\Exception\WebsocketException;
16
use Nekland\Woketo\Http\Request;
17
use Nekland\Woketo\Http\Response;
18
use Nekland\Woketo\Message\MessageHandlerInterface;
19
use Nekland\Woketo\Rfc6455\Frame;
20
use Nekland\Woketo\Rfc6455\Message;
21
use Nekland\Woketo\Rfc6455\MessageProcessor;
22
use Nekland\Woketo\Rfc6455\ServerHandshake;
23
use React\EventLoop\LoopInterface;
24
use React\EventLoop\Timer\TimerInterface;
25
use React\Socket\ConnectionInterface;
26
27
class Connection
28
{
29
    /**
30
     * 5 seconds
31
     */
32
    const DEFAULT_TIMEOUT = 5;
33
34
    /**
35
     * @var ConnectionInterface
36
     */
37
    private $socketStream;
38
39
    /**
40
     * @var MessageHandlerInterface
41
     */
42
    private $handler;
43
44
    /**
45
     * @var bool
46
     */
47
    private $handshakeDone;
48
49
    /**
50
     * @var ServerHandshake
51
     */
52
    private $handshake;
53
54
    /**
55
     * @var Message
56
     */
57
    private $currentMessage;
58
59
    /**
60
     * @var MessageProcessor
61
     */
62
    private $messageProcessor;
63
64
    /**
65
     * @var LoopInterface
66
     */
67
    private $loop;
68
69
    /**
70
     * @var TimerInterface
71
     */
72
    private $timeout;
73
    
74 2
    public function __construct(ConnectionInterface $socketStream, MessageHandlerInterface $messageHandler, LoopInterface $loop, MessageProcessor $messageProcessor, ServerHandshake $handshake = null)
75
    {
76 2
        $this->socketStream = $socketStream;
77 2
        $this->initListeners();
78 2
        $this->handler = $messageHandler;
79 2
        $this->handshake = $handshake ?: new ServerHandshake;
80 2
        $this->loop = $loop;
81 2
        $this->messageProcessor = $messageProcessor;
82 2
    }
83
84 2
    private function initListeners()
85
    {
86
        $this->socketStream->on('data', function ($data) {
87 2
            $this->processData($data);
88 2
        });
89 2
        $this->socketStream->on('error', [$this, 'error']);
90 2
    }
91
92 2
    private function processData($data)
93
    {
94
        try {
95 2
            if (!$this->handshakeDone) {
96 2
                $this->processHandcheck($data);
97
            } else {
98 2
                $this->processMessage($data);
99
            }
100
101 2
            return;
102
        } catch (WebsocketException $e) {
103
            $this->messageProcessor->close($this->socketStream);
104
            $this->handler->onError($e, $this);
105
        }
106
    }
107
108
    /**
109
     * This method build a message and buffer data in case of incomplete data.
110
     *
111
     * @param string $data
112
     */
113 2
    protected function processMessage($data)
114
    {
115
        // It may be a timeout going (we were waiting for data), let's clear it.
116 2
        if ($this->timeout !== null) {
117
            $this->timeout->cancel();
118
            $this->timeout = null;
119
        }
120
121 2
        foreach ($this->messageProcessor->onData($data, $this->socketStream, $this->currentMessage) as $message) {
122 2
            $this->currentMessage = $message;
123 2
            if ($this->currentMessage->isComplete()) {
124
                // Sending the message through the woketo API.
125 2
                switch($this->currentMessage->getOpcode()) {
126 2
                    case Frame::OP_TEXT:
127 1
                        $this->handler->onMessage($this->currentMessage->getContent(), $this);
128 1
                        break;
129 1
                    case Frame::OP_BINARY:
130 1
                        $this->handler->onBinary($this->currentMessage->getContent(), $this);
131 1
                        break;
132
                }
133 2
                $this->currentMessage = null;
134
135
            } else {
136
                // We wait for more data so we start a timeout.
137 2
                $this->timeout = $this->loop->addTimer(Connection::DEFAULT_TIMEOUT, function () {
138
                    $this->messageProcessor->timeout($this->socketStream);
139 2
                });
140
            }
141
        }
142
143 2
    }
144
145
    /**
146
     * @param string|Frame $frame
147
     * @param int          $opCode An int representing binary or text data (const of Frame class)
148
     * @throws \Nekland\Woketo\Exception\RuntimeException
149
     */
150
    public function write($frame, int $opCode = Frame::OP_TEXT)
151
    {
152
        try {
153
            $this->messageProcessor->write($frame, $this->socketStream, $opCode);
154
        } catch (WebsocketException $e) {
155
            throw new RuntimeException($e);
156
        }
157
    }
158
159
    /**
160
     * @param $data
161
     */
162
    public function error($data)
163
    {
164
        echo "There is an error : \n" . $data . "\n\n";
165
    }
166
167
    /**
168
     * If it's a new client, we need to make some special actions named the handshake.
169
     *
170
     * @param string $data
171
     */
172 2
    protected function processHandcheck($data)
173
    {
174 2
        if ($this->handshakeDone) {
175
            return;
176
        }
177
178 2
        $request = Request::create($data);
179 2
        $this->handshake->verify($request);
180 2
        $response = Response::createSwitchProtocolResponse();
181 2
        $this->handshake->sign($request, $response);
0 ignored issues
show
$request is of type object<Nekland\Woketo\Http\Request>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
182 2
        $response->send($this->socketStream);
183
        
184 2
        $this->handshakeDone = true;
185 2
        $this->handler->onConnection($this);
186 2
    }
187
}
188