PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 1.30.0
Independent Analytics – WordPress Analytics Plugin v1.30.0
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 Date_Range 2 years ago Interval 3 years ago Menu_Bar_Stats 2 years ago Migrations 2 years ago Models 2 years ago Queries 2 years ago Statistics 2 years ago Tables 2 years ago Utils 2 years ago Campaign_Builder.php 2 years ago Capability_Manager.php 3 years ago Chart.php 3 years ago Chart_Geo.php 2 years ago Chart_SVG.php 3 years ago City_To_Country_Converter.php 3 years ago Dashboard_Options.php 2 years ago Dashboard_Widget.php 3 years ago Email_Chart.php 2 years ago Email_Reports.php 2 years ago Env.php 2 years ago Filters.php 2 years ago Geo_Database_Background_Job.php 2 years ago Geo_Database_Downloader.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 3 years ago Independent_Analytics.php 2 years ago Interrupt.php 2 years ago Known_Referrers.php 3 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 Reset_Database.php 3 years ago Resource_Identifier.php 3 years ago Settings.php 2 years ago Sort_Configuration.php 3 years ago Track_Resource_Changes.php 3 years ago View.php 2 years ago View_Counter.php 2 years ago WP_Option_Cache_Bust.php 2 years ago WooCommerce_Order.php 2 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\Request;
15 use IAWP_SCOPED\IAWP\Utils\String_Util;
16 use IAWP_SCOPED\IAWP\Utils\URL;
17 class View
18 {
19 private $payload;
20 private $referrer_url;
21 private $visitor;
22 private $campaign_fields;
23 private $viewed_at;
24 private $device_id;
25 private $resource;
26 private $session;
27 /**
28 * @param array $payload
29 * @param string|null $referrer_url
30 * @param Visitor $visitor
31 * @param array $campaign_fields
32 * @param \DateTime|null $viewed_at
33 */
34 public function __construct(array $payload, ?string $referrer_url, Visitor $visitor, array $campaign_fields, ?\DateTime $viewed_at = null)
35 {
36 $this->payload = $payload;
37 $this->referrer_url = \is_null($referrer_url) ? '' : \trim($referrer_url);
38 $this->visitor = $visitor;
39 $this->campaign_fields = $campaign_fields;
40 $this->viewed_at = $viewed_at instanceof \DateTime ? $viewed_at : new \DateTime();
41 $this->device_id = Request::device_id();
42 $this->resource = $this->fetch_or_create_resource();
43 $this->session = $this->fetch_or_create_session();
44 $view_id = $this->create_view();
45 $this->link_with_previous_view($view_id);
46 $this->set_sessions_initial_view($view_id);
47 $this->set_sessions_final_view($view_id);
48 }
49 /**
50 * @return int ID of newly created session
51 */
52 public function create_session() : int
53 {
54 $created_at = $this->viewed_at();
55 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(), 'device_id' => $this->device_id, 'created_at' => $created_at])->last_inserted_id();
56 }
57 /**
58 * Fetch the last view, if any.
59 *
60 * @return int|null
61 */
62 public function fetch_last_viewed_resource() : ?int
63 {
64 global $wpdb;
65 $views_table = Query::get_table_name(Query::VIEWS);
66 $session = Query::query('sessions/get_current_session', ['visitor_id' => $this->visitor->id()])->row();
67 if (\is_null($session)) {
68 return null;
69 }
70 $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));
71 if (\is_null($view)) {
72 return null;
73 }
74 return $view->resource_id;
75 }
76 /**
77 * @return int|null ID of referrer, if any
78 */
79 public function fetch_referrer() : ?int
80 {
81 $url = new URL($this->referrer_url);
82 if (!$url->is_valid_url()) {
83 return null;
84 }
85 if ($this->is_internal_referrer($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 $cities_table = Query::get_table_name(Query::CITIES);
113 $city_id = 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');
114 if (!\is_null($city_id)) {
115 return $city_id;
116 }
117 Illuminate_Builder::get_builder()->from($cities_table)->insertOrIgnore(['country_id' => $country_id, 'subdivision' => $this->visitor->geoposition()->subdivision(), 'city' => $this->visitor->geoposition()->city()]);
118 return 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');
119 }
120 private function viewed_at() : string
121 {
122 return $this->viewed_at->format('Y-m-d\\TH:i:s');
123 }
124 private function link_with_previous_view($view_id) : void
125 {
126 global $wpdb;
127 $views_tables = Query::get_table_name(Query::VIEWS);
128 $session = Query::query('sessions/get_session', ['session_id' => $this->session])->row();
129 if (\is_null($session)) {
130 return;
131 }
132 $final_view_id = $session->final_view_id;
133 $initial_view_id = $session->initial_view_id;
134 if (!\is_null($final_view_id)) {
135 $wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $final_view_id]);
136 } elseif (!\is_null($initial_view_id)) {
137 $wpdb->update($views_tables, ['next_view_id' => $view_id, 'next_viewed_at' => $this->viewed_at()], ['id' => $initial_view_id]);
138 }
139 }
140 private function set_sessions_initial_view(int $view_id)
141 {
142 global $wpdb;
143 $sessions_table = Query::get_table_name(Query::SESSIONS);
144 $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));
145 }
146 private function set_sessions_final_view(int $view_id)
147 {
148 global $wpdb;
149 $sessions_table = Query::get_table_name(Query::SESSIONS);
150 $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));
151 }
152 private function create_view() : ?int
153 {
154 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();
155 }
156 private function fetch_resource()
157 {
158 global $wpdb;
159 $resources_table = Query::get_table_name(Query::RESOURCES);
160 $query = '';
161 $payload_copy = \array_merge($this->payload);
162 unset($payload_copy['page']);
163 switch ($payload_copy['resource']) {
164 case 'singular':
165 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND singular_id = %d", $payload_copy['resource'], $payload_copy['singular_id']);
166 break;
167 case 'author_archive':
168 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND author_id = %d", $payload_copy['resource'], $payload_copy['author_id']);
169 break;
170 case 'date_archive':
171 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND date_archive = %s", $payload_copy['resource'], $payload_copy['date_archive']);
172 break;
173 case 'post_type_archive':
174 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND post_type = %s", $payload_copy['resource'], $payload_copy['post_type']);
175 break;
176 case 'term_archive':
177 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND term_id = %s", $payload_copy['resource'], $payload_copy['term_id']);
178 break;
179 case 'search':
180 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND search_query = %s", $payload_copy['resource'], $payload_copy['search_query']);
181 break;
182 case 'home':
183 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s ", $payload_copy['resource']);
184 break;
185 case '404':
186 $query = $wpdb->prepare("SELECT * FROM {$resources_table} WHERE resource = %s AND not_found_url = %s", $payload_copy['resource'], $payload_copy['not_found_url']);
187 break;
188 }
189 $resource = $wpdb->get_row($query);
190 if (\is_null($resource)) {
191 return null;
192 }
193 return $resource;
194 }
195 private function fetch_or_create_resource() : int
196 {
197 global $wpdb;
198 $resources_table = Query::get_table_name(Query::RESOURCES);
199 $resource = $this->fetch_resource();
200 if (\is_null($resource)) {
201 $payload_copy = \array_merge($this->payload);
202 unset($payload_copy['page']);
203 $wpdb->insert($resources_table, $payload_copy);
204 $resource = $this->fetch_resource();
205 }
206 // Todo - This probably shouldn't happen here... A call should be make to the page and then the page should
207 // know if it's a page type that needs to be cached or not.
208 switch ($resource->resource) {
209 case 'singular':
210 (new Page_Singular($resource))->update_cache();
211 break;
212 case 'author_archive':
213 (new Page_Author_Archive($resource))->update_cache();
214 break;
215 case 'post_type_archive':
216 (new Page_Post_Type_Archive($resource))->update_cache();
217 break;
218 case 'term_archive':
219 (new Page_Term_Archive($resource))->update_cache();
220 break;
221 case 'home':
222 (new Page_Home($resource))->update_cache();
223 break;
224 case 'date_archive':
225 (new Page_Date_Archive($resource))->update_cache();
226 break;
227 case '404':
228 (new Page_Not_Found($resource))->update_cache();
229 break;
230 case 'search':
231 (new Page_Search($resource))->update_cache();
232 break;
233 }
234 return $resource->id;
235 }
236 /**
237 * @param string|null $referrer_url
238 *
239 * @return bool
240 */
241 private function is_internal_referrer(?string $referrer_url) : bool
242 {
243 return !empty($referrer_url) && String_Util::str_starts_with(\strtolower($referrer_url), \strtolower(\site_url()));
244 }
245 /**
246 * @return int|null ID of the session that should be used for this view
247 */
248 private function fetch_or_create_session() : ?int
249 {
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 (!$this->is_internal_referrer($this->referrer_url) && !$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->is_internal_referrer($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