Skip to content

Commit d7ff8ab

Browse files
Merge pull request #90 from neo4j-php/84-bytearray
added missing data type bytearray
2 parents da43d95 + 2157a3d commit d7ff8ab

File tree

5 files changed

+138
-9
lines changed

5 files changed

+138
-9
lines changed

src/Bolt.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ private function unpackProtocolVersion(): string
115115
{
116116
$result = [];
117117

118-
foreach (str_split($this->connection->read(4)) as $ch) {
118+
foreach (mb_str_split($this->connection->read(4), 1, '8bit') as $ch) {
119119
$result[] = unpack('C', $ch)[1] ?? 0;
120120
}
121121

src/PackStream/v1/Packer.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
LocalDateTime,
1616
Duration,
1717
Point2D,
18-
Point3D
18+
Point3D,
19+
Bytes
1920
};
2021

2122
/**
@@ -117,6 +118,8 @@ private function p($param): string
117118
case 'object':
118119
if ($param instanceof IStructure) {
119120
return $this->packStructure($param);
121+
} elseif ($param instanceof Bytes) {
122+
return $this->packByteArray($param);
120123
} else {
121124
return $this->packMap((array)$param);
122125
}
@@ -263,4 +266,23 @@ private function packStructure(IStructure $structure): string
263266
return $output;
264267
}
265268

269+
/**
270+
* @param Bytes $bytes
271+
* @return string
272+
* @throws PackException
273+
*/
274+
private function packByteArray(Bytes $bytes): string
275+
{
276+
$size = count($bytes);
277+
if ($size < self::MEDIUM) {
278+
return chr(0xCC) . pack('C', $size) . $bytes;
279+
} elseif ($size < self::LARGE) {
280+
return chr(0xCD) . pack('n', $size) . $bytes;
281+
} elseif ($size <= 2147483647) {
282+
return chr(0xCE) . pack('N', $size) . $bytes;
283+
} else {
284+
throw new PackException('ByteArray too big');
285+
}
286+
}
287+
266288
}

src/PackStream/v1/Unpacker.php

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
LocalDateTime,
1717
Duration,
1818
Point2D,
19-
Point3D
19+
Point3D,
20+
Bytes
2021
};
2122
use Bolt\PackStream\IUnpacker;
2223
use Bolt\error\UnpackException;
@@ -130,6 +131,10 @@ private function u()
130131
if ($output !== null) {
131132
return $output;
132133
}
134+
$output = $this->unpackByteArray($marker);
135+
if ($output !== null) {
136+
return $output;
137+
}
133138

134139
return null;
135140
}
@@ -186,7 +191,7 @@ private function unpackSpecificStructure(string $class, string ...$methods): ISt
186191

187192
/**
188193
* @param int $marker
189-
* @return array
194+
* @return array|null
190195
* @throws UnpackException
191196
*/
192197
private function unpackMap(int $marker): ?array
@@ -212,7 +217,7 @@ private function unpackMap(int $marker): ?array
212217

213218
/**
214219
* @param int $marker
215-
* @return string
220+
* @return string|null
216221
*/
217222
private function unpackString(int $marker): ?string
218223
{
@@ -233,7 +238,7 @@ private function unpackString(int $marker): ?string
233238

234239
/**
235240
* @param int $marker
236-
* @return int
241+
* @return int|null
237242
*/
238243
private function unpackInteger(int $marker): ?int
239244
{
@@ -257,7 +262,7 @@ private function unpackInteger(int $marker): ?int
257262

258263
/**
259264
* @param int $marker
260-
* @return float
265+
* @return float|null
261266
*/
262267
private function unpackFloat(int $marker): ?float
263268
{
@@ -271,7 +276,7 @@ private function unpackFloat(int $marker): ?float
271276

272277
/**
273278
* @param int $marker
274-
* @return array
279+
* @return array|null
275280
* @throws UnpackException
276281
*/
277282
private function unpackList(int $marker): ?array
@@ -296,4 +301,23 @@ private function unpackList(int $marker): ?array
296301
return $output;
297302
}
298303

304+
/**
305+
* @param int $marker
306+
* @return Bytes|null
307+
*/
308+
private function unpackByteArray(int $marker): ?Bytes
309+
{
310+
if ($marker == 0xCC) {
311+
$size = (int)unpack('C', $this->next(1))[1];
312+
} elseif ($marker == 0xCD) {
313+
$size = (int)unpack('n', $this->next(2))[1];
314+
} elseif ($marker == 0xCE) {
315+
$size = (int)unpack('N', $this->next(4))[1];
316+
} else {
317+
return null;
318+
}
319+
320+
return new Bytes(mb_str_split($this->next($size), 1, '8bit'));
321+
}
322+
299323
}

src/structures/Bytes.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace Bolt\structures;
4+
5+
use ArrayAccess, Countable;
6+
7+
/**
8+
* Class ByteArray
9+
*
10+
* @author Michal Stefanak
11+
* @link https://github.com/neo4j-php/Bolt
12+
* @link https://7687.org/packstream/packstream-specification-1.html#bytes
13+
* @package Bolt\structures
14+
*/
15+
class Bytes implements ArrayAccess, Countable
16+
{
17+
private array $bytes = [];
18+
19+
public function __construct(array $bytes = [])
20+
{
21+
$this->bytes = $bytes;
22+
}
23+
24+
public function offsetExists($offset): bool
25+
{
26+
return array_key_exists($offset, $this->bytes);
27+
}
28+
29+
public function offsetGet($offset): ?string
30+
{
31+
return $this->bytes[$offset] ?? null;
32+
}
33+
34+
public function offsetSet($offset, $value)
35+
{
36+
if ($offset === null)
37+
$this->bytes[] = $value;
38+
else
39+
$this->bytes[$offset] = $value;
40+
}
41+
42+
public function offsetUnset($offset)
43+
{
44+
unset($this->bytes[$offset]);
45+
}
46+
47+
public function count(): int
48+
{
49+
return count($this->bytes);
50+
}
51+
52+
public function __toString(): string
53+
{
54+
return implode($this->bytes);
55+
}
56+
}

tests/PackStream/v1/StructuresTest.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
LocalDateTime,
1818
Duration,
1919
Point2D,
20-
Point3D
20+
Point3D,
21+
Bytes
2122
};
2223
use PHPUnit\Framework\TestCase;
2324
use Exception;
@@ -451,4 +452,30 @@ private function randomTimestamp(string $timezone = '+0000'): int
451452
}
452453
}
453454

455+
/**
456+
* @depends testInit
457+
* @dataProvider providerByteArray
458+
*/
459+
public function testByteArray(Bytes $arr, AProtocol $protocol)
460+
{
461+
try {
462+
$protocol->run('RETURN $arr', ['arr' => $arr]);
463+
$res = $protocol->pullAll();
464+
$this->assertEquals($arr, $res[0][0]);
465+
} catch (Exception $e) {
466+
$this->markTestIncomplete($e->getMessage());
467+
}
468+
}
469+
470+
public function providerByteArray(): \Generator
471+
{
472+
foreach ([0, 200, 60000, 70000] as $size) {
473+
$arr = new Bytes();
474+
while (count($arr) < $size) {
475+
$arr[] = pack('H', mt_rand(0, 255));
476+
}
477+
yield 'bytes: ' . count($arr) => [$arr];
478+
}
479+
}
480+
454481
}

0 commit comments

Comments
 (0)