Skip to content

Commit b4e189a

Browse files
committed
[+]: work on phpstan reported issues :)
... before a new release goes live.
1 parent 9eec15b commit b4e189a

24 files changed

Lines changed: 418 additions & 271 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,9 @@ jobs:
7474
php vendor/bin/phpunit -c phpunit.xml --coverage-clover=build/logs/clover.xml
7575
7676
- name: Run phpstan
77-
continue-on-error: true
7877
if: ${{ matrix.php == '8.3' }}
7978
run: |
80-
php vendor/bin/phpstan analyse
79+
php vendor/bin/phpstan analyse -c phpstan.neon --no-progress
8180
8281
- name: Upload coverage results to Coveralls
8382
continue-on-error: true

CHANGELOG

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
[PHP Simple HTML Dom - upcoming]
22

3-
[PHP Simple HTML Dom v5.0.0 - 2026-04-21]
3+
[PHP Simple HTML Dom v5.0.0 - upcoming]
44
1: BREAKING: require PHP >= 7.1.0 and drop PHP 7.0 support
5-
2: BREAKING / compatibility: nested "find*()" calls now return live, scoped nodes from the original DOM; mutating nested results (e.g. via "delete()" / "remove()") now changes the source document instead of a detached lookup result
6-
3: supported / tested PHP versions now cover PHP 7.1 - 8.4
7-
4: update runtime selector support to "symfony/css-selector": ~3.0 || ~4.0 || ~5.0 || ~6.0 || ~7.0 || ~8.0
8-
5: update dev / CI tooling for newer runtimes, including PHPUnit 8 support, modern phpunit.xml coverage config, and refreshed GitHub Actions / Travis matrices
9-
6: "SelectorConverter" -> add support for leading combinators (>, +, ~) and compound text / comment selectors, and improve selector cache / validation handling
10-
7: "HtmlDomParser" / "AbstractDomParser" -> replace invalid helper placeholder element names and fix PHP < 8 node-backed serialization / libxml newline issues, including script placeholder handling
11-
8: "HtmlDomParser" -> improve plaintext extraction (exclude style-comment content, simplify XPath handling, use array + implode())
12-
9: add "remove()" + null-returning query helpers ("findOneOrNull()" / "findMultiOrNull()") and fix nested / blank helper behavior
13-
10: fix nested XPath scoping, nested delete / serialization edge cases, paragraph wrapper handling, and empty SVG data uris inside style tags
14-
11: add broader regression coverage for Blade directives / conditionals, XML / RSS parsing, selector edge cases, serialization, PHPUnit 8 assertions, and PHP 8.5-safe reflection usage
15-
12: simplify export usage to "findMulti() + setAttribute()"
16-
13: fix -> preserve attributes and support nested node renames in "SimpleHtmlDom" / "SimpleXmlDom"
5+
2: BREAKING / compatibility: nested "find*()" now returns live scoped nodes, so mutating nested results via "delete()" / "remove()" updates the original DOM
6+
3: add support for "symfony/css-selector": ~8.0
7+
4: add "findOneOrNull()" + "findMultiOrNull()"
8+
5: add "remove()" (alias) and update public API docs in "README_API.md"
9+
6: "SelectorConverter" -> add support for leading combinators (>, +, ~) and text / comment selectors
10+
7: "HtmlDomParser" -> fix PHP < 8 node serialization, script placeholder handling, plaintext extraction, paragraph wrapper handling, and empty SVG data uris inside style tags
11+
8: "HtmlDomParser" / "XmlDomParser" -> support "\DOMDocument" input and reject directory paths in "*load*File()"
12+
9: "SimpleHtmlDom" / "SimpleXmlDom" -> fix "val()" for "<select>", multi-select arrays, and checkbox / radio arrays
13+
10: "SimpleXmlDom" -> fix "nextNonWhitespaceSibling()" and "parentNode()" edge cases
14+
11: fix nested XPath scoping, nested delete / serialization, and nested node rename handling
15+
12: refresh CI / PHPUnit / PHPStan setup and expand regression coverage
1716

