Skip to content

Commit 1d966d8

Browse files
author
LauraTaylorUK
authored
Minor improvements to fix PHPStan level 8 errors (#160)
* Minor improvements to fix PHPStan level 8 errors * Instead of an abs call add a type-hint, since this variable is (must be) always a positive integer * Cosmetic changes to make the Linter happy * Prevent negative values from passing to the fread function * Move the type-hint to the attribute definition * Add type-hints for all read function parameters, add extra check for negative values * Add checks for the return value of false, from all uses of unpack, and appease the Gods of PHPStan level 8 * Cosmetic changes to make the Linter happy * Remove the extra check for negative values because PHPStan runs with treatPhpDocTypesAsCertain set to true * One variable was used to store integers and strings, this change uses two separate variables and when using real ints the loop is faster without the extra checks and returns earlier * Cosmetic changes to make the Linter happy * Final check for the return value of false, from use of unpack * Cosmetic changes to make the Linter happy * Cosmetic changes to make the Linter happy * The Linter is misbehaving... reported errors conflicr with each other
1 parent 387cd1f commit 1d966d8

File tree

3 files changed

+116
-23
lines changed

3 files changed

+116
-23
lines changed

src/MaxMind/Db/Reader.php

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class Reader
2626
private static $METADATA_START_MARKER = "\xAB\xCD\xEFMaxMind.com";
2727

2828
/**
29-
* @var int
29+
* @var int<0, max>
3030
*/
3131
private static $METADATA_START_MARKER_LENGTH = 14;
3232

@@ -180,6 +180,11 @@ private function findAddressInTree(string $ipAddress): array
180180
}
181181

182182
$rawAddress = unpack('C*', $packedAddr);
183+
if ($rawAddress === false) {
184+
throw new InvalidDatabaseException(
185+
'Could not unpack the unsigned char of the packed in_addr representation.'
186+
);
187+
}
183188

184189
$bitCount = \count($rawAddress) * 8;
185190

@@ -247,7 +252,13 @@ private function readNode(int $nodeNumber, int $index): int
247252
switch ($this->metadata->recordSize) {
248253
case 24:
249254
$bytes = Util::read($this->fileHandle, $baseOffset + $index * 3, 3);
250-
[, $node] = unpack('N', "\x00" . $bytes);
255+
$rc = unpack('N', "\x00" . $bytes);
256+
if ($rc === false) {
257+
throw new InvalidDatabaseException(
258+
'Could not unpack the unsigned long of the node.'
259+
);
260+
}
261+
[, $node] = $rc;
251262

252263
return $node;
253264

@@ -258,13 +269,25 @@ private function readNode(int $nodeNumber, int $index): int
258269
} else {
259270
$middle = 0x0F & \ord($bytes[0]);
260271
}
261-
[, $node] = unpack('N', \chr($middle) . substr($bytes, $index, 3));
272+
$rc = unpack('N', \chr($middle) . substr($bytes, $index, 3));
273+
if ($rc === false) {
274+
throw new InvalidDatabaseException(
275+
'Could not unpack the unsigned long of the node.'
276+
);
277+
}
278+
[, $node] = $rc;
262279

263280
return $node;
264281

265282
case 32:
266283
$bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 4);
267-
[, $node] = unpack('N', $bytes);
284+
$rc = unpack('N', $bytes);
285+
if ($rc === false) {
286+
throw new InvalidDatabaseException(
287+
'Could not unpack the unsigned long of the node.'
288+
);
289+
}
290+
[, $node] = $rc;
268291

269292
return $node;
270293

@@ -303,6 +326,11 @@ private function findMetadataStart(string $filename): int
303326
{
304327
$handle = $this->fileHandle;
305328
$fstat = fstat($handle);
329+
if ($fstat === false) {
330+
throw new InvalidDatabaseException(
331+
"Error getting file information ($filename)."
332+
);
333+
}
306334
$fileSize = $fstat['size'];
307335
$marker = self::$METADATA_START_MARKER;
308336
$markerLength = self::$METADATA_START_MARKER_LENGTH;

src/MaxMind/Db/Reader/Decoder.php

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ public function decode(int $offset): array
108108
return $this->decodeByType($type, $offset, $size);
109109
}
110110

