API
1 month ago
Access
3 months ago
Application
1 month ago
Archive
1 month ago
ArchiveProcessor
1 month ago
Archiver
2 years ago
AssetManager
1 month ago
Auth
6 months ago
Category
6 months ago
Changes
1 month ago
CliMulti
1 year ago
Columns
1 month ago
Concurrency
1 month ago
Config
1 month ago
Container
1 month ago
CronArchive
3 months ago
DataAccess
1 month ago
DataFiles
2 years ago
DataTable
2 weeks ago
Db
2 weeks ago
DeviceDetector
1 year ago
Email
2 years ago
Exception
4 months ago
Http
4 months ago
Intl
3 months ago
Log
2 years ago
Mail
1 year ago
Measurable
6 months ago
Menu
1 month ago
Metrics
3 months ago
Notification
6 months ago
Period
1 month ago
Plugin
2 weeks ago
Policy
1 month ago
ProfessionalServices
1 year ago
Report
1 year ago
ReportRenderer
3 months ago
Request
3 months ago
Scheduler
1 month ago
Segment
1 month ago
Session
2 weeks ago
Settings
1 month ago
Tracker
2 weeks ago
Translation
1 month ago
Twig
1 year ago
UpdateCheck
3 months ago
Updater
1 month ago
Updates
3 days ago
Validators
1 year ago
View
1 month ago
ViewDataTable
2 weeks ago
Visualization
1 year ago
Widget
1 month ago
.htaccess
2 years ago
Access.php
1 month ago
Archive.php
1 month ago
ArchiveProcessor.php
1 month ago
AssetManager.php
1 month ago
Auth.php
6 months ago
AuthResult.php
6 months ago
BaseFactory.php
2 years ago
Cache.php
2 years ago
CacheId.php
4 months ago
CliMulti.php
1 month ago
Common.php
2 weeks ago
Config.php
1 month ago
Console.php
3 months ago
Context.php
2 years ago
Cookie.php
1 year ago
CronArchive.php
1 month ago
DI.php
3 months ago
DataArray.php
1 month ago
DataTable.php
1 month ago
Date.php
1 month ago
Db.php
1 month ago
DbHelper.php
1 month ago
Development.php
1 year ago
ErrorHandler.php
6 months ago
EventDispatcher.php
1 month ago
ExceptionHandler.php
4 months ago
FileIntegrity.php
1 month ago
Filechecks.php
1 year ago
Filesystem.php
1 month ago
FrontController.php
4 months ago
Http.php
1 month ago
IP.php
1 year ago
Log.php
3 months ago
LogDeleter.php
1 year ago
Mail.php
1 year ago
Metrics.php
1 month ago
NoAccessException.php
2 years ago
Nonce.php
6 months ago
Notification.php
1 month ago
NumberFormatter.php
5 months ago
Option.php
5 months ago
Period.php
1 month ago
Piwik.php
1 month ago
Plugin.php
1 month ago
Process.php
1 month ago
Profiler.php
6 months ago
ProxyHeaders.php
4 months ago
ProxyHttp.php
5 months ago
QuickForm2.php
3 months ago
RankingQuery.php
1 month ago
ReportRenderer.php
1 month ago
Request.php
1 month ago
Segment.php
1 month ago
Sequence.php
6 months ago
Session.php
2 weeks ago
SettingsPiwik.php
1 month ago
SettingsServer.php
1 year ago
Singleton.php
2 years ago
Site.php
1 month ago
SiteContentDetector.php
1 month ago
SupportedBrowser.php
2 years ago
TCPDF.php
1 year ago
Theme.php
1 year ago
Timer.php
1 month ago
Tracker.php
1 month ago
Twig.php
1 month ago
Unzip.php
1 year ago
UpdateCheck.php
1 month ago
Updater.php
1 month ago
UpdaterErrorException.php
2 years ago
Updates.php
3 months ago
Url.php
3 months ago
UrlHelper.php
1 month ago
Version.php
3 days ago
View.php
1 month ago
bootstrap.php
1 year ago
dispatch.php
2 years ago
testMinimumPhpVersion.php
6 months ago
Metrics.php
485 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; |
| 10 | |
| 11 | use Piwik\Cache as PiwikCache; |
| 12 | use Piwik\Columns\Dimension; |
| 13 | use Piwik\Tracker\GoalManager; |
| 14 | require_once PIWIK_INCLUDE_PATH . "/core/Piwik.php"; |
| 15 | /** |
| 16 | * This class contains metadata regarding core metrics and contains several |
| 17 | * related helper functions. |
| 18 | * |
| 19 | * Of note are the `INDEX_...` constants. In the database, metric column names |
| 20 | * in {@link DataTable} rows are stored as integers to save space. The integer |
| 21 | * values used are determined by these constants. |
| 22 | * |
| 23 | * @api |
| 24 | */ |
| 25 | class Metrics |
| 26 | { |
| 27 | /* |
| 28 | * When saving DataTables in the DB, we replace all columns name with these IDs. This saves many bytes, |
| 29 | * eg. INDEX_NB_UNIQ_VISITORS is an integer: 4 bytes, but 'nb_uniq_visitors' is 16 bytes at least |
| 30 | */ |
| 31 | public const INDEX_NB_UNIQ_VISITORS = 1; |
| 32 | public const INDEX_NB_VISITS = 2; |
| 33 | public const INDEX_NB_ACTIONS = 3; |
| 34 | public const INDEX_MAX_ACTIONS = 4; |
| 35 | public const INDEX_SUM_VISIT_LENGTH = 5; |
| 36 | public const INDEX_BOUNCE_COUNT = 6; |
| 37 | public const INDEX_NB_VISITS_CONVERTED = 7; |
| 38 | public const INDEX_NB_CONVERSIONS = 8; |
| 39 | public const INDEX_REVENUE = 9; |
| 40 | public const INDEX_GOALS = 10; |
| 41 | public const INDEX_SUM_DAILY_NB_UNIQ_VISITORS = 11; |
| 42 | // Specific to the Actions reports |
| 43 | public const INDEX_PAGE_NB_HITS = 12; |
| 44 | public const INDEX_PAGE_SUM_TIME_SPENT = 13; |
| 45 | public const INDEX_PAGE_EXIT_NB_UNIQ_VISITORS = 14; |
| 46 | public const INDEX_PAGE_EXIT_NB_VISITS = 15; |
| 47 | public const INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS = 16; |
| 48 | public const INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS = 17; |
| 49 | public const INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS = 18; |
| 50 | public const INDEX_PAGE_ENTRY_NB_VISITS = 19; |
| 51 | public const INDEX_PAGE_ENTRY_NB_ACTIONS = 20; |
| 52 | public const INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH = 21; |
| 53 | public const INDEX_PAGE_ENTRY_BOUNCE_COUNT = 22; |
| 54 | // Ecommerce Items reports |
| 55 | public const INDEX_ECOMMERCE_ITEM_REVENUE = 23; |
| 56 | public const INDEX_ECOMMERCE_ITEM_QUANTITY = 24; |
| 57 | public const INDEX_ECOMMERCE_ITEM_PRICE = 25; |
| 58 | public const INDEX_ECOMMERCE_ORDERS = 26; |
| 59 | public const INDEX_ECOMMERCE_ITEM_PRICE_VIEWED = 27; |
| 60 | // Site Search |
| 61 | public const INDEX_SITE_SEARCH_HAS_NO_RESULT = 28; |
| 62 | public const INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS = 29; |
| 63 | // Performance Analytics |
| 64 | public const INDEX_PAGE_SUM_TIME_GENERATION = 30; |
| 65 | public const INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION = 31; |
| 66 | public const INDEX_PAGE_MIN_TIME_GENERATION = 32; |
| 67 | public const INDEX_PAGE_MAX_TIME_GENERATION = 33; |
| 68 | // Events |
| 69 | public const INDEX_EVENT_NB_HITS = 34; |
| 70 | public const INDEX_EVENT_SUM_EVENT_VALUE = 35; |
| 71 | public const INDEX_EVENT_MIN_EVENT_VALUE = 36; |
| 72 | public const INDEX_EVENT_MAX_EVENT_VALUE = 37; |
| 73 | public const INDEX_EVENT_NB_HITS_WITH_VALUE = 38; |
| 74 | // Number of unique User IDs |
| 75 | public const INDEX_NB_USERS = 39; |
| 76 | public const INDEX_SUM_DAILY_NB_USERS = 40; |
| 77 | // Contents |
| 78 | public const INDEX_CONTENT_NB_IMPRESSIONS = 41; |
| 79 | public const INDEX_CONTENT_NB_INTERACTIONS = 42; |
| 80 | // Unique visitors fingerprints (useful to process unique visitors across websites) |
| 81 | public const INDEX_NB_UNIQ_FINGERPRINTS = 43; |
| 82 | // Total number of hits |
| 83 | public const INDEX_NB_HITS = 44; |
| 84 | // Goal reports |
| 85 | public const INDEX_GOAL_NB_CONVERSIONS = 1; |
| 86 | public const INDEX_GOAL_REVENUE = 2; |
| 87 | public const INDEX_GOAL_NB_VISITS_CONVERTED = 3; |
| 88 | public const INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL = 4; |
| 89 | public const INDEX_GOAL_ECOMMERCE_REVENUE_TAX = 5; |
| 90 | public const INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING = 6; |
| 91 | public const INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT = 7; |
| 92 | public const INDEX_GOAL_ECOMMERCE_ITEMS = 8; |
| 93 | public const INDEX_GOAL_NB_PAGES_UNIQ_BEFORE = 9; |
| 94 | public const INDEX_GOAL_NB_CONVERSIONS_ATTRIB = 10; |
| 95 | public const INDEX_GOAL_NB_CONVERSIONS_PAGE_RATE = 11; |
| 96 | public const INDEX_GOAL_NB_CONVERSIONS_PAGE_UNIQ = 12; |
| 97 | public const INDEX_GOAL_NB_CONVERSIONS_ENTRY_RATE = 13; |
| 98 | public const INDEX_GOAL_REVENUE_PER_ENTRY = 14; |
| 99 | public const INDEX_GOAL_REVENUE_ATTRIB = 15; |
| 100 | public const INDEX_GOAL_NB_CONVERSIONS_ENTRY = 16; |
| 101 | public const INDEX_GOAL_REVENUE_ENTRY = 17; |
| 102 | /** |
| 103 | * @var string[] |
| 104 | */ |
| 105 | public static $mappingFromIdToName = [ |
| 106 | \Piwik\Metrics::INDEX_NB_UNIQ_VISITORS => 'nb_uniq_visitors', |
| 107 | \Piwik\Metrics::INDEX_NB_UNIQ_FINGERPRINTS => 'nb_uniq_fingerprints', |
| 108 | \Piwik\Metrics::INDEX_NB_VISITS => 'nb_visits', |
| 109 | \Piwik\Metrics::INDEX_NB_ACTIONS => 'nb_actions', |
| 110 | \Piwik\Metrics::INDEX_NB_USERS => 'nb_users', |
| 111 | \Piwik\Metrics::INDEX_MAX_ACTIONS => 'max_actions', |
| 112 | \Piwik\Metrics::INDEX_SUM_VISIT_LENGTH => 'sum_visit_length', |
| 113 | \Piwik\Metrics::INDEX_BOUNCE_COUNT => 'bounce_count', |
| 114 | \Piwik\Metrics::INDEX_NB_VISITS_CONVERTED => 'nb_visits_converted', |
| 115 | \Piwik\Metrics::INDEX_NB_CONVERSIONS => 'nb_conversions', |
| 116 | \Piwik\Metrics::INDEX_REVENUE => 'revenue', |
| 117 | \Piwik\Metrics::INDEX_GOALS => 'goals', |
| 118 | \Piwik\Metrics::INDEX_SUM_DAILY_NB_UNIQ_VISITORS => 'sum_daily_nb_uniq_visitors', |
| 119 | \Piwik\Metrics::INDEX_SUM_DAILY_NB_USERS => 'sum_daily_nb_users', |
| 120 | \Piwik\Metrics::INDEX_NB_HITS => 'hits', |
| 121 | // Actions metrics |
| 122 | \Piwik\Metrics::INDEX_PAGE_NB_HITS => 'nb_hits', |
| 123 | \Piwik\Metrics::INDEX_PAGE_SUM_TIME_SPENT => 'sum_time_spent', |
| 124 | \Piwik\Metrics::INDEX_PAGE_SUM_TIME_GENERATION => 'sum_time_generation', |
| 125 | \Piwik\Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION => 'nb_hits_with_time_generation', |
| 126 | \Piwik\Metrics::INDEX_PAGE_MIN_TIME_GENERATION => 'min_time_generation', |
| 127 | \Piwik\Metrics::INDEX_PAGE_MAX_TIME_GENERATION => 'max_time_generation', |
| 128 | \Piwik\Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => 'exit_nb_uniq_visitors', |
| 129 | \Piwik\Metrics::INDEX_PAGE_EXIT_NB_VISITS => 'exit_nb_visits', |
| 130 | \Piwik\Metrics::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS => 'sum_daily_exit_nb_uniq_visitors', |
| 131 | \Piwik\Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => 'entry_nb_uniq_visitors', |
| 132 | \Piwik\Metrics::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS => 'sum_daily_entry_nb_uniq_visitors', |
| 133 | \Piwik\Metrics::INDEX_PAGE_ENTRY_NB_VISITS => 'entry_nb_visits', |
| 134 | \Piwik\Metrics::INDEX_PAGE_ENTRY_NB_ACTIONS => 'entry_nb_actions', |
| 135 | \Piwik\Metrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH => 'entry_sum_visit_length', |
| 136 | \Piwik\Metrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT => 'entry_bounce_count', |
| 137 | \Piwik\Metrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS => 'nb_hits_following_search', |
| 138 | // Items reports metrics |
| 139 | \Piwik\Metrics::INDEX_ECOMMERCE_ITEM_REVENUE => 'revenue', |
| 140 | \Piwik\Metrics::INDEX_ECOMMERCE_ITEM_QUANTITY => 'quantity', |
| 141 | \Piwik\Metrics::INDEX_ECOMMERCE_ITEM_PRICE => 'price', |
| 142 | \Piwik\Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED => 'price_viewed', |
| 143 | \Piwik\Metrics::INDEX_ECOMMERCE_ORDERS => 'orders', |
| 144 | // Events |
| 145 | \Piwik\Metrics::INDEX_EVENT_NB_HITS => 'nb_events', |
| 146 | \Piwik\Metrics::INDEX_EVENT_SUM_EVENT_VALUE => 'sum_event_value', |
| 147 | \Piwik\Metrics::INDEX_EVENT_MIN_EVENT_VALUE => 'min_event_value', |
| 148 | \Piwik\Metrics::INDEX_EVENT_MAX_EVENT_VALUE => 'max_event_value', |
| 149 | \Piwik\Metrics::INDEX_EVENT_NB_HITS_WITH_VALUE => 'nb_events_with_value', |
| 150 | // Contents |
| 151 | \Piwik\Metrics::INDEX_CONTENT_NB_IMPRESSIONS => 'nb_impressions', |
| 152 | \Piwik\Metrics::INDEX_CONTENT_NB_INTERACTIONS => 'nb_interactions', |
| 153 | ]; |
| 154 | /** |
| 155 | * @var string[] |
| 156 | */ |
| 157 | public static $mappingFromIdToNameGoal = [\Piwik\Metrics::INDEX_GOAL_NB_CONVERSIONS => 'nb_conversions', \Piwik\Metrics::INDEX_GOAL_NB_VISITS_CONVERTED => 'nb_visits_converted', \Piwik\Metrics::INDEX_GOAL_REVENUE => 'revenue', \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL => 'revenue_subtotal', \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_REVENUE_TAX => 'revenue_tax', \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING => 'revenue_shipping', \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT => 'revenue_discount', \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_ITEMS => 'items', \Piwik\Metrics::INDEX_GOAL_NB_PAGES_UNIQ_BEFORE => 'nb_conv_pages_before', \Piwik\Metrics::INDEX_GOAL_NB_CONVERSIONS_ATTRIB => 'nb_conversions_attrib', \Piwik\Metrics::INDEX_GOAL_NB_CONVERSIONS_PAGE_RATE => 'nb_conversions_page_rate', \Piwik\Metrics::INDEX_GOAL_NB_CONVERSIONS_PAGE_UNIQ => 'nb_conversions_page_uniq', \Piwik\Metrics::INDEX_GOAL_NB_CONVERSIONS_ENTRY_RATE => 'nb_conversions_entry_rate', \Piwik\Metrics::INDEX_GOAL_REVENUE_PER_ENTRY => 'revenue_per_entry', \Piwik\Metrics::INDEX_GOAL_REVENUE_ATTRIB => 'revenue_attrib', \Piwik\Metrics::INDEX_GOAL_NB_CONVERSIONS_ENTRY => 'nb_conversions_entry', \Piwik\Metrics::INDEX_GOAL_REVENUE_ENTRY => 'revenue_entry']; |
| 158 | /** |
| 159 | * @var int[] |
| 160 | */ |
| 161 | protected static $metricsAggregatedFromLogs = [\Piwik\Metrics::INDEX_NB_UNIQ_VISITORS, \Piwik\Metrics::INDEX_NB_VISITS, \Piwik\Metrics::INDEX_NB_ACTIONS, \Piwik\Metrics::INDEX_NB_USERS, \Piwik\Metrics::INDEX_MAX_ACTIONS, \Piwik\Metrics::INDEX_SUM_VISIT_LENGTH, \Piwik\Metrics::INDEX_BOUNCE_COUNT, \Piwik\Metrics::INDEX_NB_VISITS_CONVERTED]; |
| 162 | /** |
| 163 | * @return array<int, string> |
| 164 | */ |
| 165 | public static function getMappingFromIdToName() |
| 166 | { |
| 167 | $cache = PiwikCache::getTransientCache(); |
| 168 | $cacheKey = \Piwik\CacheId::siteAware(\Piwik\CacheId::pluginAware('Metrics.mappingFromIdToName')); |
| 169 | /** @var array<int, string>|false $value */ |
| 170 | $value = $cache->fetch($cacheKey); |
| 171 | if (empty($value)) { |
| 172 | $value = self::$mappingFromIdToName; |
| 173 | /** |
| 174 | * Use this event if your plugin uses custom metric integer IDs to associate those IDs with the |
| 175 | * actual metric names (eg, 2 => nb_visits). This allows matomo to automate the replacing |
| 176 | * of IDs => metric names for your new metrics. |
| 177 | * |
| 178 | * **Example** |
| 179 | * |
| 180 | * public function addMetricIdToNameMapping(&$mapping) |
| 181 | * { |
| 182 | * $mapping[Archiver::INDEX_MY_NEW_METRIC] = $mapping['MyPlugin_myNewMetric']; |
| 183 | * } |
| 184 | * |
| 185 | * @ignore |
| 186 | */ |
| 187 | \Piwik\Piwik::postEvent('Metrics.addMetricIdToNameMapping', [&$value]); |
| 188 | $cache->save($cacheKey, $value); |
| 189 | } |
| 190 | return $value; |
| 191 | } |
| 192 | /** |
| 193 | * @return array<int, string> |
| 194 | */ |
| 195 | public static function getVisitsMetricNames() |
| 196 | { |
| 197 | $names = []; |
| 198 | foreach (self::$metricsAggregatedFromLogs as $metricId) { |
| 199 | $names[$metricId] = self::$mappingFromIdToName[$metricId]; |
| 200 | } |
| 201 | return $names; |
| 202 | } |
| 203 | /** |
| 204 | * @return array<string, int> |
| 205 | */ |
| 206 | public static function getMappingFromNameToId() |
| 207 | { |
| 208 | static $nameToId = null; |
| 209 | if ($nameToId === null) { |
| 210 | $nameToId = array_flip(self::$mappingFromIdToName); |
| 211 | } |
| 212 | return $nameToId; |
| 213 | } |
| 214 | /** |
| 215 | * @return array<string, int> |
| 216 | */ |
| 217 | public static function getMappingFromNameToIdGoal() |
| 218 | { |
| 219 | static $nameToId = null; |
| 220 | if ($nameToId === null) { |
| 221 | $nameToId = array_flip(self::$mappingFromIdToNameGoal); |
| 222 | } |
| 223 | return $nameToId; |
| 224 | } |
| 225 | /** |
| 226 | * Is a lower value for a given column better? |
| 227 | * @param string $column |
| 228 | * @return bool |
| 229 | * |
| 230 | * @ignore |
| 231 | */ |
| 232 | public static function isLowerValueBetter($column) |
| 233 | { |
| 234 | $isLowerBetter = null; |
| 235 | /** |
| 236 | * Use this event to define if a lower value of a metric is better. |
| 237 | * |
| 238 | * @param string $isLowerBetter should be set to a boolean indicating if lower is better |
| 239 | * @param string $column name of the column to determine |
| 240 | * |
| 241 | * **Example** |
| 242 | * |
| 243 | * public function checkIsLowerMetricValueBetter(&$isLowerBetter, $metric) |
| 244 | * { |
| 245 | * if ($metric === 'position') { |
| 246 | * $isLowerBetter = true; |
| 247 | * } |
| 248 | * } |
| 249 | */ |
| 250 | \Piwik\Piwik::postEvent('Metrics.isLowerValueBetter', [&$isLowerBetter, $column]); |
| 251 | if (!is_null($isLowerBetter)) { |
| 252 | return \true; |
| 253 | } |
| 254 | $lowerIsBetterPatterns = array('bounce', 'exit'); |
| 255 | foreach ($lowerIsBetterPatterns as $pattern) { |
| 256 | if (strpos($column, $pattern) !== \false) { |
| 257 | return \true; |
| 258 | } |
| 259 | } |
| 260 | return \false; |
| 261 | } |
| 262 | /** |
| 263 | * Derive the unit name from a column name |
| 264 | * @param string $column |
| 265 | * @param string|int $idSite |
| 266 | * @return string |
| 267 | * @ignore |
| 268 | */ |
| 269 | public static function getUnit($column, $idSite) |
| 270 | { |
| 271 | $nameToUnit = array('_rate' => '%', 'revenue' => \Piwik\Site::getCurrencySymbolFor((int) $idSite), '_time_' => 's'); |
| 272 | /** @var string|null $unit */ |
| 273 | $unit = null; |
| 274 | /** |
| 275 | * Use this event to define units for custom metrics used in evolution graphs and row evolution only. |
| 276 | * |
| 277 | * @param string $unit should hold the unit (e.g. %, €, s or empty string) |
| 278 | * @param string $column name of the column to determine |
| 279 | * @param string $idSite id of the current site |
| 280 | */ |
| 281 | \Piwik\Piwik::postEvent('Metrics.getEvolutionUnit', [&$unit, $column, $idSite]); |
| 282 | if (!empty($unit)) { |
| 283 | return $unit; |
| 284 | } |
| 285 | foreach ($nameToUnit as $pattern => $type) { |
| 286 | if (strpos($column, $pattern) !== \false) { |
| 287 | return $type; |
| 288 | } |
| 289 | } |
| 290 | return ''; |
| 291 | } |
| 292 | /** |
| 293 | * @return array<string, string> |
| 294 | */ |
| 295 | public static function getDefaultMetricSemanticTypes() : array |
| 296 | { |
| 297 | $cacheId = \Piwik\CacheId::pluginAware('DefaultMetricSemanticTypes'); |
| 298 | $cache = PiwikCache::getTransientCache(); |
| 299 | /** @var array<string, string>|false $types */ |
| 300 | $types = $cache->fetch($cacheId); |
| 301 | if (empty($types)) { |
| 302 | $types = ['nb_visits' => Dimension::TYPE_NUMBER, 'nb_uniq_visitors' => Dimension::TYPE_NUMBER, 'nb_actions' => Dimension::TYPE_NUMBER, 'nb_users' => Dimension::TYPE_NUMBER, 'avg_time_on_page' => Dimension::TYPE_DURATION_S, 'sum_time_spent' => Dimension::TYPE_DURATION_S, 'sum_visit_length' => Dimension::TYPE_DURATION_S, 'bounce_count' => Dimension::TYPE_NUMBER, 'bounce_count_returning' => Dimension::TYPE_NUMBER, 'max_actions' => Dimension::TYPE_NUMBER, 'max_actions_returning' => Dimension::TYPE_NUMBER, 'nb_visits_converted_returning' => Dimension::TYPE_NUMBER, 'sum_visit_length_returning' => Dimension::TYPE_NUMBER, 'nb_visits_converted' => Dimension::TYPE_NUMBER, 'nb_conversions' => Dimension::TYPE_NUMBER, 'revenue' => Dimension::TYPE_MONEY, 'nb_hits' => Dimension::TYPE_NUMBER, 'hits' => Dimension::TYPE_NUMBER, 'entry_nb_visits' => Dimension::TYPE_NUMBER, 'entry_nb_uniq_visitors' => Dimension::TYPE_NUMBER, 'exit_nb_visits' => Dimension::TYPE_NUMBER, 'exit_nb_uniq_visitors' => Dimension::TYPE_NUMBER, 'entry_bounce_count' => Dimension::TYPE_NUMBER, 'exit_bounce_count' => Dimension::TYPE_NUMBER, 'exit_rate' => Dimension::TYPE_PERCENT, 'sum_daily_nb_uniq_visitors' => Dimension::TYPE_NUMBER, 'sum_daily_nb_users' => Dimension::TYPE_NUMBER, 'sum_daily_entry_nb_uniq_visitors' => Dimension::TYPE_NUMBER, 'sum_daily_exit_nb_uniq_visitors' => Dimension::TYPE_NUMBER, 'entry_nb_actions' => Dimension::TYPE_NUMBER, 'entry_sum_visit_length' => Dimension::TYPE_NUMBER, 'nb_actions_per_visit' => Dimension::TYPE_NUMBER, 'avg_time_on_site' => Dimension::TYPE_DURATION_S, 'bounce_rate' => Dimension::TYPE_PERCENT, 'conversion_rate' => Dimension::TYPE_PERCENT]; |
| 303 | /** |
| 304 | * Use this event to notify Matomo of the semantic types of metrics your plugin adds. |
| 305 | * |
| 306 | * A metric's semantic type is metadata used primarily in integrations with Matomo |
| 307 | * and third party services/applications. It provides information that can be used |
| 308 | * to determine how to display or use the information. |
| 309 | * |
| 310 | * It is recommended for your plugin to provide this information so users of third |
| 311 | * party services that connect with Matomo can make full use of the data your plugin |
| 312 | * tracks. |
| 313 | * |
| 314 | * See {@link Metrics} for the list of available semantic types. |
| 315 | * |
| 316 | * @param string $types The array mapping of metric_name => metric semantic type |
| 317 | */ |
| 318 | \Piwik\Piwik::postEvent('Metrics.getDefaultMetricSemanticTypes', [&$types]); |
| 319 | $cache->save($cacheId, $types); |
| 320 | } |
| 321 | return $types; |
| 322 | } |
| 323 | /** |
| 324 | * @return array<string, string> |
| 325 | */ |
| 326 | public static function getDefaultMetricTranslations() |
| 327 | { |
| 328 | $cacheId = \Piwik\CacheId::pluginAware('DefaultMetricTranslations'); |
| 329 | $cache = PiwikCache::getTransientCache(); |
| 330 | if ($cache->contains($cacheId)) { |
| 331 | /** @var array<string, string> */ |
| 332 | return $cache->fetch($cacheId); |
| 333 | } |
| 334 | $translations = array('label' => 'General_ColumnLabel', 'date' => 'General_Date', 'avg_time_on_page' => 'General_ColumnAverageTimeOnPage', 'sum_time_spent' => 'General_ColumnSumVisitLength', 'sum_visit_length' => 'General_ColumnSumVisitLength', 'bounce_count' => 'General_ColumnBounces', 'bounce_count_returning' => 'VisitFrequency_ColumnBounceCountForReturningVisits', 'max_actions' => 'General_ColumnMaxActions', 'max_actions_returning' => 'VisitFrequency_ColumnMaxActionsInReturningVisit', 'nb_visits_converted_returning' => 'VisitFrequency_ColumnNbReturningVisitsConverted', 'sum_visit_length_returning' => 'VisitFrequency_ColumnSumVisitLengthReturning', 'nb_visits_converted' => 'General_ColumnVisitsWithConversions', 'nb_conversions' => 'Goals_ColumnConversions', 'revenue' => 'General_ColumnRevenue', 'nb_hits' => 'General_ColumnPageviews', 'hits' => 'General_ColumnHits', 'entry_nb_visits' => 'General_ColumnEntrances', 'entry_nb_uniq_visitors' => 'General_ColumnUniqueEntrances', 'exit_nb_visits' => 'General_ColumnExits', 'exit_nb_uniq_visitors' => 'General_ColumnUniqueExits', 'entry_bounce_count' => 'General_ColumnBounces', 'exit_bounce_count' => 'General_ColumnBounces', 'exit_rate' => 'General_ColumnExitRate'); |
| 335 | $dailySum = ' (' . \Piwik\Piwik::translate('General_DailySum') . ')'; |
| 336 | $afterEntry = ' ' . \Piwik\Piwik::translate('General_AfterEntry'); |
| 337 | $translations['sum_daily_nb_uniq_visitors'] = \Piwik\Piwik::translate('General_ColumnNbUniqVisitors') . $dailySum; |
| 338 | $translations['sum_daily_nb_users'] = \Piwik\Piwik::translate('General_ColumnNbUsers') . $dailySum; |
| 339 | $translations['sum_daily_entry_nb_uniq_visitors'] = \Piwik\Piwik::translate('General_ColumnUniqueEntrances') . $dailySum; |
| 340 | $translations['sum_daily_exit_nb_uniq_visitors'] = \Piwik\Piwik::translate('General_ColumnUniqueExits') . $dailySum; |
| 341 | $translations['entry_nb_actions'] = \Piwik\Piwik::translate('General_ColumnNbActions') . $afterEntry; |
| 342 | $translations['entry_sum_visit_length'] = \Piwik\Piwik::translate('General_ColumnSumVisitLength') . $afterEntry; |
| 343 | $translations = array_merge(self::getDefaultMetrics(), self::getDefaultProcessedMetrics(), $translations); |
| 344 | /** |
| 345 | * Use this event to register translations for metrics processed by your plugin. |
| 346 | * |
| 347 | * @param string $translations The array mapping of column_name => Plugin_TranslationForColumn |
| 348 | */ |
| 349 | \Piwik\Piwik::postEvent('Metrics.getDefaultMetricTranslations', array(&$translations)); |
| 350 | $translations = array_map(array('\\Piwik\\Piwik', 'translate'), $translations); |
| 351 | $cache->save($cacheId, $translations); |
| 352 | return $translations; |
| 353 | } |
| 354 | /** |
| 355 | * @return array<string, string> |
| 356 | */ |
| 357 | public static function getDefaultMetrics() |
| 358 | { |
| 359 | $cacheId = \Piwik\CacheId::languageAware('DefaultMetrics'); |
| 360 | $cache = PiwikCache::getTransientCache(); |
| 361 | if ($cache->contains($cacheId)) { |
| 362 | /** @var array<string, string> */ |
| 363 | return $cache->fetch($cacheId); |
| 364 | } |
| 365 | $translations = array('nb_visits' => 'General_ColumnNbVisits', 'nb_uniq_visitors' => 'General_ColumnNbUniqVisitors', 'nb_actions' => 'General_ColumnNbActions', 'nb_users' => 'General_ColumnNbUsers'); |
| 366 | $translations = array_map(array('\\Piwik\\Piwik', 'translate'), $translations); |
| 367 | $cache->save($cacheId, $translations); |
| 368 | return $translations; |
| 369 | } |
| 370 | /** |
| 371 | * @return array<string, string> |
| 372 | */ |
| 373 | public static function getDefaultProcessedMetrics() |
| 374 | { |
| 375 | $cacheId = \Piwik\CacheId::languageAware('DefaultProcessedMetrics'); |
| 376 | $cache = PiwikCache::getTransientCache(); |
| 377 | if ($cache->contains($cacheId)) { |
| 378 | /** @var array<string, string> */ |
| 379 | return $cache->fetch($cacheId); |
| 380 | } |
| 381 | $translations = array( |
| 382 | // Processed in AddColumnsProcessedMetrics |
| 383 | 'nb_actions_per_visit' => 'General_ColumnActionsPerVisit', |
| 384 | 'avg_time_on_site' => 'General_ColumnAvgTimeOnSite', |
| 385 | 'bounce_rate' => 'General_ColumnBounceRate', |
| 386 | 'conversion_rate' => 'General_ColumnConversionRate', |
| 387 | ); |
| 388 | $translations = array_map(array('\\Piwik\\Piwik', 'translate'), $translations); |
| 389 | $cache->save($cacheId, $translations); |
| 390 | return $translations; |
| 391 | } |
| 392 | /** |
| 393 | * @param int|string $columnIdRaw |
| 394 | * @return int|string |
| 395 | */ |
| 396 | public static function getReadableColumnName($columnIdRaw) |
| 397 | { |
| 398 | $mappingIdToName = self::$mappingFromIdToName; |
| 399 | if (array_key_exists($columnIdRaw, $mappingIdToName)) { |
| 400 | return $mappingIdToName[$columnIdRaw]; |
| 401 | } |
| 402 | return $columnIdRaw; |
| 403 | } |
| 404 | /** |
| 405 | * @return int[] |
| 406 | */ |
| 407 | public static function getMetricIdsToProcessReportTotal() |
| 408 | { |
| 409 | return [self::INDEX_NB_VISITS, self::INDEX_NB_UNIQ_VISITORS, self::INDEX_NB_ACTIONS, self::INDEX_NB_HITS, self::INDEX_PAGE_NB_HITS, self::INDEX_NB_VISITS_CONVERTED, self::INDEX_NB_CONVERSIONS, self::INDEX_BOUNCE_COUNT, self::INDEX_PAGE_ENTRY_BOUNCE_COUNT, self::INDEX_PAGE_ENTRY_NB_VISITS, self::INDEX_PAGE_ENTRY_NB_ACTIONS, self::INDEX_PAGE_EXIT_NB_VISITS, self::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS, self::INDEX_REVENUE]; |
| 410 | } |
| 411 | /** |
| 412 | * @return array<string, string> |
| 413 | */ |
| 414 | public static function getDefaultMetricsDocumentation() |
| 415 | { |
| 416 | $cacheId = \Piwik\CacheId::pluginAware('DefaultMetricsDocumentation'); |
| 417 | $cache = PiwikCache::getTransientCache(); |
| 418 | if ($cache->contains($cacheId)) { |
| 419 | /** @var array<string, string> */ |
| 420 | return $cache->fetch($cacheId); |
| 421 | } |
| 422 | $translations = array('nb_visits' => 'General_ColumnNbVisitsDocumentation', 'nb_uniq_visitors' => 'General_ColumnNbUniqVisitorsDocumentation', 'nb_actions' => 'General_ColumnNbActionsDocumentation', 'nb_users' => 'General_ColumnNbUsersDocumentation', 'nb_actions_per_visit' => 'General_ColumnActionsPerVisitDocumentation', 'avg_time_on_site' => 'General_ColumnAvgTimeOnSiteDocumentation', 'bounce_rate' => 'General_ColumnBounceRateDocumentation', 'conversion_rate' => 'General_ColumnConversionRateDocumentation', 'avg_time_on_page' => 'General_ColumnAverageTimeOnPageDocumentation', 'nb_hits' => 'General_ColumnPageviewsDocumentation', 'hits' => 'General_ColumnHitsDocumentation', 'exit_rate' => 'General_ColumnExitRateDocumentation', 'nb_visits_converted' => 'General_VisitConvertedGoalDocumentation'); |
| 423 | /** |
| 424 | * Use this event to register translations for metrics documentation processed by your plugin. |
| 425 | * |
| 426 | * @param string[] $translations The array mapping of column_name => Plugin_TranslationForColumnDocumentation |
| 427 | */ |
| 428 | \Piwik\Piwik::postEvent('Metrics.getDefaultMetricDocumentationTranslations', array(&$translations)); |
| 429 | $translations = array_map(array('\\Piwik\\Piwik', 'translate'), $translations); |
| 430 | $cache->save($cacheId, $translations); |
| 431 | return $translations; |
| 432 | } |
| 433 | /** |
| 434 | * @return string |
| 435 | */ |
| 436 | public static function getPercentVisitColumn() |
| 437 | { |
| 438 | return str_replace(' ', ' ', \Piwik\Piwik::translate('General_ColumnPercentageVisits')); |
| 439 | } |
| 440 | /** |
| 441 | * This is a utility method used when building records through log aggregation. |
| 442 | * |
| 443 | * In records with per-goal conversion metrics the metrics are stored within DataTable Rows |
| 444 | * as a column with an array a value. The array is indexed by the goal ID and the column name |
| 445 | * is set to `Metrics::INDEX_GOALS`, for example: |
| 446 | * |
| 447 | * ``` |
| 448 | * $columns = [ |
| 449 | * Metrics::INDEX_GOALS = [ |
| 450 | * $idGoal => [ |
| 451 | * // ... conversion metrics ... |
| 452 | * ], |
| 453 | * ], |
| 454 | * ]; |
| 455 | * $row = new Row([DataTable::COLUMNS => $columns]); |
| 456 | * ``` |
| 457 | * |
| 458 | * This methods returns an array like `$columns` above based on a goal ID and a row of |
| 459 | * metric values for the goal. The result can be added directly to a DataTable record via `sumRowWithLabel()`. |
| 460 | * |
| 461 | * The goal metrics returned will differ based on whether the goal is user defined or an ecommerce goal. |
| 462 | * |
| 463 | * @param array<int, int|float> $goalsMetrics |
| 464 | * @return array<int, float> |
| 465 | */ |
| 466 | public static function makeGoalColumnsRow(int $idGoal, array $goalsMetrics) : array |
| 467 | { |
| 468 | if ($idGoal > GoalManager::IDGOAL_ORDER) { |
| 469 | // user defined goal |
| 470 | $columns = [\Piwik\Metrics::INDEX_GOAL_NB_CONVERSIONS, \Piwik\Metrics::INDEX_GOAL_NB_VISITS_CONVERTED, \Piwik\Metrics::INDEX_GOAL_REVENUE]; |
| 471 | } elseif ($idGoal == GoalManager::IDGOAL_ORDER) { |
| 472 | // ecommerce order |
| 473 | $columns = [\Piwik\Metrics::INDEX_GOAL_NB_CONVERSIONS, \Piwik\Metrics::INDEX_GOAL_NB_VISITS_CONVERTED, \Piwik\Metrics::INDEX_GOAL_REVENUE, \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL, \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_REVENUE_TAX, \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING, \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_REVENUE_DISCOUNT, \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_ITEMS]; |
| 474 | } else { |
| 475 | // idGoal == GoalManager::IDGOAL_CART (abandoned cart) |
| 476 | $columns = [\Piwik\Metrics::INDEX_GOAL_NB_CONVERSIONS, \Piwik\Metrics::INDEX_GOAL_NB_VISITS_CONVERTED, \Piwik\Metrics::INDEX_GOAL_REVENUE, \Piwik\Metrics::INDEX_GOAL_ECOMMERCE_ITEMS]; |
| 477 | } |
| 478 | $values = []; |
| 479 | foreach ($columns as $column) { |
| 480 | $values[$column] = (float) ($goalsMetrics[$column] ?? 0); |
| 481 | } |
| 482 | return $values; |
| 483 | } |
| 484 | } |
| 485 |