Skip to content

Commit 2d0285c

Browse files
committed
feat!: full NHTSA vPIC API coverage with typed DTOs
Rewrite the package from the single VinDecode class into complete coverage of the NHTSA vPIC vehicle API. - Mattsplat\VinDecode\Vpic: one method per documented vPIC endpoint (VIN decoding, WMI, manufacturers, makes, models, vehicle types, variables, equipment plant codes, parts, Canadian specifications) - Mattsplat\VinDecode\Client: Guzzle-backed HTTP wrapper, injectable - Immutable typed DTOs for every response; Response envelope wrapper - VpicException / VpicRequestException - PHPUnit suite with fixtures captured from the live API, PHPStan level 8, Laravel Pint, GitHub Actions CI (PHP 8.1-8.4) - Package hygiene: LICENSE (MIT), CHANGELOG, CONTRIBUTING, SECURITY, docs/ guides BREAKING CHANGE: root namespace is now Mattsplat\VinDecode\ and minimum PHP is 8.1. The old VinDecode\VinDecode class is kept as a deprecated shim. Claude-Session: https://claude.ai/code/session_01P1vn4Bg2KpSFyRQcnq78Cc
1 parent 32ecc13 commit 2d0285c

77 files changed

Lines changed: 5876 additions & 84 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
insert_final_newline = true
7+
trim_trailing_whitespace = true
8+
indent_style = space
9+
indent_size = 4
10+
11+
[*.{yml,yaml,json,neon}]
12+
indent_size = 2
13+
14+
[*.md]
15+
trim_trailing_whitespace = false

.gitattributes

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Keep the Composer dist tarball lean — exclude dev-only files.
2+
/.editorconfig export-ignore
3+
/.gitattributes export-ignore
4+
/.gitignore export-ignore
5+
/.github export-ignore
6+
/docs export-ignore
7+
/tests export-ignore
8+
/example.php export-ignore
9+
/phpunit.xml.dist export-ignore
10+
/phpstan.neon export-ignore
11+
/pint.json export-ignore
12+
/CONTRIBUTING.md export-ignore
13+
/SECURITY.md export-ignore
14+
15+
*.php diff=php

.github/workflows/ci.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
php: ['8.1', '8.2', '8.3', '8.4']
15+
16+
name: PHP ${{ matrix.php }}
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- name: Setup PHP
22+
uses: shivammathur/setup-php@v2
23+
with:
24+
php-version: ${{ matrix.php }}
25+
extensions: json, mbstring
26+
coverage: none
27+
28+
- name: Validate composer.json
29+
run: composer validate --strict
30+
31+
- name: Install dependencies
32+
run: composer update --prefer-dist --no-interaction --no-progress
33+
34+
- name: Check code style
35+
run: composer lint
36+
37+
- name: Static analysis
38+
run: composer analyse
39+
40+
- name: Run tests
41+
run: composer test

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
11
/vendor/
2+
/composer.lock
3+
/.phpunit.cache/
4+
/.phpunit.result.cache
5+
/.phpstan.cache/
6+
/build/
7+
/coverage/
8+
.DS_Store

