Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

User Agent Parser Bundle Laravel Package

shadesoft/user-agent-parser-bundle

A PHP bundle that parses user-agent strings to identify browser, operating system, device type, and bot traffic, exposing the results through a simple service you can inject wherever request analytics, logging, or content adaptation is needed.

View on GitHub
Deep Wiki
Context7

Getting Started

Before writing a single line of integration code, note the headline: this package is archived and abandoned, carries 0 stars and 0 dependents, and is a Symfony bundle, not a Laravel package. It exists as a thin DI wrapper around the underlying ShadeSoft/UserAgentParser library, whose only job is to turn a User-Agent header into a browser name/version and an OS name/version. Treat the bundle as documentation of how to wire up the library, not as something you install into a Laravel app.

Where to look first, in order:

  1. composer.json of the repo — check the require block for PHP and symfony/* version constraints. Packages of this vintage typically pin PHP ^5.5/^7.0 and Symfony 2/3, which will refuse to install on a modern Laravel app (PHP 8.1+, Symfony 6/7 components via illuminate/*). If Composer blocks it, stop here.
  2. The bundle's DependencyInjection/ and Resources/config/services.yml — this is where the actual service ID for the parser is defined. That service ID is the only thing of value you can lift.
  3. The underlying shadesoft/user-agent-parser library — it holds the real API (parse(), getBrowser(), getOS() style methods) and, critically, the stale UA regex/database that determines output quality.

First use case: request-level enrichment for analytics — capture browser and os alongside every page view, login event, or support ticket so you can answer "which browsers do our users actually run?" without a third-party analytics vendor.

Minimal viable path in Laravel (bypassing the bundle entirely, which is the only sane approach given its state):

composer require shadesoft/user-agent-parser  # the library, not the bundle
// app/Providers/UserAgentServiceProvider.php
public function register(): void
{
    $this->app->singleton(UserAgentParser::class, fn () => new UserAgentParser());
}
$ua = app(UserAgentParser::class);
$parsed = $ua->parse(request()->userAgent() ?? '');

// $parsed['browser'], $parsed['os'] — names from a stale lookup table

If the library itself won't install either, that is your answer: do not use this package, and jump to the "Gotchas" migration note.

Implementation Patterns

1. Container binding + injectable service. Never call the parser statically or instantiate it inline in controllers. Bind the parser as a singleton (construction is not free — it loads regex tables) and inject it:

class TrackPageView
{
    public function __construct(private UserAgentParser $ua) {}

    public function handle(Request $request, Closure $next)
    {
        $parsed = $this->ua->parse($request->userAgent() ?? '');

        $request->attributes->set('ua', [
            'browser' => $parsed['browser'] ?? 'unknown',
            'os'      => $parsed['os'] ?? 'unknown',
        ]);

        return $next($request);
    }
}

2. Cache by UA hash — this is the single most important pattern. A given browser sends an identical UA string on every request; re-parsing per request burns CPU on regex matching. Hash and memoize:

$result = Cache::remember(
    'ua:'.md5($request->userAgent() ?? ''),
    now()->addDay(),
    fn () => $this->ua->parse($request->userAgent() ?? '')
);

3. Persist normalized columns, not raw blobs. Model it as browser_name, browser_version, os_name, os_version rather than a JSON column — you will want GROUP BY browser_name in analytics queries and JSON grouping is painful across MySQL/Postgres/SQLite.

4. Blade/display helper. For support tooling, a tiny presenter that turns the parsed array into a badge is enough:

// "Chrome 120 on Windows 10"
{{ $ua['browser'] }} on {{ $ua['os'] }}

5. Fallback-everywhere wrapper. The parser returns sparse arrays on unrecognized input and the library is unmaintained, so wrap it once and never call it raw:

public function summarize(?string $header): array
{
    try {
        $r = $this->parser->parse($header ?? '');
    } catch (\Throwable) {
        $r = [];
    }

    return [
        'browser' => $r['browser'] ?? 'unknown',
        'os'      => $r['os'] ?? 'unknown',
    ];
}

6. Test with a fixtures table. UA parsing is pure-function-shaped, so make it table-driven and assert against a known-good set:

yield 'chrome-on-windows' => [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36',
    ['browser' => 'Chrome', 'os' => 'Windows'],
];

7. If you genuinely must run the Symfony bundle, it can only live inside a Symfony kernel — you'd register it in config/bundles.php (Symfony 4+) or AppKernel::registerBundles(), not in Laravel's bootstrap/app.php. There is no Laravel auto-discovery, no facade, no config publish. Bridging it means manually instantiating the bundle's DI extension, at which point installing the underlying library directly is strictly less work.

Gotchas and Tips

  • It is abandoned. This is the dominant gotcha. No security patches, no PHP 8.2/8.3 compatibility fixes, no response to CVEs in transitive Symfony components. Any audit will flag it.

  • UA databases go stale in months, not years. New Chrome/Edge/Safari major versions and new OS releases (Windows 11, iOS 17/18, Android 15) will fall through to "unknown" or be misattributed. An abandoned parser is guaranteed to be wrong about current traffic within a release cycle.

  • Symfony bundle ≠ Laravel package. No service provider, no config/ publish, no facade. Every Laravel integration requires hand-written glue; budget for that and question whether it's worth it.

  • UA strings are attacker-controlled and spoofable. Never use the output for access control, feature gating, bot blocking, or security decisions. It is analytics/display metadata only. A one-line curl -A spoofs it trivially.

  • Bot misclassification. This era of parser tends to lump crawlers and headless browsers into desktop-browser buckets. If bot traffic matters, cross-check with a separate signal (robots.txt fetch, reverse DNS, X-Requested-With, rate behavior) rather than the parser.

  • Performance. Regex-heavy parsing on every request is measurable. Always pair with the cache-by-UA-hash pattern; consider parsing lazily (only when the analytics event actually fires) rather than in global middleware.

  • Config quirks. If you do read the bundle, its config namespace is defined by the Configuration class in DependencyInjection/ — expect a bundle-specific root key (something like shade_soft_user_agent_parser) with at most a service-ID override. There is nothing meaningful to tune, which also means no extension points worth preserving; the only override is swapping the parser service for a different implementation.

  • Missing User-Agent header. API clients, health checks, and some CLI tools omit it entirely — the parser will throw or return empty. Default to unknown before it ever reaches the parser.

  • Migration path (recommended). Replace this package outright:

    • jenssegers/agent — lightweight, Laravel-native, wraps mobiledetect; good for mobile/desktop/bot flags.
    • matomo/device-detector — actively maintained, large maintained device database; the right choice for real analytics.
    • browscap/browscap-php — heavier, but the most accurate for browser/OS version detail, with regularly updated data.

    All three are drop-in replacements for the summarize() wrapper above, so write that wrapper once and you can swap the backend without touching call sites.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core