| 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!=, orswitchconditions), values of different types might be equal.For
stringvalues, the empty string''is a special case, in particular the following results might be unexpected: