Frame::getCloseReason()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Helix\Socket\WebSocket;
4
5
/**
6
 * A WebSocket frame.
7
 *
8
 * @see https://tools.ietf.org/html/rfc6455#section-5.2 Base Framing Protocol
9
 * @see https://tools.ietf.org/html/rfc6455#section-7.4.1 Defined Status Codes
10
 */
11
class Frame {
12
13
    const FIN = 0x80;
14
    const RSV123 = 0x70;
15
    const RSV1 = 0x40;
16
    const RSV2 = 0x20;
17
    const RSV3 = 0x10;
18
    const OP = 0x0f;
19
    const OP_CONTINUATION = 0x00;
20
    const OP_TEXT = 0x01;
21
    const OP_BINARY = 0x02;
22
    const OP_CLOSE = 0x08;
23
    const OP_PING = 0x09;
24
    const OP_PONG = 0x0a;
25
    const LEN = 0x7f;
26
27
    const NAMES = [
28
        self::OP_CONTINUATION => 'CONTINUATION',
29
        self::OP_TEXT => 'TEXT',
30
        self::OP_BINARY => 'BINARY',
31
        0x03 => 'RESERVED DATA 0x03',
32
        0x04 => 'RESERVED DATA 0x04',
33
        0x05 => 'RESERVED DATA 0x05',
34
        0x06 => 'RESERVED DATA 0x06',
35
        0x07 => 'RESERVED DATA 0x07',
36
        self::OP_CLOSE => 'CLOSE',
37
        self::OP_PING => 'PING',
38
        self::OP_PONG => 'PONG',
39
        0x0b => 'RESERVED CONTROL 0x0b',
40
        0x0c => 'RESERVED CONTROL 0x0c',
41
        0x0d => 'RESERVED CONTROL 0x0d',
42
        0x0e => 'RESERVED CONTROL 0x0e',
43
        0x0f => 'RESERVED CONTROL 0x0f',
44
    ];
45
46
    const CLOSE_NORMAL = 1000;              // mutual closure
47
    const CLOSE_INTERRUPT = 1001;           // abrupt closure due to hangups, reboots, "going away"
48
    const CLOSE_PROTOCOL_ERROR = 1002;      // invalid behavior / framing
49
    const CLOSE_UNHANDLED_DATA = 1003;      // message handler doesn't want the payload
50
    const CLOSE_BAD_DATA = 1007;            // message handler can't understand the payload
51
    const CLOSE_POLICY_VIOLATION = 1008;    // generic "access denied"
52
    const CLOSE_TOO_LARGE = 1009;           // unacceptable payload size
53
    const CLOSE_EXPECTATION = 1010;         // peer closed because it wants extensions (listed in the reason)
54
    const CLOSE_INTERNAL_ERROR = 1011;      // like http 500
55
56
    /**
57
     * @var bool
58
     */
59
    protected $final;
60
61
    /**
62
     * @var int
63
     */
64
    protected $opCode;
65
66
    /**
67
     * @var string
68
     */
69
    protected $payload;
70
71
    /**
72
     * The RSV bits masked out of the first byte of the frame, as-is.
73
     *
74
     * @var int
75
     */
76
    protected $rsv;
77
78
    /**
79
     * @param bool $final
80
     * @param int $rsv
81
     * @param int $opCode
82
     * @param string $payload
83
     */
84
    public function __construct (bool $final, int $rsv, int $opCode, string $payload) {
85
        $this->final = $final;
86
        $this->rsv = $rsv;
87
        $this->opCode = $opCode;
88
        $this->payload = $payload;
89
    }
90
91
    /**
92
     * The payload, or `CLOSE` reason.
93
     *
94
     * @return string
95
     */
96
    public function __toString () {
97
        if ($this->isClose()) {
98
            return $this->getCloseReason();
99
        }
100
        return $this->payload;
101
    }
102
103
    /**
104
     * The `CLOSE` code.
105
     *
106
     * @see https://tools.ietf.org/html/rfc6455#section-5.5.1
107
     *
108
     * @return int
109
     */
110
    final public function getCloseCode (): int {
111
        $code = substr($this->payload, 0, 2);
112
        return isset($code[1]) ? unpack('n', $code)[1] : self::CLOSE_NORMAL;
113
    }
114
115
    /**
116
     * The `CLOSE` reason.
117
     *
118
     * @see https://tools.ietf.org/html/rfc6455#section-5.5.1
119
     *
120
     * @return string
121
     */
122
    final public function getCloseReason (): string {
123
        return substr($this->payload, 2);
124
    }
125
126
    /**
127
     * @return int
128
     */
129
    final public function getLength (): int {
130
        return strlen($this->payload);
131
    }
132
133
    /**
134
     * @return string
135
     */
136
    public function getName (): string {
137
        return self::NAMES[$this->opCode];
138
    }
139
140
    /**
141
     * @return int
142
     */
143
    final public function getOpCode (): int {
144
        return $this->opCode;
145
    }
146
147
    /**
148
     * @return string
149
     */
150
    final public function getPayload (): string {
151
        return $this->payload;
152
    }
153
154
    /**
155
     * @return int
156
     */
157
    final public function getRsv (): int {
158
        return $this->rsv;
159
    }
160
161
    /**
162
     * @return bool
163
     */
164
    final public function hasRsv1 (): bool {
165
        return (bool)($this->rsv & self::RSV1);
166
    }
167
168
    /**
169
     * @return bool
170
     */
171
    final public function hasRsv2 (): bool {
172
        return (bool)($this->rsv & self::RSV2);
173
    }
174
175
    /**
176
     * @return bool
177
     */
178
    final public function hasRsv3 (): bool {
179
        return (bool)($this->rsv & self::RSV3);
180
    }
181
182
    /**
183
     * @return bool
184
     */
185
    final public function isBinary (): bool {
186
        return $this->opCode === self::OP_BINARY;
187
    }
188
189
    /**
190
     * @return bool
191
     */
192
    final public function isClose (): bool {
193
        return $this->opCode === self::OP_CLOSE;
194
    }
195
196
    /**
197
     * @return bool
198
     */
199
    final public function isContinuation (): bool {
200
        return $this->opCode === self::OP_CONTINUATION;
201
    }
202
203
    /**
204
     * @return bool
205
     */
206
    final public function isControl (): bool {
207
        return $this->opCode >= self::OP_CLOSE;
208
    }
209
210
    /**
211
     * @return bool
212
     */
213
    final public function isData (): bool {
214
        return $this->opCode < self::OP_CLOSE;
215
    }
216
217
    /**
218
     * @return bool
219
     */
220
    final public function isFinal (): bool {
221
        return $this->final;
222
    }
223
224
    /**
225
     * @return bool
226
     */
227
    final public function isPing (): bool {
228
        return $this->opCode === self::OP_PING;
229
    }
230
231
    /**
232
     * @return bool
233
     */
234
    final public function isPong (): bool {
235
        return $this->opCode === self::OP_PONG;
236
    }
237
238
    /**
239
     * @return bool
240
     */
241
    final public function isText (): bool {
242
        return $this->opCode === self::OP_TEXT;
243
    }
244
245
}