Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0f991d6
Raise PHP requirement to 8.1 and add static-analysis dev tooling
sgiehl Jul 24, 2026
daa0dbb
CI: run on PHP 8.1-8.5 and add PHPStan + PHPCS quality workflow
sgiehl Jul 24, 2026
ef051f8
Add strict types and modern type hints; fix latent type bugs
sgiehl Jul 24, 2026
b8b4690
Add PHPStan configuration at max level
sgiehl Jul 24, 2026
ef8e0c4
Add PHP_CodeSniffer config (Matomo standard) and fix violations
sgiehl Jul 24, 2026
3547742
Migrate PHPUnit configuration to 10.x and update tests
sgiehl Jul 24, 2026
7b50646
Document PHP 8.1 requirement and the 4.0.0 major release
sgiehl Jul 24, 2026
3a9f331
Remove #[AllowDynamicProperties]
sgiehl Jul 24, 2026
5a15065
Allow setUserId(null) to de-assign a previously set User ID
sgiehl Jul 24, 2026
e14eee6
Fix issues found in adversarial review
sgiehl Jul 24, 2026
860e685
Greatly expand the unit test suite and report coverage in CI
sgiehl Jul 24, 2026
54c3277
Improve compatibility with Matomo's tracker usage; add test-only raw …
sgiehl Jul 25, 2026
1bc37b6
CI: harden workflows (pin setup-php, disable credential persistence)
sgiehl Jul 25, 2026
05384bf
CI: remove the code coverage job
sgiehl Jul 25, 2026
faec8a4
Distinguish unset from zero for goal/ecommerce revenue amounts
sgiehl Jul 25, 2026
a554310
Address open issues: URL-encode cip (#151), drop dead curl_close (#149)
sgiehl Jul 25, 2026
d6d9c70
Send ca=1 on event and content tracking requests (#80)
sgiehl Jul 25, 2026
6893b50
Lower default request timeouts to sane in-page values (#88)
sgiehl Jul 25, 2026
ffde370
Add setCurlOptions() for custom cURL options (#92)
sgiehl Jul 25, 2026
5e85858
Unify request-failure handling; add opt-in fail-safe mode (#105)
sgiehl Jul 25, 2026
cc393b9
Consistently encode tracking parameters; keep the request URL out of …
sgiehl Jul 25, 2026
50798be
Fix cookie header serialization/parsing; redact attribution exception
sgiehl Jul 25, 2026
e6b94a6
Address security-review findings (sensitive params, POST token, trans…
sgiehl Jul 25, 2026
8c1c17a
Encode the action type and harden the transport selection
sgiehl Jul 27, 2026
693169a
Document error handling/timeouts and clarify config comments
sgiehl Jul 27, 2026
c089a9c
Mark request URL/body sensitive in the transport option builders
sgiehl Jul 27, 2026
11d64c2
CHANGELOG: warn that the removed false sentinel now coerces to 0/''
sgiehl Jul 27, 2026
63928c0
Merge custom CURLOPT_HTTPHEADER instead of replacing the built-in hea…
sgiehl Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.git* export-ignore
phpunit* export-ignore
phpstan.neon.dist export-ignore
phpcs.xml.dist export-ignore
tests/ export-ignore
run_tests.sh export-ignore
10 changes: 6 additions & 4 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest, windows-latest, macOS-latest]
php-version: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
php-version: ['8.1', '8.2', '8.3', '8.4', '8.5']
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install PHP
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2
with:
php-version: ${{ matrix.php-version }}
tools: composer:v2
extensions: memcached
extensions: curl
- name: "Composer install"
run: |
composer install --prefer-dist
Expand Down
57 changes: 57 additions & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Code quality

on:
pull_request:
push:
branches: [ master ]

permissions:
actions: read
checks: read
contents: read
deployments: none
issues: read
packages: none
pull-requests: read
repository-projects: none
security-events: none
statuses: none

jobs:
phpstan:
name: PHPStan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2
with:
php-version: '8.1'
tools: composer:v2
extensions: curl
coverage: none
- name: "Composer install"
run: composer install --prefer-dist
- name: PHPStan
run: ./vendor/bin/phpstan analyse --no-progress

phpcs:
name: PHP_CodeSniffer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install PHP
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2
with:
php-version: '8.1'
tools: composer:v2
extensions: curl
coverage: none
- name: "Composer install"
run: composer install --prefer-dist
- name: PHP_CodeSniffer
run: ./vendor/bin/phpcs
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
/vendor/
.phpunit.result.cache
/tests/.phpunit.result.cache
/.phpunit.cache/
composer.lock
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,49 @@

This is the Developer Changelog for Matomo PHP Tracker. All breaking changes or new features are listed below.

## Matomo PHP Tracker 4.0.0

Attention: this is a major release with breaking changes.

> **Upgrade note — the `false` "not known" sentinel is gone.** Tracker 3.x let you pass `false` to 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 (`?T` or numeric unions). If your calling code does **not** use `declare(strict_types=1)` — the usual case for a drop-in tracker — PHP's weak-mode coercion silently turns `false` into `0` / `0.0` / `''` instead of raising an error, so such calls now **send a value** (`e_v=0`, `lat=0`, item price `0`) where 3.x omitted the parameter. Replace every `false` "not known" argument with `null` or simply omit it; passing `false` no 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 (extend `MatomoTracker` and 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 a `TypeError` (for example a non-numeric string for a numeric parameter, or any type mismatch when the calling code itself declares `strict_types=1`). Note that for ordinary (non-strict) callers PHP's weak-mode coercion still applies, so e.g. `false` becomes `0`/`''` rather than raising — see the upgrade note above about the removed `false` sentinel.
- Optional "unset" parameters and their corresponding properties and getters now use `null` instead of the previous `false` sentinel. For example `getUserId()`, `getUserAgent()`, `getIp()` and `getPageviewId()` now return `null` (not `false`) when no value is set, and `doTrackEvent()`/`getUrlTrackEvent()` default the event name and value to `null`.
- All public properties are now natively typed. Assigning a legacy sentinel value such as `false` to e.g. `$tracker->userAgent` now throws a `TypeError`; the `attributionInfo` property defaults to an empty array instead of `false`. Subclasses overriding methods with the old untyped signatures may need to be updated to the new signatures.
- `setUserId()` now accepts `null` to de-assign a previously set User ID, as the method documentation always promised (previously the `string` type hint made that impossible).
- `setUrlReferrer()` (and the deprecated `setUrlReferer()`) accept `null` to unset the referrer.
- `setCustomTrackingParameter()` accepts an array value again (serialized via `http_build_query`, as the JS tracker does); this restores the pre-3.4.0 behavior for multi-value parameters.
- `setLatitude()` / `setLongitude()` values of `0.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 the `Matomo_`/`Piwik_` goal helpers) take `?float $revenue = null`: `null` omits `revenue` (so Matomo uses the goal's configured revenue) while `0.0` now sends `revenue=0`. Likewise the optional Ecommerce amounts (`$subTotal`, `$tax`, `$shipping`, `$discount` of `doTrackEcommerceOrder()` etc.) are `?float = null` and only sent when provided, and the required Ecommerce grand total is now always sent (a `0.0` order/cart sends `revenue=0`). Previously an explicit `0`/`0.0` was silently omitted for all of these.
- The `do*` tracking methods now declare a `string|bool` return type. In bulk mode they return boolean `true` (previously the value was coerced to the string `"1"`).
- `doTrackSiteSearch()` / `getUrlTrackSiteSearch()` accept `?int $countResults` and only send `&search_count` when a count is provided (previously `&search_count=0` was always sent).
- Both transports now consistently throw a `RuntimeException` on request failure (DNS, connection or timeout errors) by default; previously only the cURL transport threw while the stream fallback silently returned `false`. Call `setExceptionsEnabled(false)` to make failed requests return `false` instead, 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 the `download`/`link` action type passed to `getUrlTrackAction()`/`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_auth` in the request body are now sent as `POST`; previously the stream transport sent them as `GET`, 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 incoming `Set-Cookie` response headers are parsed instead of only the last one; `getIncomingTrackerCookie()` returns `string|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).

### Added
- 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()` (`@internal` test 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). Custom `CURLOPT_HTTPHEADER` entries are merged with the tracker's own headers rather than replacing them, so adding a header no longer drops the built-in `Content-Type` (which would otherwise break bulk requests).

## Matomo PHP Tracker 3.4.0
### Changed

Expand Down
Loading
Loading