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