From d54ed8a1f62e448d17a0d46bcab8a0def0269f80 Mon Sep 17 00:00:00 2001 From: sgiehl Date: Mon, 27 Jul 2026 16:58:21 +0200 Subject: [PATCH 1/2] Detect the tracked page URL from REQUEST_URI, not PATH_INFO (#141) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getCurrentScriptName() builds the auto-detected page URL (the path between host and query string). It preferred $_SERVER['PATH_INFO'], which only holds the trailing path-info segment — so with front- controller / path-info routing (e.g. /dir1/page handled by dir1/index.php) the tracker recorded a truncated '/page' instead of '/dir1/page'. REQUEST_URI already contains the full requested path (PATH_INFO is always just a suffix of it), so use it as the source and drop PATH_INFO entirely; SCRIPT_NAME stays as the fallback when REQUEST_URI is absent. This also aligns the primary source with Matomo core's Url helper. Reported in #141. --- CHANGELOG.md | 1 + MatomoTracker.php | 26 ++++++++++++++------------ tests/Unit/MatomoTrackerTest.php | 14 +++++++++++--- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b558746..743d797 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ Attention: this is a major release with breaking changes. - 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_URI` as the source instead of `PATH_INFO`. With front-controller / path-info routing (e.g. `/dir1/page` handled by `dir1/index.php`), `PATH_INFO` only holds the trailing `/page`, so the tracker previously recorded a truncated URL; it now records the full requested path. `PATH_INFO` is no longer used at all (`SCRIPT_NAME` remains the fallback when `REQUEST_URI` is unavailable) (#141). ### 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. diff --git a/MatomoTracker.php b/MatomoTracker.php index f77dcbc..591ba59 100644 --- a/MatomoTracker.php +++ b/MatomoTracker.php @@ -2621,24 +2621,26 @@ protected function getCookieMatchingName(string $name): string|false } /** - * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1¶m2=value2" - * will return "/dir1/dir2/index.php" + * Returns the path portion of the URL the visitor requested (everything between the host and + * the query string). For "http://example.org/dir1/dir2/index.php?param1=value1" this returns + * "/dir1/dir2/index.php"; for a front-controller URL such as "http://example.org/dir1/page" + * (where "/page" is handled by dir1/index.php) it returns "/dir1/page". + * + * The full request path is taken from REQUEST_URI. PATH_INFO is deliberately not used: it only + * holds the trailing path-info segment (e.g. "/page"), so it would drop the directory/script + * prefix and yield a truncated URL. SCRIPT_NAME is the fallback when REQUEST_URI is unavailable. * * @ignore */ protected static function getCurrentScriptName(): string { $url = ''; - if (!empty($_SERVER['PATH_INFO'])) { - $url = self::toStringValue($_SERVER['PATH_INFO']); - } else { - if (!empty($_SERVER['REQUEST_URI'])) { - $requestUri = self::toStringValue($_SERVER['REQUEST_URI']); - if (($pos = strpos($requestUri, '?')) !== false) { - $url = substr($requestUri, 0, $pos); - } else { - $url = $requestUri; - } + if (!empty($_SERVER['REQUEST_URI'])) { + $requestUri = self::toStringValue($_SERVER['REQUEST_URI']); + if (($pos = strpos($requestUri, '?')) !== false) { + $url = substr($requestUri, 0, $pos); + } else { + $url = $requestUri; } } if (empty($url) && isset($_SERVER['SCRIPT_NAME'])) { diff --git a/tests/Unit/MatomoTrackerTest.php b/tests/Unit/MatomoTrackerTest.php index de2057a..731a489 100644 --- a/tests/Unit/MatomoTrackerTest.php +++ b/tests/Unit/MatomoTrackerTest.php @@ -1848,17 +1848,24 @@ public function testGetCurrentScriptName(): void unset($_SERVER['PATH_INFO'], $_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']); $this->assertSame('/', TestableMatomoTracker::callGetCurrentScriptName()); + // SCRIPT_NAME is only the fallback when REQUEST_URI is unavailable. $_SERVER['SCRIPT_NAME'] = 'script.php'; $this->assertSame('/script.php', TestableMatomoTracker::callGetCurrentScriptName()); + // REQUEST_URI is the primary source; the query string is stripped. $_SERVER['REQUEST_URI'] = '/dir/page.php?query=1'; $this->assertSame('/dir/page.php', TestableMatomoTracker::callGetCurrentScriptName()); $_SERVER['REQUEST_URI'] = '/dir/other.php'; $this->assertSame('/dir/other.php', TestableMatomoTracker::callGetCurrentScriptName()); - $_SERVER['PATH_INFO'] = '/path/info'; - $this->assertSame('/path/info', TestableMatomoTracker::callGetCurrentScriptName()); + // Front-controller / path-info routing (#141): with a request for /dir1/page handled by + // dir1/index.php, PATH_INFO is only "/page". The full requested path must still be tracked, + // so REQUEST_URI wins and PATH_INFO is ignored (previously it truncated the URL to "/page"). + $_SERVER['REQUEST_URI'] = '/dir1/page'; + $_SERVER['PATH_INFO'] = '/page'; + $_SERVER['SCRIPT_NAME'] = '/dir1/index.php'; + $this->assertSame('/dir1/page', TestableMatomoTracker::callGetCurrentScriptName()); } public function testGetCurrentQueryStringAndUrl(): void @@ -1871,7 +1878,8 @@ public function testGetCurrentQueryStringAndUrl(): void $_SERVER['HTTPS'] = 'on'; $_SERVER['HTTP_HOST'] = 'matomo.example'; - $_SERVER['PATH_INFO'] = '/page'; + unset($_SERVER['PATH_INFO']); + $_SERVER['REQUEST_URI'] = '/page'; $this->assertSame('https://matomo.example/page?a=b&c=d', TestableMatomoTracker::callGetCurrentUrl()); } From 0232a48f49e0fc7f4b678bb15cfdfb6f0ee82ef2 Mon Sep 17 00:00:00 2001 From: sgiehl Date: Mon, 27 Jul 2026 17:07:58 +0200 Subject: [PATCH 2/2] Drop the ticket number from an inline test comment Keep issue references in the CHANGELOG and git history, not in code. --- tests/Unit/MatomoTrackerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/MatomoTrackerTest.php b/tests/Unit/MatomoTrackerTest.php index 731a489..e971e1d 100644 --- a/tests/Unit/MatomoTrackerTest.php +++ b/tests/Unit/MatomoTrackerTest.php @@ -1859,7 +1859,7 @@ public function testGetCurrentScriptName(): void $_SERVER['REQUEST_URI'] = '/dir/other.php'; $this->assertSame('/dir/other.php', TestableMatomoTracker::callGetCurrentScriptName()); - // Front-controller / path-info routing (#141): with a request for /dir1/page handled by + // Front-controller / path-info routing: with a request for /dir1/page handled by // dir1/index.php, PATH_INFO is only "/page". The full requested path must still be tracked, // so REQUEST_URI wins and PATH_INFO is ignored (previously it truncated the URL to "/page"). $_SERVER['REQUEST_URI'] = '/dir1/page';