Skip to content

Commit d3d3f61

Browse files
committed
Add support for Timestamp type
Fixes #17.
1 parent 1e9eda5 commit d3d3f61

File tree

12 files changed

+661
-382
lines changed

12 files changed

+661
-382
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2015-2021 Eugene Leonovich
3+
Copyright (c) 2015-2022 Eugene Leonovich
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 367 additions & 373 deletions
Large diffs are not rendered by default.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the rybakit/msgpack.php package.
5+
*
6+
* (c) Eugene Leonovich <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace MessagePack\Extension;
13+
14+
use MessagePack\BufferUnpacker;
15+
use MessagePack\Extension;
16+
use MessagePack\Packer;
17+
use MessagePack\Type\Timestamp;
18+
19+
final class TimestampExtension implements Extension
20+
{
21+
private const TYPE = -1;
22+
23+
public function getType() : int
24+
{
25+
return self::TYPE;
26+
}
27+
28+
public function pack(Packer $packer, $value) : ?string
29+
{
30+
if (!$value instanceof Timestamp) {
31+
return null;
32+
}
33+
34+
$sec = $value->getSeconds();
35+
$nsec = $value->getNanoseconds();
36+
37+
if ($sec >> 34) {
38+
return $packer->packExt(self::TYPE, \pack('NJ', $nsec, $sec));
39+
}
40+
41+
return (0 === $nsec && $sec <= 0xffffffff)
42+
? $packer->packExt(self::TYPE, \pack('N', $sec))
43+
: $packer->packExt(self::TYPE, \pack('J', ($nsec << 34) | $sec));
44+
}
45+
46+
/**
47+
* @return Timestamp
48+
*/
49+
public function unpackExt(BufferUnpacker $unpacker, int $extLength)
50+
{
51+
if (4 === $extLength) {
52+
$data = $unpacker->read(4);
53+
54+
$sec = \ord($data[0]) << 24
55+
| \ord($data[1]) << 16
56+
| \ord($data[2]) << 8
57+
| \ord($data[3]);
58+
59+
return new Timestamp($sec);
60+
}
61+
if (8 === $extLength) {
62+
$data = $unpacker->read(8);
63+
64+
$num = \unpack('J', $data)[1];
65+
$nsec = $num >> 34;
66+
if ($nsec < 0) {
67+
$nsec += 0x40000000;
68+
}
69+
70+
return new Timestamp($num & 0x3ffffffff, $nsec);
71+
}
72+
73+
$data = $unpacker->read(12);
74+
75+
$nsec = \ord($data[0]) << 24
76+
| \ord($data[1]) << 16
77+
| \ord($data[2]) << 8
78+
| \ord($data[3]);
79+
80+
return new Timestamp(\unpack('J', $data, 4)[1], $nsec);
81+
}
82+
}

src/MessagePack.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@
1414
use MessagePack\Exception\InvalidOptionException;
1515
use MessagePack\Exception\PackingFailedException;
1616
use MessagePack\Exception\UnpackingFailedException;
17+
use MessagePack\Extension\TimestampExtension;
1718

1819
final class MessagePack
1920
{
21+
/** @var Extension[]|null */
22+
private static $extensions;
23+
2024
/**
2125
* @codeCoverageIgnore
2226
*/
@@ -33,7 +37,7 @@ private function __construct()
3337
*/
3438
public static function pack($value, $options = null) : string
3539
{
36-
return (new Packer($options))->pack($value);
40+
return (new Packer($options, self::getBuiltInExtensions()))->pack($value);
3741
}
3842

3943
/**
@@ -46,6 +50,13 @@ public static function pack($value, $options = null) : string
4650
*/
4751
public static function unpack(string $data, $options = null)
4852
{
49-
return (new BufferUnpacker($data, $options))->unpack();
53+
return (new BufferUnpacker($data, $options, self::getBuiltInExtensions()))->unpack();
54+
}
55+
56+
private static function getBuiltInExtensions() : array
57+
{
58+
return self::$extensions ?? self::$extensions = [
59+
new TimestampExtension(),
60+
];
5061
}
5162
}

