Skip to content

Commit f70587c

Browse files
committed
init prject
0 parents  commit f70587c

File tree

10 files changed

+789
-0
lines changed

10 files changed

+789
-0
lines changed

.editorconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
root = true
2+
3+
# 对所有文件生效
4+
[*]
5+
charset = utf-8
6+
indent_style = space
7+
indent_size = 2
8+
end_of_line = lf
9+
insert_final_newline = true
10+
trim_trailing_whitespace = true
11+
12+
# 对后缀名为 md 的文件生效
13+
[*.md]
14+
trim_trailing_whitespace = false
15+
16+
[*.php]
17+
indent_size = 4

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.idea/
2+
.phpintel/
3+
!README.md
4+
!.gitkeep
5+
composer.lock
6+
*.swp
7+
*.log
8+
*.pid
9+
*.patch
10+
.DS_Store

LICENSE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016 inhere
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
this software and associated documentation files (the "Software"), to deal in
7+
the Software without restriction, including without limitation the rights to
8+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9+
the Software, and to permit persons to whom the Software is furnished to do so,
10+
subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# websocket utils
2+
3+
## install
4+
5+
```bash
6+
composer require mylib/websocket-utils
7+
```
8+
9+
## license
10+
11+
MIT

composer.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "mylib/websocket-utils",
3+
"type": "library",
4+
"description": "some helper tool library of the php",
5+
"keywords": ["library","tool","php"],
6+
"homepage": "https://github.com/php-mylib/websocket-utils",
7+
"license": "MIT",
8+
"authors": [
9+
{
10+
"name": "inhere",
11+
"email": "[email protected]",
12+
"homepage": "http://www.yzone.net/"
13+
}
14+
],
15+
"require": {
16+
"php": ">=7.0.0",
17+
"mylib/arr-utils": "~1.0",
18+
"mylib/obj-utils": "~1.0",
19+
"mylib/str-utils": "~1.0",
20+
"mylib/sys-utils": "~1.0",
21+
"mylib/php-utils": "~1.0"
22+
},
23+
"autoload": {
24+
"psr-4": {
25+
"MyLib\\WebSocket\\Client\\" : "src/"
26+
}
27+
},
28+
"suggest": {
29+
"inhere/php-validate": "Very lightweight data validate tool",
30+
"inhere/console": "a lightweight php console application library."
31+
}
32+
}

phpunit.xml.dist

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<phpunit backupGlobals="false"
4+
backupStaticAttributes="false"
5+
bootstrap="test/boot.php"
6+
colors="false"
7+
convertErrorsToExceptions="true"
8+
convertNoticesToExceptions="true"
9+
convertWarningsToExceptions="true"
10+
stopOnFailure="false"
11+
syntaxCheck="false"
12+
>
13+
<testsuites>
14+
<testsuite name="Php Library Test Suite">
15+
<directory>test</directory>
16+
</testsuite>
17+
</testsuites>
18+
19+
<filter>
20+
<whitelist>
21+
<directory suffix=".php">src</directory>
22+
</whitelist>
23+
</filter>
24+
</phpunit>

