Completed
Push — master ( 0da600...8233a7 )
by Andreu
04:17
created

UniversalTimestamp::fromWhatever()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 17
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7.0178

Importance

Changes 2
Bugs 1 Features 1
Metric Value
c 2
b 1
f 1
dl 0
loc 17
ccs 13
cts 14
cp 0.9286
rs 8.2222
cc 7
eloc 15
nc 7
nop 1
crap 7.0178
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 8 and the first side effect is on line 8.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
4
namespace Litipk\Jiffy;
5
6
7 1
if (extension_loaded('mongo') && extension_loaded('mongodb')) {
8
    trait TsExtension { use MongoAdapter; use MongodbAdapter; };
9 1
} elseif (extension_loaded('mongo')) {
10
    trait TsExtension { use MongoAdapter; };
0 ignored issues
show
Comprehensibility Best Practice introduced by
The type Litipk\Jiffy\TsExtension has been defined more than once; this definition is ignored, only the first definition in this file (L8-8) is considered.

This check looks for classes that have been defined more than once in the same file.

If you can, we would recommend to use standard object-oriented programming techniques. For example, to avoid multiple types, it might make sense to create a common interface, and then multiple, different implementations for that interface.

This also has the side-effect of providing you with better IDE auto-completion, static analysis and also better OPCode caching from PHP.

Loading history...
Coding Style Compatibility introduced by
Each trait must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
11
} elseif (extension_loaded('mongodb')) {
12
    trait TsExtension { use MongodbAdapter; };
0 ignored issues
show
Comprehensibility Best Practice introduced by
The type Litipk\Jiffy\TsExtension has been defined more than once; this definition is ignored, only the first definition in this file (L8-8) is considered.

This check looks for classes that have been defined more than once in the same file.

If you can, we would recommend to use standard object-oriented programming techniques. For example, to avoid multiple types, it might make sense to create a common interface, and then multiple, different implementations for that interface.

This also has the side-effect of providing you with better IDE auto-completion, static analysis and also better OPCode caching from PHP.

Loading history...
Coding Style Compatibility introduced by
Each trait must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
13
} else {
14
    trait TsExtension {};
0 ignored issues
show
Comprehensibility Best Practice introduced by
The type Litipk\Jiffy\TsExtension has been defined more than once; this definition is ignored, only the first definition in this file (L8-8) is considered.

This check looks for classes that have been defined more than once in the same file.

If you can, we would recommend to use standard object-oriented programming techniques. For example, to avoid multiple types, it might make sense to create a common interface, and then multiple, different implementations for that interface.

This also has the side-effect of providing you with better IDE auto-completion, static analysis and also better OPCode caching from PHP.

Loading history...
Coding Style Compatibility introduced by
Each trait must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
15
}
16
17
18
/**
19
 * Class UniversalTimestamp
20
 * @package Litipk\Jiffy
21
 */
