Skip to content

Commit 1acbff5

Browse files
committed
Create a timestamp class to normalise the differing values that could come back from the db
1 parent 1af058c commit 1acbff5

File tree

4 files changed

+224
-1
lines changed

4 files changed

+224
-1
lines changed

src/AntiMattr/MongoDB/Migrations/Configuration/Configuration.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ public function getMigratedVersions()
304304
* @return string
305305
*
306306
* @throws AntiMattr\MongoDB\Migrations\Exception\UnknownVersionException Throws exception if migration version does not exist
307+
* @throws DomainException If more than one version exists
307308
*/
308309
public function getMigratedTimestamp($version)
309310
{
@@ -325,7 +326,10 @@ public function getMigratedTimestamp($version)
325326

326327
$returnVersion = $cursor->getNext();
327328

328-
return (string) $returnVersion['t'];
329+
// Convert to normalised timestamp
330+
$ts = new Timestamp($returnVersion['t']);
331+
332+
return (string) $ts;
329333
}
330334

331335
/**
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the AntiMattr MongoDB Migrations Library, a library by Matthew Fitzgerald.
5+
*
6+
* (c) 2014 Matthew Fitzgerald
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 AntiMattr\MongoDB\Migrations\Configuration;
13+
14+
/**
15+
* This class is to normalise the potential values from the 't' version
16+
* attribute
17+
*
18+
* @author Douglas Reith <douglas@reith.com.au>
19+
*/
20+
class Timestamp
21+
{
22+
private $t;
23+
24+
/**
25+
* __construct
26+
*
27+
* @param mixed $t
28+
*/
29+
public function __construct($t)
30+
{
31+
$this->t = $t;
32+
}
33+
34+
/**
35+
* __invoke
36+
*
37+
* @param mixed $t
38+
* @return int
39+
*/
40+
public function __invoke($t): int
41+
{
42+
return (new static($t))->getTimestamp();
43+
}
44+
45+
/**
46+
* getTimestamp
47+
*
48+
* Normalise based on the different options for backward/forward
49+
* compatibility
50+
*
51+
* @return int Time in seconds since 1970
52+
*/
53+
public function getTimestamp(): int
54+
{
55+
$supportedClasses = implode(
56+
', ',
57+
[
58+
'\MongoTimestamp',
59+
'\MongoDate',
60+
'\MongoDB\BSON\Timestamp',
61+
'\MongoDB\BSON\UTCDateTime',
62+
'\DateTimeInterface',
63+
]
64+
);
65+
66+
if (!$this->t || !is_object($this->t)) {
67+
throw new \DomainException(
68+
'The timestamp to normalise must be one of ' . $supportedClasses . ' but it is not an object'
69+
);
70+
}
71+
72+
switch(get_class($this->t)) {
73+
case 'MongoTimestamp':
74+
return (int) $this->t->__toString();
75+
76+
case 'MongoDate':
77+
return $this->t->sec;
78+
79+
case 'MongoDB\BSON\Timestamp':
80+
return $this->t->getTimestamp();
81+
82+
case 'MongoDB\BSON\UTCDateTime':
83+
return (int) $this->t->toDateTime()->format('U');
84+
}
85+
86+
if ($this->t instanceof \DateTimeInterface) {
87+
return $this->t->getTimestamp();
88+
}
89+
90+
throw new \DomainException(
91+
'The normalised timestamp must be one of ' . $supportedClasses
92+
);
93+
}
94+
95+
/**
96+
* __toString
97+
*
98+
* @return string
99+
*/
100+
public function __toString(): string
101+
{
102+
return (string) $this->getTimestamp();
103+
}
104+
}

tests/AntiMattr/Tests/MongoDB/Migrations/Configuration/ConfigurationTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,41 @@ public function testDuplicateThrowsException()
306306
$this->configuration->getMigratedTimestamp('1');
307307
}
308308

309+
public function testGetMigratedTimestamp()
310+
{
311+
$this->prepareValidConfiguration();
312+
313+
$collection = $this->buildMock('Doctrine\MongoDB\Collection');
314+
$database = $this->buildMock('Doctrine\MongoDB\Database');
315+
316+
$this->connection->expects($this->once())
317+
->method('selectDatabase')
318+
->with('test_antimattr_migrations')
319+
->willReturn($database);
320+
321+
$database->expects($this->once())
322+
->method('selectCollection')
323+
->with('antimattr_migration_versions_test')
324+
->willReturn($collection);
325+
326+
$cursor = $this->buildMock('Doctrine\MongoDB\Cursor');
327+
328+
$collection->expects($this->once())
329+
->method('find')
330+
->willReturn($cursor);
331+
332+
$cursor->expects($this->exactly(2))
333+
->method('count')
334+
->willReturn(1);
335+
336+
$cursor->expects($this->once())
337+
->method('getNext')
338+
->willReturn(['t' => new \DateTime()]);
339+
340+
$this->assertTrue(is_numeric($this->configuration->getMigratedTimestamp('1')));
341+
}
342+
343+
309344
private function prepareValidConfiguration()
310345
{
311346
$directory = dirname(__DIR__) . '/Resources/Migrations/';
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace AntiMattr\Tests\MongoDB\Migrations\Configuration;
4+
5+
use AntiMattr\TestCase\AntiMattrTestCase;
6+
use AntiMattr\MongoDB\Migrations\Configuration\Timestamp;
7+
8+
class TimestampTest extends AntiMattrTestCase
9+
{
10+
/**
11+
* testTimestamp
12+
*
13+
* @dataProvider provideTimestamps
14+
*/
15+
public function testTimestamp($ts, $expected)
16+
{
17+
self::assertEquals($expected, (new Timestamp($ts))->getTimestamp());
18+
}
19+
20+
public function provideTimestamps(): array
21+
{
22+
$rightNow = new \DateTimeImmutable();
23+
$secondsSince1970 = (int) $rightNow->format('U');
24+
$millisecondsSince1970 = $secondsSince1970 * 1000;
25+
26+
$timestamps = [
27+
[$rightNow, $secondsSince1970]
28+
];
29+
30+
if (class_exists('\MongoDB\BSON\Timestamp')) {
31+
$timestamps[] = [
32+
new \MongoDB\BSON\Timestamp(0, $secondsSince1970),
33+
$secondsSince1970
34+
];
35+
}
36+
37+
if (class_exists('\MongoDB\BSON\UTCDateTime')) {
38+
$timestamps[] = [
39+
new \MongoDB\BSON\UTCDateTime($millisecondsSince1970),
40+
$secondsSince1970
41+
];
42+
}
43+
44+
if (class_exists('\MongoDate')) {
45+
$timestamps[] = [
46+
new \MongoDate($secondsSince1970),
47+
$secondsSince1970
48+
];
49+
}
50+
51+
if (class_exists('\MongoTimestamp')) {
52+
$timestamps[] = [
53+
new \MongoTimestamp($secondsSince1970),
54+
$secondsSince1970
55+
];
56+
}
57+
58+
return $timestamps;
59+
}
60+
61+
/**
62+
* testWillThrowAnExceptionForUnknownClass
63+
*
64+
* @expectedException \DomainException
65+
*/
66+
public function testWillThrowAnExceptionForUnknownClass()
67+
{
68+
(new Timestamp(new \stdClass()))->getTimestamp();
69+
}
70+
71+
/**
72+
* testWillThrowAnExceptionForNull
73+
*
74+
* @expectedException \DomainException
75+
*/
76+
public function testWillThrowAnExceptionForNull()
77+
{
78+
(new Timestamp(null))->getTimestamp();
79+
}
80+
}

0 commit comments

Comments
 (0)