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

ShapeRecord::loadPointMRecord()   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 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 1
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_values;
29
use function count;
30
use function fwrite;
31
use function in_array;
32
use function is_array;
33
use function pack;
34
use function sprintf;
35
use function strlen;
36
37
/**
38
 * ShapeFile record class.
39
 */
40
class ShapeRecord
41
{
42
    /** @var resource */
43
    private $shpFile = null;
44
    /** @var resource */
45
    private $dbfFile = null;
46
    /** @var ShapeFile */
47
    private $shapeFile = null;
48
49
    /** @var int */
50
    private $size = 0;
51
    /** @var int */
52
    private $read = 0;
53
54
    /** @var int|null */
55
    public $recordNumber = null;
56
    public $shapeType = null;
57
58
     /** @var string */
59
    public $lastError = '';
60
61
    /** @var array */
62
    public $shpData = [];
63
    /** @var array */
64
    public $dbfData = [];
65
66 172
    public function __construct(int $shapeType)
67
    {
68 172
        $this->shapeType = $shapeType;
69 172
    }
70
71
    /**
72
     * Loads record from files.
73
     *
74
     * @param ShapeFile $shapeFile The ShapeFile object
75
     * @param resource  $shpFile   Opened SHP file (by reference)
76
     * @param resource  $dbfFile   Opened DBF file (by reference)
77
     */
78 164
    public function loadFromFile(ShapeFile &$shapeFile, &$shpFile, &$dbfFile): void
79
    {
80 164
        $this->shapeFile = $shapeFile;
81 164
        $this->shpFile = $shpFile;
82 164
        $this->dbfFile = $dbfFile;
83 164
        $this->loadHeaders();
84
85
        /* No header read */
86 164
        if ($this->read === 0) {
87 164
            return;
88
        }
89
90 164
        switch ($this->shapeType) {
91 164
            case 0:
92
                $this->loadNullRecord();
93
                break;
94 164
            case 1:
95 44
                $this->loadPointRecord();
96 44
                break;
97 132
            case 21:
98 20
                $this->loadPointMRecord();
99 20
                break;
100 124
            case 11:
101 20
                $this->loadPointZRecord();
102 20
                break;
103 116
            case 3:
104 20
                $this->loadPolyLineRecord();
105 20
                break;
106 96
            case 23:
107 8
                $this->loadPolyLineMRecord();
108 8
                break;
109 88
            case 13:
110 8
                $this->loadPolyLineZRecord();
111 8
                break;
112 80
            case 5:
113 24
                $this->loadPolygonRecord();
114 24
                break;
115 56
            case 25:
116 8
                $this->loadPolygonMRecord();
117 8
                break;
118 48
            case 15:
119 16
                $this->loadPolygonZRecord();
120 16
                break;
121 32
            case 8:
122 8
                $this->loadMultiPointRecord();
123 8
                break;
124 24
            case 28:
125 8
                $this->loadMultiPointMRecord();
126 8
                break;
127 16
            case 18:
128 16
                $this->loadMultiPointZRecord();
129 16
                break;
130
            default:
131
                $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType));
132
                break;
133
        }
134
135
        /* We need to skip rest of the record */
136 164
        while ($this->read < $this->size) {
137 48
            $this->loadData('V', 4);
138
        }
139
140
        /* Check if we didn't read too much */
141 164
        if ($this->read !== $this->size) {
142
            $this->setError(sprintf('Failed to parse record, read=%d, size=%d', $this->read, $this->size));
143
        }
144
145 164
        if (! ShapeFile::supportsDbase() || ! isset($this->dbfFile)) {
146 76
            return;
147
        }
148
149 88
        $this->loadDBFData();
150 88
    }
151
152
    /**
153
     * Saves record to files.
154
     *
155
     * @param resource $shpFile      Opened SHP file
156
     * @param resource $dbfFile      Opened DBF file
157
     * @param int      $recordNumber Record number
158
     */
159 108
    public function saveToFile(&$shpFile, &$dbfFile, int $recordNumber): void
