Completed
Push — master ( 6481a4...d743ab )
by William
01:44
created

ShapeFile::saveRecords()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

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

328
            if (! feof(/** @scrutinizer ignore-type */ $DBFFile)) {
Loading history...
329 88
                $buff32 = fread($DBFFile, 32);
0 ignored issues
show
Bug introduced by
It seems like $DBFFile can also be of type false; however, parameter $handle 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

329
                $buff32 = fread(/** @scrutinizer ignore-type */ $DBFFile, 32);
Loading history...
330 88
                if ($i > 1) {
331 88
                    if (substr($buff32, 0, 1) === chr(13)) {
332 88
                        $inHeader = false;
333
                    } else {
334 88
                        $pos = strpos(substr($buff32, 0, 10), chr(0));
335 88
                        $pos = ($pos === false ? 10 : $pos);
336
337 88
                        $fieldName = substr($buff32, 0, $pos);
338 88
                        $fieldType = substr($buff32, 11, 1);
339 88
                        $fieldLen = ord(substr($buff32, 16, 1));
340 88
                        $fieldDec = ord(substr($buff32, 17, 1));
341
342 88
                        array_push($result, [$fieldName, $fieldType, $fieldLen, $fieldDec]);
343
                    }
344
                }
345
346 88
                ++$i;
347
            } else {
348
                $inHeader = false;
349
            }
350
        }
351
352 88
        fclose($DBFFile);
0 ignored issues
show
Bug introduced by
It seems like $DBFFile can also be of type false; however, parameter $handle 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

352
        fclose(/** @scrutinizer ignore-type */ $DBFFile);
Loading history...
353
354 88
        return $result;
355
    }
356
357
    /**
358
     * Deletes record from the DBF file.
359
     */
360 4
    private function deleteRecordFromDBF(int $index): void
361
    {
362 4
        if ($this->dbfFile === null || ! @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 === null || ! @/** @scrutinizer ignore-call */ dbase_delete_record($this->dbfFile, $index)) {
Loading history...
363 4
            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 168
    private function loadHeaders(): bool
373
    {
374 168
        if (Util::loadData('N', $this->readSHP(4)) !== self::MAGIC) {
375 4
            $this->setError('Not a SHP file (file code mismatch)');
376
377 4
            return false;
378
        }
379
380
        /* Skip 20 unused bytes */
381 164
        $this->readSHP(20);
382
383 164
        $this->fileLength = Util::loadData('N', $this->readSHP(4));
0 ignored issues
show
Documentation Bug introduced by
It seems like PhpMyAdmin\ShapeFile\Uti...'N', $this->readSHP(4)) can also be of type false. However, the property $fileLength is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
384
385
        /* We currently ignore version */
386 164
        $this->readSHP(4);
387
388 164
        $this->shapeType = Util::loadData('V', $this->readSHP(4));
0 ignored issues
show
Documentation Bug introduced by
It seems like PhpMyAdmin\ShapeFile\Uti...'V', $this->readSHP(4)) can also be of type false. However, the property $shapeType is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
389
390 164
        $this->boundingBox = [];
391 164
        $this->boundingBox['xmin'] = Util::loadData('d', $this->readSHP(8));
392 164
        $this->boundingBox['ymin'] = Util::loadData('d', $this->readSHP(8));
393 164
        $this->boundingBox['xmax'] = Util::loadData('d', $this->readSHP(8));
394 164
        $this->boundingBox['ymax'] = Util::loadData('d', $this->readSHP(8));
395 164
        $this->boundingBox['zmin'] = Util::loadData('d', $this->readSHP(8));
396 164
        $this->boundingBox['zmax'] = Util::loadData('d', $this->readSHP(8));
397 164
        $this->boundingBox['mmin'] = Util::loadData('d', $this->readSHP(8));
398 164
        $this->boundingBox['mmax'] = Util::loadData('d', $this->readSHP(8));
399
400 164
        if (self::supportsDbase()) {
401 88
            $this->dbfHeader = $this->loadDBFHeader();
402
        }
403
404 164
        return true;
405
    }
406
407
    /**
408
     * Saves bounding box record, possibly using 0 instead of not set values.
409
     *
410
     * @param resource $file File object
411
     * @param string   $type Bounding box dimension (eg. xmax, mmin...)
412
     */
413 116
    private function saveBBoxRecord($file, string $type): void
414
    {
415 116
        fwrite($file, Util::packDouble(
416 116
            $this->boundingBox[$type] ?? 0
417
        ));
418 116
    }
