PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 1.28.3
Independent Analytics – WordPress Analytics Plugin v1.28.3
2.15.5 2.15.4 2.15.3 2.15.2 2.15.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 3 years ago Date_Range 3 years ago Interval 3 years ago Menu_Bar_Stats 3 years ago Migrations 3 years ago Models 3 years ago Queries 3 years ago Statistics 3 years ago Tables 3 years ago Utils 3 years ago Campaign_Builder.php 3 years ago Capability_Manager.php 3 years ago Chart.php 3 years ago Chart_Geo.php 3 years ago Chart_SVG.php 3 years ago City_To_Country_Converter.php 3 years ago Dashboard_Options.php 3 years ago Dashboard_Widget.php 3 years ago Email_Chart.php 3 years ago Email_Reports.php 3 years ago Filters.php 3 years ago Geo_Database_Background_Job.php 3 years ago Geo_Database_Downloader.php 3 years ago Geoposition.php 3 years ago Health_Check.php 3 years ago Illuminate_Builder.php 3 years ago Independent_Analytics.php 3 years ago Known_Referrers.php 3 years ago Query.php 3 years ago Quick_Stats.php 3 years ago REST_API.php 3 years ago Real_Time.php 3 years ago Reset_Database.php 3 years ago Resource_Identifier.php 3 years ago Settings.php 3 years ago Sort_Configuration.php 3 years ago Track_Resource_Changes.php 3 years ago View.php 3 years ago View_Counter.php 3 years ago WP_Option_Cache_Bust.php 3 years ago WooCommerce_Order.php 3 years ago
View.php
305 lines
1 <?php
2
3 namespace IAWP_SCOPED\IAWP;
4
5 use IAWP_SCOPED\IAWP\Models\Page_Author_Archive;
6 use IAWP_SCOPED\IAWP\Models\Page_Date_Archive;
7 use IAWP_SCOPED\IAWP\Models\Page_Home;
8 use IAWP_SCOPED\IAWP\Models\Page_Not_Found;
9 use IAWP_SCOPED\IAWP\Models\Page_Post_Type_Archive;
10 use IAWP_SCOPED\IAWP\Models\Page_Search;
11 use IAWP_SCOPED\IAWP\Models\Page_Singular;
12 use IAWP_SCOPED\IAWP\Models\Page_Term_Archive;
13 use IAWP_SCOPED\IAWP\Models\Visitor;
14 use IAWP_SCOPED\IAWP\Utils\String_Util;
15 use IAWP_SCOPED\IAWP\Utils\URL;
16 class View
17 {
18 private $payload;
19 private $referrer_url;
20 private $visitor;
21 private $campaign_fields;
22 private $viewed_at;
23 private $resource;
24 private $session;
25 /**
26 * @param array $payload
27 * @param string|null $referrer_url
28 * @param Visitor $visitor
29 * @param array $campaign_fields
30 * @param \DateTime|null $viewed_at
31 */
32 public function __construct(array $payload, ?string $referrer_url, Visitor $visitor, array $campaign_fields, ?\DateTime $viewed_at = null)
33 {
34 $this->payload = $payload;
35 $this->referrer_url = \trim($referrer_url);
36 $this->visitor = $visitor;
37 $this->campaign_fields = $campaign_fields;
38 $this->viewed_at = $viewed_at instanceof \DateTime ? $viewed_at : new \DateTime();
39 $this->resource = $this->fetch_or_create_resource();
40 $this->session = $this->fetch_or_create_session();
41 $view_id = $this->create_view();
42 $this->link_with_previous_view($view_id);
43 $this->set_sessions_initial_view($view_id);
44 $this->set_sessions_final_view($view_id);
45 }
46 /**
47 * @return int ID of newly created session
48 */
49 public function create_session() : int
50 {
51 $created_at = $this->viewed_at();
52 return Query::query('sessions/create_session', ['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(), 'created_at' => $created_at])->last_inserted_id();
53 }
54 /**
55 * Fetch the last view, if any.
56 *
57 * @return int|null
58 */
59 public function fetch_last_viewed_resource() : ?int
60 {
61 global $wpdb;
62 $views_table = Query::get_table_name(Query::VIEWS);
63 $session = Query::query('sessions/get_current_session', ['visitor_id' => $this->visitor->id()])->row();
64 if (\is_null($session)) {
65 return null;
66 }
67 $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));
68 if (\is_null($view)) {
69 return null;
70 }
71 return $view->resource_id;
72 }
73 /**
74 * @return int|null ID of referrer, if any
75 */
76 public function fetch_referrer() : ?int
77 {
78 if (!isset($this->referrer_url) || \strlen($this->referrer_url) === 0) {
79 return null;
80 }
81 $url = new URL($this->referrer_url);
82 if (!$url->is_valid_url()) {
83 return null;
84 }
85 if ($this->from_internal_page($this->referrer_url)) {
86 return null;
87 }
88 $referrer = Query::query('get_referrer', ['domain' => $url->get_domain()])->row();
89 if (\is_null($referrer)) {
90 return null;
91 }
92 return $referrer->id;
93 }
94 public function fetch_or_create_country() : ?int
95 {
96 if (!$this->visitor->geoposition()->valid_location()) {
97 return null;
98 }
99 Query::query('create_country', ['country_code' => $this->visitor->geoposition()->country_code(), 'country' => $this->visitor->geoposition()->country(), 'continent' => $this->visitor->geoposition()->continent()]);
100 $country = Query::query('get_country', ['country_code' => $this->visitor->geoposition()->country_code(), 'country' => $this->visitor->geoposition()->country(), 'continent' => $this->visitor->geoposition()->continent()])->row();
101 if (\is_null($country)) {
102 return null;
103 }
104 return $country->country_id;
105 }
106 public function fetch_or_create_city() : ?int
107 {
108 if (!$this->visitor->geoposition()->valid_location()) {
109 return null;
110 }
111 $country_id = $this->fetch_or_create_country();
112 Query::query('create_city', ['country_id' => $country_id, 'subdivision' => $this->visitor->geoposition()->subdivision(), 'city' => $this->visitor->geoposition()->city()]);
113 $city = Query::query('get_city', ['country_id' => $country_id, 'subdivision' => $this->visitor->geoposition()->subdivision(), 'city' => $this->visitor->geoposition()->city()])->row();
114 if (\is_null($city)) {
115 return null;
116 }
117 return $city->city_id;
118 }
119 private function viewed_at() : string
120 {
121 return \strstr($this->viewed_at->format('c'), '+', \true);
122 }
123 private function link_with_previous_view($view_id) : void
124 {
125 global $wpdb;
126 $views_tables = Query::get_table_name(Query::VIEWS);
127 $session = Query::query('sessions/get_session', ['session_id' => $this->session])->row();
128 if (\is_null($session)) {
129 return;
130 }
131 $final_view_id = $session->final_view_id;
132 $initial_view_id = $session->initial_view_id;
133 if (!\is_null($final_view_id)) {
134 $wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $final_view_id]);
135 } elseif (!\is_null($initial_view_id)) {
136 $wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $initial_view_id]);
137 }
138 }
139 private function set_sessions_initial_view(int $view_id)
140 {
141 global $wpdb;
142 $sessions_table = Query::get_table_name(Query::SESSIONS);
143 $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));
144 }
145 private function set_sessions_final_view(int $view_id)
146 {
147 global $wpdb;
148 $sessions_table = Query::get_table_name(Query::SESSIONS);
149 $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));
150 }
151 private function create_view() : ?int
152 {
153 return Query::query('create_view', ['resource_id' => $this->resource, 'viewed_at' => $this->viewed_at(), 'page' => $this->payload['page'], 'session_id' => $this->session])->last_inserted_id();
154 }
155 private function fetch_resource()
156 {
157 global $wpdb;
158 $resources_table = Query::get_table_name(Query::RESOURCES);
159 $query = '';
160 $payload_copy = \array_merge($this->payload);
161 unset($payload_copy['page']);
162 switch ($payload_copy['resource']) {
163 case 'singular':
164 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND singular_id = %d", $payload_copy['resource'], $payload_copy['singular_id']);
165 break;
166 case 'author_archive':
167 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND author_id = %d", $payload_copy['resource'], $payload_copy['author_id']);
168 break;
169 case 'date_archive':
170 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND date_archive = %s", $payload_copy['resource'], $payload_copy['date_archive']);
171 break;
172 case 'post_type_archive':
173 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND post_type = %s", $payload_copy['resource'], $payload_copy['post_type']);
174 break;
175 case 'term_archive':
176 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND term_id = %s", $payload_copy['resource'], $payload_copy['term_id']);
177 break;
178 case 'search':
179 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND search_query = %s", $payload_copy['resource'], $payload_copy['search_query']);
180 break;
181 case 'home':
182 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s ", $payload_copy['resource']);
183 break;
184 case '404':
185 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND not_found_url = %s", $payload_copy['resource'], $payload_copy['not_found_url']);
186 break;
187 }
188 $resource = $wpdb->get_row($query);
189 if (\is_null($resource)) {
190 return null;
191 }
192 return $resource;
193 }
194 private function fetch_or_create_resource() : int
195 {
196 global $wpdb;
197 $resources_table = Query::get_table_name(Query::RESOURCES);
198 $resource = $this->fetch_resource();
199 if (\is_null($resource)) {
200 $payload_copy = \array_merge($this->payload);
201 unset($payload_copy['page']);
202 $wpdb->insert($resources_table, $payload_copy);
203 $resource = $this->fetch_resource();
204 }
205 // Todo - This probably shouldn't happen here... A call should be make to the page and then the page should
206 // know if it's a page type that needs to be cached or not.
207 switch ($resource->resource) {
208 case 'singular':
209 (new Page_Singular($resource))->update_cache();
210 break;
211 case 'author_archive':
212 (new Page_Author_Archive($resource))->update_cache();
213 break;
214 case 'post_type_archive':
215 (new Page_Post_Type_Archive($resource))->update_cache();
216 break;
217 case 'term_archive':
218 (new Page_Term_Archive($resource))->update_cache();
219 break;
220 case 'home':
221 (new Page_Home($resource))->update_cache();
222 break;
223 case 'date_archive':
224 (new Page_Date_Archive($resource))->update_cache();
225 break;
226 case '404':
227 (new Page_Not_Found($resource))->update_cache();
228 break;
229 case 'search':
230 (new Page_Search($resource))->update_cache();
231 break;
232 }
233 return $resource->id;
234 }
235 /**
236 * @param string|null $referrer_url
237 *
238 * @return bool
239 */
240 private function from_internal_page(?string $referrer_url) : bool
241 {
242 return !empty($referrer_url) && String_Util::str_starts_with(\strtolower($referrer_url), \strtolower(\site_url()));
243 }
244 /**
245 * @return int|null ID of the session that should be used for this view
246 */
247 private function fetch_or_create_session() : ?int
248 {
249 $from_external_page = !$this->from_internal_page($this->referrer_url);
250 $session = Query::query('sessions/get_current_session', ['visitor_id' => $this->visitor->id()])->row();
251 if (\is_null($session)) {
252 return $this->create_session();
253 }
254 $same_referrer = $this->fetch_referrer() === $session->referrer_id;
255 $same_resource = \intval($this->fetch_resource()->id) === $this->fetch_last_viewed_resource();
256 $same_as_previous_view = $same_referrer && $same_resource;
257 // The goal here is to prevent a page refresh from creating another session
258 if ($from_external_page && !$same_as_previous_view) {
259 return $this->create_session();
260 }
261 return $session->session_id;
262 }
263 private function fetch_or_create_referrer() : ?int
264 {
265 $referrer_id = $this->fetch_referrer();
266 if (!\is_null($referrer_id)) {
267 return $referrer_id;
268 }
269 $url = new URL($this->referrer_url);
270 if (!$url->is_valid_url()) {
271 return null;
272 }
273 if ($this->from_internal_page($this->referrer_url)) {
274 return null;
275 }
276 Query::query('create_referrer', ['domain' => $url->get_domain()]);
277 return $this->fetch_referrer();
278 }
279 private function get_campaign() : ?int
280 {
281 global $wpdb;
282 $required_fields = ['utm_source', 'utm_medium', 'utm_campaign'];
283 $valid = \true;
284 foreach ($required_fields as $field) {
285 if (!isset($this->campaign_fields[$field])) {
286 $valid = \false;
287 }
288 }
289 if (!$valid) {
290 return null;
291 }
292 $campaigns_table = Query::get_table_name(Query::CAMPAIGNS);
293 $campaign = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$campaigns_table} WHERE 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->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));
294 if (!\is_null($campaign)) {
295 return $campaign->campaign_id;
296 }
297 $wpdb->insert($campaigns_table, ['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']]);
298 $campaign = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$campaigns_table} WHERE 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->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));
299 if (!\is_null($campaign)) {
300 return $campaign->campaign_id;
301 }
302 return null;
303 }
304 }
305