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