Passed
Push — master ( 7d22f9...1460f7 )
by Jean Paul
01:51
created

StackOverflowWebPageHandler::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Coco\SourceWatcher\Vendors\StackOverflow;
4
5
use Coco\SourceWatcher\Watcher\Handler\WebPageHandler;
6
use DOMElement;
7
use DOMNamedNodeMap;
8
use DOMNodeList;
9
use DOMXPath;
10
11
/**
12
 * Class StackOverflowWebPageHandler
13
 *
14
 * @package Coco\SourceWatcher\Vendors\StackOverflow
15
 */
16
class StackOverflowWebPageHandler extends WebPageHandler
17
{
18
    private array $results = [];
19
20
    public function getResults () : array
21
    {
22
        return $this->results;
23
    }
24
25
    public function read () : void
26
    {
27
        parent::read();
28
29
        $this->results = [];
30
31
        $finder = new DomXPath( $this->dom );
32
33
        $classname = "listResults";
34
        $listResultsDom = $finder->query( "//*[contains(@class, '$classname')]" ); // DOMNodeList
35
36
        for ( $i = 0; $i < $listResultsDom->count(); $i++ ) {
37
            $this->processListResults( $listResultsDom->item( $i ) );
38
        }
39
    }
40
41
    private function processListResults ( DOMElement $currentDomNode ) : void
42
    {
43
        if ( $currentDomNode->hasChildNodes() ) {
44
            $children = $currentDomNode->childNodes; // DOMNodeList
45
46
            for ( $i = 0; $i < $children->count(); $i++ ) {
47
                $currentChildrenNode = $children->item( $i );
48
49
                if ( $currentChildrenNode instanceof DOMElement ) {
50
                    $this->processChildNodes( $currentChildrenNode );
51
                }
52
            }
53
        }
54
    }
55
56
    private function processChildNodes ( DOMElement $currentChildrenNode ) : void
57
    {
58
        $currentJob = new StackOverflowJob();
59
60
        $currentJob = $this->setBasicAttributes( $currentChildrenNode->attributes, $currentJob );
61
62
        if ( $currentChildrenNode->hasChildNodes() ) {
63
            if ( trim( $currentChildrenNode->nodeValue ) == "You might be interested in these jobs:" ) {
64
                echo "Found job separator" . PHP_EOL;
65
            } else {
66
                $extraChildNodes = $currentChildrenNode->childNodes; // DOMNodeList
67
68
                for ( $i = 0; $i < $extraChildNodes->count(); $i++ ) {
69
                    // DOMElement or DOMText
70
                    $currentExtraChildNode = $extraChildNodes->item( $i );
71
72
                    if ( $currentExtraChildNode instanceof DOMElement ) {
73
                        $currentJob = $this->processExtraChildNodes( $currentExtraChildNode, $currentJob );
74
                    }
75
                }
76
            }
77
        }
78
79
        if ( $currentJob->allAttributesDefined() ) {
80
            array_push( $this->results, $currentJob );
81
        } else {
82
            echo "Ignoring job because of missing attributes" . PHP_EOL;
83
        }
84
    }
85
86
    private function setBasicAttributes (
87
        DOMNamedNodeMap $attributes,
88
        StackOverflowJob $stackOverflowJob
89
    ) : StackOverflowJob {
90
        if ( $attributes != null ) {
91
            foreach ( $attributes as $currentAttribute ) {
92
                if ( $currentAttribute->name != null ) {
93
                    if ( $currentAttribute->name == "data-jobid" ) {
94
                        $stackOverflowJob->setJobId( $currentAttribute->value );
95
                    }
96
97
                    if ( $currentAttribute->name == "data-result-id" ) {
98
                        $stackOverflowJob->setResultId( $currentAttribute->value );
99
                    }
100
101
                    if ( $currentAttribute->name == "data-preview-url" ) {
102
                        $stackOverflowJob->setPreviewUrl( $currentAttribute->value );
103
                    }
104
                }
105
            }
106
        }
107
108
        return $stackOverflowJob;
109
    }
110
111
    private function processExtraChildNodes (
112
        DOMElement $currentExtraChildNode,
113
        StackOverflowJob $stackOverflowJob
114
    ) : StackOverflowJob {
115
        if ( $currentExtraChildNode->hasChildNodes() ) {
116
            $currentExtraChildNodeChildren = $currentExtraChildNode->childNodes; // DOMNodeList
117
118
            $nodeCount = $currentExtraChildNodeChildren->count();
119
120
            if ( $nodeCount >= 6 ) {
121
                for ( $i = 0; $i < $nodeCount; $i++ ) {
122
                    $currentDeepNode = $currentExtraChildNodeChildren->item( $i ); // DOMElement or DOMText
123
124
                    if ( $currentDeepNode instanceof DOMElement ) {
125
                        $stackOverflowJob = $this->processImageDivBlocks( $currentDeepNode, $stackOverflowJob );
126
                    }
127
                }
128
            }
129
        }
130
131
        return $stackOverflowJob;
132
    }
133
134
    private function processImageDivBlocks (
135
        DOMElement $currentDeepNode,
136
        StackOverflowJob $stackOverflowJob
137
    ) : StackOverflowJob {
138
        if ( $currentDeepNode->childNodes->count() == 2 ) {
139
            $stackOverflowJob = $this->processImageBlock( $currentDeepNode->childNodes->item( 1 )->attributes,
140
                $stackOverflowJob );
141
        }
142
143
        if ( $currentDeepNode->tagName == "div" && $currentDeepNode->hasChildNodes() ) {
144
            $stackOverflowJob = $this->processDivBlock( $currentDeepNode->childNodes, $stackOverflowJob );
145
        }
146
147
        return $stackOverflowJob;
148
    }
149
150
    private function processImageBlock (
151
        DOMNamedNodeMap $currentDeepNodeAttributes,
152
        StackOverflowJob $stackOverflowJob
153
    ) : StackOverflowJob {
154
        if ( $currentDeepNodeAttributes != null && sizeof( $currentDeepNodeAttributes ) == 2 ) {
155
            $attr1 = $currentDeepNodeAttributes[0]; // DOMAttr
156
            $attr2 = $currentDeepNodeAttributes[1]; // DOMAttr
157
158
            if ( $attr1->name == "src" ) {
159
                $stackOverflowJob->setLogo( $attr1->value );
160
            }
161
162
            if ( $attr2->name == "src" ) {
163
                $stackOverflowJob->setLogo( $attr2->value );
164
            }
165
        }
166
167
        return $stackOverflowJob;
168
    }
169
170
    private function processDivBlock (
171
        DOMNodeList $currentDeepNodeChildren,
172
        StackOverflowJob $stackOverflowJob
173
    ) : StackOverflowJob {
174
        for ( $i = 0; $i < $currentDeepNodeChildren->count(); $i++ ) {
175
            $currentDeepNodeChildrenElement = $currentDeepNodeChildren->item( $i ); // DOMElement or DOMText
176
177
            if ( $currentDeepNodeChildrenElement instanceof DOMElement ) {
178
                $stackOverflowJob = $this->processH2AndH3Elements( $currentDeepNodeChildrenElement, $stackOverflowJob );
179
            }
180
        }
181
182
        return $stackOverflowJob;
183
    }
184
185
    private function processH2AndH3Elements (
186
        DOMElement $currentDeepNodeChildrenElement,
187
        StackOverflowJob $stackOverflowJob
188
    ) : StackOverflowJob {
189
        if ( $currentDeepNodeChildrenElement->tagName == "h2" ) {
190
            $stackOverflowJob->setTitle( trim( $currentDeepNodeChildrenElement->nodeValue ) );
191
        }
192
193
        if ( $currentDeepNodeChildrenElement->tagName == "h3" ) {
194
            if ( $currentDeepNodeChildrenElement->hasChildNodes() ) {
195
                $companyAndLocationDomNodeList = $currentDeepNodeChildrenElement->childNodes; // DOMNodeList
196
197
                for ( $i = 0; $i < $companyAndLocationDomNodeList->count(); $i++ ) {
198
                    $currentCompanyAndLocationElement = $companyAndLocationDomNodeList->item( $i ); // DOMElement or DOMText
199
200
                    if ( $currentCompanyAndLocationElement instanceof DOMElement ) {
201
                        if ( $currentCompanyAndLocationElement->nodeName == "span" ) {
202
                            if ( $currentCompanyAndLocationElement->attributes->count() == 0 ) {
203
                                $companyName = trim( $currentCompanyAndLocationElement->nodeValue );
204
205
                                if ( strpos( $companyName, "\r\n" ) !== false ) {
206
                                    $companyNameParts = explode( "\r\n", $companyName );
207
208
                                    foreach ( $companyNameParts as $index => $currentCompanyNamePart ) {
209
                                        $companyNameParts[$index] = trim( $currentCompanyNamePart );
210
                                    }
211
212
                                    $stackOverflowJob->setCompany( implode( " ", $companyNameParts ) );
213
                                } else {
214
                                    $stackOverflowJob->setCompany( $companyName );
215
                                }
216
                            }
217
218
                            if ( $currentCompanyAndLocationElement->attributes->count() == 1 && $currentCompanyAndLocationElement->getAttribute( "class" ) == "fc-black-500" ) {
219
                                $stackOverflowJob->setLocation( trim( $currentCompanyAndLocationElement->nodeValue ) );
220
                            }
221
                        }
222
                    }
223
                }
224
            }
225
        }
226
227
        return $stackOverflowJob;
228
    }
229
}
230