Completed
Push — master ( 46f060...0fe4dc )
by Charlotte
11s
created

Message::addFrame()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6.0208

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 11
cts 12
cp 0.9167
rs 8.5906
c 0
b 0
f 0
cc 6
eloc 12
nc 5
nop 1
crap 6.0208
1
<?php
2
/**
3
 * This file is a part of Woketo package.
4
 *
5
 * (c) Nekland <[email protected]>
6
 *
7
 * For the full license, take a look to the LICENSE file
8
 * on the root directory of this project
9
 */
10
11
namespace Nekland\Woketo\Rfc6455;
12
13
use Nekland\Tools\StringTools;
14
use Nekland\Woketo\Exception\Frame\WrongEncodingException;
15
use Nekland\Woketo\Exception\LimitationException;
16
use Nekland\Woketo\Exception\MissingDataException;
17
18
class Message
19
{
20
    const MAX_MESSAGES_BUFFERING = 1000;
21
    /**
22
     * @var array
23
     */
24
    private $frames;
25
26
    /**
27
     * @var bool
28
     */
29
    private $isComplete;
30
31
    /**
32
     * @var string
33
     */
34
    private $buffer;
35
36 43
    public function __construct()
37
    {
38 43
        $this->frames = [];
39 43
        $this->isComplete = false;
40 43
        $this->buffer = '';
41 43
    }
42
43
    /**
44
     * Add some data to the buffer.
45
     *
46
     * @param $data
47
     */
48 15
    public function addBuffer($data)
49
    {
50 15
        $this->buffer .= $data;
51 15
    }
52
53
    /**
54
     * Clear the buffer.
55
     */
56 1
    public function clearBuffer()
57
    {
58 1
        $this->buffer = '';
59 1
    }
60
61
    /**
62
     * Get data inside the buffer.
63
     *
64
     * @return string
65
     */
66 15
    public function getBuffer()
67
    {
68 15
        return $this->buffer;
69
    }
70
71
    /**
72
     * Remove data from the start of the buffer.
73
     *
74
     * @param Frame $frame
75
     * @return string
76
     */
77 10
    public function removeFromBuffer(Frame $frame) : string
78
    {
79 10
        $this->buffer = StringTools::removeStart($this->getBuffer(), $frame->getRawData(), '8bit');
80
81 10
        return $this->buffer;
82
    }
83
84
    /**
85
     * @param Frame $frame
86
     * @return Message
87
     * @throws \InvalidArgumentException
88
     * @throws LimitationException
89
     * @throws WrongEncodingException
90
     */
91 38
    public function addFrame(Frame $frame) : Message
92
    {
93 38
        if ($this->isComplete) {
94
            throw new \InvalidArgumentException('The message is already complete.');
95
        }
96
97 38
        if (count($this->frames) > Message::MAX_MESSAGES_BUFFERING) {
98 1
            throw new LimitationException(
99 1
                sprintf('We don\'t accept more than %s frames by message. This is a security limitation.', Message::MAX_MESSAGES_BUFFERING)
100
            );
101
        }
102
103 38
        $this->isComplete = $frame->isFinal();
104 38
        $this->frames[] = $frame;
105
106 38
        if ($this->isComplete()) {
107 35
            if ($this->getFirstFrame()->getOpcode() === Frame::OP_TEXT && !mb_check_encoding($this->getContent(), 'UTF-8')) {
108 2
                throw new WrongEncodingException('The text is not encoded in UTF-8.');
109
            }
110
        }
111
112 36
        return $this;
113
    }
114
115
    /**
116
     * @return Frame
117
     * @throws MissingDataException
118
     */
119 35
    public function getFirstFrame() : Frame
120
    {
121 35
        if (empty($this->frames[0])) {
122
            throw new MissingDataException('There is no first frame for now.');
123
        }
124
125 35
        return $this->frames[0];
126
    }
127
128
    /**
129
     * This could in the future be deprecated in favor of a stream object.
130
     *
131
     * @return string
132
     * @throws MissingDataException
133
     */
134 16
    public function getContent() : string
135
    {
136 16
        if (!$this->isComplete) {
137 1
            throw new MissingDataException('The message is not complete. Frames are missing.');
138
        }
139
140 15
        $res = '';
141
142 15
        foreach ($this->frames as $frame) {
143 15
            $res .= $frame->getPayload();
144
        }
145
146 15
        return $res;
147
    }
148
149
    /**
150
     * @return int
151
     */
152 19
    public function getOpcode()
153
    {
154 19
        return $this->getFirstFrame()->getOpcode();
155
    }
156
157
    /**
158
     * @return bool
159
     */
160 38
    public function isComplete()
161
    {
162 38
        return $this->isComplete;
163
    }
164
165
    /**
166
     * @return bool
167
     */
168
    public function isOperation()
169
    {
170
        return in_array($this->getFirstFrame()->getOpcode(), [Frame::OP_TEXT, Frame::OP_BINARY]);
171
    }
172
173
    /**
174
     * @return Frame[]
175
     */
176 4
    public function getFrames()
177
    {
178 4
        return $this->frames;
179
    }
180
181
    /**
182
     * @return bool
183
     */
184 11
    public function hasFrames()
185
    {
186 11
        return !empty($this->frames);
187
    }
188
189
    /**
190
     * @return int
191
     */
192
    public function countFrames() : int
193
    {
194
        return count($this->frames);
195
    }
196
}
197