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