matomo/device-detector
Parses User-Agent strings and browser Client Hints to identify device type (desktop/tablet/mobile/TV/console), client apps (browsers, media players, feed readers), operating systems, and device brand/model. Composer-ready PHP library.
Installation:
composer require matomo/device-detector
Add to composer.json under require or require-dev if only for testing.
First Use Case: Parse the current request's user agent in a Laravel controller or middleware:
use DeviceDetector\DeviceDetector;
use DeviceDetector\ClientHints;
$userAgent = request()->userAgent();
$clientHints = ClientHints::factory($_SERVER);
$dd = new DeviceDetector($userAgent, $clientHints);
$dd->parse();
// Basic checks
if ($dd->isMobile()) {
return response()->json(['device' => 'mobile']);
}
Where to Look First:
DeviceDetector\Cache\LaravelCache for caching.Request Parsing in Middleware: Create a middleware to parse and store device data in the request object:
namespace App\Http\Middleware;
use Closure;
use DeviceDetector\DeviceDetector;
use DeviceDetector\ClientHints;
class DetectDevice
{
public function handle($request, Closure $next)
{
$userAgent = $request->userAgent();
$clientHints = ClientHints::factory($_SERVER);
$dd = new DeviceDetector($userAgent, $clientHints);
$dd->parse();
$request->merge([
'device' => $dd->getDeviceName(),
'is_mobile' => $dd->isMobile(),
'is_tablet' => $dd->isTablet(),
'client' => $dd->getClient(),
'os' => $dd->getOs(),
]);
return $next($request);
}
}
Register in app/Http/Kernel.php:
protected $middleware = [
\App\Http\Middleware\DetectDevice::class,
];
Caching Strategies:
$dd->setCache(new \DeviceDetector\Cache\LaravelCache());
$cache = Cache::store('redis');
$dd->setCache(new \DeviceDetector\Cache\PSR6Bridge($cache));
Conditional Logic: Use device detection to route users or modify responses:
if (request()->is_mobile) {
return view('mobile.home');
}
return view('desktop.home');
Bot Detection: Skip bot-specific logic early:
if ($dd->isBot()) {
return response()->json(['status' => 'bot'], 403);
}
Client Hints Integration:
Enable for modern browsers (requires server Accept-CH header):
$clientHints = ClientHints::factory($_SERVER, [
'Accept-CH' => 'Sec-CH-UA, Sec-CH-UA-Mobile',
]);
DeviceDetector instantiation in a service provider for dependency injection.public function scopeMobile($query)
{
return $query->whereHas('user', function ($q) {
$q->where('device_type', 'mobile');
});
}
\Matomo\Tracker::getInstance('https://your.matomo.url')
->setUserAgent(request()->userAgent())
->setDevice(request()->device)
->doTrackPageView();
User Agent Spoofing:
Sec-CH-UA headers) when possible.if ($dd->getClient('name') === 'Chrome' && !$dd->isMobile()) {
// Likely a desktop Chrome, but could be spoofed.
}
Performance Overhead:
$dd->setCache(new \DeviceDetector\Cache\LaravelCache());
Client Hints Limitations:
Accept-CH. Fall back to user agent parsing:
if (!$clientHints->isSupported()) {
$dd = new DeviceDetector($userAgent);
}
Version Truncation:
12.3.4 → 12.3). Disable if full versions are needed:
use DeviceDetector\Parser\Device\AbstractDeviceParser;
AbstractDeviceParser::setVersionTruncation(AbstractDeviceParser::VERSION_TRUNCATION_NONE);
Bot Detection Edge Cases:
discardBotInformation() for performance if bots are irrelevant:
$dd->discardBotInformation();
\Log::debug('User Agent:', [$userAgent]);
$dd = new DeviceDetector('Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) ...');
Custom YAML Parser:
Override the default Spyc parser for Symfony/YAML:
$dd->setYamlParser(new \DeviceDetector\Yaml\Symfony());
Custom Cache:
Implement DeviceDetector\Cache\CacheInterface for bespoke storage:
class MyCache implements CacheInterface {
public function get($key) { ... }
public function set($key, $value, $ttl = null) { ... }
}
$dd->setCache(new MyCache());
Update Rules:
Manually update detection rules (located in vendor/matomo/device-detector/DeviceDetector/Parser/) if new devices/browsers are missing. Contribute upstream!
Laravel Service Container:
Bind DeviceDetector to the container for easy access:
$this->app->singleton(DeviceDetector::class, function ($app) {
$userAgent = request()->userAgent();
$clientHints = ClientHints::factory($_SERVER);
$dd = new DeviceDetector($userAgent, $clientHints);
$dd->setCache(new \DeviceDetector\Cache\LaravelCache());
return $dd;
});
Then inject via constructor:
public function __construct(private DeviceDetector $dd) {}
request() helpers for cleaner code:
$isMobile = request()->device->isMobile();
if (app()->environment('production')) {
$dd->skipBotDetection();
}
if ($dd->getDeviceName() === 'Unknown') {
\Log::warning('Unknown device detected:', [$userAgent]);
}
How can I help you explore Laravel packages today?