ikimea/browser
Ikimea Browser provides reliable user-agent detection for PHP and Laravel apps, identifying browser name and version, operating system, platform, device type, and bots. Its simple API helps tailor content, handle mobile visitors, and enrich analytics.
Install with Composer — there is no Laravel service provider baked in:
composer require ikimea/browser
The package exposes a single class, Ikimea\Browser\Browser. Construct it once and ask it about the current request:
use Ikimea\Browser\Browser;
$browser = new Browser();
$browser->getBrowser(); // Browser::BROWSER_CHROME
$browser->getVersion(); // '120.0.0.0'
$browser->getPlatform(); // Browser::PLATFORM_WINDOWS
$browser->isMobile(); // bool
$browser->isRobot(); // bool
$browser->getUserAgent(); // raw UA string
$browser->getAcceptLanguages();// ['en-US', 'en', 'fr']
Where to look first:
src/Browser.php — the detection logic and every constant you will branch on. Read the constant block (BROWSER_*, PLATFORM_*) before writing conditionals.src/AcceptLanguage.php — the Accept-Language parser used by getAcceptLanguages().First real use case: feature-gating a legacy asset bundle or showing an "upgrade your browser" banner. Because the constructor accepts overrides, you can verify behavior without a real browser:
$browser = new Browser('Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko');
if ($browser->getBrowser() === Browser::BROWSER_IE && version_compare($browser->getVersion(), '11', '<=')) {
// serve legacy CSS / show banner
}
Bind it as a singleton in a service provider. The UA does not change during a request, so parse it once:
$this->app->singleton(Browser::class, fn () => new Browser(
request()->userAgent(),
request()->header('Accept-Language')
));
Then inject Browser into controllers, middleware, or jobs. Using the request headers explicitly (rather than relying on $_SERVER) keeps it testable and consistent behind proxies.
Middleware for coarse routing. A mobile redirect or a "block ancient IE" middleware is a natural fit:
public function handle($request, Closure $next)
{
$browser = app(Browser::class);
if ($browser->isRobot() || $browser->getBrowser() === Browser::BROWSER_UNKNOWN) {
return $next($request); // never redirect bots or unknown UAs
}
if ($request->is('desktop/*') && $browser->isMobile()) {
return redirect()->to(str_replace('desktop', 'm', $request->fullUrl()));
}
return $next($request);
}
Blade / view composer for conditional assets. Share a small view-model instead of calling the class in templates:
View::composer('layouts.app', function ($view) {
$b = app(Browser::class);
$view->with('legacyBrowser', $b->getBrowser() === Browser::BROWSER_IE
&& version_compare($b->getVersion(), '11', '<='));
});
Localization fallback. getAcceptLanguages() returns languages in preference order, which is ideal for choosing a default locale on first visit:
$supported = ['en', 'fr', 'de'];
$locale = collect($browser->getAcceptLanguages())
->map(fn ($l) => substr($l, 0, 2))
->first(fn ($l) => in_array($l, $supported)) ?? config('app.locale');
Analytics and logging. Store getBrowser(), getVersion(), getPlatform(), and isMobile() as columns/attributes on request logs. Keep the raw UA too — it is the only way to re-parse historical data when detection rules change.
Feature flags. Wrap version checks in a dedicated policy class (BrowserPolicy::supportsWebp()) rather than sprinkling constants through controllers; the underlying library is stable but the policy will need to evolve.
$browser->getVersion() > 10; use version_compare($browser->getVersion(), '10', '>='). Chrome's UA reduction often reports 120.0.0.0, and some UAs only expose a major version.@supports, Modernizr, or navigator.userAgentData client-side for real capability gating.Chrome/ in their UA. If a specific browser matters, verify against getUserAgent() and add extra checks rather than trusting a single constant.BROWSER_UNKNOWN is your "do not trust this" bucket. Empty UA, curl, and spoofed strings land here. Do not treat unknown as an error; branch safely.isRobot() first, otherwise you will redirect crawlers to a mobile site and hurt SEO.getBrowser().Accept-Language strings in unit tests instead of mutating $_SERVER; this also avoids global-state bleed between tests.getAcceptLanguages() reads Accept-Language and ignores cookies/session. If a user has already chosen a locale, respect that before consulting this.matomo/device-detector or whichbrowser/parser, which maintain larger and regularly updated regex datasets; ikimea/browser is best when you want a lightweight, dependency-free check for coarse branching.How can I help you explore Laravel packages today?