1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
|
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later
* @version $Id$
*
* @package Piwik_LogStats
*/
/**
* Class used by the logging script piwik.php called by the javascript tag.
* Handles the visitor & his/her actions on the website, saves the data in the DB, saves information in the cookie, etc.
*
* To maximise the performance of the logging module, we use different techniques.
*
* On the PHP-only side:
* - minimize the number of external files included.
* Ideally only one (the configuration file) in all the normal cases.
* We load the Loggers only when an error occurs ; this error is logged in the DB/File/etc
* depending on the loggers settings in the configuration file.
* - we may have to include external classes but we try to include only very
* simple code without any dependency, so that we could simply write a script
* that would merge all this simple code into a big piwik.php file.
*
* On the Database-related side:
* - write all the SQL queries without using any DB abstraction layer.
* Of course we carefully filter all input values.
* - minimize the number of SQL queries necessary to complete the algorithm.
* - carefully index the tables used
* - try to have fixed length rows
*
* [ - use a partitionning by date for the tables ]
*
* - handle the timezone settings??
*
* We could also imagine a batch system that would read a log file every 5min,
* and which prepares the file containg the rows to insert, then we load DATA INFILE
*
*
* Configuration options for the statsLogEngine module:
* - use_cookie ; defines if we try to get/set a cookie to help recognize a unique visitor
*
* @package Piwik_LogStats
*/
class Piwik_LogStats
{
protected $stateValid;
protected $urlToRedirect;
/**
*
* @var Piwik_LogStats_Db
*/
protected $db = null;
const STATE_NOTHING_TO_NOTICE = 1;
const STATE_TO_REDIRECT_URL = 2;
const STATE_LOGGING_DISABLE = 10;
const STATE_NO_GET_VARIABLE = 11;
const COOKIE_INDEX_IDVISITOR = 1;
const COOKIE_INDEX_TIMESTAMP_LAST_ACTION = 2;
const COOKIE_INDEX_TIMESTAMP_FIRST_ACTION = 3;
const COOKIE_INDEX_ID_VISIT = 4;
const COOKIE_INDEX_ID_LAST_ACTION = 5;
const VISIT_STANDARD_LENGTH = 1800;
public function __construct()
{
$this->stateValid = self::STATE_NOTHING_TO_NOTICE;
}
// create the database object
function connectDatabase()
{
$configDb = Piwik_LogStats_Config::getInstance()->database;
// we decode the password. Password is html encoded because it's enclosed between " double quotes
$configDb['password'] = htmlspecialchars_decode($configDb['password']);
$this->db = new Piwik_LogStats_Db( $configDb['host'],
$configDb['username'],
$configDb['password'],
$configDb['dbname']
);
$this->db->connect();
}
private function initProcess()
{
try{
$pluginsLogStats = Piwik_LogStats_Config::getInstance()->Plugins_LogStats;
if(is_array($pluginsLogStats)
&& count($pluginsLogStats) != 0)
{
Piwik_PluginsManager::getInstance()->doNotLoadAlwaysActivatedPlugins();
Piwik_PluginsManager::getInstance()->setPluginsToLoad( $pluginsLogStats['Plugins_LogStats'] );
}
} catch(Exception $e) {
}
$saveStats = Piwik_LogStats_Config::getInstance()->LogStats['record_statistics'];
if($saveStats == 0)
{
$this->setState(self::STATE_LOGGING_DISABLE);
}
if( count($_GET) == 0)
{
$this->setState(self::STATE_NO_GET_VARIABLE);
}
$downloadVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_url_var_name'];
$urlDownload = Piwik_Common::getRequestVar( $downloadVariableName, '', 'string');
if( !empty($urlDownload) )
{
if( Piwik_Common::getRequestVar( 'redirect', 1, 'int') == 1)
{
$this->setState( self::STATE_TO_REDIRECT_URL );
}
$this->setUrlToRedirect ( $urlDownload);
}
$outlinkVariableName = Piwik_LogStats_Config::getInstance()->LogStats['outlink_url_var_name'];
$urlOutlink = Piwik_Common::getRequestVar( $outlinkVariableName, '', 'string');
if( !empty($urlOutlink) )
{
if( Piwik_Common::getRequestVar( 'redirect', 1, 'int') == 1)
{
$this->setState( self::STATE_TO_REDIRECT_URL );
}
$this->setUrlToRedirect ( $urlOutlink);
}
}
private function processVisit()
{
return $this->stateValid !== self::STATE_LOGGING_DISABLE
&& $this->stateValid !== self::STATE_NO_GET_VARIABLE;
}
private function getState()
{
return $this->stateValid;
}
private function setUrlToRedirect( $url )
{
$this->urlToRedirect = $url;
}
private function getUrlToRedirect()
{
return $this->urlToRedirect;
}
private function setState( $value )
{
$this->stateValid = $value;
}
/**
* Returns the LogStats_Visit object.
* This method can be overwritten so that we use a different LogStats_Visit object
*
* @return LogStats_Visit
*/
protected function getNewVisitObject()
{
return new Piwik_LogStats_Visit($this->db);
}
// main algorithm
// => input : variables filtered
// => action : read cookie, read database, database logging, cookie writing
function main()
{
$this->initProcess();
if( $this->processVisit() )
{
try {
$this->connectDatabase();
$visit = $this->getNewVisitObject();
$visit->handle();
} catch (PDOException $e) {
$this->setState(self::STATE_LOGGING_DISABLE);
}
}
$this->endProcess();
}
// display the logo or pixel 1*1 GIF
// or a marketing page if no parameters in the url
// or redirect to a url
// or load a URL (rss feed) (transmit the cookie as well)
protected function endProcess()
{
switch($this->getState())
{
case self::STATE_LOGGING_DISABLE:
printDebug("Logging disabled, display transparent logo");
$this->outputTransparentGif();
break;
case self::STATE_NO_GET_VARIABLE:
printDebug("No get variables => piwik page");
echo "<a href='index.php'>Piwik</a> is a free open source <a href='http://piwik.org'>web analytics</a> alternative to Google analytics.";
break;
case self::STATE_TO_REDIRECT_URL:
$this->sendHeader('Location: ' . $this->getUrlToRedirect());
break;
case self::STATE_NOTHING_TO_NOTICE:
default:
printDebug("Nothing to notice => default behaviour");
$this->outputTransparentGif();
break;
}
printDebug("End of the page.");
if($GLOBALS['DEBUGPIWIK'] === true)
{
Piwik::printSqlProfilingReportLogStats($this->db);
}
$this->disconnectDb();
}
protected function disconnectDb()
{
if(isset($this->db))
{
$this->db->disconnect();
}
}
protected function outputTransparentGif()
{
if( !isset($GLOBALS['DEBUGPIWIK']) || !$GLOBALS['DEBUGPIWIK'] )
{
$trans_gif_64 = "R0lGODlhAQABAJEAAAAAAP///////wAAACH5BAEAAAIALAAAAAABAAEAAAICVAEAOw==";
header("Content-type: image/gif");
print(base64_decode($trans_gif_64));
}
}
protected function sendHeader($header)
{
header($header);
}
}
function printDebug( $info = '' )
{
if(isset($GLOBALS['DEBUGPIWIK']) && $GLOBALS['DEBUGPIWIK'])
{
if(is_array($info))
{
print("<PRE>");
print(var_export($info,true));
print("</PRE>");
}
else
{
print($info . "<br>\n");
}
}
}
|