shetabit/visitor
Laravel package to extract and access visitor information like browser, IP, platform, and device. It also lets you recognize online users, check if a user is online, and log visits for your models using the Visitable trait.
Architecture fit. The package is a conventional Laravel satellite library: a service provider with auto-discovery (5.5+), a Visitor facade with a visitor() helper, two Eloquent traits (Visitable on target models, Visitor on User), and an optional LogVisits middleware that hooks route model binding. It sits cleanly inside a Laravel monolith or modular monolith and adds no new infrastructure — no queues, no cache cluster, no external SaaS, no client-side JS. For a TPM this is the right shape: small surface area, framework-idiomatic, easy to reason about in a code review. It is a poor fit for decoupled/API-only architectures where the "visitor" is a mobile client or an edge-cached request, and it is a poor fit if the actual requirement is product analytics (funnels, retention, session replay) rather than raw visitor metadata plus visit counters.
Integration feasibility. Low effort, high reversibility. Install, publish, migrate, add traits, optionally register middleware. The happy path is roughly 1–2 engineer-days including a test pass; the expensive parts are not installation but the policy decisions it forces (what to store, retention, consent, where IP truth comes from behind a CDN). Two integration details need explicit verification before commitment: the composer.json constraints against our exact Laravel/PHP pins, and whether LogVisits middleware ordering interacts with our auth/session middleware stack.
Technical risk. Material, but bounded and mostly operational rather than architectural:
| Risk | Severity | Notes |
|---|---|---|
| Ecosystem signal | High | 568 stars but 0 dependents and a composite score of ~32.6 (opportunity ~29.8). No downstream production validation we can point to. |
| Bus factor | High | Single-vendor package; release history is largely community compat PRs (Laravel 11/12/13, PHP 8.4 nullable types, mobiledetectlib 4.10). Framework upgrades are absorbed reactively, not proactively. |
| Data protection | High | The package's purpose is persisting IP, user agent, and full request inputs. Full request capture is a PII/secret-leak vector; earlier releases had to add route exclusions and password handling. Defaults are not a compliance posture. |
| Write amplification | Medium | Middleware logs on every request hitting a Visitable-bound model — synchronous INSERT per request. No queue, no batching, no aggregation table. |
| Query cost | Medium | "Unique visitors" is distinct('ip')->count('ip') on the raw log table; this degrades linearly and cannot be served from an index-friendly path at volume. |
| IP correctness | Medium | All metadata derives from the resolved client IP; behind a load balancer/CDN this is only as correct as our trusted-proxy configuration. |
| Release metadata quality | Low | Last release is dated 2026-08-12 and the changelog template is empty ("Nothing" under every heading) — treat version/date signals as unreliable and verify against the repo tags. |
Key questions.
Visitable on day one, and what is the expected request volume through those routes?Stack fit. Laravel 8–13 range is claimed via release notes, PHP 8.x with 8.4-specific fixes landed. Requires mobiledetectlib for device/browser classification; GeoIP enrichment arrived as an optional 4.5.0 feature (note that 4.5.3 fixed empty GeoIP stubs and a missing migration publish — pin ≥ 4.5.3). Database: anything Eloquent supports; MySQL/Postgres are the realistic targets. Verify our exact Laravel minor is inside the tested matrix rather than inferred from changelog prose.
Migration path. There is no backfill story — historical visits do not exist, so counters start at zero. If we currently derive view counts some other way, run both briefly and reconcile rather than switching blind. If we already run an analytics vendor, position this package as an application-side feature store (per-model counts, online presence) and keep product analytics where it is; do not let it become a shadow analytics stack with no schema discipline. Rollback is clean if we treat the published migration as ours: remove middleware, remove traits, drop or archive the log table.
Compatibility and sequencing. Gate on a matrix check (Laravel minor × PHP minor × DB driver) before any code lands, then sequence:
browser, device, ip, isOnline) under our real proxy/CDN setup, and confirm the composer constraints.request-payload capture either disabled or allowlisted; sensitive routes excluded via wildcard exception paths; IP handling (raw vs hashed) chosen as a written decision, not a default.Visitable model, middleware enabled, a dashboard or test asserting row shape and count correctness, plus a query-plan review of the count path.Keep the integration behind our own interface/abstraction (a small internal VisitTracker) rather than calling visitor() throughout the codebase; that is what makes the eventual fork-or-replace decision cheap.
Maintenance. Low monthly effort, unpredictable annual effort. The realistic maintenance is framework-upgrade chasing (11 → 12 → 13 all required external PRs) plus dependency drift on mobiledetectlib. Budget a standing line item each major Laravel upgrade and pin the package version explicitly; do not float to latest in production. Owning a fork should be an accepted contingency, which means our integration should not depend on private internals.
Support. No vendor SLA, no commercial backing, MIT with community triage. Internal support burden is ours: we need a named owner for the log table, its schema, its retention job, and its GDPR/CCPA deletion path. Expect to answer "why is this counter off?" questions independently of the upstream project.
Scaling. The dominant cost is one synchronous INSERT per qualifying request plus growing read cost on distinct-IP counting. Mitigations, in order: restrict middleware to the few routes that need it rather than globally; filter bots and asset/health-check traffic; index (visitable_type, visitable_id, created_at) and ip; pre-aggregate counts into a rollup table or cache instead of distinct()->count() at read time; add a retention window with scheduled purge; partition or archive once row count crosses tens of millions. Online-user detection is a hot read path if surfaced on every page — cache it (short TTL) rather than recomputing per render. Above roughly 10M daily visitors this design is the wrong tool and should be replaced by an event pipeline.
Failure modes. A failed log write surfaces as a request-path error unless middleware is wrapped; decide explicitly whether tracking failure is fail-open (never break the request) — it should be. GeoIP lookups add a failure surface and latency if configured; keep them optional and degrade to null. Bot traffic inflates counts if unfiltered. Misconfigured trusted proxies silently poison every downstream field, and it looks like a data problem rather than a config problem. Online-presence indicators depend on whatever recency window the package uses — expect staleness and set user expectations in the UI copy. PII over-retention is the slowest-burning failure and the one most likely to reach legal.
Ramp-up. Roughly half a day for a Laravel-fluent engineer to be productive: the API is small (device, platform, browser, languages, ip, request, useragent, isOnline, visit, createVisitLog, onlineVisitors). The learning curve is not the package — it is the proxy/IP semantics, the retention policy, and our abstraction layer. Document those three alongside the install steps, or the next team will re-derive them from scratch.
How can I help you explore Laravel packages today?