160
    {
161 108
        $this->shpFile = $shpFile;
162 108
        $this->dbfFile = $dbfFile;
163 108
        $this->recordNumber = $recordNumber;
164 108
        $this->saveHeaders();
165
166 108
        switch ($this->shapeType) {
167 108
            case 0:
168
                // Nothing to save
169
                break;
170 108
            case 1:
171 20
                $this->savePointRecord();
172 20
                break;
173 100
            case 21:
174 20
                $this->savePointMRecord();
175 20
                break;
176 92
            case 11:
177 20
                $this->savePointZRecord();
178 20
                break;
179 84
            case 3:
180 20
                $this->savePolyLineRecord();
181 20
                break;
182 64
            case 23:
183 8
                $this->savePolyLineMRecord();
184 8
                break;
185 56
            case 13:
186 8
                $this->savePolyLineZRecord();
187 8
                break;
188 48
            case 5:
189 8
                $this->savePolygonRecord();
190 8
                break;
191 40
            case 25:
192 8
                $this->savePolygonMRecord();
193 8
                break;
194 32
            case 15:
195 8
                $this->savePolygonZRecord();
196 8
                break;
197 24
            case 8:
198 8
                $this->saveMultiPointRecord();
199 8
                break;
200 16
            case 28:
201 8
                $this->saveMultiPointMRecord();
202 8
                break;
203 8
            case 18:
204 8
                $this->saveMultiPointZRecord();
205 8
                break;
206
            default:
207
                $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType));
208
                break;
209
        }
210
211 108
        if (! ShapeFile::supportsDbase() || $this->dbfFile === null) {
212 48
            return;
213
        }
214
215 60
        $this->saveDBFData();
216 60
    }
217
218
    /**
219
     * Updates DBF data to match header.
220
     *
221
     * @param array $header DBF structure header
222
     */
223 108
    public function updateDBFInfo(array $header): void
224
    {
225 108
        $tmp = $this->dbfData;
226 108
        unset($this->dbfData);
227 108
        $this->dbfData = [];
228 108
        foreach ($header as $value) {
229 108
            $this->dbfData[$value[0]] = $tmp[$value[0]] ?? '';
230
        }
231 108
    }
232
233
    /**
234
     * Reads data.
235
     *
236
     * @param string $type  type for unpack()
237
     * @param int    $count number of bytes
238
     *
239
     * @return mixed
240
     */
241 164
    private function loadData(string $type, int $count)
242
    {
243 164
        $data = $this->shapeFile->readSHP($count);
244 164
        if ($data === false) {
245
            return false;
246
        }
247
248 164
        $this->read += strlen($data);
249
250 164
        return Util::loadData($type, $data);
251
    }
252
253
    /**
254
     * Loads metadata header from a file.
255
     */
256 164
    private function loadHeaders(): void
257
    {
258 164
        $this->shapeType = false;
259 164
        $this->recordNumber = $this->loadData('N', 4);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->loadData('N', 4) can also be of type false. However, the property $recordNumber is declared as type integer|null. 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...
260 164
        if ($this->recordNumber === false) {
261 164
            return;
262
        }
263
264
        // We read the length of the record
265 164
        $this->size = $this->loadData('N', 4);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->loadData('N', 4) can also be of type false. However, the property $size 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...
266 164
        if ($this->size === false) {
267
            return;
268
        }
269
270 164
        $this->size = ($this->size * 2) + 8;
271 164
        $this->shapeType = $this->loadData('V', 4);
272 164
    }
273
274
    /**
275
     * Saves metadata header to a file.
276
     */
277 108
    private function saveHeaders(): void
278
    {
279 108
        fwrite($this->shpFile, pack('N', $this->recordNumber));
280 108
        fwrite($this->shpFile, pack('N', $this->getContentLength()));
281 108
        fwrite($this->shpFile, pack('V', $this->shapeType));
282 108
    }
283
284 164
    private function loadPoint(): array
285
    {
286 164
        $data = [];
287
288 164
        $data['x'] = $this->loadData('d', 8);
289 164
        $data['y'] = $this->loadData('d', 8);
290
291 164
        return $data;
292
    }
293
294 20
    private function loadPointM(): array
295
    {
296 20
        $data = $this->loadPoint();
297
298 20
        $data['m'] = $this->loadData('d', 8);
299
300 20
        return $data;
301
    }
302
303 20
    private function loadPointZ(): array
304
    {
305 20
        $data = $this->loadPoint();
306
307 20
        $data['z'] = $this->loadData('d', 8);
308 20
        $data['m'] = $this->loadData('d', 8);
309
310 20
        return $data;
311
    }
