Passed
Pull Request — master (#38)
by
unknown
02:30
created

ShapeFile::setAllowNoDbf()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

362
        if ($this->dbfFile === false || ! @/** @scrutinizer ignore-call */ dbase_delete_record($this->dbfFile, $index)) {
Loading history...
363 1
            return;
364
        }
365
366
        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

366
        /** @scrutinizer ignore-call */ 
367
        dbase_pack($this->dbfFile);
Loading history...
367
    }
368
369
    /**
370
     * Loads SHP file metadata.
371
     */
372 86
    private function loadHeaders(): bool
373
    {
374 86
        if (Util::loadData('N', $this->readSHP(4)) !== self::MAGIC) {
375 6
            $this->setError('Not a SHP file (file code mismatch)');
376
377 6
            return false;
378
        }
379
380
        /* Skip 20 unused bytes */
381 80
        $this->readSHP(20);
382
383 80
        $this->fileLength = (int) Util::loadData('N', $this->readSHP(4));
384
385
        /* We currently ignore version */
386 80
        $this->readSHP(4);
387
388 80
        $shapeType = Util::loadData('V', $this->readSHP(4));
389 80
        if ($shapeType === false) {
390
            $this->shapeType = ShapeType::Unknown;
391
        } else {
392 80
            $this->shapeType = ShapeType::tryFrom((int) $shapeType) ?? ShapeType::Unknown;
393
        }
394
395 80
        $this->boundingBox = [];
396 80
        $this->boundingBox['xmin'] = Util::loadData('d', $this->readSHP(8));
397 80
        $this->boundingBox['ymin'] = Util::loadData('d', $this->readSHP(8));
398 80
        $this->boundingBox['xmax'] = Util::loadData('d', $this->readSHP(8));
399 80
        $this->boundingBox['ymax'] = Util::loadData('d', $this->readSHP(8));
400 80
        $this->boundingBox['zmin'] = Util::loadData('d', $this->readSHP(8));
401 80
        $this->boundingBox['zmax'] = Util::loadData('d', $this->readSHP(8));
402 80
        $this->boundingBox['mmin'] = Util::loadData('d', $this->readSHP(8));
403 80
        $this->boundingBox['mmax'] = Util::loadData('d', $this->readSHP(8));
404
405 80
        if (self::supportsDbase()) {
406 23
            $this->dbfHeader = $this->loadDBFHeader();
407
        }
408
409 80
        return true;
410
    }
411
412
    /**
413
     * Saves bounding box record, possibly using 0 instead of not set values.
414
     *
415
     * @param resource $file File object
416
     * @param string   $type Bounding box dimension (eg. xmax, mmin...)
417
     */
418 55
    private function saveBBoxRecord($file, string $type): void
419
    {
420 55
        fwrite($file, Util::packDouble(
421 55
            $this->boundingBox[$type] ?? 0,
422 55
        ));
423
    }
424
425
    /**
426
     * Saves bounding box to a file.
427
     *
428
     * @param resource $file File object
429
     */
430 55
    private function saveBBox($file): void
431
    {
432 55
        $this->saveBBoxRecord($file, 'xmin');
433 55
        $this->saveBBoxRecord($file, 'ymin');
434 55
        $this->saveBBoxRecord($file, 'xmax');
435 55
        $this->saveBBoxRecord($file, 'ymax');
436 55
        $this->saveBBoxRecord($file, 'zmin');
437 55
        $this->saveBBoxRecord($file, 'zmax');
438 55
        $this->saveBBoxRecord($file, 'mmin');
439 55
        $this->saveBBoxRecord($file, 'mmax');
440
    }
441
442
    /**
443
     * Saves SHP and SHX file metadata.
444
     */
445 55
    private function saveHeaders(): void
