7 * LICENSE: This source file is licensed under the terms of the GNU General Public License.
9 * @package Magic3 Framework
10 * @author 平田直毅(Naoki Hirata) <naoki@aplo.co.jp>
11 * @copyright Copyright 2006-2012 Magic3 Project.
12 * @license http://www.gnu.org/copyleft/gpl.html GPL License
13 * @version SVN: $Id: admin_analyticsDb.php 4889 2012-04-26 22:29:06Z fishbone $
14 * @link http://www.magic3.org
16 require_once($gEnvManager->getDbPath() . '/baseDb.php');
18 class admin_analyticsDb extends BaseDb
20 const MAX_URL_LENGTH = 180; // URLの長さ最大値
25 * @param array $row 取得レコード
26 * @param bool true=成功、false=失敗
28 function getOldAccessLog(&$row)
30 $queryStr = 'SELECT * FROM _access_log ';
31 $queryStr .= 'ORDER BY al_serial';
32 $ret = $this->selectRecord($queryStr, array(), $row);
38 * @param string $key キーとなる項目値
39 * @param string $value 値
40 * @return true = 正常、false=異常
42 function updateStatus($key, $value)
44 $now = date("Y/m/d H:i:s"); // 現在日時
47 $startTran = false; // この関数でトランザクションを開始したかどうか
48 if (!$this->isInTransaction()){
49 $this->startTransaction();
53 $queryStr = 'SELECT as_value FROM _analyze_status ';
54 $queryStr .= 'WHERE as_id = ? ';
55 $ret = $this->selectRecord($queryStr, array($key), $row);
57 $queryStr = 'UPDATE _analyze_status ';
58 $queryStr .= 'SET as_value = ?, ';
59 $queryStr .= 'as_update_dt = ? ';
60 $queryStr .= 'WHERE as_id = ? ';
61 $ret = $this->execStatement($queryStr, array($value, $now, $key));
63 $queryStr = 'INSERT INTO _analyze_status (';
64 $queryStr .= 'as_id, ';
65 $queryStr .= 'as_value, ';
66 $queryStr .= 'as_update_dt ';
67 $queryStr .= ') VALUES (';
68 $queryStr .= '?, ?, ?';
70 $ret = $this->execStatement($queryStr, array($key, $value, $now));
73 if ($startTran) $ret = $this->endTransaction();
79 * @param string $key キーとなる項目値
80 * @return string $value 値
82 function getStatus($key)
85 $queryStr = 'SELECT as_value FROM _analyze_status ';
86 $queryStr .= 'WHERE as_id = ?';
87 $ret = $this->selectRecord($queryStr, array($key), $row);
88 if ($ret) $retValue = $row['as_value'];
94 * @param date $date 集計する日付
95 * @param bool true=成功、false=失敗
97 public function calcDatePv($date)
100 $queryStr = 'DELETE FROM _analyze_page_view ';
101 $queryStr .= 'WHERE ap_date = ? ';
102 $ret = $this->execStatement($queryStr, array($date));
103 if (!$ret) return false;
104 $queryStr = 'DELETE FROM _analyze_daily_count ';
105 $queryStr .= 'WHERE aa_date = ? ';
106 $ret = $this->execStatement($queryStr, array($date));
107 if (!$ret) return false;
109 // ##### 時間単位で集計 #####
110 for ($i = 0; $i < 24; $i++){
112 $startDt = $date . ' ' . $i . ':0:0';
114 $endDt = $date . ' ' . ($i + 1) . ':0:0';
116 $endDt = date("Y/m/d", strtotime("$date 1 day")) . ' 0:0:0'; // 翌日
120 $queryStr = 'SELECT COUNT(*) AS total,al_uri,al_path FROM _access_log ';
121 $queryStr .= 'WHERE (? <= al_dt AND al_dt < ?) ';
122 $params[] = $startDt;
124 $queryStr .= 'GROUP BY al_uri ';
125 $queryStr .= 'ORDER BY total DESC';
126 $ret = $this->selectRecords($queryStr, $params, $rows);
129 if ($ret){ // データありのとき
130 for ($j = 0; $j < count($rows); $j++){
131 $total = $rows[$j]["total"];
132 $path = $rows[$j]["al_path"];
135 $rowUpdated = false; // 更新したかどうか
136 $url = $this->makeTruncStr($rows[$j]["al_uri"], self::MAX_URL_LENGTH);
137 if ($url != $rows[$j]["al_uri"]){ // URLが長いときは省略形で登録
138 $queryStr = 'SELECT * FROM _analyze_page_view ';
139 $queryStr .= 'WHERE ap_type = ? ';
140 $queryStr .= 'AND ap_url = ? ';
141 $queryStr .= 'AND ap_date = ? ';
142 $queryStr .= 'AND ap_hour = ?';
143 $ret = $this->selectRecord($queryStr, array(0/*すべてのデータ*/, $url, $date, $i), $row);
145 $serial = $row['ap_serial'];
146 $count = $row['ap_count'] + $total;
148 $queryStr = 'UPDATE _analyze_page_view ';
149 $queryStr .= 'SET ap_count = ? ';
150 $queryStr .= 'WHERE ap_serial = ? ';
151 $ret = $this->execStatement($queryStr, array($count, $serial));
152 if (!$ret) return false; // エラー発生
154 $rowUpdated = true; // 更新したかどうか
157 if (!$rowUpdated){ // データ更新していないとき
158 $queryStr = 'INSERT INTO _analyze_page_view (';
159 $queryStr .= 'ap_type, ';
160 $queryStr .= 'ap_url, ';
161 $queryStr .= 'ap_date, ';
162 $queryStr .= 'ap_hour, ';
163 $queryStr .= 'ap_count, ';
164 $queryStr .= 'ap_path ';
165 $queryStr .= ') VALUES (';
166 $queryStr .= '?, ?, ?, ?, ?, ?';
168 $ret = $this->execStatement($queryStr, array(0/*すべてのデータ*/, $url, $date, $i, $total, $path));
169 if (!$ret) return false; // エラー発生
175 // ##### 訪問数を集計 #####
177 $startDt = $date . ' 0:0:0';
178 $endDt = date("Y/m/d", strtotime("$date 1 day")) . ' 0:0:0'; // 翌日
182 $queryStr = 'SELECT COUNT(DISTINCT al_session) AS total,al_uri,al_path FROM _access_log ';
183 $queryStr .= 'WHERE (? <= al_dt AND al_dt < ?) ';
184 $params[] = $startDt;
186 $queryStr .= 'GROUP BY al_uri ';
187 $queryStr .= 'ORDER BY total DESC';
188 $ret = $this->selectRecords($queryStr, $params, $rows);
191 if ($ret){ // データありのとき
192 for ($j = 0; $j < count($rows); $j++){
193 $total = $rows[$j]["total"];
194 $path = $rows[$j]["al_path"];
197 $rowUpdated = false; // 更新したかどうか
198 $url = $this->makeTruncStr($rows[$j]["al_uri"], self::MAX_URL_LENGTH);
199 if ($url != $rows[$j]["al_uri"]){ // URLが長いときは省略形で登録
200 $queryStr = 'SELECT * FROM _analyze_daily_count ';
201 $queryStr .= 'WHERE aa_type = ? ';
202 $queryStr .= 'AND aa_url = ? ';
203 $queryStr .= 'AND aa_date = ? ';
204 $ret = $this->selectRecord($queryStr, array(0/*訪問数*/, $url, $date), $row);
206 $serial = $row['aa_serial'];
207 $count = $row['aa_count'] + $total;
209 $queryStr = 'UPDATE _analyze_daily_count ';
210 $queryStr .= 'SET aa_count = ? ';
211 $queryStr .= 'WHERE aa_serial = ? ';
212 $ret = $this->execStatement($queryStr, array($count, $serial));
213 if (!$ret) return false; // エラー発生
215 $rowUpdated = true; // 更新したかどうか
218 if (!$rowUpdated){ // データ更新していないとき
219 $queryStr = 'INSERT INTO _analyze_daily_count (';
220 $queryStr .= 'aa_type, ';
221 $queryStr .= 'aa_url, ';
222 $queryStr .= 'aa_date, ';
223 $queryStr .= 'aa_count, ';
224 $queryStr .= 'aa_path ';
225 $queryStr .= ') VALUES (';
226 $queryStr .= '?, ?, ?, ?, ?';
228 $ret = $this->execStatement($queryStr, array(0/*訪問数*/, $url, $date, $total, $path));
229 if (!$ret) return false; // エラー発生
233 // 一日あたりアクセスポイントごとの集計
235 $queryStr = 'SELECT COUNT(DISTINCT al_session) AS total,al_uri,al_path FROM _access_log ';
236 $queryStr .= 'WHERE (? <= al_dt AND al_dt < ?) ';
237 $params[] = $startDt;
239 $queryStr .= 'GROUP BY al_path ';
240 $queryStr .= 'ORDER BY total DESC';
241 $ret = $this->selectRecords($queryStr, $params, $rows);
244 if ($ret){ // データありのとき
245 for ($j = 0; $j < count($rows); $j++){
246 $total = $rows[$j]["total"];
247 $path = $rows[$j]["al_path"];
249 // FCKEditorからのアクセスは、アクセスポイントを使用しないので除く
251 $queryStr = 'INSERT INTO _analyze_daily_count (';
252 $queryStr .= 'aa_type, ';
253 $queryStr .= 'aa_url, ';
254 $queryStr .= 'aa_date, ';
255 $queryStr .= 'aa_count, ';
256 $queryStr .= 'aa_path ';
257 $queryStr .= ') VALUES (';
258 $queryStr .= '?, ?, ?, ?, ?';
260 $ret = $this->execStatement($queryStr, array(0/*訪問数*/, ''/*アクセスポイント指定*/, $date, $total, $path));
261 if (!$ret) return false; // エラー発生
265 // 一日あたりすべてのアクセスポイントの集計
266 // MySQL v5.0.22でエラー発生(2010/12/3)
267 // SQLエラーメッセージ(Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause error code: 42000)
268 // 「GROUP BY」を付けると回避可能
270 //$queryStr = 'SELECT COUNT(DISTINCT al_session) AS total,al_uri,al_path FROM _access_log '; // NG
271 $queryStr = 'SELECT COUNT(DISTINCT al_session) AS total FROM _access_log '; // OK
272 $queryStr .= 'WHERE (? <= al_dt AND al_dt < ?) ';
273 $params[] = $startDt;
275 $queryStr .= 'ORDER BY total DESC';
276 $ret = $this->selectRecord($queryStr, $params, $row);
279 if ($ret && $row["total"] > 0){ // データありのとき
280 $total = $row["total"];
282 $queryStr = 'INSERT INTO _analyze_daily_count (';
283 $queryStr .= 'aa_type, ';
284 $queryStr .= 'aa_url, ';
285 $queryStr .= 'aa_date, ';
286 $queryStr .= 'aa_count, ';
287 $queryStr .= 'aa_path ';
288 $queryStr .= ') VALUES (';
289 $queryStr .= '?, ?, ?, ?, ?';
291 $ret = $this->execStatement($queryStr, array(0/*訪問数*/, ''/*アクセスポイント指定*/, $date, $total, ''/*すべてのアクセスポイント*/));
292 if (!$ret) return false; // エラー発生
294 // ##### 訪問者数を集計 #####
296 $startDt = $date . ' 0:0:0';
297 $endDt = date("Y/m/d", strtotime("$date 1 day")) . ' 0:0:0'; // 翌日
301 $queryStr = 'SELECT COUNT(DISTINCT al_cookie_value) AS total,al_uri,al_path FROM _access_log ';
302 $queryStr .= 'WHERE (? <= al_dt AND al_dt < ?) ';
303 $params[] = $startDt;
305 $queryStr .= 'GROUP BY al_uri ';
306 $queryStr .= 'ORDER BY total DESC';
307 $ret = $this->selectRecords($queryStr, $params, $rows);
310 if ($ret){ // データありのとき
311 for ($j = 0; $j < count($rows); $j++){
312 $total = $rows[$j]["total"];
313 $path = $rows[$j]["al_path"];
316 $rowUpdated = false; // 更新したかどうか
317 $url = $this->makeTruncStr($rows[$j]["al_uri"], self::MAX_URL_LENGTH);
318 if ($url != $rows[$j]["al_uri"]){ // URLが長いときは省略形で登録
319 $queryStr = 'SELECT * FROM _analyze_daily_count ';
320 $queryStr .= 'WHERE aa_type = ? ';
321 $queryStr .= 'AND aa_url = ? ';
322 $queryStr .= 'AND aa_date = ? ';
323 $ret = $this->selectRecord($queryStr, array(1/*訪問者数*/, $url, $date), $row);
325 $serial = $row['aa_serial'];
326 $count = $row['aa_count'] + $total;
328 $queryStr = 'UPDATE _analyze_daily_count ';
329 $queryStr .= 'SET aa_count = ? ';
330 $queryStr .= 'WHERE aa_serial = ? ';
331 $ret = $this->execStatement($queryStr, array($count, $serial));
332 if (!$ret) return false; // エラー発生
334 $rowUpdated = true; // 更新したかどうか
337 if (!$rowUpdated){ // データ更新していないとき
338 $queryStr = 'INSERT INTO _analyze_daily_count (';
339 $queryStr .= 'aa_type, ';
340 $queryStr .= 'aa_url, ';
341 $queryStr .= 'aa_date, ';
342 $queryStr .= 'aa_count, ';
343 $queryStr .= 'aa_path ';
344 $queryStr .= ') VALUES (';
345 $queryStr .= '?, ?, ?, ?, ?';
347 $ret = $this->execStatement($queryStr, array(1/*訪問者数*/, $url, $date, $total, $path));
348 if (!$ret) return false; // エラー発生
353 // 1日あたりアクセスポイントごとの集計
355 $queryStr = 'SELECT COUNT(DISTINCT al_cookie_value) AS total,al_uri,al_path FROM _access_log ';
356 $queryStr .= 'WHERE (? <= al_dt AND al_dt < ?) ';
357 $params[] = $startDt;
359 $queryStr .= 'GROUP BY al_path ';
360 $queryStr .= 'ORDER BY total DESC';
361 $ret = $this->selectRecords($queryStr, $params, $rows);
364 if ($ret){ // データありのとき
365 for ($j = 0; $j < count($rows); $j++){
366 $total = $rows[$j]["total"];
367 $path = $rows[$j]["al_path"];
369 // FCKEditorからのアクセスは、アクセスポイントを使用しないので除く
371 $queryStr = 'INSERT INTO _analyze_daily_count (';
372 $queryStr .= 'aa_type, ';
373 $queryStr .= 'aa_url, ';
374 $queryStr .= 'aa_date, ';
375 $queryStr .= 'aa_count, ';
376 $queryStr .= 'aa_path ';
377 $queryStr .= ') VALUES (';
378 $queryStr .= '?, ?, ?, ?, ?';
380 $ret = $this->execStatement($queryStr, array(1/*訪問者数*/, ''/*アクセスポイント指定*/, $date, $total, $path));
381 if (!$ret) return false; // エラー発生
388 //$queryStr = 'SELECT COUNT(DISTINCT al_cookie_value) AS total,al_uri,al_path FROM _access_log '; // NG
389 $queryStr = 'SELECT COUNT(DISTINCT al_cookie_value) AS total FROM _access_log '; // OK
390 $queryStr .= 'WHERE (? <= al_dt AND al_dt < ?) ';
391 $params[] = $startDt;
393 $queryStr .= 'ORDER BY total DESC';
394 $ret = $this->selectRecord($queryStr, $params, $row);
397 if ($ret && $row["total"] > 0){ // データありのとき
398 $total = $row["total"];
400 $queryStr = 'INSERT INTO _analyze_daily_count (';
401 $queryStr .= 'aa_type, ';
402 $queryStr .= 'aa_url, ';
403 $queryStr .= 'aa_date, ';
404 $queryStr .= 'aa_count, ';
405 $queryStr .= 'aa_path ';
406 $queryStr .= ') VALUES (';
407 $queryStr .= '?, ?, ?, ?, ?';
409 $ret = $this->execStatement($queryStr, array(1/*訪問者数*/, ''/*アクセスポイント指定*/, $date, $total, ''/*すべてのアクセスポイント*/));
410 if (!$ret) return false; // エラー発生
417 * @param string $path アクセスポイント(NULL=すべてのデータ)
418 * @param date $startDate 集計期間、開始日(NULL=先頭からすべて)
419 * @param date $endDate 集計期間、終了日
420 * @param function $callback コールバック関数
423 function getPageViewByDate($path, $startDate, $endDate, $callback)
426 $queryStr = 'SELECT SUM(ap_count) AS total, ap_date FROM _analyze_page_view ';
427 $queryStr .= 'WHERE ap_type = ? '; $params[] = 0;
428 if (is_null($startDate)){
429 $queryStr .= 'AND ap_date <= ? ';
431 $queryStr .= 'AND (? <= ap_date AND ap_date <= ?) ';
432 $params[] = $startDate;
434 $params[] = $endDate;
435 if (!is_null($path)){
436 $queryStr .= 'AND ap_path = ? ';
439 $queryStr .= 'GROUP BY ap_date ';
440 $queryStr .= 'ORDER BY ap_date';
441 $this->selectLoop($queryStr, $params, $callback);
446 * @param int $type データのタイプ(0=訪問数、1=訪問者数)
447 * @param string $path アクセスポイント(NULL=すべてのデータ)
448 * @param date $startDate 集計期間、開始日(NULL=先頭からすべて)
449 * @param date $endDate 集計期間、終了日
450 * @param function $callback コールバック関数
453 function getDailyCountByDate($type, $path, $startDate, $endDate, $callback)
456 $queryStr = 'SELECT SUM(aa_count) AS total, aa_date FROM _analyze_daily_count ';
457 $queryStr .= 'WHERE aa_type = ? '; $params[] = $type;
458 $queryStr .= 'AND aa_url = ? '; $params[] = ''; /*アクセスポイントごとの集計値*/
459 if (is_null($startDate)){
460 $queryStr .= 'AND aa_date <= ? ';
462 $queryStr .= 'AND (? <= aa_date AND aa_date <= ?) ';
463 $params[] = $startDate;
465 $params[] = $endDate;
466 if (is_null($path)){ // すべてのアクセスポイントの場合
467 $queryStr .= 'AND aa_path = ? ';
470 $queryStr .= 'AND aa_path = ? ';
473 $queryStr .= 'GROUP BY aa_date ';
474 $queryStr .= 'ORDER BY aa_date';
475 $this->selectLoop($queryStr, $params, $callback);
480 * @param string $str 変換元文字列
481 * @param int $len 文字列長
482 * @return string 作成した文字列
484 function makeTruncStr($str, $len)
486 if (strlen($str) > $len) $addStr = '...';
487 $destStr = substr($str, 0, $len) . $addStr;
493 * @param function $callback コールバック関数
494 * @param int $type リストの種別
495 * @param int $filter データのフィルタリング(-1=PC携帯スマートフォンに関係なく取得、0=PC用のみ、1=携帯用のみ、2=スマートフォン用のみ)
498 function getPageIdList($callback, $type, $filter = -1)
500 $params = array($type);
501 $queryStr = 'SELECT * FROM _page_id ';
502 $queryStr .= 'WHERE pg_type = ? ';
504 // pg_device_typeは後で追加したため、pg_mobileを残しておく
505 if ($filter != 1){ // 携帯以外のとき
506 $queryStr .= 'AND pg_device_type = ? '; $params[] = $filter;
509 $queryStr .= 'AND pg_mobile = ? ';
511 if ($filter == 1) $mobile = 1; // 携帯のとき
514 $queryStr .= 'ORDER BY pg_priority';
515 $this->selectLoop($queryStr, $params, $callback);
520 * @param array $rows 取得レコード
521 * @return true=取得、false=取得せず
523 function getAnalyticsAccessPoint(&$rows)
525 $queryStr = 'SELECT * FROM _page_id ';
526 $queryStr .= 'WHERE pg_type = 0 '; // アクセスポイント
527 $queryStr .= 'AND pg_analytics = true '; // アクセス解析あり
528 $queryStr .= 'AND pg_visible = true '; // 公開しているアクセスポイントのみ
529 $queryStr .= 'AND pg_active = true '; // アクセス許可しているアクセスポイントのみ
530 $queryStr .= 'ORDER BY pg_priority';
531 $retValue = $this->selectRecords($queryStr, array(), $rows);