111+
/**
112+
* @param int<0, max> $size
113+
*/
111114
private function decodeByType(int $type, int $offset, int $size): array
112115
{
113116
switch ($type) {
@@ -185,7 +188,13 @@ private function decodeDouble(string $bytes): float
185188
{
186189
// This assumes IEEE 754 doubles, but most (all?) modern platforms
187190
// use them.
188-
[, $double] = unpack('E', $bytes);
191+
$rc = unpack('E', $bytes);
192+
if ($rc === false) {
193+
throw new InvalidDatabaseException(
194+
'Could not unpack a double value from the given bytes.'
195+
);
196+
}
197+
[, $double] = $rc;
189198

190199
return $double;
191200
}
@@ -194,7 +203,13 @@ private function decodeFloat(string $bytes): float
194203
{
195204
// This assumes IEEE 754 floats, but most (all?) modern platforms
196205
// use them.
197-
[, $float] = unpack('G', $bytes);
206+
$rc = unpack('G', $bytes);
207+
if ($rc === false) {
208+
throw new InvalidDatabaseException(
209+
'Could not unpack a float value from the given bytes.'
210+
);
211+
}
212+
[, $float] = $rc;
198213

199214
return $float;
200215
}
@@ -221,7 +236,13 @@ private function decodeInt32(string $bytes, int $size): int
221236
);
222237
}
223238

224-
[, $int] = unpack('l', $this->maybeSwitchByteOrder($bytes));
239+
$rc = unpack('l', $this->maybeSwitchByteOrder($bytes));
240+
if ($rc === false) {
241+
throw new InvalidDatabaseException(
242+
'Could not unpack a 32bit integer value from the given bytes.'
243+
);
244+
}
245+
[, $int] = $rc;
225246

226247
return $int;
227248
}
@@ -249,14 +270,26 @@ private function decodePointer(int $ctrlByte, int $offset): array
249270
switch ($pointerSize) {
250271
case 1:
251272
$packed = \chr($ctrlByte & 0x7) . $buffer;
252-
[, $pointer] = unpack('n', $packed);
273+
$rc = unpack('n', $packed);
274+
if ($rc === false) {
275+
throw new InvalidDatabaseException(
276+
'Could not unpack an unsigned short value from the given bytes (pointerSize is 1).'
277+
);
278+
}
279+
[, $pointer] = $rc;
253280
$pointer += $this->pointerBase;
254281

255282
break;
256283

257284
case 2:
258285
$packed = "\x00" . \chr($ctrlByte & 0x7) . $buffer;
259-
[, $pointer] = unpack('N', $packed);
286+
$rc = unpack('N', $packed);
287+
if ($rc === false) {
288+
throw new InvalidDatabaseException(
289+
'Could not unpack an unsigned long value from the given bytes (pointerSize is 2).'
290+
);
291+
}
292+
[, $pointer] = $rc;
260293
$pointer += $this->pointerBase + 2048;
261294

262295
break;
@@ -266,7 +299,13 @@ private function decodePointer(int $ctrlByte, int $offset): array
266299

267300
// It is safe to use 'N' here, even on 32 bit machines as the
268301
// first bit is 0.
269-
[, $pointer] = unpack('N', $packed);
302+
$rc = unpack('N', $packed);
303+
if ($rc === false) {
304+
throw new InvalidDatabaseException(
305+
'Could not unpack an unsigned long value from the given bytes (pointerSize is 3).'
306+
);
307+
}
308+
[, $pointer] = $rc;
270309
$pointer += $this->pointerBase + 526336;
271310

272311
break;
@@ -304,32 +343,39 @@ private function decodeUint(string $bytes, int $byteLength)
304343
return 0;
305344
}
306345

