mirror of
https://github.com/j0k3r/php-readability.git
synced 2026-09-27 06:26:17 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f825dcf55a | ||
|
|
9a9373de4b | ||
|
|
d454c3a462 | ||
|
|
8b1ef07401 | ||
|
|
5885dbbe78 | ||
|
|
6947999782 | ||
|
|
da755013aa | ||
|
|
5b9551d1e3 | ||
|
|
c7b10dcc45 | ||
|
|
80adfe870b | ||
|
|
cb6b6ac577 | ||
|
|
677f3f096e | ||
|
|
29122763db | ||
|
|
89d3b74259 | ||
|
|
e792644fe8 | ||
|
|
648d8c605b | ||
|
|
f28191a728 | ||
|
|
2103853a1b | ||
|
|
7f4c6cfcbd |
@@ -11,16 +11,16 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
coding-standards:
|
coding-standards:
|
||||||
name: "CS Fixer & PHPStan"
|
name: "CS Fixer & PHPStan"
|
||||||
runs-on: "ubuntu-20.04"
|
runs-on: "ubuntu-22.04"
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php:
|
php:
|
||||||
- "7.2"
|
- "7.4"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: "actions/checkout@v3"
|
uses: "actions/checkout@v4"
|
||||||
|
|
||||||
- name: "Install PHP"
|
- name: "Install PHP"
|
||||||
uses: "shivammathur/setup-php@v2"
|
uses: "shivammathur/setup-php@v2"
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: "Install dependencies with Composer"
|
- name: "Install dependencies with Composer"
|
||||||
uses: "ramsey/composer-install@v2"
|
uses: "ramsey/composer-install@v3"
|
||||||
with:
|
with:
|
||||||
composer-options: "--optimize-autoloader --prefer-dist"
|
composer-options: "--optimize-autoloader --prefer-dist"
|
||||||
|
|
||||||
|
|||||||
@@ -14,22 +14,21 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
phpunit:
|
phpunit:
|
||||||
name: "PHPUnit (PHP ${{ matrix.php }})"
|
name: "PHPUnit (PHP ${{ matrix.php }})"
|
||||||
runs-on: "ubuntu-20.04"
|
runs-on: "ubuntu-22.04"
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php:
|
php:
|
||||||
- "7.2"
|
|
||||||
- "7.3"
|
|
||||||
- "7.4"
|
- "7.4"
|
||||||
- "8.0"
|
- "8.0"
|
||||||
- "8.1"
|
- "8.1"
|
||||||
- "8.2"
|
- "8.2"
|
||||||
- "8.3"
|
- "8.3"
|
||||||
|
- "8.4"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: "actions/checkout@v3"
|
uses: "actions/checkout@v4"
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ jobs:
|
|||||||
run: "composer remove friendsofphp/php-cs-fixer --dev --no-progress --no-update"
|
run: "composer remove friendsofphp/php-cs-fixer --dev --no-progress --no-update"
|
||||||
|
|
||||||
- name: "Install dependencies with Composer"
|
- name: "Install dependencies with Composer"
|
||||||
uses: "ramsey/composer-install@v2"
|
uses: "ramsey/composer-install@v3"
|
||||||
with:
|
with:
|
||||||
composer-options: "--optimize-autoloader --prefer-dist"
|
composer-options: "--optimize-autoloader --prefer-dist"
|
||||||
|
|
||||||
@@ -60,16 +59,16 @@ jobs:
|
|||||||
|
|
||||||
phpunit-coverage:
|
phpunit-coverage:
|
||||||
name: "PHPUnit coverage (PHP ${{ matrix.php }})"
|
name: "PHPUnit coverage (PHP ${{ matrix.php }})"
|
||||||
runs-on: "ubuntu-20.04"
|
runs-on: "ubuntu-22.04"
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php:
|
php:
|
||||||
- "7.4"
|
- "8.0"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: "actions/checkout@v3"
|
uses: "actions/checkout@v4"
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
@@ -88,7 +87,7 @@ jobs:
|
|||||||
run: "composer remove friendsofphp/php-cs-fixer --dev --no-progress --no-update"
|
run: "composer remove friendsofphp/php-cs-fixer --dev --no-progress --no-update"
|
||||||
|
|
||||||
- name: "Install dependencies with Composer"
|
- name: "Install dependencies with Composer"
|
||||||
uses: "ramsey/composer-install@v2"
|
uses: "ramsey/composer-install@v3"
|
||||||
with:
|
with:
|
||||||
composer-options: "--optimize-autoloader --prefer-dist"
|
composer-options: "--optimize-autoloader --prefer-dist"
|
||||||
|
|
||||||
@@ -99,7 +98,7 @@ jobs:
|
|||||||
run: "php vendor/bin/simple-phpunit -v --coverage-clover build/logs/clover.xml"
|
run: "php vendor/bin/simple-phpunit -v --coverage-clover build/logs/clover.xml"
|
||||||
|
|
||||||
- name: "Retrieve Coveralls phar"
|
- name: "Retrieve Coveralls phar"
|
||||||
run: "wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.4.2/php-coveralls.phar"
|
run: "wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.7.0/php-coveralls.phar"
|
||||||
|
|
||||||
- name: "Enable Coveralls phar"
|
- name: "Enable Coveralls phar"
|
||||||
run: "chmod +x php-coveralls.phar"
|
run: "chmod +x php-coveralls.phar"
|
||||||
@@ -111,16 +110,16 @@ jobs:
|
|||||||
|
|
||||||
phpunit-lowest:
|
phpunit-lowest:
|
||||||
name: "PHPUnit lowest deps (PHP ${{ matrix.php }})"
|
name: "PHPUnit lowest deps (PHP ${{ matrix.php }})"
|
||||||
runs-on: "ubuntu-20.04"
|
runs-on: "ubuntu-22.04"
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php:
|
php:
|
||||||
- "7.2"
|
- "7.4"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: "actions/checkout@v3"
|
uses: "actions/checkout@v4"
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
@@ -139,7 +138,7 @@ jobs:
|
|||||||
run: "composer remove friendsofphp/php-cs-fixer --dev --no-progress --no-update"
|
run: "composer remove friendsofphp/php-cs-fixer --dev --no-progress --no-update"
|
||||||
|
|
||||||
- name: "Install dependencies with Composer"
|
- name: "Install dependencies with Composer"
|
||||||
uses: "ramsey/composer-install@v2"
|
uses: "ramsey/composer-install@v3"
|
||||||
with:
|
with:
|
||||||
composer-options: "--optimize-autoloader --prefer-dist"
|
composer-options: "--optimize-autoloader --prefer-dist"
|
||||||
dependency-versions: "lowest"
|
dependency-versions: "lowest"
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ return (new PhpCsFixer\Config())
|
|||||||
'strict_comparison' => true,
|
'strict_comparison' => true,
|
||||||
'strict_param' => true,
|
'strict_param' => true,
|
||||||
'concat_space' => ['spacing' => 'one'],
|
'concat_space' => ['spacing' => 'one'],
|
||||||
|
// Pulled in by @Symfony:risky but we still support PHP 7.4
|
||||||
|
'modernize_strpos' => false,
|
||||||
|
// Pulled in by @Symfony, we cannot add property types until we bump PHP to ≥ 7.4
|
||||||
|
'no_null_property_initialization' => false,
|
||||||
])
|
])
|
||||||
->setFinder($finder)
|
->setFinder($finder)
|
||||||
;
|
;
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@
|
|||||||
"role": "Developer (original JS version)"
|
"role": "Developer (original JS version)"
|
||||||
}],
|
}],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.2.0",
|
"php": ">=7.4.0",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"psr/log": "^1.0.1 || ^2.0 || ^3.0",
|
"psr/log": "^1.0.1 || ^2.0 || ^3.0",
|
||||||
"masterminds/html5": "^2.7"
|
"masterminds/html5": "^2.7"
|
||||||
|
|||||||
+2
-2
@@ -23,9 +23,9 @@ return static function (RectorConfig $rectorConfig): void {
|
|||||||
|
|
||||||
// Define what rule sets will be applied
|
// Define what rule sets will be applied
|
||||||
$rectorConfig->sets([
|
$rectorConfig->sets([
|
||||||
LevelSetList::UP_TO_PHP_72,
|
LevelSetList::UP_TO_PHP_74,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// is your PHP version different from the one your refactor to?
|
// is your PHP version different from the one your refactor to?
|
||||||
$rectorConfig->phpVersion(PhpVersion::PHP_72);
|
$rectorConfig->phpVersion(PhpVersion::PHP_74);
|
||||||
};
|
};
|
||||||
|
|||||||
+68
-57
@@ -142,7 +142,7 @@ class Readability implements LoggerAwareInterface
|
|||||||
* @param string $parser Which parser to use for turning raw HTML into a DOMDocument
|
* @param string $parser Which parser to use for turning raw HTML into a DOMDocument
|
||||||
* @param bool $useTidy Use tidy
|
* @param bool $useTidy Use tidy
|
||||||
*/
|
*/
|
||||||
public function __construct(string $html, string $url = null, string $parser = 'libxml', bool $useTidy = true)
|
public function __construct(string $html, ?string $url = null, string $parser = 'libxml', bool $useTidy = true)
|
||||||
{
|
{
|
||||||
$this->url = $url;
|
$this->url = $url;
|
||||||
$this->html = $html;
|
$this->html = $html;
|
||||||
@@ -302,8 +302,7 @@ class Readability implements LoggerAwareInterface
|
|||||||
$articleLinks = $articleContent->getElementsByTagName('a');
|
$articleLinks = $articleContent->getElementsByTagName('a');
|
||||||
$linkCount = 0;
|
$linkCount = 0;
|
||||||
|
|
||||||
for ($i = 0; $i < $articleLinks->length; ++$i) {
|
foreach ($articleLinks as $articleLink) {
|
||||||
$articleLink = $articleLinks->item($i);
|
|
||||||
$footnoteLink = $articleLink->cloneNode(true);
|
$footnoteLink = $articleLink->cloneNode(true);
|
||||||
$refLink = $this->dom->createElement('a');
|
$refLink = $this->dom->createElement('a');
|
||||||
$footnote = $this->dom->createElement('li');
|
$footnote = $this->dom->createElement('li');
|
||||||
@@ -383,8 +382,8 @@ class Readability implements LoggerAwareInterface
|
|||||||
|
|
||||||
// Remove service data-candidate attribute.
|
// Remove service data-candidate attribute.
|
||||||
$elems = $xpath->query('.//*[@data-candidate]', $articleContent);
|
$elems = $xpath->query('.//*[@data-candidate]', $articleContent);
|
||||||
for ($i = $elems->length - 1; $i >= 0; --$i) {
|
foreach ($elems as $elem) {
|
||||||
$elems->item($i)->removeAttribute('data-candidate');
|
$elem->removeAttribute('data-candidate');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean out junk from the article content.
|
// Clean out junk from the article content.
|
||||||
@@ -520,11 +519,12 @@ class Readability implements LoggerAwareInterface
|
|||||||
$textLength = mb_strlen($this->getInnerText($e, true, true));
|
$textLength = mb_strlen($this->getInnerText($e, true, true));
|
||||||
$linkLength = 0;
|
$linkLength = 0;
|
||||||
|
|
||||||
for ($dRe = $this->domainRegExp, $i = 0, $il = $links->length; $i < $il; ++$i) {
|
$dRe = $this->domainRegExp;
|
||||||
if ($excludeExternal && $dRe && !preg_match($dRe, $links->item($i)->getAttribute('href'))) {
|
foreach ($links as $link) {
|
||||||
|
if ($excludeExternal && $dRe && !preg_match($dRe, $link->getAttribute('href'))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$linkLength += mb_strlen($this->getInnerText($links->item($i)));
|
$linkLength += mb_strlen($this->getInnerText($link));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($textLength > 0 && $linkLength > 0) {
|
if ($textLength > 0 && $linkLength > 0) {
|
||||||
@@ -586,7 +586,7 @@ class Readability implements LoggerAwareInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Then check the elements inside this element for the same.
|
// Then check the elements inside this element for the same.
|
||||||
if (preg_match($this->regexps['media'], $targetList->item($y)->getInnerHTML())) {
|
if (preg_match($this->regexps['media'], $currentItem->getInnerHTML())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -640,15 +640,15 @@ class Readability implements LoggerAwareInterface
|
|||||||
$embedCount = 0;
|
$embedCount = 0;
|
||||||
$embeds = $node->getElementsByTagName('embed');
|
$embeds = $node->getElementsByTagName('embed');
|
||||||
|
|
||||||
for ($ei = 0, $il = $embeds->length; $ei < $il; ++$ei) {
|
foreach ($embeds as $embed) {
|
||||||
if (preg_match($this->regexps['media'], $embeds->item($ei)->getAttribute('src'))) {
|
if (preg_match($this->regexps['media'], $embed->getAttribute('src'))) {
|
||||||
++$embedCount;
|
++$embedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$embeds = $node->getElementsByTagName('iframe');
|
$embeds = $node->getElementsByTagName('iframe');
|
||||||
for ($ei = 0, $il = $embeds->length; $ei < $il; ++$ei) {
|
foreach ($embeds as $embed) {
|
||||||
if (preg_match($this->regexps['media'], $embeds->item($ei)->getAttribute('src'))) {
|
if (preg_match($this->regexps['media'], $embed->getAttribute('src'))) {
|
||||||
++$embedCount;
|
++$embedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -668,10 +668,10 @@ class Readability implements LoggerAwareInterface
|
|||||||
$this->logger->debug(' content length less than 6 chars, 0 embeds and either 0 images or more than 2 images');
|
$this->logger->debug(' content length less than 6 chars, 0 embeds and either 0 images or more than 2 images');
|
||||||
$toRemove = true;
|
$toRemove = true;
|
||||||
} elseif (!$isList && $weight < 25 && $linkDensity > 0.25) {
|
} elseif (!$isList && $weight < 25 && $linkDensity > 0.25) {
|
||||||
$this->logger->debug(' weight is ' . $weight . ' < 25 and link density is ' . sprintf('%.2f', $linkDensity) . ' > 0.25');
|
$this->logger->debug(' weight is ' . $weight . ' < 25 and link density is ' . \sprintf('%.2f', $linkDensity) . ' > 0.25');
|
||||||
$toRemove = true;
|
$toRemove = true;
|
||||||
} elseif ($a > 2 && ($weight >= 25 && $linkDensity > 0.5)) {
|
} elseif ($a > 2 && ($weight >= 25 && $linkDensity > 0.5)) {
|
||||||
$this->logger->debug(' more than 2 links and weight is ' . $weight . ' > 25 but link density is ' . sprintf('%.2f', $linkDensity) . ' > 0.5');
|
$this->logger->debug(' more than 2 links and weight is ' . $weight . ' > 25 but link density is ' . \sprintf('%.2f', $linkDensity) . ' > 0.5');
|
||||||
$toRemove = true;
|
$toRemove = true;
|
||||||
} elseif ($embedCount > 3) {
|
} elseif ($embedCount > 3) {
|
||||||
$this->logger->debug(' more than 3 embeds');
|
$this->logger->debug(' more than 3 embeds');
|
||||||
@@ -691,10 +691,10 @@ class Readability implements LoggerAwareInterface
|
|||||||
$this->logger->debug(' content length less than 10 chars and 0 images, or more than 2 images');
|
$this->logger->debug(' content length less than 10 chars and 0 images, or more than 2 images');
|
||||||
$toRemove = true;
|
$toRemove = true;
|
||||||
} elseif (!$isList && $weight < 25 && $linkDensity > 0.2) {
|
} elseif (!$isList && $weight < 25 && $linkDensity > 0.2) {
|
||||||
$this->logger->debug(' weight is ' . $weight . ' lower than 0 and link density is ' . sprintf('%.2f', $linkDensity) . ' > 0.2');
|
$this->logger->debug(' weight is ' . $weight . ' lower than 0 and link density is ' . \sprintf('%.2f', $linkDensity) . ' > 0.2');
|
||||||
$toRemove = true;
|
$toRemove = true;
|
||||||
} elseif ($weight >= 25 && $linkDensity > 0.5) {
|
} elseif ($weight >= 25 && $linkDensity > 0.5) {
|
||||||
$this->logger->debug(' weight above 25 but link density is ' . sprintf('%.2f', $linkDensity) . ' > 0.5');
|
$this->logger->debug(' weight above 25 but link density is ' . \sprintf('%.2f', $linkDensity) . ' > 0.5');
|
||||||
$toRemove = true;
|
$toRemove = true;
|
||||||
} elseif ((1 === $embedCount && $contentLength < 75) || $embedCount > 1) {
|
} elseif ((1 === $embedCount && $contentLength < 75) || $embedCount > 1) {
|
||||||
$this->logger->debug(' 1 embed and content length smaller than 75 chars, or more than one embed');
|
$this->logger->debug(' 1 embed and content length smaller than 75 chars, or more than one embed');
|
||||||
@@ -719,8 +719,9 @@ class Readability implements LoggerAwareInterface
|
|||||||
$headers = $e->getElementsByTagName('h' . $headerIndex);
|
$headers = $e->getElementsByTagName('h' . $headerIndex);
|
||||||
|
|
||||||
for ($i = $headers->length - 1; $i >= 0; --$i) {
|
for ($i = $headers->length - 1; $i >= 0; --$i) {
|
||||||
if ($this->getWeight($headers->item($i)) < 0 || $this->getLinkDensity($headers->item($i)) > 0.33) {
|
$header = $headers->item($i);
|
||||||
$headers->item($i)->parentNode->removeChild($headers->item($i));
|
if ($this->getWeight($header) < 0 || $this->getLinkDensity($header) > 0.33) {
|
||||||
|
$header->parentNode->removeChild($header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -739,7 +740,7 @@ class Readability implements LoggerAwareInterface
|
|||||||
*/
|
*/
|
||||||
public function addFlag(int $flag): void
|
public function addFlag(int $flag): void
|
||||||
{
|
{
|
||||||
$this->flags = $this->flags | $flag;
|
$this->flags |= $flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -747,7 +748,7 @@ class Readability implements LoggerAwareInterface
|
|||||||
*/
|
*/
|
||||||
public function removeFlag(int $flag): void
|
public function removeFlag(int $flag): void
|
||||||
{
|
{
|
||||||
$this->flags = $this->flags & ~$flag;
|
$this->flags &= ~$flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -812,12 +813,14 @@ class Readability implements LoggerAwareInterface
|
|||||||
// Remove all style tags in head.
|
// Remove all style tags in head.
|
||||||
$styleTags = $this->dom->getElementsByTagName('style');
|
$styleTags = $this->dom->getElementsByTagName('style');
|
||||||
for ($i = $styleTags->length - 1; $i >= 0; --$i) {
|
for ($i = $styleTags->length - 1; $i >= 0; --$i) {
|
||||||
$styleTags->item($i)->parentNode->removeChild($styleTags->item($i));
|
$styleTag = $styleTags->item($i);
|
||||||
|
$styleTag->parentNode->removeChild($styleTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
$linkTags = $this->dom->getElementsByTagName('link');
|
$linkTags = $this->dom->getElementsByTagName('link');
|
||||||
for ($i = $linkTags->length - 1; $i >= 0; --$i) {
|
for ($i = $linkTags->length - 1; $i >= 0; --$i) {
|
||||||
$linkTags->item($i)->parentNode->removeChild($linkTags->item($i));
|
$linkTag = $linkTags->item($i);
|
||||||
|
$linkTag->parentNode->removeChild($linkTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -893,11 +896,9 @@ class Readability implements LoggerAwareInterface
|
|||||||
* Using a variety of metrics (content score, classname, element types), find the content that is
|
* Using a variety of metrics (content score, classname, element types), find the content that is
|
||||||
* most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
|
* most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
|
||||||
*
|
*
|
||||||
* @param \DOMElement $page
|
|
||||||
*
|
|
||||||
* @return \DOMElement|false
|
* @return \DOMElement|false
|
||||||
*/
|
*/
|
||||||
protected function grabArticle(\DOMElement $page = null)
|
protected function grabArticle(?\DOMElement $page = null)
|
||||||
{
|
{
|
||||||
if (!$page) {
|
if (!$page) {
|
||||||
$page = $this->dom;
|
$page = $this->dom;
|
||||||
@@ -933,9 +934,9 @@ class Readability implements LoggerAwareInterface
|
|||||||
// Remove unlikely candidates
|
// Remove unlikely candidates
|
||||||
$unlikelyMatchString = $node->getAttribute('class') . ' ' . $node->getAttribute('id') . ' ' . $node->getAttribute('style');
|
$unlikelyMatchString = $node->getAttribute('class') . ' ' . $node->getAttribute('id') . ' ' . $node->getAttribute('style');
|
||||||
|
|
||||||
if (mb_strlen($unlikelyMatchString) > 3 && // don't process "empty" strings
|
if (mb_strlen($unlikelyMatchString) > 3 // don't process "empty" strings
|
||||||
preg_match($this->regexps['unlikelyCandidates'], $unlikelyMatchString) &&
|
&& preg_match($this->regexps['unlikelyCandidates'], $unlikelyMatchString)
|
||||||
!preg_match($this->regexps['okMaybeItsACandidate'], $unlikelyMatchString)
|
&& !preg_match($this->regexps['okMaybeItsACandidate'], $unlikelyMatchString)
|
||||||
) {
|
) {
|
||||||
$this->logger->debug('Removing unlikely candidate (using conf) ' . $node->getNodePath() . ' by "' . $unlikelyMatchString . '"');
|
$this->logger->debug('Removing unlikely candidate (using conf) ' . $node->getNodePath() . ' by "' . $unlikelyMatchString . '"');
|
||||||
$node->parentNode->removeChild($node);
|
$node->parentNode->removeChild($node);
|
||||||
@@ -1017,15 +1018,15 @@ class Readability implements LoggerAwareInterface
|
|||||||
* A score is determined by things like number of commas, class names, etc.
|
* A score is determined by things like number of commas, class names, etc.
|
||||||
* Maybe eventually link density.
|
* Maybe eventually link density.
|
||||||
*/
|
*/
|
||||||
for ($pt = 0, $scored = \count($nodesToScore); $pt < $scored; ++$pt) {
|
foreach ($nodesToScore as $nodeToScore) {
|
||||||
$ancestors = $this->getAncestors($nodesToScore[$pt], 5);
|
$ancestors = $this->getAncestors($nodeToScore, 5);
|
||||||
|
|
||||||
// No parent node? Move on...
|
// No parent node? Move on...
|
||||||
if (0 === \count($ancestors)) {
|
if (0 === \count($ancestors)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$innerText = $this->getInnerText($nodesToScore[$pt]);
|
$innerText = $this->getInnerText($nodeToScore);
|
||||||
|
|
||||||
// If this paragraph is less than MIN_PARAGRAPH_LENGTH (default:20) characters, don't even count it.
|
// If this paragraph is less than MIN_PARAGRAPH_LENGTH (default:20) characters, don't even count it.
|
||||||
if (mb_strlen($innerText) < self::MIN_PARAGRAPH_LENGTH) {
|
if (mb_strlen($innerText) < self::MIN_PARAGRAPH_LENGTH) {
|
||||||
@@ -1078,11 +1079,6 @@ class Readability implements LoggerAwareInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$candidates = $xpath->query('.//*[not(self::body) and (@class or @id or @style) and ((number(@readability) < 40) or not(@readability))]', $page->documentElement);
|
|
||||||
|
|
||||||
for ($c = $candidates->length - 1; $c >= 0; --$c) {
|
|
||||||
$node = $candidates->item($c);
|
|
||||||
}
|
|
||||||
unset($candidates);
|
unset($candidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1120,9 +1116,11 @@ class Readability implements LoggerAwareInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$topCandidates = array_filter($topCandidates, function ($v, $idx) {
|
$topCandidates = array_filter(
|
||||||
return 0 === $idx || null !== $v;
|
$topCandidates,
|
||||||
}, \ARRAY_FILTER_USE_BOTH);
|
fn ($v, $idx) => 0 === $idx || null !== $v,
|
||||||
|
\ARRAY_FILTER_USE_BOTH
|
||||||
|
);
|
||||||
$topCandidate = $topCandidates[0];
|
$topCandidate = $topCandidates[0];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1231,11 +1229,6 @@ class Readability implements LoggerAwareInterface
|
|||||||
$parentOfTopCandidate = $topCandidate->parentNode;
|
$parentOfTopCandidate = $topCandidate->parentNode;
|
||||||
$siblingNodes = $parentOfTopCandidate->childNodes;
|
$siblingNodes = $parentOfTopCandidate->childNodes;
|
||||||
|
|
||||||
if (0 === $siblingNodes->length) {
|
|
||||||
$siblingNodes = new \stdClass();
|
|
||||||
$siblingNodes->length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($s = 0, $sl = $siblingNodes->length; $s < $sl; ++$s) {
|
for ($s = 0, $sl = $siblingNodes->length; $s < $sl; ++$s) {
|
||||||
$siblingNode = $siblingNodes->item($s);
|
$siblingNode = $siblingNodes->item($s);
|
||||||
$siblingNodeName = $siblingNode->nodeName;
|
$siblingNodeName = $siblingNode->nodeName;
|
||||||
@@ -1442,7 +1435,7 @@ class Readability implements LoggerAwareInterface
|
|||||||
libxml_use_internal_errors(false);
|
libxml_use_internal_errors(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->dom->registerNodeClass(\DOMElement::class, \Readability\JSLikeHTMLElement::class);
|
$this->dom->registerNodeClass(\DOMElement::class, JSLikeHTMLElement::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getAncestors(\DOMElement $node, int $maxDepth = 0): array
|
private function getAncestors(\DOMElement $node, int $maxDepth = 0): array
|
||||||
@@ -1464,21 +1457,38 @@ class Readability implements LoggerAwareInterface
|
|||||||
{
|
{
|
||||||
return \XML_TEXT_NODE === $node->nodeType
|
return \XML_TEXT_NODE === $node->nodeType
|
||||||
|| \in_array(strtoupper($node->nodeName), $this->phrasingElements, true)
|
|| \in_array(strtoupper($node->nodeName), $this->phrasingElements, true)
|
||||||
|| (\in_array(strtoupper($node->nodeName), ['A', 'DEL', 'INS'], true) && !\in_array(false, array_map(function ($c) {
|
|| (
|
||||||
return $this->isPhrasingContent($c);
|
\in_array(strtoupper($node->nodeName), ['A', 'DEL', 'INS'], true)
|
||||||
}, iterator_to_array($node->childNodes)), true));
|
&& !\in_array(
|
||||||
|
false,
|
||||||
|
array_map(
|
||||||
|
fn ($c) => $this->isPhrasingContent($c),
|
||||||
|
iterator_to_array($node->childNodes)
|
||||||
|
),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if `$node` has only whitespace and a single element with `$tag` for the tag name.
|
||||||
|
* Returns false if `$node` contains non-empty text nodes
|
||||||
|
* or if it contains no element with given tag or more than 1 element.
|
||||||
|
*/
|
||||||
private function hasSingleTagInsideElement(\DOMElement $node, string $tag): bool
|
private function hasSingleTagInsideElement(\DOMElement $node, string $tag): bool
|
||||||
{
|
{
|
||||||
if (1 !== $node->childNodes->length || $node->childNodes->item(0)->nodeName !== $tag) {
|
$childNodes = iterator_to_array($node->childNodes);
|
||||||
|
$children = array_filter($childNodes, fn ($childNode) => $childNode instanceof \DOMElement);
|
||||||
|
|
||||||
|
// There should be exactly 1 element child with given tag
|
||||||
|
if (1 !== \count($children) || $children[0]->nodeName !== $tag) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$a = array_filter(iterator_to_array($node->childNodes), function ($childNode) {
|
$a = array_filter(
|
||||||
return $childNode instanceof \DOMText &&
|
$childNodes,
|
||||||
preg_match($this->regexps['hasContent'], $this->getInnerText($childNode));
|
fn ($childNode) => $childNode instanceof \DOMText && preg_match($this->regexps['hasContent'], $this->getInnerText($childNode))
|
||||||
});
|
);
|
||||||
|
|
||||||
return 0 === \count($a);
|
return 0 === \count($a);
|
||||||
}
|
}
|
||||||
@@ -1491,9 +1501,10 @@ class Readability implements LoggerAwareInterface
|
|||||||
*/
|
*/
|
||||||
private function isNodeVisible(\DOMElement $node): bool
|
private function isNodeVisible(\DOMElement $node): bool
|
||||||
{
|
{
|
||||||
return !($node->hasAttribute('style')
|
return !(
|
||||||
&& preg_match($this->regexps['isNotVisible'], $node->getAttribute('style'))
|
$node->hasAttribute('style')
|
||||||
|
&& preg_match($this->regexps['isNotVisible'], $node->getAttribute('style'))
|
||||||
)
|
)
|
||||||
&& !$node->hasAttribute('hidden');
|
&& !$node->hasAttribute('hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -335,12 +335,12 @@ class ReadabilityTest extends \PHPUnit\Framework\TestCase
|
|||||||
|
|
||||||
public function testAutoClosingIframeNotThrowingException(): void
|
public function testAutoClosingIframeNotThrowingException(): void
|
||||||
{
|
{
|
||||||
$oldErrorReporting = error_reporting(\E_ALL | \E_STRICT);
|
$oldErrorReporting = error_reporting(\E_ALL);
|
||||||
$oldDisplayErrors = ini_set('display_errors', '1');
|
$oldDisplayErrors = ini_set('display_errors', '1');
|
||||||
// dummy function to be used to the next test
|
// dummy function to be used to the next test
|
||||||
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline, array $errcontext) {
|
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||||
throw new \Exception($errstr, $errno);
|
throw new \Exception($errstr, $errno);
|
||||||
}, \E_ALL | \E_STRICT);
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$data = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
$data = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
@@ -550,7 +550,7 @@ class ReadabilityTest extends \PHPUnit\Framework\TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getReadability(string $html, string $url = null, string $parser = 'libxml', bool $useTidy = true): Readability
|
private function getReadability(string $html, ?string $url = null, string $parser = 'libxml', bool $useTidy = true): Readability
|
||||||
{
|
{
|
||||||
$readability = new Readability($html, $url, $parser, $useTidy);
|
$readability = new Readability($html, $url, $parser, $useTidy);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user