419
420
    /**
421
     * Saves bounding box to a file.
422
     *
423
     * @param resource $file File object
424
     */
425 116
    private function saveBBox($file): void
426
    {
427 116
        $this->saveBBoxRecord($file, 'xmin');
428 116
        $this->saveBBoxRecord($file, 'ymin');
429 116
        $this->saveBBoxRecord($file, 'xmax');
430 116
        $this->saveBBoxRecord($file, 'ymax');
431 116
        $this->saveBBoxRecord($file, 'zmin');
432 116
        $this->saveBBoxRecord($file, 'zmax');
433 116
        $this->saveBBoxRecord($file, 'mmin');
434 116
        $this->saveBBoxRecord($file, 'mmax');
435 116
    }
436
437
    /**
438
     * Saves SHP and SHX file metadata.
439
     */
440 116
    private function saveHeaders(): void
441
    {
442 116
        fwrite($this->shpFile, pack('NNNNNN', self::MAGIC, 0, 0, 0, 0, 0));
443 116
        fwrite($this->shpFile, pack('N', $this->fileLength));
444 116
        fwrite($this->shpFile, pack('V', 1000));
445 116
        fwrite($this->shpFile, pack('V', $this->shapeType));
446 116
        $this->saveBBox($this->shpFile);
447
448 116
        fwrite($this->shxFile, pack('NNNNNN', self::MAGIC, 0, 0, 0, 0, 0));
449 116
        fwrite($this->shxFile, pack('N', 50 + 4 * count($this->records)));
450 116
        fwrite($this->shxFile, pack('V', 1000));
451 116
        fwrite($this->shxFile, pack('V', $this->shapeType));
452 116
        $this->saveBBox($this->shxFile);
453 116
    }
454
455
    /**
456
     * Loads records from SHP file (and DBF).
457
     */
458 164
    private function loadRecords(): bool
459
    {
460
        /* Need to start at offset 100 */
461 164
        while (! $this->eofSHP()) {
462 164
            $record = new ShapeRecord(-1);
463 164
            $record->loadFromFile($this, $this->shpFile, $this->dbfFile);
464 164
            if ($record->lastError !== '') {
465
                $this->setError($record->lastError);
466
467
                return false;
468
            }
469
470 164
            if (($record->shapeType === false || $record->shapeType === '') && $this->eofSHP()) {
471 164
                break;
472
            }
473
474 164
            $this->records[] = $record;
475
        }
476
477 164
        return true;
478
    }
479
480
    /**
481
     * Saves records to SHP and SHX files.
482
     */
483 116
    private function saveRecords(): void
484
    {
485 116
        $offset = 50;
486 116
        if (! is_array($this->records) || (count($this->records) <= 0)) {
0 ignored issues
show
introduced by
The condition is_array($this->records) is always true.
Loading history...
487 8
            return;
488
        }
489
490 108
        foreach ($this->records as $index => $record) {
491
            //Save the record to the .shp file
492 108
            $record->saveToFile($this->shpFile, $this->dbfFile, $index + 1);
493
494
            //Save the record to the .shx file
495 108
            fwrite($this->shxFile, pack('N', $offset));
496 108
            fwrite($this->shxFile, pack('N', $record->getContentLength()));
497 108
            $offset += 4 + $record->getContentLength();
498
        }
499 108
    }
500
501
    /**
502
     * Generic interface to open files.
503
     *
504
     * @param bool   $toWrite   Whether file should be opened for writing
505
     * @param string $extension File extension
506
     * @param string $name      Verbose file name to report errors
507
     *
508
     * @return resource|false File handle
509
     */
510 204
    private function openFile(bool $toWrite, string $extension, string $name)
511
    {
512 204
        $shpName = $this->getFilename($extension);
513 204
        $result = @fopen($shpName, ($toWrite ? 'wb+' : 'rb'));
514 204
        if (! $result) {
0 ignored issues
show
introduced by
$result is of type false|resource, thus it always evaluated to false.
Loading history...
515 16
            $this->setError(sprintf('It wasn\'t possible to open the %s file "%s"', $name, $shpName));
516
517 16
            return false;
518
        }
519
520 188
        return $result;
521
    }
522
523
    /**
524
     * Opens SHP file.
525
     *
526
     * @param bool $toWrite Whether file should be opened for writing
527
     */
528 204
    private function openSHPFile(bool $toWrite = false): bool
529
    {
530 204
        $this->shpFile = $this->openFile($toWrite, '.shp', 'Shape');
531
532 204
        return (bool) $this->shpFile;
533
    }
