Completed
Push — master ( 48e3f2...0b9e3e )
by Cody
20s queued 14s
created
plugins/af_readability/vendor/andreskrey/Readability/Readability.php 2 patches
Indentation   +1655 added lines, -1655 removed lines patch added patch discarded remove patch
@@ -14,154 +14,154 @@  discard block
 block discarded – undo
14 14
  */
15 15
 class Readability
16 16
 {
17
-    /**
18
-     * Main DOMDocument where all the magic happens.
19
-     *
20
-     * @var DOMDocument
21
-     */
22
-    protected $dom;
23
-
24
-    /**
25
-     * Title of the article.
26
-     *
27
-     * @var string|null
28
-     */
29
-    protected $title = null;
30
-
31
-    /**
32
-     * Final DOMDocument with the fully parsed HTML.
33
-     *
34
-     * @var DOMDocument|null
35
-     */
36
-    protected $content = null;
37
-
38
-    /**
39
-     * Excerpt of the article.
40
-     *
41
-     * @var string|null
42
-     */
43
-    protected $excerpt = null;
44
-
45
-    /**
46
-     * Main image of the article.
47
-     *
48
-     * @var string|null
49
-     */
50
-    protected $image = null;
51
-
52
-    /**
53
-     * Author of the article. Extracted from the byline tags and other social media properties.
54
-     *
55
-     * @var string|null
56
-     */
57
-    protected $author = null;
58
-
59
-    /**
60
-     * Website name.
61
-     *
62
-     * @var string|null
63
-     */
64
-    protected $siteName = null;
65
-
66
-    /**
67
-     * Direction of the text.
68
-     *
69
-     * @var string|null
70
-     */
71
-    protected $direction = null;
72
-
73
-    /**
74
-     * Configuration object.
75
-     *
76
-     * @var Configuration
77
-     */
78
-    private $configuration;
79
-
80
-    /**
81
-     * Logger object.
82
-     *
83
-     * @var LoggerInterface
84
-     */
85
-    private $logger;
86
-
87
-    /**
88
-     * Collection of attempted text extractions.
89
-     *
90
-     * @var array
91
-     */
92
-    private $attempts = [];
93
-
94
-    /**
95
-     * @var array
96
-     */
97
-    private $defaultTagsToScore = [
98
-        'section',
99
-        'h2',
100
-        'h3',
101
-        'h4',
102
-        'h5',
103
-        'h6',
104
-        'p',
105
-        'td',
106
-        'pre',
107
-    ];
108
-
109
-    /**
110
-     * @var array
111
-     */
112
-    private $alterToDIVExceptions = [
113
-        'div',
114
-        'article',
115
-        'section',
116
-        'p',
117
-    ];
118
-
119
-    /**
120
-     * Readability constructor.
121
-     *
122
-     * @param Configuration $configuration
123
-     */
124
-    public function __construct(Configuration $configuration)
125
-    {
126
-        $this->configuration = $configuration;
127
-        $this->logger = $this->configuration->getLogger();
128
-    }
129
-
130
-    /**
131
-     * Main parse function.
132
-     *
133
-     * @param $html
134
-     *
135
-     * @throws ParseException
136
-     *
137
-     * @return bool
138
-     */
139
-    public function parse($html)
140
-    {
141
-        $this->logger->info('*** Starting parse process...');
142
-
143
-        $this->dom = $this->loadHTML($html);
144
-
145
-        // Checking for minimum HTML to work with.
146
-        if (!($root = $this->dom->getElementsByTagName('body')->item(0)) || !$root->firstChild) {
147
-            $this->logger->emergency('No body tag present or body tag empty');
148
-
149
-            throw new ParseException('Invalid or incomplete HTML.');
150
-        }
151
-
152
-        $this->getMetadata();
153
-
154
-        $this->getMainImage();
155
-
156
-        while (true) {
157
-            $root = $root->firstChild;
158
-
159
-            $elementsToScore = $this->getNodes($root);
160
-            $this->logger->debug(sprintf('Elements to score: \'%s\'', count($elementsToScore)));
161
-
162
-            $result = $this->rateNodes($elementsToScore);
163
-
164
-            /*
17
+	/**
18
+	 * Main DOMDocument where all the magic happens.
19
+	 *
20
+	 * @var DOMDocument
21
+	 */
22
+	protected $dom;
23
+
24
+	/**
25
+	 * Title of the article.
26
+	 *
27
+	 * @var string|null
28
+	 */
29
+	protected $title = null;
30
+
31
+	/**
32
+	 * Final DOMDocument with the fully parsed HTML.
33
+	 *
34
+	 * @var DOMDocument|null
35
+	 */
36
+	protected $content = null;
37
+
38
+	/**
39
+	 * Excerpt of the article.
40
+	 *
41
+	 * @var string|null
42
+	 */
43
+	protected $excerpt = null;
44
+
45
+	/**
46
+	 * Main image of the article.
47
+	 *
48
+	 * @var string|null
49
+	 */
50
+	protected $image = null;
51
+
52
+	/**
53
+	 * Author of the article. Extracted from the byline tags and other social media properties.
54
+	 *
55
+	 * @var string|null
56
+	 */
57
+	protected $author = null;
58
+
59
+	/**
60
+	 * Website name.
61
+	 *
62
+	 * @var string|null
63
+	 */
64
+	protected $siteName = null;
65
+
66
+	/**
67
+	 * Direction of the text.
68
+	 *
69
+	 * @var string|null
70
+	 */
71
+	protected $direction = null;
72
+
73
+	/**
74
+	 * Configuration object.
75
+	 *
76
+	 * @var Configuration
77
+	 */
78
+	private $configuration;
79
+
80
+	/**
81
+	 * Logger object.
82
+	 *
83
+	 * @var LoggerInterface
84
+	 */
85
+	private $logger;
86
+
87
+	/**
88
+	 * Collection of attempted text extractions.
89
+	 *
90
+	 * @var array
91
+	 */
92
+	private $attempts = [];
93
+
94
+	/**
95
+	 * @var array
96
+	 */
97
+	private $defaultTagsToScore = [
98
+		'section',
99
+		'h2',
100
+		'h3',
101
+		'h4',
102
+		'h5',
103
+		'h6',
104
+		'p',
105
+		'td',
106
+		'pre',
107
+	];
108
+
109
+	/**
110
+	 * @var array
111
+	 */
112
+	private $alterToDIVExceptions = [
113
+		'div',
114
+		'article',
115
+		'section',
116
+		'p',
117
+	];
118
+
119
+	/**
120
+	 * Readability constructor.
121
+	 *
122
+	 * @param Configuration $configuration
123
+	 */
124
+	public function __construct(Configuration $configuration)
125
+	{
126
+		$this->configuration = $configuration;
127
+		$this->logger = $this->configuration->getLogger();
128
+	}
129
+
130
+	/**
131
+	 * Main parse function.
132
+	 *
133
+	 * @param $html
134
+	 *
135
+	 * @throws ParseException
136
+	 *
137
+	 * @return bool
138
+	 */
139
+	public function parse($html)
140
+	{
141
+		$this->logger->info('*** Starting parse process...');
142
+
143
+		$this->dom = $this->loadHTML($html);
144
+
145
+		// Checking for minimum HTML to work with.
146
+		if (!($root = $this->dom->getElementsByTagName('body')->item(0)) || !$root->firstChild) {
147
+			$this->logger->emergency('No body tag present or body tag empty');
148
+
149
+			throw new ParseException('Invalid or incomplete HTML.');
150
+		}
151
+
152
+		$this->getMetadata();
153
+
154
+		$this->getMainImage();
155
+
156
+		while (true) {
157
+			$root = $root->firstChild;
158
+
159
+			$elementsToScore = $this->getNodes($root);
160
+			$this->logger->debug(sprintf('Elements to score: \'%s\'', count($elementsToScore)));
161
+
162
+			$result = $this->rateNodes($elementsToScore);
163
+
164
+			/*
165 165
              * Now that we've gone through the full algorithm, check to see if
166 166
              * we got any meaningful content. If we didn't, we may need to re-run
167 167
              * grabArticle with different flags set. This gives us a higher likelihood of
@@ -169,897 +169,897 @@  discard block
 block discarded – undo
169 169
              * finding the -right- content.
170 170
              */
171 171
 
172
-            $length = mb_strlen(preg_replace(NodeUtility::$regexps['onlyWhitespace'], '', $result->textContent));
173
-
174
-            $this->logger->info(sprintf('[Parsing] Article parsed. Amount of words: %s. Current threshold is: %s', $length, $this->configuration->getCharThreshold()));
175
-
176
-            if ($result && $length < $this->configuration->getCharThreshold()) {
177
-                $this->dom = $this->loadHTML($html);
178
-                $root = $this->dom->getElementsByTagName('body')->item(0);
179
-
180
-                if ($this->configuration->getStripUnlikelyCandidates()) {
181
-                    $this->logger->debug('[Parsing] Threshold not met, trying again setting StripUnlikelyCandidates as false');
182
-                    $this->configuration->setStripUnlikelyCandidates(false);
183
-                    $this->attempts[] = ['articleContent' => $result, 'textLength' => $length];
184
-                } elseif ($this->configuration->getWeightClasses()) {
185
-                    $this->logger->debug('[Parsing] Threshold not met, trying again setting WeightClasses as false');
186
-                    $this->configuration->setWeightClasses(false);
187
-                    $this->attempts[] = ['articleContent' => $result, 'textLength' => $length];
188
-                } elseif ($this->configuration->getCleanConditionally()) {
189
-                    $this->logger->debug('[Parsing] Threshold not met, trying again setting CleanConditionally as false');
190
-                    $this->configuration->setCleanConditionally(false);
191
-                    $this->attempts[] = ['articleContent' => $result, 'textLength' => $length];
192
-                } else {
193
-                    $this->logger->debug('[Parsing] Threshold not met, searching across attempts for some content.');
194
-                    $this->attempts[] = ['articleContent' => $result, 'textLength' => $length];
195
-
196
-                    // No luck after removing flags, just return the longest text we found during the different loops
197
-                    usort($this->attempts, function ($a, $b) {
198
-                        return $a['textLength'] < $b['textLength'];
199
-                    });
200
-
201
-                    // But first check if we actually have something
202
-                    if (!$this->attempts[0]['textLength']) {
203
-                        $this->logger->emergency('[Parsing] Could not parse text, giving up :(');
204
-
205
-                        throw new ParseException('Could not parse text.');
206
-                    }
207
-
208
-                    $this->logger->debug('[Parsing] Threshold not met, but found some content in previous attempts.');
209
-
210
-                    $result = $this->attempts[0]['articleContent'];
211
-                    break;
212
-                }
213
-            } else {
214
-                break;
215
-            }
216
-        }
217
-
218
-        $result = $this->postProcessContent($result);
219
-
220
-        // If we haven't found an excerpt in the article's metadata, use the article's
221
-        // first paragraph as the excerpt. This can be used for displaying a preview of
222
-        // the article's content.
223
-        if (!$this->getExcerpt()) {
224
-            $this->logger->debug('[Parsing] No excerpt text found on metadata, extracting first p node and using it as excerpt.');
225
-            $paragraphs = $result->getElementsByTagName('p');
226
-            if ($paragraphs->length > 0) {
227
-                $this->setExcerpt(trim($paragraphs->item(0)->textContent));
228
-            }
229
-        }
230
-
231
-        $this->setContent($result);
232
-
233
-        $this->logger->info('*** Parse successful :)');
234
-
235
-        return true;
236
-    }
237
-
238
-    /**
239
-     * Creates a DOM Document object and loads the provided HTML on it.
240
-     *
241
-     * Used for the first load of Readability and subsequent reloads (when disabling flags and rescanning the text)
242
-     * Previous versions of Readability used this method one time and cloned the DOM to keep a backup. This caused bugs
243
-     * because cloning the DOM object keeps a relation between the clone and the original one, doing changes in both
244
-     * objects and ruining the backup.
245
-     *
246
-     * @param string $html
247
-     *
248
-     * @return DOMDocument
249
-     */
250
-    private function loadHTML($html)
251
-    {
252
-        $this->logger->debug('[Loading] Loading HTML...');
253
-
254
-        // To avoid throwing a gazillion of errors on malformed HTMLs
255
-        libxml_use_internal_errors(true);
256
-
257
-        $dom = new DOMDocument('1.0', 'utf-8');
258
-
259
-        if (!$this->configuration->getSubstituteEntities()) {
260
-            // Keep the original HTML entities
261
-            $dom->substituteEntities = false;
262
-        }
263
-
264
-        if ($this->configuration->getNormalizeEntities()) {
265
-            $this->logger->debug('[Loading] Normalized entities via mb_convert_encoding.');
266
-            // Replace UTF-8 characters with the HTML Entity equivalent. Useful to fix html with mixed content
267
-            $html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
268
-        }
269
-
270
-        if ($this->configuration->getSummonCthulhu()) {
271
-            $this->logger->debug('[Loading] Removed script tags via regex H̶͈̩̟̬̱͠E̡̨̬͔̳̜͢͠ ̡̧̯͉̩͙̩̹̞̠͎͈̹̥̠͞ͅͅC̶͉̞̘̖̝̗͓̬̯͍͉̤̬͢͢͞Ò̟̘͉͖͎͉̱̭̣̕M̴̯͈̻̱̱̣̗͈̠̙̲̥͘͞E̷̛͙̼̲͍͕̹͍͇̗̻̬̮̭̱̥͢Ş̛̟͔̙̜̤͇̮͍̙̝̀͘');
272
-            $html = preg_replace('/<script\b[^>]*>([\s\S]*?)<\/script>/', '', $html);
273
-        }
274
-
275
-        // Prepend the XML tag to avoid having issues with special characters. Should be harmless.
276
-        $dom->loadHTML('<?xml encoding="UTF-8">' . $html);
277
-        $dom->encoding = 'UTF-8';
278
-
279
-        $this->removeScripts($dom);
280
-
281
-        $this->prepDocument($dom);
282
-
283
-        $this->logger->debug('[Loading] Loaded HTML successfully.');
284
-
285
-        return $dom;
286
-    }
287
-
288
-    /**
289
-     * Tries to guess relevant info from metadata of the html. Sets the results in the Readability properties.
290
-     */
291
-    private function getMetadata()
292
-    {
293
-        $this->logger->debug('[Metadata] Retrieving metadata...');
294
-
295
-        $values = [];
296
-        // property is a space-separated list of values
297
-        $propertyPattern = '/\s*(dc|dcterm|og|twitter)\s*:\s*(author|creator|description|title|image|site_name)(?!:)\s*/i';
298
-
299
-        // name is a single value
300
-        $namePattern = '/^\s*(?:(dc|dcterm|og|twitter|weibo:(article|webpage))\s*[\.:]\s*)?(author|creator|description|title|image|site_name)(?!:)\s*$/i';
301
-
302
-        // Find description tags.
303
-        foreach ($this->dom->getElementsByTagName('meta') as $meta) {
304
-            /* @var DOMNode $meta */
305
-            $elementName = $meta->getAttribute('name');
306
-            $elementProperty = $meta->getAttribute('property');
307
-            $content = $meta->getAttribute('content');
308
-            $matches = null;
309
-            $name = null;
310
-
311
-            if ($elementProperty) {
312
-                if (preg_match($propertyPattern, $elementProperty, $matches)) {
313
-                    for ($i = count($matches) - 1; $i >= 0; $i--) {
314
-                        // Convert to lowercase, and remove any whitespace
315
-                        // so we can match below.
316
-                        $name = preg_replace('/\s/', '', mb_strtolower($matches[$i]));
317
-                        // multiple authors
318
-                        $values[$name] = trim($content);
319
-                    }
320
-                }
321
-            }
322
-
323
-            if (!$matches && $elementName && preg_match($namePattern, $elementName)) {
324
-                $name = $elementName;
325
-                if ($content) {
326
-                    // Convert to lowercase, remove any whitespace, and convert dots
327
-                    // to colons so we can match below.
328
-                    $name = preg_replace(['/\s/', '/\./'], ['', ':'], mb_strtolower($name));
329
-                    $values[$name] = trim($content);
330
-                }
331
-            }
332
-        }
333
-
334
-        // get title
335
-        /*
172
+			$length = mb_strlen(preg_replace(NodeUtility::$regexps['onlyWhitespace'], '', $result->textContent));
173
+
174
+			$this->logger->info(sprintf('[Parsing] Article parsed. Amount of words: %s. Current threshold is: %s', $length, $this->configuration->getCharThreshold()));
175
+
176
+			if ($result && $length < $this->configuration->getCharThreshold()) {
177
+				$this->dom = $this->loadHTML($html);
178
+				$root = $this->dom->getElementsByTagName('body')->item(0);
179
+
180
+				if ($this->configuration->getStripUnlikelyCandidates()) {
181
+					$this->logger->debug('[Parsing] Threshold not met, trying again setting StripUnlikelyCandidates as false');
182
+					$this->configuration->setStripUnlikelyCandidates(false);
183
+					$this->attempts[] = ['articleContent' => $result, 'textLength' => $length];
184
+				} elseif ($this->configuration->getWeightClasses()) {
185
+					$this->logger->debug('[Parsing] Threshold not met, trying again setting WeightClasses as false');
186
+					$this->configuration->setWeightClasses(false);
187
+					$this->attempts[] = ['articleContent' => $result, 'textLength' => $length];
188
+				} elseif ($this->configuration->getCleanConditionally()) {
189
+					$this->logger->debug('[Parsing] Threshold not met, trying again setting CleanConditionally as false');
190
+					$this->configuration->setCleanConditionally(false);
191
+					$this->attempts[] = ['articleContent' => $result, 'textLength' => $length];
192
+				} else {
193
+					$this->logger->debug('[Parsing] Threshold not met, searching across attempts for some content.');
194
+					$this->attempts[] = ['articleContent' => $result, 'textLength' => $length];
195
+
196
+					// No luck after removing flags, just return the longest text we found during the different loops
197
+					usort($this->attempts, function ($a, $b) {
198
+						return $a['textLength'] < $b['textLength'];
199
+					});
200
+
201
+					// But first check if we actually have something
202
+					if (!$this->attempts[0]['textLength']) {
203
+						$this->logger->emergency('[Parsing] Could not parse text, giving up :(');
204
+
205
+						throw new ParseException('Could not parse text.');
206
+					}
207
+
208
+					$this->logger->debug('[Parsing] Threshold not met, but found some content in previous attempts.');
209
+
210
+					$result = $this->attempts[0]['articleContent'];
211
+					break;
212
+				}
213
+			} else {
214
+				break;
215
+			}
216
+		}
217
+
218
+		$result = $this->postProcessContent($result);
219
+
220
+		// If we haven't found an excerpt in the article's metadata, use the article's
221
+		// first paragraph as the excerpt. This can be used for displaying a preview of
222
+		// the article's content.
223
+		if (!$this->getExcerpt()) {
224
+			$this->logger->debug('[Parsing] No excerpt text found on metadata, extracting first p node and using it as excerpt.');
225
+			$paragraphs = $result->getElementsByTagName('p');
226
+			if ($paragraphs->length > 0) {
227
+				$this->setExcerpt(trim($paragraphs->item(0)->textContent));
228
+			}
229
+		}
230
+
231
+		$this->setContent($result);
232
+
233
+		$this->logger->info('*** Parse successful :)');
234
+
235
+		return true;
236
+	}
237
+
238
+	/**
239
+	 * Creates a DOM Document object and loads the provided HTML on it.
240
+	 *
241
+	 * Used for the first load of Readability and subsequent reloads (when disabling flags and rescanning the text)
242
+	 * Previous versions of Readability used this method one time and cloned the DOM to keep a backup. This caused bugs
243
+	 * because cloning the DOM object keeps a relation between the clone and the original one, doing changes in both
244
+	 * objects and ruining the backup.
245
+	 *
246
+	 * @param string $html
247
+	 *
248
+	 * @return DOMDocument
249
+	 */
250
+	private function loadHTML($html)
251
+	{
252
+		$this->logger->debug('[Loading] Loading HTML...');
253
+
254
+		// To avoid throwing a gazillion of errors on malformed HTMLs
255
+		libxml_use_internal_errors(true);
256
+
257
+		$dom = new DOMDocument('1.0', 'utf-8');
258
+
259
+		if (!$this->configuration->getSubstituteEntities()) {
260
+			// Keep the original HTML entities
261
+			$dom->substituteEntities = false;
262
+		}
263
+
264
+		if ($this->configuration->getNormalizeEntities()) {
265
+			$this->logger->debug('[Loading] Normalized entities via mb_convert_encoding.');
266
+			// Replace UTF-8 characters with the HTML Entity equivalent. Useful to fix html with mixed content
267
+			$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
268
+		}
269
+
270
+		if ($this->configuration->getSummonCthulhu()) {
271
+			$this->logger->debug('[Loading] Removed script tags via regex H̶͈̩̟̬̱͠E̡̨̬͔̳̜͢͠ ̡̧̯͉̩͙̩̹̞̠͎͈̹̥̠͞ͅͅC̶͉̞̘̖̝̗͓̬̯͍͉̤̬͢͢͞Ò̟̘͉͖͎͉̱̭̣̕M̴̯͈̻̱̱̣̗͈̠̙̲̥͘͞E̷̛͙̼̲͍͕̹͍͇̗̻̬̮̭̱̥͢Ş̛̟͔̙̜̤͇̮͍̙̝̀͘');
272
+			$html = preg_replace('/<script\b[^>]*>([\s\S]*?)<\/script>/', '', $html);
273
+		}
274
+
275
+		// Prepend the XML tag to avoid having issues with special characters. Should be harmless.
276
+		$dom->loadHTML('<?xml encoding="UTF-8">' . $html);
277
+		$dom->encoding = 'UTF-8';
278
+
279
+		$this->removeScripts($dom);
280
+
281
+		$this->prepDocument($dom);
282
+
283
+		$this->logger->debug('[Loading] Loaded HTML successfully.');
284
+
285
+		return $dom;
286
+	}
287
+
288
+	/**
289
+	 * Tries to guess relevant info from metadata of the html. Sets the results in the Readability properties.
290
+	 */
291
+	private function getMetadata()
292
+	{
293
+		$this->logger->debug('[Metadata] Retrieving metadata...');
294
+
295
+		$values = [];
296
+		// property is a space-separated list of values
297
+		$propertyPattern = '/\s*(dc|dcterm|og|twitter)\s*:\s*(author|creator|description|title|image|site_name)(?!:)\s*/i';
298
+
299
+		// name is a single value
300
+		$namePattern = '/^\s*(?:(dc|dcterm|og|twitter|weibo:(article|webpage))\s*[\.:]\s*)?(author|creator|description|title|image|site_name)(?!:)\s*$/i';
301
+
302
+		// Find description tags.
303
+		foreach ($this->dom->getElementsByTagName('meta') as $meta) {
304
+			/* @var DOMNode $meta */
305
+			$elementName = $meta->getAttribute('name');
306
+			$elementProperty = $meta->getAttribute('property');
307
+			$content = $meta->getAttribute('content');
308
+			$matches = null;
309
+			$name = null;
310
+
311
+			if ($elementProperty) {
312
+				if (preg_match($propertyPattern, $elementProperty, $matches)) {
313
+					for ($i = count($matches) - 1; $i >= 0; $i--) {
314
+						// Convert to lowercase, and remove any whitespace
315
+						// so we can match below.
316
+						$name = preg_replace('/\s/', '', mb_strtolower($matches[$i]));
317
+						// multiple authors
318
+						$values[$name] = trim($content);
319
+					}
320
+				}
321
+			}
322
+
323
+			if (!$matches && $elementName && preg_match($namePattern, $elementName)) {
324
+				$name = $elementName;
325
+				if ($content) {
326
+					// Convert to lowercase, remove any whitespace, and convert dots
327
+					// to colons so we can match below.
328
+					$name = preg_replace(['/\s/', '/\./'], ['', ':'], mb_strtolower($name));
329
+					$values[$name] = trim($content);
330
+				}
331
+			}
332
+		}
333
+
334
+		// get title
335
+		/*
336 336
          * This is a very convoluted way of extracting the first matching key of the $values array
337 337
          * against a set of options.
338 338
          *
339 339
          * This could be easily replaced with an ugly set of isset($values['key']) or a bunch of ??s.
340 340
          * Will probably replace it with ??s after dropping support of PHP5.6
341 341
          */
342
-        $key = current(array_intersect([
343
-            'dc:title',
344
-            'dcterm:title',
345
-            'og:title',
346
-            'weibo:article:title',
347
-            'weibo:webpage:title',
348
-            'title',
349
-            'twitter:title'
350
-        ], array_keys($values)));
351
-
352
-        $this->setTitle(isset($values[$key]) ? trim($values[$key]) : null);
353
-
354
-        if (!$this->getTitle()) {
355
-            $this->setTitle($this->getArticleTitle());
356
-        }
357
-
358
-        // get author
359
-        $key = current(array_intersect([
360
-            'dc:creator',
361
-            'dcterm:creator',
362
-            'author'
363
-        ], array_keys($values)));
364
-
365
-        $this->setAuthor(isset($values[$key]) ? $values[$key] : null);
366
-
367
-        // get description
368
-        $key = current(array_intersect([
369
-            'dc:description',
370
-            'dcterm:description',
371
-            'og:description',
372
-            'weibo:article:description',
373
-            'weibo:webpage:description',
374
-            'description',
375
-            'twitter:description'
376
-        ], array_keys($values)));
377
-
378
-        $this->setExcerpt(isset($values[$key]) ? $values[$key] : null);
379
-
380
-        // get main image
381
-        $key = current(array_intersect([
382
-            'image',
383
-            'og:image',
384
-            'twitter:image'
385
-        ], array_keys($values)));
386
-
387
-        $this->setImage(isset($values[$key]) ? $values[$key] : null);
388
-
389
-        $key = current(array_intersect([
390
-            'og:site_name'
391
-        ], array_keys($values)));
392
-
393
-        $this->setSiteName(isset($values[$key]) ? $values[$key] : null);
394
-    }
395
-
396
-    /**
397
-     * Returns all the images of the parsed article.
398
-     *
399
-     * @return array
400
-     */
401
-    public function getImages()
402
-    {
403
-        $result = [];
404
-        if ($this->getImage()) {
405
-            $result[] = $this->getImage();
406
-        }
407
-
408
-        if (null == $this->getDOMDocument()) {
409
-            return $result;
410
-        }
411
-
412
-        foreach ($this->getDOMDocument()->getElementsByTagName('img') as $img) {
413
-            if ($src = $img->getAttribute('src')) {
414
-                $result[] = $src;
415
-            }
416
-        }
417
-
418
-        if ($this->configuration->getFixRelativeURLs()) {
419
-            foreach ($result as &$imgSrc) {
420
-                $imgSrc = $this->toAbsoluteURI($imgSrc);
421
-            }
422
-        }
423
-
424
-        $result = array_unique(array_filter($result));
425
-
426
-        return $result;
427
-    }
428
-
429
-    /**
430
-     * Tries to get the main article image. Will only update the metadata if the getMetadata function couldn't
431
-     * find a correct image.
432
-     */
433
-    public function getMainImage()
434
-    {
435
-        $imgUrl = false;
436
-
437
-        if ($this->getImage() !== null) {
438
-            $imgUrl = $this->getImage();
439
-        }
440
-
441
-        if (!$imgUrl) {
442
-            foreach ($this->dom->getElementsByTagName('link') as $link) {
443
-                /** @var \DOMElement $link */
444
-                /*
342
+		$key = current(array_intersect([
343
+			'dc:title',
344
+			'dcterm:title',
345
+			'og:title',
346
+			'weibo:article:title',
347
+			'weibo:webpage:title',
348
+			'title',
349
+			'twitter:title'
350
+		], array_keys($values)));
351
+
352
+		$this->setTitle(isset($values[$key]) ? trim($values[$key]) : null);
353
+
354
+		if (!$this->getTitle()) {
355
+			$this->setTitle($this->getArticleTitle());
356
+		}
357
+
358
+		// get author
359
+		$key = current(array_intersect([
360
+			'dc:creator',
361
+			'dcterm:creator',
362
+			'author'
363
+		], array_keys($values)));
364
+
365
+		$this->setAuthor(isset($values[$key]) ? $values[$key] : null);
366
+
367
+		// get description
368
+		$key = current(array_intersect([
369
+			'dc:description',
370
+			'dcterm:description',
371
+			'og:description',
372
+			'weibo:article:description',
373
+			'weibo:webpage:description',
374
+			'description',
375
+			'twitter:description'
376
+		], array_keys($values)));
377
+
378
+		$this->setExcerpt(isset($values[$key]) ? $values[$key] : null);
379
+
380
+		// get main image
381
+		$key = current(array_intersect([
382
+			'image',
383
+			'og:image',
384
+			'twitter:image'
385
+		], array_keys($values)));
386
+
387
+		$this->setImage(isset($values[$key]) ? $values[$key] : null);
388
+
389
+		$key = current(array_intersect([
390
+			'og:site_name'
391
+		], array_keys($values)));
392
+
393
+		$this->setSiteName(isset($values[$key]) ? $values[$key] : null);
394
+	}
395
+
396
+	/**
397
+	 * Returns all the images of the parsed article.
398
+	 *
399
+	 * @return array
400
+	 */
401
+	public function getImages()
402
+	{
403
+		$result = [];
404
+		if ($this->getImage()) {
405
+			$result[] = $this->getImage();
406
+		}
407
+
408
+		if (null == $this->getDOMDocument()) {
409
+			return $result;
410
+		}
411
+
412
+		foreach ($this->getDOMDocument()->getElementsByTagName('img') as $img) {
413
+			if ($src = $img->getAttribute('src')) {
414
+				$result[] = $src;
415
+			}
416
+		}
417
+
418
+		if ($this->configuration->getFixRelativeURLs()) {
419
+			foreach ($result as &$imgSrc) {
420
+				$imgSrc = $this->toAbsoluteURI($imgSrc);
421
+			}
422
+		}
423
+
424
+		$result = array_unique(array_filter($result));
425
+
426
+		return $result;
427
+	}
428
+
429
+	/**
430
+	 * Tries to get the main article image. Will only update the metadata if the getMetadata function couldn't
431
+	 * find a correct image.
432
+	 */
433
+	public function getMainImage()
434
+	{
435
+		$imgUrl = false;
436
+
437
+		if ($this->getImage() !== null) {
438
+			$imgUrl = $this->getImage();
439
+		}
440
+
441
+		if (!$imgUrl) {
442
+			foreach ($this->dom->getElementsByTagName('link') as $link) {
443
+				/** @var \DOMElement $link */
444
+				/*
445 445
                  * Check for the rel attribute, then check if the rel attribute is either img_src or image_src, and
446 446
                  * finally check for the existence of the href attribute, which should hold the image url.
447 447
                  */
448
-                if ($link->hasAttribute('rel') && ($link->getAttribute('rel') === 'img_src' || $link->getAttribute('rel') === 'image_src') && $link->hasAttribute('href')) {
449
-                    $imgUrl = $link->getAttribute('href');
450
-                    break;
451
-                }
452
-            }
453
-        }
454
-
455
-        if (!empty($imgUrl) && $this->configuration->getFixRelativeURLs()) {
456
-            $this->setImage($this->toAbsoluteURI($imgUrl));
457
-        }
458
-    }
459
-
460
-    /**
461
-     * Returns the title of the html. Prioritizes the title from the metadata against the title tag.
462
-     *
463
-     * @return string|null
464
-     */
465
-    private function getArticleTitle()
466
-    {
467
-        $originalTitle = null;
468
-
469
-        if ($this->getTitle()) {
470
-            $originalTitle = $this->getTitle();
471
-        } else {
472
-            $this->logger->debug('[Metadata] Could not find title in metadata, searching for the title tag...');
473
-            $titleTag = $this->dom->getElementsByTagName('title');
474
-            if ($titleTag->length > 0) {
475
-                $this->logger->info(sprintf('[Metadata] Using title tag as article title: \'%s\'', $titleTag->item(0)->nodeValue));
476
-                $originalTitle = $titleTag->item(0)->nodeValue;
477
-            }
478
-        }
479
-
480
-        if ($originalTitle === null) {
481
-            return null;
482
-        }
483
-
484
-        $curTitle = $originalTitle = trim($originalTitle);
485
-        $titleHadHierarchicalSeparators = false;
486
-
487
-        /*
448
+				if ($link->hasAttribute('rel') && ($link->getAttribute('rel') === 'img_src' || $link->getAttribute('rel') === 'image_src') && $link->hasAttribute('href')) {
449
+					$imgUrl = $link->getAttribute('href');
450
+					break;
451
+				}
452
+			}
453
+		}
454
+
455
+		if (!empty($imgUrl) && $this->configuration->getFixRelativeURLs()) {
456
+			$this->setImage($this->toAbsoluteURI($imgUrl));
457
+		}
458
+	}
459
+
460
+	/**
461
+	 * Returns the title of the html. Prioritizes the title from the metadata against the title tag.
462
+	 *
463
+	 * @return string|null
464
+	 */
465
+	private function getArticleTitle()
466
+	{
467
+		$originalTitle = null;
468
+
469
+		if ($this->getTitle()) {
470
+			$originalTitle = $this->getTitle();
471
+		} else {
472
+			$this->logger->debug('[Metadata] Could not find title in metadata, searching for the title tag...');
473
+			$titleTag = $this->dom->getElementsByTagName('title');
474
+			if ($titleTag->length > 0) {
475
+				$this->logger->info(sprintf('[Metadata] Using title tag as article title: \'%s\'', $titleTag->item(0)->nodeValue));
476
+				$originalTitle = $titleTag->item(0)->nodeValue;
477
+			}
478
+		}
479
+
480
+		if ($originalTitle === null) {
481
+			return null;
482
+		}
483
+
484
+		$curTitle = $originalTitle = trim($originalTitle);
485
+		$titleHadHierarchicalSeparators = false;
486
+
487
+		/*
488 488
          * If there's a separator in the title, first remove the final part
489 489
          *
490 490
          * Sanity warning: if you eval this match in PHPStorm's "Evaluate expression" box, it will return false
491 491
          * I can assure you it works properly if you let the code run.
492 492
          */
493
-        if (preg_match('/ [\|\-\\\\\/>»] /i', $curTitle)) {
494
-            $titleHadHierarchicalSeparators = (bool)preg_match('/ [\\\\\/>»] /', $curTitle);
495
-            $curTitle = preg_replace('/(.*)[\|\-\\\\\/>»] .*/i', '$1', $originalTitle);
496
-
497
-            $this->logger->info(sprintf('[Metadata] Found hierarchical separators in title, new title is: \'%s\'', $curTitle));
498
-
499
-            // If the resulting title is too short (3 words or fewer), remove
500
-            // the first part instead:
501
-            if (count(preg_split('/\s+/', $curTitle)) < 3) {
502
-                $curTitle = preg_replace('/[^\|\-\\\\\/>»]*[\|\-\\\\\/>»](.*)/i', '$1', $originalTitle);
503
-                $this->logger->info(sprintf('[Metadata] Title too short, using the first part of the title instead: \'%s\'', $curTitle));
504
-            }
505
-        } elseif (strpos($curTitle, ': ') !== false) {
506
-            // Check if we have an heading containing this exact string, so we
507
-            // could assume it's the full title.
508
-            $match = false;
509
-            for ($i = 1; $i <= 2; $i++) {
510
-                foreach ($this->dom->getElementsByTagName('h' . $i) as $hTag) {
511
-                    // Trim texts to avoid having false negatives when the title is surrounded by spaces or tabs
512
-                    if (trim($hTag->nodeValue) === trim($curTitle)) {
513
-                        $match = true;
514
-                    }
515
-                }
516
-            }
517
-
518
-            // If we don't, let's extract the title out of the original title string.
519
-            if (!$match) {
520
-                $curTitle = substr($originalTitle, strrpos($originalTitle, ':') + 1);
521
-
522
-                $this->logger->info(sprintf('[Metadata] Title has a colon in the middle, new title is: \'%s\'', $curTitle));
523
-
524
-                // If the title is now too short, try the first colon instead:
525
-                if (count(preg_split('/\s+/', $curTitle)) < 3) {
526
-                    $curTitle = substr($originalTitle, strpos($originalTitle, ':') + 1);
527
-                    $this->logger->info(sprintf('[Metadata] Title too short, using the first part of the title instead: \'%s\'', $curTitle));
528
-                } elseif (count(preg_split('/\s+/', substr($curTitle, 0, strpos($curTitle, ':')))) > 5) {
529
-                    // But if we have too many words before the colon there's something weird
530
-                    // with the titles and the H tags so let's just use the original title instead
531
-                    $curTitle = $originalTitle;
532
-                }
533
-            }
534
-        } elseif (mb_strlen($curTitle) > 150 || mb_strlen($curTitle) < 15) {
535
-            $hOnes = $this->dom->getElementsByTagName('h1');
536
-
537
-            if ($hOnes->length === 1) {
538
-                $curTitle = $hOnes->item(0)->nodeValue;
539
-                $this->logger->info(sprintf('[Metadata] Using title from an H1 node: \'%s\'', $curTitle));
540
-            }
541
-        }
542
-
543
-        $curTitle = trim($curTitle);
544
-
545
-        /*
493
+		if (preg_match('/ [\|\-\\\\\/>»] /i', $curTitle)) {
494
+			$titleHadHierarchicalSeparators = (bool)preg_match('/ [\\\\\/>»] /', $curTitle);
495
+			$curTitle = preg_replace('/(.*)[\|\-\\\\\/>»] .*/i', '$1', $originalTitle);
496
+
497
+			$this->logger->info(sprintf('[Metadata] Found hierarchical separators in title, new title is: \'%s\'', $curTitle));
498
+
499
+			// If the resulting title is too short (3 words or fewer), remove
500
+			// the first part instead:
501
+			if (count(preg_split('/\s+/', $curTitle)) < 3) {
502
+				$curTitle = preg_replace('/[^\|\-\\\\\/>»]*[\|\-\\\\\/>»](.*)/i', '$1', $originalTitle);
503
+				$this->logger->info(sprintf('[Metadata] Title too short, using the first part of the title instead: \'%s\'', $curTitle));
504
+			}
505
+		} elseif (strpos($curTitle, ': ') !== false) {
506
+			// Check if we have an heading containing this exact string, so we
507
+			// could assume it's the full title.
508
+			$match = false;
509
+			for ($i = 1; $i <= 2; $i++) {
510
+				foreach ($this->dom->getElementsByTagName('h' . $i) as $hTag) {
511
+					// Trim texts to avoid having false negatives when the title is surrounded by spaces or tabs
512
+					if (trim($hTag->nodeValue) === trim($curTitle)) {
513
+						$match = true;
514
+					}
515
+				}
516
+			}
517
+
518
+			// If we don't, let's extract the title out of the original title string.
519
+			if (!$match) {
520
+				$curTitle = substr($originalTitle, strrpos($originalTitle, ':') + 1);
521
+
522
+				$this->logger->info(sprintf('[Metadata] Title has a colon in the middle, new title is: \'%s\'', $curTitle));
523
+
524
+				// If the title is now too short, try the first colon instead:
525
+				if (count(preg_split('/\s+/', $curTitle)) < 3) {
526
+					$curTitle = substr($originalTitle, strpos($originalTitle, ':') + 1);
527
+					$this->logger->info(sprintf('[Metadata] Title too short, using the first part of the title instead: \'%s\'', $curTitle));
528
+				} elseif (count(preg_split('/\s+/', substr($curTitle, 0, strpos($curTitle, ':')))) > 5) {
529
+					// But if we have too many words before the colon there's something weird
530
+					// with the titles and the H tags so let's just use the original title instead
531
+					$curTitle = $originalTitle;
532
+				}
533
+			}
534
+		} elseif (mb_strlen($curTitle) > 150 || mb_strlen($curTitle) < 15) {
535
+			$hOnes = $this->dom->getElementsByTagName('h1');
536
+
537
+			if ($hOnes->length === 1) {
538
+				$curTitle = $hOnes->item(0)->nodeValue;
539
+				$this->logger->info(sprintf('[Metadata] Using title from an H1 node: \'%s\'', $curTitle));
540
+			}
541
+		}
542
+
543
+		$curTitle = trim($curTitle);
544
+
545
+		/*
546 546
          * If we now have 4 words or fewer as our title, and either no
547 547
          * 'hierarchical' separators (\, /, > or ») were found in the original
548 548
          * title or we decreased the number of words by more than 1 word, use
549 549
          * the original title.
550 550
          */
551
-        $curTitleWordCount = count(preg_split('/\s+/', $curTitle));
552
-        $originalTitleWordCount = count(preg_split('/\s+/', preg_replace('/[\|\-\\\\\/>»]+/', '', $originalTitle))) - 1;
553
-
554
-        if ($curTitleWordCount <= 4 &&
555
-            (!$titleHadHierarchicalSeparators || $curTitleWordCount !== $originalTitleWordCount)) {
556
-            $curTitle = $originalTitle;
557
-
558
-            $this->logger->info(sprintf('Using title from an H1 node: \'%s\'', $curTitle));
559
-        }
560
-
561
-        return $curTitle;
562
-    }
563
-
564
-    /**
565
-     * Convert URI to an absolute URI.
566
-     *
567
-     * @param $uri string URI to convert
568
-     *
569
-     * @return string
570
-     */
571
-    private function toAbsoluteURI($uri)
572
-    {
573
-        list($pathBase, $scheme, $prePath) = $this->getPathInfo($this->configuration->getOriginalURL());
574
-
575
-        // If this is already an absolute URI, return it.
576
-        if (preg_match('/^[a-zA-Z][a-zA-Z0-9\+\-\.]*:/', $uri)) {
577
-            return $uri;
578
-        }
579
-
580
-        // Scheme-rooted relative URI.
581
-        if (substr($uri, 0, 2) === '//') {
582
-            return $scheme . '://' . substr($uri, 2);
583
-        }
584
-
585
-        // Prepath-rooted relative URI.
586
-        if (substr($uri, 0, 1) === '/') {
587
-            return $prePath . $uri;
588
-        }
589
-
590
-        // Dotslash relative URI.
591
-        if (strpos($uri, './') === 0) {
592
-            return $pathBase . substr($uri, 2);
593
-        }
594
-        // Ignore hash URIs:
595
-        if (substr($uri, 0, 1) === '#') {
596
-            return $uri;
597
-        }
598
-
599
-        // Standard relative URI; add entire path. pathBase already includes a
600
-        // trailing "/".
601
-        return $pathBase . $uri;
602
-    }
603
-
604
-    /**
605
-     * Returns full path info of an URL.
606
-     *
607
-     * @param  string $url
608
-     *
609
-     * @return array [$pathBase, $scheme, $prePath]
610
-     */
611
-    public function getPathInfo($url)
612
-    {
613
-        // Check for base URLs
614
-        if ($this->dom->baseURI !== null) {
615
-            if (substr($this->dom->baseURI, 0, 1) === '/') {
616
-                // URLs starting with '/' override completely the URL defined in the link
617
-                $pathBase = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . $this->dom->baseURI;
618
-            } else {
619
-                // Otherwise just prepend the base to the actual path
620
-                $pathBase = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . dirname(parse_url($url, PHP_URL_PATH)) . '/' . rtrim($this->dom->baseURI, '/') . '/';
621
-            }
622
-        } else {
623
-            $pathBase = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . dirname(parse_url($url, PHP_URL_PATH)) . '/';
624
-        }
625
-
626
-        $scheme = parse_url($pathBase, PHP_URL_SCHEME);
627
-        $prePath = $scheme . '://' . parse_url($pathBase, PHP_URL_HOST);
628
-
629
-        return [$pathBase, $scheme, $prePath];
630
-    }
631
-
632
-    /**
633
-     * Gets nodes from the root element.
634
-     *
635
-     * @param $node DOMNode|DOMText
636
-     *
637
-     * @return array
638
-     */
639
-    private function getNodes($node)
640
-    {
641
-        $this->logger->info('[Get Nodes] Retrieving nodes...');
642
-
643
-        $stripUnlikelyCandidates = $this->configuration->getStripUnlikelyCandidates();
644
-
645
-        $elementsToScore = [];
646
-
647
-        /*
551
+		$curTitleWordCount = count(preg_split('/\s+/', $curTitle));
552
+		$originalTitleWordCount = count(preg_split('/\s+/', preg_replace('/[\|\-\\\\\/>»]+/', '', $originalTitle))) - 1;
553
+
554
+		if ($curTitleWordCount <= 4 &&
555
+			(!$titleHadHierarchicalSeparators || $curTitleWordCount !== $originalTitleWordCount)) {
556
+			$curTitle = $originalTitle;
557
+
558
+			$this->logger->info(sprintf('Using title from an H1 node: \'%s\'', $curTitle));
559
+		}
560
+
561
+		return $curTitle;
562
+	}
563
+
564
+	/**
565
+	 * Convert URI to an absolute URI.
566
+	 *
567
+	 * @param $uri string URI to convert
568
+	 *
569
+	 * @return string
570
+	 */
571
+	private function toAbsoluteURI($uri)
572
+	{
573
+		list($pathBase, $scheme, $prePath) = $this->getPathInfo($this->configuration->getOriginalURL());
574
+
575
+		// If this is already an absolute URI, return it.
576
+		if (preg_match('/^[a-zA-Z][a-zA-Z0-9\+\-\.]*:/', $uri)) {
577
+			return $uri;
578
+		}
579
+
580
+		// Scheme-rooted relative URI.
581
+		if (substr($uri, 0, 2) === '//') {
582
+			return $scheme . '://' . substr($uri, 2);
583
+		}
584
+
585
+		// Prepath-rooted relative URI.
586
+		if (substr($uri, 0, 1) === '/') {
587
+			return $prePath . $uri;
588
+		}
589
+
590
+		// Dotslash relative URI.
591
+		if (strpos($uri, './') === 0) {
592
+			return $pathBase . substr($uri, 2);
593
+		}
594
+		// Ignore hash URIs:
595
+		if (substr($uri, 0, 1) === '#') {
596
+			return $uri;
597
+		}
598
+
599
+		// Standard relative URI; add entire path. pathBase already includes a
600
+		// trailing "/".
601
+		return $pathBase . $uri;
602
+	}
603
+
604
+	/**
605
+	 * Returns full path info of an URL.
606
+	 *
607
+	 * @param  string $url
608
+	 *
609
+	 * @return array [$pathBase, $scheme, $prePath]
610
+	 */
611
+	public function getPathInfo($url)
612
+	{
613
+		// Check for base URLs
614
+		if ($this->dom->baseURI !== null) {
615
+			if (substr($this->dom->baseURI, 0, 1) === '/') {
616
+				// URLs starting with '/' override completely the URL defined in the link
617
+				$pathBase = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . $this->dom->baseURI;
618
+			} else {
619
+				// Otherwise just prepend the base to the actual path
620
+				$pathBase = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . dirname(parse_url($url, PHP_URL_PATH)) . '/' . rtrim($this->dom->baseURI, '/') . '/';
621
+			}
622
+		} else {
623
+			$pathBase = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . dirname(parse_url($url, PHP_URL_PATH)) . '/';
624
+		}
625
+
626
+		$scheme = parse_url($pathBase, PHP_URL_SCHEME);
627
+		$prePath = $scheme . '://' . parse_url($pathBase, PHP_URL_HOST);
628
+
629
+		return [$pathBase, $scheme, $prePath];
630
+	}
631
+
632
+	/**
633
+	 * Gets nodes from the root element.
634
+	 *
635
+	 * @param $node DOMNode|DOMText
636
+	 *
637
+	 * @return array
638
+	 */
639
+	private function getNodes($node)
640
+	{
641
+		$this->logger->info('[Get Nodes] Retrieving nodes...');
642
+
643
+		$stripUnlikelyCandidates = $this->configuration->getStripUnlikelyCandidates();
644
+
645
+		$elementsToScore = [];
646
+
647
+		/*
648 648
          * First, node prepping. Trash nodes that look cruddy (like ones with the
649 649
          * class name "comment", etc), and turn divs into P tags where they have been
650 650
          * used inappropriately (as in, where they contain no other block level elements.)
651 651
          */
652 652
 
653
-        while ($node) {
654
-            // Remove DOMComments nodes as we don't need them and mess up children counting
655
-            if ($node->nodeType === XML_COMMENT_NODE) {
656
-                $this->logger->debug(sprintf('[Get Nodes] Found comment node, removing... Node content was: \'%s\'', substr($node->nodeValue, 0, 128)));
657
-                $node = NodeUtility::removeAndGetNext($node);
658
-                continue;
659
-            }
660
-
661
-            $matchString = $node->getAttribute('class') . ' ' . $node->getAttribute('id');
662
-
663
-            if (!$node->isProbablyVisible()) {
664
-                $this->logger->debug(sprintf('[Get Nodes] Removing hidden node... Match string was: \'%s\'', $matchString));
665
-                $node = NodeUtility::removeAndGetNext($node);
666
-                continue;
667
-            }
668
-
669
-            // Check to see if this node is a byline, and remove it if it is.
670
-            if ($this->checkByline($node, $matchString)) {
671
-                $this->logger->debug(sprintf('[Get Nodes] Found byline, removing... Node content was: \'%s\'', substr($node->nodeValue, 0, 128)));
672
-                $node = NodeUtility::removeAndGetNext($node);
673
-                continue;
674
-            }
675
-
676
-            // Remove unlikely candidates
677
-            if ($stripUnlikelyCandidates) {
678
-                if (
679
-                    preg_match(NodeUtility::$regexps['unlikelyCandidates'], $matchString) &&
680
-                    !preg_match(NodeUtility::$regexps['okMaybeItsACandidate'], $matchString) &&
681
-                    $node->nodeName !== 'body' &&
682
-                    $node->nodeName !== 'a'
683
-                ) {
684
-                    $this->logger->debug(sprintf('[Get Nodes] Removing unlikely candidate. Node content was: \'%s\'', substr($node->nodeValue, 0, 128)));
685
-                    $node = NodeUtility::removeAndGetNext($node);
686
-                    continue;
687
-                }
688
-            }
689
-
690
-            // Remove DIV, SECTION, and HEADER nodes without any content(e.g. text, image, video, or iframe).
691
-            if (($node->nodeName === 'div' || $node->nodeName === 'section' || $node->nodeName === 'header' ||
692
-                    $node->nodeName === 'h1' || $node->nodeName === 'h2' || $node->nodeName === 'h3' ||
693
-                    $node->nodeName === 'h4' || $node->nodeName === 'h5' || $node->nodeName === 'h6' ||
694
-                    $node->nodeName === 'p') &&
695
-                $node->isElementWithoutContent()) {
696
-                $this->logger->debug(sprintf('[Get Nodes] Removing empty \'%s\' node.', $node->nodeName));
697
-                $node = NodeUtility::removeAndGetNext($node);
698
-                continue;
699
-            }
700
-
701
-            if (in_array(strtolower($node->nodeName), $this->defaultTagsToScore)) {
702
-                $this->logger->debug(sprintf('[Get Nodes] Adding node to score list, node content is: \'%s\'', substr($node->nodeValue, 0, 128)));
703
-                $elementsToScore[] = $node;
704
-            }
705
-
706
-            // Turn all divs that don't have children block level elements into p's
707
-            if ($node->nodeName === 'div') {
708
-                // Put phrasing content into paragraphs.
709
-                $p = null;
710
-                $childNode = $node->firstChild;
711
-                while ($childNode) {
712
-                    $nextSibling = $childNode->nextSibling;
713
-                    if ($childNode->isPhrasingContent()) {
714
-                        if ($p !== null) {
715
-                            $p->appendChild($childNode);
716
-                        } elseif (!$childNode->isWhitespace()) {
717
-                            $p = $this->dom->createElement('p');
718
-                            $node->replaceChild($p, $childNode);
719
-                            $p->appendChild($childNode);
720
-                        }
721
-                    } elseif ($p !== null) {
722
-                        while ($p->lastChild && $p->lastChild->isWhitespace()) {
723
-                            $p->removeChild($p->lastChild);
724
-                        }
725
-                        $p = null;
726
-                    }
727
-                    $childNode = $nextSibling;
728
-                }
729
-
730
-                /*
653
+		while ($node) {
654
+			// Remove DOMComments nodes as we don't need them and mess up children counting
655
+			if ($node->nodeType === XML_COMMENT_NODE) {
656
+				$this->logger->debug(sprintf('[Get Nodes] Found comment node, removing... Node content was: \'%s\'', substr($node->nodeValue, 0, 128)));
657
+				$node = NodeUtility::removeAndGetNext($node);
658
+				continue;
659
+			}
660
+
661
+			$matchString = $node->getAttribute('class') . ' ' . $node->getAttribute('id');
662
+
663
+			if (!$node->isProbablyVisible()) {
664
+				$this->logger->debug(sprintf('[Get Nodes] Removing hidden node... Match string was: \'%s\'', $matchString));
665
+				$node = NodeUtility::removeAndGetNext($node);
666
+				continue;
667
+			}
668
+
669
+			// Check to see if this node is a byline, and remove it if it is.
670
+			if ($this->checkByline($node, $matchString)) {
671
+				$this->logger->debug(sprintf('[Get Nodes] Found byline, removing... Node content was: \'%s\'', substr($node->nodeValue, 0, 128)));
672
+				$node = NodeUtility::removeAndGetNext($node);
673
+				continue;
674
+			}
675
+
676
+			// Remove unlikely candidates
677
+			if ($stripUnlikelyCandidates) {
678
+				if (
679
+					preg_match(NodeUtility::$regexps['unlikelyCandidates'], $matchString) &&
680
+					!preg_match(NodeUtility::$regexps['okMaybeItsACandidate'], $matchString) &&
681
+					$node->nodeName !== 'body' &&
682
+					$node->nodeName !== 'a'
683
+				) {
684
+					$this->logger->debug(sprintf('[Get Nodes] Removing unlikely candidate. Node content was: \'%s\'', substr($node->nodeValue, 0, 128)));
685
+					$node = NodeUtility::removeAndGetNext($node);
686
+					continue;
687
+				}
688
+			}
689
+
690
+			// Remove DIV, SECTION, and HEADER nodes without any content(e.g. text, image, video, or iframe).
691
+			if (($node->nodeName === 'div' || $node->nodeName === 'section' || $node->nodeName === 'header' ||
692
+					$node->nodeName === 'h1' || $node->nodeName === 'h2' || $node->nodeName === 'h3' ||
693
+					$node->nodeName === 'h4' || $node->nodeName === 'h5' || $node->nodeName === 'h6' ||
694
+					$node->nodeName === 'p') &&
695
+				$node->isElementWithoutContent()) {
696
+				$this->logger->debug(sprintf('[Get Nodes] Removing empty \'%s\' node.', $node->nodeName));
697
+				$node = NodeUtility::removeAndGetNext($node);
698
+				continue;
699
+			}
700
+
701
+			if (in_array(strtolower($node->nodeName), $this->defaultTagsToScore)) {
702
+				$this->logger->debug(sprintf('[Get Nodes] Adding node to score list, node content is: \'%s\'', substr($node->nodeValue, 0, 128)));
703
+				$elementsToScore[] = $node;
704
+			}
705
+
706
+			// Turn all divs that don't have children block level elements into p's
707
+			if ($node->nodeName === 'div') {
708
+				// Put phrasing content into paragraphs.
709
+				$p = null;
710
+				$childNode = $node->firstChild;
711
+				while ($childNode) {
712
+					$nextSibling = $childNode->nextSibling;
713
+					if ($childNode->isPhrasingContent()) {
714
+						if ($p !== null) {
715
+							$p->appendChild($childNode);
716
+						} elseif (!$childNode->isWhitespace()) {
717
+							$p = $this->dom->createElement('p');
718
+							$node->replaceChild($p, $childNode);
719
+							$p->appendChild($childNode);
720
+						}
721
+					} elseif ($p !== null) {
722
+						while ($p->lastChild && $p->lastChild->isWhitespace()) {
723
+							$p->removeChild($p->lastChild);
724
+						}
725
+						$p = null;
726
+					}
727
+					$childNode = $nextSibling;
728
+				}
729
+
730
+				/*
731 731
                  * Sites like http://mobile.slate.com encloses each paragraph with a DIV
732 732
                  * element. DIVs with only a P element inside and no text content can be
733 733
                  * safely converted into plain P elements to avoid confusing the scoring
734 734
                  * algorithm with DIVs with are, in practice, paragraphs.
735 735
                  */
736
-                if ($node->hasSingleTagInsideElement('p') && $node->getLinkDensity() < 0.25) {
737
-                    $this->logger->debug(sprintf('[Get Nodes] Found DIV with a single P node, removing DIV. Node content is: \'%s\'', substr($node->nodeValue, 0, 128)));
738
-                    $pNode = NodeUtility::filterTextNodes($node->childNodes)->item(0);
739
-                    $node->parentNode->replaceChild($pNode, $node);
740
-                    $node = $pNode;
741
-                    $elementsToScore[] = $node;
742
-                } elseif (!$node->hasSingleChildBlockElement()) {
743
-                    $this->logger->debug(sprintf('[Get Nodes] Found DIV with a single child block element, converting to a P node. Node content is: \'%s\'', substr($node->nodeValue, 0, 128)));
744
-                    $node = NodeUtility::setNodeTag($node, 'p');
745
-                    $elementsToScore[] = $node;
746
-                }
747
-            }
748
-
749
-            $node = NodeUtility::getNextNode($node);
750
-        }
751
-
752
-        return $elementsToScore;
753
-    }
754
-
755
-    /**
756
-     * Checks if the node is a byline.
757
-     *
758
-     * @param DOMNode $node
759
-     * @param string $matchString
760
-     *
761
-     * @return bool
762
-     */
763
-    private function checkByline($node, $matchString)
764
-    {
765
-        if (!$this->configuration->getArticleByLine()) {
766
-            return false;
767
-        }
768
-
769
-        /*
736
+				if ($node->hasSingleTagInsideElement('p') && $node->getLinkDensity() < 0.25) {
737
+					$this->logger->debug(sprintf('[Get Nodes] Found DIV with a single P node, removing DIV. Node content is: \'%s\'', substr($node->nodeValue, 0, 128)));
738
+					$pNode = NodeUtility::filterTextNodes($node->childNodes)->item(0);
739
+					$node->parentNode->replaceChild($pNode, $node);
740
+					$node = $pNode;
741
+					$elementsToScore[] = $node;
742
+				} elseif (!$node->hasSingleChildBlockElement()) {
743
+					$this->logger->debug(sprintf('[Get Nodes] Found DIV with a single child block element, converting to a P node. Node content is: \'%s\'', substr($node->nodeValue, 0, 128)));
744
+					$node = NodeUtility::setNodeTag($node, 'p');
745
+					$elementsToScore[] = $node;
746
+				}
747
+			}
748
+
749
+			$node = NodeUtility::getNextNode($node);
750
+		}
751
+
752
+		return $elementsToScore;
753
+	}
754
+
755
+	/**
756
+	 * Checks if the node is a byline.
757
+	 *
758
+	 * @param DOMNode $node
759
+	 * @param string $matchString
760
+	 *
761
+	 * @return bool
762
+	 */
763
+	private function checkByline($node, $matchString)
764
+	{
765
+		if (!$this->configuration->getArticleByLine()) {
766
+			return false;
767
+		}
768
+
769
+		/*
770 770
          * Check if the byline is already set
771 771
          */
772
-        if ($this->getAuthor()) {
773
-            return false;
774
-        }
775
-
776
-        $rel = $node->getAttribute('rel');
777
-
778
-        if ($rel === 'author' || preg_match(NodeUtility::$regexps['byline'], $matchString) && $this->isValidByline($node->getTextContent())) {
779
-            $this->logger->info(sprintf('[Metadata] Found article author: \'%s\'', $node->getTextContent()));
780
-            $this->setAuthor(trim($node->getTextContent()));
781
-
782
-            return true;
783
-        }
784
-
785
-        return false;
786
-    }
787
-
788
-    /**
789
-     * Checks the validity of a byLine. Based on string length.
790
-     *
791
-     * @param string $text
792
-     *
793
-     * @return bool
794
-     */
795
-    private function isValidByline($text)
796
-    {
797
-        if (gettype($text) == 'string') {
798
-            $byline = trim($text);
799
-
800
-            return (mb_strlen($byline) > 0) && (mb_strlen($byline) < 100);
801
-        }
802
-
803
-        return false;
804
-    }
805
-
806
-    /**
807
-     * Removes all the scripts of the html.
808
-     *
809
-     * @param DOMDocument $dom
810
-     */
811
-    private function removeScripts(DOMDocument $dom)
812
-    {
813
-        foreach (['script', 'noscript'] as $tag) {
814
-            $nodes = $dom->getElementsByTagName($tag);
815
-            foreach (iterator_to_array($nodes) as $node) {
816
-                NodeUtility::removeNode($node);
817
-            }
818
-        }
819
-    }
820
-
821
-    /**
822
-     * Prepares the document for parsing.
823
-     *
824
-     * @param DOMDocument $dom
825
-     */
826
-    private function prepDocument(DOMDocument $dom)
827
-    {
828
-        $this->logger->info('[PrepDocument] Preparing document for parsing...');
829
-
830
-        foreach ($dom->shiftingAwareGetElementsByTagName('br') as $br) {
831
-            $next = $br->nextSibling;
832
-
833
-            /*
772
+		if ($this->getAuthor()) {
773
+			return false;
774
+		}
775
+
776
+		$rel = $node->getAttribute('rel');
777
+
778
+		if ($rel === 'author' || preg_match(NodeUtility::$regexps['byline'], $matchString) && $this->isValidByline($node->getTextContent())) {
779
+			$this->logger->info(sprintf('[Metadata] Found article author: \'%s\'', $node->getTextContent()));
780
+			$this->setAuthor(trim($node->getTextContent()));
781
+
782
+			return true;
783
+		}
784
+
785
+		return false;
786
+	}
787
+
788
+	/**
789
+	 * Checks the validity of a byLine. Based on string length.
790
+	 *
791
+	 * @param string $text
792
+	 *
793
+	 * @return bool
794
+	 */
795
+	private function isValidByline($text)
796
+	{
797
+		if (gettype($text) == 'string') {
798
+			$byline = trim($text);
799
+
800
+			return (mb_strlen($byline) > 0) && (mb_strlen($byline) < 100);
801
+		}
802
+
803
+		return false;
804
+	}
805
+
806
+	/**
807
+	 * Removes all the scripts of the html.
808
+	 *
809
+	 * @param DOMDocument $dom
810
+	 */
811
+	private function removeScripts(DOMDocument $dom)
812
+	{
813
+		foreach (['script', 'noscript'] as $tag) {
814
+			$nodes = $dom->getElementsByTagName($tag);
815
+			foreach (iterator_to_array($nodes) as $node) {
816
+				NodeUtility::removeNode($node);
817
+			}
818
+		}
819
+	}
820
+
821
+	/**
822
+	 * Prepares the document for parsing.
823
+	 *
824
+	 * @param DOMDocument $dom
825
+	 */
826
+	private function prepDocument(DOMDocument $dom)
827
+	{
828
+		$this->logger->info('[PrepDocument] Preparing document for parsing...');
829
+
830
+		foreach ($dom->shiftingAwareGetElementsByTagName('br') as $br) {
831
+			$next = $br->nextSibling;
832
+
833
+			/*
834 834
              * Whether 2 or more <br> elements have been found and replaced with a
835 835
              * <p> block.
836 836
              */
837
-            $replaced = false;
837
+			$replaced = false;
838 838
 
839
-            /*
839
+			/*
840 840
              * If we find a <br> chain, remove the <br>s until we hit another element
841 841
              * or non-whitespace. This leaves behind the first <br> in the chain
842 842
              * (which will be replaced with a <p> later).
843 843
              */
844
-            while (($next = NodeUtility::nextElement($next)) && ($next->nodeName === 'br')) {
845
-                $this->logger->debug('[PrepDocument] Removing chain of BR nodes...');
844
+			while (($next = NodeUtility::nextElement($next)) && ($next->nodeName === 'br')) {
845
+				$this->logger->debug('[PrepDocument] Removing chain of BR nodes...');
846 846
 
847
-                $replaced = true;
848
-                $brSibling = $next->nextSibling;
849
-                $next->parentNode->removeChild($next);
850
-                $next = $brSibling;
851
-            }
847
+				$replaced = true;
848
+				$brSibling = $next->nextSibling;
849
+				$next->parentNode->removeChild($next);
850
+				$next = $brSibling;
851
+			}
852 852
 
853
-            /*
853
+			/*
854 854
              * If we removed a <br> chain, replace the remaining <br> with a <p>. Add
855 855
              * all sibling nodes as children of the <p> until we hit another <br>
856 856
              * chain.
857 857
              */
858 858
 
859
-            if ($replaced) {
860
-                $p = $dom->createElement('p');
861
-                $br->parentNode->replaceChild($p, $br);
862
-
863
-                $next = $p->nextSibling;
864
-                while ($next) {
865
-                    // If we've hit another <br><br>, we're done adding children to this <p>.
866
-                    if ($next->nodeName === 'br') {
867
-                        $nextElem = NodeUtility::nextElement($next->nextSibling);
868
-                        if ($nextElem && $nextElem->nodeName === 'br') {
869
-                            break;
870
-                        }
871
-                    }
872
-
873
-                    if (!$next->isPhrasingContent()) {
874
-                        break;
875
-                    }
876
-
877
-                    $this->logger->debug('[PrepDocument] Replacing BR with a P node...');
878
-
879
-                    // Otherwise, make this node a child of the new <p>.
880
-                    $sibling = $next->nextSibling;
881
-                    $p->appendChild($next);
882
-                    $next = $sibling;
883
-                }
884
-
885
-                while ($p->lastChild && $p->lastChild->isWhitespace()) {
886
-                    $p->removeChild($p->lastChild);
887
-                }
888
-
889
-                if ($p->parentNode->tagName === 'p') {
890
-                    NodeUtility::setNodeTag($p->parentNode, 'div');
891
-                }
892
-            }
893
-        }
894
-
895
-        // Replace font tags with span
896
-        $fonts = $dom->getElementsByTagName('font');
897
-        $length = $fonts->length;
898
-        for ($i = 0; $i < $length; $i++) {
899
-            $this->logger->debug('[PrepDocument] Converting font tag into a span tag.');
900
-            $font = $fonts->item($length - 1 - $i);
901
-            NodeUtility::setNodeTag($font, 'span');
902
-        }
903
-    }
904
-
905
-    /**
906
-     * Assign scores to each node. Returns full article parsed or false on error.
907
-     *
908
-     * @param array $nodes
909
-     *
910
-     * @return DOMDocument|bool
911
-     */
912
-    private function rateNodes($nodes)
913
-    {
914
-        $this->logger->info('[Rating] Rating nodes...');
915
-
916
-        $candidates = [];
917
-
918
-        /** @var DOMElement $node */
919
-        foreach ($nodes as $node) {
920
-            if (is_null($node->parentNode)) {
921
-                continue;
922
-            }
923
-
924
-            // Discard nodes with less than 25 characters, without blank space
925
-            if (mb_strlen($node->getTextContent(true)) < 25) {
926
-                continue;
927
-            }
928
-
929
-            $ancestors = $node->getNodeAncestors();
930
-
931
-            // Exclude nodes with no ancestor
932
-            if (count($ancestors) === 0) {
933
-                continue;
934
-            }
935
-
936
-            // Start with a point for the paragraph itself as a base.
937
-            $contentScore = 1;
938
-
939
-            // Add points for any commas within this paragraph.
940
-            $contentScore += count(explode(',', $node->getTextContent(true)));
941
-
942
-            // For every 100 characters in this paragraph, add another point. Up to 3 points.
943
-            $contentScore += min(floor(mb_strlen($node->getTextContent(true)) / 100), 3);
944
-
945
-            $this->logger->debug(sprintf('[Rating] Node score %s, content: \'%s\'', $contentScore, substr($node->nodeValue, 0, 128)));
946
-
947
-            /** @var $ancestor DOMElement */
948
-            foreach ($ancestors as $level => $ancestor) {
949
-                $this->logger->debug('[Rating] Found ancestor, initializing and adding it as a candidate...');
950
-                if (!$ancestor->isInitialized()) {
951
-                    $ancestor->initializeNode($this->configuration->getWeightClasses());
952
-                    $candidates[] = $ancestor;
953
-                }
954
-
955
-                /*
859
+			if ($replaced) {
860
+				$p = $dom->createElement('p');
861
+				$br->parentNode->replaceChild($p, $br);
862
+
863
+				$next = $p->nextSibling;
864
+				while ($next) {
865
+					// If we've hit another <br><br>, we're done adding children to this <p>.
866
+					if ($next->nodeName === 'br') {
867
+						$nextElem = NodeUtility::nextElement($next->nextSibling);
868
+						if ($nextElem && $nextElem->nodeName === 'br') {
869
+							break;
870
+						}
871
+					}
872
+
873
+					if (!$next->isPhrasingContent()) {
874
+						break;
875
+					}
876
+
877
+					$this->logger->debug('[PrepDocument] Replacing BR with a P node...');
878
+
879
+					// Otherwise, make this node a child of the new <p>.
880
+					$sibling = $next->nextSibling;
881
+					$p->appendChild($next);
882
+					$next = $sibling;
883
+				}
884
+
885
+				while ($p->lastChild && $p->lastChild->isWhitespace()) {
886
+					$p->removeChild($p->lastChild);
887
+				}
888
+
889
+				if ($p->parentNode->tagName === 'p') {
890
+					NodeUtility::setNodeTag($p->parentNode, 'div');
891
+				}
892
+			}
893
+		}
894
+
895
+		// Replace font tags with span
896
+		$fonts = $dom->getElementsByTagName('font');
897
+		$length = $fonts->length;
898
+		for ($i = 0; $i < $length; $i++) {
899
+			$this->logger->debug('[PrepDocument] Converting font tag into a span tag.');
900
+			$font = $fonts->item($length - 1 - $i);
901
+			NodeUtility::setNodeTag($font, 'span');
902
+		}
903
+	}
904
+
905
+	/**
906
+	 * Assign scores to each node. Returns full article parsed or false on error.
907
+	 *
908
+	 * @param array $nodes
909
+	 *
910
+	 * @return DOMDocument|bool
911
+	 */
912
+	private function rateNodes($nodes)
913
+	{
914
+		$this->logger->info('[Rating] Rating nodes...');
915
+
916
+		$candidates = [];
917
+
918
+		/** @var DOMElement $node */
919
+		foreach ($nodes as $node) {
920
+			if (is_null($node->parentNode)) {
921
+				continue;
922
+			}
923
+
924
+			// Discard nodes with less than 25 characters, without blank space
925
+			if (mb_strlen($node->getTextContent(true)) < 25) {
926
+				continue;
927
+			}
928
+
929
+			$ancestors = $node->getNodeAncestors();
930
+
931
+			// Exclude nodes with no ancestor
932
+			if (count($ancestors) === 0) {
933
+				continue;
934
+			}
935
+
936
+			// Start with a point for the paragraph itself as a base.
937
+			$contentScore = 1;
938
+
939
+			// Add points for any commas within this paragraph.
940
+			$contentScore += count(explode(',', $node->getTextContent(true)));
941
+
942
+			// For every 100 characters in this paragraph, add another point. Up to 3 points.
943
+			$contentScore += min(floor(mb_strlen($node->getTextContent(true)) / 100), 3);
944
+
945
+			$this->logger->debug(sprintf('[Rating] Node score %s, content: \'%s\'', $contentScore, substr($node->nodeValue, 0, 128)));
946
+
947
+			/** @var $ancestor DOMElement */
948
+			foreach ($ancestors as $level => $ancestor) {
949
+				$this->logger->debug('[Rating] Found ancestor, initializing and adding it as a candidate...');
950
+				if (!$ancestor->isInitialized()) {
951
+					$ancestor->initializeNode($this->configuration->getWeightClasses());
952
+					$candidates[] = $ancestor;
953
+				}
954
+
955
+				/*
956 956
                  * Node score divider:
957 957
                  *  - parent:             1 (no division)
958 958
                  *  - grandparent:        2
959 959
                  *  - great grandparent+: ancestor level * 3
960 960
                  */
961 961
 
962
-                if ($level === 0) {
963
-                    $scoreDivider = 1;
964
-                } elseif ($level === 1) {
965
-                    $scoreDivider = 2;
966
-                } else {
967
-                    $scoreDivider = $level * 3;
968
-                }
962
+				if ($level === 0) {
963
+					$scoreDivider = 1;
964
+				} elseif ($level === 1) {
965
+					$scoreDivider = 2;
966
+				} else {
967
+					$scoreDivider = $level * 3;
968
+				}
969 969
 
970
-                $currentScore = $ancestor->contentScore;
971
-                $ancestor->contentScore = $currentScore + ($contentScore / $scoreDivider);
970
+				$currentScore = $ancestor->contentScore;
971
+				$ancestor->contentScore = $currentScore + ($contentScore / $scoreDivider);
972 972
 
973
-                $this->logger->debug(sprintf('[Rating] Ancestor score %s, value: \'%s\'', $ancestor->contentScore, substr($ancestor->nodeValue, 0, 128)));
974
-            }
975
-        }
973
+				$this->logger->debug(sprintf('[Rating] Ancestor score %s, value: \'%s\'', $ancestor->contentScore, substr($ancestor->nodeValue, 0, 128)));
974
+			}
975
+		}
976 976
 
977
-        /*
977
+		/*
978 978
          * After we've calculated scores, loop through all of the possible
979 979
          * candidate nodes we found and find the one with the highest score.
980 980
          */
981 981
 
982
-        $topCandidates = [];
983
-        foreach ($candidates as $candidate) {
982
+		$topCandidates = [];
983
+		foreach ($candidates as $candidate) {
984 984
 
985
-            /*
985
+			/*
986 986
              * Scale the final candidates score based on link density. Good content
987 987
              * should have a relatively small link density (5% or less) and be mostly
988 988
              * unaffected by this operation.
989 989
              */
990 990
 
991
-            $candidate->contentScore = $candidate->contentScore * (1 - $candidate->getLinkDensity());
991
+			$candidate->contentScore = $candidate->contentScore * (1 - $candidate->getLinkDensity());
992 992
 
993
-            for ($i = 0; $i < $this->configuration->getMaxTopCandidates(); $i++) {
994
-                $aTopCandidate = isset($topCandidates[$i]) ? $topCandidates[$i] : null;
993
+			for ($i = 0; $i < $this->configuration->getMaxTopCandidates(); $i++) {
994
+				$aTopCandidate = isset($topCandidates[$i]) ? $topCandidates[$i] : null;
995 995
 
996
-                if (!$aTopCandidate || $candidate->contentScore > $aTopCandidate->contentScore) {
997
-                    array_splice($topCandidates, $i, 0, [$candidate]);
998
-                    if (count($topCandidates) > $this->configuration->getMaxTopCandidates()) {
999
-                        array_pop($topCandidates);
1000
-                    }
1001
-                    break;
1002
-                }
1003
-            }
1004
-        }
996
+				if (!$aTopCandidate || $candidate->contentScore > $aTopCandidate->contentScore) {
997
+					array_splice($topCandidates, $i, 0, [$candidate]);
998
+					if (count($topCandidates) > $this->configuration->getMaxTopCandidates()) {
999
+						array_pop($topCandidates);
1000
+					}
1001
+					break;
1002
+				}
1003
+			}
1004
+		}
1005 1005
 
1006
-        $topCandidate = isset($topCandidates[0]) ? $topCandidates[0] : null;
1007
-        $parentOfTopCandidate = null;
1006
+		$topCandidate = isset($topCandidates[0]) ? $topCandidates[0] : null;
1007
+		$parentOfTopCandidate = null;
1008 1008
 
1009
-        /*
1009
+		/*
1010 1010
          * If we still have no top candidate, just use the body as a last resort.
1011 1011
          * We also have to copy the body node so it is something we can modify.
1012 1012
          */
1013 1013
 
1014
-        if ($topCandidate === null || $topCandidate->nodeName === 'body') {
1015
-            $this->logger->info('[Rating] No top candidate found or top candidate is the body tag. Moving all child nodes to a new DIV node.');
1016
-
1017
-            // Move all of the page's children into topCandidate
1018
-            $topCandidate = new DOMDocument('1.0', 'utf-8');
1019
-            $topCandidate->encoding = 'UTF-8';
1020
-            $topCandidate->appendChild($topCandidate->createElement('div', ''));
1021
-            $kids = $this->dom->getElementsByTagName('body')->item(0)->childNodes;
1022
-
1023
-            // Cannot be foreached, don't ask me why.
1024
-            for ($i = 0; $i < $kids->length; $i++) {
1025
-                $import = $topCandidate->importNode($kids->item($i), true);
1026
-                $topCandidate->firstChild->appendChild($import);
1027
-            }
1028
-
1029
-            // Candidate must be created using firstChild to grab the DOMElement instead of the DOMDocument.
1030
-            $topCandidate = $topCandidate->firstChild;
1031
-        } elseif ($topCandidate) {
1032
-            $this->logger->info(sprintf('[Rating] Found top candidate, score: %s', $topCandidate->contentScore));
1033
-            // Find a better top candidate node if it contains (at least three) nodes which belong to `topCandidates` array
1034
-            // and whose scores are quite closed with current `topCandidate` node.
1035
-            $alternativeCandidateAncestors = [];
1036
-            for ($i = 1; $i < count($topCandidates); $i++) {
1037
-                // In some cases we may end up with a top candidate with zero content score. To avoid dividing by zero
1038
-                // we have to use max() and replace zero with a low value like 0.1
1039
-                if ($topCandidates[$i]->contentScore / max($topCandidate->contentScore, 0.1) >= 0.75) {
1040
-                    array_push($alternativeCandidateAncestors, $topCandidates[$i]->getNodeAncestors(false));
1041
-                }
1042
-            }
1043
-
1044
-            $MINIMUM_TOPCANDIDATES = 3;
1045
-            if (count($alternativeCandidateAncestors) >= $MINIMUM_TOPCANDIDATES) {
1046
-                $parentOfTopCandidate = $topCandidate->parentNode;
1047
-
1048
-                // Check if we are actually dealing with a DOMNode and not a DOMDocument node or higher
1049
-                while ($parentOfTopCandidate->nodeName !== 'body' && $parentOfTopCandidate->nodeType === XML_ELEMENT_NODE) {
1050
-                    $listsContainingThisAncestor = 0;
1051
-                    for ($ancestorIndex = 0; $ancestorIndex < count($alternativeCandidateAncestors) && $listsContainingThisAncestor < $MINIMUM_TOPCANDIDATES; $ancestorIndex++) {
1052
-                        $listsContainingThisAncestor += (int)in_array($parentOfTopCandidate, $alternativeCandidateAncestors[$ancestorIndex]);
1053
-                    }
1054
-                    if ($listsContainingThisAncestor >= $MINIMUM_TOPCANDIDATES) {
1055
-                        $topCandidate = $parentOfTopCandidate;
1056
-                        break;
1057
-                    }
1058
-                    $parentOfTopCandidate = $parentOfTopCandidate->parentNode;
1059
-                }
1060
-            }
1061
-
1062
-            /*
1014
+		if ($topCandidate === null || $topCandidate->nodeName === 'body') {
1015
+			$this->logger->info('[Rating] No top candidate found or top candidate is the body tag. Moving all child nodes to a new DIV node.');
1016
+
1017
+			// Move all of the page's children into topCandidate
1018
+			$topCandidate = new DOMDocument('1.0', 'utf-8');
1019
+			$topCandidate->encoding = 'UTF-8';
1020
+			$topCandidate->appendChild($topCandidate->createElement('div', ''));
1021
+			$kids = $this->dom->getElementsByTagName('body')->item(0)->childNodes;
1022
+
1023
+			// Cannot be foreached, don't ask me why.
1024
+			for ($i = 0; $i < $kids->length; $i++) {
1025
+				$import = $topCandidate->importNode($kids->item($i), true);
1026
+				$topCandidate->firstChild->appendChild($import);
1027
+			}
1028
+
1029
+			// Candidate must be created using firstChild to grab the DOMElement instead of the DOMDocument.
1030
+			$topCandidate = $topCandidate->firstChild;
1031
+		} elseif ($topCandidate) {
1032
+			$this->logger->info(sprintf('[Rating] Found top candidate, score: %s', $topCandidate->contentScore));
1033
+			// Find a better top candidate node if it contains (at least three) nodes which belong to `topCandidates` array
1034
+			// and whose scores are quite closed with current `topCandidate` node.
1035
+			$alternativeCandidateAncestors = [];
1036
+			for ($i = 1; $i < count($topCandidates); $i++) {
1037
+				// In some cases we may end up with a top candidate with zero content score. To avoid dividing by zero
1038
+				// we have to use max() and replace zero with a low value like 0.1
1039
+				if ($topCandidates[$i]->contentScore / max($topCandidate->contentScore, 0.1) >= 0.75) {
1040
+					array_push($alternativeCandidateAncestors, $topCandidates[$i]->getNodeAncestors(false));
1041
+				}
1042
+			}
1043
+
1044
+			$MINIMUM_TOPCANDIDATES = 3;
1045
+			if (count($alternativeCandidateAncestors) >= $MINIMUM_TOPCANDIDATES) {
1046
+				$parentOfTopCandidate = $topCandidate->parentNode;
1047
+
1048
+				// Check if we are actually dealing with a DOMNode and not a DOMDocument node or higher
1049
+				while ($parentOfTopCandidate->nodeName !== 'body' && $parentOfTopCandidate->nodeType === XML_ELEMENT_NODE) {
1050
+					$listsContainingThisAncestor = 0;
1051
+					for ($ancestorIndex = 0; $ancestorIndex < count($alternativeCandidateAncestors) && $listsContainingThisAncestor < $MINIMUM_TOPCANDIDATES; $ancestorIndex++) {
1052
+						$listsContainingThisAncestor += (int)in_array($parentOfTopCandidate, $alternativeCandidateAncestors[$ancestorIndex]);
1053
+					}
1054
+					if ($listsContainingThisAncestor >= $MINIMUM_TOPCANDIDATES) {
1055
+						$topCandidate = $parentOfTopCandidate;
1056
+						break;
1057
+					}
1058
+					$parentOfTopCandidate = $parentOfTopCandidate->parentNode;
1059
+				}
1060
+			}
1061
+
1062
+			/*
1063 1063
              * Because of our bonus system, parents of candidates might have scores
1064 1064
              * themselves. They get half of the node. There won't be nodes with higher
1065 1065
              * scores than our topCandidate, but if we see the score going *up* in the first
@@ -1069,736 +1069,736 @@  discard block
 block discarded – undo
1069 1069
              * tree.
1070 1070
              */
1071 1071
 
1072
-            $parentOfTopCandidate = $topCandidate->parentNode;
1073
-            $lastScore = $topCandidate->contentScore;
1074
-
1075
-            // The scores shouldn't get too low.
1076
-            $scoreThreshold = $lastScore / 3;
1077
-
1078
-            /* @var DOMElement $parentOfTopCandidate */
1079
-            while ($parentOfTopCandidate->nodeName !== 'body') {
1080
-                $parentScore = $parentOfTopCandidate->contentScore;
1081
-                if ($parentScore < $scoreThreshold) {
1082
-                    break;
1083
-                }
1084
-
1085
-                if ($parentScore > $lastScore) {
1086
-                    // Alright! We found a better parent to use.
1087
-                    $topCandidate = $parentOfTopCandidate;
1088
-                    $this->logger->info('[Rating] Found a better top candidate.');
1089
-                    break;
1090
-                }
1091
-                $lastScore = $parentOfTopCandidate->contentScore;
1092
-                $parentOfTopCandidate = $parentOfTopCandidate->parentNode;
1093
-            }
1094
-
1095
-            // If the top candidate is the only child, use parent instead. This will help sibling
1096
-            // joining logic when adjacent content is actually located in parent's sibling node.
1097
-            $parentOfTopCandidate = $topCandidate->parentNode;
1098
-            while ($parentOfTopCandidate->nodeName !== 'body' && count(NodeUtility::filterTextNodes($parentOfTopCandidate->childNodes)) === 1) {
1099
-                $topCandidate = $parentOfTopCandidate;
1100
-                $parentOfTopCandidate = $topCandidate->parentNode;
1101
-            }
1102
-        }
1103
-
1104
-        /*
1072
+			$parentOfTopCandidate = $topCandidate->parentNode;
1073
+			$lastScore = $topCandidate->contentScore;
1074
+
1075
+			// The scores shouldn't get too low.
1076
+			$scoreThreshold = $lastScore / 3;
1077
+
1078
+			/* @var DOMElement $parentOfTopCandidate */
1079
+			while ($parentOfTopCandidate->nodeName !== 'body') {
1080
+				$parentScore = $parentOfTopCandidate->contentScore;
1081
+				if ($parentScore < $scoreThreshold) {
1082
+					break;
1083
+				}
1084
+
1085
+				if ($parentScore > $lastScore) {
1086
+					// Alright! We found a better parent to use.
1087
+					$topCandidate = $parentOfTopCandidate;
1088
+					$this->logger->info('[Rating] Found a better top candidate.');
1089
+					break;
1090
+				}
1091
+				$lastScore = $parentOfTopCandidate->contentScore;
1092
+				$parentOfTopCandidate = $parentOfTopCandidate->parentNode;
1093
+			}
1094
+
1095
+			// If the top candidate is the only child, use parent instead. This will help sibling
1096
+			// joining logic when adjacent content is actually located in parent's sibling node.
1097
+			$parentOfTopCandidate = $topCandidate->parentNode;
1098
+			while ($parentOfTopCandidate->nodeName !== 'body' && count(NodeUtility::filterTextNodes($parentOfTopCandidate->childNodes)) === 1) {
1099
+				$topCandidate = $parentOfTopCandidate;
1100
+				$parentOfTopCandidate = $topCandidate->parentNode;
1101
+			}
1102
+		}
1103
+
1104
+		/*
1105 1105
          * Now that we have the top candidate, look through its siblings for content
1106 1106
          * that might also be related. Things like preambles, content split by ads
1107 1107
          * that we removed, etc.
1108 1108
          */
1109 1109
 
1110
-        $this->logger->info('[Rating] Creating final article content document...');
1110
+		$this->logger->info('[Rating] Creating final article content document...');
1111 1111
 
1112
-        $articleContent = new DOMDocument('1.0', 'utf-8');
1113
-        $articleContent->createElement('div');
1112
+		$articleContent = new DOMDocument('1.0', 'utf-8');
1113
+		$articleContent->createElement('div');
1114 1114
 
1115
-        $siblingScoreThreshold = max(10, $topCandidate->contentScore * 0.2);
1116
-        // Keep potential top candidate's parent node to try to get text direction of it later.
1117
-        $parentOfTopCandidate = $topCandidate->parentNode;
1118
-        $siblings = $parentOfTopCandidate->childNodes;
1115
+		$siblingScoreThreshold = max(10, $topCandidate->contentScore * 0.2);
1116
+		// Keep potential top candidate's parent node to try to get text direction of it later.
1117
+		$parentOfTopCandidate = $topCandidate->parentNode;
1118
+		$siblings = $parentOfTopCandidate->childNodes;
1119 1119
 
1120
-        $hasContent = false;
1120
+		$hasContent = false;
1121 1121
 
1122
-        $this->logger->info('[Rating] Adding top candidate siblings...');
1122
+		$this->logger->info('[Rating] Adding top candidate siblings...');
1123 1123
 
1124
-        /* @var DOMElement $sibling */
1125
-        // Can't foreach here because down there we might change the tag name and that causes the foreach to skip items
1126
-        for ($i = 0; $i < $siblings->length; $i++) {
1127
-            $sibling = $siblings[$i];
1128
-            $append = false;
1124
+		/* @var DOMElement $sibling */
1125
+		// Can't foreach here because down there we might change the tag name and that causes the foreach to skip items
1126
+		for ($i = 0; $i < $siblings->length; $i++) {
1127
+			$sibling = $siblings[$i];
1128
+			$append = false;
1129 1129
 
1130
-            if ($sibling === $topCandidate) {
1131
-                $this->logger->debug('[Rating] Sibling is equal to the top candidate, adding to the final article...');
1130
+			if ($sibling === $topCandidate) {
1131
+				$this->logger->debug('[Rating] Sibling is equal to the top candidate, adding to the final article...');
1132 1132
 
1133
-                $append = true;
1134
-            } else {
1135
-                $contentBonus = 0;
1133
+				$append = true;
1134
+			} else {
1135
+				$contentBonus = 0;
1136 1136
 
1137
-                // Give a bonus if sibling nodes and top candidates have the example same classname
1138
-                if ($sibling->getAttribute('class') === $topCandidate->getAttribute('class') && $topCandidate->getAttribute('class') !== '') {
1139
-                    $contentBonus += $topCandidate->contentScore * 0.2;
1140
-                }
1141
-                if ($sibling->contentScore + $contentBonus >= $siblingScoreThreshold) {
1142
-                    $append = true;
1143
-                } elseif ($sibling->nodeName === 'p') {
1144
-                    $linkDensity = $sibling->getLinkDensity();
1145
-                    $nodeContent = $sibling->getTextContent(true);
1137
+				// Give a bonus if sibling nodes and top candidates have the example same classname
1138
+				if ($sibling->getAttribute('class') === $topCandidate->getAttribute('class') && $topCandidate->getAttribute('class') !== '') {
1139
+					$contentBonus += $topCandidate->contentScore * 0.2;
1140
+				}
1141
+				if ($sibling->contentScore + $contentBonus >= $siblingScoreThreshold) {
1142
+					$append = true;
1143
+				} elseif ($sibling->nodeName === 'p') {
1144
+					$linkDensity = $sibling->getLinkDensity();
1145
+					$nodeContent = $sibling->getTextContent(true);
1146 1146
 
1147
-                    if (mb_strlen($nodeContent) > 80 && $linkDensity < 0.25) {
1148
-                        $append = true;
1149
-                    } elseif ($nodeContent && mb_strlen($nodeContent) < 80 && $linkDensity === 0 && preg_match('/\.( |$)/', $nodeContent)) {
1150
-                        $append = true;
1151
-                    }
1152
-                }
1153
-            }
1147
+					if (mb_strlen($nodeContent) > 80 && $linkDensity < 0.25) {
1148
+						$append = true;
1149
+					} elseif ($nodeContent && mb_strlen($nodeContent) < 80 && $linkDensity === 0 && preg_match('/\.( |$)/', $nodeContent)) {
1150
+						$append = true;
1151
+					}
1152
+				}
1153
+			}
1154 1154
 
1155
-            if ($append) {
1156
-                $this->logger->debug(sprintf('[Rating] Appending sibling to final article, content is: \'%s\'', substr($sibling->nodeValue, 0, 128)));
1155
+			if ($append) {
1156
+				$this->logger->debug(sprintf('[Rating] Appending sibling to final article, content is: \'%s\'', substr($sibling->nodeValue, 0, 128)));
1157 1157
 
1158
-                $hasContent = true;
1158
+				$hasContent = true;
1159 1159
 
1160
-                if (!in_array(strtolower($sibling->nodeName), $this->alterToDIVExceptions)) {
1161
-                    /*
1160
+				if (!in_array(strtolower($sibling->nodeName), $this->alterToDIVExceptions)) {
1161
+					/*
1162 1162
                      * We have a node that isn't a common block level element, like a form or td tag.
1163 1163
                      * Turn it into a div so it doesn't get filtered out later by accident.
1164 1164
                      */
1165
-                    $sibling = NodeUtility::setNodeTag($sibling, 'div');
1166
-                }
1165
+					$sibling = NodeUtility::setNodeTag($sibling, 'div');
1166
+				}
1167 1167
 
1168
-                $import = $articleContent->importNode($sibling, true);
1169
-                $articleContent->appendChild($import);
1168
+				$import = $articleContent->importNode($sibling, true);
1169
+				$articleContent->appendChild($import);
1170 1170
 
1171
-                /*
1171
+				/*
1172 1172
                  * No node shifting needs to be check because when calling getChildren, an array is made with the
1173 1173
                  * children of the parent node, instead of using the DOMElement childNodes function, which, when used
1174 1174
                  * along with appendChild, would shift the nodes position and the current foreach will behave in
1175 1175
                  * unpredictable ways.
1176 1176
                  */
1177
-            }
1178
-        }
1179
-
1180
-        $articleContent = $this->prepArticle($articleContent);
1181
-
1182
-        if ($hasContent) {
1183
-            // Find out text direction from ancestors of final top candidate.
1184
-            $ancestors = array_merge([$parentOfTopCandidate, $topCandidate], $parentOfTopCandidate->getNodeAncestors());
1185
-            foreach ($ancestors as $ancestor) {
1186
-                $articleDir = $ancestor->getAttribute('dir');
1187
-                if ($articleDir) {
1188
-                    $this->setDirection($articleDir);
1189
-                    $this->logger->debug(sprintf('[Rating] Found article direction: %s', $articleDir));
1190
-                    break;
1191
-                }
1192
-            }
1193
-
1194
-            return $articleContent;
1195
-        } else {
1196
-            return false;
1197
-        }
1198
-    }
1199
-
1200
-    /**
1201
-     * Cleans up the final article.
1202
-     *
1203
-     * @param DOMDocument $article
1204
-     *
1205
-     * @return DOMDocument
1206
-     */
1207
-    public function prepArticle(DOMDocument $article)
1208
-    {
1209
-        $this->logger->info('[PrepArticle] Preparing final article...');
1210
-
1211
-        $this->_cleanStyles($article);
1212
-        $this->_clean($article, 'style');
1213
-
1214
-        // Check for data tables before we continue, to avoid removing items in
1215
-        // those tables, which will often be isolated even though they're
1216
-        // visually linked to other content-ful elements (text, images, etc.).
1217
-        $this->_markDataTables($article);
1218
-
1219
-        // Clean out junk from the article content
1220
-        $this->_cleanConditionally($article, 'form');
1221
-        $this->_cleanConditionally($article, 'fieldset');
1222
-        $this->_clean($article, 'object');
1223
-        $this->_clean($article, 'embed');
1224
-        $this->_clean($article, 'h1');
1225
-        $this->_clean($article, 'footer');
1226
-        $this->_clean($article, 'link');
1227
-        $this->_clean($article, 'aside');
1228
-
1229
-        // Clean out elements have "share" in their id/class combinations from final top candidates,
1230
-        // which means we don't remove the top candidates even they have "share".
1231
-        foreach ($article->childNodes as $child) {
1232
-            $this->_cleanMatchedNodes($child, '/share/i');
1233
-        }
1234
-
1235
-        /*
1177
+			}
1178
+		}
1179
+
1180
+		$articleContent = $this->prepArticle($articleContent);
1181
+
1182
+		if ($hasContent) {
1183
+			// Find out text direction from ancestors of final top candidate.
1184
+			$ancestors = array_merge([$parentOfTopCandidate, $topCandidate], $parentOfTopCandidate->getNodeAncestors());
1185
+			foreach ($ancestors as $ancestor) {
1186
+				$articleDir = $ancestor->getAttribute('dir');
1187
+				if ($articleDir) {
1188
+					$this->setDirection($articleDir);
1189
+					$this->logger->debug(sprintf('[Rating] Found article direction: %s', $articleDir));
1190
+					break;
1191
+				}
1192
+			}
1193
+
1194
+			return $articleContent;
1195
+		} else {
1196
+			return false;
1197
+		}
1198
+	}
1199
+
1200
+	/**
1201
+	 * Cleans up the final article.
1202
+	 *
1203
+	 * @param DOMDocument $article
1204
+	 *
1205
+	 * @return DOMDocument
1206
+	 */
1207
+	public function prepArticle(DOMDocument $article)
1208
+	{
1209
+		$this->logger->info('[PrepArticle] Preparing final article...');
1210
+
1211
+		$this->_cleanStyles($article);
1212
+		$this->_clean($article, 'style');
1213
+
1214
+		// Check for data tables before we continue, to avoid removing items in
1215
+		// those tables, which will often be isolated even though they're
1216
+		// visually linked to other content-ful elements (text, images, etc.).
1217
+		$this->_markDataTables($article);
1218
+
1219
+		// Clean out junk from the article content
1220
+		$this->_cleanConditionally($article, 'form');
1221
+		$this->_cleanConditionally($article, 'fieldset');
1222
+		$this->_clean($article, 'object');
1223
+		$this->_clean($article, 'embed');
1224
+		$this->_clean($article, 'h1');
1225
+		$this->_clean($article, 'footer');
1226
+		$this->_clean($article, 'link');
1227
+		$this->_clean($article, 'aside');
1228
+
1229
+		// Clean out elements have "share" in their id/class combinations from final top candidates,
1230
+		// which means we don't remove the top candidates even they have "share".
1231
+		foreach ($article->childNodes as $child) {
1232
+			$this->_cleanMatchedNodes($child, '/share/i');
1233
+		}
1234
+
1235
+		/*
1236 1236
          * If there is only one h2 and its text content substantially equals article title,
1237 1237
          * they are probably using it as a header and not a subheader,
1238 1238
          * so remove it since we already extract the title separately.
1239 1239
          */
1240
-        $h2 = $article->getElementsByTagName('h2');
1241
-        if ($h2->length === 1) {
1242
-            $lengthSimilarRate = (mb_strlen($h2->item(0)->textContent) - mb_strlen($this->getTitle())) / max(mb_strlen($this->getTitle()), 1);
1243
-
1244
-            if (abs($lengthSimilarRate) < 0.5) {
1245
-                if ($lengthSimilarRate > 0) {
1246
-                    $titlesMatch = strpos($h2->item(0)->textContent, $this->getTitle()) !== false;
1247
-                } else {
1248
-                    $titlesMatch = strpos($this->getTitle(), $h2->item(0)->textContent) !== false;
1249
-                }
1250
-                if ($titlesMatch) {
1251
-                    $this->logger->info('[PrepArticle] Found title repeated in an H2 node, removing...');
1252
-                    $this->_clean($article, 'h2');
1253
-                }
1254
-            }
1255
-        }
1256
-
1257
-        $this->_clean($article, 'iframe');
1258
-        $this->_clean($article, 'input');
1259
-        $this->_clean($article, 'textarea');
1260
-        $this->_clean($article, 'select');
1261
-        $this->_clean($article, 'button');
1262
-        $this->_cleanHeaders($article);
1263
-
1264
-        // Do these last as the previous stuff may have removed junk
1265
-        // that will affect these
1266
-        $this->_cleanConditionally($article, 'table');
1267
-        $this->_cleanConditionally($article, 'ul');
1268
-        $this->_cleanConditionally($article, 'div');
1269
-
1270
-        $this->_cleanExtraParagraphs($article);
1271
-
1272
-        foreach (iterator_to_array($article->getElementsByTagName('br')) as $br) {
1273
-            $next = $br->nextSibling;
1274
-            if ($next && $next->nodeName === 'p') {
1275
-                $this->logger->debug('[PrepArticle] Removing br node next to a p node.');
1276
-                $br->parentNode->removeChild($br);
1277
-            }
1278
-        }
1279
-
1280
-        // Remove single-cell tables
1281
-        foreach ($article->shiftingAwareGetElementsByTagName('table') as $table) {
1282
-            /** @var DOMNode $table */
1283
-            $tbody = $table->hasSingleTagInsideElement('tbody') ? $table->getFirstElementChild() : $table;
1284
-            if ($tbody->hasSingleTagInsideElement('tr')) {
1285
-                $row = $tbody->getFirstElementChild();
1286
-                if ($row->hasSingleTagInsideElement('td')) {
1287
-                    $cell = $row->getFirstElementChild();
1288
-                    $cell = NodeUtility::setNodeTag($cell, (array_reduce(iterator_to_array($cell->childNodes), function ($carry, $node) {
1289
-                        return $node->isPhrasingContent() && $carry;
1290
-                    }, true)) ? 'p' : 'div');
1291
-                    $table->parentNode->replaceChild($cell, $table);
1292
-                }
1293
-            }
1294
-        }
1295
-
1296
-        return $article;
1297
-    }
1298
-
1299
-    /**
1300
-     * Look for 'data' (as opposed to 'layout') tables, for which we use
1301
-     * similar checks as
1302
-     * https://dxr.mozilla.org/mozilla-central/rev/71224049c0b52ab190564d3ea0eab089a159a4cf/accessible/html/HTMLTableAccessible.cpp#920.
1303
-     *
1304
-     * @param DOMDocument $article
1305
-     *
1306
-     * @return void
1307
-     */
1308
-    public function _markDataTables(DOMDocument $article)
1309
-    {
1310
-        $tables = $article->getElementsByTagName('table');
1311
-        foreach ($tables as $table) {
1312
-            /** @var DOMElement $table */
1313
-            $role = $table->getAttribute('role');
1314
-            if ($role === 'presentation') {
1315
-                $table->setReadabilityDataTable(false);
1316
-                continue;
1317
-            }
1318
-            $datatable = $table->getAttribute('datatable');
1319
-            if ($datatable == '0') {
1320
-                $table->setReadabilityDataTable(false);
1321
-                continue;
1322
-            }
1323
-            $summary = $table->getAttribute('summary');
1324
-            if ($summary) {
1325
-                $table->setReadabilityDataTable(true);
1326
-                continue;
1327
-            }
1328
-
1329
-            $caption = $table->getElementsByTagName('caption');
1330
-            if ($caption->length > 0 && $caption->item(0)->childNodes->length > 0) {
1331
-                $table->setReadabilityDataTable(true);
1332
-                continue;
1333
-            }
1334
-
1335
-            // If the table has a descendant with any of these tags, consider a data table:
1336
-            foreach (['col', 'colgroup', 'tfoot', 'thead', 'th'] as $dataTableDescendants) {
1337
-                if ($table->getElementsByTagName($dataTableDescendants)->length > 0) {
1338
-                    $table->setReadabilityDataTable(true);
1339
-                    continue 2;
1340
-                }
1341
-            }
1342
-
1343
-            // Nested tables indicate a layout table:
1344
-            if ($table->getElementsByTagName('table')->length > 0) {
1345
-                $table->setReadabilityDataTable(false);
1346
-                continue;
1347
-            }
1348
-
1349
-            $sizeInfo = $table->getRowAndColumnCount();
1350
-            if ($sizeInfo['rows'] >= 10 || $sizeInfo['columns'] > 4) {
1351
-                $table->setReadabilityDataTable(true);
1352
-                continue;
1353
-            }
1354
-            // Now just go by size entirely:
1355
-            $table->setReadabilityDataTable($sizeInfo['rows'] * $sizeInfo['columns'] > 10);
1356
-        }
1357
-    }
1358
-
1359
-    /**
1360
-     * Remove the style attribute on every e and under.
1361
-     *
1362
-     * @param $node DOMDocument|DOMNode
1363
-     **/
1364
-    public function _cleanStyles($node)
1365
-    {
1366
-        if (property_exists($node, 'tagName') && $node->tagName === 'svg') {
1367
-            return;
1368
-        }
1369
-
1370
-        // Do not bother if there's no method to remove an attribute
1371
-        if (method_exists($node, 'removeAttribute')) {
1372
-            $presentational_attributes = ['align', 'background', 'bgcolor', 'border', 'cellpadding', 'cellspacing', 'frame', 'hspace', 'rules', 'style', 'valign', 'vspace'];
1373
-            // Remove `style` and deprecated presentational attributes
1374
-            foreach ($presentational_attributes as $presentational_attribute) {
1375
-                $node->removeAttribute($presentational_attribute);
1376
-            }
1377
-
1378
-            $deprecated_size_attribute_elems = ['table', 'th', 'td', 'hr', 'pre'];
1379
-            if (property_exists($node, 'tagName') && in_array($node->tagName, $deprecated_size_attribute_elems)) {
1380
-                $node->removeAttribute('width');
1381
-                $node->removeAttribute('height');
1382
-            }
1383
-        }
1384
-
1385
-        $cur = $node->firstChild;
1386
-        while ($cur !== null) {
1387
-            $this->_cleanStyles($cur);
1388
-            $cur = $cur->nextSibling;
1389
-        }
1390
-    }
1391
-
1392
-    /**
1393
-     * Clean out elements whose id/class combinations match specific string.
1394
-     *
1395
-     * @param $node DOMElement Node to clean
1396
-     * @param $regex string Match id/class combination.
1397
-     *
1398
-     * @return void
1399
-     **/
1400
-    public function _cleanMatchedNodes($node, $regex)
1401
-    {
1402
-        $endOfSearchMarkerNode = NodeUtility::getNextNode($node, true);
1403
-        $next = NodeUtility::getNextNode($node);
1404
-        while ($next && $next !== $endOfSearchMarkerNode) {
1405
-            if (preg_match($regex, sprintf('%s %s', $next->getAttribute('class'), $next->getAttribute('id')))) {
1406
-                $this->logger->debug(sprintf('Removing matched node with regex: \'%s\', node class was: \'%s\', id: \'%s\'', $regex, $next->getAttribute('class'), $next->getAttribute('id')));
1407
-                $next = NodeUtility::removeAndGetNext($next);
1408
-            } else {
1409
-                $next = NodeUtility::getNextNode($next);
1410
-            }
1411
-        }
1412
-    }
1413
-
1414
-    /**
1415
-     * @param DOMDocument $article
1416
-     *
1417
-     * @return void
1418
-     */
1419
-    public function _cleanExtraParagraphs(DOMDocument $article)
1420
-    {
1421
-        $paragraphs = $article->getElementsByTagName('p');
1422
-        $length = $paragraphs->length;
1423
-
1424
-        for ($i = 0; $i < $length; $i++) {
1425
-            $paragraph = $paragraphs->item($length - 1 - $i);
1426
-
1427
-            $imgCount = $paragraph->getElementsByTagName('img')->length;
1428
-            $embedCount = $paragraph->getElementsByTagName('embed')->length;
1429
-            $objectCount = $paragraph->getElementsByTagName('object')->length;
1430
-            // At this point, nasty iframes have been removed, only remain embedded video ones.
1431
-            $iframeCount = $paragraph->getElementsByTagName('iframe')->length;
1432
-            $totalCount = $imgCount + $embedCount + $objectCount + $iframeCount;
1433
-
1434
-            if ($totalCount === 0 && !preg_replace(NodeUtility::$regexps['onlyWhitespace'], '', $paragraph->textContent)) {
1435
-                $this->logger->debug(sprintf('[PrepArticle] Removing extra paragraph. Text content was: \'%s\'', substr($paragraph->textContent, 0, 128)));
1436
-                $paragraph->parentNode->removeChild($paragraph);
1437
-            }
1438
-        }
1439
-    }
1440
-
1441
-    /**
1442
-     * @param DOMDocument $article
1443
-     * @param string $tag Tag to clean conditionally
1444
-     *
1445
-     * @return void
1446
-     */
1447
-    public function _cleanConditionally(DOMDocument $article, $tag)
1448
-    {
1449
-        if (!$this->configuration->getCleanConditionally()) {
1450
-            return;
1451
-        }
1452
-
1453
-        $isList = in_array($tag, ['ul', 'ol']);
1454
-
1455
-        /*
1240
+		$h2 = $article->getElementsByTagName('h2');
1241
+		if ($h2->length === 1) {
1242
+			$lengthSimilarRate = (mb_strlen($h2->item(0)->textContent) - mb_strlen($this->getTitle())) / max(mb_strlen($this->getTitle()), 1);
1243
+
1244
+			if (abs($lengthSimilarRate) < 0.5) {
1245
+				if ($lengthSimilarRate > 0) {
1246
+					$titlesMatch = strpos($h2->item(0)->textContent, $this->getTitle()) !== false;
1247
+				} else {
1248
+					$titlesMatch = strpos($this->getTitle(), $h2->item(0)->textContent) !== false;
1249
+				}
1250
+				if ($titlesMatch) {
1251
+					$this->logger->info('[PrepArticle] Found title repeated in an H2 node, removing...');
1252
+					$this->_clean($article, 'h2');
1253
+				}
1254
+			}
1255
+		}
1256
+
1257
+		$this->_clean($article, 'iframe');
1258
+		$this->_clean($article, 'input');
1259
+		$this->_clean($article, 'textarea');
1260
+		$this->_clean($article, 'select');
1261
+		$this->_clean($article, 'button');
1262
+		$this->_cleanHeaders($article);
1263
+
1264
+		// Do these last as the previous stuff may have removed junk
1265
+		// that will affect these
1266
+		$this->_cleanConditionally($article, 'table');
1267
+		$this->_cleanConditionally($article, 'ul');
1268
+		$this->_cleanConditionally($article, 'div');
1269
+
1270
+		$this->_cleanExtraParagraphs($article);
1271
+
1272
+		foreach (iterator_to_array($article->getElementsByTagName('br')) as $br) {
1273
+			$next = $br->nextSibling;
1274
+			if ($next && $next->nodeName === 'p') {
1275
+				$this->logger->debug('[PrepArticle] Removing br node next to a p node.');
1276
+				$br->parentNode->removeChild($br);
1277
+			}
1278
+		}
1279
+
1280
+		// Remove single-cell tables
1281
+		foreach ($article->shiftingAwareGetElementsByTagName('table') as $table) {
1282
+			/** @var DOMNode $table */
1283
+			$tbody = $table->hasSingleTagInsideElement('tbody') ? $table->getFirstElementChild() : $table;
1284
+			if ($tbody->hasSingleTagInsideElement('tr')) {
1285
+				$row = $tbody->getFirstElementChild();
1286
+				if ($row->hasSingleTagInsideElement('td')) {
1287
+					$cell = $row->getFirstElementChild();
1288
+					$cell = NodeUtility::setNodeTag($cell, (array_reduce(iterator_to_array($cell->childNodes), function ($carry, $node) {
1289
+						return $node->isPhrasingContent() && $carry;
1290
+					}, true)) ? 'p' : 'div');
1291
+					$table->parentNode->replaceChild($cell, $table);
1292
+				}
1293
+			}
1294
+		}
1295
+
1296
+		return $article;
1297
+	}
1298
+
1299
+	/**
1300
+	 * Look for 'data' (as opposed to 'layout') tables, for which we use
1301
+	 * similar checks as
1302
+	 * https://dxr.mozilla.org/mozilla-central/rev/71224049c0b52ab190564d3ea0eab089a159a4cf/accessible/html/HTMLTableAccessible.cpp#920.
1303
+	 *
1304
+	 * @param DOMDocument $article
1305
+	 *
1306
+	 * @return void
1307
+	 */
1308
+	public function _markDataTables(DOMDocument $article)
1309
+	{
1310
+		$tables = $article->getElementsByTagName('table');
1311
+		foreach ($tables as $table) {
1312
+			/** @var DOMElement $table */
1313
+			$role = $table->getAttribute('role');
1314
+			if ($role === 'presentation') {
1315
+				$table->setReadabilityDataTable(false);
1316
+				continue;
1317
+			}
1318
+			$datatable = $table->getAttribute('datatable');
1319
+			if ($datatable == '0') {
1320
+				$table->setReadabilityDataTable(false);
1321
+				continue;
1322
+			}
1323
+			$summary = $table->getAttribute('summary');
1324
+			if ($summary) {
1325
+				$table->setReadabilityDataTable(true);
1326
+				continue;
1327
+			}
1328
+
1329
+			$caption = $table->getElementsByTagName('caption');
1330
+			if ($caption->length > 0 && $caption->item(0)->childNodes->length > 0) {
1331
+				$table->setReadabilityDataTable(true);
1332
+				continue;
1333
+			}
1334
+
1335
+			// If the table has a descendant with any of these tags, consider a data table:
1336
+			foreach (['col', 'colgroup', 'tfoot', 'thead', 'th'] as $dataTableDescendants) {
1337
+				if ($table->getElementsByTagName($dataTableDescendants)->length > 0) {
1338
+					$table->setReadabilityDataTable(true);
1339
+					continue 2;
1340
+				}
1341
+			}
1342
+
1343
+			// Nested tables indicate a layout table:
1344
+			if ($table->getElementsByTagName('table')->length > 0) {
1345
+				$table->setReadabilityDataTable(false);
1346
+				continue;
1347
+			}
1348
+
1349
+			$sizeInfo = $table->getRowAndColumnCount();
1350
+			if ($sizeInfo['rows'] >= 10 || $sizeInfo['columns'] > 4) {
1351
+				$table->setReadabilityDataTable(true);
1352
+				continue;
1353
+			}
1354
+			// Now just go by size entirely:
1355
+			$table->setReadabilityDataTable($sizeInfo['rows'] * $sizeInfo['columns'] > 10);
1356
+		}
1357
+	}
1358
+
1359
+	/**
1360
+	 * Remove the style attribute on every e and under.
1361
+	 *
1362
+	 * @param $node DOMDocument|DOMNode
1363
+	 **/
1364
+	public function _cleanStyles($node)
1365
+	{
1366
+		if (property_exists($node, 'tagName') && $node->tagName === 'svg') {
1367
+			return;
1368
+		}
1369
+
1370
+		// Do not bother if there's no method to remove an attribute
1371
+		if (method_exists($node, 'removeAttribute')) {
1372
+			$presentational_attributes = ['align', 'background', 'bgcolor', 'border', 'cellpadding', 'cellspacing', 'frame', 'hspace', 'rules', 'style', 'valign', 'vspace'];
1373
+			// Remove `style` and deprecated presentational attributes
1374
+			foreach ($presentational_attributes as $presentational_attribute) {
1375
+				$node->removeAttribute($presentational_attribute);
1376
+			}
1377
+
1378
+			$deprecated_size_attribute_elems = ['table', 'th', 'td', 'hr', 'pre'];
1379
+			if (property_exists($node, 'tagName') && in_array($node->tagName, $deprecated_size_attribute_elems)) {
1380
+				$node->removeAttribute('width');
1381
+				$node->removeAttribute('height');
1382
+			}
1383
+		}
1384
+
1385
+		$cur = $node->firstChild;
1386
+		while ($cur !== null) {
1387
+			$this->_cleanStyles($cur);
1388
+			$cur = $cur->nextSibling;
1389
+		}
1390
+	}
1391
+
1392
+	/**
1393
+	 * Clean out elements whose id/class combinations match specific string.
1394
+	 *
1395
+	 * @param $node DOMElement Node to clean
1396
+	 * @param $regex string Match id/class combination.
1397
+	 *
1398
+	 * @return void
1399
+	 **/
1400
+	public function _cleanMatchedNodes($node, $regex)
1401
+	{
1402
+		$endOfSearchMarkerNode = NodeUtility::getNextNode($node, true);
1403
+		$next = NodeUtility::getNextNode($node);
1404
+		while ($next && $next !== $endOfSearchMarkerNode) {
1405
+			if (preg_match($regex, sprintf('%s %s', $next->getAttribute('class'), $next->getAttribute('id')))) {
1406
+				$this->logger->debug(sprintf('Removing matched node with regex: \'%s\', node class was: \'%s\', id: \'%s\'', $regex, $next->getAttribute('class'), $next->getAttribute('id')));
1407
+				$next = NodeUtility::removeAndGetNext($next);
1408
+			} else {
1409
+				$next = NodeUtility::getNextNode($next);
1410
+			}
1411
+		}
1412
+	}
1413
+
1414
+	/**
1415
+	 * @param DOMDocument $article
1416
+	 *
1417
+	 * @return void
1418
+	 */
1419
+	public function _cleanExtraParagraphs(DOMDocument $article)
1420
+	{
1421
+		$paragraphs = $article->getElementsByTagName('p');
1422
+		$length = $paragraphs->length;
1423
+
1424
+		for ($i = 0; $i < $length; $i++) {
1425
+			$paragraph = $paragraphs->item($length - 1 - $i);
1426
+
1427
+			$imgCount = $paragraph->getElementsByTagName('img')->length;
1428
+			$embedCount = $paragraph->getElementsByTagName('embed')->length;
1429
+			$objectCount = $paragraph->getElementsByTagName('object')->length;
1430
+			// At this point, nasty iframes have been removed, only remain embedded video ones.
1431
+			$iframeCount = $paragraph->getElementsByTagName('iframe')->length;
1432
+			$totalCount = $imgCount + $embedCount + $objectCount + $iframeCount;
1433
+
1434
+			if ($totalCount === 0 && !preg_replace(NodeUtility::$regexps['onlyWhitespace'], '', $paragraph->textContent)) {
1435
+				$this->logger->debug(sprintf('[PrepArticle] Removing extra paragraph. Text content was: \'%s\'', substr($paragraph->textContent, 0, 128)));
1436
+				$paragraph->parentNode->removeChild($paragraph);
1437
+			}
1438
+		}
1439
+	}
1440
+
1441
+	/**
1442
+	 * @param DOMDocument $article
1443
+	 * @param string $tag Tag to clean conditionally
1444
+	 *
1445
+	 * @return void
1446
+	 */
1447
+	public function _cleanConditionally(DOMDocument $article, $tag)
1448
+	{
1449
+		if (!$this->configuration->getCleanConditionally()) {
1450
+			return;
1451
+		}
1452
+
1453
+		$isList = in_array($tag, ['ul', 'ol']);
1454
+
1455
+		/*
1456 1456
          * Gather counts for other typical elements embedded within.
1457 1457
          * Traverse backwards so we can remove nodes at the same time
1458 1458
          * without effecting the traversal.
1459 1459
          */
1460 1460
 
1461
-        $DOMNodeList = $article->getElementsByTagName($tag);
1462
-        $length = $DOMNodeList->length;
1463
-        for ($i = 0; $i < $length; $i++) {
1464
-            /** @var $node DOMElement */
1465
-            $node = $DOMNodeList->item($length - 1 - $i);
1466
-
1467
-            // First check if we're in a data table, in which case don't remove us.
1468
-            if ($node->hasAncestorTag('table', -1, function ($node) {
1469
-                return $node->isReadabilityDataTable();
1470
-            })) {
1471
-                continue;
1472
-            }
1473
-
1474
-            $weight = 0;
1475
-            if ($this->configuration->getWeightClasses()) {
1476
-                $weight = $node->getClassWeight();
1477
-            }
1478
-
1479
-            if ($weight < 0) {
1480
-                $this->logger->debug(sprintf('[PrepArticle] Removing tag \'%s\' with 0 or less weight', $tag));
1481
-
1482
-                NodeUtility::removeNode($node);
1483
-                continue;
1484
-            }
1485
-
1486
-            if (substr_count($node->getTextContent(), ',') < 10) {
1487
-                /*
1461
+		$DOMNodeList = $article->getElementsByTagName($tag);
1462
+		$length = $DOMNodeList->length;
1463
+		for ($i = 0; $i < $length; $i++) {
1464
+			/** @var $node DOMElement */
1465
+			$node = $DOMNodeList->item($length - 1 - $i);
1466
+
1467
+			// First check if we're in a data table, in which case don't remove us.
1468
+			if ($node->hasAncestorTag('table', -1, function ($node) {
1469
+				return $node->isReadabilityDataTable();
1470
+			})) {
1471
+				continue;
1472
+			}
1473
+
1474
+			$weight = 0;
1475
+			if ($this->configuration->getWeightClasses()) {
1476
+				$weight = $node->getClassWeight();
1477
+			}
1478
+
1479
+			if ($weight < 0) {
1480
+				$this->logger->debug(sprintf('[PrepArticle] Removing tag \'%s\' with 0 or less weight', $tag));
1481
+
1482
+				NodeUtility::removeNode($node);
1483
+				continue;
1484
+			}
1485
+
1486
+			if (substr_count($node->getTextContent(), ',') < 10) {
1487
+				/*
1488 1488
                  * If there are not very many commas, and the number of
1489 1489
                  * non-paragraph elements is more than paragraphs or other
1490 1490
                  * ominous signs, remove the element.
1491 1491
                  */
1492 1492
 
1493
-                $p = $node->getElementsByTagName('p')->length;
1494
-                $img = $node->getElementsByTagName('img')->length;
1495
-                $li = $node->getElementsByTagName('li')->length - 100;
1496
-                $input = $node->getElementsByTagName('input')->length;
1497
-
1498
-                $embedCount = 0;
1499
-                $embeds = $node->getElementsByTagName('embed');
1500
-
1501
-                foreach ($embeds as $embedNode) {
1502
-                    if (preg_match(NodeUtility::$regexps['videos'], $embedNode->C14N())) {
1503
-                        $embedCount++;
1504
-                    }
1505
-                }
1506
-
1507
-                $linkDensity = $node->getLinkDensity();
1508
-                $contentLength = mb_strlen($node->getTextContent(true));
1509
-
1510
-                $haveToRemove =
1511
-                    ($img > 1 && $p / $img < 0.5 && !$node->hasAncestorTag('figure')) ||
1512
-                    (!$isList && $li > $p) ||
1513
-                    ($input > floor($p / 3)) ||
1514
-                    (!$isList && $contentLength < 25 && ($img === 0 || $img > 2) && !$node->hasAncestorTag('figure')) ||
1515
-                    (!$isList && $weight < 25 && $linkDensity > 0.2) ||
1516
-                    ($weight >= 25 && $linkDensity > 0.5) ||
1517
-                    (($embedCount === 1 && $contentLength < 75) || $embedCount > 1);
1518
-
1519
-                if ($haveToRemove) {
1520
-                    $this->logger->debug(sprintf('[PrepArticle] Removing tag \'%s\'.', $tag));
1521
-
1522
-                    NodeUtility::removeNode($node);
1523
-                }
1524
-            }
1525
-        }
1526
-    }
1527
-
1528
-    /**
1529
-     * Clean a node of all elements of type "tag".
1530
-     * (Unless it's a youtube/vimeo video. People love movies.).
1531
-     *
1532
-     * @param $article DOMDocument
1533
-     * @param $tag string tag to clean
1534
-     *
1535
-     * @return void
1536
-     **/
1537
-    public function _clean(DOMDocument $article, $tag)
1538
-    {
1539
-        $isEmbed = in_array($tag, ['object', 'embed', 'iframe']);
1540
-
1541
-        $DOMNodeList = $article->getElementsByTagName($tag);
1542
-        $length = $DOMNodeList->length;
1543
-        for ($i = 0; $i < $length; $i++) {
1544
-            $item = $DOMNodeList->item($length - 1 - $i);
1545
-
1546
-            // Allow youtube and vimeo videos through as people usually want to see those.
1547
-            if ($isEmbed) {
1548
-                $attributeValues = [];
1549
-                foreach ($item->attributes as $value) {
1550
-                    $attributeValues[] = $value->nodeValue;
1551
-                }
1552
-                $attributeValues = implode('|', $attributeValues);
1553
-
1554
-                // First, check the elements attributes to see if any of them contain youtube or vimeo
1555
-                if (preg_match(NodeUtility::$regexps['videos'], $attributeValues)) {
1556
-                    continue;
1557
-                }
1558
-
1559
-                // Then check the elements inside this element for the same.
1560
-                if (preg_match(NodeUtility::$regexps['videos'], $item->C14N())) {
1561
-                    continue;
1562
-                }
1563
-            }
1564
-            $this->logger->debug(sprintf('[PrepArticle] Removing node \'%s\'.', $item->tagName));
1565
-
1566
-            NodeUtility::removeNode($item);
1567
-        }
1568
-    }
1569
-
1570
-    /**
1571
-     * Clean out spurious headers from an Element. Checks things like classnames and link density.
1572
-     *
1573
-     * @param DOMDocument $article
1574
-     *
1575
-     * @return void
1576
-     **/
1577
-    public function _cleanHeaders(DOMDocument $article)
1578
-    {
1579
-        for ($headerIndex = 1; $headerIndex < 3; $headerIndex++) {
1580
-            $headers = $article->getElementsByTagName('h' . $headerIndex);
1581
-            /** @var $header DOMElement */
1582
-            foreach ($headers as $header) {
1583
-                $weight = 0;
1584
-                if ($this->configuration->getWeightClasses()) {
1585
-                    $weight = $header->getClassWeight();
1586
-                }
1587
-
1588
-                if ($weight < 0) {
1589
-                    $this->logger->debug(sprintf('[PrepArticle] Removing H node with 0 or less weight. Content was: \'%s\'', substr($header->nodeValue, 0, 128)));
1590
-
1591
-                    NodeUtility::removeNode($header);
1592
-                }
1593
-            }
1594
-        }
1595
-    }
1596
-
1597
-    /**
1598
-     * Removes the class="" attribute from every element in the given
1599
-     * subtree.
1600
-     *
1601
-     * Readability.js has a special filter to avoid cleaning the classes that the algorithm adds. We don't add classes
1602
-     * here so no need to filter those.
1603
-     *
1604
-     * @param DOMDocument|DOMNode $node
1605
-     *
1606
-     * @return void
1607
-     **/
1608
-    public function _cleanClasses($node)
1609
-    {
1610
-        if ($node->getAttribute('class') !== '') {
1611
-            $node->removeAttribute('class');
1612
-        }
1613
-
1614
-        for ($node = $node->getFirstElementChild(); $node !== null; $node = $node->nextSibling) {
1615
-            $this->_cleanClasses($node);
1616
-        }
1617
-    }
1618
-
1619
-    /**
1620
-     * @param DOMDocument $article
1621
-     *
1622
-     * @return DOMDocument
1623
-     */
1624
-    public function postProcessContent(DOMDocument $article)
1625
-    {
1626
-        $this->logger->info('[PostProcess] PostProcessing content...');
1627
-
1628
-        // Readability cannot open relative uris so we convert them to absolute uris.
1629
-        if ($this->configuration->getFixRelativeURLs()) {
1630
-            foreach (iterator_to_array($article->getElementsByTagName('a')) as $link) {
1631
-                /** @var DOMElement $link */
1632
-                $href = $link->getAttribute('href');
1633
-                if ($href) {
1634
-                    // Replace links with javascript: URIs with text content, since
1635
-                    // they won't work after scripts have been removed from the page.
1636
-                    if (strpos($href, 'javascript:') === 0) {
1637
-                        $this->logger->debug(sprintf('[PostProcess] Removing \'javascript:\' link. Content is: \'%s\'', substr($link->textContent, 0, 128)));
1638
-
1639
-                        $text = $article->createTextNode($link->textContent);
1640
-                        $link->parentNode->replaceChild($text, $link);
1641
-                    } else {
1642
-                        $this->logger->debug(sprintf('[PostProcess] Converting link to absolute URI: \'%s\'', substr($href, 0, 128)));
1643
-
1644
-                        $link->setAttribute('href', $this->toAbsoluteURI($href));
1645
-                    }
1646
-                }
1647
-            }
1648
-
1649
-            foreach ($article->getElementsByTagName('img') as $img) {
1650
-                /** @var DOMElement $img */
1651
-                /*
1493
+				$p = $node->getElementsByTagName('p')->length;
1494
+				$img = $node->getElementsByTagName('img')->length;
1495
+				$li = $node->getElementsByTagName('li')->length - 100;
1496
+				$input = $node->getElementsByTagName('input')->length;
1497
+
1498
+				$embedCount = 0;
1499
+				$embeds = $node->getElementsByTagName('embed');
1500
+
1501
+				foreach ($embeds as $embedNode) {
1502
+					if (preg_match(NodeUtility::$regexps['videos'], $embedNode->C14N())) {
1503
+						$embedCount++;
1504
+					}
1505
+				}
1506
+
1507
+				$linkDensity = $node->getLinkDensity();
1508
+				$contentLength = mb_strlen($node->getTextContent(true));
1509
+
1510
+				$haveToRemove =
1511
+					($img > 1 && $p / $img < 0.5 && !$node->hasAncestorTag('figure')) ||
1512
+					(!$isList && $li > $p) ||
1513
+					($input > floor($p / 3)) ||
1514
+					(!$isList && $contentLength < 25 && ($img === 0 || $img > 2) && !$node->hasAncestorTag('figure')) ||
1515
+					(!$isList && $weight < 25 && $linkDensity > 0.2) ||
1516
+					($weight >= 25 && $linkDensity > 0.5) ||
1517
+					(($embedCount === 1 && $contentLength < 75) || $embedCount > 1);
1518
+
1519
+				if ($haveToRemove) {
1520
+					$this->logger->debug(sprintf('[PrepArticle] Removing tag \'%s\'.', $tag));
1521
+
1522
+					NodeUtility::removeNode($node);
1523
+				}
1524
+			}
1525
+		}
1526
+	}
1527
+
1528
+	/**
1529
+	 * Clean a node of all elements of type "tag".
1530
+	 * (Unless it's a youtube/vimeo video. People love movies.).
1531
+	 *
1532
+	 * @param $article DOMDocument
1533
+	 * @param $tag string tag to clean
1534
+	 *
1535
+	 * @return void
1536
+	 **/
1537
+	public function _clean(DOMDocument $article, $tag)
1538
+	{
1539
+		$isEmbed = in_array($tag, ['object', 'embed', 'iframe']);
1540
+
1541
+		$DOMNodeList = $article->getElementsByTagName($tag);
1542
+		$length = $DOMNodeList->length;
1543
+		for ($i = 0; $i < $length; $i++) {
1544
+			$item = $DOMNodeList->item($length - 1 - $i);
1545
+
1546
+			// Allow youtube and vimeo videos through as people usually want to see those.
1547
+			if ($isEmbed) {
1548
+				$attributeValues = [];
1549
+				foreach ($item->attributes as $value) {
1550
+					$attributeValues[] = $value->nodeValue;
1551
+				}
1552
+				$attributeValues = implode('|', $attributeValues);
1553
+
1554
+				// First, check the elements attributes to see if any of them contain youtube or vimeo
1555
+				if (preg_match(NodeUtility::$regexps['videos'], $attributeValues)) {
1556
+					continue;
1557
+				}
1558
+
1559
+				// Then check the elements inside this element for the same.
1560
+				if (preg_match(NodeUtility::$regexps['videos'], $item->C14N())) {
1561
+					continue;
1562
+				}
1563
+			}
1564
+			$this->logger->debug(sprintf('[PrepArticle] Removing node \'%s\'.', $item->tagName));
1565
+
1566
+			NodeUtility::removeNode($item);
1567
+		}
1568
+	}
1569
+
1570
+	/**
1571
+	 * Clean out spurious headers from an Element. Checks things like classnames and link density.
1572
+	 *
1573
+	 * @param DOMDocument $article
1574
+	 *
1575
+	 * @return void
1576
+	 **/
1577
+	public function _cleanHeaders(DOMDocument $article)
1578
+	{
1579
+		for ($headerIndex = 1; $headerIndex < 3; $headerIndex++) {
1580
+			$headers = $article->getElementsByTagName('h' . $headerIndex);
1581
+			/** @var $header DOMElement */
1582
+			foreach ($headers as $header) {
1583
+				$weight = 0;
1584
+				if ($this->configuration->getWeightClasses()) {
1585
+					$weight = $header->getClassWeight();
1586
+				}
1587
+
1588
+				if ($weight < 0) {
1589
+					$this->logger->debug(sprintf('[PrepArticle] Removing H node with 0 or less weight. Content was: \'%s\'', substr($header->nodeValue, 0, 128)));
1590
+
1591
+					NodeUtility::removeNode($header);
1592
+				}
1593
+			}
1594
+		}
1595
+	}
1596
+
1597
+	/**
1598
+	 * Removes the class="" attribute from every element in the given
1599
+	 * subtree.
1600
+	 *
1601
+	 * Readability.js has a special filter to avoid cleaning the classes that the algorithm adds. We don't add classes
1602
+	 * here so no need to filter those.
1603
+	 *
1604
+	 * @param DOMDocument|DOMNode $node
1605
+	 *
1606
+	 * @return void
1607
+	 **/
1608
+	public function _cleanClasses($node)
1609
+	{
1610
+		if ($node->getAttribute('class') !== '') {
1611
+			$node->removeAttribute('class');
1612
+		}
1613
+
1614
+		for ($node = $node->getFirstElementChild(); $node !== null; $node = $node->nextSibling) {
1615
+			$this->_cleanClasses($node);
1616
+		}
1617
+	}
1618
+
1619
+	/**
1620
+	 * @param DOMDocument $article
1621
+	 *
1622
+	 * @return DOMDocument
1623
+	 */
1624
+	public function postProcessContent(DOMDocument $article)
1625
+	{
1626
+		$this->logger->info('[PostProcess] PostProcessing content...');
1627
+
1628
+		// Readability cannot open relative uris so we convert them to absolute uris.
1629
+		if ($this->configuration->getFixRelativeURLs()) {
1630
+			foreach (iterator_to_array($article->getElementsByTagName('a')) as $link) {
1631
+				/** @var DOMElement $link */
1632
+				$href = $link->getAttribute('href');
1633
+				if ($href) {
1634
+					// Replace links with javascript: URIs with text content, since
1635
+					// they won't work after scripts have been removed from the page.
1636
+					if (strpos($href, 'javascript:') === 0) {
1637
+						$this->logger->debug(sprintf('[PostProcess] Removing \'javascript:\' link. Content is: \'%s\'', substr($link->textContent, 0, 128)));
1638
+
1639
+						$text = $article->createTextNode($link->textContent);
1640
+						$link->parentNode->replaceChild($text, $link);
1641
+					} else {
1642
+						$this->logger->debug(sprintf('[PostProcess] Converting link to absolute URI: \'%s\'', substr($href, 0, 128)));
1643
+
1644
+						$link->setAttribute('href', $this->toAbsoluteURI($href));
1645
+					}
1646
+				}
1647
+			}
1648
+
1649
+			foreach ($article->getElementsByTagName('img') as $img) {
1650
+				/** @var DOMElement $img */
1651
+				/*
1652 1652
                  * Extract all possible sources of img url and select the first one on the list.
1653 1653
                  */
1654
-                $url = [
1655
-                    $img->getAttribute('src'),
1656
-                    $img->getAttribute('data-src'),
1657
-                    $img->getAttribute('data-original'),
1658
-                    $img->getAttribute('data-orig'),
1659
-                    $img->getAttribute('data-url')
1660
-                ];
1661
-
1662
-                $src = array_filter($url);
1663
-                $src = reset($src);
1664
-                if ($src) {
1665
-                    $this->logger->debug(sprintf('[PostProcess] Converting image URL to absolute URI: \'%s\'', substr($src, 0, 128)));
1666
-
1667
-                    $img->setAttribute('src', $this->toAbsoluteURI($src));
1668
-                }
1669
-            }
1670
-        }
1671
-
1672
-        $this->_cleanClasses($article);
1673
-
1674
-        return $article;
1675
-    }
1676
-
1677
-    /**
1678
-     * @return null|string
1679
-     */
1680
-    public function __toString()
1681
-    {
1682
-        return sprintf('<h1>%s</h1>%s', $this->getTitle(), $this->getContent());
1683
-    }
1684
-
1685
-    /**
1686
-     * @return string|null
1687
-     */
1688
-    public function getTitle()
1689
-    {
1690
-        return $this->title;
1691
-    }
1692
-
1693
-    /**
1694
-     * @param string $title
1695
-     */
1696
-    protected function setTitle($title)
1697
-    {
1698
-        $this->title = $title;
1699
-    }
1700
-
1701
-    /**
1702
-     * @return string|null
1703
-     */
1704
-    public function getContent()
1705
-    {
1706
-        return ($this->content instanceof DOMDocument) ? $this->content->C14N() : null;
1707
-    }
1708
-
1709
-    /**
1710
-     * @return DOMDocument|null
1711
-     */
1712
-    public function getDOMDocument()
1713
-    {
1714
-        return $this->content;
1715
-    }
1716
-
1717
-    /**
1718
-     * @param DOMDocument $content
1719
-     */
1720
-    protected function setContent(DOMDocument $content)
1721
-    {
1722
-        $this->content = $content;
1723
-    }
1724
-
1725
-    /**
1726
-     * @return null|string
1727
-     */
1728
-    public function getExcerpt()
1729
-    {
1730
-        return $this->excerpt;
1731
-    }
1732
-
1733
-    /**
1734
-     * @param null|string $excerpt
1735
-     */
1736
-    public function setExcerpt($excerpt)
1737
-    {
1738
-        $this->excerpt = $excerpt;
1739
-    }
1740
-
1741
-    /**
1742
-     * @return string|null
1743
-     */
1744
-    public function getImage()
1745
-    {
1746
-        return $this->image;
1747
-    }
1748
-
1749
-    /**
1750
-     * @param string $image
1751
-     */
1752
-    protected function setImage($image)
1753
-    {
1754
-        $this->image = $image;
1755
-    }
1756
-
1757
-    /**
1758
-     * @return string|null
1759
-     */
1760
-    public function getAuthor()
1761
-    {
1762
-        return $this->author;
1763
-    }
1764
-
1765
-    /**
1766
-     * @param string $author
1767
-     */
1768
-    protected function setAuthor($author)
1769
-    {
1770
-        $this->author = $author;
1771
-    }
1772
-
1773
-    /**
1774
-     * @return string|null
1775
-     */
1776
-    public function getSiteName()
1777
-    {
1778
-        return $this->siteName;
1779
-    }
1780
-
1781
-    /**
1782
-     * @param string $siteName
1783
-     */
1784
-    protected function setSiteName($siteName)
1785
-    {
1786
-        $this->siteName = $siteName;
1787
-    }
1788
-
1789
-    /**
1790
-     * @return null|string
1791
-     */
1792
-    public function getDirection()
1793
-    {
1794
-        return $this->direction;
1795
-    }
1796
-
1797
-    /**
1798
-     * @param null|string $direction
1799
-     */
1800
-    public function setDirection($direction)
1801
-    {
1802
-        $this->direction = $direction;
1803
-    }
1654
+				$url = [
1655
+					$img->getAttribute('src'),
1656
+					$img->getAttribute('data-src'),
1657
+					$img->getAttribute('data-original'),
1658
+					$img->getAttribute('data-orig'),
1659
+					$img->getAttribute('data-url')
1660
+				];
1661
+
1662
+				$src = array_filter($url);
1663
+				$src = reset($src);
1664
+				if ($src) {
1665
+					$this->logger->debug(sprintf('[PostProcess] Converting image URL to absolute URI: \'%s\'', substr($src, 0, 128)));
1666
+
1667
+					$img->setAttribute('src', $this->toAbsoluteURI($src));
1668
+				}
1669
+			}
1670
+		}
1671
+
1672
+		$this->_cleanClasses($article);
1673
+
1674
+		return $article;
1675
+	}
1676
+
1677
+	/**
1678
+	 * @return null|string
1679
+	 */
1680
+	public function __toString()
1681
+	{
1682
+		return sprintf('<h1>%s</h1>%s', $this->getTitle(), $this->getContent());
1683
+	}
1684
+
1685
+	/**
1686
+	 * @return string|null
1687
+	 */
1688
+	public function getTitle()
1689
+	{
1690
+		return $this->title;
1691
+	}
1692
+
1693
+	/**
1694
+	 * @param string $title
1695
+	 */
1696
+	protected function setTitle($title)
1697
+	{
1698
+		$this->title = $title;
1699
+	}
1700
+
1701
+	/**
1702
+	 * @return string|null
1703
+	 */
1704
+	public function getContent()
1705
+	{
1706
+		return ($this->content instanceof DOMDocument) ? $this->content->C14N() : null;
1707
+	}
1708
+
1709
+	/**
1710
+	 * @return DOMDocument|null
1711
+	 */
1712
+	public function getDOMDocument()
1713
+	{
1714
+		return $this->content;
1715
+	}
1716
+
1717
+	/**
1718
+	 * @param DOMDocument $content
1719
+	 */
1720
+	protected function setContent(DOMDocument $content)
1721
+	{
1722
+		$this->content = $content;
1723
+	}
1724
+
1725
+	/**
1726
+	 * @return null|string
1727
+	 */
1728
+	public function getExcerpt()
1729
+	{
1730
+		return $this->excerpt;
1731
+	}
1732
+
1733
+	/**
1734
+	 * @param null|string $excerpt
1735
+	 */
1736
+	public function setExcerpt($excerpt)
1737
+	{
1738
+		$this->excerpt = $excerpt;
1739
+	}
1740
+
1741
+	/**
1742
+	 * @return string|null
1743
+	 */
1744
+	public function getImage()
1745
+	{
1746
+		return $this->image;
1747
+	}
1748
+
1749
+	/**
1750
+	 * @param string $image
1751
+	 */
1752
+	protected function setImage($image)
1753
+	{
1754
+		$this->image = $image;
1755
+	}
1756
+
1757
+	/**
1758
+	 * @return string|null
1759
+	 */
1760
+	public function getAuthor()
1761
+	{
1762
+		return $this->author;
1763
+	}
1764
+
1765
+	/**
1766
+	 * @param string $author
1767
+	 */
1768
+	protected function setAuthor($author)
1769
+	{
1770
+		$this->author = $author;
1771
+	}
1772
+
1773
+	/**
1774
+	 * @return string|null
1775
+	 */
1776
+	public function getSiteName()
1777
+	{
1778
+		return $this->siteName;
1779
+	}
1780
+
1781
+	/**
1782
+	 * @param string $siteName
1783
+	 */
1784
+	protected function setSiteName($siteName)
1785
+	{
1786
+		$this->siteName = $siteName;
1787
+	}
1788
+
1789
+	/**
1790
+	 * @return null|string
1791
+	 */
1792
+	public function getDirection()
1793
+	{
1794
+		return $this->direction;
1795
+	}
1796
+
1797
+	/**
1798
+	 * @param null|string $direction
1799
+	 */
1800
+	public function setDirection($direction)
1801
+	{
1802
+		$this->direction = $direction;
1803
+	}
1804 1804
 }
Please login to merge, or discard this patch.
Spacing   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -194,7 +194,7 @@  discard block
 block discarded – undo
194 194
                     $this->attempts[] = ['articleContent' => $result, 'textLength' => $length];
195 195
 
196 196
                     // No luck after removing flags, just return the longest text we found during the different loops
197
-                    usort($this->attempts, function ($a, $b) {
197
+                    usort($this->attempts, function($a, $b) {
198 198
                         return $a['textLength'] < $b['textLength'];
199 199
                     });
200 200
 
@@ -273,7 +273,7 @@  discard block
 block discarded – undo
273 273
         }
274 274
 
275 275
         // Prepend the XML tag to avoid having issues with special characters. Should be harmless.
276
-        $dom->loadHTML('<?xml encoding="UTF-8">' . $html);
276
+        $dom->loadHTML('<?xml encoding="UTF-8">'.$html);
277 277
         $dom->encoding = 'UTF-8';
278 278
 
279 279
         $this->removeScripts($dom);
@@ -491,7 +491,7 @@  discard block
 block discarded – undo
491 491
          * I can assure you it works properly if you let the code run.
492 492
          */
493 493
         if (preg_match('/ [\|\-\\\\\/>»] /i', $curTitle)) {
494
-            $titleHadHierarchicalSeparators = (bool)preg_match('/ [\\\\\/>»] /', $curTitle);
494
+            $titleHadHierarchicalSeparators = (bool) preg_match('/ [\\\\\/>»] /', $curTitle);
495 495
             $curTitle = preg_replace('/(.*)[\|\-\\\\\/>»] .*/i', '$1', $originalTitle);
496 496
 
497 497
             $this->logger->info(sprintf('[Metadata] Found hierarchical separators in title, new title is: \'%s\'', $curTitle));
@@ -507,7 +507,7 @@  discard block
 block discarded – undo
507 507
             // could assume it's the full title.
508 508
             $match = false;
509 509
             for ($i = 1; $i <= 2; $i++) {
510
-                foreach ($this->dom->getElementsByTagName('h' . $i) as $hTag) {
510
+                foreach ($this->dom->getElementsByTagName('h'.$i) as $hTag) {
511 511
                     // Trim texts to avoid having false negatives when the title is surrounded by spaces or tabs
512 512
                     if (trim($hTag->nodeValue) === trim($curTitle)) {
513 513
                         $match = true;
@@ -579,17 +579,17 @@  discard block
 block discarded – undo
579 579
 
580 580
         // Scheme-rooted relative URI.
581 581
         if (substr($uri, 0, 2) === '//') {
582
-            return $scheme . '://' . substr($uri, 2);
582
+            return $scheme.'://'.substr($uri, 2);
583 583
         }
584 584
 
585 585
         // Prepath-rooted relative URI.
586 586
         if (substr($uri, 0, 1) === '/') {
587
-            return $prePath . $uri;
587
+            return $prePath.$uri;
588 588
         }
589 589
 
590 590
         // Dotslash relative URI.
591 591
         if (strpos($uri, './') === 0) {
592
-            return $pathBase . substr($uri, 2);
592
+            return $pathBase.substr($uri, 2);
593 593
         }
594 594
         // Ignore hash URIs:
595 595
         if (substr($uri, 0, 1) === '#') {
@@ -598,7 +598,7 @@  discard block
 block discarded – undo
598 598
 
599 599
         // Standard relative URI; add entire path. pathBase already includes a
600 600
         // trailing "/".
601
-        return $pathBase . $uri;
601
+        return $pathBase.$uri;
602 602
     }
603 603
 
604 604
     /**
@@ -614,17 +614,17 @@  discard block
 block discarded – undo
614 614
         if ($this->dom->baseURI !== null) {
615 615
             if (substr($this->dom->baseURI, 0, 1) === '/') {
616 616
                 // URLs starting with '/' override completely the URL defined in the link
617
-                $pathBase = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . $this->dom->baseURI;
617
+                $pathBase = parse_url($url, PHP_URL_SCHEME).'://'.parse_url($url, PHP_URL_HOST).$this->dom->baseURI;
618 618
             } else {
619 619
                 // Otherwise just prepend the base to the actual path
620
-                $pathBase = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . dirname(parse_url($url, PHP_URL_PATH)) . '/' . rtrim($this->dom->baseURI, '/') . '/';
620
+                $pathBase = parse_url($url, PHP_URL_SCHEME).'://'.parse_url($url, PHP_URL_HOST).dirname(parse_url($url, PHP_URL_PATH)).'/'.rtrim($this->dom->baseURI, '/').'/';
621 621
             }
622 622
         } else {
623
-            $pathBase = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . dirname(parse_url($url, PHP_URL_PATH)) . '/';
623
+            $pathBase = parse_url($url, PHP_URL_SCHEME).'://'.parse_url($url, PHP_URL_HOST).dirname(parse_url($url, PHP_URL_PATH)).'/';
624 624
         }
625 625
 
626 626
         $scheme = parse_url($pathBase, PHP_URL_SCHEME);
627
-        $prePath = $scheme . '://' . parse_url($pathBase, PHP_URL_HOST);
627
+        $prePath = $scheme.'://'.parse_url($pathBase, PHP_URL_HOST);
628 628
 
629 629
         return [$pathBase, $scheme, $prePath];
630 630
     }
@@ -658,7 +658,7 @@  discard block
 block discarded – undo
658 658
                 continue;
659 659
             }
660 660
 
661
-            $matchString = $node->getAttribute('class') . ' ' . $node->getAttribute('id');
661
+            $matchString = $node->getAttribute('class').' '.$node->getAttribute('id');
662 662
 
663 663
             if (!$node->isProbablyVisible()) {
664 664
                 $this->logger->debug(sprintf('[Get Nodes] Removing hidden node... Match string was: \'%s\'', $matchString));
@@ -1049,7 +1049,7 @@  discard block
 block discarded – undo
1049 1049
                 while ($parentOfTopCandidate->nodeName !== 'body' && $parentOfTopCandidate->nodeType === XML_ELEMENT_NODE) {
1050 1050
                     $listsContainingThisAncestor = 0;
1051 1051
                     for ($ancestorIndex = 0; $ancestorIndex < count($alternativeCandidateAncestors) && $listsContainingThisAncestor < $MINIMUM_TOPCANDIDATES; $ancestorIndex++) {
1052
-                        $listsContainingThisAncestor += (int)in_array($parentOfTopCandidate, $alternativeCandidateAncestors[$ancestorIndex]);
1052
+                        $listsContainingThisAncestor += (int) in_array($parentOfTopCandidate, $alternativeCandidateAncestors[$ancestorIndex]);
1053 1053
                     }
1054 1054
                     if ($listsContainingThisAncestor >= $MINIMUM_TOPCANDIDATES) {
1055 1055
                         $topCandidate = $parentOfTopCandidate;
@@ -1285,7 +1285,7 @@  discard block
 block discarded – undo
1285 1285
                 $row = $tbody->getFirstElementChild();
1286 1286
                 if ($row->hasSingleTagInsideElement('td')) {
1287 1287
                     $cell = $row->getFirstElementChild();
1288
-                    $cell = NodeUtility::setNodeTag($cell, (array_reduce(iterator_to_array($cell->childNodes), function ($carry, $node) {
1288
+                    $cell = NodeUtility::setNodeTag($cell, (array_reduce(iterator_to_array($cell->childNodes), function($carry, $node) {
1289 1289
                         return $node->isPhrasingContent() && $carry;
1290 1290
                     }, true)) ? 'p' : 'div');
1291 1291
                     $table->parentNode->replaceChild($cell, $table);
@@ -1465,7 +1465,7 @@  discard block
 block discarded – undo
1465 1465
             $node = $DOMNodeList->item($length - 1 - $i);
1466 1466
 
1467 1467
             // First check if we're in a data table, in which case don't remove us.
1468
-            if ($node->hasAncestorTag('table', -1, function ($node) {
1468
+            if ($node->hasAncestorTag('table', -1, function($node) {
1469 1469
                 return $node->isReadabilityDataTable();
1470 1470
             })) {
1471 1471
                 continue;
@@ -1577,7 +1577,7 @@  discard block
 block discarded – undo
1577 1577
     public function _cleanHeaders(DOMDocument $article)
1578 1578
     {
1579 1579
         for ($headerIndex = 1; $headerIndex < 3; $headerIndex++) {
1580
-            $headers = $article->getElementsByTagName('h' . $headerIndex);
1580
+            $headers = $article->getElementsByTagName('h'.$headerIndex);
1581 1581
             /** @var $header DOMElement */
1582 1582
             foreach ($headers as $header) {
1583 1583
                 $weight = 0;
Please login to merge, or discard this patch.
plugins/af_readability/vendor/andreskrey/Readability/Configuration.php 1 patch
Indentation   +334 added lines, -334 removed lines patch added patch discarded remove patch
@@ -11,338 +11,338 @@
 block discarded – undo
11 11
  */
12 12
 class Configuration
13 13
 {
14
-    use LoggerAwareTrait;
15
-
16
-    /**
17
-     * @var int
18
-     */
19
-    protected $maxTopCandidates = 5;
20
-
21
-    /**
22
-     * @var int
23
-     */
24
-    protected $charThreshold = 500;
25
-
26
-    /**
27
-     * @var bool
28
-     */
29
-    protected $articleByLine = false;
30
-
31
-    /**
32
-     * @var bool
33
-     */
34
-    protected $stripUnlikelyCandidates = true;
35
-
36
-    /**
37
-     * @var bool
38
-     */
39
-    protected $cleanConditionally = true;
40
-
41
-    /**
42
-     * @var bool
43
-     */
44
-    protected $weightClasses = true;
45
-
46
-    /**
47
-     * @var bool
48
-     */
49
-    protected $fixRelativeURLs = false;
50
-
51
-    /**
52
-     * @var bool
53
-     */
54
-    protected $substituteEntities = false;
55
-
56
-    /**
57
-     * @var bool
58
-     */
59
-    protected $normalizeEntities = false;
60
-
61
-    /**
62
-     * @var bool
63
-     */
64
-    protected $summonCthulhu = false;
65
-
66
-    /**
67
-     * @var string
68
-     */
69
-    protected $originalURL = 'http://fakehost';
70
-
71
-    /**
72
-     * Configuration constructor.
73
-     *
74
-     * @param array $params
75
-     */
76
-    public function __construct(array $params = [])
77
-    {
78
-        foreach ($params as $key => $value) {
79
-            $setter = sprintf('set%s', $key);
80
-            if (method_exists($this, $setter)) {
81
-                call_user_func([$this, $setter], $value);
82
-            }
83
-        }
84
-    }
85
-
86
-    /**
87
-     * Returns an array-representation of configuration.
88
-     *
89
-     * @return array
90
-     */
91
-    public function toArray()
92
-    {
93
-        $out = [];
94
-        foreach ($this as $key => $value) {
95
-            $getter = sprintf('get%s', $key);
96
-            if (!is_object($value) && method_exists($this, $getter)) {
97
-                $out[$key] = call_user_func([$this, $getter]);
98
-            }
99
-        }
100
-
101
-        return $out;
102
-    }
103
-
104
-    /**
105
-     * @return LoggerInterface
106
-     */
107
-    public function getLogger()
108
-    {
109
-        // If no logger has been set, just return a null logger
110
-        if ($this->logger === null) {
111
-            return new NullLogger();
112
-        }
113
-
114
-        return $this->logger;
115
-    }
116
-
117
-    /**
118
-     * @param LoggerInterface $logger
119
-     *
120
-     * @return Configuration
121
-     */
122
-    public function setLogger(LoggerInterface $logger)
123
-    {
124
-        $this->logger = $logger;
125
-
126
-        return $this;
127
-    }
128
-
129
-    /**
130
-     * @return int
131
-     */
132
-    public function getMaxTopCandidates()
133
-    {
134
-        return $this->maxTopCandidates;
135
-    }
136
-
137
-    /**
138
-     * @param int $maxTopCandidates
139
-     *
140
-     * @return $this
141
-     */
142
-    public function setMaxTopCandidates($maxTopCandidates)
143
-    {
144
-        $this->maxTopCandidates = $maxTopCandidates;
145
-
146
-        return $this;
147
-    }
148
-
149
-    /**
150
-     * @return int
151
-     */
152
-    public function getCharThreshold()
153
-    {
154
-        return $this->charThreshold;
155
-    }
156
-
157
-    /**
158
-     * @param int $charThreshold
159
-     *
160
-     * @return $this
161
-     */
162
-    public function setCharThreshold($charThreshold)
163
-    {
164
-        $this->charThreshold = $charThreshold;
165
-
166
-        return $this;
167
-    }
168
-
169
-    /**
170
-     * @return bool
171
-     */
172
-    public function getArticleByLine()
173
-    {
174
-        return $this->articleByLine;
175
-    }
176
-
177
-    /**
178
-     * @param bool $articleByLine
179
-     *
180
-     * @return $this
181
-     */
182
-    public function setArticleByLine($articleByLine)
183
-    {
184
-        $this->articleByLine = $articleByLine;
185
-
186
-        return $this;
187
-    }
188
-
189
-    /**
190
-     * @return bool
191
-     */
192
-    public function getStripUnlikelyCandidates()
193
-    {
194
-        return $this->stripUnlikelyCandidates;
195
-    }
196
-
197
-    /**
198
-     * @param bool $stripUnlikelyCandidates
199
-     *
200
-     * @return $this
201
-     */
202
-    public function setStripUnlikelyCandidates($stripUnlikelyCandidates)
203
-    {
204
-        $this->stripUnlikelyCandidates = $stripUnlikelyCandidates;
205
-
206
-        return $this;
207
-    }
208
-
209
-    /**
210
-     * @return bool
211
-     */
212
-    public function getCleanConditionally()
213
-    {
214
-        return $this->cleanConditionally;
215
-    }
216
-
217
-    /**
218
-     * @param bool $cleanConditionally
219
-     *
220
-     * @return $this
221
-     */
222
-    public function setCleanConditionally($cleanConditionally)
223
-    {
224
-        $this->cleanConditionally = $cleanConditionally;
225
-
226
-        return $this;
227
-    }
228
-
229
-    /**
230
-     * @return bool
231
-     */
232
-    public function getWeightClasses()
233
-    {
234
-        return $this->weightClasses;
235
-    }
236
-
237
-    /**
238
-     * @param bool $weightClasses
239
-     *
240
-     * @return $this
241
-     */
242
-    public function setWeightClasses($weightClasses)
243
-    {
244
-        $this->weightClasses = $weightClasses;
245
-
246
-        return $this;
247
-    }
248
-
249
-    /**
250
-     * @return bool
251
-     */
252
-    public function getFixRelativeURLs()
253
-    {
254
-        return $this->fixRelativeURLs;
255
-    }
256
-
257
-    /**
258
-     * @param bool $fixRelativeURLs
259
-     *
260
-     * @return $this
261
-     */
262
-    public function setFixRelativeURLs($fixRelativeURLs)
263
-    {
264
-        $this->fixRelativeURLs = $fixRelativeURLs;
265
-
266
-        return $this;
267
-    }
268
-
269
-    /**
270
-     * @return bool
271
-     */
272
-    public function getSubstituteEntities()
273
-    {
274
-        return $this->substituteEntities;
275
-    }
276
-
277
-    /**
278
-     * @param bool $substituteEntities
279
-     *
280
-     * @return $this
281
-     */
282
-    public function setSubstituteEntities($substituteEntities)
283
-    {
284
-        $this->substituteEntities = $substituteEntities;
285
-
286
-        return $this;
287
-    }
288
-
289
-    /**
290
-     * @return bool
291
-     */
292
-    public function getNormalizeEntities()
293
-    {
294
-        return $this->normalizeEntities;
295
-    }
296
-
297
-    /**
298
-     * @param bool $normalizeEntities
299
-     *
300
-     * @return $this
301
-     */
302
-    public function setNormalizeEntities($normalizeEntities)
303
-    {
304
-        $this->normalizeEntities = $normalizeEntities;
305
-
306
-        return $this;
307
-    }
308
-
309
-    /**
310
-     * @return string
311
-     */
312
-    public function getOriginalURL()
313
-    {
314
-        return $this->originalURL;
315
-    }
316
-
317
-    /**
318
-     * @param string $originalURL
319
-     *
320
-     * @return $this
321
-     */
322
-    public function setOriginalURL($originalURL)
323
-    {
324
-        $this->originalURL = $originalURL;
325
-
326
-        return $this;
327
-    }
328
-
329
-    /**
330
-     * @return bool
331
-     */
332
-    public function getSummonCthulhu()
333
-    {
334
-        return $this->summonCthulhu;
335
-    }
336
-
337
-    /**
338
-     * @param bool $summonCthulhu
339
-     *
340
-     * @return $this
341
-     */
342
-    public function setSummonCthulhu($summonCthulhu)
343
-    {
344
-        $this->summonCthulhu = $summonCthulhu;
345
-
346
-        return $this;
347
-    }
14
+	use LoggerAwareTrait;
15
+
16
+	/**
17
+	 * @var int
18
+	 */
19
+	protected $maxTopCandidates = 5;
20
+
21
+	/**
22
+	 * @var int
23
+	 */
24
+	protected $charThreshold = 500;
25
+
26
+	/**
27
+	 * @var bool
28
+	 */
29
+	protected $articleByLine = false;
30
+
31
+	/**
32
+	 * @var bool
33
+	 */
34
+	protected $stripUnlikelyCandidates = true;
35
+
36
+	/**
37
+	 * @var bool
38
+	 */
39
+	protected $cleanConditionally = true;
40
+
41
+	/**
42
+	 * @var bool
43
+	 */
44
+	protected $weightClasses = true;
45
+
46
+	/**
47
+	 * @var bool
48
+	 */
49
+	protected $fixRelativeURLs = false;
50
+
51
+	/**
52
+	 * @var bool
53
+	 */
54
+	protected $substituteEntities = false;
55
+
56
+	/**
57
+	 * @var bool
58
+	 */
59
+	protected $normalizeEntities = false;
60
+
61
+	/**
62
+	 * @var bool
63
+	 */
64
+	protected $summonCthulhu = false;
65
+
66
+	/**
67
+	 * @var string
68
+	 */
69
+	protected $originalURL = 'http://fakehost';
70
+
71
+	/**
72
+	 * Configuration constructor.
73
+	 *
74
+	 * @param array $params
75
+	 */
76
+	public function __construct(array $params = [])
77
+	{
78
+		foreach ($params as $key => $value) {
79
+			$setter = sprintf('set%s', $key);
80
+			if (method_exists($this, $setter)) {
81
+				call_user_func([$this, $setter], $value);
82
+			}
83
+		}
84
+	}
85
+
86
+	/**
87
+	 * Returns an array-representation of configuration.
88
+	 *
89
+	 * @return array
90
+	 */
91
+	public function toArray()
92
+	{
93
+		$out = [];
94
+		foreach ($this as $key => $value) {
95
+			$getter = sprintf('get%s', $key);
96
+			if (!is_object($value) && method_exists($this, $getter)) {
97
+				$out[$key] = call_user_func([$this, $getter]);
98
+			}
99
+		}
100
+
101
+		return $out;
102
+	}
103
+
104
+	/**
105
+	 * @return LoggerInterface
106
+	 */
107
+	public function getLogger()
108
+	{
109
+		// If no logger has been set, just return a null logger
110
+		if ($this->logger === null) {
111
+			return new NullLogger();
112
+		}
113
+
114
+		return $this->logger;
115
+	}
116
+
117
+	/**
118
+	 * @param LoggerInterface $logger
119
+	 *
120
+	 * @return Configuration
121
+	 */
122
+	public function setLogger(LoggerInterface $logger)
123
+	{
124
+		$this->logger = $logger;
125
+
126
+		return $this;
127
+	}
128
+
129
+	/**
130
+	 * @return int
131
+	 */
132
+	public function getMaxTopCandidates()
133
+	{
134
+		return $this->maxTopCandidates;
135
+	}
136
+
137
+	/**
138
+	 * @param int $maxTopCandidates
139
+	 *
140
+	 * @return $this
141
+	 */
142
+	public function setMaxTopCandidates($maxTopCandidates)
143
+	{
144
+		$this->maxTopCandidates = $maxTopCandidates;
145
+
146
+		return $this;
147
+	}
148
+
149
+	/**
150
+	 * @return int
151
+	 */
152
+	public function getCharThreshold()
153
+	{
154
+		return $this->charThreshold;
155
+	}
156
+
157
+	/**
158
+	 * @param int $charThreshold
159
+	 *
160
+	 * @return $this
161
+	 */
162
+	public function setCharThreshold($charThreshold)
163
+	{
164
+		$this->charThreshold = $charThreshold;
165
+
166
+		return $this;
167
+	}
168
+
169
+	/**
170
+	 * @return bool
171
+	 */
172
+	public function getArticleByLine()
173
+	{
174
+		return $this->articleByLine;
175
+	}
176
+
177
+	/**
178
+	 * @param bool $articleByLine
179
+	 *
180
+	 * @return $this
181
+	 */
182
+	public function setArticleByLine($articleByLine)
183
+	{
184
+		$this->articleByLine = $articleByLine;
185
+
186
+		return $this;
187
+	}
188
+
189
+	/**
190
+	 * @return bool
191
+	 */
192
+	public function getStripUnlikelyCandidates()
193
+	{
194
+		return $this->stripUnlikelyCandidates;
195
+	}
196
+
197
+	/**
198
+	 * @param bool $stripUnlikelyCandidates
199
+	 *
200
+	 * @return $this
201
+	 */
202
+	public function setStripUnlikelyCandidates($stripUnlikelyCandidates)
203
+	{
204
+		$this->stripUnlikelyCandidates = $stripUnlikelyCandidates;
205
+
206
+		return $this;
207
+	}
208
+
209
+	/**
210
+	 * @return bool
211
+	 */
212
+	public function getCleanConditionally()
213
+	{
214
+		return $this->cleanConditionally;
215
+	}
216
+
217
+	/**
218
+	 * @param bool $cleanConditionally
219
+	 *
220
+	 * @return $this
221
+	 */
222
+	public function setCleanConditionally($cleanConditionally)
223
+	{
224
+		$this->cleanConditionally = $cleanConditionally;
225
+
226
+		return $this;
227
+	}
228
+
229
+	/**
230
+	 * @return bool
231
+	 */
232
+	public function getWeightClasses()
233
+	{
234
+		return $this->weightClasses;
235
+	}
236
+
237
+	/**
238
+	 * @param bool $weightClasses
239
+	 *
240
+	 * @return $this
241
+	 */
242
+	public function setWeightClasses($weightClasses)
243
+	{
244
+		$this->weightClasses = $weightClasses;
245
+
246
+		return $this;
247
+	}
248
+
249
+	/**
250
+	 * @return bool
251
+	 */
252
+	public function getFixRelativeURLs()
253
+	{
254
+		return $this->fixRelativeURLs;
255
+	}
256
+
257
+	/**
258
+	 * @param bool $fixRelativeURLs
259
+	 *
260
+	 * @return $this
261
+	 */
262
+	public function setFixRelativeURLs($fixRelativeURLs)
263
+	{
264
+		$this->fixRelativeURLs = $fixRelativeURLs;
265
+
266
+		return $this;
267
+	}
268
+
269
+	/**
270
+	 * @return bool
271
+	 */
272
+	public function getSubstituteEntities()
273
+	{
274
+		return $this->substituteEntities;
275
+	}
276
+
277
+	/**
278
+	 * @param bool $substituteEntities
279
+	 *
280
+	 * @return $this
281
+	 */
282
+	public function setSubstituteEntities($substituteEntities)
283
+	{
284
+		$this->substituteEntities = $substituteEntities;
285
+
286
+		return $this;
287
+	}
288
+
289
+	/**
290
+	 * @return bool
291
+	 */
292
+	public function getNormalizeEntities()
293
+	{
294
+		return $this->normalizeEntities;
295
+	}
296
+
297
+	/**
298
+	 * @param bool $normalizeEntities
299
+	 *
300
+	 * @return $this
301
+	 */
302
+	public function setNormalizeEntities($normalizeEntities)
303
+	{
304
+		$this->normalizeEntities = $normalizeEntities;
305
+
306
+		return $this;
307
+	}
308
+
309
+	/**
310
+	 * @return string
311
+	 */
312
+	public function getOriginalURL()
313
+	{
314
+		return $this->originalURL;
315
+	}
316
+
317
+	/**
318
+	 * @param string $originalURL
319
+	 *
320
+	 * @return $this
321
+	 */
322
+	public function setOriginalURL($originalURL)
323
+	{
324
+		$this->originalURL = $originalURL;
325
+
326
+		return $this;
327
+	}
328
+
329
+	/**
330
+	 * @return bool
331
+	 */
332
+	public function getSummonCthulhu()
333
+	{
334
+		return $this->summonCthulhu;
335
+	}
336
+
337
+	/**
338
+	 * @param bool $summonCthulhu
339
+	 *
340
+	 * @return $this
341
+	 */
342
+	public function setSummonCthulhu($summonCthulhu)
343
+	{
344
+		$this->summonCthulhu = $summonCthulhu;
345
+
346
+		return $this;
347
+	}
348 348
 }
Please login to merge, or discard this patch.
plugins/af_redditimgur/init.php 3 patches
Indentation   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -280,36 +280,36 @@
 block discarded – undo
280 280
 					$found = true;
281 281
 				}
282 282
 
283
-                // imgur via link rel="image_src" href="..."
284
-                if (!$found && preg_match("/imgur/", $entry->getAttribute("href"))) {
283
+				// imgur via link rel="image_src" href="..."
284
+				if (!$found && preg_match("/imgur/", $entry->getAttribute("href"))) {
285 285
 
286
-                    Debug::log("handling as imgur page/whatever", Debug::$LOG_VERBOSE);
286
+					Debug::log("handling as imgur page/whatever", Debug::$LOG_VERBOSE);
287 287
 
288
-                    $content = fetch_file_contents(["url" => $entry->getAttribute("href"),
289
-                        "http_accept" => "text/*"]);
288
+					$content = fetch_file_contents(["url" => $entry->getAttribute("href"),
289
+						"http_accept" => "text/*"]);
290 290
 
291
-                    if ($content) {
292
-                        $cdoc = new DOMDocument();
291
+					if ($content) {
292
+						$cdoc = new DOMDocument();
293 293
 
294
-                        if (@$cdoc->loadHTML($content)) {
295
-                            $cxpath = new DOMXPath($cdoc);
294
+						if (@$cdoc->loadHTML($content)) {
295
+							$cxpath = new DOMXPath($cdoc);
296 296
 
297
-                            $rel_image = $cxpath->query("//link[@rel='image_src']")->item(0);
297
+							$rel_image = $cxpath->query("//link[@rel='image_src']")->item(0);
298 298
 
299
-                            if ($rel_image) {
299
+							if ($rel_image) {
300 300
 
301
-                                $img = $doc->createElement('img');
302
-                                $img->setAttribute("src", $rel_image->getAttribute("href"));
301
+								$img = $doc->createElement('img');
302
+								$img->setAttribute("src", $rel_image->getAttribute("href"));
303 303
 
304
-                                $br = $doc->createElement('br');
305
-                                $entry->parentNode->insertBefore($img, $entry);
306
-                                $entry->parentNode->insertBefore($br, $entry);
304
+								$br = $doc->createElement('br');
305
+								$entry->parentNode->insertBefore($img, $entry);
306
+								$entry->parentNode->insertBefore($br, $entry);
307 307
 
308
-                                $found = true;
309
-                            }
310
-                        }
311
-                    }
312
-                }
308
+								$found = true;
309
+							}
310
+						}
311
+					}
312
+				}
313 313
 
314 314
 				// wtf is this even
315 315
 				if (!$found && preg_match("/^https?:\/\/gyazo\.com\/([^\.\/]+$)/", $entry->getAttribute("href"), $matches)) {
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -57,13 +57,13 @@  discard block
 block discarded – undo
57 57
 		print "<fieldset class='narrow'>";
58 58
 		print "<label class='checkbox'>";
59 59
 		print_checkbox("enable_readability", $enable_readability);
60
-		print " " . __("Extract missing content using Readability (requires af_readability)") . "</label>";
60
+		print " ".__("Extract missing content using Readability (requires af_readability)")."</label>";
61 61
 		print "</fieldset>";
62 62
 
63 63
 		print "<fieldset class='narrow'>";
64 64
 		print "<label class='checkbox'>";
65 65
 		print_checkbox("enable_content_dupcheck", $enable_content_dupcheck);
66
-		print " " . __("Enable additional duplicate checking") . "</label>";
66
+		print " ".__("Enable additional duplicate checking")."</label>";
67 67
 		print "</fieldset>";
68 68
 
69 69
 		print_button("submit", __("Save"), 'class="alt-primary"');
@@ -96,14 +96,14 @@  discard block
 block discarded – undo
96 96
 		foreach ($entries as $entry) {
97 97
 			if ($entry->hasAttribute("href") && strpos($entry->getAttribute("href"), "reddit.com") === FALSE) {
98 98
 
99
-				Debug::log("processing href: " . $entry->getAttribute("href"), Debug::$LOG_VERBOSE);
99
+				Debug::log("processing href: ".$entry->getAttribute("href"), Debug::$LOG_VERBOSE);
100 100
 
101 101
 				$matches = array();
102 102
 
103 103
 				if (!$found && preg_match("/^https?:\/\/twitter.com\/(.*?)\/status\/(.*)/", $entry->getAttribute("href"), $matches)) {
104
-					Debug::log("handling as twitter: " . $matches[1] . " " . $matches[2], Debug::$LOG_VERBOSE);
104
+					Debug::log("handling as twitter: ".$matches[1]." ".$matches[2], Debug::$LOG_VERBOSE);
105 105
 
106
-					$oembed_result = fetch_file_contents("https://publish.twitter.com/oembed?url=" . urlencode($entry->getAttribute("href")));
106
+					$oembed_result = fetch_file_contents("https://publish.twitter.com/oembed?url=".urlencode($entry->getAttribute("href")));
107 107
 
108 108
 					if ($oembed_result) {
109 109
 						$oembed_result = json_decode($oembed_result, true);
@@ -111,7 +111,7 @@  discard block
 block discarded – undo
111 111
 						if ($oembed_result && isset($oembed_result["html"])) {
112 112
 
113 113
 							$tmp = new DOMDocument();
114
-							if ($tmp->loadHTML('<?xml encoding="utf-8" ?>' . $oembed_result["html"])) {
114
+							if ($tmp->loadHTML('<?xml encoding="utf-8" ?>'.$oembed_result["html"])) {
115 115
 								$p = $doc->createElement("p");
116 116
 
117 117
 								$p->appendChild($doc->importNode(
@@ -135,8 +135,8 @@  discard block
 block discarded – undo
135 135
 
136 136
 					Debug::log("Handling as Gfycat", Debug::$LOG_VERBOSE);
137 137
 
138
-					$source_stream = 'https://giant.gfycat.com/' . $matches[2] . '.mp4';
139
-					$poster_url = 'https://thumbs.gfycat.com/' . $matches[2] . '-mobile.jpg';
138
+					$source_stream = 'https://giant.gfycat.com/'.$matches[2].'.mp4';
139
+					$poster_url = 'https://thumbs.gfycat.com/'.$matches[2].'-mobile.jpg';
140 140
 
141 141
 					$content_type = $this->get_content_type($source_stream);
142 142
 
@@ -184,7 +184,7 @@  discard block
 block discarded – undo
184 184
 					}
185 185
 
186 186
 					if (!$source_stream) {
187
-						$source_stream = "https://v.redd.it/" . $matches[1] . "/DASH_600_K";
187
+						$source_stream = "https://v.redd.it/".$matches[1]."/DASH_600_K";
188 188
 					}
189 189
 
190 190
 					$this->handle_as_video($doc, $entry, $source_stream, $poster_url);
@@ -542,7 +542,7 @@  discard block
 block discarded – undo
542 542
 
543 543
 			// do not try to embed posts linking back to other reddit posts
544 544
 			// readability.php requires PHP 5.6
545
-			if ($url &&	strpos($url, "reddit.com") === FALSE && version_compare(PHP_VERSION, '5.6.0', '>=')) {
545
+			if ($url && strpos($url, "reddit.com") === FALSE && version_compare(PHP_VERSION, '5.6.0', '>=')) {
546 546
 
547 547
 				/* link may lead to a huge video file or whatever, we need to check content type before trying to
548 548
 				parse it which p much requires curl */
Please login to merge, or discard this patch.
Braces   +13 added lines, -7 removed lines patch added patch discarded remove patch
@@ -22,7 +22,9 @@  discard block
 block discarded – undo
22 22
 	}
23 23
 
24 24
 	function hook_prefs_tab($args) {
25
-		if ($args != "prefFeeds") return;
25
+		if ($args != "prefFeeds") {
26
+			return;
27
+		}
26 28
 
27 29
 		print "<div dojoType=\"dijit.layout.AccordionPane\" 
28 30
 			title=\"<i class='material-icons'>extension</i> ".__('Reddit content settings (af_redditimgur)')."\">";
@@ -173,8 +175,7 @@  discard block
 block discarded – undo
173 175
 									if ($child["data"]["url"] == $matches[0]) {
174 176
 										try {
175 177
 											$source_stream = $child["data"]["media"]["reddit_video"]["fallback_url"];
176
-										}
177
-										catch (Exception $e) {
178
+										} catch (Exception $e) {
178 179
 										}
179 180
 										break 2;
180 181
 									}
@@ -230,8 +231,9 @@  discard block
 block discarded – undo
230 231
 
231 232
 					$source_stream = str_replace(".gifv", ".mp4", $entry->getAttribute("href"));
232 233
 
233
-					if (strpos($source_stream, "imgur.com") !== FALSE)
234
-						$poster_url = str_replace(".mp4", "h.jpg", $source_stream);
234
+					if (strpos($source_stream, "imgur.com") !== FALSE) {
235
+											$poster_url = str_replace(".mp4", "h.jpg", $source_stream);
236
+					}
235 237
 
236 238
 					$this->handle_as_video($doc, $entry, $source_stream, $poster_url);
237 239
 
@@ -426,7 +428,9 @@  discard block
 block discarded – undo
426 428
 					if ($row = $sth->fetch()) {
427 429
 						$num_found = $row['cid'];
428 430
 
429
-						if ($num_found > 0) $article["force_catchup"] = true;
431
+						if ($num_found > 0) {
432
+							$article["force_catchup"] = true;
433
+						}
430 434
 					}
431 435
 				}
432 436
 			}
@@ -458,7 +462,9 @@  discard block
 block discarded – undo
458 462
 		$video->setAttribute("controls", "1");
459 463
 		$video->setAttribute("loop", "1");
460 464
 
461
-		if ($poster_url) $video->setAttribute("poster", $poster_url);
465
+		if ($poster_url) {
466
+			$video->setAttribute("poster", $poster_url);
467
+		}
462 468
 
463 469
 		$source = $doc->createElement('source');
464 470
 		$source->setAttribute("src", $source_stream);
Please login to merge, or discard this patch.
plugins/af_comics/init.php 2 patches
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -19,9 +19,9 @@  discard block
 block discarded – undo
19 19
 		$host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
20 20
 		$host->add_hook($host::HOOK_PREFS_TAB, $this);
21 21
 
22
-		require_once __DIR__ . "/filter_base.php";
22
+		require_once __DIR__."/filter_base.php";
23 23
 
24
-		$filters = array_merge(glob(__DIR__ . "/filters.local/*.php"), glob(__DIR__ . "/filters/*.php"));
24
+		$filters = array_merge(glob(__DIR__."/filters.local/*.php"), glob(__DIR__."/filters/*.php"));
25 25
 		$names = [];
26 26
 
27 27
 		foreach ($filters as $file) {
@@ -50,7 +50,7 @@  discard block
 block discarded – undo
50 50
 		print "<div dojoType=\"dijit.layout.AccordionPane\" 
51 51
 			title=\"<i class='material-icons'>photo</i> ".__('Feeds supported by af_comics')."\">";
52 52
 
53
-		print "<p>" . __("The following comics are currently supported:") . "</p>";
53
+		print "<p>".__("The following comics are currently supported:")."</p>";
54 54
 
55 55
 		$comics = array("GoComics");
56 56
 
@@ -93,9 +93,9 @@  discard block
 block discarded – undo
93 93
 			return $feed_data;
94 94
 
95 95
 		if (preg_match('#^https?://(?:feeds\.feedburner\.com/uclick|www\.gocomics\.com)/([-a-z0-9]+)$#i', $fetch_url, $comic)) {
96
-			$site_url = 'https://www.gocomics.com/' . $comic[1];
96
+			$site_url = 'https://www.gocomics.com/'.$comic[1];
97 97
 
98
-			$article_link = $site_url . date('/Y/m/d');
98
+			$article_link = $site_url.date('/Y/m/d');
99 99
 
100 100
 			$body = fetch_file_contents(array('url' => $article_link, 'type' => 'text/html', 'followlocation' => false));
101 101
 
@@ -131,7 +131,7 @@  discard block
 block discarded – undo
131 131
 							$title = date('l, F d, Y');
132 132
 						}
133 133
 
134
-						foreach (['srcset', 'sizes', 'data-srcset', 'width'] as $attr ) {
134
+						foreach (['srcset', 'sizes', 'data-srcset', 'width'] as $attr) {
135 135
 							$node->removeAttribute($attr);
136 136
 						}
137 137
 
Please login to merge, or discard this patch.
Braces   +25 added lines, -15 removed lines patch added patch discarded remove patch
@@ -45,7 +45,9 @@  discard block
 block discarded – undo
45 45
 	}
46 46
 
47 47
 	function hook_prefs_tab($args) {
48
-		if ($args != "prefFeeds") return;
48
+		if ($args != "prefFeeds") {
49
+			return;
50
+		}
49 51
 
50 52
 		print "<div dojoType=\"dijit.layout.AccordionPane\" 
51 53
 			title=\"<i class='material-icons'>photo</i> ".__('Feeds supported by af_comics')."\">";
@@ -77,8 +79,9 @@  discard block
 block discarded – undo
77 79
 
78 80
 	function hook_article_filter($article) {
79 81
 		foreach ($this->filters as $f) {
80
-			if ($f->process($article))
81
-				break;
82
+			if ($f->process($article)) {
83
+							break;
84
+			}
82 85
 		}
83 86
 
84 87
 		return $article;
@@ -89,8 +92,9 @@  discard block
 block discarded – undo
89 92
 	 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
90 93
 	 */
91 94
 	function hook_fetch_feed($feed_data, $fetch_url, $owner_uid, $feed, $last_article_timestamp, $auth_login, $auth_pass) {
92
-		if ($auth_login || $auth_pass)
93
-			return $feed_data;
95
+		if ($auth_login || $auth_pass) {
96
+					return $feed_data;
97
+		}
94 98
 
95 99
 		if (preg_match('#^https?://(?:feeds\.feedburner\.com/uclick|www\.gocomics\.com)/([-a-z0-9]+)$#i', $fetch_url, $comic)) {
96 100
 			$site_url = 'https://www.gocomics.com/' . $comic[1];
@@ -153,29 +157,35 @@  discard block
 block discarded – undo
153 157
 
154 158
 			$tpl->addBlock('feed');
155 159
 
156
-			if ($tpl->generateOutputToString($tmp_data))
157
-				$feed_data = $tmp_data;
160
+			if ($tpl->generateOutputToString($tmp_data)) {
161
+							$feed_data = $tmp_data;
162
+			}
158 163
 		}
159 164
 
160 165
 		return $feed_data;
161 166
 	}
162 167
 
163 168
 	function hook_subscribe_feed($contents, $url, $auth_login, $auth_pass) {
164
-		if ($auth_login || $auth_pass)
165
-			return $contents;
169
+		if ($auth_login || $auth_pass) {
170
+					return $contents;
171
+		}
166 172
 
167
-		if (preg_match('#^https?://www\.gocomics\.com/([-a-z0-9]+)$#i', $url))
168
-			return '<?xml version="1.0" encoding="utf-8"?>'; // Get is_html() to return false.
173
+		if (preg_match('#^https?://www\.gocomics\.com/([-a-z0-9]+)$#i', $url)) {
174
+					return '<?xml version="1.0" encoding="utf-8"?>';
175
+		}
176
+		// Get is_html() to return false.
169 177
 
170 178
 		return $contents;
171 179
 	}
172 180
 
173 181
 	function hook_feed_basic_info($basic_info, $fetch_url, $owner_uid, $feed, $auth_login, $auth_pass) {
174
-		if ($auth_login || $auth_pass)
175
-			return $basic_info;
182
+		if ($auth_login || $auth_pass) {
183
+					return $basic_info;
184
+		}
176 185
 
177
-		if (preg_match('#^https?://www\.gocomics\.com/([-a-z0-9]+)$#i', $fetch_url, $matches))
178
-			$basic_info = array('title' => ucfirst($matches[1]), 'site_url' => $matches[0]);
186
+		if (preg_match('#^https?://www\.gocomics\.com/([-a-z0-9]+)$#i', $fetch_url, $matches)) {
187
+					$basic_info = array('title' => ucfirst($matches[1]), 'site_url' => $matches[0]);
188
+		}
179 189
 
180 190
 		return $basic_info;
181 191
 	}
Please login to merge, or discard this patch.
plugins/af_comics/filters/af_comics_whomp.php 1 patch
Braces   +3 added lines, -2 removed lines patch added patch discarded remove patch
@@ -14,8 +14,9 @@
 block discarded – undo
14 14
 
15 15
 			global $fetch_last_error_content;
16 16
 
17
-			if (!$res && $fetch_last_error_content)
18
-				$res = $fetch_last_error_content;
17
+			if (!$res && $fetch_last_error_content) {
18
+							$res = $fetch_last_error_content;
19
+			}
19 20
 
20 21
 			$doc = new DOMDocument();
21 22
 
Please login to merge, or discard this patch.
plugins/af_comics/filters/af_comics_darklegacy.php 1 patch
Braces   +3 added lines, -2 removed lines patch added patch discarded remove patch
@@ -15,8 +15,9 @@
 block discarded – undo
15 15
 
16 16
 				global $fetch_last_error_content;
17 17
 
18
-				if (!$res && $fetch_last_error_content)
19
-					$res = $fetch_last_error_content;
18
+				if (!$res && $fetch_last_error_content) {
19
+									$res = $fetch_last_error_content;
20
+				}
20 21
 
21 22
 				$doc = new DOMDocument();
22 23
 
Please login to merge, or discard this patch.
plugins/af_comics/filters/af_comics_pa.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -54,7 +54,7 @@
 block discarded – undo
54 54
 						$i->parentNode->removeChild($i);
55 55
 					}
56 56
 
57
-					if ($basenode){
57
+					if ($basenode) {
58 58
 						$article["content"] = $doc->saveHTML($basenode);
59 59
 					}
60 60
 				}
Please login to merge, or discard this patch.
Braces   +3 added lines, -2 removed lines patch added patch discarded remove patch
@@ -46,8 +46,9 @@
 block discarded – undo
46 46
 
47 47
 					$avatar = $xpath->query('(//div[@class="avatar"]//img)')->item(0);
48 48
 
49
-					if ($basenode)
50
-						$basenode->insertBefore($avatar, $basenode->firstChild);
49
+					if ($basenode) {
50
+											$basenode->insertBefore($avatar, $basenode->firstChild);
51
+					}
51 52
 
52 53
 					$uninteresting = $xpath->query('(//div[@class="avatar"])');
53 54
 					foreach ($uninteresting as $i) {
Please login to merge, or discard this patch.
plugins/af_comics/filters/af_comics_cad.php 1 patch
Braces   +3 added lines, -2 removed lines patch added patch discarded remove patch
@@ -17,8 +17,9 @@
 block discarded – undo
17 17
 					false, false, 0,
18 18
 					"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0");
19 19
 
20
-				if (!$res && $fetch_last_error_content)
21
-					$res = $fetch_last_error_content;
20
+				if (!$res && $fetch_last_error_content) {
21
+									$res = $fetch_last_error_content;
22
+				}
22 23
 
23 24
 				if (@$doc->loadHTML($res)) {
24 25
 					$xpath = new DOMXPath($doc);
Please login to merge, or discard this patch.
plugins/af_comics/filters/af_comics_tfd.php 2 patches
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -7,7 +7,7 @@
 block discarded – undo
7 7
 
8 8
 	function process(&$article) {
9 9
 		if (strpos($article["link"], "toothpastefordinner.com") !== FALSE ||
10
-		    strpos($article["link"], "marriedtothesea.com") !== FALSE) {
10
+			strpos($article["link"], "marriedtothesea.com") !== FALSE) {
11 11
 			$res = fetch_file_contents($article["link"], false, false, false,
12 12
 				false, false, 0,
13 13
 				"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)");
Please login to merge, or discard this patch.
Braces   +3 added lines, -1 removed lines patch added patch discarded remove patch
@@ -12,7 +12,9 @@
 block discarded – undo
12 12
 				false, false, 0,
13 13
 				"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)");
14 14
 
15
-			if (!$res) return $article;
15
+			if (!$res) {
16
+				return $article;
17
+			}
16 18
 
17 19
 			$doc = new DOMDocument();
18 20
 
Please login to merge, or discard this patch.