PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.3.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.3.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 / TableLogAction.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
TableLogAction.php
294 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\Container\StaticContainer;
14 use Piwik\Segment\SegmentExpression;
15
16 /**
17 * This class is used to query Action IDs from the log_action table.
18 *
19 * A pageview, outlink, download or site search are made of several "Action IDs"
20 * For example pageview is idaction_url and idaction_name.
21 *
22 */
23 class TableLogAction
24 {
25 /**
26 * This function will find the idaction from the lookup table log_action,
27 * given an Action name, type, and an optional URL Prefix.
28 *
29 * This is used to record Page URLs, Page Titles, Ecommerce items SKUs, item names, item categories
30 *
31 * If the action name does not exist in the lookup table, it will INSERT it
32 * @param array $actionsNameAndType Array of one or many (name,type)
33 * @return array Returns the an array (Field name => idaction)
34 */
35 public static function loadIdsAction($actionsNameAndType)
36 {
37 // Add url prefix if not set
38 foreach ($actionsNameAndType as &$action) {
39 if (2 == count($action)) {
40 $action[] = null;
41 }
42 }
43
44 $actionIds = self::queryIdsAction($actionsNameAndType);
45
46 [$queriedIds, $fieldNamesToInsert] = self::processIdsToInsert($actionsNameAndType, $actionIds);
47
48 $insertedIds = self::insertNewIdsAction($actionsNameAndType, $fieldNamesToInsert);
49 $queriedIds = $queriedIds + $insertedIds;
50
51 return $queriedIds;
52 }
53
54 /**
55 * @param $matchType
56 * @param $actionType
57 * @return string
58 * @throws \Exception
59 */
60 private static function getSelectQueryWhereNameContains($matchType, $actionType)
61 {
62 // now, we handle the cases =@ (contains) and !@ (does not contain)
63 // build the expression based on the match type
64 $sql = 'SELECT idaction FROM ' . Common::prefixTable('log_action') . ' WHERE %s AND type = ' . $actionType . ' )';
65
66 switch ($matchType) {
67 case SegmentExpression::MATCH_CONTAINS:
68 // use concat to make sure, no %s occurs because some plugins use %s in their sql
69 $where = '( name LIKE CONCAT(\'%\', ?, \'%\') ';
70 break;
71 case SegmentExpression::MATCH_DOES_NOT_CONTAIN:
72 $where = '( name NOT LIKE CONCAT(\'%\', ?, \'%\') ';
73 break;
74 case SegmentExpression::MATCH_STARTS_WITH:
75 // use concat to make sure, no %s occurs because some plugins use %s in their sql
76 $where = '( name LIKE CONCAT(?, \'%\') ';
77 break;
78 case SegmentExpression::MATCH_ENDS_WITH:
79 // use concat to make sure, no %s occurs because some plugins use %s in their sql
80 $where = '( name LIKE CONCAT(\'%\', ?) ';
81 break;
82 default:
83 throw new \Exception("This match type $matchType is not available for action-segments.");
84 break;
85 }
86
87 $sql = sprintf($sql, $where);
88
89 return $sql;
90 }
91
92 private static function insertNewIdsAction($actionsNameAndType, $fieldNamesToInsert)
93 {
94 // Then, we insert all new actions in the lookup table
95 $inserted = array();
96
97 foreach ($fieldNamesToInsert as $fieldName) {
98 [$name, $type, $urlPrefix] = $actionsNameAndType[$fieldName];
99
100 $actionId = self::getModel()->createNewIdAction($name, $type, $urlPrefix);
101
102 Common::printDebug("Recorded a new action (" . Action::getTypeAsString($type) . ") in the lookup table: " . $name . " (idaction = " . $actionId . ")");
103
104 $inserted[$fieldName] = $actionId;
105 }
106
107 return $inserted;
108 }
109
110 private static function getModel()
111 {
112 return new Model();
113 }
114
115 private static function queryIdsAction($actionsNameAndType)
116 {
117 $toQuery = array();
118 foreach ($actionsNameAndType as &$actionNameType) {
119 [$name, $type, $urlPrefix] = $actionNameType;
120 $toQuery[] = array('name' => $name, 'type' => $type);
121 }
122
123 $actionIds = self::getModel()->getIdsAction($toQuery);
124
125 return $actionIds;
126 }
127
128 private static function processIdsToInsert($actionsNameAndType, $actionIds)
129 {
130 // For the Actions found in the lookup table, add the idaction in the array,
131 // If not found in lookup table, queue for INSERT
132 $fieldNamesToInsert = $fieldNameToActionId = array();
133
134 foreach ($actionsNameAndType as $fieldName => &$actionNameType) {
135 @list($name, $type, $urlPrefix) = $actionNameType;
136 if (empty($name)) {
137 $fieldNameToActionId[$fieldName] = false;
138 continue;
139 }
140
141 $found = false;
142 foreach ($actionIds as $row) {
143 if ($name == $row['name']
144 && $type == $row['type']
145 ) {
146 $found = true;
147
148 $fieldNameToActionId[$fieldName] = $row['idaction'];
149 continue;
150 }
151 }
152 if (!$found) {
153 $fieldNamesToInsert[] = $fieldName;
154 }
155 }
156
157 return array($fieldNameToActionId, $fieldNamesToInsert);
158 }
159
160 /**
161 * Convert segment expression to an action ID or an SQL expression.
162 *
163 * This method is used as a sqlFilter-callback for the segments of this plugin.
164 * Usually, these callbacks only return a value that should be compared to the
165 * column in the database. In this case, that doesn't work since multiple IDs
166 * can match an expression (e.g. "pageUrl=@foo").
167 * @param string $valueToMatch
168 * @param string $sqlField
169 * @param string $matchType
170 * @param string $segmentName
171 * @throws \Exception
172 * @return array|int|string
173 */
174 public static function getIdActionFromSegment($valueToMatch, $sqlField, $matchType, $segmentName)
175 {
176 if ($segmentName === 'actionType') {
177 $actionType = (int) $valueToMatch;
178 $valueToMatch = array();
179 $sql = 'SELECT idaction FROM ' . Common::prefixTable('log_action') . ' WHERE type = ' . $actionType . ' )';
180 } else {
181 $actionType = self::guessActionTypeFromSegment($segmentName);
182 if ($actionType == Action::TYPE_PAGE_URL || $segmentName == 'eventUrl') {
183 // for urls trim protocol and www because it is not recorded in the db
184 $valueToMatch = preg_replace('@^http[s]?://(www\.)?@i', '', $valueToMatch);
185 }
186
187 $unsanitizedValue = $valueToMatch;
188 $valueToMatch = self::normaliseActionString($actionType, $valueToMatch);
189 if ($matchType == SegmentExpression::MATCH_EQUAL
190 || $matchType == SegmentExpression::MATCH_NOT_EQUAL
191 ) {
192 $idAction = self::getModel()->getIdActionMatchingNameAndType($valueToMatch, $actionType);
193 // If action can't be found normalized try search for it with original value
194 // This can eg happen for outlinks that contain a &amp; see https://github.com/matomo-org/matomo/issues/11806
195 if (empty($idAction)) {
196 $idAction = self::getModel()->getIdActionMatchingNameAndType($unsanitizedValue, $actionType);
197 // Action is not found (eg. &segment=pageTitle==Větrnásssssss)
198 if (empty($idAction)) {
199 $idAction = null;
200 }
201 }
202 return $idAction;
203 }
204
205 // "name contains $string" match can match several idaction so we cannot return yet an idaction
206 // special case
207 $sql = self::getSelectQueryWhereNameContains($matchType, $actionType);
208 }
209
210
211 $cache = StaticContainer::get('Piwik\Tracker\TableLogAction\Cache');
212 return $cache->getIdActionFromSegment($valueToMatch, $sql);
213 }
214
215 /**
216 * @param $segmentName
217 * @return int
218 * @throws \Exception
219 */
220 private static function guessActionTypeFromSegment($segmentName)
221 {
222 $exactMatch = array(
223 'outlinkUrl' => Action::TYPE_OUTLINK,
224 'downloadUrl' => Action::TYPE_DOWNLOAD,
225 'eventUrl' => Action::TYPE_EVENT,
226 'eventAction' => Action::TYPE_EVENT_ACTION,
227 'eventCategory' => Action::TYPE_EVENT_CATEGORY,
228 'eventName' => Action::TYPE_EVENT_NAME,
229 'contentPiece' => Action::TYPE_CONTENT_PIECE,
230 'contentTarget' => Action::TYPE_CONTENT_TARGET,
231 'contentName' => Action::TYPE_CONTENT_NAME,
232 'contentInteraction' => Action::TYPE_CONTENT_INTERACTION,
233 'productName' => Action::TYPE_ECOMMERCE_ITEM_NAME,
234 'productSku' => Action::TYPE_ECOMMERCE_ITEM_SKU,
235 'productViewName' => Action::TYPE_ECOMMERCE_ITEM_NAME,
236 'productViewSku' => Action::TYPE_ECOMMERCE_ITEM_SKU
237 );
238
239 if (!empty($exactMatch[$segmentName])) {
240 return $exactMatch[$segmentName];
241 }
242
243 if (stripos($segmentName, 'pageurl') !== false) {
244 return Action::TYPE_PAGE_URL;
245 } elseif (stripos($segmentName, 'pagetitle') !== false) {
246 return Action::TYPE_PAGE_TITLE;
247 } elseif (stripos($segmentName, 'sitesearch') !== false) {
248 return Action::TYPE_SITE_SEARCH;
249 } elseif (stripos($segmentName, 'productcategory') !== false
250 || stripos($segmentName, 'productviewcategory') !== false) {
251 return Action::TYPE_ECOMMERCE_ITEM_CATEGORY;
252 } else {
253 throw new \Exception("We cannot guess the action type from the segment $segmentName.");
254 }
255 }
256
257 /**
258 * This function will sanitize or not if it's needed for the specified action type
259 *
260 * URLs (Download URL, Outlink URL) are stored raw (unsanitized)
261 * while other action types are stored Sanitized
262 *
263 * @param $actionType
264 * @param $actionString
265 * @return string
266 */
267 private static function normaliseActionString($actionType, $actionString)
268 {
269 $actionString = Common::unsanitizeInputValue($actionString);
270
271 if (self::isActionTypeStoredUnsanitized($actionType)) {
272 return $actionString;
273 }
274
275 return Common::sanitizeInputValue($actionString);
276 }
277
278 /**
279 * @param $actionType
280 * @return bool
281 */
282 private static function isActionTypeStoredUnsanitized($actionType)
283 {
284 $actionsTypesStoredUnsanitized = array(
285 Action::TYPE_DOWNLOAD,
286 Action::TYPE_OUTLINK,
287 Action::TYPE_PAGE_URL,
288 Action::TYPE_CONTENT,
289 );
290
291 return in_array($actionType, $actionsTypesStoredUnsanitized);
292 }
293 }
294