Current File : /var/www/pediatribu/wp-content/plugins/independent-analytics/IAWP/View.php |
<?php
namespace IAWP;
use IAWP\Custom_WordPress_Columns\Views_Column;
use IAWP\Models\Page;
use IAWP\Models\Page_Home;
use IAWP\Models\Page_Post_Type_Archive;
use IAWP\Models\Page_Singular;
use IAWP\Models\Visitor;
use IAWP\Utils\Device;
use IAWP\Utils\String_Util;
use IAWP\Utils\URL;
use IAWPSCOPED\Illuminate\Database\Query\JoinClause;
/** @internal */
class View
{
private $payload;
private $referrer_url;
private $visitor;
private $campaign_fields;
private $viewed_at;
private $resource;
private $session;
/**
* @param array $payload
* @param string|null $referrer_url
* @param Visitor $visitor
* @param array $campaign_fields
* @param \DateTime|null $viewed_at
*/
public function __construct(array $payload, ?string $referrer_url, Visitor $visitor, array $campaign_fields, ?\DateTime $viewed_at = null)
{
$this->payload = $payload;
$this->referrer_url = \is_null($referrer_url) ? '' : \trim($referrer_url);
$this->visitor = $visitor;
$this->campaign_fields = $campaign_fields;
$this->viewed_at = $viewed_at instanceof \DateTime ? $viewed_at : new \DateTime();
$this->resource = $this->fetch_or_create_resource();
// If a resource can't be found or created, a view cannot be recorded
if (\is_null($this->resource)) {
return;
}
$this->session = $this->fetch_or_create_session();
$view_id = $this->create_view();
$this->link_with_previous_view($view_id);
$this->set_session_total_views();
$this->set_sessions_initial_view($view_id);
$this->set_sessions_final_view($view_id);
$this->set_views_postmeta($this->resource);
}
/**
* @return int ID of newly created session
*/
public function create_session() : int
{
$sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
return \IAWP\Illuminate_Builder::new()->from($sessions_table)->insertGetId(['visitor_id' => $this->visitor->id(), 'referrer_id' => $this->fetch_or_create_referrer(), 'country_id' => $this->fetch_or_create_country(), 'city_id' => $this->fetch_or_create_city(), 'campaign_id' => $this->get_campaign(), 'device_type_id' => Device::getInstance()->type_id(), 'device_os_id' => Device::getInstance()->os_id(), 'device_browser_id' => Device::getInstance()->browser_id(), 'is_first_session' => $this->visitor->is_first_session(), 'created_at' => $this->viewed_at()]);
}
public function fetch_or_create_country() : ?int
{
if (!$this->visitor->geoposition()->valid_location()) {
return null;
}
$countries_table = \IAWP\Query::get_table_name(\IAWP\Query::COUNTRIES);
$country_id = \IAWP\Illuminate_Builder::new()->from($countries_table)->where('country_code', '=', $this->visitor->geoposition()->country_code())->where('country', '=', $this->visitor->geoposition()->country())->where('continent', '=', $this->visitor->geoposition()->continent())->value('country_id');
if (!\is_null($country_id)) {
return $country_id;
}
\IAWP\Illuminate_Builder::new()->from($countries_table)->insertOrIgnore(['country_code' => $this->visitor->geoposition()->country_code(), 'country' => $this->visitor->geoposition()->country(), 'continent' => $this->visitor->geoposition()->continent()]);
return \IAWP\Illuminate_Builder::new()->from($countries_table)->where('country_code', '=', $this->visitor->geoposition()->country_code())->where('country', '=', $this->visitor->geoposition()->country())->where('continent', '=', $this->visitor->geoposition()->continent())->value('country_id');
}
public function fetch_or_create_city() : ?int
{
if (!$this->visitor->geoposition()->valid_location()) {
return null;
}
$country_id = $this->fetch_or_create_country();
$cities_table = \IAWP\Query::get_table_name(\IAWP\Query::CITIES);
$city_id = \IAWP\Illuminate_Builder::new()->from($cities_table)->where('country_id', $country_id)->where('subdivision', '=', $this->visitor->geoposition()->subdivision())->where('city', '=', $this->visitor->geoposition()->city())->value('city_id');
if (!\is_null($city_id)) {
return $city_id;
}
\IAWP\Illuminate_Builder::new()->from($cities_table)->insertOrIgnore(['country_id' => $country_id, 'subdivision' => $this->visitor->geoposition()->subdivision(), 'city' => $this->visitor->geoposition()->city()]);
return \IAWP\Illuminate_Builder::new()->from($cities_table)->where('country_id', $country_id)->where('subdivision', '=', $this->visitor->geoposition()->subdivision())->where('city', '=', $this->visitor->geoposition()->city())->value('city_id');
}
/**
* Fetch the last view, if any.
*
* @return int|null
*/
private function fetch_last_viewed_resource() : ?int
{
global $wpdb;
$views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
$session = $this->fetch_current_session();
if (\is_null($session)) {
return null;
}
$view = $wpdb->get_row($wpdb->prepare("\n SELECT * FROM {$views_table} WHERE session_id = %d ORDER BY viewed_at DESC LIMIT 1\n ", $session->session_id));
if (\is_null($view)) {
return null;
}
return $view->resource_id;
}
private function viewed_at() : string
{
return $this->viewed_at->format('Y-m-d\\TH:i:s');
}
private function link_with_previous_view($view_id) : void
{
global $wpdb;
$views_tables = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
$sessions_tables = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
$session = \IAWP\Illuminate_Builder::new()->from($sessions_tables)->where('session_id', '=', $this->session)->first();
if (\is_null($session)) {
return;
}
$final_view_id = $session->final_view_id;
$initial_view_id = $session->initial_view_id;
if (!\is_null($final_view_id)) {
$wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $final_view_id]);
} elseif (!\is_null($initial_view_id)) {
$wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $initial_view_id]);
}
}
private function set_session_total_views()
{
global $wpdb;
$sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
$views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
$wpdb->query($wpdb->prepare("\n UPDATE {$sessions_table} AS sessions\n LEFT JOIN (\n SELECT\n session_id,\n COUNT(*) AS view_count\n FROM\n {$views_table} AS views\n WHERE\n views.session_id = %d\n GROUP BY\n session_id) AS view_counts ON sessions.session_id = view_counts.session_id\n SET\n sessions.total_views = COALESCE(view_counts.view_count, 0)\n WHERE sessions.session_id = %d\n ", $this->session, $this->session));
}
private function set_sessions_initial_view(int $view_id)
{
global $wpdb;
$sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
$wpdb->query($wpdb->prepare("UPDATE {$sessions_table} SET initial_view_id = %d WHERE session_id = %d AND initial_view_id IS NULL", $view_id, $this->session));
}
private function set_sessions_final_view(int $view_id)
{
global $wpdb;
$sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
$wpdb->query($wpdb->prepare("\n UPDATE {$sessions_table} AS sessions\n SET\n sessions.final_view_id = %d,\n sessions.ended_at = %s\n WHERE sessions.session_id = %d AND sessions.initial_view_id IS NOT NULL AND sessions.initial_view_id != %d\n ", $view_id, $this->viewed_at(), $this->session, $view_id));
}
private function create_view() : int
{
$views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
return \IAWP\Illuminate_Builder::new()->from($views_table)->insertGetId(['resource_id' => $this->resource->id(), 'viewed_at' => $this->viewed_at(), 'page' => $this->payload['page'], 'session_id' => $this->session]);
}
private function fetch_resource()
{
global $wpdb;
$resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
$query = '';
$payload_copy = \array_merge($this->payload);
unset($payload_copy['page']);
switch ($payload_copy['resource']) {
case 'singular':
$query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND singular_id = %d", $payload_copy['resource'], $payload_copy['singular_id']);
break;
case 'author_archive':
$query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND author_id = %d", $payload_copy['resource'], $payload_copy['author_id']);
break;
case 'date_archive':
$query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND date_archive = %s", $payload_copy['resource'], $payload_copy['date_archive']);
break;
case 'post_type_archive':
$query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND post_type = %s", $payload_copy['resource'], $payload_copy['post_type']);
break;
case 'term_archive':
$query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND term_id = %s", $payload_copy['resource'], $payload_copy['term_id']);
break;
case 'search':
$query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND search_query = %s", $payload_copy['resource'], $payload_copy['search_query']);
break;
case 'home':
$query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s ", $payload_copy['resource']);
break;
case '404':
$query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND not_found_url = %s", $payload_copy['resource'], $payload_copy['not_found_url']);
break;
case 'virtual_page':
$query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND virtual_page_id = %s", $payload_copy['resource'], $payload_copy['virtual_page_id']);
break;
}
$resource = $wpdb->get_row($query);
if (\is_null($resource)) {
return null;
}
return $resource;
}
private function fetch_or_create_resource() : ?Page
{
global $wpdb;
$resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
$resource = $this->fetch_resource();
if (\is_null($resource)) {
$payload_copy = \array_merge($this->payload);
unset($payload_copy['page']);
$wpdb->insert($resources_table, $payload_copy);
$resource = $this->fetch_resource();
}
if (\is_null($resource)) {
return null;
}
$page = Page::from_row($resource);
$page->update_cache();
return $page;
}
/**
* @return int|null ID of the session that should be used for this view
*/
private function fetch_or_create_session() : ?int
{
$session = $this->fetch_current_session();
if (\is_null($session)) {
return $this->create_session();
}
$is_same_referrer = $this->fetch_or_create_referrer() === $session->referrer_id;
$is_same_resource = \intval($this->fetch_resource()->id) === $this->fetch_last_viewed_resource();
$same_as_previous_view = $is_same_referrer && $is_same_resource;
// The goal here is to prevent opening multiple tabs to the site from creating multiple sessions
if ($is_same_referrer) {
return $session->session_id;
}
// The goal here is to prevent a page refresh from creating another session
if ($this->is_internal_referrer($this->referrer_url) || $same_as_previous_view) {
return $session->session_id;
}
return $this->create_session();
}
/**
* @param string|null $referrer_url
*
* @return bool
*/
private function is_internal_referrer(?string $referrer_url) : bool
{
return !empty($referrer_url) && String_Util::str_starts_with(\strtolower($referrer_url), \strtolower(\site_url()));
}
private function fetch_referrer(array $referrer) : int
{
$referrers_table = \IAWP\Query::get_table_name(\IAWP\Query::REFERRERS);
$id = \IAWP\Illuminate_Builder::new()->select('id')->from($referrers_table)->where('domain', '=', $referrer['domain'])->value('id');
if (\is_null($id)) {
$id = \IAWP\Illuminate_Builder::new()->from($referrers_table)->insertGetId(['domain' => $referrer['domain'], 'type' => $referrer['type'], 'referrer' => $referrer['referrer']]);
}
return $id;
}
private function fetch_or_create_referrer() : int
{
$url = new URL($this->referrer_url);
$domain = $url->get_domain() ?? $this->referrer_url;
$known_referrer = \IAWP\Known_Referrers::get_group_for($domain);
// Is the referrer one of a special set of predefined known referrers?
if ($known_referrer) {
return $this->fetch_referrer(['domain' => $known_referrer['domain'], 'type' => $known_referrer['type'], 'referrer' => $known_referrer['name']]);
}
// Is the referrer invalid or for the current site?
if (!$url->is_valid_url() || $this->is_internal_referrer($this->referrer_url)) {
return $this->fetch_referrer(['domain' => '', 'type' => 'Direct', 'referrer' => 'Direct']);
}
return $this->fetch_referrer(['domain' => $url->get_domain(), 'type' => 'Referrer', 'referrer' => $this->strip_www($url->get_domain())]);
}
private function strip_www(string $string) : string
{
if (\strpos($string, "www.") !== 0) {
return $string;
}
return \substr($string, 4);
}
private function get_campaign() : ?int
{
global $wpdb;
$required_fields = ['utm_source', 'utm_medium', 'utm_campaign'];
$valid = \true;
foreach ($required_fields as $field) {
if (!isset($this->campaign_fields[$field])) {
$valid = \false;
}
}
if (!$valid) {
return null;
}
$campaigns_table = \IAWP\Query::get_table_name(\IAWP\Query::CAMPAIGNS);
$campaign = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$campaigns_table} WHERE landing_page_title = %s AND utm_source = %s AND utm_medium = %s AND utm_campaign = %s AND (utm_term = %s OR (%d = 0 AND utm_term IS NULL)) AND (utm_content = %s OR (%d = 0 AND utm_content IS NULL))", $this->resource->title(), $this->campaign_fields['utm_source'], $this->campaign_fields['utm_medium'], $this->campaign_fields['utm_campaign'], $this->campaign_fields['utm_term'], isset($this->campaign_fields['utm_term']) ? 1 : 0, $this->campaign_fields['utm_content'], isset($this->campaign_fields['utm_content']) ? 1 : 0));
if (!\is_null($campaign)) {
return $campaign->campaign_id;
}
$wpdb->insert($campaigns_table, ['landing_page_title' => $this->resource->title(), 'utm_source' => $this->campaign_fields['utm_source'], 'utm_medium' => $this->campaign_fields['utm_medium'], 'utm_campaign' => $this->campaign_fields['utm_campaign'], 'utm_term' => $this->campaign_fields['utm_term'], 'utm_content' => $this->campaign_fields['utm_content']]);
$campaign = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$campaigns_table} WHERE landing_page_title = %s AND utm_source = %s AND utm_medium = %s AND utm_campaign = %s AND (utm_term = %s OR (%d = 0 AND utm_term IS NULL)) AND (utm_content = %s OR (%d = 0 AND utm_content IS NULL))", $this->resource->title(), $this->campaign_fields['utm_source'], $this->campaign_fields['utm_medium'], $this->campaign_fields['utm_campaign'], $this->campaign_fields['utm_term'], isset($this->campaign_fields['utm_term']) ? 1 : 0, $this->campaign_fields['utm_content'], isset($this->campaign_fields['utm_content']) ? 1 : 0));
if (!\is_null($campaign)) {
return $campaign->campaign_id;
}
return null;
}
private function fetch_current_session() : ?object
{
$sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
$session = \IAWP\Illuminate_Builder::new()->from($sessions_table, 'sessions')->selectRaw('IFNULL(ended_at, created_at) AS latest_view_at')->selectRaw('sessions.*')->where('visitor_id', '=', $this->visitor->id())->havingRaw('latest_view_at > DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE)')->orderBy('latest_view_at', 'DESC')->first();
return $session;
}
private function set_views_postmeta(Page $resource) : void
{
if ($resource instanceof Page_Singular) {
$this->set_views_postmeta_for_singular($resource);
} elseif ($resource instanceof Page_Home) {
$this->set_views_postmeta_for_home($resource);
} elseif ($resource instanceof Page_Post_Type_Archive && $resource->post_type() === 'product') {
$this->set_views_postmeta_for_shop_page($resource);
}
}
private function set_views_postmeta_for_singular(Page_Singular $resource) : void
{
$singular_id = $resource->get_singular_id();
if ($singular_id === null) {
return;
}
$views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
$resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
$total_views = \IAWP\Illuminate_Builder::new()->selectRaw('COUNT(*) AS views')->from("{$resources_table} as resources")->join("{$views_table} AS views", function (JoinClause $join) {
$join->on('resources.id', '=', 'views.resource_id');
})->where('singular_id', '=', $singular_id)->value('views');
\update_post_meta($singular_id, Views_Column::$meta_key, $total_views);
}
private function set_views_postmeta_for_home(Page_Home $resource) : void
{
$blog_page_id = \get_option('page_for_posts');
if (\is_string($blog_page_id) && \ctype_digit($blog_page_id)) {
$blog_page_id = \intval($blog_page_id);
}
$blog_page = \get_post($blog_page_id);
if ($blog_page === null) {
return;
}
$views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
$resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
$total_views = \IAWP\Illuminate_Builder::new()->selectRaw('COUNT(*) AS views')->from("{$resources_table} as resources")->join("{$views_table} AS views", function (JoinClause $join) {
$join->on('resources.id', '=', 'views.resource_id');
})->where('resource', '=', 'home')->value('views');
\update_post_meta($blog_page_id, Views_Column::$meta_key, $total_views);
}
private function set_views_postmeta_for_shop_page(Page_Post_Type_Archive $resource) : void
{
try {
$shop_id = wc_get_page_id('shop');
if ($shop_id === -1) {
return;
}
$views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
$resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
$total_views = \IAWP\Illuminate_Builder::new()->selectRaw('COUNT(*) AS views')->from("{$resources_table} as resources")->join("{$views_table} AS views", function (JoinClause $join) {
$join->on('resources.id', '=', 'views.resource_id');
})->where('resource', '=', 'post_type_archive')->where('post_type', '=', 'product')->value('views');
\update_post_meta($shop_id, Views_Column::$meta_key, $total_views);
} catch (\Throwable $e) {
}
}
}