312
313 92
    private function savePoint(array $data): void
314
    {
315 92
        fwrite($this->shpFile, Util::packDouble($data['x']));
316 92
        fwrite($this->shpFile, Util::packDouble($data['y']));
317 92
    }
318
319 20
    private function savePointM(array $data): void
320
    {
321 20
        fwrite($this->shpFile, Util::packDouble($data['x']));
322 20
        fwrite($this->shpFile, Util::packDouble($data['y']));
323 20
        fwrite($this->shpFile, Util::packDouble($data['m']));
324 20
    }
325
326 20
    private function savePointZ(array $data): void
327
    {
328 20
        fwrite($this->shpFile, Util::packDouble($data['x']));
329 20
        fwrite($this->shpFile, Util::packDouble($data['y']));
330 20
        fwrite($this->shpFile, Util::packDouble($data['z']));
331 20
        fwrite($this->shpFile, Util::packDouble($data['m']));
332 20
    }
333
334
    private function loadNullRecord(): void
335
    {
336
        $this->shpData = [];
337
    }
338
339 44
    private function loadPointRecord(): void
340
    {
341 44
        $this->shpData = $this->loadPoint();
342 44
    }
343
344 20
    private function loadPointMRecord(): void
345
    {
346 20
        $this->shpData = $this->loadPointM();
347 20
    }
348
349 20
    private function loadPointZRecord(): void
350
    {
351 20
        $this->shpData = $this->loadPointZ();
352 20
    }
353
354 20
    private function savePointRecord(): void
355
    {
356 20
        $this->savePoint($this->shpData);
357 20
    }
358
359 20
    private function savePointMRecord(): void
360
    {
361 20
        $this->savePointM($this->shpData);
362 20
    }
363
364 20
    private function savePointZRecord(): void
365
    {
366 20
        $this->savePointZ($this->shpData);
367 20
    }
368
369 116
    private function loadBBox(): void
370
    {
371 116
        $this->shpData['xmin'] = $this->loadData('d', 8);
372 116
        $this->shpData['ymin'] = $this->loadData('d', 8);
373 116
        $this->shpData['xmax'] = $this->loadData('d', 8);
374 116
        $this->shpData['ymax'] = $this->loadData('d', 8);
375 116
    }
376
377 32
    private function loadMultiPointRecord(): void
378
    {
379 32
        $this->shpData = [];
380 32
        $this->loadBBox();
381
382 32
        $this->shpData['numpoints'] = $this->loadData('V', 4);
383
384 32
        for ($i = 0; $i < $this->shpData['numpoints']; ++$i) {
385 32
            $this->shpData['points'][] = $this->loadPoint();
386
        }
387 32
    }
388
389 24
    private function loadMultiPointMZRecord(string $type): void
390
    {
391
        /* The m dimension is optional, depends on bounding box data */
392 24
        if ($type === 'm' && ! $this->shapeFile->hasMeasure()) {
393 24
            return;
394
        }
395
396 16
        $this->shpData[$type . 'min'] = $this->loadData('d', 8);
397 16
        $this->shpData[$type . 'max'] = $this->loadData('d', 8);
398
399 16
        for ($i = 0; $i < $this->shpData['numpoints']; ++$i) {
400 16
            $this->shpData['points'][$i][$type] = $this->loadData('d', 8);
401
        }
402 16
    }
403
404 8
    private function loadMultiPointMRecord(): void
405
    {
406 8
        $this->loadMultiPointRecord();
407
408 8
        $this->loadMultiPointMZRecord('m');
409 8
    }
410
411 16
    private function loadMultiPointZRecord(): void
412
    {
413 16
        $this->loadMultiPointRecord();
414
415 16
        $this->loadMultiPointMZRecord('z');
416 16
        $this->loadMultiPointMZRecord('m');
417 16
    }
418
419 24
    private function saveMultiPointRecord(): void
420
    {
421 24
        fwrite($this->shpFile, pack(
422 24
            'dddd',
423 24
            $this->shpData['xmin'],
424 24
            $this->shpData['ymin'],
425 24
            $this->shpData['xmax'],
426 24
            $this->shpData['ymax']
427
        ));
428
429 24
        fwrite($this->shpFile, pack('V', $this->shpData['numpoints']));
430
431 24
        for ($i = 0; $i < $this->shpData['numpoints']; ++$i) {
432 24
            $this->savePoint($this->shpData['points'][$i]);
433
        }
434 24
    }