22
class UniversalTimestamp
0 ignored issues
show
Coding Style Compatibility introduced by
Each trait must be in a file by itself

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
23
{
24
    const ISO8601_WITH_MILLISECONDS = '_ISO8601_WITH_MILLIS_';
25
    const ISO8601_WITH_MILLISECONDS_WITHOUT_TZ = '_ISO8601_WITH_MILLIS_WITHOUT_TZ';
26
    const ISO8601_WITH_MICROSECONDS = 'Y-m-d\TH:i:s.uO';
27
    const ISO8601_WITH_MICROSECONDS_WITHOUT_TZ = 'Y-m-d\TH:i:s.u';
28
29
    use TsExtension;
30
31
    /** @var int */
32
    private $millis;
33
34
    /** @var int */
35
    private $micros;
36
37
    /**
38
     * Constructor.
39
     *
40
     * @param integer $millisSinceEpoch
41
     * @param integer $micros
42
     */
43 17
    private function __construct($millisSinceEpoch, $micros = 0)
44
    {
45 17
        if ($millisSinceEpoch < 0 || $micros < 0) {
46 1
            throw new JiffyException('The number of milliseconds and microseconds must be positive');
47
        }
48
49 16
        $this->millis = $millisSinceEpoch + (int)($micros/1000);
50 16
        $this->micros = $micros % 1000;
51 16
    }
52
53
    /**
54
     * @return UniversalTimestamp
55
     */
56 10
    public static function now()
57
    {
58 10
        $ts_parts = explode(' ', microtime());
59
60 10
        return new UniversalTimestamp(
61 10
            (int)floor($ts_parts[0]*1000) + (int)$ts_parts[1]*1000,  // Millis
62 10
            ((int)round($ts_parts[0]*1000000))%1000                  // Micros
63 10
        );
64
    }
65
66
    /**
67
     * @param \DateTimeInterface $dateTime
68
     * @return UniversalTimestamp
69
     */
70 3
    public static function fromDateTimeInterface(\DateTimeInterface $dateTime)
71
    {
72 3
        $dtU = (int)$dateTime->format('u');
73
74 3
        return new UniversalTimestamp(
75 3
            $dateTime->getTimestamp()*1000 + (int)floor($dtU/1000),
76
            $dtU % 1000
77 3
        );
78
    }
79
80
    /**
81
     * @param int $secondsSinceEpoch
82
     * @return UniversalTimestamp
83
     */
84 1
    public static function fromSecondsTimestamp($secondsSinceEpoch)
85
    {
86 1
        return new UniversalTimestamp($secondsSinceEpoch*1000);
87
    }
88
89
    /**
90
     * @param int $millisSinceEpoch
91
     * @param int $micros
92
     * @return UniversalTimestamp
93
     */
94 9
    public static function fromMillisecondsTimestamp($millisSinceEpoch, $micros = 0)
95
    {
96 9
        return new UniversalTimestamp($millisSinceEpoch, $micros);
97
    }
98
99
    /**
100
     * @param mixed $dateObject   If it's an integer, then it's understood as milliseconds since epoch
101
     * @return UniversalTimestamp
102
     */
103 2
    public static function fromWhatever($dateObject) {
104 2
        if (null === $dateObject) {
105 1
            return static::now();
106 2
        } elseif (is_int($dateObject)) {
107 1
            return static::fromMillisecondsTimestamp($dateObject);
108 2
        } elseif ($dateObject instanceof UniversalTimestamp) {
109
            return $dateObject;
110 2
        } elseif ($dateObject instanceof \DateTimeInterface) {
111 1
            return static::fromDateTimeInterface($dateObject);
112 2
        } elseif ($dateObject instanceof \MongoDate) {
113 1
            return static::fromMongoDate($dateObject);
114 2
        } elseif ($dateObject instanceof \MongoDB\BSON\UTCDatetime) {
0 ignored issues
show
Bug introduced by
The class MongoDB\BSON\UTCDatetime does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
115 1
            return static::fromMongodbUTCDateTime($dateObject);
116
        }else {
117 1
            throw new JiffyException('The provided value cannot be interpreted as a timestamp');
118
        }
119
    }
120
121
    /**
122
     * @param UniversalTimestamp $otherTimestamp
123
     * @return boolean
124
     */
125 3
    public function isGreaterThan(UniversalTimestamp $otherTimestamp)
126
    {
127
        return (
128 3
            $this->millis > $otherTimestamp->millis ||
129 3
            $this->millis === $otherTimestamp->millis && $this->micros > $otherTimestamp->micros
130 3
        );
131
    }
132
133
    /**
134
     * @param int $seconds
135
     * @return UniversalTimestamp
136
     */
137 1
    public function addSeconds($seconds)
138
    {
139 1
        return new UniversalTimestamp($this->millis + 1000*$seconds, $this->micros);
140
    }
141
142
    /**
143
     * @param int $millis
144
     * @return UniversalTimestamp
145
     */
146 1
    public function addMilliseconds($millis)
147
    {
148 1
        return new UniversalTimestamp($this->millis + $millis, $this->micros);
149
    }
150
151
    /**
152
     * @return int
153
     */
154 10
    public function asSeconds()
155
    {
156 10
        return (int)floor($this->millis/1000);
157
    }
158
159
    /**
160
     * @return int
161
     */
162 9
    public function asMilliseconds()
163
    {
164 9
        return $this->millis;
165
    }
166
167
    /**
168
     * @return int
169
     */
170 1
    public function getRemainingMicroseconds()
171
    {
172 1
        return $this->micros;
173
    }
174
175
    /**
176
     * @param string|\DateTimeZone $tz
177
     * @return \DateTimeImmutable
178
     */
179 2
    public function asDateTimeInterface($tz = 'UTC')
180
    {
181 2
        $dateTime = new \DateTimeImmutable('@'.((string)$this->asSeconds()));
182 2
        $dateTime = $dateTime->setTimezone(is_string($tz) ? new \DateTimeZone($tz) : $tz);
183
184 2
        return new \DateTimeImmutable(
185 2
            $dateTime->format('Y-m-d\TH:i:s').'.'.
186 2
            sprintf("%03d", $this->millis%1000).sprintf("%03d", $this->micros).
187 2
            $dateTime->format('O')
188 2
        );
189
    }
190
191
    /**
192
     * @param string $format
193
     * @param string|\DateTimeZone $tz
194
     * @return string
195
     */
196 2
    public function asFormattedString($format = self::ISO8601_WITH_MICROSECONDS, $tz = 'UTC')
197
    {
198 2
        if (self::ISO8601_WITH_MILLISECONDS === $format) {
199 1
            $rParts = preg_split('/\+/', $this->asDateTimeInterface($tz)->format(\DateTime::ISO8601));
200 1
            return $rParts[0].'.'.((string)$this->millis%1000).'+'.$rParts[1];
201 2
        } elseif (self::ISO8601_WITH_MILLISECONDS_WITHOUT_TZ === $format) {
202 1
            $rParts = preg_split('/\+/', $this->asDateTimeInterface($tz)->format(\DateTime::ISO8601));
203 1
            return $rParts[0].'.'.((string)$this->millis%1000);
204
        } else {
205 2
            return $this->asDateTimeInterface($tz)->format($format);
206
        }
207
    }
208
209
    /**
210
     * @return string
211
     */
212 1
    public function __toString()
213
    {
214 1
        return $this->asFormattedString();
215
    }
216
}
217