PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / trunk
Matomo Analytics – Powerful, Privacy-First Insights for WordPress vtrunk
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Tracker / PageUrl.php
matomo / app / core / Tracker Last commit date
Config 4 months ago Db 3 months ago Handler 2 years ago Visit 1 month ago Action.php 3 months ago ActionPageview.php 2 years ago BotRequest.php 3 months ago BotRequestProcessor.php 1 month ago Cache.php 6 months ago Db.php 1 year ago Failures.php 6 months ago FingerprintSalt.php 1 year ago GoalManager.php 1 month ago Handler.php 2 years ago IgnoreCookie.php 1 year ago LogTable.php 1 year ago Model.php 6 months ago PageUrl.php 2 weeks ago Request.php 1 month ago RequestHandlerTrait.php 4 months ago RequestProcessor.php 1 month ago RequestSet.php 6 months ago Response.php 3 months ago ScheduledTasksRunner.php 1 year ago Settings.php 3 months ago TableLogAction.php 6 months ago TrackerCodeGenerator.php 1 year ago TrackerConfig.php 1 month ago Visit.php 3 months ago VisitExcluded.php 3 months ago VisitInterface.php 3 months ago Visitor.php 1 month ago VisitorNotFoundInDb.php 1 month ago VisitorRecognizer.php 1 year ago
PageUrl.php
355 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 */
9 namespace Piwik\Tracker;
10
11 use Piwik\Cache;
12 use Piwik\CacheId;
13 use Piwik\Tracker\Cache as TrackerCache;
14 use Piwik\Common;
15 use Piwik\Config;
16 use Piwik\Piwik;
17 use Piwik\Plugins\SitesManager\API as APISitesManager;
18 use Piwik\UrlHelper;
19 class PageUrl
20 {
21 /**
22 * Map URL prefixes to integers.
23 * @see self::normalizeUrl(), self::reconstructNormalizedUrl()
24 */
25 public static $urlPrefixMap = array('http://www.' => 1, 'http://' => 0, 'https://www.' => 3, 'https://' => 2);
26 /**
27 * Given the Input URL, will exclude all query parameters set for this site
28 *
29 * @static
30 * @param string $originalUrl
31 * @param $idSite
32 * @param array $additionalParametersToExclude
33 * @return bool|string Returned URL is HTML entities decoded
34 */
35 public static function excludeQueryParametersFromUrl($originalUrl, $idSite, $additionalParametersToExclude = [])
36 {
37 $originalUrl = self::cleanupUrl($originalUrl);
38 $parsedUrl = @parse_url($originalUrl);
39 $parsedUrl = self::cleanupHostAndHashTag($parsedUrl, $idSite);
40 $parametersToExclude = array_merge(self::getQueryParametersToExclude($idSite), $additionalParametersToExclude);
41 if (empty($parsedUrl['query'])) {
42 if (empty($parsedUrl['fragment'])) {
43 return UrlHelper::getParseUrlReverse($parsedUrl);
44 }
45 // Exclude from the hash tag as well
46 $queryParameters = UrlHelper::getArrayFromQueryString($parsedUrl['fragment']);
47 $parsedUrl['fragment'] = UrlHelper::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude);
48 $url = UrlHelper::getParseUrlReverse($parsedUrl);
49 return $url;
50 }
51 $queryParameters = UrlHelper::getArrayFromQueryString($parsedUrl['query']);
52 $parsedUrl['query'] = UrlHelper::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude);
53 $url = UrlHelper::getParseUrlReverse($parsedUrl);
54 return $url;
55 }
56 /**
57 * Returns the array of parameters names that must be excluded from the Query String in all tracked URLs
58 * @static
59 * @param $idSite
60 * @return array
61 */
62 public static function getQueryParametersToExclude($idSite)
63 {
64 $campaignTrackingParameters = Common::getCampaignParameters();
65 $campaignTrackingParameters = array_merge(
66 $campaignTrackingParameters[0],
67 // campaign name parameters
68 $campaignTrackingParameters[1]
69 );
70 $website = TrackerCache::getCacheWebsiteAttributes($idSite);
71 $excludedParameters = self::getExcludedParametersFromWebsite($website);
72 $parametersToExclude = array_merge(['ignore_referrer', 'ignore_referer'], $excludedParameters, self::getUrlParameterNamesToExcludeFromUrl(), $campaignTrackingParameters);
73 /**
74 * Triggered before setting the action url in Piwik\Tracker\Action so plugins can register
75 * parameters to be excluded from the tracking URL (e.g. campaign parameters).
76 *
77 * @param array &$parametersToExclude An array of parameters to exclude from the tracking url.
78 */
79 Piwik::postEvent('Tracker.PageUrl.getQueryParametersToExclude', array(&$parametersToExclude));
80 if (!empty($parametersToExclude)) {
81 Common::printDebug('Excluding parameters "' . implode(',', $parametersToExclude) . '" from URL');
82 }
83 $parametersToExclude = array_map('strtolower', $parametersToExclude);
84 return $parametersToExclude;
85 }
86 /**
87 * Returns the list of URL query parameters that should be removed from the tracked URL query string.
88 *
89 * @return array
90 */
91 protected static function getUrlParameterNamesToExcludeFromUrl()
92 {
93 $paramsToExclude = Config::getInstance()->Tracker['url_query_parameter_to_exclude_from_url'];
94 $paramsToExclude = explode(",", $paramsToExclude);
95 $paramsToExclude = array_map('trim', $paramsToExclude);
96 return $paramsToExclude;
97 }
98 /**
99 * Returns true if URL fragments should be removed for a specific site,
100 * false if otherwise.
101 *
102 * This function uses the Tracker cache and not the MySQL database.
103 *
104 * @param $idSite int The ID of the site to check for.
105 * @return bool
106 */
107 public static function shouldRemoveURLFragmentFor($idSite)
108 {
109 $websiteAttributes = TrackerCache::getCacheWebsiteAttributes($idSite);
110 return empty($websiteAttributes['keep_url_fragment']);
111 }
112 /**
113 * Cleans and/or removes the URL fragment of a URL.
114 *
115 * @param $urlFragment string The URL fragment to process.
116 * @param $idSite int|bool If not false, this function will check if URL fragments
117 * should be removed for the site w/ this ID and if so,
118 * the returned processed fragment will be empty.
119 *
120 * @return string The processed URL fragment.
121 */
122 public static function processUrlFragment($urlFragment, $idSite = \false)
123 {
124 // if we should discard the url fragment for this site, return an empty string as
125 // the processed url fragment
126 if ($idSite !== \false && \Piwik\Tracker\PageUrl::shouldRemoveURLFragmentFor($idSite)) {
127 return '';
128 } else {
129 // Remove trailing Hash tag in ?query#hash#
130 if (substr($urlFragment, -1) == '#') {
131 $urlFragment = substr($urlFragment, 0, strlen($urlFragment) - 1);
132 }
133 return $urlFragment;
134 }
135 }
136 /**
137 * Will cleanup the hostname (some browser do not strolower the hostname),
138 * and deal ith the hash tag on incoming URLs based on website setting.
139 *
140 * @param $parsedUrl
141 * @param $idSite int|bool The site ID of the current visit. This parameter is
142 * only used by the tracker to see if we should remove
143 * the URL fragment for this site.
144 * @return array
145 */
146 protected static function cleanupHostAndHashTag($parsedUrl, $idSite = \false)
147 {
148 if (empty($parsedUrl)) {
149 return $parsedUrl;
150 }
151 if (!empty($parsedUrl['host'])) {
152 $parsedUrl['host'] = mb_strtolower($parsedUrl['host']);
153 }
154 if (!empty($parsedUrl['fragment'])) {
155 $parsedUrl['fragment'] = \Piwik\Tracker\PageUrl::processUrlFragment($parsedUrl['fragment'], $idSite);
156 }
157 return $parsedUrl;
158 }
159 /**
160 * Converts Matrix URL format
161 * from https://example.org/thing;paramA=1;paramB=6542
162 * to https://example.org/thing?paramA=1&paramB=6542
163 *
164 * @param string $originalUrl
165 * @return string
166 */
167 public static function convertMatrixUrl($originalUrl)
168 {
169 $posFirstSemiColon = strpos($originalUrl, ";");
170 if (\false === $posFirstSemiColon) {
171 return $originalUrl;
172 }
173 $posQuestionMark = strpos($originalUrl, "?");
174 $replace = \false === $posQuestionMark;
175 if ($posQuestionMark > $posFirstSemiColon) {
176 $originalUrl = substr_replace($originalUrl, ";", $posQuestionMark, 1);
177 $replace = \true;
178 }
179 if ($replace) {
180 $originalUrl = substr_replace($originalUrl, "?", strpos($originalUrl, ";"), 1);
181 $originalUrl = str_replace(";", "&", $originalUrl);
182 }
183 return $originalUrl;
184 }
185 /**
186 * Clean up string contents (filter, truncate, ...)
187 *
188 * @param string $string Dirty string
189 * @return string
190 */
191 public static function cleanupString($string)
192 {
193 $string = trim($string);
194 $string = str_replace(array("\n", "\r", "\x00"), '', $string);
195 $limit = Config::getInstance()->Tracker['page_maximum_length'];
196 $clean = substr($string, 0, $limit);
197 return $clean;
198 }
199 protected static function reencodeParameterValue($value, $encoding)
200 {
201 if (is_string($value)) {
202 $decoded = urldecode($value);
203 try {
204 if (function_exists('mb_check_encoding') && @mb_check_encoding($decoded, $encoding)) {
205 $value = urlencode(mb_convert_encoding($decoded, 'UTF-8', $encoding));
206 }
207 } catch (\Error $e) {
208 // mb_check_encoding might throw an ValueError on PHP 8 if the given encoding does not exist
209 // we can't simply catch ValueError as it was introduced in PHP 8
210 }
211 }
212 return $value;
213 }
214 protected static function reencodeParametersArray($queryParameters, $encoding)
215 {
216 foreach ($queryParameters as &$value) {
217 if (is_array($value)) {
218 $value = self::reencodeParametersArray($value, $encoding);
219 } else {
220 $value = \Piwik\Tracker\PageUrl::reencodeParameterValue($value, $encoding);
221 }
222 }
223 return $queryParameters;
224 }
225 /**
226 * Checks if query parameters are of a non-UTF-8 encoding and converts the values
227 * from the specified encoding to UTF-8.
228 * This method is used to workaround browser/webapp bugs (see #3450). When
229 * browsers fail to encode query parameters in UTF-8, the tracker will send the
230 * charset of the page viewed and we can sometimes work around invalid data
231 * being stored.
232 *
233 * @param array $queryParameters Name/value mapping of query parameters.
234 * @param bool|string $encoding of the HTML page the URL is for. Used to workaround
235 * browser bugs & mis-coded webapps. See #3450.
236 *
237 * @return array
238 */
239 public static function reencodeParameters(&$queryParameters, $encoding = \false)
240 {
241 if (function_exists('mb_check_encoding')) {
242 // if query params are encoded w/ non-utf8 characters (due to browser bug or whatever),
243 // encode to UTF-8.
244 if (is_string($encoding) && strtolower($encoding) !== 'utf-8') {
245 Common::printDebug("Encoding page URL query parameters to {$encoding}.");
246 $queryParameters = \Piwik\Tracker\PageUrl::reencodeParametersArray($queryParameters, $encoding);
247 }
248 } else {
249 Common::printDebug("Page charset supplied in tracking request, but mbstring extension is not available.");
250 }
251 return $queryParameters;
252 }
253 public static function cleanupUrl($url)
254 {
255 $url = Common::unsanitizeInputValue($url);
256 $url = \Piwik\Tracker\PageUrl::cleanupString($url);
257 $url = \Piwik\Tracker\PageUrl::convertMatrixUrl($url);
258 return $url;
259 }
260 /**
261 * Build the full URL from the prefix ID and the rest.
262 *
263 * @param string $url
264 * @param integer $prefixId
265 * @return string
266 */
267 public static function reconstructNormalizedUrl($url, $prefixId)
268 {
269 $map = array_flip(self::$urlPrefixMap);
270 if ($prefixId !== null && isset($map[$prefixId])) {
271 $fullUrl = $map[$prefixId] . $url;
272 } else {
273 $fullUrl = $url;
274 }
275 // Clean up host & hash tags, for URLs
276 $parsedUrl = @parse_url($fullUrl);
277 $parsedUrl = \Piwik\Tracker\PageUrl::cleanupHostAndHashTag($parsedUrl);
278 $url = UrlHelper::getParseUrlReverse($parsedUrl);
279 if (!empty($url)) {
280 return $url;
281 }
282 return $fullUrl;
283 }
284 /**
285 * Returns if the given host is also configured as https in page urls of given site
286 *
287 * @param $idSite
288 * @param $host
289 * @return false|mixed
290 * @throws \Exception
291 */
292 public static function shouldUseHttpsHost($idSite, $host)
293 {
294 $cache = Cache::getTransientCache();
295 $cacheKeySiteUrls = CacheId::siteAware('siteurls', [$idSite]);
296 $cacheKeyHttpsForHost = CacheId::siteAware(sprintf('shouldusehttps-%s', $host), [$idSite]);
297 $siteUrlCache = $cache->fetch($cacheKeySiteUrls);
298 if (empty($siteUrlCache)) {
299 $siteUrlCache = APISitesManager::getInstance()->getSiteUrlsFromId($idSite);
300 $cache->save($cacheKeySiteUrls, $siteUrlCache);
301 }
302 if (!$cache->contains($cacheKeyHttpsForHost)) {
303 $hostSiteCache = \false;
304 foreach ($siteUrlCache as $siteUrl) {
305 if (strpos(mb_strtolower($siteUrl), mb_strtolower('https://' . $host)) === 0) {
306 $hostSiteCache = \true;
307 break;
308 }
309 }
310 $cache->save($cacheKeyHttpsForHost, $hostSiteCache);
311 }
312 return $cache->fetch($cacheKeyHttpsForHost);
313 }
314 /**
315 * Extract the prefix from a URL.
316 * Return the prefix ID and the rest.
317 *
318 * @param string $url
319 * @return array
320 */
321 public static function normalizeUrl($url)
322 {
323 foreach (self::$urlPrefixMap as $prefix => $id) {
324 if (strtolower(substr($url, 0, strlen($prefix))) == $prefix) {
325 return array('url' => substr($url, strlen($prefix)), 'prefixId' => $id);
326 }
327 }
328 return array('url' => $url, 'prefixId' => null);
329 }
330 public static function getUrlIfLookValid($url)
331 {
332 $url = \Piwik\Tracker\PageUrl::cleanupString($url);
333 if (!UrlHelper::isLookLikeUrl($url)) {
334 Common::printDebug("WARNING: URL looks invalid and is discarded");
335 return \false;
336 }
337 return $url;
338 }
339 private static function getExcludedParametersFromWebsite($website)
340 {
341 if (isset($website['excluded_parameters'])) {
342 return $website['excluded_parameters'];
343 }
344 return array();
345 }
346 public static function urldecodeValidUtf8($value)
347 {
348 $value = urldecode($value);
349 if (function_exists('mb_check_encoding') && !@mb_check_encoding($value, 'utf-8')) {
350 return urlencode($value);
351 }
352 return $value;
353 }
354 }
355