534
535
    /**
536
     * Closes SHP file.
537
     */
538 176
    private function closeSHPFile(): void
539
    {
540 176
        if (! $this->shpFile) {
541
            return;
542
        }
543
544 176
        fclose($this->shpFile);
545 176
        $this->shpFile = null;
546 176
    }
547
548
    /**
549
     * Opens SHX file.
550
     *
551
     * @param bool $toWrite Whether file should be opened for writing
552
     */
553 116
    private function openSHXFile(bool $toWrite = false): bool
554
    {
555 116
        $this->shxFile = $this->openFile($toWrite, '.shx', 'Index');
556
557 116
        return (bool) $this->shxFile;
558
    }
559
560
    /**
561
     * Closes SHX file.
562
     */
563 116
    private function closeSHXFile(): void
564
    {
565 116
        if (! $this->shxFile) {
566
            return;
567
        }
568
569 116
        fclose($this->shxFile);
570 116
        $this->shxFile = null;
571 116
    }
572
573
    /**
574
     * Creates DBF file.
575
     */
576 116
    private function createDBFFile(): bool
577
    {
578 116
        if (! self::supportsDbase() || ! is_array($this->dbfHeader) || count($this->dbfHeader) === 0) {
579 56
            $this->dbfFile = null;
580
581 56
            return true;
582
        }
583
584 60
        $dbfName = $this->getFilename('.dbf');
585
586
        /* Unlink existing file */
587 60
        if (file_exists($dbfName)) {
588 8
            unlink($dbfName);
589
        }
590
591
        /* Create new file */
592 60
        $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

592
        $this->dbfFile = @/** @scrutinizer ignore-call */ dbase_create($dbfName, $this->dbfHeader);
Loading history...
593 60
        if ($this->dbfFile === false) {
594
            $this->setError(sprintf('It wasn\'t possible to create the DBase file "%s"', $dbfName));
595
596
            return false;
597
        }
598
599 60
        return true;
600
    }
601
602
    /**
603
     * Loads DBF file if supported.
604
     */
605 180
    private function openDBFFile(): bool
606
    {
607 180
        if (! self::supportsDbase()) {
608 80
            $this->dbfFile = null;
609
610 80
            return true;
611
        }
612
613 100
        $dbfName = $this->getFilename('.dbf');
614 100
        if (! is_readable($dbfName)) {
615 8
            $this->setError(sprintf('It wasn\'t possible to find the DBase file "%s"', $dbfName));
616
617 8
            return false;
618
        }
619
620 92
        $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

620
        $this->dbfFile = @/** @scrutinizer ignore-call */ dbase_open($dbfName, 0);
Loading history...
621 92
        if (! $this->dbfFile) {
622 4
            $this->setError(sprintf('It wasn\'t possible to open the DBase file "%s"', $dbfName));
623
624 4
            return false;
625
        }
626
627 88
        return true;
628
    }
629
630
    /**
631
     * Closes DBF file.
632
     */
633 176
    private function closeDBFFile(): void
634
    {
635 176
        if (! $this->dbfFile) {
636 88
            return;
637
        }
638
639 88
        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

639
        /** @scrutinizer ignore-call */ 
640
        dbase_close($this->dbfFile);
Loading history...
640 88
        $this->dbfFile = null;
641 88
    }
642
643
    /**
644
     * Sets error message.
645
     */
646 32
    public function setError(string $error): void
647
    {
648 32
        $this->lastError = $error;
649 32
    }
650
651
    /**
652
     * Reads given number of bytes from SHP file.
653
     *
654
     * @return string|false
655
     */
656 168
    public function readSHP(int $bytes)
657
    {
658 168
        return fread($this->shpFile, $bytes);
659
    }
660
661
    /**
662
     * Checks whether file is at EOF.
663
     */
664 164
    public function eofSHP(): bool
665
    {
666 164
        return feof($this->shpFile);
667
    }
668
669
    /**
670
     * Returns shape name.
671
     */
672 8
    public function getShapeName(): string
673
    {
674 8
        return Util::nameShape($this->shapeType);
675
    }
676
677
    /**
678
     * Check whether file contains measure data.
679
     *
680
     * For some reason this is distinguished by zero bounding box in the
681
     * specification.
682
     */
683 64
    public function hasMeasure(): bool
684
    {
685 64
        return $this->boundingBox['mmin'] != 0 || $this->boundingBox['mmax'] != 0;
686
    }
687
}
688