Passed
Pull Request — master (#37)
by
unknown
02:27 queued 16s
created

ShapeFile::loadHeaders()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 4.0012

Importance

Changes 0
Metric Value
cc 4
eloc 23
nc 5
nop 0
dl 0
loc 38
ccs 22
cts 23
cp 0.9565
crap 4.0012
rs 9.552
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * phpMyAdmin ShapeFile library
7
 * <https://github.com/phpmyadmin/shapefile/>.
8
 *
9
 * Copyright 2006-2007 Ovidio <ovidio AT users.sourceforge.net>
10
 * Copyright 2016 - 2017 Michal Čihař <[email protected]>
11
 *
12
 * This program is free software; you can redistribute it and/or
13
 * modify it under the terms of the GNU General Public License
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program; if not, you can download one from
23
 * https://www.gnu.org/copyleft/gpl.html.
24
 */
25
26
namespace PhpMyAdmin\ShapeFile;
27
28
use function chr;
29
use function count;
30
use function extension_loaded;
31
use function fclose;
32
use function feof;
33
use function file_exists;
34
use function fopen;
35
use function fread;
36
use function fwrite;
37
use function in_array;
38
use function is_readable;
39
use function ord;
40
use function pack;
41
use function sprintf;
42
use function str_replace;
43
use function strpos;
44
use function strtoupper;
45
use function substr;
46
use function trim;
47
use function unlink;
48
49
/**
50
 * ShapeFile class.
51
 */
52
class ShapeFile
53
{
54
    public final const MAGIC = 0x270a;
55
56
    /** @var resource|false */
57
    private $shpFile = false;
58
59
    /** @var resource|false */
60
    private $shxFile = false;
61
62
    /** @var resource|false */
63
    private $dbfFile = false;
64
65
    /** @var mixed[]|null */
66
    private array|null $dbfHeader = null;
67
68
    public string $lastError = '';
69
70
    /**
71
     * The value for file length is the total length of the file in 16-bit words
72
     * (including the fifty 16-bit words that make up the header).
73
     */
74
    private int $fileLength = 50;
75
76
    /** @var array<int, ShapeRecord> */
77
    public array $records = [];
78
79
    /**
80
     * Checks whether dbase manipulations are supported.
81
     */
82 93
    public static function supportsDbase(): bool
83
    {
84 93
        return extension_loaded('dbase');
85
    }
86
87
    /**
88
     * @param ShapeType   $shapeType   File shape type, should be same as all records
89
     * @param mixed[]     $boundingBox File bounding box
90
     * @param string|null $fileName    File name
91
     */
92 109
    public function __construct(
93
        public ShapeType $shapeType,
94
        public array $boundingBox = [
95
            'xmin' => 0.0,
96
            'ymin' => 0.0,
97
            'xmax' => 0.0,
98
            'ymax' => 0.0,
99
        ],
100
        public string|null $fileName = null,
101
    ) {
102 109
    }
103
104
    /**
105
     * Loads shapefile and dbase (if supported).
106
     *
107
     * @param string $fileName File mask to load (eg. example.*)
108
     */
109 97
    public function loadFromFile(string $fileName): bool
110
    {
111 97
        if ($fileName !== '') {
112 93
            $this->fileName = $fileName;
113 93
            $result = $this->openSHPFile();
114
        } else {
115
            /* We operate on buffer emulated by readSHP / eofSHP */
116 4
            $result = true;
117
        }
118
119 97
        if ($result && ($this->openDBFFile())) {
120 85
            if (! $this->loadHeaders()) {
121 6
                $this->closeSHPFile();
122 6
                $this->closeDBFFile();
123
124 6
                return false;
125
            }
126
127 79
            if (! $this->loadRecords()) {
128
                $this->closeSHPFile();
129
                $this->closeDBFFile();
130
131
                return false;
132
            }
133
134 79
            $this->closeSHPFile();
135 79
            $this->closeDBFFile();
136
137 79
            return true;
138
        }
139
140 12
        return false;
141
    }
142
143
    /**
144
     * Saves shapefile.
145
     *
146
     * @param string|null $fileName Name of file, otherwise existing is used
147
     */
148 55
    public function saveToFile(string|null $fileName = null): bool
149
    {
150 55
        if ($fileName !== null) {
151 55
            $this->fileName = $fileName;
152
        }
153
154 55
        if (! $this->openSHPFile(true) || (! $this->openSHXFile(true)) || (! $this->createDBFFile())) {
155
            return false;
156
        }
157
158 55
        $this->saveHeaders();
159 55
        $this->saveRecords();
160 55
        $this->closeSHPFile();
161 55
        $this->closeSHXFile();
162 55
        $this->closeDBFFile();
163
164 55
        return true;
165
    }
166
167
    /**
168
     * Generates filename with given extension.
169
     *
170
     * @param string $extension Extension to use (including dot)
171
     */
172 98
    private function getFilename(string $extension): string
173
    {
174 98
        return str_replace('.*', $extension, (string) $this->fileName);
175
    }
176
177
    /**
178
     * Updates bounding box based on shpData.
179
     *
180
     * @param string  $type Type of box
181
     * @param mixed[] $data ShapeRecord shpData
182
     */
183 51
    private function updateBBox(string $type, array $data): void
184
    {
185 51
        $min = $type . 'min';
186 51
        $max = $type . 'max';
187
188
        if (
189 51
            ! isset($this->boundingBox[$min])
190 51
            || $this->boundingBox[$min] == 0.0 // phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators
191 51
            || ($this->boundingBox[$min] > $data[$min])
192
        ) {
193 51
            $this->boundingBox[$min] = $data[$min];
194
        }
195
196
        if (
197 51
            isset($this->boundingBox[$max])
198 51
            && $this->boundingBox[$max] != 0.0 // phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators
199 51
            && ($this->boundingBox[$max] >= $data[$max])
200
        ) {
201 3
            return;
202
        }
203
204 51
        $this->boundingBox[$max] = $data[$max];
205
    }
206
207
    /**
208
     * Adds record to shape file.
209
     *
210
     * @return int Number of added record
211
     */
212 51
    public function addRecord(ShapeRecord $record): int
213
    {
214 51
        if ($this->dbfHeader !== null) {
215 49
            $record->updateDBFInfo($this->dbfHeader);
216
        }
217
218 51
        $this->fileLength += $record->getContentLength() + 4;
219 51
        $this->records[] = $record;
220 51
        $this->records[count($this->records) - 1]->recordNumber = count($this->records);
221
222 51
        $this->updateBBox('x', $record->shpData);
223 51
        $this->updateBBox('y', $record->shpData);
224
225 51
        if (in_array($this->shapeType, ShapeType::MEASURED_TYPES, true)) {
0 ignored issues
show
Bug introduced by
The constant PhpMyAdmin\ShapeFile\ShapeType::MEASURED_TYPES was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
226 32
            $this->updateBBox('m', $record->shpData);
227
        }
228
229 51
        if (in_array($this->shapeType, ShapeType::TYPES_WITH_Z, true)) {
0 ignored issues
show
Bug introduced by
The constant PhpMyAdmin\ShapeFile\ShapeType::TYPES_WITH_Z was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
230 16
            $this->updateBBox('z', $record->shpData);
231
        }
232
233 51
        return count($this->records) - 1;
234
    }
235
236
    /**
237
     * Deletes record from shapefile.
238
     */
239 1
    public function deleteRecord(int $index): void
240
    {
241 1
        if (! isset($this->records[$index])) {
242
            return;
243
        }
244
245 1
        $this->fileLength -= $this->records[$index]->getContentLength() + 4;
246 1
        $count = count($this->records) - 1;
247 1
        for ($i = $index; $i < $count; ++$i) {
248 1
            $this->records[$i] = $this->records[$i + 1];
249
        }
250
251 1
        unset($this->records[count($this->records) - 1]);
252 1
        $this->deleteRecordFromDBF($index);
253
    }
254
255
    /**
256
     * Returns array defining fields in DBF file.
257
     *
258
     * @return mixed[]|null see setDBFHeader for more information
259
     */
260 4
    public function getDBFHeader(): array|null
261
    {
262 4
        return $this->dbfHeader;
263
    }
264
265
    /**
266
     * Changes array defining fields in DBF file, used in dbase_create call.
267
     *
268
     * @param mixed[] $header An array of arrays, each array describing the
269
     *                        format of one field of the database. Each
270
     *                        field consists of a name, a character indicating
271
     *                        the field type, and optionally, a length,
272
     *                        a precision and a nullable flag.
273
     */
274 51
    public function setDBFHeader(array $header): void
275
    {
276 51
        $this->dbfHeader = $header;
277
278 51
        foreach ($this->records as $record) {
279 3
            $record->updateDBFInfo($header);
280
        }
281
    }
282
283
    /**
284
     * Lookups value in the DBF file and returns index.
285
     */
286 4
    public function getIndexFromDBFData(string $field, string $value): int
287
    {
288 4
        foreach ($this->records as $index => $record) {
289
            if (
290 4
                isset($record->dbfData[$field]) &&
291 4
                (trim(strtoupper($record->dbfData[$field])) === strtoupper($value))
292
            ) {
293 1
                return $index;
294
            }
295
        }
296
297 4
        return -1;
298
    }
299
300
    /**
301
     * Loads DBF metadata.
302
     *
303
     * @return array{string, string, int, int}[]
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{string, string, int, int}[] at position 2 could not be parsed: Expected ':' at position 2, but found 'string'.
Loading history...
304
     */
305 22
    private function loadDBFHeader(): array
306
    {
307 22
        $dbfFile = fopen($this->getFilename('.dbf'), 'r');
308 22
        if ($dbfFile === false) {
309
            return [];
310
        }
311
312 22
        $result = [];
313 22
        $i = 1;
314
315 22
        while (true) {
316 22
            if (feof($dbfFile)) {
317
                break;
318
            }
319
320 22
            $buff32 = fread($dbfFile, 32);
321 22
            if ($i > 1) {
322 22
                if (substr($buff32, 0, 1) === chr(13)) {
323 22
                    break;
324
                }
325
326 22
                $pos = strpos(substr($buff32, 0, 10), chr(0));
327 22
                $pos = ($pos === false ? 10 : $pos);
328
329 22
                $fieldName = substr($buff32, 0, $pos);
330 22
                $fieldType = substr($buff32, 11, 1);
331 22
                $fieldLen = ord(substr($buff32, 16, 1));
332 22
                $fieldDec = ord(substr($buff32, 17, 1));
333
334 22
                $result[] = [$fieldName, $fieldType, $fieldLen, $fieldDec];
335
            }
336
337 22
            ++$i;
338
        }
339
340 22
        fclose($dbfFile);
341
342 22
        return $result;
343
    }
344
345
    /**
346
     * Deletes record from the DBF file.
347
     */
348 1
    private function deleteRecordFromDBF(int $index): void
349
    {
350 1
        if ($this->dbfFile === false || ! @dbase_delete_record($this->dbfFile, $index)) {
0 ignored issues
show
Bug introduced by
The function dbase_delete_record was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

350
        if ($this->dbfFile === false || ! @/** @scrutinizer ignore-call */ dbase_delete_record($this->dbfFile, $index)) {
Loading history...
introduced by
Function dbase_delete_record() should not be referenced via a fallback global name, but via a use statement.
Loading history...
351 1
            return;
352
        }
353
354
        dbase_pack($this->dbfFile);
0 ignored issues
show
Bug introduced by
The function dbase_pack was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

354
        /** @scrutinizer ignore-call */ 
355
        dbase_pack($this->dbfFile);
Loading history...
introduced by
Function dbase_pack() should not be referenced via a fallback global name, but via a use statement.
Loading history...
355
    }
356
357
    /**
358
     * Loads SHP file metadata.
359
     */
360 85
    private function loadHeaders(): bool
361
    {
362 85
        if (Util::loadData('N', $this->readSHP(4)) !== self::MAGIC) {
363 6
            $this->setError('Not a SHP file (file code mismatch)');
364
365 6
            return false;
366
        }
367
368
        /* Skip 20 unused bytes */
369 79
        $this->readSHP(20);
370
371 79
        $this->fileLength = (int) Util::loadData('N', $this->readSHP(4));
372
373
        /* We currently ignore version */
374 79
        $this->readSHP(4);
375
376 79
        $shapeType = Util::loadData('V', $this->readSHP(4));
377 79
        if ($shapeType === false) {
378
            $this->shapeType = ShapeType::Unknown;
379
        } else {
380 79
            $this->shapeType = ShapeType::tryFrom((int) $shapeType) ?? ShapeType::Unknown;
381
        }
382
383 79
        $this->boundingBox = [];
384 79
        $this->boundingBox['xmin'] = Util::loadData('d', $this->readSHP(8));
385 79
        $this->boundingBox['ymin'] = Util::loadData('d', $this->readSHP(8));
386 79
        $this->boundingBox['xmax'] = Util::loadData('d', $this->readSHP(8));
387 79
        $this->boundingBox['ymax'] = Util::loadData('d', $this->readSHP(8));
388 79
        $this->boundingBox['zmin'] = Util::loadData('d', $this->readSHP(8));
389 79
        $this->boundingBox['zmax'] = Util::loadData('d', $this->readSHP(8));
390 79
        $this->boundingBox['mmin'] = Util::loadData('d', $this->readSHP(8));
391 79
        $this->boundingBox['mmax'] = Util::loadData('d', $this->readSHP(8));
392
393 79
        if (self::supportsDbase()) {
394 22
            $this->dbfHeader = $this->loadDBFHeader();
395
        }
396
397 79
        return true;
398
    }
399
400
    /**
401
     * Saves bounding box record, possibly using 0 instead of not set values.
402
     *
403
     * @param resource $file File object
404
     * @param string   $type Bounding box dimension (eg. xmax, mmin...)
405
     */
406 55
    private function saveBBoxRecord($file, string $type): void
407
    {
408 55
        fwrite($file, Util::packDouble(
409 55
            $this->boundingBox[$type] ?? 0,
410 55
        ));
411
    }
412
413
    /**
414
     * Saves bounding box to a file.
415
     *
416
     * @param resource $file File object
417
     */
418 55
    private function saveBBox($file): void
419
    {
420 55
        $this->saveBBoxRecord($file, 'xmin');
421 55
        $this->saveBBoxRecord($file, 'ymin');
422 55
        $this->saveBBoxRecord($file, 'xmax');
423 55
        $this->saveBBoxRecord($file, 'ymax');
424 55
        $this->saveBBoxRecord($file, 'zmin');
425 55
        $this->saveBBoxRecord($file, 'zmax');
426 55
        $this->saveBBoxRecord($file, 'mmin');
427 55
        $this->saveBBoxRecord($file, 'mmax');
428
    }
429
430
    /**
431
     * Saves SHP and SHX file metadata.
432
     */
433 55
    private function saveHeaders(): void
434
    {
435 55
        if ($this->shpFile === false) {
436
            return;
437
        }
438
439 55
        fwrite($this->shpFile, pack('NNNNNN', self::MAGIC, 0, 0, 0, 0, 0));
0 ignored issues
show
Bug introduced by
It seems like $this->shpFile can also be of type true; however, parameter $stream of fwrite() does only seem to accept resource, 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

439
        fwrite(/** @scrutinizer ignore-type */ $this->shpFile, pack('NNNNNN', self::MAGIC, 0, 0, 0, 0, 0));
Loading history...
440 55
        fwrite($this->shpFile, pack('N', $this->fileLength));
441 55
        fwrite($this->shpFile, pack('V', 1000));
442 55
        fwrite($this->shpFile, pack('V', $this->shapeType->value));
443 55
        $this->saveBBox($this->shpFile);
0 ignored issues
show
Bug introduced by
It seems like $this->shpFile can also be of type true; however, parameter $file of PhpMyAdmin\ShapeFile\ShapeFile::saveBBox() does only seem to accept resource, 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

443
        $this->saveBBox(/** @scrutinizer ignore-type */ $this->shpFile);
Loading history...
444
445 55
        if ($this->shxFile === false) {
446
            return;
447
        }
448
449 55
        fwrite($this->shxFile, pack('NNNNNN', self::MAGIC, 0, 0, 0, 0, 0));
450 55
        fwrite($this->shxFile, pack('N', 50 + 4 * count($this->records)));
451 55
        fwrite($this->shxFile, pack('V', 1000));
452 55
        fwrite($this->shxFile, pack('V', $this->shapeType->value));
453 55
        $this->saveBBox($this->shxFile);
454
    }
455
456
    /**
457
     * Loads records from SHP file (and DBF).
458
     */
459 79
    private function loadRecords(): bool
460
    {
461
        /* Need to start at offset 100 */
462 79
        while (! $this->eofSHP()) {
463 79
            $record = new ShapeRecord(ShapeType::Unknown);
464 79
            $record->loadFromFile($this, $this->dbfFile);
465 79
            if ($record->lastError !== '') {
466
                $this->setError($record->lastError);
467
468
                return false;
469
            }
470
471 79
            if (($record->shapeType === ShapeType::Unknown) && $this->eofSHP()) {
472 79
                break;
473
            }
474
475 79
            $this->records[] = $record;
476
        }
477
478 79
        return true;
479
    }
480
481
    /**
482
     * Saves records to SHP and SHX files.
483
     */
484 55
    private function saveRecords(): void
485
    {
486 55
        $offset = 50;
487 55
        if ($this->records === [] || $this->shxFile === false || $this->shpFile === false) {
488 4
            return;
489
        }
490
491 51
        foreach ($this->records as $index => $record) {
492
            //Save the record to the .shp file
493 51
            $record->saveToFile($this->shpFile, $this->dbfFile, $index + 1);
494
495
            //Save the record to the .shx file
496 51
            fwrite($this->shxFile, pack('N', $offset));
0 ignored issues
show
Bug introduced by
It seems like $this->shxFile can also be of type true; however, parameter $stream of fwrite() does only seem to accept resource, 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

496
            fwrite(/** @scrutinizer ignore-type */ $this->shxFile, pack('N', $offset));
Loading history...
497 51
            fwrite($this->shxFile, pack('N', $record->getContentLength()));
498 51
            $offset += 4 + $record->getContentLength();
499
        }
500
    }
501
502
    /**
503
     * Generic interface to open files.
504
     *
505
     * @param bool   $toWrite   Whether file should be opened for writing
506
     * @param string $extension File extension
507
     * @param string $name      Verbose file name to report errors
508
     *
509
     * @return resource|false File handle
510
     */
511 97
    private function openFile(bool $toWrite, string $extension, string $name)
512
    {
513 97
        $shpName = $this->getFilename($extension);
514 97
        $result = @fopen($shpName, ($toWrite ? 'wb+' : 'rb'));
515 97
        if (! $result) {
0 ignored issues
show
introduced by
$result is of type false|resource, thus it always evaluated to false.
Loading history...
516 8
            $this->setError(sprintf('It wasn\'t possible to open the %s file "%s"', $name, $shpName));
517
518 8
            return false;
519
        }
520
521 89
        return $result;
522
    }
523
524
    /**
525
     * Opens SHP file.
526
     *
527
     * @param bool $toWrite Whether file should be opened for writing
528
     */
529 97
    private function openSHPFile(bool $toWrite = false): bool
530
    {
531 97
        $this->shpFile = $this->openFile($toWrite, '.shp', 'Shape');
532
533 97
        return (bool) $this->shpFile;
534
    }
535
536
    /**
537
     * Closes SHP file.
538
     */
539 89
    private function closeSHPFile(): void
540
    {
541 89
        if ($this->shpFile === false) {
542 3
            return;
543
        }
544
545 86
        fclose($this->shpFile);
0 ignored issues
show
Bug introduced by
It seems like $this->shpFile can also be of type true; however, parameter $stream of fclose() does only seem to accept resource, 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

545
        fclose(/** @scrutinizer ignore-type */ $this->shpFile);
Loading history...
546 86
        $this->shpFile = false;
547
    }
548
549
    /**
550
     * Opens SHX file.
551
     *
552
     * @param bool $toWrite Whether file should be opened for writing
553
     */
554 55
    private function openSHXFile(bool $toWrite = false): bool
555
    {
556 55
        $this->shxFile = $this->openFile($toWrite, '.shx', 'Index');
557
558 55
        return (bool) $this->shxFile;
559
    }
560
561
    /**
562
     * Closes SHX file.
563
     */
564 55
    private function closeSHXFile(): void
565
    {
566 55
        if ($this->shxFile === false) {
567
            return;
568
        }
569
570 55
        fclose($this->shxFile);
0 ignored issues
show
Bug introduced by
It seems like $this->shxFile can also be of type true; however, parameter $stream of fclose() does only seem to accept resource, 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

570
        fclose(/** @scrutinizer ignore-type */ $this->shxFile);
Loading history...
571 55
        $this->shxFile = false;
572
    }
573
574
    /**
575
     * Creates DBF file.
576
     */
577 55
    private function createDBFFile(): bool
578
    {
579 55
        if (! self::supportsDbase() || $this->dbfHeader === null || $this->dbfHeader === []) {
580 40
            $this->dbfFile = false;
581
582 40
            return true;
583
        }
584
585 15
        $dbfName = $this->getFilename('.dbf');
586
587
        /* Unlink existing file */
588 15
        if (file_exists($dbfName)) {
589 2
            unlink($dbfName);
590
        }
591
592
        /* Create new file */
593 15
        $this->dbfFile = @dbase_create($dbfName, $this->dbfHeader);
0 ignored issues
show
Bug introduced by
The function dbase_create was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

593
        $this->dbfFile = @/** @scrutinizer ignore-call */ dbase_create($dbfName, $this->dbfHeader);
Loading history...
introduced by
Function dbase_create() should not be referenced via a fallback global name, but via a use statement.
Loading history...
594 15
        if ($this->dbfFile === false) {
595
            $this->setError(sprintf('It wasn\'t possible to create the DBase file "%s"', $dbfName));
596
597
            return false;
598
        }
599
600 15
        return true;
601
    }
602
603
    /**
604
     * Loads DBF file if supported.
605
     */
606 89
    private function openDBFFile(): bool
607
    {
608 89
        if (! self::supportsDbase()) {
609 63
            $this->dbfFile = false;
610
611 63
            return true;
612
        }
613
614 26
        $dbfName = $this->getFilename('.dbf');
615 26
        if (! is_readable($dbfName)) {
616 3
            $this->setError(sprintf('It wasn\'t possible to find the DBase file "%s"', $dbfName));
617
618 3
            return false;
619
        }
620
621 23
        $this->dbfFile = @dbase_open($dbfName, 0);
0 ignored issues
show
Bug introduced by
The function dbase_open was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

621
        $this->dbfFile = @/** @scrutinizer ignore-call */ dbase_open($dbfName, 0);
Loading history...
introduced by
Function dbase_open() should not be referenced via a fallback global name, but via a use statement.
Loading history...
622 23
        if ($this->dbfFile === false) {
623 1
            $this->setError(sprintf('It wasn\'t possible to open the DBase file "%s"', $dbfName));
624
625 1
            return false;
626
        }
627
628 22
        return true;
629
    }
630
631
    /**
632
     * Closes DBF file.
633
     */
634 89
    private function closeDBFFile(): void
635
    {
636 89
        if ($this->dbfFile === false) {
637 67
            return;
638
        }
639
640 22
        dbase_close($this->dbfFile);
0 ignored issues
show
Bug introduced by
The function dbase_close was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

640
        /** @scrutinizer ignore-call */ 
641
        dbase_close($this->dbfFile);
Loading history...
introduced by
Function dbase_close() should not be referenced via a fallback global name, but via a use statement.
Loading history...
641 22
        $this->dbfFile = false;
642
    }
643
644
    /**
645
     * Sets error message.
646
     */
647 18
    public function setError(string $error): void
648
    {
649 18
        $this->lastError = $error;
650
    }
651
652
    /**
653
     * Reads given number of bytes from SHP file.
654
     *
655
     * @param int<0, max> $bytes
656
     */
657 85
    public function readSHP(int $bytes): string|false
658
    {
659 85
        if ($this->shpFile === false) {
660 3
            return false;
661
        }
662
663 82
        return fread($this->shpFile, $bytes);
0 ignored issues
show
Bug introduced by
It seems like $this->shpFile can also be of type true; however, parameter $stream of fread() does only seem to accept resource, 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

663
        return fread(/** @scrutinizer ignore-type */ $this->shpFile, $bytes);
Loading history...
664
    }
665
666
    /**
667
     * Checks whether file is at EOF.
668
     */
669 79
    public function eofSHP(): bool
670
    {
671 79
        return feof($this->shpFile);
0 ignored issues
show
Bug introduced by
It seems like $this->shpFile can also be of type boolean; however, parameter $stream of feof() does only seem to accept resource, 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

671
        return feof(/** @scrutinizer ignore-type */ $this->shpFile);
Loading history...
672
    }
673
674
    /**
675
     * Returns shape name.
676
     *
677
     * @psalm-return non-empty-string
678
     */
679 4
    public function getShapeName(): string
680
    {
681 4
        return ShapeType::name($this->shapeType);
682
    }
683
684
    /**
685
     * Check whether file contains measure data.
686
     *
687
     * For some reason this is distinguished by zero bounding box in the
688
     * specification.
689
     */
690 32
    public function hasMeasure(): bool
691
    {
692
        // phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator
693 32
        return $this->boundingBox['mmin'] != 0 || $this->boundingBox['mmax'] != 0;
694
    }
695
}
696