446
    {
447 55
        if ($this->shpFile === false) {
448
            return;
449
        }
450
451 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

451
        fwrite(/** @scrutinizer ignore-type */ $this->shpFile, pack('NNNNNN', self::MAGIC, 0, 0, 0, 0, 0));
Loading history...
452 55
        fwrite($this->shpFile, pack('N', $this->fileLength));
453 55
        fwrite($this->shpFile, pack('V', 1000));
454 55
        fwrite($this->shpFile, pack('V', $this->shapeType->value));
455 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

455
        $this->saveBBox(/** @scrutinizer ignore-type */ $this->shpFile);
Loading history...
456
457 55
        if ($this->shxFile === false) {
458
            return;
459
        }
460
461 55
        fwrite($this->shxFile, pack('NNNNNN', self::MAGIC, 0, 0, 0, 0, 0));
462 55
        fwrite($this->shxFile, pack('N', 50 + 4 * count($this->records)));
463 55
        fwrite($this->shxFile, pack('V', 1000));
464 55
        fwrite($this->shxFile, pack('V', $this->shapeType->value));
465 55
        $this->saveBBox($this->shxFile);
466
    }
467
468
    /**
469
     * Loads records from SHP file (and DBF).
470
     */
471 80
    private function loadRecords(): bool
472
    {
473
        /* Need to start at offset 100 */
474 80
        while (! $this->eofSHP()) {
475 80
            $record = new ShapeRecord(ShapeType::Unknown);
476 80
            $record->loadFromFile($this, $this->dbfFile);
477 80
            if ($record->lastError !== '') {
478
                $this->setError($record->lastError);
479
480
                return false;
481
            }
482
483 80
            if (($record->shapeType === ShapeType::Unknown) && $this->eofSHP()) {
484 80
                break;
485
            }
486
487 80
            $this->records[] = $record;
488
        }
489
490 80
        return true;
491
    }
492
493
    /**
494
     * Saves records to SHP and SHX files.
495
     */
496 55
    private function saveRecords(): void
497
    {
498 55
        $offset = 50;
499 55
        if ($this->records === [] || $this->shxFile === false || $this->shpFile === false) {
500 4
            return;
501
        }
502
503 51
        foreach ($this->records as $index => $record) {
504
            //Save the record to the .shp file
505 51
            $record->saveToFile($this->shpFile, $this->dbfFile, $index + 1);
506
507
            //Save the record to the .shx file
508 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

508
            fwrite(/** @scrutinizer ignore-type */ $this->shxFile, pack('N', $offset));
Loading history...
509 51
            fwrite($this->shxFile, pack('N', $record->getContentLength()));
510 51
            $offset += 4 + $record->getContentLength();
511
        }
512
    }
513
514
    /**
515
     * Generic interface to open files.
516
     *
517
     * @param bool   $toWrite   Whether file should be opened for writing
518
     * @param string $extension File extension
519
     * @param string $name      Verbose file name to report errors
520
     *
521
     * @return resource|false File handle
522
     */
523 98
    private function openFile(bool $toWrite, string $extension, string $name)
524
    {
525 98
        $shpName = $this->getFilename($extension);
526 98
        $result = @fopen($shpName, ($toWrite ? 'wb+' : 'rb'));
527 98
        if (! $result) {
0 ignored issues
show
introduced by
$result is of type false|resource, thus it always evaluated to false.
Loading history...
528 8
            $this->setError(sprintf('It wasn\'t possible to open the %s file "%s"', $name, $shpName));
529
530 8
            return false;
531
        }
532
533 90
        return $result;
534
    }
535
536
    /**
537
     * Opens SHP file.
538
     *
539
     * @param bool $toWrite Whether file should be opened for writing
540
     */
541 98
    private function openSHPFile(bool $toWrite = false): bool
542
    {
543 98
        $this->shpFile = $this->openFile($toWrite, '.shp', 'Shape');
544
545 98
        return (bool) $this->shpFile;
546
    }
547
548
    /**
549
     * Closes SHP file.
550
     */
551 90
    private function closeSHPFile(): void
552
    {
553 90
        if ($this->shpFile === false) {
554 3
            return;
555
        }
556
557 87
        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

557
        fclose(/** @scrutinizer ignore-type */ $this->shpFile);
Loading history...
558 87
        $this->shpFile = false;
559
    }
560
561
    /**
562
     * Opens SHX file.
563
     *
564
     * @param bool $toWrite Whether file should be opened for writing
565
     */
566 55
    private function openSHXFile(bool $toWrite = false): bool
567
    {
568 55
        $this->shxFile = $this->openFile($toWrite, '.shx', 'Index');
569
570 55
        return (bool) $this->shxFile;
571
    }
