db->insert( $t['hits'], array( 'visitor_id' => $h->visitor_id, 'session_id' => $h->session_id, 'ts' => $h->ts, 'event_type' => $h->event_type, 'path' => $h->path, 'is_entry' => $h->is_entry ? 1 : 0, 'duration' => $h->duration, 'target' => $h->target, ) ); if ( Saddle_Analytics_Event::PAGEVIEW === $h->event_type ) { $this->upsert_visitor( $h ); } else { $this->db->query( $this->db->prepare( "UPDATE {$t['visitors']} SET last_seen = %s WHERE visitor_id = %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $h->ts, $h->visitor_id ) ); } } private function upsert_visitor( Saddle_Analytics_Hit $h ): void { $t = Saddle_Analytics_Install::tables(); // Direct return visits touch last_seen only; last_* survives (spec: visitor update rules). $new_session = $h->is_new_session ? 1 : 0; $session_with_ref = ( $h->is_new_session && Saddle_Analytics_Channel::DIRECT !== $h->channel ) ? 1 : 0; $this->db->query( $this->db->prepare( "INSERT INTO {$t['visitors']} (visitor_id, first_seen, last_seen, first_channel, first_referrer, first_source, first_medium, first_campaign, first_landing, last_channel, last_referrer, last_campaign, country, browser, os, device, pageviews, sessions) VALUES (%s, %s, %s, %d, NULLIF(%s,''), NULLIF(%s,''), NULLIF(%s,''), NULLIF(%s,''), NULLIF(%s,''), %d, NULLIF(%s,''), NULLIF(%s,''), NULLIF(%s,''), NULLIF(%s,''), NULLIF(%s,''), NULLIF(%d,0), 1, 1) ON DUPLICATE KEY UPDATE last_seen = VALUES(last_seen), pageviews = pageviews + 1, sessions = sessions + CASE WHEN %d = 1 THEN 1 ELSE 0 END, last_channel = CASE WHEN %d = 1 THEN VALUES(last_channel) ELSE last_channel END, last_referrer = CASE WHEN %d = 1 THEN VALUES(last_referrer) ELSE last_referrer END, last_campaign = CASE WHEN %d = 1 THEN VALUES(last_campaign) ELSE last_campaign END", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $h->visitor_id, $h->ts, $h->ts, $h->channel, (string) $h->referrer_host, (string) $h->source, (string) $h->medium, (string) $h->campaign, (string) $h->landing, $h->channel, (string) $h->referrer_host, (string) $h->campaign, (string) $h->country, (string) $h->browser, (string) $h->os, (int) $h->device, $new_session, $session_with_ref, $session_with_ref, $session_with_ref ) ); } /** Late duration beacon: stamp the latest pageview of the session. Still no SELECT. */ public function duration( string $visitor_id, string $session_id, int $seconds ): void { $t = Saddle_Analytics_Install::tables(); $this->db->query( $this->db->prepare( "UPDATE {$t['hits']} SET duration = %d WHERE visitor_id = %s AND session_id = %s AND event_type = %d ORDER BY id DESC LIMIT 1", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared min( 32767, max( 0, $seconds ) ), $visitor_id, $session_id, Saddle_Analytics_Event::PAGEVIEW ) ); } }