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

Phpbrowscap Laravel Package

garetjax/phpbrowscap

PHP Browscap provides fast, accurate browser and user-agent detection for PHP applications. It parses browscap data to identify browser, platform, device, and crawler details, with caching support for production performance.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and isolate the data directory first. The package is a standalone PHP library, so a Laravel install is two steps and one filesystem decision:

composer require garetjax/phpbrowscap
mkdir -p storage/app/browscap && chmod -R ug+rw storage/app/browscap

Keep browscap.ini, the downloaded archive, and the parsed cache file all under storage/app/browscap — never inside vendor/, which gets wiped on every Composer run.

Read src/phpbrowscap/Browscap.php before writing any glue. It's one class; skim the UPDATE_* constants and the properties you'll actually toggle: updateMethod, remoteIniUrl, localFile, doAutoUpdate, updateInterval, silent, cacheFilename. Then look at getBrowser($userAgent = null) — note it silently falls back to $_SERVER['HTTP_USER_AGENT'] when the argument is null, and that it returns a stdClass whose keys mirror the ini (browser_name, browser, version, platform, device_type, isMobileDevice, crawler, renderingengine_*, etc.) with majorver/minorver as integers.

Warm the cache once manually in Tinker or a throwaway command, because the first call is the expensive one:

$bc = new \phpbrowscap\Browscap(storage_path('app/browscap'));
$bc->updateMethod = \phpbrowscap\Browscap::UPDATE_LOCAL;
$bc->localFile    = storage_path('app/browscap/browscap.ini');
$bc->doAutoUpdate = false;

$info = $bc->getBrowser('Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 ...)');

That initial call downloads tens of MB from browscap.org, extracts the ini, and parses it into the cache. Everything after that is a fast regex lookup against the parsed file.

Then bind it once. Don't new Browscap in controllers — it carries parsed state and is expensive to construct repeatedly:

// config/browscap.php
return [
    'cache_dir'     => storage_path('app/browscap'),
    'ini_file'      => storage_path('app/browscap/browscap.ini'),
    'auto_update'   => false,
    'update_method' => \phpbrowscap\Browscap::UPDATE_LOCAL,
];

// app/Providers/BrowscapServiceProvider.php
$this->app->singleton(\phpbrowscap\Browscap::class, function () {
    $bc = new \phpbrowscap\Browscap(config('browscap.cache_dir'));
    $bc->updateMethod = config('browscap.update_method');
    $bc->localFile    = config('browscap.ini_file');
    $bc->doAutoUpdate = config('browscap.auto_update');
    return $bc;
});

First real use case: server-side request enrichment for non-critical features — legacy-browser notices, device-class routing, analytics tags, support-diagnostic context. Inject Browscap and call getBrowser($request->userAgent()).

Implementation Patterns

Singleton + config + thin facade. Bind the class as a singleton in a service provider, keep all paths in config/browscap.php, and expose a small helper (or a BrowserDetector contract) so application code never imports the package namespace directly. That contract is your escape hatch to browscap/browscap-php later without touching consumers.

Update on a schedule, never per request. Flip doAutoUpdate off in config and drive refreshes from a scheduled command:

public function handle(Browscap $bc) {
    $bc->updateMethod = Browscap::UPDATE_CURL; // or UPDATE_FOPEN / UPDATE_FSOCKOPEN
    $bc->updateCache();
}
// Kernel::schedule()
$schedule->command('browscap:update')->weekly()->withoutOverlapping();

The download-and-parse cost stays off the request path, and withoutOverlapping() prevents partial-write races across concurrent deploys.

Memoize per user agent — the biggest practical win. The regex pass across ~100k patterns is not free:

$key  = 'bc:'.md5($ua);
$info = Cache::remember($key, now()->addDay(), fn () => $bc->getBrowser($ua));

With a warm shared cache (Redis in production) you can drop silent mode safely and let real failures surface.

Middleware that decorates the request. Parse once per request, then attach the result so downstream code (Blade, controllers, logs) reads a request attribute rather than re-parsing:

class AttachBrowserInfo {
    public function __construct(private Browscap $bc) {}
    public function handle($request, Closure $next) {
        $request->attributes->set('browser', $this->bc->getBrowser($request->userAgent()));
        return $next($request);
    }
}

Shadow mode before cutover. If you already have get_browser() calls or jenssegers/agent, run the new detector alongside on a sampled corpus of real UA strings and diff the output. Promote one consumer at a time — analytics/logging first, then bot tagging, then any user-facing variation. Roll back per consumer, not per deployment.

Health check on fallback rate. Log whenever the result is the generic "unknown browser" object. A rising fallback rate means a corrupt ini or a failed refresh, and it's the cheapest early-warning signal you get.

Bot tagging, conservatively. The crawler flag is useful for tagging traffic and excluding it from analytics, but never gate blocking behavior on it alone — UA is trivially spoofable.

Gotchas and Tips

The ini is tens of megabytes. Cold-cache parsing is CPU- and memory-heavy. Use the "lite" ini unless you genuinely need the full capability set — it's an order-of-magnitude difference in both. Ship the ini in the image, pre-warm the parsed cache via a deploy job, and make sure a cache miss never blocks a request on a full parse.

Immutable containers and serverless fight this design. Every node needs the ini present on disk; serverless cold starts compound the parse cost. Plan for a baked-in file plus a warm-up job, or accept a degraded "unknown" result on cold start.

Implicit $_SERVER fallback. getBrowser() reads HTTP_USER_AGENT when passed null — under CLI or queue workers that's absent and you'll get the unknown-browser object instead of an error. Always pass $request->userAgent() explicitly.

The cache interface predates PSR-6/PSR-16. If you want Laravel's cache store backing the package's internal lookups rather than its own files, you'll write a small bridge adapter. In practice most teams just use the package's file cache plus a Cache::remember wrapper at the call site.

silent mode hides the failures you care about. The package is effectively unmaintained and legacy-era; run a POC on your actual PHP version before committing. Old-style code, no strict types, and no active maintenance mean PHP 8 upgrades can throw notices or fatals.

Pin the version and own the refresh runbook. The scheduled update needs an owner, an integrity check on the downloaded file, an atomic swap, and a rollback slot for bad upstream releases. Without that, accuracy decays silently as user agents evolve.

Add a null-object fallback behind the contract. If the detector throws (PHP upgrade, missing ini, corrupt cache), consumers should degrade gracefully rather than 500. Implement a BrowserDetector no-op that returns a generic object and swap it in on exception.

Don't duplicate what you already have. Before adding this, check whether the stack already ships jenssegers/agent or matomo/device-detector — you may be re-introducing a solved problem with a less-maintained library. 429 stars and zero dependents means you're the one proving it works on modern Laravel.

Hard guardrail: never use UA data for security, access control, fraud decisions, or anything high-stakes. It's spoofable and stale by design; keep it to low-risk enrichment where a wrong answer is a cosmetic problem, not a correctness one.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo