webfleet-connect / php-sdk
A typed community PHP SDK for the WEBFLEET.connect API
Requires
- php: ^8.3
- ext-json: *
- guzzlehttp/guzzle: ^7.9
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.1 || ^2.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.75
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5 || ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-07 11:39:11 UTC
README
An unofficial, typed PHP 8.3+ client for the WEBFLEET.connect CSV/JSON API. The first release focuses on driver trips, logbooks, and historical GPS coordinates.
The SDK follows the current Webfleet authentication model: the username and password are sent with HTTP Basic Auth. They are never added to the request URL. Account and API key remain API query parameters as required by Webfleet.
Installation
composer require webfleet-connect/php-sdk
Create a client
use Webfleet\Connect\Credentials; use Webfleet\Connect\WebfleetClient; $client = WebfleetClient::create(new Credentials( account: 'company-account', username: 'api-user', password: 'secret', apiKey: 'api-key', ));
Credentials can also be loaded explicitly from WEBFLEET_CONNECT_ACCOUNT, WEBFLEET_CONNECT_USERNAME, WEBFLEET_CONNECT_PASSWORD, and WEBFLEET_CONNECT_APIKEY:
$client = WebfleetClient::create(Credentials::fromEnvironment());
Historical coordinates
Coordinates are preserved in Webfleet's integer microdegree format. GeoPoint::latitude() and longitude() return regular decimal degrees.
use Webfleet\Connect\Query\TracksQuery; use Webfleet\Connect\Value\DateRange; use Webfleet\Connect\Value\ObjectIdentifier; $object = ObjectIdentifier::number('VEHICLE-01'); $range = new DateRange( new DateTimeImmutable('2026-07-20T00:00:00+00:00'), new DateTimeImmutable('2026-07-21T00:00:00+00:00'), ); foreach ($client->tracks(new TracksQuery($object, $range)) as $point) { echo $point->recordedAt?->format(DATE_ATOM), ' ', $point->position?->latitude(), ', ', $point->position?->longitude(), PHP_EOL; }
Webfleet limits one showTracks request to 48 hours. For a longer range, use the lazy iterator. It divides requests into valid windows, removes overlap duplicates, and applies the documented request limit:
foreach ($client->trackHistory($object, $longRange) as $point) { // Points are fetched and yielded one window at a time. }
Logbook and driver trips
showLogbook contains the current logbook state, including driver information and trip start/end coordinates. showLogbookHistory is the audit history of manual edits; it is not GPS history.
use Webfleet\Connect\Query\LogbookHistoryQuery; use Webfleet\Connect\Query\LogbookQuery; $entries = $client->logbook(LogbookQuery::forObject($object, $range)); $changes = $client->logbookHistory(LogbookHistoryQuery::forObject($object, $range));
For a direct driver-filtered trip report:
use Webfleet\Connect\Query\TripQuery; use Webfleet\Connect\Value\DriverIdentifier; $trips = $client->trips(TripQuery::forDriver( DriverIdentifier::number('DRIVER-01'), $range, ));
Driver trip reports are limited to one request per minute. A range over one calendar month also requires an object number. The query objects validate these restrictions before a request is sent.
Lookups and raw actions
$drivers = $client->drivers(); $objects = $client->fleetObjects(); $raw = $client->request('showStandStills', [ 'objectno' => 'VEHICLE-01', ...$range->toParameters(), ]); foreach ($raw->rows as $row) { // Exact Webfleet field names and values. }
The raw escape hatch sends GET requests. It does not provide typed mutation methods or POST payload support in version 0.1.
HTTP clients and rate limiting
Guzzle is used by WebfleetClient::create(). Applications can inject any PSR-18 client and PSR-17 request factory with WebfleetClient::withHttpClient(). An optional RateLimiterInterface can replace the built-in, per-client rolling-window limiter.
All requested and returned timestamps use ISO-8601 and are normalized to UTC. Missing fields in Webfleet responses become null; the original row remains available through each DTO's source property.
TachoShare.connect
TachoShare.connect is a separate API at https://tachoshareapi.webfleet.com. It uses OAuth2 Authorization Code Grant with bearer access tokens, rather than WEBFLEET.connect credentials.
Set TACHO_CLIENT_ID, TACHO_CLIENT_SECRET and TACHO_REDIRECT_URI in your application's environment. Register the redirect URI with Webfleet (for example, https://example.com/callback/webfleet/tacho). Request offline_access to obtain a non-expiring offline refresh token:
use Webfleet\Connect\Tacho\OAuth\OAuthClient; $oauth = OAuthClient::fromEnvironment(); $state = bin2hex(random_bytes(32)); // Store in the user's session. $authorizationUrl = $oauth->authorizationUrl($state); // Redirect the user to $authorizationUrl to log in.
At the callback, verify that the returned state matches the session value before exchanging the short-lived authorization code:
$token = $oauth->exchangeCode($code); // Persist $token->refreshToken securely before making API calls.
Build a client with that token, or restore a saved refresh token on later runs. The callback must persist each rotated refresh token:
use Webfleet\Connect\Tacho\OAuth\AccessToken; use Webfleet\Connect\Tacho\TachoShareClient; $token = AccessToken::fromRefreshToken($savedRefreshToken); $tacho = TachoShareClient::create( $oauth, $token, onTokenRefreshed: static function (AccessToken $token): void { // Persist $token->refreshToken securely, replacing the previous value. }, );
Access tokens refresh automatically before expiry and once after an HTTP 401. OAuth and API requests share the same HTTP client. For PSR injection, use TachoShareClient::withHttpClient($oauth, $token, $httpClient, $factory, $onTokenRefreshed); $factory must implement both PSR-17 request and stream factories. $oauth->withHttpClient($httpClient, $factory) returns an OAuth client using the supplied transport.
use Webfleet\Connect\Tacho\Dto\DriverFileFormat; use Webfleet\Connect\Tacho\Dto\VehicleFileFormat; $vehicles = $tacho->vehicles(); $vehicle = $tacho->vehicle($vin); $drivers = $tacho->drivers(); $companyCards = $tacho->companyCards(); $companyCardGroups = $tacho->companyCardGroups(); $from = new DateTimeImmutable('-7 days'); $to = new DateTimeImmutable(); $vehicleFile = $tacho->vehicleFile($vin, $from, $to, VehicleFileFormat::DDD); $driverZip = $tacho->driverFile($cardNumber, '0x1A', $from, $to, DriverFileFormat::DDD); if (null !== $driverZip) { // $driverZip->filename, $driverZip->contentType and $driverZip->contents } // Explicitly request a remote vehicle download (changes server state): $updatedVehicle = $tacho->requestVehicleDownload($vin); // Revoke a saved refresh token when disconnecting the integration: $oauth->revoke($savedRefreshToken);
File methods return null on HTTP 204. Files are buffered in memory. Query dates use UTC calendar days; when both dates are supplied, the end must follow the start and be within two calendar years. With one or neither date supplied, the API applies its default range (90 days). Vehicle formats are DDD, V1B_V1C and TGD; driver formats are DDD, C1B_C1C and TGD (driver downloads are ZIPs). Status fields remain strings so newly introduced statuses can be read. DTOs retain the original row in source.
Failures throw Webfleet\Connect\Exception\OAuthException or Webfleet\Connect\Exception\TachoShareApiException. The latter exposes statusCode, description, url and traceId, plus retryAfter on HTTP 429. PSR transport failures and invalid response data use the existing TransportException and UnexpectedResponseException classes.
Development
composer install composer check
Tests use mocked PSR responses and never require live credentials. An optional composer test-integration command uses the four WEBFLEET credential variables plus WEBFLEET_CONNECT_TEST_OBJECTNO, and/or TACHO_CLIENT_ID, TACHO_CLIENT_SECRET, TACHO_REDIRECT_URI and TACHO_REFRESH_TOKEN. The integration bootstrap loads .env without overriding real environment variables. Integration tests are excluded from the normal test suite; TachoShare integration tests make read-only calls and never request a remote download. See .env.example for the variable names.
License
MIT. This community project is not an official Webfleet product.