PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / site / helpers / libraries / ical / reader / optimizer.php
vikappointments / site / helpers / libraries / ical / reader Last commit date
file.php 2 days ago http.php 2 days ago index.html 2 days ago optimizer.php 2 days ago
optimizer.php
152 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * Decorator used to optimize the ICS file downloaded through a different reader.
16 *
17 * Here are listed all the supported configuration settings:
18 *
19 * @var integer filesize The optimization will be performed only in case the
20 * size of the buffer exceeds the specified threshold.
21 * @var integer exclude_prev_days Ignore all the events prior the current date minus
22 * the specified amount of days.
23 * @var integer exclude_next_days Ignore all the events after the current date plus
24 * the specified amount of days.
25 *
26 * @since 1.7.4
27 */
28 final class VAPIcalReaderOptimizer implements VAPIcalReader
29 {
30 /** @var VAPIcalReader */
31 protected $reader;
32
33 /** @var JRegistry */
34 protected $options;
35
36 /**
37 * Class constructor.
38 *
39 * @param VAPIcalReader $reader The reader implementor.
40 * @param array $options A configuration array.
41 */
42 public function __construct(VAPIcalReader $reader, array $options = [])
43 {
44 $this->reader = $reader;
45 $this->options = new JRegistry($options);
46 }
47
48 /**
49 * Loads the iCalendar through the attached reader and tries to reduce
50 * the file by excluding the events that do not match the specified query.
51 *
52 * @return string The iCalendar string.
53 */
54 public function load()
55 {
56 // obtain buffer
57 $buffer = $this->reader->load();
58
59 if (strlen($buffer) > (int) $this->options->get('filesize', 0))
60 {
61 // the length of the buffer exceeds the specified threshold,
62 // we should proceed with the optimization of the calendar
63 $buffer = $this->optimize($buffer);
64 }
65
66 return $buffer;
67 }
68
69 /**
70 * Attempts to optimize the received calendar buffer.
71 *
72 * @param string $buffer The iCalendar string.
73 *
74 * @return string The optimized iCalendar string.
75 */
76 protected function optimize(string $buffer)
77 {
78 // normalize all end of lines
79 $buffer = preg_replace("/\R/", PHP_EOL, $buffer);
80
81 // keep the heading of the calendar
82 $ics = substr($buffer, 0, $offset = strpos($buffer, 'BEGIN:VEVENT'));
83
84 // define the left threshold to exclude all the previous events
85 $beforeThreshold = $this->options->get('exclude_prev_days', null);
86
87 if (!is_null($beforeThreshold))
88 {
89 $beforeThreshold = JFactory::getDate('-' . (int) $beforeThreshold . ' days');
90 }
91
92 // define the right threshold to exclude all the next events
93 $afterThreshold = $this->options->get('exclude_next_days', null);
94
95 if (!is_null($afterThreshold))
96 {
97 $afterThreshold = JFactory::getDate('+' . (int) $beforeThreshold . ' days');
98 }
99
100 // iterate as long as we can find an event
101 while (($next = strpos($buffer, 'BEGIN:VEVENT', $offset + 1)) !== false)
102 {
103 // obtain the whole event declaration
104 $event = substr($buffer, $offset, $next - $offset);
105
106 // update offset with the starting position of the next event
107 $offset = $next;
108
109 // detect event date start
110 if (preg_match("/DTSTART(.*?):(.*?)\R/", $event, $match))
111 {
112 $dateString = $match[2];
113
114 /**
115 * Check if we have a date without a time, otherwise the date parser will consider
116 * the provided string as a UNIX timestamp.
117 *
118 * @since 1.7.7
119 */
120 if (!strcasecmp($match[1], ';VALUE=DATE'))
121 {
122 // full date provided, concat the time
123 $dateString .= 'T00:00:00Z';
124 }
125
126 try
127 {
128 // try to instantiate a date time
129 $dt = JFactory::getDate($dateString);
130
131 if ((!$beforeThreshold || $beforeThreshold <= $dt) && (!$afterThreshold || $dt <= $afterThreshold))
132 {
133 // event compliant, append it within the optimized calendar
134 $ics .= $event;
135 }
136 }
137 catch (Exception $e) {
138 // invalid date time, ignore event
139 }
140 }
141 }
142
143 // Copy all the remaining characters.
144 // NOTE: the last event will never be checked against the specified filters, because the loop
145 // always stops while reaching the starting declaration of the latter. Anyway, we don't have
146 // to worry about as a single event cannot compromise the whole loading process.
147 $ics .= substr($buffer, $offset);
148
149 return $ics;
150 }
151 }
152