File: /var/www/studis.kauko.lt/wp-content/plugins/wp-statistics/includes/class-wp-statistics-visitor.php
<?php
namespace WP_STATISTICS;
use WP_STATISTICS\Helper;
use WP_Statistics\Utils\Url;
use WP_Statistics\Models\ViewsModel;
use WP_Statistics\Models\VisitorsModel;
use WP_Statistics\Service\Analytics\VisitorProfile;
use WP_Statistics\Service\Geolocation\GeolocationFactory;
use WP_Statistics\Service\Integrations\IntegrationHelper;
use WP_Statistics\Service\Analytics\DeviceDetection\DeviceHelper;
class Visitor
{
/**
* For each visit to account for several hits.
*
* @var int
*/
public static $coefficient = 1;
/**
* Get Coefficient
*/
public static function getCoefficient()
{
return apply_filters('wp_statistics_coefficient_per_visitor', self::$coefficient);
}
/**
* Check Active Record Visitors
*
* @return mixed
*/
public static function active()
{
return (has_filter('wp_statistics_active_visitors')) ? apply_filters('wp_statistics_active_visitors', true) : true;
}
/**
* Save new Visitor To DB
*
* @param array $visitor
* @param $visitorProfile VisitorProfile
* @return INT
*/
public static function save_visitor($visitor, $visitorProfile)
{
global $wpdb;
# Add Filter Insert ignore
add_filter('query', array('\WP_STATISTICS\DB', 'insert_ignore'), 10);
# Save to WordPress Database
$insert = $wpdb->insert(
DB::table('visitor'),
$visitor
);
if (!$insert) {
if (!empty($wpdb->last_error)) {
\WP_Statistics::log($wpdb->last_error, 'warning');
}
}
# Get Visitor ID
$visitor_id = $wpdb->insert_id;
# Remove ignore filter
remove_filter('query', array('\WP_STATISTICS\DB', 'insert_ignore'), 10);
# Do Action After Save New Visitor
do_action('wp_statistics_save_visitor', $visitor_id, $visitor, $visitorProfile->getCurrentPageType());
return $visitor_id;
}
/**
* Check This ip has recorded in Custom Day
*
* @param $ip
* @param $date
* @param array $fields
* @return bool|object
*/
public static function exist_ip_in_day($ip, $date = false, $fields = [])
{
global $wpdb;
$columns = (empty($fields) ? '*' : Helper::prepareArrayToStringForQuery($fields));
$columns = str_replace("'", '', $columns);
$last_counter = ($date === false ? TimeZone::getCurrentDate('Y-m-d') : $date);
$visitor = $wpdb->get_row(
$wpdb->prepare("SELECT {$columns} FROM `" . DB::table('visitor') . "` WHERE `last_counter` = %s AND `ip` = %s", $last_counter, $ip)
);
return (!$visitor ? false : $visitor);
}
/**
* Record Uniq Visitor Detail in DB
*
* @param array $arg
* @param VisitorProfile $visitorProfile
* @return bool|INT
* @throws \Exception
*/
public static function record($visitorProfile, $arg = array())
{
global $wpdb;
// Define the array of defaults
$defaults = array(
'location' => '',
'exclusion_match' => false,
'exclusion_reason' => '',
'page_id' => 0
);
$args = wp_parse_args($arg, $defaults);
$userAgent = $visitorProfile->getUserAgent();
$same_visitor = $visitorProfile->isIpActiveToday();
// If we have a new Visitor in Day
if (!$same_visitor) {
// Prepare Visitor information
$visitor = array(
'last_counter' => TimeZone::getCurrentDate('Y-m-d'),
'referred' => $visitorProfile->getReferrer(),
'source_name' => $visitorProfile->getSource()->getName(),
'source_channel' => $visitorProfile->getSource()->getChannel(),
'agent' => $userAgent->getBrowser(),
'platform' => $userAgent->getPlatform(),
'version' => $userAgent->getVersion(),
'device' => $userAgent->getDevice(),
'model' => $userAgent->getModel(),
'ip' => $visitorProfile->getProcessedIPForStorage(),
'location' => $visitorProfile->getCountry(),
'city' => $visitorProfile->getCity(),
'region' => $visitorProfile->getRegion(),
'continent' => $visitorProfile->getContinent(),
'user_id' => $visitorProfile->getUserId(),
'UAString' => ((Option::get('store_ua') == true && !IntegrationHelper::shouldTrackAnonymously()) ? $visitorProfile->getHttpUserAgent() : ''),
'hits' => 1,
'honeypot' => ($args['exclusion_reason'] == 'Honeypot' ? 1 : 0),
'first_page' => $args['page_id'],
'first_view' => TimeZone::getCurrentDate(),
'last_page' => $args['page_id'],
'last_view' => TimeZone::getCurrentDate()
);
$visitor = apply_filters('wp_statistics_visitor_information', $visitor);
//Save Visitor TO DB
$visitor_id = self::save_visitor($visitor, $visitorProfile);
} else {
//Get Current Visitor ID
$visitor_id = $same_visitor->ID;
// Update Same Visitor Hits
if ($args['exclusion_reason'] != 'Robot threshold') {
// Action Before Visitor Update
do_action('wp_statistics_update_visitor_hits', $visitor_id, $same_visitor);
$data = [
'hits' => $same_visitor->hits + 1,
'user_id' => !empty($same_visitor->user_id) ? $same_visitor->user_id : $visitorProfile->getUserId()
];
$data['last_page'] = $args['page_id'];
$data['last_view'] = TimeZone::getCurrentDate('Y-m-d H:i:s');
$data = apply_filters('wp_statistics_visitor_data_before_update', $data, $visitorProfile);
$visitorModel = new VisitorsModel();
$visitorModel->updateVisitor($visitor_id, $data);
}
}
$visitor_id = (isset($visitor_id) ? $visitor_id : false);
// Do Action After Record New Visitor
do_action('wp_statistics_record_visitor', $visitor_id);
return $visitor_id;
}
/**
* Saves or updates a visitor relationship entry in the database.
*
* @param $page_id
* @param $visitor_id
* @return int
*/
public static function save_visitors_relationships($page_id, $visitor_id)
{
global $wpdb;
$tableName = DB::table('visitor_relationships');
$currentDate = TimeZone::getCurrentDate('Y-m-d');
/**
* Check if a record already exists for the same visitor_id, page_id, and current date.
* The query counts the number of matching records.
*
* Note: Ideally, this operation should be handled with a REPLACE INTO or INSERT OR REPLACE query.
* However, since the table was not considered a unique key at first for these fields, As they say, "Fools tie knots, and wise men loose them :)" we manually check for the record's existence,
*
*/
$row = $wpdb->get_row(
$wpdb->prepare("SELECT ID, page_id FROM `" . $tableName . "` WHERE `visitor_id` = %d AND DATE(`date`) = %s ORDER BY `date` DESC LIMIT 1", $visitor_id, $currentDate)
);
// Insert a new record in visitor relationship only if the last viewed page is not equal to the current page
if (empty($row) || $row->page_id != $page_id) {
$result = $wpdb->insert($tableName,
array(
'visitor_id' => $visitor_id,
'page_id' => $page_id,
'date' => TimeZone::getCurrentDate()
),
array('%d', '%d', '%s')
);
if (!$result) {
if (!empty($wpdb->last_error)) {
\WP_Statistics::log($wpdb->last_error);
}
}
}
$insert_id = $wpdb->insert_id;
/**
* Trigger a WordPress action hook after saving the visitor relationship.
* This allows for custom actions to be executed.
*/
do_action('wp_statistics_save_visitor_relationship', $page_id, $visitor_id, $insert_id);
return $insert_id;
}
/**
* Get Top Visitors
*
* @param array $arg
* @return array
* @throws \Exception
*/
public static function getTop($arg = array())
{
global $wpdb;
// Define the array of defaults
$defaults = array(
'day' => 'today',
'per_page' => 10,
'paged' => 1,
);
$args = wp_parse_args($arg, $defaults);
// Prepare time
if ($args['day'] == 'today') {
$sql_time = TimeZone::getCurrentDate('Y-m-d');
} else {
$sql_time = date('Y-m-d', strtotime($args['day'])); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
}
// Prepare Query
$args['sql'] = $wpdb->prepare("SELECT * FROM `" . DB::table('visitor') . "` WHERE last_counter = %s ORDER BY hits DESC", $sql_time);
// Get Visitors Data
return self::get($args);
}
/**
* Get Visitors List By Custom Query
*
* @param array $arg
* @return mixed
* @throws \Exception
*/
public static function get($arg = array())
{
global $wpdb;
// Define the array of defaults
$defaults = array(
'per_page' => 10,
'paged' => 1,
'fields' => 'all',
'order' => 'DESC',
'orderby' => 'ID'
);
$args = wp_parse_args($arg, $defaults);
$limit = (($args['paged'] - 1) * $args['per_page']);
// Prepare the Query & Set Pagination
if (empty($args['sql'])) {
$args['sql'] = "SELECT * FROM `" . DB::table('visitor') . "` ORDER BY ID DESC";
}
$args['sql'] = $args['sql'] . $wpdb->prepare(" LIMIT %d, %d", $limit, $args['per_page']);
// Send Request
$result = $wpdb->get_results($args['sql']); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
// Get Visitor Data
return self::prepareData($result);
}
/**
* Prepare Visitor Data
*
* @param array $result
* @return array
* @throws \Exception
*/
public static function prepareData($result = array())
{
// Prepare List
$list = array();
// Push to List
foreach ($result as $items) {
$ip = esc_html($items->ip);
$agent = esc_html($items->agent);
$version = esc_html($items->version);
$platform = esc_html($items->platform);
$geoLocation = false;
$item = array(
'hits' => (int)$items->hits,
'referred' => Referred::get_referrer_link($items->referred),
'refer' => $items->referred,
'date' => date_i18n(get_option('date_format'), strtotime($items->last_counter)),
'agent' => $agent,
'platform' => $platform,
'version' => esc_html($version)
);
if (isset($items->date)) {
$item['date'] = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($items->date));
}
// Push User Data
if ($items->user_id > 0 and User::exists($items->user_id)) {
$user_data = User::get($items->user_id);
$item['user'] = array(
'ID' => $items->user_id,
'user_login' => $user_data['user_login']
);
}
// Push Browser
$item['browser'] = array(
'name' => $agent,
'version' => $version,
'logo' => DeviceHelper::getBrowserLogo($agent),
'link' => Menus::admin_url('visitors', array('agent' => $agent))
);
// Push IP
if (IP::IsHashIP($ip)) {
$item['ip'] = array('value' => substr($ip, 6, 10), 'link' => Menus::admin_url('visitors', array('type' => 'single-visitor', 'visitor_id' => $items->ID)));
} else {
$item['ip'] = array('value' => $ip, 'link' => Menus::admin_url('visitors', array('type' => 'single-visitor', 'visitor_id' => $items->ID)));
$item['map'] = Helper::geoIPTools($ip);
}
/**
* Backward compatibility for the location field
*
* Set location from $items if it's not empty and not 'Unknown', otherwise use GeoIP to get the location
*/
if ($items->location && $items->location !== 'Unknown') {
$location = $items->location;
} else {
$geoLocation = GeolocationFactory::getLocation($ip); // Call once and reuse
$location = $geoLocation['country'];
}
// Push Country
$item['country'] = array(
'location' => $location,
'flag' => Country::flag($location),
'name' => Country::getName($location)
);
/**
* Backward compatibility for the region field
*
* Set city from $items if it's not empty and not 'Unknown', otherwise use GeoIP to get the city
*/
if ($items->location && $items->city !== __('Unknown', 'wp-statistics')) {
$item['city'] = $items->city;
$item['region'] = $items->region;
} else if (isset($geoLocation['city']) && $geoLocation['city']) {
$item['city'] = $geoLocation['city'];
$item['region'] = $geoLocation['region'];
}
// Get What is Page
if (isset($items->page_id)) {
$item['page'] = self::get_page_by_id($items->page_id);
}
$list[] = $item;
}
return $list;
}
/**
* Get Page Information By page ID
*
* @param $page_id
* @return mixed
*/
public static function get_page_by_id($page_id)
{
// Default Params
$params = ['id' => '', 'link' => '', 'title' => '', 'query' => '', 'report' => '', 'sub_page' => ''];
$viewsModel = new ViewsModel();
$item = $viewsModel->getPageRecord(['page_id' => $page_id]);
if (!empty($item)) {
$item = (array) $item;
$postTypes = Helper::get_list_post_type();
$postIdUri = get_page_uri($item['id']);
$dbUri = trim(Url::getPath(home_url($item['uri'])), '/');
$params = Pages::get_page_info($item['id'], $item['type'], $item['uri']);
$linkWithParams = !empty($item['uri']) ? home_url() . $item['uri'] : '';
$params['query'] = Url::getParams($linkWithParams);
$params['id'] = $item['id'];
if ($postIdUri != $dbUri && in_array($item['type'], $postTypes)) {
$params['sub_page'] = $item['uri'];
}
}
return $params;
}
/**
* Get Top Pages Visited by a visitor
*
* @param $visitor_ID
* @param $total
*
* @return mixed
*/
public static function get_pages_by_visitor_id($visitor_ID, $total = 5)
{
global $wpdb;
$visitor_relationships_table = DB::table('visitor_relationships');
$pages_table = DB::table('pages');
// Get Result
return $wpdb->get_results(
$wpdb->prepare("SELECT DISTINCT {$pages_table}.id, {$pages_table}.uri FROM {$pages_table} INNER JOIN {$visitor_relationships_table} ON {$pages_table}.page_id = {$visitor_relationships_table}.page_id WHERE {$visitor_relationships_table}.visitor_id = %d ORDER BY {$pages_table}.count DESC LIMIT %d", $visitor_ID, $total), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
ARRAY_N
);
}
/**
* Count User By Custom Filter
*
* @param array $args
* @return int
*/
public static function Count($args = array())
{
global $wpdb;
$sql = "SELECT COUNT(*) FROM `" . DB::table('visitor') . "`";
$sql .= Helper::getConditionSQL($args);
return $wpdb->get_var($sql);
}
/**
* Get List Of visitor that Registered in WordPress Users
*
* @return array
*/
public static function get_users_visitor()
{
global $wpdb;
$query = $wpdb->get_results(
"SELECT visitors.user_id, users.user_login, users.user_email FROM `" . DB::table('visitor') . "` AS visitors JOIN `" . $wpdb->users . "` AS users ON visitors.user_id = users.ID WHERE visitors.user_id > 0 GROUP BY visitors.user_id ORDER BY visitors.user_id DESC;",
ARRAY_A
);
$item = array();
foreach ($query as $row) {
$item[$row['user_id']] = array(
'user_login' => $row['user_login'],
'user_email' => $row['user_email']
);
}
return $item;
}
}