307-
$integer = 0;
308-
309346
// PHP integers are signed. PHP_INT_SIZE - 1 is the number of
310347
// complete bytes that can be converted to an integer. However,
311348
// we can convert another byte if the leading bit is zero.
312349
$useRealInts = $byteLength <= \PHP_INT_SIZE - 1
313350
|| ($byteLength === \PHP_INT_SIZE && (\ord($bytes[0]) & 0x80) === 0);
314351

352+
if ($useRealInts) {
353+
$integer = 0;
354+
for ($i = 0; $i < $byteLength; ++$i) {
355+
$part = \ord($bytes[$i]);
356+
$integer = ($integer << 8) + $part;
357+
}
358+
359+
return $integer;
360+
}
361+
362+
// We only use gmp or bcmath if the final value is too big
363+
$integerAsString = '0';
315364
for ($i = 0; $i < $byteLength; ++$i) {
316365
$part = \ord($bytes[$i]);
317366

318-
// We only use gmp or bcmath if the final value is too big
319-
if ($useRealInts) {
320-
$integer = ($integer << 8) + $part;
321-
} elseif (\extension_loaded('gmp')) {
322-
$integer = gmp_strval(gmp_add(gmp_mul((string) $integer, '256'), $part));
367+
if (\extension_loaded('gmp')) {
368+
$integerAsString = gmp_strval(gmp_add(gmp_mul($integerAsString, '256'), $part));
323369
} elseif (\extension_loaded('bcmath')) {
324-
$integer = bcadd(bcmul((string) $integer, '256'), (string) $part);
370+
$integerAsString = bcadd(bcmul($integerAsString, '256'), (string) $part);
325371
} else {
326372
throw new \RuntimeException(
327373
'The gmp or bcmath extension must be installed to read this database.'
328374
);
329375
}
330376
}
331377

332-
return $integer;
378+
return $integerAsString;
333379
}
334380

335381
private function sizeFromCtrlByte(int $ctrlByte, int $offset): array
@@ -346,10 +392,22 @@ private function sizeFromCtrlByte(int $ctrlByte, int $offset): array
346392
if ($size === 29) {
347393
$size = 29 + \ord($bytes);
348394
} elseif ($size === 30) {
349-
[, $adjust] = unpack('n', $bytes);
395+
$rc = unpack('n', $bytes);
396+
if ($rc === false) {
397+
throw new InvalidDatabaseException(
398+
'Could not unpack an unsigned short value from the given bytes.'
399+
);
400+
}
401+
[, $adjust] = $rc;
350402
$size = 285 + $adjust;
351403
} else {
352-
[, $adjust] = unpack('N', "\x00" . $bytes);
404+
$rc = unpack('N', "\x00" . $bytes);
405+
if ($rc === false) {
406+
throw new InvalidDatabaseException(
407+
'Could not unpack an unsigned long value from the given bytes.'
408+
);
409+
}
410+
[, $adjust] = $rc;
353411
$size = $adjust + 65821;
354412
}
355413

@@ -365,7 +423,13 @@ private function isPlatformLittleEndian(): bool
365423
{
366424
$testint = 0x00FF;
367425
$packed = pack('S', $testint);
426+
$rc = unpack('v', $packed);
427+
if ($rc === false) {
428+
throw new InvalidDatabaseException(
429+
'Could not unpack an unsigned short value from the given bytes.'
430+
);
431+
}
368432

369-
return $testint === current(unpack('v', $packed));
433+
return $testint === current($rc);
370434
}
371435
}

src/MaxMind/Db/Reader/Util.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
class Util
88
{
99
/**
10-
* @param resource $stream
10+
* @param resource $stream
11+
* @param int<0, max> $numberOfBytes
1112
*/
1213
public static function read($stream, int $offset, int $numberOfBytes): string
1314
{

0 commit comments

Comments
 (0)