1817
[PHP Simple HTML Dom v4.8.10 - 2024-07-03]
1918
1: fix -> "HtmlDomHelper::mergeHtmlAttributes()" with zero values

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
A HTML DOM parser written in PHP - let you manipulate HTML in a very easy way!
1313
This is a fork of [PHP Simple HTML DOM Parser project](http://simplehtmldom.sourceforge.net/) but instead of string manipulation we use DOMDocument and modern php classes like "Symfony CssSelector".
1414

15-
- PHP 7.1+ & 8.x Support
15+
- PHP 7.1+ runtime support, tested on PHP 7.1 - 8.4
1616
- PHP-FIG Standard
1717
- Composer & PSR-4 support
1818
- PHPUnit testing via GitHub Actions
19+
- PHPStan-clean source tree on the current release branch
1920
- PHP-Quality testing via SensioLabsInsight
2021
- UTF-8 Support (more support via "voku/portable-utf8")
2122
- Invalid HTML Support (partly ...)
@@ -30,6 +31,12 @@ composer require voku/simple_html_dom
3031
composer require voku/portable-utf8 # if you need e.g. UTF-8 fixed output
3132
```
3233

34+
### Upgrade notes for v5.0.0
35+
36+
- PHP 7.0 is no longer supported; the package now requires PHP 7.1 or newer.
37+
- Nested `find*()` calls now return live nodes scoped to the original DOM, so mutating nested results updates the source document.
38+
- See the [CHANGELOG](https://github.com/voku/simple_html_dom/blob/master/CHANGELOG) for the full release notes.
39+
3340
### Quick Start
3441

3542
```php

README_API.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[//]: # (AUTO-GENERATED BY "PHP README Helper": base file -> docs/api.md)
1+
[//]: # (AUTO-GENERATED BY "PHP README Helper": base file -> build/docs/api.md)
22
# :scroll: Simple Html Dom Parser for PHP
33

44
### DomParser API
@@ -31,12 +31,12 @@
3131
### SimpleHtmlDomNode (group of dom elements) API
3232

3333
<p id="voku-php-readme-class-methods"></p><table><tr><td><a href="#count-int">count</a>
34-
</td><td><a href="#findstring-selector-int-idx-simplehtmldomnodesimplehtmldomnodenull">find</a>
34+
</td><td><a href="#findstring-selector-int-idx-simplehtmldominterfacesimplehtmldomnodeinterfacesimplehtmldominterfacenull">find</a>
3535
</td><td><a href="#findmultistring-selector-simplehtmldominterfacesimplehtmldomnodeinterfacesimplehtmldominterface">findMulti</a>
3636
</td><td><a href="#findmultiorfalsestring-selector-falsesimplehtmldominterfacesimplehtmldomnodeinterfacesimplehtmldominterface">findMultiOrFalse</a>
3737
</td></tr><tr><td><a href="#findmultiornullstring-selector-nullsimplehtmldominterfacesimplehtmldomnodeinterfacesimplehtmldominterface">findMultiOrNull</a>
38-
</td><td><a href="#findonestring-selector-simplehtmldomnodeinterface">findOne</a>
39-
</td><td><a href="#findoneorfalsestring-selector-falsesimplehtmldomnodeinterface">findOneOrFalse</a>
38+
</td><td><a href="#findonestring-selector-simplehtmldominterfacesimplehtmldomnodeinterfacesimplehtmldominterface">findOne</a>
39+
</td><td><a href="#findoneorfalsestring-selector-falsesimplehtmldominterface">findOneOrFalse</a>
4040
</td><td><a href="#findoneornullstring-selector-nullsimplehtmldominterface">findOneOrNull</a>
4141
</td></tr><tr><td><a href="#getiterator">getIterator</a>
4242
</td><td><a href="#innerhtml-string">innerHtml</a>
@@ -48,7 +48,7 @@
4848
### SimpleHtmlDom (single dom element) API
4949

5050
<p id="voku-php-readme-class-methods"></p><table><tr><td><a href="#childnodesint-idx-simplehtmldominterfacesimplehtmldominterfacesimplehtmldomnodeinterfacenull">childNodes</a>
51-
</td><td><a href="#delete-mixed">delete</a>
51+
</td><td><a href="#delete-void">delete</a>
5252
</td><td><a href="#findstring-selector-intnull-idx-simplehtmldominterfacesimplehtmldominterfacesimplehtmldomnodeinterfacesimplehtmldominterface">find</a>
5353
</td><td><a href="#findmultistring-selector-simplehtmldominterfacesimplehtmldomnodeinterfacesimplehtmldominterface">findMulti</a>
5454
</td></tr><tr><td><a href="#findmultiorfalsestring-selector-falsesimplehtmldominterfacesimplehtmldomnodeinterfacesimplehtmldominterface">findMultiOrFalse</a>
@@ -79,7 +79,7 @@
7979
</td><td><a href="#parentnode-simplehtmldominterfacenull">parentNode</a>
8080
</td><td><a href="#previousnonwhitespacesibling-simplehtmldominterfacenull">previousNonWhitespaceSibling</a>
8181
</td><td><a href="#previoussibling-simplehtmldominterfacenull">previousSibling</a>
82-
</td></tr><tr><td><a href="#remove-mixed">remove</a>
82+
</td></tr><tr><td><a href="#remove-void">remove</a>
8383
</td><td><a href="#removeattributestring-name-simplehtmldominterface">removeAttribute</a>
8484
</td><td><a href="#removeattributes-simplehtmldominterface">removeAttributes</a>
8585
</td><td><a href="#setattributestring-name-stringnull-value-bool-strictemptyvaluecheck-simplehtmldominterface">setAttribute</a>
@@ -390,7 +390,7 @@ __nothing__
390390

391391
--------
392392

393-
## find(string $selector, int $idx): SimpleHtmlDomNode|\SimpleHtmlDomNode[]|null
393+
## find(string $selector, int $idx): SimpleHtmlDomInterface|\SimpleHtmlDomNodeInterface<\SimpleHtmlDomInterface>|null
394394
<a href="#voku-php-readme-class-methods">↑</a>
395395
Find list of nodes with a CSS selector.
396396

@@ -399,7 +399,7 @@ Find list of nodes with a CSS selector.
399399
- `int $idx`
400400

401401
**Return:**
402-
- `\SimpleHtmlDomNode|\SimpleHtmlDomNode[]|null`
402+
- `\SimpleHtmlDomInterface|\SimpleHtmlDomNodeInterface<\SimpleHtmlDomInterface>|null`
403403

404404
--------
405405

@@ -439,27 +439,27 @@ Find nodes with a CSS selector or null, if no element is found.
439439

440440
--------
441441

442-
## findOne(string $selector): SimpleHtmlDomNodeInterface
442+
## findOne(string $selector): SimpleHtmlDomInterface|\SimpleHtmlDomNodeInterface<\SimpleHtmlDomInterface>
443443
<a href="#voku-php-readme-class-methods">↑</a>
444444
Find one node with a CSS selector.
445445

446446
**Parameters:**
447447
- `string $selector`
448448

449449
**Return:**
450-
- `\SimpleHtmlDomNodeInterface`
450+
- `\SimpleHtmlDomInterface|\SimpleHtmlDomNodeInterface<\SimpleHtmlDomInterface>`
451451

452452
--------
453453

454-
## findOneOrFalse(string $selector): false|\SimpleHtmlDomNodeInterface
454+
## findOneOrFalse(string $selector): false|\SimpleHtmlDomInterface
455455
<a href="#voku-php-readme-class-methods">↑</a>
456456
Find one node with a CSS selector or false, if no element is found.
457457

458458
**Parameters:**
459459
- `string $selector`
460460

461461
**Return:**
462-
- `false|\SimpleHtmlDomNodeInterface`
462+
- `false|\SimpleHtmlDomInterface`
463463

464464
--------
465465

@@ -548,15 +548,15 @@ Returns children of node.
548548

549549
--------
550550

551-
## delete(): mixed
551+
## delete(): void
552552
<a href="#voku-php-readme-class-methods">↑</a>
553-
Delete
553+
Remove this node from the DOM.
554554

555555
**Parameters:**
556556
__nothing__
557557

558558
**Return:**
559-
- `mixed`
559+
- `void`
560560

561561
--------
562562

@@ -929,15 +929,15 @@ __nothing__
929929

930930
--------
931931

932-
## remove(): mixed
932+
## remove(): void
933933
<a href="#voku-php-readme-class-methods">↑</a>
934934
Remove this node from the DOM (alias for delete).
935935

936936
**Parameters:**
937937
__nothing__
938938

939939
**Return:**
940-
- `mixed`
940+
- `void`
941941

942942
--------
943943

phpstan.neon

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,3 @@ parameters:
33
level: 8
44
paths:
55
- %currentWorkingDirectory%/src/
6-
ignoreErrors:
7-
## todo?
8-
- '#type specified in iterable type#'
9-
- '#Unsafe usage of new static#'
10-
- '#extends generic class#'
11-
- '#return type with generic#'
12-
- '#DOMNode\|null#'
13-
- '#function call_user_func_array expects callable#'
14-
## old stuff
15-
- '#Method voku\\helper\\HtmlDomParser::findOne\(\) should return#'
16-
- '#Method voku\\helper\\HtmlDomParser::findMulti\(\) should return#'
17-
- '#Method voku\\helper\\HtmlDomParser::findOneOrFalse\(\) should return#'
18-
- '#Method voku\\helper\\HtmlDomParser::findMultiOrFalse\(\) should return#'
19-
- '#Method voku\\helper\\SimpleHtmlDomNode::findOne\(\) should return#'
20-
- '#Method voku\\helper\\SimpleHtmlDomNode::findMulti\(\) should return#'
21-
- '#Method voku\\helper\\SimpleHtmlDomNode::findOneOrFalse\(\) should return#'
22-
- '#Method voku\\helper\\SimpleHtmlDomNode::findMultiOrFalse\(\) should return#'
23-
- '#Method voku\\helper\\XmlDomParser::findOne\(\) should return#'
24-
- '#Method voku\\helper\\XmlDomParser::findMulti\(\) should return#'
25-
- '#Method voku\\helper\\XmlDomParser::findOneOrFalse\(\) should return#'
26-
- '#Method voku\\helper\\XmlDomParser::findMultiOrFalse\(\) should return#'
27-
- '#Method voku\\helper\\SimpleXmlDomNode::findOne\(\) should return#'
28-
- '#Method voku\\helper\\SimpleXmlDomNode::findMulti\(\) should return#'
29-
- '#Method voku\\helper\\SimpleXmlDomNode::findOneOrFalse\(\) should return#'
30-
- '#Method voku\\helper\\SimpleXmlDomNode::findMultiOrFalse\(\) should return#'
31-
- '#method voku\\helper\\SimpleHtmlDomNodeBlank::findOne\(\) should be compatible with return type#'

src/voku/helper/AbstractDomParser.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ abstract class AbstractDomParser implements DomParserInterface
2424
protected static $domHtmlSpecialScriptHelper = 'simplevokuspecialscript';
2525

2626
/**
27-
* @var array
27+
* @var array<string, array<int, string>>
2828
*/
2929
protected static $domBrokenReplaceHelper = [];
3030

@@ -59,7 +59,7 @@ abstract class AbstractDomParser implements DomParserInterface
5959
/**
6060
* @var callable|null
6161
*
62-
* @phpstan-var null|callable(\voku\helper\XmlDomParser|\voku\helper\HtmlDomParser): void
62+
* @phpstan-var null|callable(array{0: \voku\helper\XmlDomParser|\voku\helper\HtmlDomParser}): void
6363
*/
6464
protected static $callback;
6565

@@ -125,8 +125,8 @@ protected function registerDynamicDomBrokenReplaceHelper(string $original, strin
125125
protected $encoding = 'UTF-8';
126126

127127
/**
128-
* @param string $name
129-
* @param array $arguments
128+
* @param string $name
129+
* @param array<mixed> $arguments
130130
*
131131
* @return bool|mixed
132132
*/
@@ -135,15 +135,17 @@ public function __call($name, $arguments)
135135
$name = \strtolower($name);
136136

137137
if (isset(self::$functionAliases[$name])) {
138-
return \call_user_func_array([$this, self::$functionAliases[$name]], $arguments);
138+
$method = self::$functionAliases[$name];
139+
140+
return $this->{$method}(...$arguments);
139141
}
140142

141143
throw new \BadMethodCallException('Method does not exist: ' . $name);
142144
}
143145

144146
/**
145-
* @param string $name
146-
* @param array $arguments
147+
* @param string $name
148+
* @param array<mixed> $arguments
147149
*
148150
* @throws \BadMethodCallException
149151
* @throws \RuntimeException
@@ -398,7 +400,7 @@ public function save(string $filepath = ''): string
398400
/**
399401
* @param callable $functionName
400402
*
401-
* @phpstan-param callable(\voku\helper\XmlDomParser|\voku\helper\HtmlDomParser): void $functionName
403+
* @phpstan-param callable(array{0: \voku\helper\XmlDomParser|\voku\helper\HtmlDomParser}): void $functionName
402404
*
403405
* @return void
404406
*/

src/voku/helper/AbstractSimpleHtmlDom.php

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
abstract class AbstractSimpleHtmlDom
88
{
99
/**
10-
* @var array
10+
* @var array<string, string>
1111
*/
1212
protected static $functionAliases = [
1313
'children' => 'childNodes',
@@ -43,8 +43,8 @@ abstract class AbstractSimpleHtmlDom
4343
private $classListCache;
4444

4545
/**
46-
* @param string $name
47-
* @param array $arguments
46+
* @param string $name
47+
* @param array<mixed> $arguments
4848
*
4949
* @throws \BadMethodCallException
5050
*
@@ -55,7 +55,9 @@ public function __call($name, $arguments)
5555
$name = \strtolower($name);
5656

5757
if (isset(self::$functionAliases[$name])) {
58-
return \call_user_func_array([$this, self::$functionAliases[$name]], $arguments);
58+
$method = self::$functionAliases[$name];
59+
60+
return $this->{$method}(...$arguments);
5961
}
6062

6163
throw new \BadMethodCallException('Method does not exist');
@@ -64,7 +66,7 @@ public function __call($name, $arguments)
6466
/**
6567
* @param string $name
6668
*
67-
* @return SimpleHtmlAttributes|string|string[]|null
69+
* @return array<int, string>|SimpleHtmlAttributes|string|null
6870
*/
6971
public function __get($name)
7072
{
@@ -90,7 +92,10 @@ public function __get($name)
9092
return $this->getAllAttributes();
9193
case 'classlist':
9294
if ($this->classListCache === null) {
93-
$this->classListCache = new SimpleHtmlAttributes($this->node ?? null, 'class');
95+
$this->classListCache = new SimpleHtmlAttributes(
96+
$this->node instanceof \DOMElement ? $this->node : null,
97+
'class'
98+
);
9499
}
95100

96101
return $this->classListCache;
@@ -151,24 +156,28 @@ public function __isset($name)
151156
* @param string $name
152157
* @param mixed $value
153158
*
154-
* @return SimpleHtmlDomInterface|null
159+
* @return void
155160
*/
156-
public function __set($name, $value)
161+
public function __set($name, $value): void
157162
{
158163
$nameOrig = $name;
159164
$name = \strtolower($name);
160165

161166
switch ($name) {
162167
case 'outerhtml':
163168
case 'outertext':
164-
return $this->replaceNodeWithString($value);
169+
$this->replaceNodeWithString($value);
170+
return;
165171
case 'innertext':
166172
case 'innerhtml':
167-
return $this->replaceChildWithString($value);
173+
$this->replaceChildWithString($value);
174+
return;
168175
case 'innerhtmlkeep':
169-
return $this->replaceChildWithString($value, false);
176+
$this->replaceChildWithString($value, false);
177+
return;
170178
case 'plaintext':
171-
return $this->replaceTextWithString($value);
179+
$this->replaceTextWithString($value);
180+
return;
172181
case 'classlist':
173182
$name = 'class';
174183
$nameOrig = 'class';
@@ -182,12 +191,11 @@ public function __set($name, $value)
182191

183192
if ($value !== null) {
184193
$this->node->{$nameOrig} = $value;
185-
186-
return $this->node->{$nameOrig};
194+
return;
187195
}
188196
}
189197

190-
return $this->setAttribute($name, $value);
198+
$this->setAttribute($name, $value);
191199
}
192200
}
193201

0 commit comments

Comments
 (0)