Skip to content

DriveRight Client Library

Overview

Standalone PHP client for the DriveRight vehicle data API. Provides Dutch license plate lookup and vehicle fitment data.

Package: atraxion/driveright-client

Installation

composer require atraxion/driveright-client

Configuration

use Atraxion\DriveRight\Config;
use Atraxion\DriveRight\DriveRightClient;

$config = new Config(
    apiKey: 'your-api-key',
    baseUrl: 'https://api.driveright.nl/v2',
    timeout: 30
);

$client = new DriveRightClient($config);

Core Features

License Plate Lookup

$vehicle = $client->lookupPlate('AB-123-CD');

if ($vehicle) {
    echo $vehicle->getMake();      // "Volkswagen"
    echo $vehicle->getModel();     // "Golf"
    echo $vehicle->getYear();      // 2020
    echo $vehicle->getFuelType();  // "petrol"
    echo $vehicle->getEngineCode(); // "DFYA"
    echo $vehicle->getPowerKw();   // 110
    echo $vehicle->getPowerHp();   // 150
}

Get Vehicle Fitments

$fitments = $client->getFitments($vehicle->getVehicleId());

// Tyre fitments
foreach ($fitments->getTyreSizes() as $size) {
    echo $size->getWidth();       // 225
    echo $size->getAspectRatio(); // 45
    echo $size->getDiameter();    // 17
    echo $size->getCode();        // "225/45R17"
    echo $size->isOriginalEquipment(); // true
    echo $size->getPosition();    // "both" or "front"/"rear"
}

// Wheel fitments
foreach ($fitments->getWheelSpecs() as $spec) {
    echo $spec->getDiameter();    // 17
    echo $spec->getWidth();       // 7.5
    echo $spec->getPcd();         // "5x112"
    echo $spec->getCenterBore();  // 57.1
    echo $spec->getOffsetMin();   // 35
    echo $spec->getOffsetMax();   // 50
}

Get All Makes

$makes = $client->getMakes();

foreach ($makes as $make) {
    echo $make->getId();      // "volkswagen"
    echo $make->getName();    // "Volkswagen"
    echo $make->getLogoUrl(); // "https://..."
}

Get Models by Make

$models = $client->getModels('volkswagen');

foreach ($models as $model) {
    echo $model->getId();        // "golf"
    echo $model->getName();      // "Golf"
    echo $model->getYearFrom();  // 1974
    echo $model->getYearTo();    // null (still in production)
}

Get Generations

$generations = $client->getGenerations('volkswagen', 'golf');

foreach ($generations as $gen) {
    echo $gen->getId();        // "golf-8"
    echo $gen->getName();      // "Golf 8 (CD)"
    echo $gen->getYearFrom();  // 2019
    echo $gen->getYearTo();    // null
    echo $gen->getBodyType();  // "hatchback"
}

Get Variants

$variants = $client->getVariants('volkswagen', 'golf', 'golf-8');

foreach ($variants as $variant) {
    echo $variant->getId();         // "golf-8-20-tdi-150"
    echo $variant->getName();       // "2.0 TDI 150hp"
    echo $variant->getEngineCode(); // "DFYA"
    echo $variant->getPowerKw();    // 110
    echo $variant->getFuelType();   // "diesel"
}

Models

Vehicle

readonly class Vehicle
{
    public function __construct(
        public string $vehicleId,
        public string $make,
        public string $model,
        public int $year,
        public ?string $variant,
        public ?string $engineCode,
        public string $fuelType,
        public ?int $powerKw,
        public ?int $powerHp,
        public ?int $cylinderCapacity,
        public ?string $bodyType,
        public ?int $doors,
        public ?string $transmission,
        public ?string $driveType,
        public ?float $weight,
        public ?string $vin
    ) {}

    public function getDisplayName(): string
    {
        $name = "{$this->make} {$this->model}";
        if ($this->variant) {
            $name .= " {$this->variant}";
        }
        return $name;
    }
}

TyreSize

readonly class TyreSize
{
    public function __construct(
        public int $width,
        public int $aspectRatio,
        public float $diameter,
        public string $position = 'both',
        public bool $isOriginalEquipment = false,
        public ?string $loadIndex = null,
        public ?string $speedIndex = null
    ) {}

    public function getCode(): string
    {
        return sprintf('%d/%dR%.0f', $this->width, $this->aspectRatio, $this->diameter);
    }

    public function getFullCode(): string
    {
        $code = $this->getCode();
        if ($this->loadIndex && $this->speedIndex) {
            $code .= " {$this->loadIndex}{$this->speedIndex}";
        }
        return $code;
    }
}

WheelSpec