435
436 16
    private function saveMultiPointMZRecord(string $type): void
437
    {
438 16
        fwrite($this->shpFile, pack('dd', $this->shpData[$type . 'min'], $this->shpData[$type . 'max']));
439
440 16
        for ($i = 0; $i < $this->shpData['numpoints']; ++$i) {
441 16
            fwrite($this->shpFile, Util::packDouble($this->shpData['points'][$i][$type]));
442
        }
443 16
    }
444
445 8
    private function saveMultiPointMRecord(): void
446
    {
447 8
        $this->saveMultiPointRecord();
448
449 8
        $this->saveMultiPointMZRecord('m');
450 8
    }
451
452 8
    private function saveMultiPointZRecord(): void
453
    {
454 8
        $this->saveMultiPointRecord();
455
456 8
        $this->saveMultiPointMZRecord('z');
457 8
        $this->saveMultiPointMZRecord('m');
458 8
    }
459
460 84
    private function loadPolyLineRecord(): void
461
    {
462 84
        $this->shpData = [];
463 84
        $this->loadBBox();
464
465 84
        $this->shpData['numparts'] = $this->loadData('V', 4);
466 84
        $this->shpData['numpoints'] = $this->loadData('V', 4);
467
468 84
        $numparts = $this->shpData['numparts'];
469 84
        $numpoints = $this->shpData['numpoints'];
470
471 84
        for ($i = 0; $i < $numparts; ++$i) {
472 84
            $this->shpData['parts'][$i] = $this->loadData('V', 4);
473
        }
474
475 84
        $part = 0;
476 84
        for ($i = 0; $i < $numpoints; ++$i) {
477 84
            if ($part + 1 < $numparts && $i === $this->shpData['parts'][$part + 1]) {
478 76
                ++$part;
479
            }
480
481 84
            if (! isset($this->shpData['parts'][$part]['points'])
482 84
                || ! is_array($this->shpData['parts'][$part]['points'])
483
            ) {
484 84
                $this->shpData['parts'][$part] = ['points' => []];
485
            }
486
487 84
            $this->shpData['parts'][$part]['points'][] = $this->loadPoint();
488
        }
489 84
    }
490
491 40
    private function loadPolyLineMZRecord(string $type): void
492
    {
493
        /* The m dimension is optional, depends on bounding box data */
494 40
        if ($type === 'm' && ! $this->shapeFile->hasMeasure()) {
495 40
            return;
496
        }
497
498 24
        $this->shpData[$type . 'min'] = $this->loadData('d', 8);
499 24
        $this->shpData[$type . 'max'] = $this->loadData('d', 8);
500
501 24
        $numparts = $this->shpData['numparts'];
502 24
        $numpoints = $this->shpData['numpoints'];
503
504 24
        $part = 0;
505 24
        for ($i = 0; $i < $numpoints; ++$i) {
506 24
            if ($part + 1 < $numparts && $i === $this->shpData['parts'][$part + 1]) {
507
                ++$part;
508
            }
509
510 24
            $this->shpData['parts'][$part]['points'][$i][$type] = $this->loadData('d', 8);
511
        }
512 24
    }
513
514 16
    private function loadPolyLineMRecord(): void
515
    {
516 16
        $this->loadPolyLineRecord();
517
518 16
        $this->loadPolyLineMZRecord('m');
519 16
    }
520
521 24
    private function loadPolyLineZRecord(): void
522
    {
523 24
        $this->loadPolyLineRecord();
524
525 24
        $this->loadPolyLineMZRecord('z');
526 24
        $this->loadPolyLineMZRecord('m');
527 24
    }
528
529 60
    private function savePolyLineRecord(): void
530
    {
531 60
        fwrite($this->shpFile, pack(
532 60
            'dddd',
533 60
            $this->shpData['xmin'],
534 60
            $this->shpData['ymin'],
535 60
            $this->shpData['xmax'],
536 60
            $this->shpData['ymax']
537
        ));
538
539 60
        fwrite($this->shpFile, pack('VV', $this->shpData['numparts'], $this->shpData['numpoints']));
540
541 60
        $partIndex = 0;
542 60
        for ($i = 0; $i < $this->shpData['numparts']; ++$i) {
543 60
            fwrite($this->shpFile, pack('V', $partIndex));
544 60
            $partIndex += count($this->shpData['parts'][$i]['points']);
545
        }
546
547 60
        foreach ($this->shpData['parts'] as $partData) {
548 60
            foreach ($partData['points'] as $pointData) {
549 60
                $this->savePoint($pointData);
550
            }
551
        }
552 60
    }
