Issues (63)

src/Utils.php (4 issues)

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
    public static function roundUp($x, $y)
29
    {
30
        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
    public static function bytesToString(array $bytes)
52
    {
53
        $str = '';
54
        foreach ($bytes as $byte) {
55
            $str .= chr($byte);
56
        }
57
58
        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
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
    public static function bytesToInt(array $bytes, $unsigned = true)
91
    {
92
        $bytes = array_reverse($bytes);
93
        $value = 0;
94
        foreach ($bytes as $i => $b) {
95
            $value |= $b << $i * 8;
96
        }
97
98
        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
    public static function intToBytes($int, $size = 32)
107
    {
108
        $size = self::roundUp($size, 8);
109
        $bytes = [];
110
        for ($i = 0; $i < $size; $i += 8) {
111
            $bytes[] = 0xFF & $int >> $i;
112
        }
113
        $bytes = array_reverse($bytes);
114
115
        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 8-byte units (octs) used to write data for a variable of the given WIDTH.
145
     *
146
     * @param int $width
147
     * @return int
148
     */
149
    public static function widthToOcts($width)
150
    {
151
        $result = 0;
152
        foreach(self::getSegments($width) as $segmentWidth) {
153
            $result += ceil($segmentWidth / 8);
154
        }
155
        return (int) max(1, $result);
156
    }
157
158
    /**
159
     * Returns the number of "segments" used for writing case data for a variable of the given WIDTH.
160
     * A segment is a physical variable in the system file that represents some piece of a logical variable.
161
     * Only very long string variables have more than one segment.
162
     *
163
     * @param int $width
164
     * @return int
165
     */
166
    public static function widthToSegments(int $width): int
167
    {
168
        return (int) Variable::isVeryLong($width) ? ceil($width / Variable::EFFECTIVE_VLS_CHUNK) : 1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return (int)SPSS\Sav\Rec...FFECTIVE_VLS_CHUNK) : 1 could return the type double which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
169
    }
170
171
    public static function getSegments(int $width): iterable
172
    {
173
        $count = self::widthToSegments($width);
174
        for($i = 1; $i < $count; $i++) {
175
            yield 255;
176
        }
177
        yield $width - ($count - 1) * Variable::EFFECTIVE_VLS_CHUNK;
178
    }
179
180
181
182
}
183