readonly class WheelSpec
{
    public function __construct(
        public float $diameter,
        public float $width,
        public string $pcd,
        public float $centerBore,
        public int $offsetMin,
        public int $offsetMax,
        public int $boltCount,
        public float $boltCircle
    ) {}

    public function getPcdParts(): array
    {
        // Parse "5x112" into [5, 112]
        preg_match('/(\d+)x(\d+(?:\.\d+)?)/', $this->pcd, $matches);
        return [
            'bolts' => (int) $matches[1],
            'circle' => (float) $matches[2],
        ];
    }

    public function isOffsetInRange(int $offset): bool
    {
        return $offset >= $this->offsetMin && $offset <= $this->offsetMax;
    }
}

VehicleFitments

readonly class VehicleFitments
{
    public function __construct(
        /** @var TyreSize[] */
        public array $tyreSizes,
        /** @var WheelSpec[] */
        public array $wheelSpecs,
        public array $originalEquipment = []
    ) {}

    public function getOeTyreSizes(): array
    {
        return array_filter(
            $this->tyreSizes,
            fn(TyreSize $size) => $size->isOriginalEquipment
        );
    }

    public function hasDifferentFrontRear(): bool
    {
        $positions = array_unique(array_map(
            fn(TyreSize $s) => $s->position,
            $this->tyreSizes
        ));
        return in_array('front', $positions) || in_array('rear', $positions);
    }
}

Error Handling

use Atraxion\DriveRight\Exception\ApiException;
use Atraxion\DriveRight\Exception\VehicleNotFoundException;
use Atraxion\DriveRight\Exception\InvalidPlateException;
use Atraxion\DriveRight\Exception\RateLimitException;

try {
    $vehicle = $client->lookupPlate('XX-000-XX');
} catch (InvalidPlateException $e) {
    // Invalid plate format
    echo "Invalid license plate format";
} catch (VehicleNotFoundException $e) {
    // Plate not found in database
    echo "Vehicle not found";
} catch (RateLimitException $e) {
    // Too many requests
    $retryAfter = $e->getRetryAfterSeconds();
} catch (ApiException $e) {
    // Other API error
    $this->logger->error('DriveRight error', [
        'code' => $e->getCode(),
        'message' => $e->getMessage(),
    ]);
}

Caching

use Atraxion\DriveRight\Cache\CachedDriveRightClient;

$cachedClient = new CachedDriveRightClient(
    client: $client,
    cache: $psr16Cache,
    plateTtl: 86400,      // Cache plate lookups for 24 hours
    fitmentsTtl: 604800,  // Cache fitments for 7 days
    catalogTtl: 86400     // Cache makes/models for 24 hours
);

Testing

Mock Client

use Atraxion\DriveRight\Testing\MockDriveRightClient;

$mock = new MockDriveRightClient();

$mock->addVehicle('AB-123-CD', new Vehicle(
    vehicleId: 'VH123',
    make: 'Volkswagen',
    model: 'Golf',
    year: 2020,
    fuelType: 'petrol'
));

$mock->addFitments('VH123', new VehicleFitments(
    tyreSizes: [
        new TyreSize(225, 45, 17, 'both', true),
    ],
    wheelSpecs: [
        new WheelSpec(17, 7.5, '5x112', 57.1, 35, 50, 5, 112),
    ]
));

// Use in tests
$vehicle = $mock->lookupPlate('AB-123-CD');

Gherkin Scenarios

Feature: DriveRight Client
  As a developer
  I want to lookup vehicles by license plate
  Using the DriveRight API

  Scenario: Lookup Dutch license plate
    Given valid DriveRight credentials
    When I lookup plate "AB-123-CD"
    Then I should receive vehicle details
    And vehicle should have make and model

  Scenario: Get fitments for vehicle
    Given vehicle "VH123" exists
    When I request fitments
    Then I should receive tyre sizes
    And I should receive wheel specifications

  Scenario: Invalid license plate format
    When I lookup plate "INVALID"
    Then InvalidPlateException should be thrown

  Scenario: Vehicle not found
    When I lookup plate "00-000-00"
    Then VehicleNotFoundException should be thrown

  Scenario: Browse vehicle catalog
    When I request all makes
    Then I should receive list of car manufacturers
    And I can get models for each make

  Scenario: Get OE tyre sizes
    Given vehicle has multiple tyre options
    When I request fitments
    Then I can filter original equipment sizes

Configuration Reference

Parameter Type Required Default Description
apiKey string Yes - DriveRight API key
baseUrl string No production API base URL
timeout int No 30 Request timeout in seconds
retryOnRateLimit bool No true Auto-retry on 429
maxRetries int No 3 Max retry attempts

API Endpoints Used

Endpoint Method Purpose
/kenteken/{plate} GET License plate lookup
/voertuig/{id}/passingen GET Get vehicle fitments
/merken GET List all makes
/merken/{make}/modellen GET List models for make
/modellen/{model}/generaties GET List generations
/generaties/{gen}/uitvoeringen GET List variants