Issues (10)

php-src/SimpleVin.php (6 issues)

Labels
Severity
1
<?php
2
3
namespace kalanis\simple_vin;
4
5
6
use DateTime;
7
use Psr\Clock\ClockInterface;
8
9
10
/**
11
 * Basic class for accessing VIN data
12
 */
13
class SimpleVin
14
{
15
    protected const VALID_VIN_LENGTH = 17;
16
    protected const CHECK_INDEX_ON_DIGIT = 8;
17
    // Character weights for 17 characters in VIN
18
    // Beware - some cars (like in Australia) does not have 17 characters VIN; then you cannot use this library
19
    /** @var int[] */
20
    protected static array $CharacterWeights = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];
21
22
    protected readonly int $startYear;
23
    protected readonly int $nextYear;
24
25
    protected readonly Datasources\Years $years;
26
    protected readonly Datasources\ValidCharacters $validCheckCharacters;
27
    protected readonly Datasources\CharacterTransliteration $characterTransliteration;
28
    protected readonly Datasources\WorldManufacturerIdentifiers $worldManufacturerIdentifiers;
29
30 37
    public function __construct(
31
        ?ClockInterface $clock = null,
32
    )
33
    {
34
        // no DI
35 37
        $this->years = new Datasources\Years();
0 ignored issues
show
The property years is declared read-only in kalanis\simple_vin\SimpleVin.
Loading history...
36 37
        $this->validCheckCharacters = new Datasources\ValidCharacters();
0 ignored issues
show
The property validCheckCharacters is declared read-only in kalanis\simple_vin\SimpleVin.
Loading history...
37 37
        $this->characterTransliteration = new Datasources\CharacterTransliteration();
0 ignored issues
show
The property characterTransliteration is declared read-only in kalanis\simple_vin\SimpleVin.
Loading history...
38 37
        $this->worldManufacturerIdentifiers = new Datasources\WorldManufacturerIdentifiers();
0 ignored issues
show
The property worldManufacturerIdentifiers is declared read-only in kalanis\simple_vin\SimpleVin.
Loading history...
39
40 37
        $clocks = $clock ? $clock->now() : new DateTime(); // because it has not a correct interface!
41
42 37
        $this->startYear = intdiv(intval($clocks->format('Y')), 30) * 30; // base year
0 ignored issues
show
The property startYear is declared read-only in kalanis\simple_vin\SimpleVin.
Loading history...
43 37
        $this->nextYear = intval($clocks->format('Y')) + 1; // next year against current date
0 ignored issues
show
The property nextYear is declared read-only in kalanis\simple_vin\SimpleVin.
Loading history...
44
    }
45
46 15
    public function isValid(string $vin): bool
47
    {
48 15
        if (static::VALID_VIN_LENGTH != strlen($vin)) {
49 2
            return false;
50
        }
51
52 13
        $checkCharacter = $vin[static::CHECK_INDEX_ON_DIGIT];
53
54 13
        $calculated = $this->calculateChecksum($vin);
55 13
        if (is_null($calculated)) {
56 2
            return false;
57
        }
58
59 11
        return $calculated == $this->validCheckCharacters[$checkCharacter];
60
    }
61
62 2
    public function restoreChecksum(string $vin): ?string
63
    {
64 2
        $char = $this->restoreChecksumCharacter($vin);
65
66 2
        if (is_null($char)) {
67 1
            return null;
68
        }
69
70 1
        $vin[static::CHECK_INDEX_ON_DIGIT] = $char;
71 1
        return $vin;
72
    }
73
74 5
    public function restoreChecksumCharacter(string $vin): ?string
75
    {
76 5
        if (static::VALID_VIN_LENGTH != strlen($vin)) {
77 2
            return null;
78
        }
79
80 3
        $calculated = $this->calculateChecksum($vin);
81 3
        if (is_null($calculated)) {
82 1
            return null;
83
        }
84
85 2
        $chars = array_map('strval', array_flip($this->validCheckCharacters->getArrayCopy()));
86
87 2
        return $chars[$calculated] ?? null;
88
    }
89
90 16
    protected function calculateChecksum(string $vin): ?int
91
    {
92 16
        $value = 0;
93
94 16
        for ($i = 0; $i < static::VALID_VIN_LENGTH; $i++) {
95 16
            if (empty(static::$CharacterWeights[$i])) {
96 13
                continue;
97
            }
98 16
            if (!isset($this->characterTransliteration[$vin[$i]])) {
99 3
                return null;
100
            }
101 16
            $value += (static::$CharacterWeights[$i] * ($this->characterTransliteration[$vin[$i]]));
102
        }
103
104 13
        return $value % 11;
105
    }
106
107 6
    public function getWorldManufacturer(string $vinOrWmi): string
108
    {
109 6
        if (empty($vinOrWmi)) {
110 1
            return '';
111
        }
112
113 5
        if (2 > strlen($vinOrWmi)) {
114 1
            return '';
115
        }
116
117 4
        $prefix = substr($vinOrWmi, 0, 3);
118 4
        if (2 < strlen($vinOrWmi) && isset($this->worldManufacturerIdentifiers[$prefix])) {
119 2
            return $this->worldManufacturerIdentifiers[$prefix];
120
        }
121
122 2
        $prefix = substr($vinOrWmi, 0, 2);
123 2
        return $this->worldManufacturerIdentifiers[$prefix] ?? '';
124
    }
125
126 17
    public function getModelYear(string $ident, ?int $startYear = null, ?int $nextYear = null): ?int
127
    {
128 17
        if (empty($ident)) {
129 1
            return null;
130
        }
131
132 16
        if (empty($startYear)) {
133 13
            $startYear = $this->startYear;
134
        }
135
136 16
        if (empty($nextYear)) {
137 15
            $nextYear = $this->nextYear;
138
        }
139
140 16
        if (!$this->years->checkStart($startYear)) {
141 1
            return null;
142
        }
143
144 15
        if (10 > strlen($ident)) {
145 2
            return $this->getModelYearByNumber($ident, $startYear, $nextYear);
146
        }
147
148 13
        return $this->getModelYearByNumber($ident[9], $startYear, $nextYear);
149
    }
150
151 15
    protected function getModelYearByNumber(string $yearCharacter, int $startYear, int $nextYear): ?int
152
    {
153 15
        if (isset($this->years[$yearCharacter])) {
154 12
            $year = $startYear + $this->years[$yearCharacter];
155 12
            if ($year > $nextYear) {
156 6
                $year -= 30;
157
            }
158 12
            return $year;
159
        }
160
161 3
        return null;
162
    }
163
}
164