Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

[*.{yml,yaml,json,neon}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
15 changes: 15 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Keep the Composer dist tarball lean — exclude dev-only files.
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.github export-ignore
/docs export-ignore
/tests export-ignore
/example.php export-ignore
/phpunit.xml.dist export-ignore
/phpstan.neon export-ignore
/pint.json export-ignore
/CONTRIBUTING.md export-ignore
/SECURITY.md export-ignore

*.php diff=php
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
push:
branches: [master]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.1', '8.2', '8.3', '8.4']

name: PHP ${{ matrix.php }}

steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: json, mbstring
coverage: none

- name: Validate composer.json
run: composer validate --strict

- name: Install dependencies
run: composer update --prefer-dist --no-interaction --no-progress

- name: Check code style
run: composer lint

- name: Static analysis
run: composer analyse

- name: Run tests
run: composer test
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
/vendor/
/composer.lock
/.phpunit.cache/
/.phpunit.result.cache
/.phpstan.cache/
/build/
/coverage/
.DS_Store
55 changes: 55 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Changelog

All notable changes to `mattsplat/vin-decode` are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

Full rewrite: complete coverage of the NHTSA vPIC vehicle API.

### Added
- `Mattsplat\VinDecode\Vpic` — one method per documented vPIC endpoint:
- VIN decoding: `decodeVin`, `decodeVinFlat`, `decodeVinExtended`,
`decodeVinFlatExtended`, `decodeVinBatch`
- WMI: `decodeWmi`, `wmisForManufacturer`
- Manufacturers & makes: `allManufacturers`, `manufacturerDetails`,
`makesForManufacturer`, `makesForManufacturerAndYear`, `allMakes`
- Models: `modelsForMake`, `modelsForMakeId`, `modelsForMakeYear`,
`modelsForMakeIdYear`
- Vehicle types: `makesForVehicleType`, `vehicleTypesForMake`,
`vehicleTypesForMakeId`
- Variables, equipment & parts: `vehicleVariableList`, `vehicleVariableValues`,
`equipmentPlantCodes`, `parts`
- Canadian specifications: `canadianVehicleSpecifications`
- `Mattsplat\VinDecode\Client` — Guzzle-backed HTTP wrapper; accepts a custom
`ClientInterface` and base URI.
- Immutable typed DTOs for every response type under `Mattsplat\VinDecode\DTO`.
- `Mattsplat\VinDecode\Responses\Response` — iterable / countable / array-accessible
wrapper over the `{Count, Message, SearchCriteria, Results}` envelope.
- `Mattsplat\VinDecode\Exceptions\VpicException` and `VpicRequestException`.
- Test suite (PHPUnit, mocked HTTP with fixtures captured from the live API),
PHPStan (level 8) and Laravel Pint configs, and a GitHub Actions CI workflow
(PHP 8.1–8.4).
- Package hygiene: `LICENSE` (MIT), this changelog, `CONTRIBUTING.md`,
`SECURITY.md`, `.editorconfig`, `.gitattributes`, `docs/` guides.

### Changed
- **BREAKING:** root namespace is now `Mattsplat\VinDecode\`.
- **BREAKING:** minimum PHP version is 8.1.
- `composer.json` filled out with license, authors, keywords, dev dependencies
and Composer scripts; adds `guzzlehttp/guzzle` as a runtime dependency.

### Deprecated
- `VinDecode\VinDecode` — kept as a thin backwards-compatible shim over `Vpic`
(`setVIN()` / `searchVIN()` behave as before). Use `Mattsplat\VinDecode\Vpic`
directly. Scheduled for removal in the next major release.

## [0.1.0] - 2018-08-13

### Added
- Initial `VinDecode` class: `setVIN()` + `searchVIN()` against
`vehicles/DecodeVin`, returning a snake_cased flat array.

[Unreleased]: https://github.com/mattsplat/vin-decoder/compare/master...HEAD
38 changes: 38 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Contributing

Thanks for your interest in improving `mattsplat/vin-decode`.

## Getting set up

```bash
git clone git@github.com:mattsplat/vin-decoder.git
cd vin-decoder
composer install
```

## Before you open a PR

Run the full check suite locally — CI runs the same thing on PHP 8.1 through 8.4:

```bash
composer lint # Pint, code style (composer format to auto-fix)
composer analyse # PHPStan static analysis
composer test # PHPUnit
```

## Guidelines

- **One logical change per PR.** Keep the diff focused.
- **Tests are required** for any behavioural change. HTTP is mocked with Guzzle's
`MockHandler`; response fixtures live in `tests/fixtures/` and are captured
verbatim from real vPIC API responses. When adding an endpoint, add a fixture
and a test that asserts the DTO mapping.
- **Document new endpoints** in the relevant `docs/` page and the README method
reference table.
- **Update `CHANGELOG.md`** under `## [Unreleased]`.
- Follow the existing code style (PSR-12 via Pint). Don't hand-format.

## Reporting bugs

Open an issue with the VIN or request parameters used, the response you got, and
the response you expected. Do not include personal data in issues.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 Matt Coleman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
172 changes: 164 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,170 @@
# vin-decoder
### Install
`composer require mattsplat/vin-decode`
# vin-decode

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

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

- One method per documented vPIC endpoint
- Typed value objects for every response
- Guzzle under the hood; inject your own client for testing or custom middleware
- No API key required (vPIC is free and unauthenticated)

## Install

```bash
composer require mattsplat/vin-decode
```

Requires PHP 8.1+.

## Quick start

```php
use Mattsplat\VinDecode\Vpic;

$vpic = new Vpic();

// Decode a VIN into one flat object
$vehicle = $vpic->decodeVinFlat('5UXWX7C5*BA', 2011);

$vehicle->make(); // "BMW"
$vehicle->model(); // "X3"
$vehicle->modelYear(); // 2011
$vehicle->bodyClass(); // "Sport Utility Vehicle (SUV)/Multipurpose Vehicle (MPV)"
$vehicle->engineCylinders(); // 6
$vehicle->get('PlantCity'); // "MUNICH" — any raw vPIC field

// List every model a make has registered
foreach ($vpic->modelsForMake('Honda') as $model) {
echo $model->name, "\n";
}
```
$vin_decode = new VinDecode();

$vin_decode->setVIN('4G2JB3249VB205377');
$vin_decode->searchVIN();
List endpoints return a `Response` that is countable, iterable and array-accessible:

```php
$response = $vpic->allManufacturers(page: 2);

$response->count(); // rows in this response
$response->reportedCount; // vPIC's own Count field
$response->message; // vPIC status message
$response->first(); // first DTO, or null
$response->all(); // list<Manufacturer>
```

Errors (transport failure, non-2xx, non-JSON body) throw
`Mattsplat\VinDecode\Exceptions\VpicRequestException`, which extends
`Mattsplat\VinDecode\Exceptions\VpicException`.

## Method reference

<a name="method-reference"></a>

### VIN decoding

| Method | vPIC endpoint | Returns |
| --- | --- | --- |
| `decodeVin(string $vin, ?int $modelYear = null)` | `DecodeVin` | `Response<VinVariable>` |
| `decodeVinFlat(string $vin, ?int $modelYear = null)` | `DecodeVinValues` | `VinResult` |
| `decodeVinExtended(string $vin, ?int $modelYear = null)` | `DecodeVinExtended` | `Response<VinVariable>` |
| `decodeVinFlatExtended(string $vin, ?int $modelYear = null)` | `DecodeVinValuesExtended` | `VinResult` |
| `decodeVinBatch(array $vins)` | `DecodeVinValuesBatch` (POST, max 50) | `Response<VinResult>` |

### WMI

| Method | vPIC endpoint | Returns |
| --- | --- | --- |
| `decodeWmi(string $wmi)` | `DecodeWMI` | `Wmi` |
| `wmisForManufacturer(string\|int $manufacturer, ?string $vehicleType = null)` | `GetWMIsForManufacturer` | `Response<WmiManufacturer>` |

### Manufacturers & makes

| Method | vPIC endpoint | Returns |
| --- | --- | --- |
| `allManufacturers(?string $manufacturerType = null, int $page = 1)` | `GetAllManufacturers` | `Response<Manufacturer>` |
| `manufacturerDetails(string\|int $manufacturer, int $page = 1)` | `GetManufacturerDetails` | `Response<ManufacturerDetail>` |
| `makesForManufacturer(string\|int $manufacturer)` | `GetMakeForManufacturer` | `Response<Make>` |
| `makesForManufacturerAndYear(string\|int $manufacturer, int $year)` | `GetMakesForManufacturerAndYear` | `Response<Make>` |
| `allMakes()` | `GetAllMakes` | `Response<Make>` |

### Models

| Method | vPIC endpoint | Returns |
| --- | --- | --- |
| `modelsForMake(string $make)` | `GetModelsForMake` | `Response<Model>` |
| `modelsForMakeId(int $makeId)` | `GetModelsForMakeId` | `Response<Model>` |
| `modelsForMakeYear(string $make, int $year, ?string $vehicleType = null)` | `GetModelsForMakeYear` | `Response<Model>` |
| `modelsForMakeIdYear(int $makeId, int $year, ?string $vehicleType = null)` | `GetModelsForMakeIdYear` | `Response<Model>` |

### Vehicle types

| Method | vPIC endpoint | Returns |
| --- | --- | --- |
| `makesForVehicleType(string $vehicleType)` | `GetMakesForVehicleType` | `Response<Make>` |
| `vehicleTypesForMake(string $make)` | `GetVehicleTypesForMake` | `Response<VehicleType>` |
| `vehicleTypesForMakeId(int $makeId)` | `GetVehicleTypesForMakeId` | `Response<VehicleType>` |

### Variables, equipment & parts

| Method | vPIC endpoint | Returns |
| --- | --- | --- |
| `vehicleVariableList()` | `GetVehicleVariableList` | `Response<Variable>` |
| `vehicleVariableValues(string\|int $variable)` | `GetVehicleVariableValuesList` | `Response<VariableValue>` |
| `equipmentPlantCodes(int $year, int $equipmentType, string $reportType = 'All')` | `GetEquipmentPlantCodes` | `Response<PlantCode>` |
| `parts(int $type, string $fromDate, string $toDate, string\|int\|null $manufacturer = null, int $page = 1)` | `GetParts` | `Response<Part>` |

### Canadian vehicle specifications

| Method | vPIC endpoint | Returns |
| --- | --- | --- |
| `canadianVehicleSpecifications(int $year, ?string $make = null, ?string $model = null, string $units = 'Metric')` | `GetCanadianVehicleSpecifications` | `Response<CanadianSpecification>` |

## Documentation

Per-endpoint guides with request/response examples live in [`docs/`](docs/README.md).

## Custom HTTP client

```php
use GuzzleHttp\Client as GuzzleClient;
use Mattsplat\VinDecode\Client;
use Mattsplat\VinDecode\Vpic;

$vpic = new Vpic(new Client(new GuzzleClient([
'timeout' => 5,
// ... proxy, middleware, retry handler, etc.
])));
```

## Upgrading from 0.x

The original `VinDecode\VinDecode` class still works and is kept as a thin,
**deprecated** shim over `Vpic`:

```php
use VinDecode\VinDecode; // still works

$decode = new VinDecode();
$decode->setVIN('5UXWX7C5XBL41234');
$decode->searchVIN(); // snake_cased flat array, as before
echo $decode->make;
```

New code should use `Mattsplat\VinDecode\Vpic` directly. See [CHANGELOG.md](CHANGELOG.md).

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md). Run `composer test`, `composer lint` and
`composer analyse` before opening a PR.

## Security

See [SECURITY.md](SECURITY.md).

## License

MIT — see [LICENSE](LICENSE).
14 changes: 14 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Security Policy

## Supported versions

The latest tagged release receives security fixes.

## Reporting a vulnerability

Please do not open a public issue for security problems. Report them privately
via GitHub's [security advisories](https://github.com/mattsplat/vin-decoder/security/advisories/new)
or by emailing matthewjohncoleman@gmail.com.

You will get an acknowledgement within a few days, and a fix or mitigation plan
once the report is confirmed.
Loading
Loading