Completed
Pull Request — master (#28)
by Maxime
01:54
created

Message::addData()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 18
rs 8.8571
cc 5
eloc 12
nc 4
nop 1
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\IncompleteFrameException;
15
use Nekland\Woketo\Exception\LimitationException;
16
use Nekland\Woketo\Exception\MissingDataException;
17
use Nekland\Woketo\Utils\BitManipulation;
18
19
class Message
20
{
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
    public function __construct()
37
    {
38
        $this->frames = [];
39
        $this->isComplete = false;
40
        $this->buffer = '';
41
    }
42
43
    public function addData($data)
44
    {
45
        $this->buffer .= $data;
46
        do {
47
            try {
48
                $this->addFrame($frame = new Frame($this->buffer));
49
                $this->buffer = StringTools::removeStart($this->buffer, $frame->getRawData(), '8bit');
50
            } catch (IncompleteFrameException $e) {
51
                return ''; // There is no more frame we can generate, the data is saved as buffer.
52
            }
53
        } while(!$this->isComplete() && !empty($this->buffer));
54
55
        if ($this->isComplete()) {
56
            return $this->buffer;
57
        }
58
59
        return '';
60
    }
61
62
    /**
63
     * @param Frame $frame
64
     * @return Message
65
     * @throws \InvalidArgumentException
66
     * @throws LimitationException
67
     */
68
    public function addFrame(Frame $frame) : Message
69
    {
70
        if ($this->isComplete) {
71
            throw new \InvalidArgumentException('The message is already complete.');
72
        }
73
74
        if (count($this->frames) > 19) {
75
            throw new LimitationException('We don\'t accept more than 20 frame by message. This is a security limitation.');
76
        }
77
78
        $this->isComplete = $frame->isFinal();
79
        $this->frames[] = $frame;
80
81
        return $this;
82
    }
83
84
    /**
85
     * @return Frame
86
     * @throws MissingDataException
87
     */
88
    public function getFirstFrame() : Frame
89
    {
90
        if (empty($this->frames[0])) {
91
            throw new MissingDataException('There is no first frame for now.');
92
        }
93
94
        return $this->frames[0];
95
    }
96
97
    /**
98
     * This could in the future be deprecated in favor of a stream object.
99
     *
100
     * @return string
101
     * @throws MissingDataException
102
     */
103
    public function getContent() : string
104
    {
105
        if (!$this->isComplete) {
106
            throw new MissingDataException('The message is not complete. Frames are missing.');
107
        }
108
109
        $res = '';
110
111
        foreach ($this->frames as $frame) {
112
            $res .= $frame->getPayload();
113
        }
114
115
        return $res;
116
    }
117
118
    /**
119
     * @return int
120
     */
121
    public function getOpcode()
122
    {
123
        return $this->getFirstFrame()->getOpcode();
124
    }
125
126
    /**
127
     * @return bool
128
     */
129
    public function isComplete()
130
    {
131
        return $this->isComplete;
132
    }
133
134
    /**
135
     * @return bool
136
     */
137
    public function isOperation()
138
    {
139
        return in_array($this->getFirstFrame()->getOpcode(), [Frame::OP_TEXT, Frame::OP_BINARY]);
140
    }
141
}
142