Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: schorschii/matomo-php-tracker
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: matomo-org/matomo-php-tracker
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 7 commits
  • 14 files changed
  • 10 contributors

Commits on Dec 2, 2025

  1. Configuration menu
    Copy the full SHA
    9ddccf0 View commit details
    Browse the repository at this point in the history

Commits on Dec 12, 2025

  1. Declare supported PHP versions explicitly and run tests on all suppor…

    …ted versions (matomo-org#147)
    
    * Declare supported PHP versions explicitly and run tests on all supported versions
    
    * Apply suggestion from @sgiehl
    
    ---------
    
    Co-authored-by: Stefan Giehl <stefan@matomo.org>
    gsteel and sgiehl authored Dec 12, 2025
    Configuration menu
    Copy the full SHA
    b029aa2 View commit details
    Browse the repository at this point in the history

Commits on Dec 20, 2025

  1. Fix: Not possible to create multiple Piwik tracker instances having d…

    …ifferent API urls (matomo-org#145)
    
    * Allow setting form factors client hint (matomo-org#136)
    
    * Allow settting form factors client hint
    
    * update changelog
    
    * Fix handling of some attributes
    
    * Apply review feedback
    
    Co-authored-by: Michal Kleiner <mk@011.nz>
    
    ---------
    
    Co-authored-by: Michal Kleiner <mk@011.nz>
    
    * deprecate $URL
    
    * use old $URL if apiUrl is empty
    
    ---------
    
    Co-authored-by: Stefan Giehl <stefan@matomo.org>
    Co-authored-by: Michal Kleiner <mk@011.nz>
    Co-authored-by: Andrii Lutskevych <andrii.lutskevych@check24.de>
    4 people authored Dec 20, 2025
    Configuration menu
    Copy the full SHA
    9462dc6 View commit details
    Browse the repository at this point in the history

Commits on Jan 16, 2026

  1. Conditionally close the curl handle (matomo-org#150)

    `curl_close` has no effect since PHP 8.0 and is deprecated since 8.5
    
    https://www.php.net/curl_close
    gsteel authored Jan 16, 2026
    Configuration menu
    Copy the full SHA
    262acfd View commit details
    Browse the repository at this point in the history

Commits on Jul 24, 2026

  1. Add method to track an AI bot request, if the current user agent is a…

    … known AI bot (matomo-org#148)
    
    * add methods to detect and track pageviews if the current user agent is an AI bot
    
    * add supported query parameters for AI bot tracking
    
    * add more tests for AI bot tracking methods
    
    * use recMode parameter
    
    * make user agent substrings array a public const and fill out missing php docs
    
    * remove phpstorm added spacing
    
    * update tests
    
    * set url in test
    
    * remove Devin user agent check
    
    Co-authored-by: Thomas ZILLIOX <thomas@zilliox.me>
    
    * Apply suggestions from code review
    
    Co-authored-by: Stefan Giehl <stefan@matomo.org>
    
    ---------
    
    Co-authored-by: Thomas ZILLIOX <thomas@zilliox.me>
    Co-authored-by: Stefan Giehl <stefan@matomo.org>
    3 people authored Jul 24, 2026
    Configuration menu
    Copy the full SHA
    f2afbb8 View commit details
    Browse the repository at this point in the history

Commits on Jul 27, 2026

  1. Modernize tracker for 4.0.0: PHP 8.1, strict types, static analysis, …

    …full test coverage (matomo-org#152)
    
    * Raise PHP requirement to 8.1 and add static-analysis dev tooling
    
    - Drop PHP 7.2-8.0 support; require ^8.1.
    - Bump PHPUnit to ^10.5 (PHP 8.1 floor rules out PHPUnit 11).
    - Add PHPStan ^2, PHP_CodeSniffer ^3.10, the Matomo coding standard
      (matomo-org/matomo-coding-standards via VCS repo) and the phpcs
      composer-installer plugin.
    - Pin config.platform.php to 8.1.0 and allow the installer plugin.
    - Add composer scripts: test, phpstan, phpcs, phpcbf.
    
    * CI: run on PHP 8.1-8.5 and add PHPStan + PHPCS quality workflow
    
    - phpunit.yml: drop PHP 7.2-8.0 from the matrix, bump to
      actions/checkout@v4, and install the json + curl extensions.
    - Add quality.yml running PHPStan and PHP_CodeSniffer on PHP 8.1
      for every pull request and push to master.
    
    * Add strict types and modern type hints; fix latent type bugs
    
    Add declare(strict_types=1) to both source files and give every
    method proper parameter and return type hints aligned with how
    Matomo core coerces each tracking parameter. Declare all class
    properties explicitly with concrete/nullable types and array-shape
    PHPDocs (the class keeps #[AllowDynamicProperties] for consumer BC).
    
    Optional 'unset' parameters and their backing properties/getters
    move from the legacy `= false` sentinel to nullable (`?T = null`);
    fluent setters now declare `: self`, and the do* tracking methods
    declare `: string|bool`.
    
    Fixes latent type issues that become fatal TypeErrors under strict
    types (previously masked by weak-mode coercion), including:
    - sendRequest() now returns string|bool (bulk mode really returns
      true, not the coerced "1"); cascaded to all do* methods.
    - forceDotAsSeparatorForDecimalPoint() no longer returns null/false
      from a : string method.
    - setCustomTrackingParameter() casts the dimension id to int.
    - doTrackSiteSearch()/getUrlTrackSiteSearch() use ?int and only emit
      &search_count when a count is provided (no longer always sends 0).
    - getUrlTrackCrash()/getUrlTrackEcommerceOrder() cast int/id values
      before urlencode(); getRequest() casts lat/long before urlencode().
    - getUrlTrackEvent() guards the nullable name/value correctly.
    - clientHints is reset to [] (not false) in bulk mode.
    - getCustomVariablesFromCookie() validates decoded cookie data.
    
    BREAKING CHANGE: passing mismatched scalar types now throws
    TypeError instead of being silently coerced, and unset sentinels are
    null rather than false. This targets a new major release.
    
    * Add PHPStan configuration at max level
    
    Analyse both source files at level max with the PHP 8.1 platform.
    No baseline: the codebase is fully clean at max level.
    
    * Add PHP_CodeSniffer config (Matomo standard) and fix violations
    
    Add phpcs.xml.dist referencing the Matomo coding standard with a
    400-char line limit and scoped exceptions: the two source files
    intentionally mix a class with global helper functions/includes and
    stay in the global namespace for backwards compatibility.
    
    Apply the resulting fixes (blank line after the open tag, control
    structure / operator spacing, trailing newline) and clean up the
    test file: reorder the file header, add constant visibility, and
    rename the test methods to camelCase.
    
    * Migrate PHPUnit configuration to 10.x and update tests
    
    - phpunit.xml.dist: run --migrate-configuration (drop the removed
      verbose attribute, refresh the schema to 10.5, add cacheDirectory);
      keep backupGlobals.
    - Make the data-provider methods static as required by PHPUnit 10.
    - Ignore the new .phpunit.cache directory.
    
    * Document PHP 8.1 requirement and the 4.0.0 major release
    
    - README: state the PHP 8.1+ requirement and add a Development
      section covering the composer test/phpstan/phpcs scripts.
    - CHANGELOG: add the 4.0.0 entry describing the breaking changes
      (PHP floor, strict types, false->null sentinels, do* return type,
      search_count / stream-fallback behaviour) and the new tooling.
    - .gitattributes: export-ignore phpstan.neon.dist and phpcs.xml.dist
      so dist archives stay lean.
    
    * Remove #[AllowDynamicProperties]
    
    All properties are now declared explicitly, so the tracker no longer
    creates dynamic properties and the attribute is unnecessary. Dropping
    it makes undeclared dynamic property writes raise the standard PHP 8.2
    deprecation, in line with the strict, fully-typed rewrite.
    
    * Allow setUserId(null) to de-assign a previously set User ID
    
    The method documentation always promised that the User ID can be
    unset again, but the string type hint made that impossible. Accept
    null to reset it, matching the null sentinel used everywhere else.
    
    * Fix issues found in adversarial review
    
    - Cast cookie names to string before strpos(): cookies with purely
      numeric names are exposed as integer keys in $_COOKIE and caused a
      TypeError under strict_types, breaking tracker construction.
    - Do not read $http_response_header when the stream fallback request
      failed without an HTTP response; previously this caused a TypeError
      in parseIncomingCookies() instead of returning false.
    - Send latitude/longitude values of 0.0 instead of silently dropping
      coordinates on the equator or prime meridian.
    - Guard the POST URL split against URLs without a query string and
      replace an always-true elseif condition with else.
    - Remove dead branches in setEcommerceView() that guarded against
      values the string type hints now make impossible.
    - Restore the dropped $category docblock in doTrackSiteSearch(), fix
      the setClientHints() fullVersionList docblock shape, remove leftover
      array() syntax and strval() calls, and document all behavior changes
      in the 4.0.0 changelog.
    
    * Greatly expand the unit test suite and report coverage in CI
    
    - Add tests for every tracking URL parameter, all do*/getUrlTrack*
      methods, custom variables/dimensions, ecommerce, client hints,
      attribution info, visitor/user id handling, bulk tracking, cookie
      handling and request preparation, including regression tests for
      the behavior changes of the 4.0.0 release (search_count omission,
      bulk true return, null sentinels, numeric cookie names).
    - Introduce a TestableMatomoTracker capturing requests and cookies
      instead of performing network or header I/O.
    - Analyse the test suite with PHPStan at max level.
    - Add a phpunit <source> section and a CI coverage job (pcov).
    
    * Improve compatibility with Matomo's tracker usage; add test-only raw param helper
    
    Findings from auditing tracker usage across Matomo core and plugins:
    
    - setUrlReferrer()/setUrlReferer() accept ?string again so callers can
      unset the referrer with null (used by several Matomo fixtures).
    - setCustomTrackingParameter() accepts string|array again; array values
      are serialized via http_build_query exactly as before 4.0.0, restoring
      multi-value parameters used by FormAnalytics and HeatmapSessionRecording.
    - Add setDebugTrackingParameter(string, string) (@internal): appends a raw,
      unvalidated parameter that overrides any built-in of the same name, so
      Matomo integration tests can verify server-side handling of malformed
      values without bypassing the tracker. Cleared per request.
    
    Adds unit tests and CHANGELOG entries for all three.
    
    * CI: harden workflows (pin setup-php, disable credential persistence)
    
    - Pin shivammathur/setup-php to a commit hash (2.37.2) instead of the
      floating @v2 tag, mitigating supply-chain risk (Aikido high finding).
    - Set persist-credentials: false on actions/checkout steps so the
      GITHUB_TOKEN is not left in the git config for later steps (Aikido).
    
    * CI: remove the code coverage job
    
    Drop the pcov-based coverage job for now; the standard PHPUnit matrix,
    PHPStan and PHPCS jobs remain.
    
    * Distinguish unset from zero for goal/ecommerce revenue amounts
    
    Core treats an absent 'revenue' as 'use the goal's configured revenue',
    and defaults the optional ecommerce amounts to false (not 0), so a real
    zero is meaningful and must be distinguishable from 'not provided'.
    
    - doTrackGoal()/getUrlTrackGoal() (+ Matomo_/Piwik_ helpers) take
      ?float $revenue = null: null omits revenue, 0.0 now sends revenue=0.
    - doTrackEcommerceOrder()/getUrlTrackEcommerceOrder()/getUrlTrackEcommerce()
      take ?float $subTotal/$tax/$shipping/$discount = null, sent only when
      provided; the required grand total is now always sent (0.0 -> revenue=0).
    
    Previously an explicit 0/0.0 was silently dropped by the !empty() guards.
    Adds tests and a CHANGELOG entry.
    
    * Address open issues: URL-encode cip (matomo-org#151), drop dead curl_close (matomo-org#149)
    
    - URL-encode the cip (override IP) tracking parameter before adding it to
      the request, matching every other value and preventing tracking-parameter
      injection when an application sets cip from untrusted input. Fixes matomo-org#151.
    - Remove the curl_close() call guarded by PHP_VERSION_ID < 80000: it is dead
      code now that PHP 8.1 is the minimum, and curl_close() is a no-op (and
      deprecated in PHP 8.5) on all supported versions. Fixes matomo-org#149.
    
    * Send ca=1 on event and content tracking requests (matomo-org#80)
    
    Append &ca=1 (custom action) to the event and content-tracking URLs,
    matching the JS tracker. This prevents Matomo from falling back to
    recording these requests as page views when the handling plugin is
    disabled. Fixes matomo-org#80.
    
    * Lower default request timeouts to sane in-page values (matomo-org#88)
    
    Change the default requestTimeout from 600s to 5s and connect timeout
    from 300s to 2s, so a slow or unreachable Matomo can no longer block the
    calling page for up to 10 minutes. Callers can still raise them via
    setRequestTimeout()/setRequestConnectTimeout(). Fixes matomo-org#88.
    
    * Add setCurlOptions() for custom cURL options (matomo-org#92)
    
    Allow callers to pass additional cURL options (e.g. CURLOPT_IPRESOLVE,
    CURLOPT_HTTP_VERSION) that are applied after the built-in options, so
    they can tune the request without the library committing to opinionated
    defaults. Fixes matomo-org#92.
    
    * Unify request-failure handling; add opt-in fail-safe mode (matomo-org#105)
    
    Previously the cURL transport threw a RuntimeException on DNS/connection
    failures while the stream transport silently returned false. Both now
    throw by default (preserving cURL behavior and making the two transports
    consistent). Call setExceptionsEnabled(false) to make failed requests
    return false instead, so tracking never breaks the calling application.
    Refs matomo-org#105.
    
    * Consistently encode tracking parameters; keep the request URL out of exceptions
    
    - URL-encode the remaining raw query values: _refts (from attribution
      data), customData (data) and pageCharset (cs), so a value can no longer
      inject extra tracking parameters.
    - Validate the visitor ID read from the first-party cookie as a
      16-character hexadecimal string in loadVisitorIdCookie(), matching
      setVisitorId().
    - Request-failure exceptions now include only the target host, never the
      full URL (which can carry token_auth/PII in its query string).
    
    * Fix cookie header serialization/parsing; redact attribution exception
    
    - Build the outgoing Cookie header as url-encoded pairs joined with '; '
      (was http_build_query, which joined with '&'), and parse every incoming
      Set-Cookie header by its first '=' so multiple cookies accumulate
      (parse_str previously overwrote the whole set per header and applied
      query-string bracket semantics). getIncomingTrackerCookie() now returns
      string|false.
    - setAttributionInfo() no longer echoes the supplied JSON in its exception
      message and marks the parameter #[\SensitiveParameter].
    
    * Address security-review findings (sensitive params, POST token, transports, bulk)
    
    - Mark sendRequest() $url and $data as #[\SensitiveParameter] so they are
      redacted from exception stack traces (token_auth/PII).
    - Force POST when token_auth is placed in the request body, so the stream
      transport no longer sends it as a GET body that Matomo ignores.
    - Stream transport sets ignore_errors so HTTP 4xx/5xx return the response
      body (parity with cURL) instead of a failure.
    - Bulk tracking uses a >=30s timeout and retains the queued actions when a
      batch fails to send (previously discarded), allowing retry.
    - Docs: clarify setUserId(null) does not unlink the current server-side
      visit; drop the stale 'coverage reported in CI' CHANGELOG claim.
    
    Adds tests for all of the above.
    
    * Encode the action type and harden the transport selection
    
    - getUrlTrackAction()/doTrackAction() now URL-encode the action type (not
      just the value), so a crafted or plugin-specific action parameter cannot
      inject an additional query-string parameter.
    - Extract the visitor-id hex character set into a single HEX_CHARACTERS
      constant used by both setVisitorId() and loadVisitorIdCookie().
    - Make cURL detection overridable via hasCurlSupport() so the stream
      transport can be exercised in tests; add coverage for the stream
      failure (host-only exception message) and fail-safe paths.
    
    * Document error handling/timeouts and clarify config comments
    
    - README: add an 'Error handling and timeouts' section covering the
      default exceptions, the short 5s/2s timeouts, setExceptionsEnabled(),
      setRequestTimeout()/setRequestConnectTimeout() and bulk retention.
    - CHANGELOG: correct the multi-value BC note to reference pre-3.4.0 and
      mention that the download/link action type is now URL-encoded.
    - phpcs.xml.dist: explain that the 400-char line limit matches Matomo
      core's own coding standard rather than being an arbitrary value.
    
    * Mark request URL/body sensitive in the transport option builders
    
    sendRequest() appends token_auth into the URL/body and passes them to
    prepareCurlOptions()/prepareStreamOptions(). #[\SensitiveParameter] only
    redacts the frame where the parameter is declared, so mark those params
    too: should either builder (or something it calls) ever throw, the token
    and PII carried in $url/$data are redacted from that frame as well.
    
    * CHANGELOG: warn that the removed false sentinel now coerces to 0/''
    
    The blanket 'mismatched scalar now throws a TypeError' claim was
    misleading: strict/weak mode is set by the *caller's* file, so a normal
    (non-strict) consumer still gets weak-mode coercion. Legacy calls that
    passed false to mean 'not known' (doTrackEvent value, addEcommerceItem
    price, setLatitude/setLongitude, ...) now silently send 0/0.0/'' instead
    of omitting the parameter. Add an explicit upgrade note and correct the
    TypeError wording so the transition is spelled out.
    
    * Merge custom CURLOPT_HTTPHEADER instead of replacing the built-in headers
    
    setCurlOptions() applied caller options via array_replace(), so a caller
    adding a single header wholesale-replaced the tracker's own header list.
    For POST/bulk requests that dropped the Content-Type, making Matomo unable
    to parse the body — a silent failure. Merge (append) custom HTTP headers
    onto the built-in ones instead, and document the special-casing.
    
    Also extend the sensitive-parameter test to assert redaction on the
    transport option builders (prepareCurlOptions/prepareStreamOptions), not
    just sendRequest, since the URL/body are forwarded to them.
    sgiehl authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    bde2d19 View commit details
    Browse the repository at this point in the history
  2. Detect Google-GeminiNotebook as an AI bot (matomo-org#153)

    * Apply Google-GeminiNotebook update
    
    * Detect Google-GeminiNotebook user agent
    
    ---------
    
    Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
    eldk and github-actions[bot] authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    b3e57c4 View commit details
    Browse the repository at this point in the history
Loading