PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.3.1
Independent Analytics – WordPress Analytics Plugin v2.3.1
2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / IAWP / View.php
independent-analytics / IAWP Last commit date
AJAX 2 years ago Admin_Page 2 years ago Date_Range 2 years ago Filter_Lists 2 years ago Interval 2 years ago Menu_Bar_Stats 2 years ago Migrations 2 years ago Models 2 years ago Public_API 2 years ago Rows 2 years ago Statistics 2 years ago Tables 2 years ago Utils 2 years ago Campaign_Builder.php 2 years ago Capability_Manager.php 2 years ago Chart.php 2 years ago Chart_Geo.php 2 years ago Cron_Manager.php 2 years ago Current_Traffic_Finder.php 2 years ago Dashboard_Options.php 2 years ago Dashboard_Widget.php 2 years ago Database.php 2 years ago Database_Manager.php 2 years ago Email_Chart.php 2 years ago Email_Reports.php 2 years ago Empty_Report_Option.php 2 years ago Env.php 2 years ago Filters.php 2 years ago Geo_Database_Background_Job.php 2 years ago Geo_Database_Manager.php 2 years ago Geoposition.php 2 years ago Icon_Directory.php 2 years ago Icon_Directory_Factory.php 2 years ago Illuminate_Builder.php 2 years ago Independent_Analytics.php 2 years ago Interrupt.php 2 years ago Known_Referrers.php 2 years ago Plugin_Conflict_Detector.php 2 years ago Query.php 2 years ago Quick_Stats.php 2 years ago REST_API.php 2 years ago Real_Time.php 2 years ago Report.php 2 years ago Report_Finder.php 2 years ago Report_Options_Parser.php 2 years ago Resource_Identifier.php 2 years ago Settings.php 2 years ago Sort_Configuration.php 2 years ago Track_Resource_Changes.php 2 years ago View.php 2 years ago View_Counter.php 2 years ago Visitors_Over_Time_Finder.php 2 years ago WP_Option_Cache_Bust.php 2 years ago WooCommerce_Order.php 2 years ago WooCommerce_Referrer_Meta_Box.php 2 years ago
View.php
284 lines
1 <?php
2
3 namespace IAWP;
4
5 use IAWP\Models\Page;
6 use IAWP\Models\Visitor;
7 use IAWP\Utils\Device;
8 use IAWP\Utils\String_Util;
9 use IAWP\Utils\URL;
10 /** @internal */
11 class View
12 {
13 private $payload;
14 private $referrer_url;
15 private $visitor;
16 private $campaign_fields;
17 private $viewed_at;
18 private $resource;
19 private $session;
20 /**
21 * @param array $payload
22 * @param string|null $referrer_url
23 * @param Visitor $visitor
24 * @param array $campaign_fields
25 * @param \DateTime|null $viewed_at
26 */
27 public function __construct(array $payload, ?string $referrer_url, Visitor $visitor, array $campaign_fields, ?\DateTime $viewed_at = null)
28 {
29 $this->payload = $payload;
30 $this->referrer_url = \is_null($referrer_url) ? '' : \trim($referrer_url);
31 $this->visitor = $visitor;
32 $this->campaign_fields = $campaign_fields;
33 $this->viewed_at = $viewed_at instanceof \DateTime ? $viewed_at : new \DateTime();
34 $this->resource = $this->fetch_or_create_resource();
35 $this->session = $this->fetch_or_create_session();
36 $view_id = $this->create_view();
37 $this->link_with_previous_view($view_id);
38 $this->set_session_total_views();
39 $this->set_sessions_initial_view($view_id);
40 $this->set_sessions_final_view($view_id);
41 }
42 /**
43 * @return int ID of newly created session
44 */
45 public function create_session() : int
46 {
47 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
48 return \IAWP\Illuminate_Builder::get_builder()->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(), 'created_at' => $this->viewed_at()]);
49 }
50 public function fetch_or_create_country() : ?int
51 {
52 if (!$this->visitor->geoposition()->valid_location()) {
53 return null;
54 }
55 $countries_table = \IAWP\Query::get_table_name(\IAWP\Query::COUNTRIES);
56 $country_id = \IAWP\Illuminate_Builder::get_builder()->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');
57 if (!\is_null($country_id)) {
58 return $country_id;
59 }
60 \IAWP\Illuminate_Builder::get_builder()->from($countries_table)->insertOrIgnore(['country_code' => $this->visitor->geoposition()->country_code(), 'country' => $this->visitor->geoposition()->country(), 'continent' => $this->visitor->geoposition()->continent()]);
61 return \IAWP\Illuminate_Builder::get_builder()->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');
62 }
63 public function fetch_or_create_city() : ?int
64 {
65 if (!$this->visitor->geoposition()->valid_location()) {
66 return null;
67 }
68 $country_id = $this->fetch_or_create_country();
69 $cities_table = \IAWP\Query::get_table_name(\IAWP\Query::CITIES);
70 $city_id = \IAWP\Illuminate_Builder::get_builder()->from($cities_table)->where('country_id', $country_id)->where('subdivision', '=', $this->visitor->geoposition()->subdivision())->where('city', '=', $this->visitor->geoposition()->city())->value('city_id');
71 if (!\is_null($city_id)) {
72 return $city_id;
73 }
74 \IAWP\Illuminate_Builder::get_builder()->from($cities_table)->insertOrIgnore(['country_id' => $country_id, 'subdivision' => $this->visitor->geoposition()->subdivision(), 'city' => $this->visitor->geoposition()->city()]);
75 return \IAWP\Illuminate_Builder::get_builder()->from($cities_table)->where('country_id', $country_id)->where('subdivision', '=', $this->visitor->geoposition()->subdivision())->where('city', '=', $this->visitor->geoposition()->city())->value('city_id');
76 }
77 /**
78 * Fetch the last view, if any.
79 *
80 * @return int|null
81 */
82 private function fetch_last_viewed_resource() : ?int
83 {
84 global $wpdb;
85 $views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
86 $session = $this->get_current_session();
87 if (\is_null($session)) {
88 return null;
89 }
90 $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));
91 if (\is_null($view)) {
92 return null;
93 }
94 return $view->resource_id;
95 }
96 private function viewed_at() : string
97 {
98 return $this->viewed_at->format('Y-m-d\\TH:i:s');
99 }
100 private function link_with_previous_view($view_id) : void
101 {
102 global $wpdb;
103 $views_tables = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
104 $sessions_tables = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
105 $session = \IAWP\Illuminate_Builder::get_builder()->from($sessions_tables)->where('session_id', '=', $this->session)->first();
106 if (\is_null($session)) {
107 return;
108 }
109 $final_view_id = $session->final_view_id;
110 $initial_view_id = $session->initial_view_id;
111 if (!\is_null($final_view_id)) {
112 $wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $final_view_id]);
113 } elseif (!\is_null($initial_view_id)) {
114 $wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $initial_view_id]);
115 }
116 }
117 private function set_session_total_views()
118 {
119 global $wpdb;
120 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
121 $views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
122 $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));
123 }
124 private function set_sessions_initial_view(int $view_id)
125 {
126 global $wpdb;
127 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
128 $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));
129 }
130 private function set_sessions_final_view(int $view_id)
131 {
132 global $wpdb;
133 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
134 $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));
135 }
136 private function create_view() : int
137 {
138 $views_table = \IAWP\Query::get_table_name(\IAWP\Query::VIEWS);
139 return \IAWP\Illuminate_Builder::get_builder()->from($views_table)->insertGetId(['resource_id' => $this->resource->id(), 'viewed_at' => $this->viewed_at(), 'page' => $this->payload['page'], 'session_id' => $this->session]);
140 }
141 private function fetch_resource()
142 {
143 global $wpdb;
144 $resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
145 $query = '';
146 $payload_copy = \array_merge($this->payload);
147 unset($payload_copy['page']);
148 switch ($payload_copy['resource']) {
149 case 'singular':
150 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND singular_id = %d", $payload_copy['resource'], $payload_copy['singular_id']);
151 break;
152 case 'author_archive':
153 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND author_id = %d", $payload_copy['resource'], $payload_copy['author_id']);
154 break;
155 case 'date_archive':
156 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND date_archive = %s", $payload_copy['resource'], $payload_copy['date_archive']);
157 break;
158 case 'post_type_archive':
159 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND post_type = %s", $payload_copy['resource'], $payload_copy['post_type']);
160 break;
161 case 'term_archive':
162 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND term_id = %s", $payload_copy['resource'], $payload_copy['term_id']);
163 break;
164 case 'search':
165 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND search_query = %s", $payload_copy['resource'], $payload_copy['search_query']);
166 break;
167 case 'home':
168 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s ", $payload_copy['resource']);
169 break;
170 case '404':
171 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND not_found_url = %s", $payload_copy['resource'], $payload_copy['not_found_url']);
172 break;
173 case 'virtual_page':
174 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND virtual_page_id = %s", $payload_copy['resource'], $payload_copy['virtual_page_id']);
175 break;
176 }
177 $resource = $wpdb->get_row($query);
178 if (\is_null($resource)) {
179 return null;
180 }
181 return $resource;
182 }
183 private function fetch_or_create_resource() : Page
184 {
185 global $wpdb;
186 $resources_table = \IAWP\Query::get_table_name(\IAWP\Query::RESOURCES);
187 $resource = $this->fetch_resource();
188 if (\is_null($resource)) {
189 $payload_copy = \array_merge($this->payload);
190 unset($payload_copy['page']);
191 $wpdb->insert($resources_table, $payload_copy);
192 $resource = $this->fetch_resource();
193 }
194 $page = Page::from_row($resource);
195 $page->update_cache();
196 return $page;
197 }
198 /**
199 * @return int|null ID of the session that should be used for this view
200 */
201 private function fetch_or_create_session() : ?int
202 {
203 $session = $this->get_current_session();
204 if (\is_null($session)) {
205 return $this->create_session();
206 }
207 $same_referrer = $this->fetch_or_create_referrer() === $session->referrer_id;
208 $same_resource = \intval($this->fetch_resource()->id) === $this->fetch_last_viewed_resource();
209 $same_as_previous_view = $same_referrer && $same_resource;
210 // The goal here is to prevent a page refresh from creating another session
211 if (!$this->is_internal_referrer($this->referrer_url) && !$same_as_previous_view) {
212 return $this->create_session();
213 }
214 return $session->session_id;
215 }
216 /**
217 * @param string|null $referrer_url
218 *
219 * @return bool
220 */
221 private function is_internal_referrer(?string $referrer_url) : bool
222 {
223 return !empty($referrer_url) && String_Util::str_starts_with(\strtolower($referrer_url), \strtolower(\site_url()));
224 }
225 private function fetch_referrer(array $referrer) : int
226 {
227 $referrers_table = \IAWP\Query::get_table_name(\IAWP\Query::REFERRERS);
228 $id = \IAWP\Illuminate_Builder::get_builder()->select('id')->from($referrers_table)->where('domain', '=', $referrer['domain'])->value('id');
229 if (\is_null($id)) {
230 $id = \IAWP\Illuminate_Builder::get_builder()->from($referrers_table)->insertGetId(['domain' => $referrer['domain'], 'type' => $referrer['type'], 'referrer' => $referrer['referrer']]);
231 }
232 return $id;
233 }
234 private function fetch_or_create_referrer() : int
235 {
236 $url = new URL($this->referrer_url);
237 if (!$url->is_valid_url() || $this->is_internal_referrer($this->referrer_url)) {
238 return $this->fetch_referrer(['domain' => '', 'type' => 'Direct', 'referrer' => 'Direct']);
239 } elseif (!\is_null(\IAWP\Known_Referrers::get_group_for($url->get_domain()))) {
240 $group = \IAWP\Known_Referrers::get_group_for($url->get_domain());
241 return $this->fetch_referrer(['domain' => $group['domain'], 'type' => $group['type'], 'referrer' => $group['name']]);
242 } else {
243 return $this->fetch_referrer(['domain' => $url->get_domain(), 'type' => 'Referrer', 'referrer' => $this->strip_www($url->get_domain())]);
244 }
245 }
246 private function strip_www(string $string) : string
247 {
248 if (\strpos($string, "www.") !== 0) {
249 return $string;
250 }
251 return \substr($string, 4);
252 }
253 private function get_campaign() : ?int
254 {
255 global $wpdb;
256 $required_fields = ['utm_source', 'utm_medium', 'utm_campaign'];
257 $valid = \true;
258 foreach ($required_fields as $field) {
259 if (!isset($this->campaign_fields[$field])) {
260 $valid = \false;
261 }
262 }
263 if (!$valid) {
264 return null;
265 }
266 $campaigns_table = \IAWP\Query::get_table_name(\IAWP\Query::CAMPAIGNS);
267 $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));
268 if (!\is_null($campaign)) {
269 return $campaign->campaign_id;
270 }
271 $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']]);
272 $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));
273 if (!\is_null($campaign)) {
274 return $campaign->campaign_id;
275 }
276 return null;
277 }
278 private function get_current_session()
279 {
280 $sessions_table = \IAWP\Query::get_table_name(\IAWP\Query::SESSIONS);
281 return \IAWP\Illuminate_Builder::get_builder()->from($sessions_table)->where('visitor_id', '=', $this->visitor->id())->orderBy('created_at', 'desc')->first();
282 }
283 }
284