572
573
    /**
574
     * Closes SHX file.
575
     */
576 55
    private function closeSHXFile(): void
577
    {
578 55
        if ($this->shxFile === false) {
579
            return;
580
        }
581
582 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

582
        fclose(/** @scrutinizer ignore-type */ $this->shxFile);
Loading history...
583 55
        $this->shxFile = false;
584
    }
585
586
    /**
587
     * Creates DBF file.
588
     */
589 55
    private function createDBFFile(): bool
590
    {
591 55
        if (! self::supportsDbase() || $this->dbfHeader === null || $this->dbfHeader === []) {
592 40
            $this->dbfFile = false;
593
594 40
            return true;
595
        }
596
597 15
        $dbfName = $this->getFilename('.dbf');
598
599
        /* Unlink existing file */
600 15
        if (file_exists($dbfName)) {
601 2
            unlink($dbfName);
602
        }
603
604
        /* Create new file */
605 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

605
        $this->dbfFile = @/** @scrutinizer ignore-call */ dbase_create($dbfName, $this->dbfHeader);
Loading history...
606 15
        if ($this->dbfFile === false) {
607
            $this->setError(sprintf('It wasn\'t possible to create the DBase file "%s"', $dbfName));
608
609
            return false;
610
        }
611
612 15
        return true;
613
    }
614
615
    /**
616
     * Loads DBF file if supported.
617
     */
618 90
    private function openDBFFile(): bool
619
    {
620 90
        if (! self::supportsDbase()) {
621 63
            $this->dbfFile = false;
622
623 63
            return true;
624
        }
625
626 27
        $dbfName = $this->getFilename('.dbf');
627 27
        if (! is_readable($dbfName)) {
628 4
            if ($this->allowNoDbf) {
629 1
                return true;
630
            }
631
632 3
            $this->setError(sprintf('It wasn\'t possible to find the DBase file "%s"', $dbfName));
633
634 3
            return false;
635
        }
636
637 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

637
        $this->dbfFile = @/** @scrutinizer ignore-call */ dbase_open($dbfName, 0);
Loading history...
638 23
        if ($this->dbfFile === false) {
639 1
            $this->setError(sprintf('It wasn\'t possible to open the DBase file "%s"', $dbfName));
640
641 1
            return false;
642
        }
643
644 22
        return true;
645
    }
646
647
    /**
648
     * Closes DBF file.
649
     */
650 90
    private function closeDBFFile(): void
651
    {
652 90
        if ($this->dbfFile === false) {
653 68
            return;
654
        }
655
656 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

656
        /** @scrutinizer ignore-call */ 
657
        dbase_close($this->dbfFile);
Loading history...
657 22
        $this->dbfFile = false;
658
    }
659
660
    /**
661
     * Sets error message.
662
     */
663 18
    public function setError(string $error): void
664
    {
665 18
        $this->lastError = $error;
666
    }
667
668
    /**
669
     * Reads given number of bytes from SHP file.
670
     *
671
     * @param int<0, max> $bytes
672
     */
673 86
    public function readSHP(int $bytes): string|false
674
    {
675 86
        if ($this->shpFile === false) {
676 3
            return false;
677
        }
678
679 83
        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

679
        return fread(/** @scrutinizer ignore-type */ $this->shpFile, $bytes);
Loading history...
680
    }
681
682
    /**
683
     * Checks whether file is at EOF.
684
     */
685 80
    public function eofSHP(): bool
686
    {
687 80
        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

687
        return feof(/** @scrutinizer ignore-type */ $this->shpFile);
Loading history...
688
    }
689
690
    /**
691
     * Returns shape name.
692
     *
693
     * @psalm-return non-empty-string
694
     */
695 4
    public function getShapeName(): string
696
    {
697 4
        return ShapeType::name($this->shapeType);
698
    }
699
700
    /**
701
     * Check whether file contains measure data.
702
     *
703
     * For some reason this is distinguished by zero bounding box in the
704
     * specification.
705
     */
706 32
    public function hasMeasure(): bool
707
    {
708
        // phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator
709 32
        return $this->boundingBox['mmin'] != 0 || $this->boundingBox['mmax'] != 0;
710
    }
711
}
712