553
554 32
    private function savePolyLineMZRecord(string $type): void
555
    {
556 32
        fwrite($this->shpFile, pack('dd', $this->shpData[$type . 'min'], $this->shpData[$type . 'max']));
557
558 32
        foreach ($this->shpData['parts'] as $partData) {
559 32
            foreach ($partData['points'] as $pointData) {
560 32
                fwrite($this->shpFile, Util::packDouble($pointData[$type]));
561
            }
562
        }
563 32
    }
564
565 16
    private function savePolyLineMRecord(): void
566
    {
567 16
        $this->savePolyLineRecord();
568
569 16
        $this->savePolyLineMZRecord('m');
570 16
    }
571
572 16
    private function savePolyLineZRecord(): void
573
    {
574 16
        $this->savePolyLineRecord();
575
576 16
        $this->savePolyLineMZRecord('z');
577 16
        $this->savePolyLineMZRecord('m');
578 16
    }
579
580 24
    private function loadPolygonRecord(): void
581
    {
582 24
        $this->loadPolyLineRecord();
583 24
    }
584
585 8
    private function loadPolygonMRecord(): void
586
    {
587 8
        $this->loadPolyLineMRecord();
588 8
    }
589
590 16
    private function loadPolygonZRecord(): void
591
    {
592 16
        $this->loadPolyLineZRecord();
593 16
    }
594
595 8
    private function savePolygonRecord(): void
596
    {
597 8
        $this->savePolyLineRecord();
598 8
    }
599
600 8
    private function savePolygonMRecord(): void
601
    {
602 8
        $this->savePolyLineMRecord();
603 8
    }
604
605 8
    private function savePolygonZRecord(): void
606
    {
607 8
        $this->savePolyLineZRecord();
608 8
    }
609
610 108
    private function adjustBBox(array $point): void
611
    {
612
        // Adjusts bounding box based on point
613
        $directions = [
614 108
            'x',
615
            'y',
616
            'z',
617
            'm',
618
        ];
619 108
        foreach ($directions as $direction) {
620 108
            if (! isset($point[$direction])) {
621 76
                continue;
622
            }
623
624 108
            $min = $direction . 'min';
625 108
            $max = $direction . 'max';
626 108
            if (! isset($this->shpData[$min]) || ($this->shpData[$min] > $point[$direction])) {
627 108
                $this->shpData[$min] = $point[$direction];
628
            }
629
630 108
            if (isset($this->shpData[$max]) && ($this->shpData[$max] >= $point[$direction])) {
631 84
                continue;
632
            }
633
634 108
            $this->shpData[$max] = $point[$direction];
635
        }
636 108
    }
637
638
    /**
639
     * Sets dimension to 0 if not set.
640
     *
641
     * @param array  $point     Point to check
642
     * @param string $dimension Dimension to check
643
     *
644
     * @return array
645
     */
646 76
    private function fixPoint(array $point, string $dimension): array
647
    {
648 76
        if (! isset($point[$dimension])) {
649 64
            $point[$dimension] = 0.0; // no_value
650
        }
651
652 76
        return $point;
653
    }
654
655
    /**
656
     * Adjust point and bounding box when adding point.
657
     *
658
     * @param array $point Point data
659
     *
660
     * @return array Fixed point data
661
     */
662 108
    private function adjustPoint(array $point): array
663
    {
664 108
        $type = $this->shapeType / 10;
665 108
        if ($type >= 2) {
666 44
            $point = $this->fixPoint($point, 'm');
667 76
        } elseif ($type >= 1) {
668 44
            $point = $this->fixPoint($point, 'z');
669 44
            $point = $this->fixPoint($point, 'm');
670
        }
671
672 108
        return $point;
673
    }
674
675
    /**
676
     * Adds point to a record.
677
     *
678
     * @param array $point     Point data
679
     * @param int   $partIndex Part index
680
     */
