Conditions | 56 |
Paths | > 20000 |
Total Lines | 275 |
Code Lines | 166 |
Lines | 0 |
Ratio | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
1 | <?php |
||
70 | return $agenda; |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Read .ical data and parse to day-based array. |
||
75 | * |
||
76 | * @param string $data ical raw data |
||
77 | * |
||
78 | * @return array events |
||
79 | */ |
||
80 | public function parseIcal($data) |
||
81 | { |
||
82 | // Init ICal parser |
||
83 | $ical = new ICal(); |
||
84 | $ical->initString($data); |
||
85 | |||
86 | // Retrieve event for this week only |
||
87 | $events = $ical->eventsFromRange(self::DAYS[0].' this week', self::DAYS[count(self::DAYS) - 1].' this week 23:59'); |
||
88 | |||
89 | if (!is_array($events) || !count($events)) { |
||
90 | return null; |
||
91 | } |
||
92 | |||
93 | // Use own timezone to display |
||
94 | $tz = new \DateTimeZone(ini_get('date.timezone')); |
||
95 | // Always transliterate text contents |
||
96 | self::$translit = \Transliterator::create('Latin-ASCII'); |
||
97 | if (!self::$translit) { |
||
98 | return null; |
||
99 | } |
||
100 | |||
101 | // Base agenda format info |
||
102 | $format = [ |
||
103 | 'minHour' => self::HOUR_MIN, |
||
104 | 'maxHour' => self::HOUR_MAX, |
||
105 | 'days' => [], |
||
106 | ]; |
||
107 | |||
108 | $parsedEvents = []; |
||
109 | |||
110 | foreach ($events as $e) { |
||
111 | // Convert timezones |
||
112 | $start = (new \DateTime($e->dtstart))->setTimeZone($tz); |
||
113 | $end = (new \DateTime($e->dtend))->setTimeZone($tz); |
||
114 | |||
115 | // Event info |
||
116 | $b = [ |
||
117 | 'dow' => $start->format('w') - 1, |
||
118 | 'start' => $start->format('G') + ($start->format('i') / 60.0), |
||
119 | 'startStr' => $start->format('G:i'), |
||
120 | 'end' => $end->format('G') + ($end->format('i') / 60.0), |
||
121 | 'endStr' => $end->format('G:i'), |
||
122 | 'name' => self::filter($e->summary, 'name'), |
||
123 | 'locations' => self::arrayFilter(explode(',', $e->location), 'location'), |
||
124 | 'desc' => self::arrayFilter(explode(PHP_EOL, $e->description), 'description'), |
||
125 | ]; |
||
126 | $b['duration'] = $b['end'] - $b['start']; |
||
127 | |||
128 | // Adjust agenda format based on events |
||
129 | if ($b['start'] < $format['minHour']) { |
||
130 | $format['minHour'] = $b['start']; |
||
131 | } |
||
132 | if ($b['end'] > $format['maxHour']) { |
||
133 | $format['maxHour'] = $b['end']; |
||
134 | } |
||
135 | |||
136 | // Only add days with events |
||
137 | if (!array_key_exists($b['dow'], $parsedEvents)) { |
||
138 | $parsedEvents[$b['dow']] = []; |
||
139 | $format['days'][$b['dow']] = $start->format('d/m'); |
||
140 | } |
||
141 | |||
142 | $parsedEvents[$b['dow']][] = $b; |
||
143 | } |
||
144 | |||
145 | $format['dayLen'] = $format['maxHour'] - $format['minHour']; |
||
146 | |||
147 | return ['info' => $format, 'events' => $parsedEvents]; |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Use agenda events data to build blocks for rendering. |
||
152 | * |
||
153 | * @param array $agenda |
||
154 | * |
||
155 | * @return array blocks |
||
156 | */ |
||
157 | public function blockize($agenda) |
||
158 | { |
||
159 | $scanOffset = 0.1; |
||
160 | |||
161 | $blocks = []; |
||
162 | |||
163 | foreach ($agenda['events'] as $day => $events) { |
||
164 | // Sort by desc first line |
||
165 | usort($events, function ($a, $b) { |
||
166 | return strcmp($a['desc'][0], $b['desc'][0]); |
||
167 | }); |
||
168 | |||
169 | // Scan each 0.1h for overlapping events |
||
170 | for ($i = $agenda['info']['minHour']; $i <= $agenda['info']['maxHour']; $i += $scanOffset) { |
||
171 | // $overlap is every overlapping event |
||
172 | $overlap = []; |
||
173 | foreach ($events as $k => $e) { |
||
174 | if ($e['start'] < $i && $i < $e['end']) { |
||
175 | $overlap[] = $k; |
||
176 | } |
||
177 | } |
||
178 | |||
179 | // $overlaps is maximum concurrent overlappings |
||
180 | // Used to fix block width |
||
181 | $overlaps = count($overlap); |
||
182 | |||
183 | foreach ($events as $k => $e) { |
||
184 | if ($e['start'] < $i && $i < $e['end']) { |
||
185 | if (!array_key_exists('overlaps', $e)) { |
||
186 | $e['overlaps'] = $overlaps; |
||
187 | $e['overlap'] = $overlap; |
||
188 | } else { |
||
189 | if ($overlaps >= $e['overlaps']) { |
||
190 | $e['overlaps'] = $overlaps; |
||
191 | } |
||
192 | // Merge overlap to always get full range of overlapping events |
||
193 | // Used to calculate block position |
||
194 | $e['overlap'] = array_unique(array_merge($e['overlap'], $overlap)); |
||
195 | } |
||
196 | |||
197 | $events[$k] = $e; |
||
198 | } |
||
199 | } |
||
200 | } |
||
201 | |||
202 | foreach ($events as $k => $e) { |
||
203 | if ($e['overlaps'] < 2) { |
||
204 | // No overlap, easy mode |
||
205 | $e['position'] = 0; |
||
206 | $events[$k] = $e; |
||
207 | continue; |
||
208 | } |
||
209 | |||
210 | if (array_key_exists('position', $e)) { |
||
211 | // Position already set, don't touch |
||
212 | continue; |
||
213 | } |
||
214 | |||
215 | // Find available spots for this event |
||
216 | $spots = range(0, $e['overlaps'] - 1); |
||
217 | for ($i = 0; $i < count($e['overlap']); ++$i) { |
||
218 | $overlaped = $events[$e['overlap'][$i]]; |
||
219 | if (array_key_exists('position', $overlaped)) { |
||
220 | unset($spots[$overlaped['position']]); |
||
221 | } |
||
222 | } |
||
223 | |||
224 | // Take first one |
||
225 | $e['position'] = array_shift($spots); |
||
226 | |||
227 | $events[$k] = $e; |
||
228 | } |
||
229 | |||
230 | $blocks[$day] = $events; |
||
231 | } |
||
232 | |||
233 | return $blocks; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * Scan agenda events for distinct locations, count them and sort desc. |
||
238 | * |
||
239 | * @param array $agenda |
||
240 | * |
||
241 | * @return array locations |
||
242 | */ |
||
243 | private static function locations($agenda) |
||
244 | { |
||
245 | $locations = []; |
||
246 | foreach ($agenda['events'] as $events) { |
||
247 | foreach ($events as $e) { |
||
248 | if (!array_key_exists('locations', $e)) { |
||
249 | continue; |
||
250 | } |
||
251 | |||
252 | foreach ($e['locations'] as $l) { |
||
253 | if (!array_key_exists($l, $locations)) { |
||
254 | $locations[$l] = 0; |
||
255 | } |
||
256 | ++$locations[$l]; |
||
257 | } |
||
258 | } |
||
259 | } |
||
260 | |||
261 | arsort($locations); |
||
262 | |||
263 | return $locations; |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Scan agenda events for distinct descriptions, count them (with overlap weight) and sort desc. |
||
268 | * |
||
269 | * @param array $agenda |
||
270 | * |
||
271 | * @return array descriptions |
||
272 | */ |
||
273 | private static function descriptions($agenda) |
||
274 | { |
||
275 | $descriptions = []; |
||
276 | foreach ($agenda['events'] as $events) { |
||
277 | foreach ($events as $e) { |
||
278 | if (!array_key_exists('desc', $e)) { |
||
279 | continue; |
||
280 | } |
||
281 | |||
282 | foreach ($e['desc'] as $d) { |
||
283 | if (!array_key_exists($d, $descriptions)) { |
||
284 | $descriptions[$d] = 0; |
||
285 | } |
||
286 | $descriptions[$d] += 1 / ($e['overlaps'] * 2); |
||
287 | break; |
||
288 | } |
||
289 | } |
||
290 | } |
||
291 | |||
292 | arsort($descriptions); |
||
293 | |||
294 | return $descriptions; |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * Scan agenda events for overlaps. |
||
299 | * |
||
300 | * @param array $agenda |
||
301 | * |
||
302 | * @return bool has overlaps |
||
303 | */ |
||
304 | private static function hasOverlaps($agenda) |
||
305 | { |
||
306 | foreach ($agenda['events'] as $events) { |
||
307 | foreach ($events as $e) { |
||
308 | if ($e['overlaps'] > 1) { |
||
309 | return true; |
||
310 | } |
||
311 | } |
||
312 | } |
||
313 | |||
314 | return false; |
||
315 | } |
||
316 | |||
317 | /** |
||
318 | * Scan agenda and guess best title based on locations and descriptions. |
||
319 | * |
||
320 | * @param array $agenda |
||
321 | * |
||
322 | * @return string title |
||
323 | */ |
||
324 | private static function genTitle($agenda) |
||
325 | { |
||
326 | if (!self::hasOverlaps($agenda)) { |
||
327 | $locations = self::locations($agenda); |
||
328 | if ($locations < 3) { |
||
329 | reset($locations); |
||
330 | |||
331 | return key($locations); |
||
332 | } |
||
333 | } |
||
334 | |||
335 | $descriptions = self::descriptions($agenda); |
||
336 | reset($descriptions); |
||
337 | |||
338 | return key($descriptions); |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * Last processing before render |
||
343 | * Generate title. |
||
344 | * |
||
345 | * @param array $agenda |
||
543 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
string
values, the empty string''
is a special case, in particular the following results might be unexpected: