Deploy b92dc4b from main
This commit is contained in:
47
includes/class-peptide-news-analytics.php
Normal file → Executable file
47
includes/class-peptide-news-analytics.php
Normal file → Executable file
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
declare( strict_types=1 );
|
||||
/**
|
||||
* Click analytics tracking and reporting.
|
||||
*
|
||||
@@ -16,7 +17,7 @@ class Peptide_News_Analytics {
|
||||
* @param array $meta Additional context (IP, UA, referrer, etc.).
|
||||
* @return bool
|
||||
*/
|
||||
public static function record_click( $article_id, $meta = array() ) {
|
||||
public static function record_click( int $article_id, array $meta = array() ): bool {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'peptide_news_clicks';
|
||||
@@ -61,7 +62,7 @@ class Peptide_News_Analytics {
|
||||
* @param int $article_id
|
||||
* @param string $ip Anonymized IP for unique visitor counting.
|
||||
*/
|
||||
private static function update_daily_stats( $article_id, $ip ) {
|
||||
private static function update_daily_stats( int $article_id, string $ip ): void {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'peptide_news_daily_stats';
|
||||
@@ -114,7 +115,7 @@ class Peptide_News_Analytics {
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public static function get_top_articles( $start_date, $end_date, $limit = 20 ) {
|
||||
public static function get_top_articles( string $start_date, string $end_date, int $limit = 20 ): array {
|
||||
global $wpdb;
|
||||
|
||||
$stats_table = $wpdb->prefix . 'peptide_news_daily_stats';
|
||||
@@ -143,7 +144,7 @@ class Peptide_News_Analytics {
|
||||
* @param string $end_date
|
||||
* @return array
|
||||
*/
|
||||
public static function get_click_trends( $start_date, $end_date ) {
|
||||
public static function get_click_trends( string $start_date, string $end_date ): array {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'peptide_news_daily_stats';
|
||||
@@ -167,7 +168,7 @@ class Peptide_News_Analytics {
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public static function get_popular_topics( $start_date, $end_date, $limit = 20 ) {
|
||||
public static function get_popular_topics( string $start_date, string $end_date, int $limit = 20 ): array {
|
||||
global $wpdb;
|
||||
|
||||
$stats_table = $wpdb->prefix . 'peptide_news_daily_stats';
|
||||
@@ -221,7 +222,7 @@ class Peptide_News_Analytics {
|
||||
* @param string $end_date
|
||||
* @return array
|
||||
*/
|
||||
public static function get_device_breakdown( $start_date, $end_date ) {
|
||||
public static function get_device_breakdown( string $start_date, string $end_date ): array {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'peptide_news_clicks';
|
||||
@@ -244,7 +245,7 @@ class Peptide_News_Analytics {
|
||||
* @param string $end_date
|
||||
* @return array
|
||||
*/
|
||||
public static function get_source_performance( $start_date, $end_date ) {
|
||||
public static function get_source_performance( string $start_date, string $end_date ): array {
|
||||
global $wpdb;
|
||||
|
||||
$stats_table = $wpdb->prefix . 'peptide_news_daily_stats';
|
||||
@@ -265,27 +266,41 @@ class Peptide_News_Analytics {
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hard cap on CSV export rows to prevent memory exhaustion on large datasets.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const CSV_EXPORT_MAX_ROWS = 50000;
|
||||
|
||||
/**
|
||||
* Export click data as CSV-ready array.
|
||||
*
|
||||
* @param string $start_date
|
||||
* @param string $end_date
|
||||
* @return array
|
||||
* Capped at CSV_EXPORT_MAX_ROWS to prevent memory exhaustion on sites
|
||||
* with large click volumes. The cap is applied at the SQL level so
|
||||
* only the limited result set is ever loaded into PHP memory.
|
||||
*
|
||||
* @param string $start_date Y-m-d format.
|
||||
* @param string $end_date Y-m-d format.
|
||||
* @return array Array of associative arrays (one per click row).
|
||||
*/
|
||||
public static function export_clicks_csv( $start_date, $end_date ) {
|
||||
public static function export_clicks_csv( string $start_date, string $end_date ): array {
|
||||
global $wpdb;
|
||||
|
||||
$clicks_table = $wpdb->prefix . 'peptide_news_clicks';
|
||||
$articles_table = $wpdb->prefix . 'peptide_news_articles';
|
||||
|
||||
return $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT c.clicked_at, a.title, a.source, a.source_url, a.categories,
|
||||
c.referrer_url, c.page_url, c.device_type, c.session_id
|
||||
FROM {$clicks_table} c
|
||||
INNER JOIN {$articles_table} a ON a.id = c.article_id
|
||||
WHERE DATE(c.clicked_at) BETWEEN %s AND %s
|
||||
ORDER BY c.clicked_at DESC",
|
||||
ORDER BY c.clicked_at DESC
|
||||
LIMIT %d",
|
||||
$start_date,
|
||||
$end_date
|
||||
$end_date,
|
||||
self::CSV_EXPORT_MAX_ROWS
|
||||
), ARRAY_A );
|
||||
}
|
||||
|
||||
@@ -294,7 +309,7 @@ class Peptide_News_Analytics {
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private static function get_client_ip() {
|
||||
private static function get_client_ip(): ?string {
|
||||
$ip_keys = array( 'HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR' );
|
||||
|
||||
foreach ( $ip_keys as $key ) {
|
||||
@@ -316,7 +331,7 @@ class Peptide_News_Analytics {
|
||||
* @param string $ip
|
||||
* @return string
|
||||
*/
|
||||
private static function anonymize_ip( $ip ) {
|
||||
private static function anonymize_ip( string $ip ): string {
|
||||
if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
|
||||
return preg_replace( '/\.\d+$/', '.0', $ip );
|
||||
}
|
||||
@@ -332,7 +347,7 @@ class Peptide_News_Analytics {
|
||||
* @param string $ua
|
||||
* @return string
|
||||
*/
|
||||
private static function detect_device( $ua ) {
|
||||
private static function detect_device( string $ua ): string {
|
||||
$ua = strtolower( $ua );
|
||||
|
||||
if ( preg_match( '/(tablet|ipad|playbook|silk)/', $ua ) ) {
|
||||
|
||||
Reference in New Issue
Block a user