PluginProbe
Blog Floating Button / trunk
Blog Floating Button vtrunk
trunk 1.4.10 1.4.11 1.4.12 1.4.13 1.4.14 1.4.15 1.4.16 1.4.17 1.4.18 1.4.19 1.4.20 1.4.21 1.4.7 1.4.8 1.4.9
blog-floating-button / tracking.php

tracking.php in Blog Floating Button trunk, at tracking.php

537 lines 17.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 class Tracking {
8
9 private $access_log_table = 'bfb_access_log';
10 private $click_log_table = 'bfb_click_log';
11 public $pagination_limit = 50;
12 public $wpdb;
13
14 public function __construct() {
15
16 global $wpdb;
17 $this->wpdb = $wpdb;
18
19 $this->access_log_table = $wpdb->prefix . $this->access_log_table;
20 $this->click_log_table = $wpdb->prefix . $this->click_log_table;
21 }
22
23 public function write_accessLog( $data ) {
24
25 $res = $this->wpdb->insert( $this->access_log_table, $data, array( '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) );
26 }
27 public function write_clickLog( $data ) {
28
29 $data['ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
30 $data['device'] = $this->ch_device( $data['ua'] );
31
32 $res = $this->wpdb->insert( $this->click_log_table, $data, array( '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) );
33 }
34
35 public function get_user_data( $postData ) {
36
37 $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
38 $ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
39
40 $data = array(
41 'post_id' => $postData['post_id'],
42 'post_url' => $postData['post_url'],
43 'ip' => $ip,
44 'ua' => $ua,
45 'device' => $this->ch_device( $ua ),
46 'referer' => $postData['referer'],
47 'optimize_id' => $postData['optimize_id'],
48 'optimize_type' => $postData['optimize_type'],
49 );
50
51 return $data;
52 }
53 private function ch_device( $ua ) {
54
55 if ( ( strpos( $ua, 'Android' ) !== false ) && ( strpos( $ua, 'Mobile' ) !== false ) || ( strpos( $ua, 'iPhone' ) !== false ) || ( strpos( $ua, 'Windows Phone' ) !== false ) ) {
56 // スマホ
57 $device = 'SP';
58 } elseif ( ( strpos( $ua, 'Android' ) !== false ) || ( strpos( $ua, 'iPad' ) !== false ) ) {
59 // タブレット
60 $device = 'Tab';
61 } elseif ( ( strpos( $ua, 'DoCoMo' ) !== false ) || ( strpos( $ua, 'KDDI' ) !== false ) || ( strpos( $ua, 'SoftBank' ) !== false ) || ( strpos( $ua, 'Vodafone' ) !== false ) || ( strpos( $ua, 'J-PHONE' ) !== false ) ) {
62 // ガラケー
63 $device = 'Mobile';
64 } else {
65 // パソコン
66 $device = 'PC';
67 }
68
69 return $device;
70 }
71
72 public function get_first_last_date( $ym = null, $ym2 = null, $span = null ) {
73
74 if ( ! $ym ) {
75 $ym = date_i18n( 'Y-m' ); }
76 if ( ! $ym2 ) {
77 $ym2 = gmdate( 'Y-m-d', strtotime( 'last day of ' . $ym ) ); }
78
79 $start = new DateTime( $ym . '-01 00:00:00' );
80
81 if ( $span == 'daily' ) {
82 $end = new DateTime( $ym2 . ' 23:59:59' );
83 $date_interval = new DateInterval( 'P1D' ); // 1日間隔
84
85 } elseif ( $span == 'monthly' ) {
86 $end = new DateTime( $ym2 . ' 23:59:59' );
87 $date_interval = new DateInterval( 'P1M' ); // 1月間隔
88 }
89
90 $date_period = new DatePeriod( $start, $date_interval, $end );
91
92 return $date_period;
93 }
94 public function get_tracking_data( $table, $data, $condi ) {
95
96 if ( $table == 'access' ) {
97 $table_name = $this->access_log_table;
98 } elseif ( $table == 'click' ) {
99 $table_name = $this->click_log_table;
100 }
101
102 $where = array();
103 $params = array();
104
105 if ( ! empty( $data['start_date'] ) && ! empty( $data['end_date'] ) ) {
106 $where[] = 'date BETWEEN %s AND %s';
107 $params[] = $data['start_date'] . ' 00:00:00';
108 $params[] = $data['end_date'] . ' 23:59:59';
109 } else {
110 $where[] = 'date BETWEEN %s AND %s';
111 $params[] = '2000-01-01 00:00:00';
112 $params[] = date_i18n( 'Y-m-d' ) . ' 23:59:59';
113 }
114
115 if ( ! empty( $data['memo'] ) ) {
116 $where[] = 'memo LIKE %s';
117 $params[] = '%' . $this->wpdb->esc_like( $data['memo'] ) . '%';
118 }
119 if ( ! empty( $data['post_url'] ) ) {
120 $where[] = 'post_url LIKE %s';
121 $params[] = '%' . $this->wpdb->esc_like( $data['post_url'] ) . '%';
122 }
123 if ( ! empty( $data['device'] ) ) {
124 $where[] = 'device LIKE %s';
125 $params[] = '%' . $this->wpdb->esc_like( $data['device'] ) . '%';
126 }
127 // テストID
128 if ( ! empty( $condi['optimize_id'] ) ) {
129 $where[] = 'optimize_id = %s';
130 $params[] = $condi['optimize_id'];
131 }
132
133 $query_params = $params;
134
135 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- テーブル名は $wpdb->prefix 由来で固定
136 $sql = 'SELECT * FROM ' . $table_name . ' WHERE ' . implode( ' AND ', $where ) . ' ORDER BY date DESC';
137
138 // 表示件数
139 if ( ! empty( $condi['limit'] ) ) {
140 $sql .= ' LIMIT %d';
141 $query_params[] = intval( $condi['limit'] );
142
143 // オフセット
144 if ( ! empty( $condi['paged'] ) && intval( $condi['paged'] ) > 1 ) {
145 $sql .= ' OFFSET %d';
146 $query_params[] = intval( $condi['limit'] ) * ( intval( $condi['paged'] ) - 1 );
147 }
148 }
149
150 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.NotPrepared -- $sql は固定の断片と %s/%d プレースホルダのみで構成し、値は prepare() でバインドしている
151 $datas = $this->wpdb->get_results( $this->wpdb->prepare( $sql, $query_params ) );
152
153 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- テーブル名は $wpdb->prefix 由来で固定
154 $count_sql = 'SELECT * FROM ' . $table_name . ' WHERE ' . implode( ' AND ', $where );
155 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.NotPrepared -- $count_sql は固定の断片と %s/%d プレースホルダのみで構成し、値は prepare() でバインドしている
156 $res_count = $this->wpdb->get_results( $this->wpdb->prepare( $count_sql, $params ) ); // ページ送りせず�
157 �件取得
158 $datas['count'] = $this->wpdb->num_rows; // 件数表示
159
160 return $datas;
161 }
162 public function get_report_data( $table, $span, $data, $unique = true ) {
163
164 $distinct = '*'; // 重複データも含め取得(PV)
165
166 if ( $table == 'access' ) {
167 $table_name = $this->access_log_table;
168 if ( $unique ) {
169 $distinct = 'DISTINCT CONCAT(ip,ua)'; // 重複データを削除(ユニークユーザー)
170 }
171 } elseif ( $table == 'click' ) {
172 $table_name = $this->click_log_table;
173 }
174
175 $where = array();
176 $params = array();
177
178 if ( ! empty( $data['start_date'] ) && ! empty( $data['end_date'] ) ) {
179 $where[] = 'date BETWEEN %s AND %s';
180 $params[] = $data['start_date'] . ' 00:00:00';
181 $params[] = $data['end_date'] . ' 23:59:59';
182 }
183 if ( ! empty( $data['post_url'] ) ) {
184 $where[] = 'post_url LIKE %s';
185 $params[] = '%' . $this->wpdb->esc_like( $data['post_url'] ) . '%';
186 }
187 if ( ! empty( $data['device'] ) ) {
188 $where[] = 'device LIKE %s';
189 $params[] = '%' . $this->wpdb->esc_like( $data['device'] ) . '%';
190 }
191
192 // テストID
193 if ( ! empty( $data['optimize_id'] ) ) {
194 $where[] = 'optimize_id = %s';
195 $params[] = $data['optimize_id'];
196 }
197
198 // optimize_type 単独では絞り込まない従来動作を維持する。
199 if ( ! empty( $data['optimize_type'] ) && ! empty( $where ) ) {
200 $where[] = 'optimize_type = %s';
201 $params[] = $data['optimize_type'];
202 }
203
204 $sql_where = '';
205 if ( ! empty( $where ) ) {
206 $sql_where = ' WHERE ' . implode( ' AND ', $where );
207 }
208
209 if ( ! empty( $params ) ) {
210 $daily_date_format = '%%Y-%%m-%%d';
211 $daily_group_format = '%%Y%%m%%d';
212 $monthly_date_format = '%%Y-%%m';
213 $monthly_group_format = '%%Y%%m';
214 } else {
215 $daily_date_format = '%Y-%m-%d';
216 $daily_group_format = '%Y%m%d';
217 $monthly_date_format = '%Y-%m';
218 $monthly_group_format = '%Y%m';
219 }
220
221 if ( $span == 'daily' ) {
222
223 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- テーブル名は $wpdb->prefix 由来で固定し、集計式と日付書式も関数�
224 の固定値
225 $sql = "
226 SELECT DATE_FORMAT(date, '" . $daily_date_format . "') as 'date',
227 COUNT(" . $distinct . ') as count
228 FROM ' . $table_name
229 . $sql_where . "
230 GROUP BY DATE_FORMAT(date, '" . $daily_group_format . "') ORDER BY date DESC
231 ";
232
233 } elseif ( $span == 'monthly' ) {
234
235 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- テーブル名は $wpdb->prefix 由来で固定し、集計式と日付書式も関数�
236 の固定値
237 $sql = "
238 SELECT DATE_FORMAT(date, '" . $monthly_date_format . "') as 'date',
239 COUNT(" . $distinct . ') as count
240 FROM ' . $table_name
241 . $sql_where . "
242 GROUP BY DATE_FORMAT(date, '" . $monthly_group_format . "') ORDER BY date DESC
243 ";
244
245 }
246
247 if ( isset( $sql ) ) {
248 if ( ! empty( $params ) ) {
249 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.NotPrepared -- $sql は固定の断片と %s/%d プレースホルダのみで構成し、値は prepare() でバインドしている
250 $datas = $this->wpdb->get_results( $this->wpdb->prepare( $sql, $params ) );
251 } else {
252 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.NotPrepared -- $sql は固定の断片のみで構成され、バインド対象の値がない
253 $datas = $this->wpdb->get_results( $sql );
254 }
255
256 }
257
258 // 日付をキーに変更
259 return array_column( $datas, 'count', 'date' );
260 }
261 // 日付取得
262 public function get_target_date( $date, $span ) {
263
264 if ( ! empty( $span ) ) {
265 if ( strpos( $span, 'month' ) === false ) {
266 return gmdate( 'Y-m-d', strtotime( $span . $date ) );
267 } else {
268 return gmdate( 'Y-m', strtotime( $span . $date ) );
269 }
270 } else {
271 return gmdate( 'Y-m-d', strtotime( $date ) );
272 }
273 }
274 // 指定日のデータ取得
275 public function get_target_data( $span = null ) {
276
277 $search_day = $this->get_target_date( date_i18n( 'Y-m-d' ), $span );
278 $search_data = array(
279 'start_date' => $search_day,
280 'end_date' => $search_day,
281 );
282
283 $res['access'] = $this->get_report_data( 'access', 'daily', $search_data );
284 $res['click'] = $this->get_report_data( 'click', 'daily', $search_data );
285
286 return $res;
287 }
288 // 月別データ取得
289 // 指定月データを取得したい場合は#$date='2020-03'などを�
290 �力。
291 // その場合の$spanは無視される
292 public function get_monthly_data( $span = null, $date = null ) {
293
294 if ( ! empty( $date ) ) {
295 // 日付指定があれば
296 $start_date = gmdate( 'Y-m-01', strtotime( $date ) );
297 $end_date = gmdate( 'Y-m-t', strtotime( $start_date ) ); // 月末取得
298 } else {
299 // 日付指定がない
300 $start_date = $this->get_target_date( date_i18n( 'Y-m-01' ), $span );
301 $end_date = gmdate( 'Y-m-t', strtotime( $start_date ) ); // 月末取得
302 }
303
304 $search_data = array(
305 'start_date' => $start_date,
306 'end_date' => $end_date,
307 );
308
309 $res['access'] = $this->get_report_data( 'access', 'monthly', $search_data );
310 $res['click'] = $this->get_report_data( 'click', 'monthly', $search_data );
311
312 return $res;
313 }
314 // クリック率を計算
315 public function calc_click_rate( $access_num_sum, $click_num_sum ) {
316
317 if ( ! empty( $access_num_sum ) ) {
318 return round( ( $click_num_sum / $access_num_sum ) * 100, 1 );
319 } else {
320 return 0;
321 }
322 }
323 // グラフ描画データ作成
324 public function graphDate( $date_datas, $access_datas, $click_datas, $span = 'daily' ) {
325
326 $max_access = 0;
327 $max_click = 0;
328 $max_click_rate = 0;
329
330 foreach ( $date_datas as $date ) {
331
332 if ( $span == 'monthly' ) {
333 $today = $date->format( 'Y-m' );
334 } else {
335 $today = $date->format( 'Y-m-d' );
336 }
337
338 $graphData['date'][] = $today;
339
340 $graphData['access'][ $today ] = '';
341 if ( isset( $access_datas[ $today ] ) ) {
342 $graphData['access'][ $today ] = $access_datas[ $today ];
343 if ( $max_access < $access_datas[ $today ] ) {
344 $max_access = $access_datas[ $today ];
345 }
346 }
347
348 $graphData['click'][ $today ] = '';
349 if ( isset( $click_datas[ $today ] ) ) {
350 $graphData['click'][ $today ] = $click_datas[ $today ];
351 if ( $max_click < $click_datas[ $today ] ) {
352 $max_click = $click_datas[ $today ];
353 }
354 }
355
356 if ( isset( $access_datas[ $today ] ) && isset( $click_datas[ $today ] ) ) {
357 $graphData['click_rate'][ $today ] = $this->calc_click_rate( $access_datas[ $today ], $click_datas[ $today ] );
358 if ( $max_click_rate < $graphData['click_rate'][ $today ] ) {
359 $max_click_rate = $graphData['click_rate'][ $today ];
360 }
361 }
362 }
363
364 $js_data['max_access'] = $max_access;
365 $js_data['max_click'] = $max_click;
366 $js_data['max_click_rate'] = $max_click_rate;
367
368 $js_data['date'] = array();
369 $js_data['access'] = array();
370 $js_data['click'] = array();
371 $js_data['click_rate'] = array();
372
373 if ( isset( $graphData ) && is_array( $graphData ) ) {
374
375 foreach ( $graphData['date'] as $date ) {
376
377 if ( isset( $date ) ) {
378 $js_data['date'][] = $date;
379 }
380 if ( isset( $graphData['access'][ $date ] ) ) {
381 $js_data['access'][] = intval( $graphData['access'][ $date ] );
382 } else {
383 $js_data['access'][] = null;
384 }
385 if ( isset( $graphData['click'][ $date ] ) ) {
386 $js_data['click'][] = intval( $graphData['click'][ $date ] );
387 } else {
388 $js_data['click'][] = null;
389 }
390 if ( isset( $graphData['click_rate'][ $date ] ) ) {
391 $js_data['click_rate'][] = floatval( $graphData['click_rate'][ $date ] );
392 } else {
393 $js_data['click_rate'][] = null;
394 }
395 }
396 }
397
398 return $js_data;
399 }
400 // ABテストのグラフ
401 public function graphDate_clickRate( $date_datas, $mainDatas, $subDatas ) {
402
403 $js_data['date'] = array();
404 $js_data['main'] = array();
405 $js_data['sub'] = array();
406
407 foreach ( $date_datas as $date ) {
408 $today = $date->format( 'Y-m-d' );
409 $graphData['date'][] = $today;
410 }
411
412 foreach ( $graphData['date'] as $date ) {
413
414 if ( ! empty( $mainDatas['access'][ $date ] ) && ! empty( $mainDatas['click'][ $date ] ) ) {
415 $mainCTR = $this->calc_click_rate( $mainDatas['access'][ $date ], $mainDatas['click'][ $date ] );
416 } else {
417 $mainCTR = 0;
418 }
419
420 if ( ! empty( $subDatas['access'][ $date ] ) && ! empty( $subDatas['click'][ $date ] ) ) {
421 $subCTR = $this->calc_click_rate( $subDatas['access'][ $date ], $subDatas['click'][ $date ] );
422 } else {
423 $subCTR = 0;
424 }
425
426 if ( isset( $date ) ) {
427 $js_data['date'][] = $date;
428 }
429 if ( isset( $mainCTR ) ) {
430 $js_data['main'][] = floatval( $mainCTR );
431 } else {
432 $js_data['main'][] = null;
433 }
434 if ( isset( $subCTR ) ) {
435 $js_data['sub'][] = floatval( $subCTR );
436 } else {
437 $js_data['sub'][] = null;
438 }
439 }
440
441 return $js_data;
442 }
443
444 // 1の位で切り上げ
445 public function ceil_1( $no ) {
446 return ceil( ( $no / 10 ) ) * 10;
447 }
448 // ページネーション
449 public function pagination( $data, $condi ) {
450
451 if ( $condi['limit'] > 0 ) {
452 $limit = $condi['limit'];
453 $paged_num = ceil( $data['count'] / $condi['limit'] );
454 } else {
455 $limit = $this->pagination_limit;
456 $paged_num = ceil( $data['count'] / $this->pagination_limit );
457 }
458
459 if ( $paged_num < 2 ) {
460 $res['count_text'] = '�
461 �' . intval( $data['count'] ) . '件を表示中';
462 return $res;
463 }
464
465 if ( $condi['paged'] > 1 ) {
466 $paged = $condi['paged'] - 1;
467 } else {
468 $paged = 0;
469 }
470
471 $res['count'] = $data['count']; // �
472 �件数
473 $res['min_count'] = $condi['limit'] * $paged + 1;
474 $res['max_count'] = $condi['limit'] * $paged + $condi['limit'];
475 if ( $res['max_count'] > $data['count'] ) {
476 $res['max_count'] = $data['count']; }
477
478 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
479 $url = mb_strstr( $request_uri, '?' );
480 $url = false === $url ? '' : ltrim( $url, '?' ); // パラメータ部分を取得
481 parse_str( $url, $param );
482
483 unset( $param['paged'] ); // パラメータのpagedは削除
484 $pagination_param = http_build_query( $param );
485
486 // 現在のページの前後のページネーション表示
487 if ( $paged_num > 10 ) {
488 if ( ( $paged - 5 ) <= 0 ) {
489 $min_pagination = 1;
490 $max_pagination = 10;
491 } else {
492 $min_pagination = ( $paged - 5 ) + 1;
493 $max_pagination = 5 + $paged;
494 }
495 if ( ( $paged + 5 ) >= $paged_num ) {
496 $min_pagination = $paged_num - 10;
497 $max_pagination = $paged_num;
498 }
499 } else {
500 $min_pagination = 1;
501 $max_pagination = $paged_num;
502 }
503
504 $res['html'] = '';
505 for ( $i = $min_pagination; $max_pagination >= $i; $i++ ) {
506
507 if ( $max_pagination > $paged_num ) {
508 break; }
509
510 $active = '';
511 if ( ! $condi['paged'] && $i == 1 ) {
512 $active = 'class="active"';
513 } elseif ( $condi['paged'] == $i ) {
514 $active = 'class="active"';
515 }
516
517 $pagination_url = '?' . $pagination_param . ( $pagination_param ? '&' : '' ) . 'paged=' . intval( $i );
518 $res['html'] .= '<a href="' . esc_url( $pagination_url ) . '" ' . $active . '>' . esc_html( intval( $i ) ) . '</a>';
519
520 }
521 if ( $min_pagination > 1 ) {
522 $pagination_url = '?' . $pagination_param . ( $pagination_param ? '&' : '' ) . 'paged=1';
523 $res['html'] = '<a href="' . esc_url( $pagination_url ) . '">最初へ</a> ' . $res['html'];
524 }
525 if ( $max_pagination < $paged_num ) {
526 $pagination_url = '?' . $pagination_param . ( $pagination_param ? '&' : '' ) . 'paged=' . intval( $paged_num );
527 $res['html'] = $res['html'] . ' <a href="' . esc_url( $pagination_url ) . '">最後へ</a>';
528 }
529 $res['html'] = '<div class="bfb_pagination">' . $res['html'] . '</div>';
530
531 $res['count_text'] = '�
532 �' . intval( $res['count'] ) . '件中' . intval( $paged > 0 ? ( $paged + 1 ) : 1 ) . 'ページ目';
533
534 return $res;
535 }
536 }
537