src/Type/Timestamp.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the rybakit/msgpack.php package.
5+
*
6+
* (c) Eugene Leonovich <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace MessagePack\Type;
13+
14+
final class Timestamp
15+
{
16+
private $seconds;
17+
private $nanoseconds;
18+
19+
public function __construct(int $seconds, int $nanoseconds = 0)
20+
{
21+
$this->seconds = $seconds;
22+
$this->nanoseconds = $nanoseconds;
23+
}
24+
25+
public static function now() : self
26+
{
27+
$date = new \DateTime();
28+
29+
return new self($date->getTimestamp(), (int) $date->format('u') * 1000);
30+
}
31+
32+
public static function fromDateTime(\DateTimeInterface $date) : self
33+
{
34+
return new self($date->getTimestamp(), (int) $date->format('u') * 1000);
35+
}
36+
37+
public function getSeconds() : int
38+
{
39+
return $this->seconds;
40+
}
41+
42+
public function getNanoseconds() : int
43+
{
44+
return $this->nanoseconds;
45+
}
46+
}

tests/DataProvider.php

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@
1717
namespace MessagePack\Tests;
1818

1919
use MessagePack\Type\Ext;
20+
use MessagePack\Type\Timestamp;
2021

2122
class DataProvider
2223
{
24+
/**
25+
* @return array<string, array{mixed, string}>
26+
*/
2327
public static function provideData() : array
2428
{
2529
return array_merge(
@@ -31,10 +35,14 @@ public static function provideData() : array
3135
self::provideBinData(),
3236
self::provideArrayData(),
3337
self::provideMapData(),
34-
self::provideExtData()
38+
self::provideExtData(),
39+
self::provideExtTimestampData()
3540
);
3641
}
3742

43+
/**
44+
* @return array<string, array{mixed, string}>
45+
*/
3846
public static function provideUnpackData() : array
3947
{
4048
return array_merge(
@@ -46,17 +54,24 @@ public static function provideUnpackData() : array
4654
self::provideBinData(),
4755
self::provideArrayData(),
4856
self::provideMapUnpackData(),
49-
self::provideExtData()
57+
self::provideExtData(),
58+
self::provideExtTimestampData()
5059
);
5160
}
5261

62+
/**
63+
* @return array<string, array{null, string}>
64+
*/
5365
public static function provideNilData() : array
5466
{
5567
return [
5668
'nil' => [null, "\xc0"],
5769
];
5870
}
5971

72+
/**
73+
* @return array<string, array{bool, string}>
74+
*/
6075
public static function provideBoolData() : array
6176
{
6277
return [
@@ -65,6 +80,9 @@ public static function provideBoolData() : array
6580
];
6681
}
6782

83+
/**
84+
* @return array<string, array{int, string}>
85+
*/
6886
public static function provideIntData() : array
6987
{
7088
return [
@@ -112,6 +130,9 @@ public static function provideIntData() : array
112130
];
113131
}
114132

133+
/**
134+
* @return array<string, array{int, string}>
135+
*/
115136
public static function provideIntUnpackData() : array
116137
{
117138
return array_merge(self::provideIntData(), [
@@ -129,6 +150,9 @@ public static function provideIntUnpackData() : array
129150
]);
130151
}
131152

153+
/**
154+
* @return array<string, array{float, string}>
155+
*/
132156
public static function provideFloat32Data() : array
133157
{
134158
return [
@@ -137,6 +161,9 @@ public static function provideFloat32Data() : array
137161
];
138162
}
139163

164+
/**
165+
* @return array<string, array{float, string}>
166+
*/
140167
public static function provideFloat64Data() : array
141168
{
142169
return [
@@ -146,6 +173,9 @@ public static function provideFloat64Data() : array
146173
];
147174
}
148175

176+
/**
177+
* @return array<string, array{float, string}>
178+
*/
149179
public static function provideFloatUnpackData() : array
150180
{
151181
return array_merge(
@@ -154,6 +184,9 @@ public static function provideFloatUnpackData() : array
154184
);
155185
}
156186

187+
/**
188+
* @return array<string, array{string, string}>
189+
*/
157190
public static function provideStrData() : array
158191
{
159192
return [
@@ -174,6 +207,9 @@ public static function provideStrData() : array
174207
];
175208
}
176209

210+
/**
211+
* @return array<string, array{string, string}>
212+
*/
177213
public static function provideBinData() : array
178214
{
179215
return [
@@ -185,6 +221,9 @@ public static function provideBinData() : array
185221
];
186222
}
187223

224+
/**
225+
* @return array<string, {array<int, mixed>, string}>
226+
*/
188227
public static function provideArrayData() : array
189228
{
190229
return [
@@ -198,6 +237,9 @@ public static function provideArrayData() : array
198237
];
199238
}
200239

240+
/**
241+
* @return array<string, {array, string}>
242+
*/
201243
public static function provideMapData() : array
202244
{
203245
return [
@@ -212,6 +254,9 @@ public static function provideMapData() : array
212254
];
213255
}
214256

257+
/**
258+
* @return array<string, {array, string}>
259+
*/
215260
public static function provideMapUnpackData() : array
216261
{
217262
return array_merge(self::provideMapData(), [
@@ -221,6 +266,9 @@ public static function provideMapUnpackData() : array
221266
]);
222267
}
223268

269+
/**
270+
* @return array<string, array{Ext, string}>
271+
*/
224272
public static function provideExtData() : array
225273
{
226274
return [
@@ -235,6 +283,28 @@ public static function provideExtData() : array
235283
];
236284
}
237285

286+
/**
287+
* @return array<string, array{Timestamp, string}>
288+
*/
289+
public static function provideExtTimestampData() : array
290+
{
291+
return [
292+
'32-bit timestamp #1' => [new Timestamp(0), "\xd6\xff\x00\x00\x00\x00"],
293+
'32-bit timestamp #2' => [new Timestamp(0xffffffff), "\xd6\xff\xff\xff\xff\xff"],
294+
295+
'64-bit timestamp #1' => [new Timestamp(0, 1), "\xd7\xff\x00\x00\x00\x04\x00\x00\x00\x00"],
296+
'64-bit timestamp #2' => [new Timestamp(0xffffffff + 1), "\xd7\xff\x00\x00\x00\x01\x00\x00\x00\x00"],
297+
'64-bit timestamp #3' => [new Timestamp(0x3ffffffff, 999999999), "\xd7\xff\xee\x6b\x27\xff\xff\xff\xff\xff"],
298+
299+
'96-bit timestamp #1' => [new Timestamp(PHP_INT_MIN), "\xc7\x0c\xff\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00"],
300+
'96-bit timestamp #2' => [new Timestamp(0x3ffffffff + 1, 0), "\xc7\x0c\xff\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00"],
301+
'96-bit timestamp #3' => [new Timestamp(PHP_INT_MAX, 999999999), "\xc7\x0c\xff\x3b\x9a\xc9\xff\x7f\xff\xff\xff\xff\xff\xff\xff"],
302+
];
303+
}
304+
305+
/**
306+
* @return list<string>
307+
*/
238308
public static function getSlowTestNames() : array
239309
{
240310
return [
@@ -245,6 +315,9 @@ public static function getSlowTestNames() : array
245315
];
246316
}
247317

318+
/**
319+
* @return list<string>
320+
*/
248321
public static function getPeclIncompatibleTestNames() : array
249322
{
250323
return [
@@ -265,6 +338,14 @@ public static function getPeclIncompatibleTestNames() : array
265338
'8-bit ext',
266339
'16-bit ext',
267340
'32-bit ext',
341+
'32-bit timestamp #1',
342+
'32-bit timestamp #2',
343+
'64-bit timestamp #1',
344+
'64-bit timestamp #2',
345+
'64-bit timestamp #3',
346+
'96-bit timestamp #1',
347+
'96-bit timestamp #2',
348+
'96-bit timestamp #3',
268349
];
269350
}
270351
}

0 commit comments

Comments
 (0)