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

Knp Piwik Client Bundle Laravel Package

knplabs/knp-piwik-client-bundle

Symfony bundle that integrates the KNP Piwik client, letting you configure Piwik/Matomo analytics through bundle config and inject a ready-to-use tracker service. Adds Twig helpers and straightforward page-view and goal tracking to Symfony apps with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Read the "no longer maintained" banner before anything else. This bundle is archived, explicitly unmaintained by KnpLabs, targets Symfony 2.x, and has 0 dependents. For a Laravel developer the honest starting position is: it is not a package you install into a Laravel app, and on a legacy Symfony 2.x app it is a liability you inherit, not a tool you adopt. Everything below is written for the realistic cases — maintaining a legacy app that already uses it, or reading it as a reference before writing your own thin Matomo client.

Minimal wiring on a legacy Symfony 2.x app:

composer require knplabs/knp-piwik-client-bundle
// app/AppKernel.php
$bundles = [ /* ... */ new Knp\Bundle\PiwikBundle\KnpPiwikBundle() ];
# app/config/config.yml
knp_piwik:
    host: "%piwik_host%"        # e.g. https://analytics.example.com
    site_id: "%piwik_site_id%"
    token_auth: "%piwik_token%" # only needed for write/admin calls
    disabled: false             # true in dev/test

Where to look first, in order:

  1. DependencyInjection/Configuration.php — the authoritative list of config keys and defaults. The README lags behind it; trust the config tree.
  2. Resources/config/services.xml — the service IDs you inject (knp_piwik.tracker and friends).
  3. The tracker class itself — it is a very thin wrapper over the upstream PHP tracker. Almost every method you will call (doTrackPageView, doTrackEvent, doTrackGoal, setUserId, setCustomVariable) belongs to the wrapped class, not the bundle.
  4. Twig/ — the extension that renders the client-side _paq JavaScript snippet.

First use case: server-side tracking the browser cannot see — a newsletter signup, an API endpoint hit, a webhook-driven conversion, a cron import. Inject the tracker into a controller and fire a goal or event:

public function signupAction(Request $request, Tracker $tracker)
{
    // ... persist ... 
    $tracker->doTrackEvent('Newsletter', 'subscribe', $request->get('source', 'unknown'));
    return $this->redirectToRoute('home');
}

If you are starting fresh on Laravel, skip this entirely: use Http::post("{$host}/matomo.php", [...]) behind a small service class, or a maintained Matomo tracker. That is roughly the same amount of code as wiring this bundle in, minus the Symfony baggage.

Implementation Patterns

Inject the service, never the container. Alias knp_piwik.tracker to a small interface of your own so call sites are insulated. This is the highest-value refactor you can do for a package you already know is dead — when you migrate to a direct HTTP call, you change one binding.

Make it environment-aware and default-off. Set disabled: true in config_dev.yml and config_test.yml. In tests, bind your interface to a fake — never let the suite open outbound HTTP connections to an analytics host. Because each tracker call is a synchronous request, a "real" tracker in tests slows suites and makes them flaky.

Treat tracking as a fire-and-forget side channel. The tracker issues one HTTP request per call, synchronously. That means:

  • For anything on the request hot path, defer to a kernel.terminate listener so the visitor gets their response before the tracking call runs.
  • Lower setRequestTimeout() before batch loops so a slow or unreachable Matomo instance can't stall a cron job.
  • Wrap calls so a failed analytics call never takes down a business transaction. A newsletter signup should succeed even when analytics is down.
public function onTerminate(TerminateEvent $event)
{
    $this->tracker->doTrackPageView($event->getRequest()->getPathInfo());
}

Use setCustomVariable() / setCustomDimension() as the extension point that outlives the bundle. These map cleanly to Matomo's current API. Store site-specific values (plan tier, tenant, A/B bucket) as custom variables now so that when you replace the wrapper, the payload shape — not the call sites — is what changes.

$tracker->setCustomVariable(1, 'plan', $user->plan, 'visit');
$tracker->doTrackEvent('Billing', 'invoice_paid', $invoice->currency);

Pair server-side goals with the Twig snippet deliberately. The Twig extension emits _paq for page views and JS events. Use the PHP tracker only for the server-authoritative events. If you call setUserId() server-side, make sure the JS snippet calls setUserId() with the same value, or Matomo splits the visit into two identities and attribution breaks.

Centralize payload construction in one place even if you keep the bundle. Wrap the tracker behind one service that owns event/category/action naming. Analytics taxonomies drift within weeks when each controller invents its own strings, and that inconsistency is what makes migration painful — you'll have to reconcile names anyway.

Batch deliberately. If you need to track an import of thousands of rows, either use the bulk/queued API the bundle exposes or accept that you are doing thousands of synchronous requests and schedule accordingly. There is no built-in retry or offline queue — if Matomo is down, those events are gone.

Gotchas and Tips

  • This is not a Laravel package. No ServiceProvider, no config/knp_piwik.php, no facade. Symfony 2.x DI, YAML config, bundle registration. In a Laravel app you would be installing Symfony DI alongside Laravel — don't. Use Http client and a tiny service class.
  • Composer will fight you. Symfony2-era constraints conflict with modern Laravel/PHP/Symfony components. If you find yourself reaching for --ignore-platform-reqs, stop — that flag is a red flag, not a workaround. You are now pinned to an abandoned package along your entire upgrade path.
  • Piwik → Matomo renamed in 2018. The client's URL construction and parameters were written against Piwik. Verify matomo.php/index.php paths and parameter names against the Matomo version you actually run. "It installed" is the beginning of validation, not the end.
  • token_auth leaks easily. Don't commit it, don't put it in a config.yml default, and check logs and error reporters — a tracker exception can dump the whole request including the token. Scope tokens to the minimum privilege Matomo offers, and rotate them if they've ever been in a stack trace.
  • disabled is not the same as "safe." Confirm that setting disabled: true actually short-circuits before any HTTP call in the version of the wrapped client you have. Verify it once by running a dev request against a host you can observe.
  • The tracker throws on failure in some configurations, and not in others. Do not rely on exception behavior as a control flow. Wrap calls in a guarded helper that swallows transport errors and logs at warning with the event name, not the token.
  • Twig snippet and PHP tracker don't share state. IDs set in PHP aren't visible to JS and vice versa. setUserId mismatch is the classic source of "why are half my conversions unattributed." Keep a single source of truth for the user ID and set it on both sides.
  • Custom variable scope matters. 'visit' vs 'page' scope changes how Matomo counts things — 'visit' is set once per session, 'page' per hit. The wrong scope silently corrupts segmentation reports, and the bundle gives you no guard rail.
  • Timeouts and connection reuse are on you. The tracker is a thin wrapper; if you need retries, backoff, or connection reuse, the bundle won't provide them. In a direct Laravel Http replacement you get ->timeout(), ->retry(), and pooling for free.
  • Bus factor is one. 17 stars, 0 dependents, archived, maintainer wanted. Before you touch production behavior, write down what the app depends on, capture a couple of real request payloads from the Matomo server side, and add a test around your wrapper. That recorded payload is your contract for the migration off this bundle.
  • The migration-ready exit is a thin wrapper, not a fork. Keep the interface, the payload shape, and a test against a recorded Matomo response. When you swap in a maintained client or a raw Http call, only the wrapper changes. Don't fork the bundle — you'd be inheriting an entire Symfony2 bundle to keep a few lines of URL construction alive.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings