Completed
Push — master ( dd1269...21c3dd )
by Sam
02:11
created

Utils   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 217
Duplicated Lines 0 %

Test Coverage

Coverage 66.06%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 43
c 1
b 0
f 0
dl 0
loc 217
ccs 37
cts 56
cp 0.6606
rs 10
wmc 26

15 Methods

Rating   Name   Duplication   Size   Complexity  
A signedToUnsigned() 0 3 1
A stringToDouble() 0 7 1
A widthToBytes() 0 15 3
A segmentAllocBytes() 0 5 2
A widthToOcts() 0 3 1
A segmentAllocWidth() 0 10 3
A doubleToString() 0 3 1
A formatDate() 0 3 1
A bytesToInt() 0 9 3
A bytesToString() 0 8 2
A roundDown() 0 3 1
A intToBytes() 0 10 2
A unsignedToSigned() 0 8 2
A roundUp() 0 3 1
A widthToSegments() 0 3 2
1
<?php
2
3
namespace SPSS;
4
5
use SPSS\Sav\Record\Variable;
6
7
class Utils
8
{
9
    /**
10
     * SPSS represents a date as the number of seconds since the epoch, midnight, Oct. 14, 1582.
11
     *
12
     * @param $timestamp
13
     * @param string $format
14
     * @return false|int
15
     */
16
    public static function formatDate($timestamp, $format = 'Y M d')
17
    {
18
        return date($format, strtotime('1582-10-04 00:00:00') + $timestamp);
0 ignored issues
show
Bug Best Practice introduced by
The expression return date($format, str...0:00:00') + $timestamp) returns the type string which is incompatible with the documented return type false|integer.
Loading history...
19
    }
20
21
    /**
22
     * Rounds X up to the next multiple of Y.
23
     *
24
     * @param int $x
25
     * @param int $y
26
     * @return int
27
     */
28 5
    public static function roundUp($x, $y)
29
    {
30 5
        return ceil($x / $y) * $y;
31
    }
32
33
    /**
34
     * Rounds X down to the prev multiple of Y.
35
     *
36
     * @param int $x
37
     * @param int $y
38
     * @return int
39
     */
40
    public static function roundDown($x, $y)
41
    {
42
        return floor($x / $y) * $y;
43
    }
44
45
    /**
46
     * Convert bytes to string
47
     *
48
     * @param array $bytes
49
     * @return string
50
     */
51 5
    public static function bytesToString(array $bytes)
52
    {
53 5
        $str = '';
54 5
        foreach ($bytes as $byte) {
55 5
            $str .= chr($byte);
56
        }
57
58 5
        return $str;
59
    }
60
61
    /**
62
     * Convert double to string
63
     *
64
     * @param double $num
65
     * @return string
66
     */
67
    public static function doubleToString($num)
68
    {
69
        return self::bytesToString(unpack('C8', pack('d', $num)));
0 ignored issues
show
Bug introduced by
It seems like unpack('C8', pack('d', $num)) can also be of type false; however, parameter $bytes of SPSS\Utils::bytesToString() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

69
        return self::bytesToString(/** @scrutinizer ignore-type */ unpack('C8', pack('d', $num)));
Loading history...
70
    }
71
72
    /**
73
     * @param string $str
74
     * @return double
75
     */
76
    public static function stringToDouble($str)
77
    {
78
        // if (strlen($str) < 8) {
79
        //     throw new Exception('String must be a 8 length');
80
        // }
81
82
        return unpack('d', pack('A8', $str))[1];
83
    }
84
85
    /**
86
     * @param array $bytes
87
     * @param bool $unsigned
88
     * @return int
89
     */
90 5
    public static function bytesToInt(array $bytes, $unsigned = true)
91
    {
92 5
        $bytes = array_reverse($bytes);
93 5
        $value = 0;
94 5
        foreach ($bytes as $i => $b) {
95 5
            $value |= $b << $i * 8;
96
        }
97
98 5
        return $unsigned ? $value : self::unsignedToSigned($value, count($bytes) * 8);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $unsigned ? $valu...lue, count($bytes) * 8) also could return the type string which is incompatible with the documented return type integer.
Loading history...
99
    }
100
101
    /**
102
     * @param $int
103
     * @param int $size
104
     * @return array
105
     */
106 5
    public static function intToBytes($int, $size = 32)
107
    {
108 5
        $size = self::roundUp($size, 8);
109 5
        $bytes = [];
110 5
        for ($i = 0; $i < $size; $i += 8) {
111 5
            $bytes[] = 0xFF & $int >> $i;
112
        }
113 5
        $bytes = array_reverse($bytes);
114
115 5
        return $bytes;
116
    }
117
118
    /**
119
     * @param int $value
120
     * @param int $size
121
     * @return string
122
     */
123
    public static function unsignedToSigned($value, $size = 32)
124
    {
125
        $size = self::roundUp($size, 8);
126
        if (bccomp($value, bcpow(2, $size - 1)) >= 0) {
127
            $value = bcsub($value, bcpow(2, $size));
128
        }
129
130
        return $value;
131
    }
132
133
    /**
134
     * @param int $value
135
     * @param int $size
136
     * @return string
137
     */
138
    public static function signedToUnsigned($value, $size = 32)
139
    {
140
        return $value + bcpow(2, $size);
141
    }
142
143
    /**
144
     * Returns the number of bytes of uncompressed case data used for writing a variable of the given WIDTH to a system file.
145
     * All required space is included, including trailing padding and internal padding.
146
     *
147
     * @param int $width
148
     * @return int
149
     */
150 5
    public static function widthToBytes($width)
151
    {
152
        // assert($width >= 0);
153
154 5
        if ($width == 0) {
155
            $bytes = 8;
156 5
        } elseif (! Variable::isVeryLong($width)) {
157 5
            $bytes = $width;
158
        } else {
159 1
            $chunks = $width / Variable::EFFECTIVE_VLS_CHUNK;
160 1
            $remainder = $width % Variable::EFFECTIVE_VLS_CHUNK;
161 1
            $bytes = floor($chunks) * Variable::REAL_VLS_CHUNK + $remainder;
162
        }
163
164 5
        return self::roundUp($bytes, 8);
0 ignored issues
show
Bug introduced by
It seems like $bytes can also be of type double; however, parameter $x of SPSS\Utils::roundUp() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

164
        return self::roundUp(/** @scrutinizer ignore-type */ $bytes, 8);
Loading history...
165
    }
166
167
    /**
168
     * Returns the number of 8-byte units (octs) used to write data for a variable of the given WIDTH.
169
     *
170
     * @param int $width
171
     * @return int
172
     */
173 5
    public static function widthToOcts($width)
174
    {
175 5
        return self::widthToBytes($width) / 8;
176
    }
177
178
    /**
179
     * Returns the number of "segments" used for writing case data for a variable of the given WIDTH.
180
     * A segment is a physical variable in the system file that represents some piece of a logical variable.
181
     * Only very long string variables have more than one segment.
182
     *
183
     * @param int $width
184
     * @return int
185
     */
186 5
    public static function widthToSegments($width)
187
    {
188 5
        return Variable::isVeryLong($width) ? ceil($width / Variable::EFFECTIVE_VLS_CHUNK) : 1;
189
    }
190
191
    /**
192
     * Returns the width to allocate to the given SEGMENT within a variable of the given WIDTH.
193
     * A segment is a physical variable in the system file that represents some piece of a logical variable.
194
     *
195
     * @param int $width
196
     * @param int $segment
197
     * @return int
198
     */
199 5
    public static function segmentAllocWidth($width, $segment = 0)
200
    {
201 5
        $segmentCount = self::widthToSegments($width);
202
        // assert($segment < $segmentCount);
203
204 5
        if (! Variable::isVeryLong($width)) {
205 5
            return $width;
206
        }
207
208 1
        return $segment < $segmentCount - 1 ? Variable::REAL_VLS_CHUNK : self::roundUp($width - $segment * Variable::EFFECTIVE_VLS_CHUNK, 8);
209
    }
210
211
    /**
212
     * Returns the number of bytes to allocate to the given SEGMENT within a variable of the given width.
213
     * This is the same as @see segmentAllocWidth, except that a numeric value takes up 8 bytes despite having a width of 0.
214
     *
215
     * @param int $width
216
     * @param int $segment
217
     * @return int
218
     */
219
    public static function segmentAllocBytes($width, $segment)
220
    {
221
        assert($segment < self::widthToSegments($width));
222
223
        return $width == 0 ? 8 : self::roundUp(self::segmentAllocWidth($width, $segment), 8);
224
    }
225
226
}
227