CHANGELOG.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Changelog
2+
3+
All notable changes to `mattsplat/vin-decode` are documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
Full rewrite: complete coverage of the NHTSA vPIC vehicle API.
11+
12+
### Added
13+
- `Mattsplat\VinDecode\Vpic` — one method per documented vPIC endpoint:
14+
- VIN decoding: `decodeVin`, `decodeVinFlat`, `decodeVinExtended`,
15+
`decodeVinFlatExtended`, `decodeVinBatch`
16+
- WMI: `decodeWmi`, `wmisForManufacturer`
17+
- Manufacturers & makes: `allManufacturers`, `manufacturerDetails`,
18+
`makesForManufacturer`, `makesForManufacturerAndYear`, `allMakes`
19+
- Models: `modelsForMake`, `modelsForMakeId`, `modelsForMakeYear`,
20+
`modelsForMakeIdYear`
21+
- Vehicle types: `makesForVehicleType`, `vehicleTypesForMake`,
22+
`vehicleTypesForMakeId`
23+
- Variables, equipment & parts: `vehicleVariableList`, `vehicleVariableValues`,
24+
`equipmentPlantCodes`, `parts`
25+
- Canadian specifications: `canadianVehicleSpecifications`
26+
- `Mattsplat\VinDecode\Client` — Guzzle-backed HTTP wrapper; accepts a custom
27+
`ClientInterface` and base URI.
28+
- Immutable typed DTOs for every response type under `Mattsplat\VinDecode\DTO`.
29+
- `Mattsplat\VinDecode\Responses\Response` — iterable / countable / array-accessible
30+
wrapper over the `{Count, Message, SearchCriteria, Results}` envelope.
31+
- `Mattsplat\VinDecode\Exceptions\VpicException` and `VpicRequestException`.
32+
- Test suite (PHPUnit, mocked HTTP with fixtures captured from the live API),
33+
PHPStan (level 8) and Laravel Pint configs, and a GitHub Actions CI workflow
34+
(PHP 8.1–8.4).
35+
- Package hygiene: `LICENSE` (MIT), this changelog, `CONTRIBUTING.md`,
36+
`SECURITY.md`, `.editorconfig`, `.gitattributes`, `docs/` guides.
37+
38+
### Changed
39+
- **BREAKING:** root namespace is now `Mattsplat\VinDecode\`.
40+
- **BREAKING:** minimum PHP version is 8.1.
41+
- `composer.json` filled out with license, authors, keywords, dev dependencies
42+
and Composer scripts; adds `guzzlehttp/guzzle` as a runtime dependency.
43+
44+
### Deprecated
45+
- `VinDecode\VinDecode` — kept as a thin backwards-compatible shim over `Vpic`
46+
(`setVIN()` / `searchVIN()` behave as before). Use `Mattsplat\VinDecode\Vpic`
47+
directly. Scheduled for removal in the next major release.
48+
49+
## [0.1.0] - 2018-08-13
50+
51+
### Added
52+
- Initial `VinDecode` class: `setVIN()` + `searchVIN()` against
53+
`vehicles/DecodeVin`, returning a snake_cased flat array.
54+
55+
[Unreleased]: https://github.com/mattsplat/vin-decoder/compare/master...HEAD

CONTRIBUTING.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Contributing
2+
3+
Thanks for your interest in improving `mattsplat/vin-decode`.
4+
5+
## Getting set up
6+
7+
```bash
8+
git clone git@github.com:mattsplat/vin-decoder.git
9+
cd vin-decoder
10+
composer install
11+
```
12+
13+
## Before you open a PR
14+
15+
Run the full check suite locally — CI runs the same thing on PHP 8.1 through 8.4:
16+
17+
```bash
18+
composer lint # Pint, code style (composer format to auto-fix)
19+
composer analyse # PHPStan static analysis
20+
composer test # PHPUnit
21+
```
22+
23+
## Guidelines
24+
25+
- **One logical change per PR.** Keep the diff focused.
26+
- **Tests are required** for any behavioural change. HTTP is mocked with Guzzle's
27+
`MockHandler`; response fixtures live in `tests/fixtures/` and are captured
28+
verbatim from real vPIC API responses. When adding an endpoint, add a fixture
29+
and a test that asserts the DTO mapping.
30+
- **Document new endpoints** in the relevant `docs/` page and the README method
31+
reference table.
32+
- **Update `CHANGELOG.md`** under `## [Unreleased]`.
33+
- Follow the existing code style (PSR-12 via Pint). Don't hand-format.
34+
35+
## Reporting bugs
36+
37+
Open an issue with the VIN or request parameters used, the response you got, and
38+
the response you expected. Do not include personal data in issues.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2018 Matt Coleman
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 164 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,170 @@
1-
# vin-decoder
2-
### Install
3-
`composer require mattsplat/vin-decode`
1+
# vin-decode
42