681 108
    public function addPoint(array $point, int $partIndex = 0): void
682
    {
683 108
        $point = $this->adjustPoint($point);
684 108
        switch ($this->shapeType) {
685 108
            case 0:
686
                //Don't add anything
687
                return;
688 108
            case 1:
689 100
            case 11:
690 92
            case 21:
691
                //Substitutes the value of the current point
692 36
                $this->shpData = $point;
693 36
                break;
694 84
            case 3:
695 64
            case 5:
696 56
            case 13:
697 48
            case 15:
698 40
            case 23:
699 32
            case 25:
700
                //Adds a new point to the selected part
701 60
                $this->shpData['parts'][$partIndex]['points'][] = $point;
702 60
                $this->shpData['numparts'] = count($this->shpData['parts']);
703 60
                $this->shpData['numpoints'] = 1 + ($this->shpData['numpoints'] ?? 0);
704 60
                break;
705 24
            case 8:
706 16
            case 18:
707 8
            case 28:
708
                //Adds a new point
709 24
                $this->shpData['points'][] = $point;
710 24
                $this->shpData['numpoints'] = 1 + ($this->shpData['numpoints'] ?? 0);
711 24
                break;
712
            default:
713
                $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType));
714
715
                return;
716
        }
717
718 108
        $this->adjustBBox($point);
719 108
    }
720
721
    /**
722
     * Deletes point from a record.
723
     *
724
     * @param int $pointIndex Point index
725
     * @param int $partIndex  Part index
726
     */
727 96
    public function deletePoint(int $pointIndex = 0, int $partIndex = 0): void
728
    {
729 96
        switch ($this->shapeType) {
730 96
            case 0:
731
                //Don't delete anything
732
                break;
733 96
            case 1:
734 88
            case 11:
735 80
            case 21:
736
                //Sets the value of the point to zero
737 24
                $this->shpData['x'] = 0.0;
738 24
                $this->shpData['y'] = 0.0;
739 24
                if (in_array($this->shapeType, [11, 21])) {
740 16
                    $this->shpData['m'] = 0.0;
741
                }
742
743 24
                if (in_array($this->shapeType, [11])) {
744 8
                    $this->shpData['z'] = 0.0;
745
                }
746
747 24
                break;
748 72
            case 3:
749 64
            case 5:
750 56
            case 13:
751 48
            case 15:
752 40
            case 23:
753 32
            case 25:
754
                //Deletes the point from the selected part, if exists
755 48
                if (isset($this->shpData['parts'][$partIndex])
756 48
                    && isset($this->shpData['parts'][$partIndex]['points'][$pointIndex])
757
                ) {
758 48
                    $count = count($this->shpData['parts'][$partIndex]['points']) - 1;
759 48
                    for ($i = $pointIndex; $i < $count; ++$i) {
760 48
                        $point = $this->shpData['parts'][$partIndex]['points'][$i + 1];
761 48
                        $this->shpData['parts'][$partIndex]['points'][$i] = $point;
762
                    }
763
764 48
                    $count = count($this->shpData['parts'][$partIndex]['points']) - 1;
765 48
                    unset($this->shpData['parts'][$partIndex]['points'][$count]);
766
767 48
                    $this->shpData['numparts'] = count($this->shpData['parts']);
768 48
                    --$this->shpData['numpoints'];
769
                }
770
771 48
                break;
772 24
            case 8:
773 16
            case 18:
774 8
            case 28:
775
                //Deletes the point, if exists
776 24
                if (isset($this->shpData['points'][$pointIndex])) {
777 24
                    $count = count($this->shpData['points']) - 1;
778 24
                    for ($i = $pointIndex; $i < $count; ++$i) {
779 24
                        $this->shpData['points'][$i] = $this->shpData['points'][$i + 1];
780
                    }
781
782 24
                    unset($this->shpData['points'][count($this->shpData['points']) - 1]);
783
784 24
                    --$this->shpData['numpoints'];
785
                }
786
787 24
                break;
788
            default:
789
                $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType));
790
                break;
791
        }
792 96
    }
793
794
    /**
795
     * Returns length of content.
796
     */
797 108
    public function getContentLength(): ?int
