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
![]() |
|||||
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
![]() |
|||||
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
|
|||||
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
|
|||||
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 |