Completed
Push — master ( d0ea51...fa824b )
by Arthur
01:26
created

WssMain::setIsPcntlLoaded()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
4
namespace WSSC\Components;
5
6
7
use WSSC\Contracts\CommonsContract;
8
use WSSC\Contracts\WebSocketServerContract;
9
10
/**
11
 * Class WssMain
12
 * @package WSSC\Components
13
 *
14
 * @property ServerConfig config
15
 */
16
class WssMain implements CommonsContract
17
{
18
    private $isPcntlLoaded = false;
19
20
    /**
21
     * Message frames decoder
22
     *
23
     * @param string $data
24
     * @return mixed null on empty data|false on improper data|array - on success
25
     */
26
    protected function decode(string $data)
27
    {
28
        if (empty($data)) {
29
            return NULL; // close has been sent
30
        }
31
32
        $unmaskedPayload = '';
33
        $decodedData = [];
34
35
        // estimate frame type:
36
        $firstByteBinary = sprintf('%08b', ord($data[0]));
37
        $secondByteBinary = sprintf('%08b', ord($data[1]));
38
        $isMasked = $secondByteBinary[0] === '1';
39
        $payloadLength = ord($data[1]) & self::MASK_127;
40
41
        // unmasked frame is received:
42
        if (!$isMasked) {
43
            return ['type' => '', 'payload' => '', 'error' => WebSocketServerContract::ERR_PROTOCOL];
44
        }
45
46
        $this->getTypeByOpCode($firstByteBinary, $decodedData);
47
        if (empty($decodedData['type'])) {
48
            return ['type' => '', 'payload' => '', 'error' => WebSocketServerContract::ERR_UNKNOWN_OPCODE];
49
        }
50
51
        $mask = substr($data, 2, 4);
52
        $payloadOffset = WebSocketServerContract::PAYLOAD_OFFSET_6;
53
        $dataLength = $payloadLength + $payloadOffset;
54
        if ($payloadLength === self::MASK_126) {
55
            $mask = substr($data, 4, 4);
56
            $payloadOffset = WebSocketServerContract::PAYLOAD_OFFSET_8;
57
            $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset;
58
        } elseif ($payloadLength === self::MASK_127) {
59
            $mask = substr($data, 10, 4);
60
            $payloadOffset = WebSocketServerContract::PAYLOAD_OFFSET_14;
61
            $tmp = '';
62
            for ($i = 0; $i < 8; $i++) {
63
                $tmp .= sprintf('%08b', ord($data[$i + 2]));
64
            }
65
            $dataLength = bindec($tmp) + $payloadOffset;
66
            unset($tmp);
67
        }
68
69
        /**
70
         * We have to check for large frames here. socket_recv cuts at 1024 bytes
71
         * so if websocket-frame is > 1024 bytes we have to wait until whole
72
         * data is transferd.
73
         */
74
        if (strlen($data) < $dataLength) {
75
            return false;
76
        }
77
78
        if ($isMasked) {
79
            for ($i = $payloadOffset; $i < $dataLength; $i++) {
80
                $j = $i - $payloadOffset;
81
                if (isset($data[$i])) {
82
                    $unmaskedPayload .= $data[$i] ^ $mask[$j % 4];
83
                }
84
            }
85
            $decodedData['payload'] = $unmaskedPayload;
86
        } else {
87
            $payloadOffset -= 4;
88
            $decodedData['payload'] = substr($data, $payloadOffset);
89
        }
90
91
        return $decodedData;
92
    }
93
94
    /**
95
     * Returns true if pcntl ext loaded and false otherwise
96
     * @return bool
97
     */
98
    protected function isPcntlLoaded(): bool
99
    {
100
        return $this->isPcntlLoaded;
101
    }
102
103
    /**
104
     * Sets pre-loaded pcntl state
105
     * @param bool $isPcntlLoaded
106
     */
107
    protected function setIsPcntlLoaded(bool $isPcntlLoaded): void
108
    {
109
        $this->isPcntlLoaded = $isPcntlLoaded;
110
    }
111
112
    /**
113
     * Detects decode data type
114
     * @param string $firstByteBinary
115
     * @param array $decodedData
116
     */
117
    private function getTypeByOpCode(string $firstByteBinary, array &$decodedData)
118
    {
119
        $opcode = bindec(substr($firstByteBinary, 4, 4));
120
        switch ($opcode) {
121
            // text frame:
122
            case self::DECODE_TEXT:
123
                $decodedData['type'] = self::EVENT_TYPE_TEXT;
124
                break;
125
            case self::DECODE_BINARY:
126
                $decodedData['type'] = self::EVENT_TYPE_BINARY;
127
                break;
128
            // connection close frame:
129
            case self::DECODE_CLOSE:
130
                $decodedData['type'] = self::EVENT_TYPE_CLOSE;
131
                break;
132
            // ping frame:
133
            case self::DECODE_PING:
134
                $decodedData['type'] = self::EVENT_TYPE_PING;
135
                break;
136
            // pong frame:
137
            case self::DECODE_PONG:
138
                $decodedData['type'] = self::EVENT_TYPE_PONG;
139
                break;
140
            default:
141
                $decodedData['type'] = '';
142
                break;
143
        }
144
    }
145
}
146