798
    {
799
        // The content length for a record is the length of the record contents section measured in 16-bit words.
800
        // one coordinate makes 4 16-bit words (64 bit double)
801 108
        switch ($this->shapeType) {
802 108
            case 0:
803
                $result = 0;
804
                break;
805 108
            case 1:
806 20
                $result = 10;
807 20
                break;
808 100
            case 21:
809 20
                $result = 10 + 4;
810 20
                break;
811 92
            case 11:
812 20
                $result = 10 + 8;
813 20
                break;
814 84
            case 3:
815 64
            case 5:
816 28
                $count = count($this->shpData['parts']);
817 28
                $result = 22 + 2 * $count;
818 28
                for ($i = 0; $i < $count; ++$i) {
819 28
                    $result += 8 * count($this->shpData['parts'][$i]['points']);
820
                }
821
822 28
                break;
823 56
            case 23:
824 48
            case 25:
825 16
                $count = count($this->shpData['parts']);
826 16
                $result = 22 + (2 * 4) + 2 * $count;
827 16
                for ($i = 0; $i < $count; ++$i) {
828 16
                    $result += (8 + 4) * count($this->shpData['parts'][$i]['points']);
829
                }
830
831 16
                break;
832 40
            case 13:
833 32
            case 15:
834 16
                $count = count($this->shpData['parts']);
835 16
                $result = 22 + (4 * 4) + 2 * $count;
836 16
                for ($i = 0; $i < $count; ++$i) {
837 16
                    $result += (8 + 8) * count($this->shpData['parts'][$i]['points']);
838
                }
839
840 16
                break;
841 24
            case 8:
842 8
                $result = 20 + 8 * count($this->shpData['points']);
843 8
                break;
844 16
            case 28:
845 8
                $result = 20 + (2 * 4) + (8 + 4) * count($this->shpData['points']);
846 8
                break;
847 8
            case 18:
848 8
                $result = 20 + (4 * 4) + (8 + 8) * count($this->shpData['points']);
849 8
                break;
850
            default:
851
                $result = null;
852
                $this->setError(sprintf('The Shape Type "%s" is not supported.', $this->shapeType));
853
                break;
854
        }
855
856 108
        return $result;
857
    }
858
859 88
    private function loadDBFData(): void
860
    {
861 88
        $this->dbfData = @dbase_get_record_with_names($this->dbfFile, $this->recordNumber);
0 ignored issues
show
Bug introduced by
The function dbase_get_record_with_names 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

861
        $this->dbfData = @/** @scrutinizer ignore-call */ dbase_get_record_with_names($this->dbfFile, $this->recordNumber);
Loading history...
862 88
        unset($this->dbfData['deleted']);
863 88
    }
864
865 60
    private function saveDBFData(): void
866
    {
867 60
        if (count($this->dbfData) === 0) {
868
            return;
869
        }
870
871 60
        unset($this->dbfData['deleted']);
872 60
        if ($this->recordNumber <= dbase_numrecords($this->dbfFile)) {
0 ignored issues
show
Bug introduced by
The function dbase_numrecords 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

872
        if ($this->recordNumber <= /** @scrutinizer ignore-call */ dbase_numrecords($this->dbfFile)) {
Loading history...
873
            if (! dbase_replace_record($this->dbfFile, array_values($this->dbfData), $this->recordNumber)) {
0 ignored issues
show
Bug introduced by
The function dbase_replace_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

873
            if (! /** @scrutinizer ignore-call */ dbase_replace_record($this->dbfFile, array_values($this->dbfData), $this->recordNumber)) {
Loading history...
874
                $this->setError('I wasn\'t possible to update the information in the DBF file.');
875
            }
876
        } else {
877 60
            if (! dbase_add_record($this->dbfFile, array_values($this->dbfData))) {
0 ignored issues
show
Bug introduced by
The function dbase_add_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

877
            if (! /** @scrutinizer ignore-call */ dbase_add_record($this->dbfFile, array_values($this->dbfData))) {
Loading history...
878
                $this->setError('I wasn\'t possible to add the information to the DBF file.');
879
            }
880
        }
881 60
    }
882
883
    /**
884
     * Sets error message.
885
     */
886
    public function setError(string $error): void
887
    {
888
        $this->lastError = $error;
889
    }
890
891
    /**
892
     * Returns shape name.
893
     */
894 8
    public function getShapeName(): string
895
    {
896 8
        return Util::nameShape($this->shapeType);
897
    }
898
}
899