Passed
Push — master ( 5b8de8...ae4a0d )
by Mikołaj
02:12
created

Table::getTable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 0
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Wulkanowy\TimetableParser;
4
5
use DOMWrap\Document;
6
use DOMWrap\Element;
7
use DOMWrap\NodeList;
8
9
class Table
10
{
11
    private $doc;
12
13
    public function __construct(string $html)
14
    {
15
        $this->doc = new Document();
16
        $this->doc->html($html);
17
    }
18
19
    public function getTable(): array
20
    {
21
        $table = $this->doc->find('.tabela')->first();
22
        $days = $this->getDays($table->find('tr th'));
23
24
        $this->setLessonHoursToDays($table, $days);
25
26
        $title = explode(' ', $this->doc->find('title')->text());
27
        $generated = preg_split('/\s+/', trim($this->doc->find('td[align=right]')->end()->text()));
28
29
        return [
30
            'name'        => $this->doc->find('.tytulnapis')->text(),
31
            'generated'   => trim($generated[1]),
32
            'description' => trim($this->doc->find('td[align=left]')->first()->text()),
33
            'typeName'    => $title[2],
34
            'days'        => $days,
35
        ];
36
    }
37
38
    private function getDays(NodeList $headerCells): array
39
    {
40
        $headerCells->shift();
41
        $headerCells->shift();
42
        $days = [];
43
44
        /** @var \DOMNode $cell */
45
        foreach ($headerCells as $cell) {
46
            $days[] = ['name' => $cell->textContent];
47
        }
48
49
        return $days;
50
    }
51
52
    private function setLessonHoursToDays(Element $table, array &$days): void
53
    {
54
        $rows = $table->find('tr');
55
        $rows->shift();
56
57
        foreach ($rows as $row) {
58
            $rowCells = $row->find('td');
59
60
            // fill hours in day
61
            /** @var NodeList $rowCells */
62
            for ($i = 2; $i < $rowCells->count(); $i++) {
63
                $days[$i - 2]['hours'][] = $this->getHourWithLessons($rowCells, $i);
64
            }
65
        }
66
    }
67
68
    private function getHourWithLessons(NodeList $rowCells, int $index): array
69
    {
70
        $hours = explode('-', $rowCells->get(1)->textContent);
71
72
        return [
73
            'number'  => $rowCells->get(0)->textContent,
74
            'start'   => trim($hours[0]),
75
            'end'     => trim($hours[1]),
76
            'lessons' => $this->getExtractedLessons($rowCells->get($index)),
77
        ];
78
    }
79
80
    private function getExtractedLessons(Element $current): array
81
    {
82
        $lessons = [];
83
84
        $chunks = explode('<br>', $current->getOuterHtml());
85
        $spans = $current->find('span[style]');
86
        $subject = $current->findXPath('./*[@class="p"]');
87
88
        if ($spans->count() > 0 && $subject->count() === 0) {
89
            foreach ($spans as $group) {
90
                $lessons[] = array_merge($this->getLesson($group, true));
91
            }
92
        } elseif (\count($chunks) > 0 && $subject->count() > 0) {
93
            foreach ($chunks as $item) {
94
                $this->setLessonFromChunk($lessons, $item);
95
            }
96
        }
97
98
        $this->updateLessonWithMultipleClasses($current, $lessons);
99
        $this->setFallbackLesson($current, $lessons);
100
101
        return $lessons;
102
    }
103
104
    private function setLessonFromChunk(array &$lessons, string $chunk): void
105
    {
106
        $doc = new Document();
107
        $doc->html($chunk);
108
109
        $span = $doc->find('span[style]');
110
        $cell = $doc->find('.l');
111
        $body = $doc->find('body');
112
113
        if ($span->count() > 0) {
114
            $lessons[] = array_merge($this->getLesson($span->first(), true));
115
        } elseif ($cell->count() > 0) {
116
            $lessons[] = $this->getLesson($cell->first());
117
        } elseif ($body->count() > 0) {
118
            $lessons[] = $this->getLesson($body->first());
119
        }
120
    }
121
122
    private function setFallbackLesson(Element $current, array &$lessons): void
123
    {
124
        if (\count($lessons) === 0 && trim($current->text(), "\xC2\xA0\n") !== '') {
125
            $lessons[] = $this->getLesson($current);
126
        }
127
    }
128
129
    private function updateLessonWithMultipleClasses(Element $current, array &$lessons): void
130
    {
131
        if ($current->findXPath('./*[@class="o"]')->count() < 2) {
132
            return;
133
        }
134
135
        $lastIndex = \count($lessons) - 1;
136
137
        unset($lessons[$lastIndex]['className'], $lessons[$lastIndex]['alt']);
138
139
        /** @var Element $item */
140
        foreach (explode(',', $current->getOuterHtml()) as $item) {
141
            $doc = new Document();
142
            $doc->html(str_replace('<td class="l">', '', $item));
143
            $el = $doc->find('body')->first();
144
            $lessons[$lastIndex]['className'][] = [
145
                'name'  => trim($el->find('a.o')->text()),
146
                'value' => $this->getUrlValue($el->find('a.o')->first(), 'o'),
147
                'alt'   => trim($el->findXPath('./text()')->text()),
148
            ];
149
        }
150
    }
151
152
    private function getLesson(Element $cell, bool $diversion = false): array
153
    {
154
        $subject = $cell->findXPath('./*[@class="p"]');
155
156
        $lesson = [
157
            'teacher'   => $this->getLessonPartValues($cell->findXPath('./*[@class="n"]'), 'n'),
158
            'room'      => $this->getLessonPartValues($cell->findXPath('./*[@class="s"]'), 's'),
159
            'className' => $this->getLessonPartValues($cell->findXPath('./*[@class="o"]'), 'o'),
160
            'subject'   => $subject->text(),
161
            'diversion' => $diversion,
162
            'alt'       => trim($cell->findXPath('./text()')->text()),
163
        ];
164
165
        $subjects = $cell->findXPath('./*[@class="p"]');
166
        if ($subjects->count() > 1) {
167
            $textBetweenSubject = $cell->findXPath('./text()[(preceding::*[@class="p"])]');
168
            if (trim($textBetweenSubject->text()) !== '') {
169
                $lesson['subject'] = $subject->first()->text().trim($textBetweenSubject->text()).' '.$subject->end()->text();
170
            } else {
171
                unset($lesson['subject']);
172
                foreach ($subjects as $subject) {
173
                    $lesson['subject'][] = $subject->text();
174
                }
175
            }
176
        }
177
178
        return $lesson;
179
    }
180
181
    private function getLessonPartValues(NodeList $part, string $prefix): array
182
    {
183
        return [
184
            'name'  => $part->text(),
185
            'value' => $this->getUrlValue($part->first(), $prefix),
186
        ];
187
    }
188
189
    private function getUrlValue(?Element $el, string $prefix): string
190
    {
191
        if (null === $el) {
192
            return '';
193
        }
194
195
        return str_replace([$prefix, '.html'], '', $el->attr('href'));
196
    }
197
}
198