PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.9.2
Independent Analytics – WordPress Analytics Plugin v2.9.2
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 / Statistics / Statistics.php
independent-analytics / IAWP / Statistics Last commit date
Intervals 1 year ago Campaign_Statistics.php 2 years ago City_Statistics.php 2 years ago Click_Statistics.php 1 year ago Country_Statistics.php 2 years ago Device_Browser_Statistics.php 2 years ago Device_OS_Statistics.php 2 years ago Device_Type_Statistics.php 2 years ago Page_Statistics.php 2 years ago Referrer_Statistics.php 2 years ago Statistic.php 1 year ago Statistics.php 1 year ago
Statistics.php
334 lines
1 <?php
2
3 namespace IAWP\Statistics;
4
5 use DateInterval;
6 use DatePeriod;
7 use DateTime;
8 use IAWP\Date_Range\Date_Range;
9 use IAWP\Form_Submissions\Form;
10 use IAWP\Illuminate_Builder;
11 use IAWP\Plugin_Group;
12 use IAWP\Query;
13 use IAWP\Query_Taps;
14 use IAWP\Rows\Rows;
15 use IAWP\Statistics\Intervals\Interval;
16 use IAWP\Statistics\Intervals\Intervals;
17 use IAWP\Tables;
18 use IAWP\Utils\Calculations;
19 use IAWPSCOPED\Illuminate\Database\Query\Builder;
20 use IAWPSCOPED\Illuminate\Database\Query\JoinClause;
21 use IAWPSCOPED\Illuminate\Support\Collection;
22 use IAWPSCOPED\Illuminate\Support\Str;
23 use IAWP\Utils\Timezone;
24 use Throwable;
25 /** @internal */
26 abstract class Statistics
27 {
28 protected $tables = Tables::class;
29 protected $date_range;
30 protected $rows;
31 protected $chart_interval;
32 private $statistics;
33 private $previous_period_statistics;
34 private $statistics_grouped_by_date_interval;
35 private $unfiltered_statistics;
36 private $previous_period_unfiltered_statistic;
37 private $unfiltered_statistics_grouped_by_date_interval;
38 private $statistic_instances;
39 // The biggest flaw here is that it requires two queries for the stats (current and previous) when
40 // that could be one. I think it would also be possible to reuse the rows query and not limit
41 // by 50 and just SUM() up all the stats columns for the quick stats. Maybe that would be faster
42 // even if two queries were still used. Needs testing.
43 public function __construct(Date_Range $date_range, ?Rows $rows = null, ?Interval $chart_interval = null)
44 {
45 $this->date_range = $date_range;
46 $this->rows = $rows;
47 $this->chart_interval = $chart_interval ?? Intervals::default_for($date_range->number_of_days());
48 if (\is_null($rows)) {
49 $this->statistics = $this->query($this->date_range);
50 $this->previous_period_statistics = $this->query($this->date_range->previous_period());
51 $this->statistics_grouped_by_date_interval = $this->query($this->date_range, null, \true);
52 } else {
53 $this->statistics = $this->query($this->date_range, $rows);
54 $this->previous_period_statistics = $this->query($this->date_range->previous_period(), $rows);
55 $this->statistics_grouped_by_date_interval = $this->query($this->date_range, $rows, \true);
56 $this->unfiltered_statistics = $this->query($this->date_range);
57 $this->previous_period_unfiltered_statistic = $this->query($this->date_range->previous_period());
58 $this->unfiltered_statistics_grouped_by_date_interval = $this->query($this->date_range, null, \true);
59 }
60 $this->statistic_instances = $this->make_statistic_instances();
61 $this->statistic_instances = \array_filter($this->statistic_instances, function (\IAWP\Statistics\Statistic $statistic) {
62 return $statistic->is_enabled();
63 });
64 }
65 /**
66 * @return Statistic[]
67 */
68 public function get_statistics() : array
69 {
70 return $this->statistic_instances;
71 }
72 public function get_grouped_statistics()
73 {
74 // This whole thing is a bit of a mess...
75 return Collection::make($this->statistic_instances)->groupBy(function (\IAWP\Statistics\Statistic $item, int $key) {
76 return Plugin_Group::get_plugin_group($item->plugin_group())->name();
77 })->map(function (Collection $group, $plugin_group) {
78 $items = $group->map(function (\IAWP\Statistics\Statistic $item) {
79 if (!$item->is_group_plugin_enabled()) {
80 return null;
81 }
82 return ['id' => $item->id(), 'name' => $item->name()];
83 })->filter();
84 if ($items->isEmpty()) {
85 return null;
86 }
87 return ['name' => $plugin_group, 'items' => $items->toArray()];
88 })->filter()->values()->toArray();
89 }
90 public function get_statistic(string $statistic_id) : ?\IAWP\Statistics\Statistic
91 {
92 foreach ($this->statistic_instances as $statistic_instance) {
93 if ($statistic_instance->id() == $statistic_id) {
94 return $statistic_instance;
95 }
96 }
97 return null;
98 }
99 public function has_filters() : bool
100 {
101 return !\is_null($this->rows);
102 }
103 public function chart_interval() : Interval
104 {
105 return $this->chart_interval;
106 }
107 /**
108 * I'm sure there's more we could do here. If you get a result back where there isn't a full
109 * page of results or where you're not paginating, then you can just count up the rows...
110 *
111 * @return int|null
112 */
113 public function total_number_of_rows() : ?int
114 {
115 $sessions_table = Query::get_table_name(Query::SESSIONS);
116 $views_table = Query::get_table_name(Query::VIEWS);
117 $column = $this->total_table_rows_column() ?? $this->required_column();
118 $query = Illuminate_Builder::new()->selectRaw("COUNT(DISTINCT {$column}) AS total_table_rows")->from("{$sessions_table} AS sessions")->join("{$views_table} AS views", function (JoinClause $join) {
119 $join->on('sessions.session_id', '=', 'views.session_id');
120 })->tap(Query_Taps::tap_authored_content_check(\true))->when(!\is_null($this->rows), function (Builder $query) {
121 $this->rows->attach_filters($query);
122 })->whereBetween('sessions.created_at', [$this->date_range->iso_start(), $this->date_range->iso_end()])->whereBetween('views.viewed_at', [$this->date_range->iso_start(), $this->date_range->iso_end()]);
123 return $query->value('total_table_rows');
124 }
125 /**
126 * Define which id column to use to count up the total table rows. This is only required
127 * for classes that don't have a required column and don't override required_column
128 *
129 * @return string|null
130 */
131 protected function total_table_rows_column() : ?string
132 {
133 return null;
134 }
135 /**
136 * Statistics can require that a column exists in order to be included. As an example, geos
137 * requires visitors.country_code and campaigns requires sessions.campaign_id
138 *
139 * @return string|null
140 */
141 protected function required_column() : ?string
142 {
143 return null;
144 }
145 /**
146 * @return Statistic[]
147 */
148 protected function make_statistic_instances() : array
149 {
150 $statistics = [$this->make_statistic(['id' => 'visitors', 'name' => \__('Visitors', 'independent-analytics'), 'plugin_group' => 'general', 'is_visible_in_dashboard_widget' => \true]), $this->make_statistic(['id' => 'views', 'name' => \__('Views', 'independent-analytics'), 'plugin_group' => 'general', 'is_visible_in_dashboard_widget' => \true]), $this->make_statistic(['id' => 'sessions', 'name' => \__('Sessions', 'independent-analytics'), 'plugin_group' => 'general']), $this->make_statistic(['id' => 'average_session_duration', 'name' => \__('Average Session Duration', 'independent-analytics'), 'plugin_group' => 'general', 'format' => 'time']), $this->make_statistic(['id' => 'bounce_rate', 'name' => \__('Bounce Rate', 'independent-analytics'), 'plugin_group' => 'general', 'format' => 'percent', 'is_growth_good' => \false, 'compute' => function (object $statistics) {
151 return Calculations::percentage($statistics->bounces, $statistics->sessions);
152 }]), $this->make_statistic(['id' => 'views_per_session', 'name' => \__('Views Per Session', 'independent-analytics'), 'plugin_group' => 'general', 'format' => 'decimal', 'compute' => function (object $statistics) {
153 return Calculations::divide($statistics->total_views, $statistics->sessions, 2);
154 }]), $this->make_statistic(['id' => 'clicks', 'name' => \__('Clicks', 'independent-analytics'), 'plugin_group' => 'general', 'requires_pro' => \true]), $this->make_statistic(['id' => 'wc_orders', 'name' => \__('Orders', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'icon' => $this->get_ecommerce_icon()]), $this->make_statistic(['id' => 'wc_gross_sales', 'name' => \__('Gross Sales', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'icon' => $this->get_ecommerce_icon(), 'format' => 'rounded-currency']), $this->make_statistic(['id' => 'wc_refunds', 'name' => \__('Refunds', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'icon' => $this->get_ecommerce_icon()]), $this->make_statistic(['id' => 'wc_refunded_amount', 'name' => \__('Refunded Amount', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'icon' => $this->get_ecommerce_icon(), 'format' => 'rounded-currency']), $this->make_statistic(['id' => 'wc_net_sales', 'name' => \__('Total Sales', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'icon' => $this->get_ecommerce_icon(), 'format' => 'rounded-currency']), $this->make_statistic(['id' => 'wc_conversion_rate', 'name' => \__('Conversion Rate', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'icon' => $this->get_ecommerce_icon(), 'format' => 'percent']), $this->make_statistic(['id' => 'wc_earnings_per_visitor', 'name' => \__('Earnings Per Visitor', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'icon' => $this->get_ecommerce_icon(), 'format' => 'currency']), $this->make_statistic(['id' => 'wc_average_order_volume', 'name' => \__('Average Order Volume', 'independent-analytics'), 'plugin_group' => 'ecommerce', 'icon' => $this->get_ecommerce_icon(), 'format' => 'rounded-currency']), $this->make_statistic(['id' => 'form_submissions', 'name' => \__('Form Submissions', 'independent-analytics'), 'plugin_group' => 'forms']), $this->make_statistic(['id' => 'form_conversion_rate', 'name' => \__('Form Conversion Rate', 'independent-analytics'), 'plugin_group' => 'forms', 'format' => 'percent', 'compute' => function (object $statistics) {
155 return Calculations::percentage($statistics->form_submissions, $statistics->visitors, 2);
156 }])];
157 foreach (Form::get_forms() as $form) {
158 if (!$form->is_plugin_active()) {
159 continue;
160 }
161 $statistics[] = $this->make_statistic(['id' => 'form_submissions_for_' . $form->id(), 'name' => \sprintf(\_x('%s Submissions', 'Title of the contact form', 'independent-analytics'), $form->title()), 'plugin_group' => 'forms', 'is_subgroup_plugin_active' => $form->is_plugin_active(), 'plugin_group_header' => $form->plugin_name(), 'icon' => $form->icon()]);
162 $statistics[] = $this->make_statistic(['id' => 'form_conversion_rate_for_' . $form->id(), 'name' => \sprintf(\_x('%s Conversion Rate', 'Title of the contact form', 'independent-analytics'), $form->title()), 'plugin_group' => 'forms', 'is_subgroup_plugin_active' => $form->is_plugin_active(), 'plugin_group_header' => $form->plugin_name(), 'icon' => $form->icon(), 'format' => 'percent', 'compute' => function (object $statistics) use($form) {
163 $form_submission_id = 'form_submissions_for_' . $form->id();
164 return Calculations::percentage($statistics->{$form_submission_id}, $statistics->visitors, 2);
165 }]);
166 }
167 return $statistics;
168 }
169 protected function make_statistic(array $attributes) : \IAWP\Statistics\Statistic
170 {
171 $statistic_id = $attributes['id'];
172 if (!\array_key_exists('compute', $attributes)) {
173 $attributes['compute'] = function ($statistics, $statistic_id) {
174 return $statistics->{$statistic_id};
175 };
176 }
177 $attributes['statistic'] = $attributes['compute']($this->statistics, $statistic_id);
178 $attributes['previous_period_statistic'] = $attributes['compute']($this->previous_period_statistics, $statistic_id);
179 $attributes['statistic_over_time'] = $this->fill_in_partial_day_range($this->statistics_grouped_by_date_interval, $attributes);
180 $attributes['unfiltered_statistic'] = $this->has_filters() ? $attributes['compute']($this->unfiltered_statistics, $statistic_id) : null;
181 return new \IAWP\Statistics\Statistic($attributes);
182 }
183 protected function query(Date_Range $range, ?Rows $rows = null, bool $is_grouped_by_date_interval = \false)
184 {
185 $utc_offset = Timezone::utc_offset();
186 $site_offset = Timezone::site_offset();
187 $sessions_table = Query::get_table_name(Query::SESSIONS);
188 $views_table = Query::get_table_name(Query::VIEWS);
189 $orders_table = Query::get_table_name(Query::ORDERS);
190 $form_submissions_table = Query::get_table_name(Query::FORM_SUBMISSIONS);
191 $form_submissions_query = Illuminate_Builder::new()->select(['form_id', 'session_id'])->selectRaw('COUNT(*) AS form_submissions')->from($form_submissions_table, 'form_submissions')->whereBetween('created_at', [$range->iso_start(), $range->iso_end()])->groupBy(['form_id', 'session_id']);
192 $session_statistics = Illuminate_Builder::new();
193 $session_statistics->select('sessions.session_id')->selectRaw('COUNT(DISTINCT views.id) AS views')->selectRaw('COUNT(DISTINCT clicks.click_id) AS clicks')->selectRaw('COUNT(DISTINCT orders.order_id) AS orders')->selectRaw('IFNULL(CAST(SUM(orders.total) AS UNSIGNED), 0) AS gross_sales')->selectRaw('IFNULL(CAST(SUM(orders.total_refunded) AS UNSIGNED), 0) AS total_refunded')->selectRaw('IFNULL(CAST(SUM(orders.total_refunds) AS UNSIGNED), 0) AS total_refunds')->selectRaw('IFNULL(CAST(SUM(orders.total - orders.total_refunded) AS UNSIGNED), 0) AS net_sales')->from("{$sessions_table} AS sessions")->join("{$views_table} AS views", function (JoinClause $join) {
194 $join->on('sessions.session_id', '=', 'views.session_id');
195 })->leftJoin("{$orders_table} AS orders", function (JoinClause $join) {
196 $join->on('views.id', '=', 'orders.initial_view_id')->where('orders.is_included_in_analytics', '=', \true);
197 })->leftJoin("{$this->tables::clicks()} AS clicks", function (JoinClause $join) {
198 $join->on('views.id', '=', 'clicks.view_id');
199 })->tap(Query_Taps::tap_authored_content_check(\true))->when(!\is_null($rows), function (Builder $query) use($rows) {
200 $rows->attach_filters($query);
201 })->whereBetween('sessions.created_at', [$range->iso_start(), $range->iso_end()])->whereBetween('views.viewed_at', [$range->iso_start(), $range->iso_end()])->groupBy('sessions.session_id')->when(!\is_null($this->required_column()), function (Builder $query) {
202 $query->whereNotNull($this->required_column());
203 });
204 $statistics = Illuminate_Builder::new();
205 $statistics->selectRaw('IFNULL(CAST(SUM(sessions.total_views) AS UNSIGNED), 0) AS total_views')->selectRaw('IFNULL(CAST(SUM(session_statistics.views) AS UNSIGNED), 0) AS views')->selectRaw('COUNT(DISTINCT sessions.visitor_id) AS visitors')->selectRaw('COUNT(DISTINCT sessions.session_id) AS sessions')->selectRaw('IFNULL(CAST(SUM(session_statistics.clicks) AS UNSIGNED), 0) AS clicks')->selectRaw('IFNULL(CAST(AVG(TIMESTAMPDIFF(SECOND, sessions.created_at, sessions.ended_at)) AS UNSIGNED), 0) AS average_session_duration')->selectRaw('COUNT(DISTINCT IF(sessions.final_view_id IS NULL, sessions.session_id, NULL)) AS bounces')->selectRaw('IFNULL(CAST(SUM(session_statistics.orders) AS UNSIGNED), 0) AS wc_orders')->selectRaw('IFNULL(CAST(SUM(session_statistics.gross_sales) AS UNSIGNED), 0) AS wc_gross_sales')->selectRaw('IFNULL(CAST(SUM(session_statistics.total_refunds) AS UNSIGNED), 0) AS wc_refunds')->selectRaw('IFNULL(CAST(SUM(session_statistics.total_refunded) AS UNSIGNED), 0) AS wc_refunded_amount')->selectRaw('IFNULL(CAST(SUM(session_statistics.net_sales) AS UNSIGNED), 0) AS wc_net_sales')->selectRaw('IFNULL(SUM(form_submissions.form_submissions), 0) AS form_submissions')->tap(function (Builder $query) {
206 foreach (Form::get_forms() as $form) {
207 $query->selectRaw('IFNULL(SUM(IF(form_submissions.form_id = ?, form_submissions.form_submissions, 0)), 0) AS ' . $form->submissions_column(), [$form->id()]);
208 }
209 })->from("{$sessions_table} AS sessions")->joinSub($session_statistics, 'session_statistics', function (JoinClause $join) {
210 $join->on('sessions.session_id', '=', 'session_statistics.session_id');
211 })->leftJoinSub($form_submissions_query, 'form_submissions', 'sessions.session_id', '=', 'form_submissions.session_id')->whereBetween('sessions.created_at', [$range->iso_start(), $range->iso_end()])->when($is_grouped_by_date_interval, function (Builder $query) use($utc_offset, $site_offset) {
212 if ($this->chart_interval->id() === 'daily') {
213 $query->selectRaw("DATE(CONVERT_TZ(sessions.created_at, '{$utc_offset}', '{$site_offset}')) AS date");
214 } elseif ($this->chart_interval->id() === 'monthly') {
215 $query->selectRaw("DATE_FORMAT(CONVERT_TZ(sessions.created_at, '{$utc_offset}', '{$site_offset}'), '%Y-%m-01 00:00:00') AS date");
216 } elseif ($this->chart_interval->id() === 'weekly') {
217 $day_of_week = \IAWPSCOPED\iawp()->get_option('iawp_dow', 0) + 1;
218 $query->selectRaw("\n IF (\n DAYOFWEEK(CONVERT_TZ(sessions.created_at, '{$utc_offset}', '{$site_offset}')) - {$day_of_week} < 0,\n DATE_FORMAT(SUBDATE(CONVERT_TZ(sessions.created_at, '{$utc_offset}', '{$site_offset}'), DAYOFWEEK(CONVERT_TZ(sessions.created_at, '{$utc_offset}', '{$site_offset}')) - {$day_of_week} + 7), '%Y-%m-%d 00:00:00'),\n DATE_FORMAT(SUBDATE(CONVERT_TZ(sessions.created_at, '{$utc_offset}', '{$site_offset}'), DAYOFWEEK(CONVERT_TZ(sessions.created_at, '{$utc_offset}', '{$site_offset}')) - {$day_of_week}), '%Y-%m-%d 00:00:00')\n ) AS date\n ");
219 } else {
220 $query->selectRaw("DATE_FORMAT(CONVERT_TZ(sessions.created_at, '{$utc_offset}', '{$site_offset}'), '%Y-%m-%d %H:00:00') AS date");
221 }
222 $query->groupByRaw("date");
223 });
224 $outer_query = Illuminate_Builder::new()->selectRaw('statistics.*')->selectRaw('IF(statistics.visitors = 0, 0, (statistics.wc_orders / statistics.visitors) * 100) AS wc_conversion_rate')->selectRaw('IF(statistics.visitors = 0, 0, (statistics.wc_gross_sales - statistics.wc_refunded_amount) / visitors) AS wc_earnings_per_visitor')->selectRaw('IF(statistics.wc_orders = 0, 0, ROUND(CAST(statistics.wc_gross_sales / statistics.wc_orders AS DECIMAL(10, 2)))) AS wc_average_order_volume')->fromSub($statistics, 'statistics');
225 $results = \array_map(function (object $statistic) : object {
226 return $this->clean_up_raw_statistic_row($statistic);
227 }, $outer_query->get()->all());
228 if (!$is_grouped_by_date_interval) {
229 return $results[0];
230 }
231 return $results;
232 }
233 protected function clean_up_raw_statistic_row(object $statistic) : object
234 {
235 if (\property_exists($statistic, 'wc_gross_sales')) {
236 $statistic->wc_gross_sales = \intval($statistic->wc_gross_sales);
237 }
238 if (\property_exists($statistic, 'wc_refunded_amount')) {
239 $statistic->wc_refunded_amount = \intval($statistic->wc_refunded_amount);
240 }
241 if (\property_exists($statistic, 'wc_net_sales')) {
242 $statistic->wc_net_sales = \intval($statistic->wc_net_sales);
243 }
244 if (\property_exists($statistic, 'wc_earnings_per_visitor')) {
245 $statistic->wc_earnings_per_visitor = \intval($statistic->wc_earnings_per_visitor);
246 }
247 foreach ($statistic as $key => $value) {
248 if (Str::startsWith($key, 'form_submissions')) {
249 $statistic->{$key} = \intval($value);
250 }
251 }
252 return $statistic;
253 }
254 /**
255 * @param array $partial_day_range
256 * @param array $attributes
257 *
258 * @return array
259 */
260 private function fill_in_partial_day_range(array $partial_day_range, array $attributes) : array
261 {
262 $original_start = (clone $this->date_range->start())->setTimezone(Timezone::site_timezone());
263 $start = $this->chart_interval->calculate_start_of_interval_for($original_start);
264 $original_end = (clone $this->date_range->end())->setTimezone(Timezone::site_timezone());
265 $end = $this->chart_interval->calculate_start_of_interval_for($original_end);
266 $end->add(new DateInterval('PT1S'));
267 $is_problematic_timezone = \in_array(Timezone::site_timezone()->getName(), ['Asia/Beirut', 'America/Santiago', 'America/Havana', 'America/Asuncion']);
268 // Imagine a scenario where the offset for the start date is -4 and the offset for the
269 // end date is -3. In that case, the DatePeriod is actually going to have its end date
270 // short an hour, causing a date we care about to be chopped off the end. This code makes
271 // sure that the end date is still the day after the last date we care about, so it
272 // can get chopped off without consequence.
273 if ($is_problematic_timezone) {
274 $offset_difference = $end->getOffset() - $start->getOffset();
275 $interval = DateInterval::createFromDateString($offset_difference . ' seconds');
276 $end->add($interval);
277 }
278 $date_range = new DatePeriod($start, $this->chart_interval->date_interval(), $end);
279 $filled_in_data = [];
280 foreach ($date_range as $date) {
281 // If the timezone switches at midnight, there will be a date where there is no midnight. It'll be something
282 // like 1am instead. All dates after the day that switches will also be at 1am even though they have a
283 // midnight. This alters those back to midnight, so they can be matched against correctly.
284 if ($is_problematic_timezone && $date->format('H:i:s') === "01:00:00") {
285 $date->setTime(0, 0, 0);
286 }
287 $stat = $this->get_statistic_for_date($partial_day_range, $date, $attributes);
288 $filled_in_data[] = [$date, $stat];
289 }
290 return $filled_in_data;
291 }
292 /**
293 * @param array $partial_day_range
294 * @param DateTime $datetime_to_match
295 * @param array $attributes
296 *
297 * @return float|int
298 */
299 private function get_statistic_for_date(array $partial_day_range, DateTime $datetime_to_match, array $attributes)
300 {
301 foreach ($partial_day_range as $day) {
302 $date = $day->date;
303 $value = $attributes['compute']($day, $attributes['id']);
304 try {
305 $datetime = new DateTime($date, Timezone::site_timezone());
306 } catch (Throwable $e) {
307 return 0;
308 }
309 // Intentionally using non-strict equality to see if two distinct DateTime objects represent the same time
310 if ($datetime == $datetime_to_match) {
311 if (\is_string($value)) {
312 if (\strpos($value, '.') !== \false) {
313 return \floatval($value);
314 } else {
315 return \intval($value);
316 }
317 }
318 return $value;
319 }
320 }
321 return 0;
322 }
323 private function get_ecommerce_icon() : ?string
324 {
325 if (\IAWPSCOPED\iawp()->is_woocommerce_support_enabled() && !\IAWPSCOPED\iawp()->is_surecart_support_enabled()) {
326 return 'woocommerce';
327 }
328 if (\IAWPSCOPED\iawp()->is_surecart_support_enabled() && !\IAWPSCOPED\iawp()->is_woocommerce_support_enabled()) {
329 return 'surecart';
330 }
331 return null;
332 }
333 }
334