From b1e6db3244713207df1ff86b77779fdf01f0c22a Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 12 Sep 2023 00:12:16 +0100 Subject: [PATCH 1/7] Modernize Util.php Signed-off-by: Kamil Tekiela --- src/Util.php | 47 +++++++++------------------------------------- tests/UtilTest.php | 17 ++--------------- 2 files changed, 11 insertions(+), 53 deletions(-) diff --git a/src/Util.php b/src/Util.php index 62a84c7..8a761b5 100644 --- a/src/Util.php +++ b/src/Util.php @@ -28,16 +28,14 @@ use function current; use function pack; use function sprintf; -use function strlen; +use function strrev; use function unpack; class Util { - /** @var bool|null */ - private static $littleEndian = null; + private static bool|null $littleEndian = null; - /** @var array */ - private static $shapeNames = [ + private const SHAPE_NAMES = [ 0 => 'Null Shape', 1 => 'Point', 3 => 'PolyLine', @@ -59,47 +57,24 @@ class Util * * @param string $type type for unpack() * @param string|false $data Data to process - * - * @return mixed|false */ - public static function loadData(string $type, $data) + public static function loadData(string $type, string|false $data): mixed { - if ($data === false) { - return false; - } - - if (strlen($data) === 0) { + if ($data === false || $data === '') { return false; } $tmp = unpack($type, $data); - return $tmp === false ? $tmp : current($tmp); - } - - /** - * Changes endianity. - * - * @param string $binValue Binary value - */ - public static function swap(string $binValue): string - { - $result = $binValue[strlen($binValue) - 1]; - for ($i = strlen($binValue) - 2; $i >= 0; --$i) { - $result .= $binValue[$i]; - } - - return $result; + return $tmp === false ? false : current($tmp); } /** * Encodes double value to correct endianity. - * - * @param float $value Value to pack */ public static function packDouble(float $value): string { - $bin = pack('d', (float) $value); + $bin = pack('d', $value); if (self::$littleEndian === null) { self::$littleEndian = (pack('L', 1) === pack('V', 1)); @@ -109,7 +84,7 @@ public static function packDouble(float $value): string return $bin; } - return self::swap($bin); + return strrev($bin); } /** @@ -117,10 +92,6 @@ public static function packDouble(float $value): string */ public static function nameShape(int $type): string { - if (isset(self::$shapeNames[$type])) { - return self::$shapeNames[$type]; - } - - return sprintf('Shape %d', $type); + return self::SHAPE_NAMES[$type] ?? sprintf('Shape %d', $type); } } diff --git a/tests/UtilTest.php b/tests/UtilTest.php index dc812a7..4318d21 100644 --- a/tests/UtilTest.php +++ b/tests/UtilTest.php @@ -34,11 +34,11 @@ class UtilTest extends TestCase * * @param string $type Data type * @param string|false $data Data to parse - * @param string|false $expected Expected result + * @param mixed $expected Expected result * * @dataProvider data */ - public function testLoadData(string $type, $data, $expected): void + public function testLoadData(string $type, string|false $data, mixed $expected): void { $this->assertEquals( $expected, @@ -48,8 +48,6 @@ public function testLoadData(string $type, $data, $expected): void /** * Data provider for loadData tests. - * - * @return array */ public static function data(): array { @@ -71,15 +69,4 @@ public static function data(): array ], ]; } - - /** - * Test for byte order changes. - */ - public function testSwap(): void - { - $this->assertEquals( - "\x01\x02\x03\x04", - Util::swap("\x04\x03\x02\x01") - ); - } } From 2d84dcc80fc749d49027f36871d6ce5b09a923a7 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 12 Sep 2023 00:23:12 +0100 Subject: [PATCH 2/7] Modernize ShapeRecord.php Signed-off-by: Kamil Tekiela Update ShapeRecord.php r --- phpstan-baseline.neon | 31 +++++- src/ShapeRecord.php | 229 +++++++++++++----------------------------- 2 files changed, 100 insertions(+), 160 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 63f71ec..61836d7 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -50,6 +50,16 @@ parameters: count: 1 path: src/ShapeFile.php + - + message: "#^Parameter \\#2 \\$shpFile of method PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:loadFromFile\\(\\) expects resource, resource\\|null given\\.$#" + count: 1 + path: src/ShapeFile.php + + - + message: "#^Parameter \\#3 \\$dbfFile of method PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:loadFromFile\\(\\) expects resource\\|false, resource\\|null given\\.$#" + count: 1 + path: src/ShapeFile.php + - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeFile\\:\\:\\$dbfFile \\(resource\\|null\\) does not accept resource\\|false\\.$#" count: 2 @@ -91,17 +101,32 @@ parameters: path: src/ShapeFile.php - - message: "#^Parameter \\#2 \\$record_number of function dbase_get_record_with_names expects int, int\\|null given\\.$#" + message: "#^Parameter \\#1 \\$dbase_identifier of function dbase_add_record expects resource, resource\\|false given\\.$#" count: 1 path: src/ShapeRecord.php - - message: "#^Parameter \\#3 \\$record_number of function dbase_replace_record expects int, int\\|null given\\.$#" + message: "#^Parameter \\#1 \\$dbase_identifier of function dbase_get_record_with_names expects resource, resource\\|false given\\.$#" + count: 1 + path: src/ShapeRecord.php + + - + message: "#^Parameter \\#1 \\$dbase_identifier of function dbase_numrecords expects resource, resource\\|false given\\.$#" + count: 1 + path: src/ShapeRecord.php + + - + message: "#^Parameter \\#1 \\$dbase_identifier of function dbase_replace_record expects resource, resource\\|false given\\.$#" count: 1 path: src/ShapeRecord.php - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:\\$dbfFile \\(resource\\) in isset\\(\\) is not nullable\\.$#" + message: "#^Parameter \\#2 \\$record_number of function dbase_get_record_with_names expects int, int\\|null given\\.$#" + count: 1 + path: src/ShapeRecord.php + + - + message: "#^Parameter \\#3 \\$record_number of function dbase_replace_record expects int, int\\|null given\\.$#" count: 1 path: src/ShapeRecord.php diff --git a/src/ShapeRecord.php b/src/ShapeRecord.php index 331b5cc..555a807 100644 --- a/src/ShapeRecord.php +++ b/src/ShapeRecord.php @@ -41,15 +41,17 @@ class ShapeRecord { /** @var resource */ private $shpFile = null; - /** @var resource */ - private $dbfFile = null; + + /** @var resource|false */ + private $dbfFile = false; + /** @var ShapeFile */ private $shapeFile = null; /** @var int */ private $size = 0; - /** @var int */ - private $read = 0; + + private int $read = 0; /** @var int|null */ public $recordNumber = null; @@ -57,13 +59,11 @@ class ShapeRecord /** @var int */ public $shapeType = null; - /** @var string */ - public $lastError = ''; + public string $lastError = ''; + + public array $shpData = []; - /** @var array */ - public $shpData = []; - /** @var array */ - public $dbfData = []; + public array $dbfData = []; public function __construct(int $shapeType) { @@ -73,11 +73,11 @@ public function __construct(int $shapeType) /** * Loads record from files. * - * @param ShapeFile $shapeFile The ShapeFile object - * @param resource $shpFile Opened SHP file (by reference) - * @param resource $dbfFile Opened DBF file (by reference) + * @param ShapeFile $shapeFile The ShapeFile object + * @param resource $shpFile Opened SHP file + * @param resource|false $dbfFile Opened DBF file */ - public function loadFromFile(ShapeFile &$shapeFile, &$shpFile, &$dbfFile): void + public function loadFromFile(ShapeFile $shapeFile, $shpFile, $dbfFile): void { $this->shapeFile = $shapeFile; $this->shpFile = $shpFile; @@ -89,50 +89,22 @@ public function loadFromFile(ShapeFile &$shapeFile, &$shpFile, &$dbfFile): void return; } - switch ($this->shapeType) { - case 0: - $this->loadNullRecord(); - break; - case 1: - $this->loadPointRecord(); - break; - case 21: - $this->loadPointMRecord(); - break; - case 11: - $this->loadPointZRecord(); - break; - case 3: - $this->loadPolyLineRecord(); - break; - case 23: - $this->loadPolyLineMRecord(); - break; - case 13: - $this->loadPolyLineZRecord(); - break; - case 5: - $this->loadPolygonRecord(); - break; - case 25: - $this->loadPolygonMRecord(); - break; - case 15: - $this->loadPolygonZRecord(); - break; - case 8: - $this->loadMultiPointRecord(); - break; - case 28: - $this->loadMultiPointMRecord(); - break; - case 18: - $this->loadMultiPointZRecord(); - break; - default: - $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType)); - break; - } + match ($this->shapeType) { + 0 => $this->loadNullRecord(), + 1 => $this->loadPointRecord(), + 21 => $this->loadPointMRecord(), + 11 => $this->loadPointZRecord(), + 3 => $this->loadPolyLineRecord(), + 23 => $this->loadPolyLineMRecord(), + 13 => $this->loadPolyLineZRecord(), + 5 => $this->loadPolygonRecord(), + 25 => $this->loadPolygonMRecord(), + 15 => $this->loadPolygonZRecord(), + 8 => $this->loadMultiPointRecord(), + 28 => $this->loadMultiPointMRecord(), + 18 => $this->loadMultiPointZRecord(), + default => $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType)), + }; /* We need to skip rest of the record */ while ($this->read < $this->size) { @@ -144,7 +116,7 @@ public function loadFromFile(ShapeFile &$shapeFile, &$shpFile, &$dbfFile): void $this->setError(sprintf('Failed to parse record, read=%d, size=%d', $this->read, $this->size)); } - if (! ShapeFile::supportsDbase() || ! isset($this->dbfFile)) { + if (! ShapeFile::supportsDbase()) { return; } @@ -154,63 +126,35 @@ public function loadFromFile(ShapeFile &$shapeFile, &$shpFile, &$dbfFile): void /** * Saves record to files. * - * @param resource $shpFile Opened SHP file - * @param resource $dbfFile Opened DBF file - * @param int $recordNumber Record number + * @param resource $shpFile Opened SHP file + * @param resource|false $dbfFile Opened DBF file + * @param int $recordNumber Record number */ - public function saveToFile(&$shpFile, &$dbfFile, int $recordNumber): void + public function saveToFile($shpFile, $dbfFile, int $recordNumber): void { $this->shpFile = $shpFile; $this->dbfFile = $dbfFile; $this->recordNumber = $recordNumber; $this->saveHeaders(); - switch ($this->shapeType) { - case 0: - // Nothing to save - break; - case 1: - $this->savePointRecord(); - break; - case 21: - $this->savePointMRecord(); - break; - case 11: - $this->savePointZRecord(); - break; - case 3: - $this->savePolyLineRecord(); - break; - case 23: - $this->savePolyLineMRecord(); - break; - case 13: - $this->savePolyLineZRecord(); - break; - case 5: - $this->savePolygonRecord(); - break; - case 25: - $this->savePolygonMRecord(); - break; - case 15: - $this->savePolygonZRecord(); - break; - case 8: - $this->saveMultiPointRecord(); - break; - case 28: - $this->saveMultiPointMRecord(); - break; - case 18: - $this->saveMultiPointZRecord(); - break; - default: - $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType)); - break; - } - - if (! ShapeFile::supportsDbase() || $this->dbfFile === null) { + match ($this->shapeType) { + 0 => null, // Nothing to save + 1 => $this->savePointRecord(), + 21 => $this->savePointMRecord(), + 11 => $this->savePointZRecord(), + 3 => $this->savePolyLineRecord(), + 23 => $this->savePolyLineMRecord(), + 13 => $this->savePolyLineZRecord(), + 5 => $this->savePolygonRecord(), + 25 => $this->savePolygonMRecord(), + 15 => $this->savePolygonZRecord(), + 8 => $this->saveMultiPointRecord(), + 28 => $this->saveMultiPointMRecord(), + 18 => $this->saveMultiPointZRecord(), + default => $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType)), + }; + + if (! ShapeFile::supportsDbase() || $this->dbfFile === false) { return; } @@ -225,22 +169,19 @@ public function saveToFile(&$shpFile, &$dbfFile, int $recordNumber): void public function updateDBFInfo(array $header): void { $tmp = $this->dbfData; - unset($this->dbfData); $this->dbfData = []; - foreach ($header as $value) { - $this->dbfData[$value[0]] = $tmp[$value[0]] ?? ''; + foreach ($header as [$value]) { + $this->dbfData[$value] = $tmp[$value] ?? ''; } } /** * Reads data. * - * @param string $type type for unpack() - * @param int $count number of bytes - * - * @return mixed + * @param string $type type for unpack() + * @param int<0, max> $count number of bytes */ - private function loadData(string $type, int $count) + private function loadData(string $type, int $count): mixed { $data = $this->shapeFile->readSHP($count); if ($data === false) { @@ -285,12 +226,10 @@ private function saveHeaders(): void private function loadPoint(): array { - $data = []; - - $data['x'] = $this->loadData('d', 8); - $data['y'] = $this->loadData('d', 8); - - return $data; + return [ + 'x' => $this->loadData('d', 8), + 'y' => $this->loadData('d', 8), + ]; } private function loadPointM(): array @@ -613,13 +552,7 @@ private function savePolygonZRecord(): void private function adjustBBox(array $point): void { // Adjusts bounding box based on point - $directions = [ - 'x', - 'y', - 'z', - 'm', - ]; - foreach ($directions as $direction) { + foreach (['x', 'y', 'z', 'm'] as $direction) { if (! isset($point[$direction])) { continue; } @@ -638,25 +571,9 @@ private function adjustBBox(array $point): void } } - /** - * Sets dimension to 0 if not set. - * - * @param array $point Point to check - * @param string $dimension Dimension to check - * - * @return array - */ - private function fixPoint(array $point, string $dimension): array - { - if (! isset($point[$dimension])) { - $point[$dimension] = 0.0; // no_value - } - - return $point; - } - /** * Adjust point and bounding box when adding point. + * Sets dimension to 0 if not set. * * @param array $point Point data * @@ -666,10 +583,10 @@ private function adjustPoint(array $point): array { $type = $this->shapeType / 10; if ($type >= 2) { - $point = $this->fixPoint($point, 'm'); + $point['m'] ??= 0.0; } elseif ($type >= 1) { - $point = $this->fixPoint($point, 'z'); - $point = $this->fixPoint($point, 'm'); + $point['z'] ??= 0.0; + $point['m'] ??= 0.0; } return $point; @@ -744,7 +661,7 @@ public function deletePoint(int $pointIndex = 0, int $partIndex = 0): void $this->shpData['m'] = 0.0; } - if (in_array($this->shapeType, [11])) { + if ($this->shapeType === 11) { $this->shpData['z'] = 0.0; } @@ -869,19 +786,17 @@ private function loadDBFData(): void private function saveDBFData(): void { - if (count($this->dbfData) === 0) { + if ($this->dbfData === []) { return; } unset($this->dbfData['deleted']); if ($this->recordNumber <= dbase_numrecords($this->dbfFile)) { if (! dbase_replace_record($this->dbfFile, array_values($this->dbfData), $this->recordNumber)) { - $this->setError('I wasn\'t possible to update the information in the DBF file.'); - } - } else { - if (! dbase_add_record($this->dbfFile, array_values($this->dbfData))) { - $this->setError('I wasn\'t possible to add the information to the DBF file.'); + $this->setError("I wasn't possible to update the information in the DBF file."); } + } elseif (! dbase_add_record($this->dbfFile, array_values($this->dbfData))) { + $this->setError("I wasn't possible to add the information to the DBF file."); } } From 04741d80c6b2936de1278f86ff5220f8dd446dc3 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 12 Sep 2023 00:35:49 +0100 Subject: [PATCH 3/7] Modernize ShapeFile.php Signed-off-by: Kamil Tekiela --- phpcs.xml.dist | 2 - phpstan-baseline.neon | 54 ++------------ src/ShapeFile.php | 156 ++++++++++++++++++---------------------- tests/ShapeFileTest.php | 6 -- 4 files changed, 77 insertions(+), 141 deletions(-) diff --git a/phpcs.xml.dist b/phpcs.xml.dist index dbc162a..0175650 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -17,8 +17,6 @@ - - diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 61836d7..01679be 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,8 +1,8 @@ parameters: ignoreErrors: - - message: "#^Parameter \\#1 \\$file of method PhpMyAdmin\\\\ShapeFile\\\\ShapeFile\\:\\:saveBBox\\(\\) expects resource, resource\\|null given\\.$#" - count: 2 + message: "#^Parameter \\#1 \\$shpFile of method PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:saveToFile\\(\\) expects resource, resource\\|false given\\.$#" + count: 1 path: src/ShapeFile.php - @@ -12,12 +12,7 @@ parameters: - message: "#^Parameter \\#1 \\$stream of function feof expects resource, resource\\|false given\\.$#" - count: 1 - path: src/ShapeFile.php - - - - message: "#^Parameter \\#1 \\$stream of function feof expects resource, resource\\|null given\\.$#" - count: 1 + count: 2 path: src/ShapeFile.php - @@ -26,13 +21,8 @@ parameters: path: src/ShapeFile.php - - message: "#^Parameter \\#1 \\$stream of function fwrite expects resource, resource\\|null given\\.$#" - count: 10 - path: src/ShapeFile.php - - - - message: "#^Parameter \\#1 \\$string of function strtoupper expects string, mixed given\\.$#" - count: 1 + message: "#^Parameter \\#1 \\$stream of function fwrite expects resource, resource\\|false given\\.$#" + count: 2 path: src/ShapeFile.php - @@ -41,42 +31,12 @@ parameters: path: src/ShapeFile.php - - message: "#^Parameter \\#1 \\$type of static method PhpMyAdmin\\\\ShapeFile\\\\Util\\:\\:nameShape\\(\\) expects int, int\\|false given\\.$#" - count: 1 - path: src/ShapeFile.php - - - - message: "#^Parameter \\#2 \\$length of function fread expects int\\<0, max\\>, int given\\.$#" - count: 1 - path: src/ShapeFile.php - - - - message: "#^Parameter \\#2 \\$shpFile of method PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:loadFromFile\\(\\) expects resource, resource\\|null given\\.$#" - count: 1 - path: src/ShapeFile.php - - - - message: "#^Parameter \\#3 \\$dbfFile of method PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:loadFromFile\\(\\) expects resource\\|false, resource\\|null given\\.$#" - count: 1 - path: src/ShapeFile.php - - - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeFile\\:\\:\\$dbfFile \\(resource\\|null\\) does not accept resource\\|false\\.$#" - count: 2 - path: src/ShapeFile.php - - - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeFile\\:\\:\\$shapeType \\(int\\|false\\) does not accept mixed\\.$#" - count: 1 - path: src/ShapeFile.php - - - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeFile\\:\\:\\$shpFile \\(resource\\|null\\) does not accept resource\\|false\\.$#" + message: "#^Parameter \\#2 \\$shpFile of method PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:loadFromFile\\(\\) expects resource, resource\\|false given\\.$#" count: 1 path: src/ShapeFile.php - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeFile\\:\\:\\$shxFile \\(resource\\|null\\) does not accept resource\\|false\\.$#" + message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeFile\\:\\:\\$shapeType \\(int\\) does not accept mixed\\.$#" count: 1 path: src/ShapeFile.php diff --git a/src/ShapeFile.php b/src/ShapeFile.php index cd13f70..0a286b8 100644 --- a/src/ShapeFile.php +++ b/src/ShapeFile.php @@ -25,7 +25,6 @@ namespace PhpMyAdmin\ShapeFile; -use function array_push; use function chr; use function count; use function extension_loaded; @@ -36,7 +35,6 @@ use function fread; use function fwrite; use function in_array; -use function is_array; use function is_readable; use function ord; use function pack; @@ -53,39 +51,29 @@ */ class ShapeFile { - public const MAGIC = 0x270a; + public final const MAGIC = 0x270a; - /** @var string|null */ - public $fileName; + /** @var resource|false */ + private $shpFile = false; - /** @var resource|null */ - private $shpFile = null; - /** @var resource|null */ - private $shxFile = null; - /** @var resource|null */ - private $dbfFile = null; + /** @var resource|false */ + private $shxFile = false; - /** @var array|null */ - private $dbfHeader; + /** @var resource|false */ + private $dbfFile = false; - /** @var string */ - public $lastError = ''; + private array|null $dbfHeader = null; - /** @var array */ - public $boundingBox = [ - 'xmin' => 0.0, - 'ymin' => 0.0, - 'xmax' => 0.0, - 'ymax' => 0.0, - ]; - /** @var int */ - private $fileLength = 0; + public string $lastError = ''; - /** @var int|false */ - public $shapeType = 0; + /** + * The value for file length is the total length of the file in 16-bit words + * (including the fifty 16-bit words that make up the header). + */ + private int $fileLength = 50; - /** @var array */ - public $records = []; + /** @var array */ + public array $records = []; /** * Checks whether dbase manipulations are supported. @@ -101,24 +89,15 @@ public static function supportsDbase(): bool * @param string|null $fileName File name */ public function __construct( - int $shapeType, - array $boundingBox = [ + public int $shapeType, + public array $boundingBox = [ 'xmin' => 0.0, 'ymin' => 0.0, 'xmax' => 0.0, 'ymax' => 0.0, ], - ?string $fileName = null + public ?string $fileName = null ) { - $this->shapeType = $shapeType; - $this->boundingBox = $boundingBox; - $this->fileName = $fileName; - - /** - * The value for file length is the total length of the file in 16-bit words - * (including the fifty 16-bit words that make up the header). - */ - $this->fileLength = 50; } /** @@ -128,7 +107,7 @@ public function __construct( */ public function loadFromFile(string $fileName): bool { - if (! empty($fileName)) { + if ($fileName !== '') { $this->fileName = $fileName; $result = $this->openSHPFile(); } else { @@ -295,19 +274,15 @@ public function setDBFHeader(array $header): void { $this->dbfHeader = $header; - $count = count($this->records); - for ($i = 0; $i < $count; ++$i) { - $this->records[$i]->updateDBFInfo($header); + foreach ($this->records as $record) { + $record->updateDBFInfo($header); } } /** * Lookups value in the DBF file and returns index. - * - * @param string $field Field to match - * @param mixed $value Value to match */ - public function getIndexFromDBFData(string $field, $value): int + public function getIndexFromDBFData(string $field, string $value): int { foreach ($this->records as $index => $record) { if ( @@ -323,6 +298,8 @@ public function getIndexFromDBFData(string $field, $value): int /** * Loads DBF metadata. + * + * @return array{string, string, int, int}[] */ private function loadDBFHeader(): array { @@ -330,31 +307,30 @@ private function loadDBFHeader(): array $result = []; $i = 1; - $inHeader = true; - - while ($inHeader) { - if (! feof($DBFFile)) { - $buff32 = fread($DBFFile, 32); - if ($i > 1) { - if (substr($buff32, 0, 1) === chr(13)) { - $inHeader = false; - } else { - $pos = strpos(substr($buff32, 0, 10), chr(0)); - $pos = ($pos === false ? 10 : $pos); - - $fieldName = substr($buff32, 0, $pos); - $fieldType = substr($buff32, 11, 1); - $fieldLen = ord(substr($buff32, 16, 1)); - $fieldDec = ord(substr($buff32, 17, 1)); - - array_push($result, [$fieldName, $fieldType, $fieldLen, $fieldDec]); - } + + while (true) { + if (feof($DBFFile)) { + break; + } + + $buff32 = fread($DBFFile, 32); + if ($i > 1) { + if (substr($buff32, 0, 1) === chr(13)) { + break; } - ++$i; - } else { - $inHeader = false; + $pos = strpos(substr($buff32, 0, 10), chr(0)); + $pos = ($pos === false ? 10 : $pos); + + $fieldName = substr($buff32, 0, $pos); + $fieldType = substr($buff32, 11, 1); + $fieldLen = ord(substr($buff32, 16, 1)); + $fieldDec = ord(substr($buff32, 17, 1)); + + $result[] = [$fieldName, $fieldType, $fieldLen, $fieldDec]; } + + ++$i; } fclose($DBFFile); @@ -367,7 +343,7 @@ private function loadDBFHeader(): array */ private function deleteRecordFromDBF(int $index): void { - if ($this->dbfFile === null || ! @dbase_delete_record($this->dbfFile, $index)) { + if ($this->dbfFile === false || ! @dbase_delete_record($this->dbfFile, $index)) { return; } @@ -388,7 +364,7 @@ private function loadHeaders(): bool /* Skip 20 unused bytes */ $this->readSHP(20); - $this->fileLength = Util::loadData('N', $this->readSHP(4)); + $this->fileLength = (int) Util::loadData('N', $this->readSHP(4)); /* We currently ignore version */ $this->readSHP(4); @@ -447,12 +423,20 @@ private function saveBBox($file): void */ private function saveHeaders(): void { + if ($this->shpFile === false) { + return; + } + fwrite($this->shpFile, pack('NNNNNN', self::MAGIC, 0, 0, 0, 0, 0)); fwrite($this->shpFile, pack('N', $this->fileLength)); fwrite($this->shpFile, pack('V', 1000)); fwrite($this->shpFile, pack('V', $this->shapeType)); $this->saveBBox($this->shpFile); + if ($this->shxFile === false) { + return; + } + fwrite($this->shxFile, pack('NNNNNN', self::MAGIC, 0, 0, 0, 0, 0)); fwrite($this->shxFile, pack('N', 50 + 4 * count($this->records))); fwrite($this->shxFile, pack('V', 1000)); @@ -491,7 +475,7 @@ private function loadRecords(): bool private function saveRecords(): void { $offset = 50; - if (! is_array($this->records) || (count($this->records) <= 0)) { + if ($this->records === []) { return; } @@ -545,12 +529,12 @@ private function openSHPFile(bool $toWrite = false): bool */ private function closeSHPFile(): void { - if (! $this->shpFile) { + if ($this->shpFile === false) { return; } fclose($this->shpFile); - $this->shpFile = null; + $this->shpFile = false; } /** @@ -570,12 +554,12 @@ private function openSHXFile(bool $toWrite = false): bool */ private function closeSHXFile(): void { - if (! $this->shxFile) { + if ($this->shxFile === false) { return; } fclose($this->shxFile); - $this->shxFile = null; + $this->shxFile = false; } /** @@ -583,8 +567,8 @@ private function closeSHXFile(): void */ private function createDBFFile(): bool { - if (! self::supportsDbase() || ! is_array($this->dbfHeader) || count($this->dbfHeader) === 0) { - $this->dbfFile = null; + if (! self::supportsDbase() || $this->dbfHeader === null || $this->dbfHeader === []) { + $this->dbfFile = false; return true; } @@ -613,7 +597,7 @@ private function createDBFFile(): bool private function openDBFFile(): bool { if (! self::supportsDbase()) { - $this->dbfFile = null; + $this->dbfFile = false; return true; } @@ -626,7 +610,7 @@ private function openDBFFile(): bool } $this->dbfFile = @dbase_open($dbfName, 0); - if (! $this->dbfFile) { + if ($this->dbfFile === false) { $this->setError(sprintf('It wasn\'t possible to open the DBase file "%s"', $dbfName)); return false; @@ -640,12 +624,12 @@ private function openDBFFile(): bool */ private function closeDBFFile(): void { - if (! $this->dbfFile) { + if ($this->dbfFile === false) { return; } dbase_close($this->dbfFile); - $this->dbfFile = null; + $this->dbfFile = false; } /** @@ -659,11 +643,11 @@ public function setError(string $error): void /** * Reads given number of bytes from SHP file. * - * @return string|false + * @param int<0, max> $bytes */ - public function readSHP(int $bytes) + public function readSHP(int $bytes): string|false { - if ($this->shpFile === null) { + if ($this->shpFile === false) { return false; } diff --git a/tests/ShapeFileTest.php b/tests/ShapeFileTest.php index 604d672..1e7bf0d 100644 --- a/tests/ShapeFileTest.php +++ b/tests/ShapeFileTest.php @@ -57,8 +57,6 @@ public function testLoad(string $filename, int $records, ?int $parts): void /** * Data provider for file loading tests. - * - * @return array */ public static function provideFiles(): array { @@ -137,8 +135,6 @@ public function testGetDBFHeader(): void /** * Data provider for file loading error tests. - * - * @return array */ public static function provideErrorFiles(): array { @@ -398,8 +394,6 @@ public function testMeasureShapeSaveLoad(int $type, array $points): void /** * Data provider for save/load testing. - * - * @return array */ public static function shapes(): array { From dd1362be80196ca4cca348f68de8bef2310b5c1a Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 12 Sep 2023 13:33:00 +0100 Subject: [PATCH 4/7] Fix the null/false mess Signed-off-by: Kamil Tekiela --- phpstan-baseline.neon | 40 ---------------------------------------- src/ShapeFile.php | 9 ++++++++- src/ShapeRecord.php | 8 ++++++-- 3 files changed, 14 insertions(+), 43 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 01679be..d683f6d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,30 +1,10 @@ parameters: ignoreErrors: - - - message: "#^Parameter \\#1 \\$shpFile of method PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:saveToFile\\(\\) expects resource, resource\\|false given\\.$#" - count: 1 - path: src/ShapeFile.php - - - - message: "#^Parameter \\#1 \\$stream of function fclose expects resource, resource\\|false given\\.$#" - count: 1 - path: src/ShapeFile.php - - message: "#^Parameter \\#1 \\$stream of function feof expects resource, resource\\|false given\\.$#" - count: 2 - path: src/ShapeFile.php - - - - message: "#^Parameter \\#1 \\$stream of function fread expects resource, resource\\|false given\\.$#" count: 1 path: src/ShapeFile.php - - - message: "#^Parameter \\#1 \\$stream of function fwrite expects resource, resource\\|false given\\.$#" - count: 2 - path: src/ShapeFile.php - - message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false given\\.$#" count: 6 @@ -60,26 +40,6 @@ parameters: count: 1 path: src/ShapeFile.php - - - message: "#^Parameter \\#1 \\$dbase_identifier of function dbase_add_record expects resource, resource\\|false given\\.$#" - count: 1 - path: src/ShapeRecord.php - - - - message: "#^Parameter \\#1 \\$dbase_identifier of function dbase_get_record_with_names expects resource, resource\\|false given\\.$#" - count: 1 - path: src/ShapeRecord.php - - - - message: "#^Parameter \\#1 \\$dbase_identifier of function dbase_numrecords expects resource, resource\\|false given\\.$#" - count: 1 - path: src/ShapeRecord.php - - - - message: "#^Parameter \\#1 \\$dbase_identifier of function dbase_replace_record expects resource, resource\\|false given\\.$#" - count: 1 - path: src/ShapeRecord.php - - message: "#^Parameter \\#2 \\$record_number of function dbase_get_record_with_names expects int, int\\|null given\\.$#" count: 1 diff --git a/src/ShapeFile.php b/src/ShapeFile.php index 0a286b8..6985813 100644 --- a/src/ShapeFile.php +++ b/src/ShapeFile.php @@ -304,6 +304,9 @@ public function getIndexFromDBFData(string $field, string $value): int private function loadDBFHeader(): array { $DBFFile = fopen($this->getFilename('.dbf'), 'r'); + if ($DBFFile === false) { + return []; + } $result = []; $i = 1; @@ -449,6 +452,10 @@ private function saveHeaders(): void */ private function loadRecords(): bool { + if ($this->shpFile === false) { + return false; + } + /* Need to start at offset 100 */ while (! $this->eofSHP()) { $record = new ShapeRecord(-1); @@ -475,7 +482,7 @@ private function loadRecords(): bool private function saveRecords(): void { $offset = 50; - if ($this->records === []) { + if ($this->records === [] || $this->shxFile === false || $this->shpFile === false) { return; } diff --git a/src/ShapeRecord.php b/src/ShapeRecord.php index 555a807..6b7fa15 100644 --- a/src/ShapeRecord.php +++ b/src/ShapeRecord.php @@ -40,7 +40,7 @@ class ShapeRecord { /** @var resource */ - private $shpFile = null; + private $shpFile; /** @var resource|false */ private $dbfFile = false; @@ -780,13 +780,17 @@ public function getContentLength(): ?int private function loadDBFData(): void { + if ($this->dbfFile === false) { + return; + } + $this->dbfData = @dbase_get_record_with_names($this->dbfFile, $this->recordNumber); unset($this->dbfData['deleted']); } private function saveDBFData(): void { - if ($this->dbfData === []) { + if ($this->dbfData === [] || $this->dbfFile === false) { return; } From fc55e056e8e9b6c865cf1f1564d7aae07c549cd6 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 12 Sep 2023 13:48:25 +0100 Subject: [PATCH 5/7] Add type for $shapeType Signed-off-by: Kamil Tekiela --- phpstan-baseline.neon | 39 +++++++-------------------------------- src/ShapeFile.php | 5 +++-- src/ShapeRecord.php | 12 ++++++++---- 3 files changed, 18 insertions(+), 38 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d683f6d..5add4cb 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,44 +1,29 @@ parameters: ignoreErrors: - - message: "#^Parameter \\#1 \\$stream of function feof expects resource, resource\\|false given\\.$#" - count: 1 - path: src/ShapeFile.php - - - - message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false given\\.$#" - count: 6 - path: src/ShapeFile.php - - - - message: "#^Parameter \\#2 \\$shpFile of method PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:loadFromFile\\(\\) expects resource, resource\\|false given\\.$#" - count: 1 - path: src/ShapeFile.php - - - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeFile\\:\\:\\$shapeType \\(int\\) does not accept mixed\\.$#" + message: "#^Cannot cast mixed to int\\.$#" count: 1 path: src/ShapeFile.php - - message: "#^Result of && is always false\\.$#" + message: "#^Parameter \\#1 \\$stream of function feof expects resource, resource\\|false given\\.$#" count: 1 path: src/ShapeFile.php - - message: "#^Result of \\|\\| is always false\\.$#" - count: 1 + message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false given\\.$#" + count: 6 path: src/ShapeFile.php - - message: "#^Strict comparison using \\=\\=\\= between int and '' will always evaluate to false\\.$#" + message: "#^Parameter \\#2 \\$shpFile of method PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:loadFromFile\\(\\) expects resource, resource\\|false given\\.$#" count: 1 path: src/ShapeFile.php - - message: "#^Strict comparison using \\=\\=\\= between int and false will always evaluate to false\\.$#" + message: "#^Cannot cast mixed to int\\.$#" count: 1 - path: src/ShapeFile.php + path: src/ShapeRecord.php - message: "#^Parameter \\#2 \\$record_number of function dbase_get_record_with_names expects int, int\\|null given\\.$#" @@ -55,16 +40,6 @@ parameters: count: 1 path: src/ShapeRecord.php - - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:\\$shapeType \\(int\\) does not accept false\\.$#" - count: 1 - path: src/ShapeRecord.php - - - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:\\$shapeType \\(int\\) does not accept mixed\\.$#" - count: 1 - path: src/ShapeRecord.php - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:\\$size \\(int\\) does not accept mixed\\.$#" count: 1 diff --git a/src/ShapeFile.php b/src/ShapeFile.php index 6985813..8cd1d0d 100644 --- a/src/ShapeFile.php +++ b/src/ShapeFile.php @@ -372,7 +372,8 @@ private function loadHeaders(): bool /* We currently ignore version */ $this->readSHP(4); - $this->shapeType = Util::loadData('V', $this->readSHP(4)); + $shapeType = Util::loadData('V', $this->readSHP(4)); + $this->shapeType = $shapeType === false ? -1 : (int) $shapeType; $this->boundingBox = []; $this->boundingBox['xmin'] = Util::loadData('d', $this->readSHP(8)); @@ -466,7 +467,7 @@ private function loadRecords(): bool return false; } - if (($record->shapeType === false || $record->shapeType === '') && $this->eofSHP()) { + if (($record->shapeType === -1) && $this->eofSHP()) { break; } diff --git a/src/ShapeRecord.php b/src/ShapeRecord.php index 6b7fa15..8dd242d 100644 --- a/src/ShapeRecord.php +++ b/src/ShapeRecord.php @@ -56,8 +56,7 @@ class ShapeRecord /** @var int|null */ public $recordNumber = null; - /** @var int */ - public $shapeType = null; + public int $shapeType; public string $lastError = ''; @@ -198,7 +197,7 @@ private function loadData(string $type, int $count): mixed */ private function loadHeaders(): void { - $this->shapeType = false; + $this->shapeType = -1; $this->recordNumber = $this->loadData('N', 4); if ($this->recordNumber === false) { return; @@ -211,7 +210,12 @@ private function loadHeaders(): void } $this->size = ($this->size * 2) + 8; - $this->shapeType = $this->loadData('V', 4); + $shapeType = $this->loadData('V', 4); + if ($shapeType === false) { + return; + } + + $this->shapeType = (int) $shapeType; } /** From 80da020344f66987e50a3cad2d32a7661193de2d Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 12 Sep 2023 13:58:57 +0100 Subject: [PATCH 6/7] Add type to $recordNumber Signed-off-by: Kamil Tekiela --- phpstan-baseline.neon | 17 +---------------- src/ShapeRecord.php | 9 +++++---- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 5add4cb..b7c2703 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -22,22 +22,7 @@ parameters: - message: "#^Cannot cast mixed to int\\.$#" - count: 1 - path: src/ShapeRecord.php - - - - message: "#^Parameter \\#2 \\$record_number of function dbase_get_record_with_names expects int, int\\|null given\\.$#" - count: 1 - path: src/ShapeRecord.php - - - - message: "#^Parameter \\#3 \\$record_number of function dbase_replace_record expects int, int\\|null given\\.$#" - count: 1 - path: src/ShapeRecord.php - - - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:\\$recordNumber \\(int\\|null\\) does not accept mixed\\.$#" - count: 1 + count: 2 path: src/ShapeRecord.php - diff --git a/src/ShapeRecord.php b/src/ShapeRecord.php index 8dd242d..d56c504 100644 --- a/src/ShapeRecord.php +++ b/src/ShapeRecord.php @@ -53,8 +53,7 @@ class ShapeRecord private int $read = 0; - /** @var int|null */ - public $recordNumber = null; + public int $recordNumber = 0; public int $shapeType; @@ -198,11 +197,13 @@ private function loadData(string $type, int $count): mixed private function loadHeaders(): void { $this->shapeType = -1; - $this->recordNumber = $this->loadData('N', 4); - if ($this->recordNumber === false) { + $recordNumber = $this->loadData('N', 4); + if ($recordNumber === false) { return; } + $this->recordNumber = (int) $recordNumber; + // We read the length of the record $this->size = $this->loadData('N', 4); if ($this->size === false) { From 67804b2841532c1385ab185fe45e4cb9cfdb3e24 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 12 Sep 2023 14:01:21 +0100 Subject: [PATCH 7/7] Add type to $size Signed-off-by: Kamil Tekiela --- phpstan-baseline.neon | 5 ----- src/ShapeRecord.php | 10 +++++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b7c2703..aa83f4c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -25,8 +25,3 @@ parameters: count: 2 path: src/ShapeRecord.php - - - message: "#^Property PhpMyAdmin\\\\ShapeFile\\\\ShapeRecord\\:\\:\\$size \\(int\\) does not accept mixed\\.$#" - count: 1 - path: src/ShapeRecord.php - diff --git a/src/ShapeRecord.php b/src/ShapeRecord.php index d56c504..de3fc27 100644 --- a/src/ShapeRecord.php +++ b/src/ShapeRecord.php @@ -48,8 +48,7 @@ class ShapeRecord /** @var ShapeFile */ private $shapeFile = null; - /** @var int */ - private $size = 0; + private int $size = 0; private int $read = 0; @@ -205,12 +204,13 @@ private function loadHeaders(): void $this->recordNumber = (int) $recordNumber; // We read the length of the record - $this->size = $this->loadData('N', 4); - if ($this->size === false) { + $size = $this->loadData('N', 4); + if ($size === false) { return; } - $this->size = ($this->size * 2) + 8; + $this->size = ($size * 2) + 8; + $shapeType = $this->loadData('V', 4); if ($shapeType === false) { return;