Attention: this is a major release with breaking changes.
Upgrade note — the
false"not known" sentinel is gone. Tracker 3.x let you passfalseto many optional arguments to mean "value not known" (e.g.doTrackEvent($cat, $act, $name, false),addEcommerceItem($sku, $name, $cat, false),setLatitude(false)). Those arguments are now typed (?Tor numeric unions). If your calling code does not usedeclare(strict_types=1)— the usual case for a drop-in tracker — PHP's weak-mode coercion silently turnsfalseinto0/0.0/''instead of raising an error, so such calls now send a value (e_v=0,lat=0, item price0) where 3.x omitted the parameter. Replace everyfalse"not known" argument withnullor simply omit it; passingfalseno longer means "unset".
Removed
- Support for PHP versions lower than 8.1. The tracker now requires PHP 8.1 or newer.
- The
#[AllowDynamicProperties]attribute. All properties are now declared explicitly, so setting undeclared dynamic properties on a tracker instance is no longer supported (extendMatomoTrackerand declare the property instead).
Changed
declare(strict_types=1)is now enabled and every method has proper parameter and return type hints aligned with how Matomo core handles the corresponding tracking parameters. Passing a value whose type cannot be coerced now throws aTypeError(for example a non-numeric string for a numeric parameter, or any type mismatch when the calling code itself declaresstrict_types=1). Note that for ordinary (non-strict) callers PHP's weak-mode coercion still applies, so e.g.falsebecomes0/''rather than raising — see the upgrade note above about the removedfalsesentinel.- Optional "unset" parameters and their corresponding properties and getters now use
nullinstead of the previousfalsesentinel. For examplegetUserId(),getUserAgent(),getIp()andgetPageviewId()now returnnull(notfalse) when no value is set, anddoTrackEvent()/getUrlTrackEvent()default the event name and value tonull. - All public properties are now natively typed. Assigning a legacy sentinel value such as
falseto e.g.$tracker->userAgentnow throws aTypeError; theattributionInfoproperty defaults to an empty array instead offalse. Subclasses overriding methods with the old untyped signatures may need to be updated to the new signatures. setUserId()now acceptsnullto de-assign a previously set User ID, as the method documentation always promised (previously thestringtype hint made that impossible).setUrlReferrer()(and the deprecatedsetUrlReferer()) acceptnullto unset the referrer.setCustomTrackingParameter()accepts an array value again (serialized viahttp_build_query, as the JS tracker does); this restores the pre-3.4.0 behavior for multi-value parameters.setLatitude()/setLongitude()values of0.0(equator / prime meridian) are now sent to Matomo. Previously coordinates of exactly zero were silently dropped.- Goal and Ecommerce revenue amounts now distinguish "not set" from an explicit
0.doTrackGoal()/getUrlTrackGoal()(and theMatomo_/Piwik_goal helpers) take?float $revenue = null:nullomitsrevenue(so Matomo uses the goal's configured revenue) while0.0now sendsrevenue=0. Likewise the optional Ecommerce amounts ($subTotal,$tax,$shipping,$discountofdoTrackEcommerceOrder()etc.) are?float = nulland only sent when provided, and the required Ecommerce grand total is now always sent (a0.0order/cart sendsrevenue=0). Previously an explicit0/0.0was silently omitted for all of these. - The
do*tracking methods now declare astring|boolreturn type. In bulk mode they return booleantrue(previously the value was coerced to the string"1"). doTrackSiteSearch()/getUrlTrackSiteSearch()accept?int $countResultsand only send&search_countwhen a count is provided (previously&search_count=0was always sent).- Both transports now consistently throw a
RuntimeExceptionon request failure (DNS, connection or timeout errors) by default; previously only the cURL transport threw while the stream fallback silently returnedfalse. CallsetExceptionsEnabled(false)to make failed requests returnfalseinstead, so tracking never breaks the calling application (#105). - Lowered the default request timeouts from 600s/300s to 5s/2s so a slow or unreachable Matomo can no longer block the calling page for minutes (#88). Raise them again via
setRequestTimeout()/setRequestConnectTimeout()if needed. - Bumped the test suite to PHPUnit 10.5.
Fixed
- All tracking parameter names and values are now consistently URL-encoded (including
_refts,data/customData,cs/charset and thedownload/linkaction type passed togetUrlTrackAction()/doTrackAction()), and the visitor ID read from the first-party cookie is validated as a 16-character hexadecimal string. - Request-failure exceptions no longer include the full request URL (only the target host), so its query string is never surfaced in error messages/logs. The request URL and body are also marked
#[\SensitiveParameter]so they are redacted from exception stack traces. - Authenticated requests that carry
token_authin the request body are now sent asPOST; previously the stream transport sent them asGET, so Matomo ignored the token in the body. - The stream transport now returns the response body for HTTP 4xx/5xx responses (like cURL) instead of turning them into a failure.
- Bulk tracking uses a more generous request timeout (at least 30s) and no longer discards the queued actions when a batch fails to send, so the batch can be retried.
- Outgoing tracker cookies are now joined with
;(not&), and all incomingSet-Cookieresponse headers are parsed instead of only the last one;getIncomingTrackerCookie()returnsstring|false. setAttributionInfo()no longer includes the supplied payload in its exception message (the parameter is also marked#[\SensitiveParameter]).- Event and content tracking requests now send
&ca=1(custom action), so Matomo no longer falls back to recording them as page views if the handling plugin is disabled (#80). - The
cip(override IP) tracking parameter is now URL-encoded like every other value (#151). - No longer calls the deprecated
curl_close()(it was already a no-op on the supported PHP versions) (#149). - Auto-detection of the tracked page URL now uses
REQUEST_URIas the source instead ofPATH_INFO. With front-controller / path-info routing (e.g./dir1/pagehandled bydir1/index.php),PATH_INFOonly holds the trailing/page, so the tracker previously recorded a truncated URL; it now records the full requested path.PATH_INFOis no longer used at all (SCRIPT_NAMEremains the fallback whenREQUEST_URIis unavailable) (#141).
Added
- Detect Google-GeminiNotebook as an AI bot by @eldk in #153
- Add method to track an AI bot request, if the current user agent is a known AI bot by @diosmosis in #148
- PHPStan static analysis at max level (
phpstan.neon.dist) and the Matomo coding standard via PHP_CodeSniffer (phpcs.xml.dist), both enforced for every pull request through GitHub Actions. - A greatly expanded unit test suite covering all tracking parameters, cookie handling and request preparation.
setDebugTrackingParameter()(@internaltest helper) to append a raw, unvalidated tracking parameter that overrides any built-in parameter of the same name, so integration tests can verify server-side handling of malformed values.setCurlOptions(array)to pass additional cURL options (e.g.CURLOPT_IPRESOLVE,CURLOPT_HTTP_VERSION) for the tracking requests; they are applied after the built-in options (#92). CustomCURLOPT_HTTPHEADERentries are merged with the tracker's own headers rather than replacing them, so adding a header no longer drops the built-inContent-Type(which would otherwise break bulk requests).
New Contributors
Full Changelog: 3.4.0...4.0.0