src/Helper.php

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
<?php
2+
/**
3+
* Created by PhpStorm.
4+
* User: Inhere
5+
* Date: 2017/4/3 0003
6+
* Time: 01:05
7+
*/
8+
9+
namespace MyLib\WebSocket\Util;
10+
11+
/**
12+
* Class WSHelper
13+
* @package MyLib\WebSocket\Util
14+
*/
15+
class Helper
16+
{
17+
public static function encode()
18+
{
19+
20+
}
21+
22+
/**
23+
* @param string$s
24+
* @return string
25+
*/
26+
public static function frame(string $s): string
27+
{
28+
$a = str_split($s, 125);
29+
$prefix = WebSocketInterface::BINARY_TYPE_BLOB;
30+
31+
if (\count($a) === 1) {
32+
return $prefix . \chr(\strlen($a[0])) . $a[0];
33+
}
34+
35+
$ns = '';
36+
37+
foreach ($a as $o) {
38+
$ns .= $prefix . \chr(\strlen($o)) . $o;
39+
}
40+
41+
return $ns;
42+
}
43+
44+
/**
45+
* @param $buffer
46+
* @return string
47+
*/
48+
public static function decode($buffer)
49+
{
50+
/*$len = $masks = $data =*/
51+
$decoded = '';
52+
$len = \ord($buffer[1]) & 127;
53+
54+
if ($len === 126) {
55+
$masks = \substr($buffer, 4, 4);
56+
$data = \substr($buffer, 8);
57+
} else if ($len === 127) {
58+
$masks = \substr($buffer, 10, 4);
59+
$data = \substr($buffer, 14);
60+
} else {
61+
$masks = \substr($buffer, 2, 4);
62+
$data = \substr($buffer, 6);
63+
}
64+
65+
$dataLen = \strlen($data);
66+
for ($index = 0; $index < $dataLen; $index++) {
67+
$decoded .= $data[$index] ^ $masks[$index % 4];
68+
}
69+
70+
return $decoded;
71+
}
72+
73+
/**
74+
* @param string $payload
75+
* @param string $type
76+
* @param bool $masked
77+
* @return bool|string
78+
* @throws \Exception
79+
*/
80+
public static function hybi10Encode(string $payload, string $type = 'text', bool $masked = true)
81+
{
82+
$frameHead = array();
83+
$payloadLength = \strlen($payload);
84+
85+
switch ($type) {
86+
//文本内容
87+
case 'text':
88+
// first byte indicates FIN, Text-Frame (10000001):
89+
$frameHead[0] = 129;
90+
break;
91+
//二进制内容
92+
case 'binary':
93+
case 'bin':
94+
// first byte indicates FIN, Text-Frame (10000010):
95+
$frameHead[0] = 130;
96+
break;
97+
case 'close':
98+
// first byte indicates FIN, Close Frame(10001000):
99+
$frameHead[0] = 136;
100+
break;
101+
case 'ping':
102+
// first byte indicates FIN, Ping frame (10001001):
103+
$frameHead[0] = 137;
104+
break;
105+
case 'pong':
106+
// first byte indicates FIN, Pong frame (10001010):
107+
$frameHead[0] = 138;
108+
break;
109+
}
110+
111+
// set mask and payload length (using 1, 3 or 9 bytes)
112+
if ($payloadLength > 65535) {
113+
$payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
114+
$frameHead[1] = ($masked === true) ? 255 : 127;
115+
116+
for ($i = 0; $i < 8; $i++) {
117+
$frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
118+
}
119+
120+
// most significant bit MUST be 0 (close connection if frame too big)
121+
if ($frameHead[2] > 127) {
122+
// todo `$this->close()`;
123+
return false;
124+
}
125+
} elseif ($payloadLength > 125) {
126+
$payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
127+
$frameHead[1] = ($masked === true) ? 254 : 126;
128+
$frameHead[2] = bindec($payloadLengthBin[0]);
129+
$frameHead[3] = bindec($payloadLengthBin[1]);
130+
} else {
131+
$frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
132+
}
133+
134+
// convert frame-head to string:
135+
foreach ($frameHead as $i => $v) {
136+
$frameHead[$i] = \chr($frameHead[$i]);
137+
}
138+
139+
// generate a random mask:
140+
$mask = array();
141+
if ($masked === true) {
142+
for ($i = 0; $i < 4; $i++) {
143+
$mask[$i] = \chr(random_int(0, 255));
144+
}
145+
146+
$frameHead = array_merge($frameHead, $mask);
147+
}
148+
149+
$frame = implode('', $frameHead);
150+
151+
// append payload to frame:
152+
for ($i = 0; $i < $payloadLength; $i++) {
153+
$frame .= $masked ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
154+
}
155+
156+
return $frame;
157+
}
158+
159+
/**
160+
* @param string $data
161+
* @return string
162+
* @throws \InvalidArgumentException
163+
*/
164+
public static function hybi10Decode(string $data): string
165+
{
166+
if (!$data) {
167+
throw new \InvalidArgumentException('data is empty');
168+
}
169+
170+
$bytes = $data;
171+
$secondByte = sprintf('%08b', \ord($bytes[1]));
172+
$masked = '1' === $secondByte[0];
173+
$dataLength = ($masked === true) ? \ord($bytes[1]) & 127 : \ord($bytes[1]);
174+
175+
//服务器不会设置mask
176+
if ($dataLength === 126) {
177+
$decodedData = \substr($bytes, 4);
178+
} elseif ($dataLength === 127) {
179+
$decodedData = \substr($bytes, 10);
180+
} else {
181+
$decodedData = \substr($bytes, 2);
182+
}
183+
184+
return $decodedData;
185+
}
186+
187+
}

0 commit comments

Comments
 (0)