jiabin/julius-referer-bundle
A Symfony bundle that integrates Julius referer parsing to detect and classify incoming traffic sources from the HTTP Referer header. Use it to identify search engines, social networks, and direct visits for analytics, attribution, and personalized content.
This bundle tracks the referer a visitor arrived with — the Referer header plus landing URL — so you can attribute a later signup or order back to its source. It targets Symfony 2.x (last release June 2014), so treat it as a reference implementation rather than something you composer require into a modern app.
kernel.request listener, and one or two services. Open those four files first — they define the entire public surface. The README is likely thin and predates Symfony Flex; the service definition (XML or YAML under Resources/config/) is the real documentation.composer require jiabin/julius-referer-bundle, then register new Jiabin\JuliusRefererBundle\JuliusRefererBundle() in AppKernel. On Symfony 4+ the bundle class is unnecessary but the listener code will still fail on removed APIs (getRequest(), ContainerAware, isMasterRequest()), so plan to port it./pricing from https://news.ycombinator.com/. The listener should capture the referer host, full URL, and timestamp on the first request of the session, then leave it alone. Verify with curl -H "Referer: https://example.com" http://localhost/pricing and dump the session.User row.If you are on Symfony 4+, skip the package: the core behaviour is ~20 lines in a listener plus one session write. The value here is the shape of the idea, not the dependency.
Capture once, on the master request. The listener must guard against subrequests and fragments, otherwise an ESI/embed request can overwrite the real referer. Check $event->isMasterRequest() (Symfony ≤5.2) or $event->isMainRequest() (5.3+), and short-circuit if the session already holds a value.
Separate "referer" from "campaign". The raw Referer header tells you the site; the query string of your own landing URL (?utm_source=…&gclid=…) tells you the campaign. Parse both at capture time and store them as discrete fields — merging them into one string makes attribution queries impossible later.
Store server-side, not in a cookie. Session storage is fine for the anonymous visit, but as soon as the visitor registers, copy the record onto a Doctrine entity (RefererRecord or fields on User). Session data dies with the session; attribution needs to survive to the order and the finance report.
// conversion-time attribution
$referer = $this->get('julius_referer.tracker')->getReferer(); // confirm service id
$user->setSignupSource($referer['host'] ?? 'direct');
$user->setSignupCampaign($referer['utm_campaign'] ?? null);
Wrap it behind your own interface. Define App\Referer\RefererProviderInterface with getReferer(): ?Referer and make the bundle's service one implementation. When you replace the bundle — which you will — only the DI alias changes.
Normalise before you store. Reduce the referer to scheme://host for classification, keep the path for the long tail, and run known search/social hosts through a map so "google.com", "google.co.uk", and "google.de" all bucket to organic_search. Do this at capture time; doing it at report time means reprocessing years of rows.
Test with a functional test, not a unit test. The listener's contract is "given this request, this session ends up like so." Use KernelBrowser/WebTestCase with a HTTP_REFERER server param and assert on the session contents — far more valuable than mocking the request stack.
Referer header is advisory. It is absent for direct traffic, HTTPS→HTTP navigation, Referrer-Policy: no-referrer, rel="noreferrer" links, and many email clients. Treat "no referer" as unknown, never as direct — you'll systematically over-report direct traffic.getReferer() returning null is a normal, expected state your UI must handle.kernel.request? (2) did the session start before it ran? (3) is it firing on subrequests? Set a temporary error_log in the listener — with a bundle this old, there is no profiler integration to lean on.kernel.request listener at a lower priority to post-process what it captured. Prefer decoration so upstream changes (if you ever pull a fork) don't collide.How can I help you explore Laravel packages today?