5-
Uses National Highway Traffic Safety Administration's API to determine vehicle information
3+
[![CI](https://github.com/mattsplat/vin-decoder/actions/workflows/ci.yml/badge.svg)](https://github.com/mattsplat/vin-decoder/actions/workflows/ci.yml)
4+
[![Latest Version](https://img.shields.io/packagist/v/mattsplat/vin-decode.svg)](https://packagist.org/packages/mattsplat/vin-decode)
5+
[![License](https://img.shields.io/packagist/l/mattsplat/vin-decode.svg)](LICENSE)
66

7-
#### Use
7+
A PHP wrapper for the [National Highway Traffic Safety Administration's vPIC
8+
API](https://vpic.nhtsa.dot.gov/api/) — decode VINs and look up vehicle makes,
9+
models, manufacturers, WMI codes, equipment plant codes and more.
810

11+
- One method per documented vPIC endpoint
12+
- Typed value objects for every response
13+
- Guzzle under the hood; inject your own client for testing or custom middleware
14+
- No API key required (vPIC is free and unauthenticated)
15+
16+
## Install
17+
18+
```bash
19+
composer require mattsplat/vin-decode
20+
```
21+
22+
Requires PHP 8.1+.
23+
24+
## Quick start
25+
26+
```php
27+
use Mattsplat\VinDecode\Vpic;
28+
29+
$vpic = new Vpic();
30+
31+
// Decode a VIN into one flat object
32+
$vehicle = $vpic->decodeVinFlat('5UXWX7C5*BA', 2011);
33+
34+
$vehicle->make(); // "BMW"
35+
$vehicle->model(); // "X3"
36+
$vehicle->modelYear(); // 2011
37+
$vehicle->bodyClass(); // "Sport Utility Vehicle (SUV)/Multipurpose Vehicle (MPV)"
38+
$vehicle->engineCylinders(); // 6
39+
$vehicle->get('PlantCity'); // "MUNICH" — any raw vPIC field
40+
41+
// List every model a make has registered
42+
foreach ($vpic->modelsForMake('Honda') as $model) {
43+
echo $model->name, "\n";
44+
}
945
```
10-
$vin_decode = new VinDecode();
1146

12-
$vin_decode->setVIN('4G2JB3249VB205377');
13-
$vin_decode->searchVIN();
47+
List endpoints return a `Response` that is countable, iterable and array-accessible:
48+
49+
```php
50+
$response = $vpic->allManufacturers(page: 2);
51+
52+
$response->count(); // rows in this response
53+
$response->reportedCount; // vPIC's own Count field
54+
$response->message; // vPIC status message
55+
$response->first(); // first DTO, or null
56+
$response->all(); // list<Manufacturer>
1457
```
58+
59+
Errors (transport failure, non-2xx, non-JSON body) throw
60+
`Mattsplat\VinDecode\Exceptions\VpicRequestException`, which extends
61+
`Mattsplat\VinDecode\Exceptions\VpicException`.
62+
63+
## Method reference
64+
65+
<a name="method-reference"></a>
66+
67+
### VIN decoding
68+
69+
| Method | vPIC endpoint | Returns |
70+
| --- | --- | --- |
71+
| `decodeVin(string $vin, ?int $modelYear = null)` | `DecodeVin` | `Response<VinVariable>` |
72+
| `decodeVinFlat(string $vin, ?int $modelYear = null)` | `DecodeVinValues` | `VinResult` |
73+
| `decodeVinExtended(string $vin, ?int $modelYear = null)` | `DecodeVinExtended` | `Response<VinVariable>` |
74+
| `decodeVinFlatExtended(string $vin, ?int $modelYear = null)` | `DecodeVinValuesExtended` | `VinResult` |
75+
| `decodeVinBatch(array $vins)` | `DecodeVinValuesBatch` (POST, max 50) | `Response<VinResult>` |
76+
77+
### WMI
78+
79+
| Method | vPIC endpoint | Returns |
80+
| --- | --- | --- |
81+
| `decodeWmi(string $wmi)` | `DecodeWMI` | `Wmi` |
82+
| `wmisForManufacturer(string\|int $manufacturer, ?string $vehicleType = null)` | `GetWMIsForManufacturer` | `Response<WmiManufacturer>` |
83+
84+
### Manufacturers & makes
85+
86+
| Method | vPIC endpoint | Returns |
87+
| --- | --- | --- |
88+
| `allManufacturers(?string $manufacturerType = null, int $page = 1)` | `GetAllManufacturers` | `Response<Manufacturer>` |
89+
| `manufacturerDetails(string\|int $manufacturer, int $page = 1)` | `GetManufacturerDetails` | `Response<ManufacturerDetail>` |
90+
| `makesForManufacturer(string\|int $manufacturer)` | `GetMakeForManufacturer` | `Response<Make>` |
91+
| `makesForManufacturerAndYear(string\|int $manufacturer, int $year)` | `GetMakesForManufacturerAndYear` | `Response<Make>` |
92+
| `allMakes()` | `GetAllMakes` | `Response<Make>` |
93+
94+
### Models
95+
96+
| Method | vPIC endpoint | Returns |
97+
| --- | --- | --- |
98+
| `modelsForMake(string $make)` | `GetModelsForMake` | `Response<Model>` |
99+
| `modelsForMakeId(int $makeId)` | `GetModelsForMakeId` | `Response<Model>` |
100+
| `modelsForMakeYear(string $make, int $year, ?string $vehicleType = null)` | `GetModelsForMakeYear` | `Response<Model>` |
101+
| `modelsForMakeIdYear(int $makeId, int $year, ?string $vehicleType = null)` | `GetModelsForMakeIdYear` | `Response<Model>` |
102+
103+
### Vehicle types
104+
105+
| Method | vPIC endpoint | Returns |
106+
| --- | --- | --- |
107+
| `makesForVehicleType(string $vehicleType)` | `GetMakesForVehicleType` | `Response<Make>` |
108+
| `vehicleTypesForMake(string $make)` | `GetVehicleTypesForMake` | `Response<VehicleType>` |
109+
| `vehicleTypesForMakeId(int $makeId)` | `GetVehicleTypesForMakeId` | `Response<VehicleType>` |
110+
111+
### Variables, equipment & parts
112+
113+
| Method | vPIC endpoint | Returns |
114+
| --- | --- | --- |
115+
| `vehicleVariableList()` | `GetVehicleVariableList` | `Response<Variable>` |
116+
| `vehicleVariableValues(string\|int $variable)` | `GetVehicleVariableValuesList` | `Response<VariableValue>` |
117+
| `equipmentPlantCodes(int $year, int $equipmentType, string $reportType = 'All')` | `GetEquipmentPlantCodes` | `Response<PlantCode>` |
118+
| `parts(int $type, string $fromDate, string $toDate, string\|int\|null $manufacturer = null, int $page = 1)` | `GetParts` | `Response<Part>` |
119+
120+
### Canadian vehicle specifications
121+
122+
| Method | vPIC endpoint | Returns |
123+
| --- | --- | --- |
124+
| `canadianVehicleSpecifications(int $year, ?string $make = null, ?string $model = null, string $units = 'Metric')` | `GetCanadianVehicleSpecifications` | `Response<CanadianSpecification>` |
125+
126+
## Documentation
127+
128+
Per-endpoint guides with request/response examples live in [`docs/`](docs/README.md).
129+
130+
## Custom HTTP client
131+
132+
```php
133+
use GuzzleHttp\Client as GuzzleClient;
134+
use Mattsplat\VinDecode\Client;
135+
use Mattsplat\VinDecode\Vpic;
136+
137+
$vpic = new Vpic(new Client(new GuzzleClient([
138+
'timeout' => 5,
139+
// ... proxy, middleware, retry handler, etc.
140+
])));
141+
```
142+
143+
## Upgrading from 0.x
144+
145+
The original `VinDecode\VinDecode` class still works and is kept as a thin,
146+
**deprecated** shim over `Vpic`:
147+
148+
```php
149+
use VinDecode\VinDecode; // still works
150+
151+
$decode = new VinDecode();
152+
$decode->setVIN('5UXWX7C5XBL41234');
153+
$decode->searchVIN(); // snake_cased flat array, as before
154+
echo $decode->make;
155+
```
156+
157+
New code should use `Mattsplat\VinDecode\Vpic` directly. See [CHANGELOG.md](CHANGELOG.md).
158+
159+
## Contributing
160+
161+
See [CONTRIBUTING.md](CONTRIBUTING.md). Run `composer test`, `composer lint` and
162+
`composer analyse` before opening a PR.
163+
164+
## Security
165+
166+
See [SECURITY.md](SECURITY.md).
167+
168+
## License
169+
170+
MIT — see [LICENSE](LICENSE).

SECURITY.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Security Policy
2+
3+
## Supported versions
4+
5+
The latest tagged release receives security fixes.
6+
7+
## Reporting a vulnerability
8+
9+
Please do not open a public issue for security problems. Report them privately
10+
via GitHub's [security advisories](https://github.com/mattsplat/vin-decoder/security/advisories/new)
11+
or by emailing matthewjohncoleman@gmail.com.
12+
13+
You will get an acknowledgement within a few days, and a fix or mitigation plan
14+
once the report is confirmed.

0 commit comments

Comments
 (0)