Skip to content

Commit

Permalink
Use JSLikeHTMLElement in type hints
Browse files Browse the repository at this point in the history
We use `DOMDocument::registerNodeClass()` to make DOM methods return
`JSLikeHTMLElement` instead of `DOMElement`. Unfortunately, it is not
possible for PHPStan to detect that so we need to cast it ourselves:
phpstan/phpstan#10748
We may want to deprecate it in the future just to get rid of this mess.

This also allows us to get rid of the assertions in tests.
  • Loading branch information
jtojnar committed Oct 11, 2024
1 parent 5758e00 commit d6cd134
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 47 deletions.
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ parameters:
bootstrapFiles:
- vendor/bin/.phpunit/phpunit/vendor/autoload.php

stubFiles:
- stubs/dom.stub

includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
12 changes: 6 additions & 6 deletions src/Readability.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ class Readability implements LoggerAwareInterface
public $revertForcedParagraphElements = false;

/**
* @var ?\DOMElement
* @var ?JSLikeHTMLElement
*/
public $articleTitle;

/**
* @var ?\DOMElement
* @var ?JSLikeHTMLElement
*/
public $articleContent;

Expand Down Expand Up @@ -245,7 +245,7 @@ public function setLogger(LoggerInterface $logger): void
/**
* Get article title element.
*
* @return \DOMElement
* @return JSLikeHTMLElement
*/
public function getTitle()
{
Expand All @@ -259,7 +259,7 @@ public function getTitle()
/**
* Get article content element.
*
* @return \DOMElement
* @return JSLikeHTMLElement
*/
public function getContent()
{
Expand Down Expand Up @@ -447,7 +447,7 @@ public function addFootnotes(\DOMElement $articleContent): void
*/
public function prepArticle(\DOMNode $articleContent): void
{
if (!$articleContent instanceof \DOMElement) {
if (!$articleContent instanceof JSLikeHTMLElement) {
return;
}

Expand Down Expand Up @@ -644,7 +644,7 @@ public function getWeight(\DOMElement $e): int
/**
* Remove extraneous break tags from a node.
*/
public function killBreaks(\DOMElement $node): void
public function killBreaks(JSLikeHTMLElement $node): void
{
$html = $node->getInnerHTML();
$html = preg_replace($this->regexps['killBreaks'], '<br />', $html);
Expand Down
36 changes: 36 additions & 0 deletions stubs/dom.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

// SPDX-FileCopyrightText: 2022 Ondřej Mirtes
// SPDX-License-Identifier: MIT
// Based on https://github.com/phpstan/phpstan-src/blob/b2a9ba4b82d19b01f37eb983746f1840f1213851/stubs/dom.stub

use Readability\JSLikeHTMLElement;

class DOMDocument
{
/** @var JSLikeHTMLElement|null */
public $documentElement;

/** @var null */
public $ownerDocument;

/**
* @param string $name
* @return DOMNodeList<JSLikeHTMLElement>
*/
public function getElementsByTagName($name) {}
}

class DOMNode
{

}

class DOMElement extends DOMNode
{
/**
* @param string $name
* @return DOMNodeList<JSLikeHTMLElement>
*/
public function getElementsByTagName($name) {}
}
41 changes: 0 additions & 41 deletions tests/ReadabilityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Monolog\Handler\TestHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use Readability\JSLikeHTMLElement;
use Readability\Readability;

class ReadabilityTest extends \PHPUnit\Framework\TestCase
Expand Down Expand Up @@ -80,8 +79,6 @@ public function testInitNoContent(): void
$res = $readability->init();

$this->assertFalse($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('Sorry, Readability was unable to parse this page for content.', $readability->getContent()->getInnerHtml());
}
Expand All @@ -92,8 +89,6 @@ public function testInitP(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is the awesome content :)', $readability->getContent()->getInnerHtml());
Expand All @@ -105,8 +100,6 @@ public function testInitDivP(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is the awesome content :)', $readability->getContent()->getInnerHtml());
Expand All @@ -119,8 +112,6 @@ public function testInitDiv(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is the awesome content :)', $readability->getContent()->getInnerHtml());
Expand All @@ -134,8 +125,6 @@ public function testWithFootnotes(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
Expand All @@ -151,8 +140,6 @@ public function testStandardClean(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
Expand All @@ -167,8 +154,6 @@ public function testWithIframe(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<div readability=', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
Expand All @@ -182,8 +167,6 @@ public function testWithArticle(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('alt="article"', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
Expand All @@ -197,8 +180,6 @@ public function testWithAside(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('<aside>', $readability->getContent()->getInnerHtml());
Expand All @@ -212,8 +193,6 @@ public function testWithClasses(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('alt="article"', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
Expand All @@ -228,8 +207,6 @@ public function testWithClassesWithoutLightClean(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('alt="article"', $readability->getContent()->getInnerHtml());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
Expand All @@ -243,8 +220,6 @@ public function testWithTd(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
}
Expand All @@ -256,8 +231,6 @@ public function testWithSameClasses(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
Expand All @@ -270,8 +243,6 @@ public function testWithScript(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertEmpty($readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
Expand All @@ -284,8 +255,6 @@ public function testTitle(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertSame('this is my title', $readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
Expand All @@ -298,8 +267,6 @@ public function testTitleWithDash(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertSame('title2 - title3', $readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
Expand All @@ -312,8 +279,6 @@ public function testTitleWithDoubleDot(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertSame('title2 : title3', $readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
Expand All @@ -326,8 +291,6 @@ public function testTitleTooShortUseH1(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertSame('this is my h1 title !', $readability->getTitle()->getInnerHtml());
$this->assertStringContainsString('This is an awesome text with some links, here there are', $readability->getContent()->getInnerHtml());
$this->assertStringNotContainsString('This text is also an awesome text and you should know that', $readability->getContent()->getInnerHtml());
Expand Down Expand Up @@ -374,8 +337,6 @@ public function testAutoClosingIframeNotThrowingException(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
$this->assertStringContainsString('<iframe src="https://www.youtube.com/embed/PUep6xNeKjA" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"> </iframe>', $readability->getContent()->getInnerHtml());
$this->assertStringContainsString('3D Touch', $readability->getTitle()->getInnerHtml());
} finally {
Expand Down Expand Up @@ -442,8 +403,6 @@ public function testAppendIdAlreadyHere(): void
$res = $readability->init();

$this->assertTrue($res);
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getContent());
$this->assertInstanceOf(JSLikeHTMLElement::class, $readability->getTitle());
}

public function testPostFilters(): void
Expand Down

0 comments on commit d6cd134

Please sign in to comment.