diff --git a/.gitattributes b/.gitattributes
index 3d291a02..aac2f3a4 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,5 +1,6 @@
.gitattributes export-ignore
.gitignore export-ignore
+.github/ export-ignore
.php_cs.dist export-ignore
.scrutinizer.yml export-ignore
.travis.yml export-ignore
diff --git a/.github/workflows/benchmark.yaml b/.github/workflows/benchmark.yaml
new file mode 100644
index 00000000..dd84591a
--- /dev/null
+++ b/.github/workflows/benchmark.yaml
@@ -0,0 +1,36 @@
+name: Benchmarks
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+
+jobs:
+ phpbench:
+ name: "PHPBench"
+ runs-on: "ubuntu-20.04"
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php-version:
+ - "5.4"
+ - "7.4"
+ - "8.3"
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-version }}
+
+ - name: "Install dependencies with Composer"
+ uses: "ramsey/composer-install@v2"
+
+ - name: Run performance tests
+ run: |
+ php test/benchmark/run.php 10
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 00000000..1ae2ed54
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,40 @@
+name: CI
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+
+jobs:
+ phpunit:
+ name: "PHPUnit"
+ runs-on: "ubuntu-20.04"
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php-version:
+ - "5.3"
+ - "5.4"
+ - "7.4"
+ - "8.0"
+ - "8.1"
+ - "8.2"
+ - "8.3"
+ - "8.4"
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-version }}
+
+ - name: "Install dependencies with Composer"
+ uses: "ramsey/composer-install@v2"
+
+ - name: Run tests
+ run: vendor/bin/phpunit
diff --git a/.github/workflows/cs.yaml b/.github/workflows/cs.yaml
new file mode 100644
index 00000000..266666de
--- /dev/null
+++ b/.github/workflows/cs.yaml
@@ -0,0 +1,32 @@
+name: CS
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+
+jobs:
+ php-cs-fixer:
+ name: "php-cs-fixer"
+ runs-on: "ubuntu-20.04"
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php-version:
+ - "7.1"
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-version }}
+
+ - name: cs fix
+ run: |
+ wget -q https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.19.3/php-cs-fixer.phar
+ php php-cs-fixer.phar fix --dry-run --diff
diff --git a/.gitignore b/.gitignore
index 1fa9e586..4e7d4721 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@ scratch.php
composer.lock
build/
.php_cs.cache
+/.phpunit.result.cache
+/php-cs-fixer.phar
diff --git a/.php_cs.dist b/.php_cs.dist
index d5e49183..f6abb593 100644
--- a/.php_cs.dist
+++ b/.php_cs.dist
@@ -7,6 +7,7 @@ $finder = PhpCsFixer\Finder::create()
return PhpCsFixer\Config::create()
->setRules(array(
'@Symfony' => true,
+ 'array_syntax' => false,
'concat_space' => array('spacing' => 'one'),
'phpdoc_annotation_without_dot' => false,
))
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
deleted file mode 100644
index dcdd2047..00000000
--- a/.scrutinizer.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-tools:
- php_mess_detector: true
- php_analyzer:
- config:
- parameter_reference_check: { enabled: false }
- checkstyle: { enabled: false, no_trailing_whitespace: true, naming: { enabled: true, local_variable: '^[a-z][a-zA-Z0-9]*$', abstract_class_name: ^Abstract|Factory$, utility_class_name: 'Utils?$', constant_name: '^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$', property_name: '^[a-z][a-zA-Z0-9]*$', method_name: '^(?:[a-z]|__)[a-zA-Z0-9]*$', parameter_name: '^[a-z][a-zA-Z0-9]*$', interface_name: '^[A-Z][a-zA-Z0-9]*Interface$', type_name: '^[A-Z][a-zA-Z0-9]*$', exception_name: '^[A-Z][a-zA-Z0-9]*Exception$', isser_method_name: '^(?:is|has|should|may|supports)' } }
- unreachable_code: { enabled: false }
- check_access_control: { enabled: false }
- typo_checks: { enabled: false }
- check_variables: { enabled: false }
- check_calls: { enabled: true, too_many_arguments: true, missing_argument: true, argument_type_checks: lenient }
- suspicious_code: { enabled: false, overriding_parameter: false, overriding_closure_use: false, parameter_closure_use_conflict: false, parameter_multiple_times: false, non_existent_class_in_instanceof_check: false, non_existent_class_in_catch_clause: false, assignment_of_null_return: false, non_commented_switch_fallthrough: false, non_commented_empty_catch_block: false, overriding_private_members: false, use_statement_alias_conflict: false, precedence_in_condition_assignment: false }
- dead_assignments: { enabled: false }
- verify_php_doc_comments: { enabled: false, parameters: false, return: false, suggest_more_specific_types: false, ask_for_return_if_not_inferrable: false, ask_for_param_type_annotation: false }
- loops_must_use_braces: { enabled: false }
- check_usage_context: { enabled: true, foreach: { value_as_reference: true, traversable: true } }
- simplify_boolean_return: { enabled: false }
- phpunit_checks: { enabled: false }
- reflection_checks: { enabled: false }
- precedence_checks: { enabled: true, assignment_in_condition: true, comparison_of_bit_result: true }
- basic_semantic_checks: { enabled: false }
- unused_code: { enabled: false }
- deprecation_checks: { enabled: false }
- useless_function_calls: { enabled: false }
- metrics_lack_of_cohesion_methods: { enabled: false }
- metrics_coupling: { enabled: true, stable_code: { namespace_prefixes: { }, classes: { } } }
- doctrine_parameter_binding: { enabled: false }
- doctrine_entity_manager_injection: { enabled: false }
- symfony_request_injection: { enabled: false }
- doc_comment_fixes: { enabled: false }
- reflection_fixes: { enabled: false }
- use_statement_fixes: { enabled: true, remove_unused: true, preserve_multiple: false, preserve_blanklines: false, order_alphabetically: false }
- php_code_sniffer: true
- sensiolabs_security_checker: true
- php_cpd: true
- php_loc: true
- php_pdepend: true
- external_code_coverage: true
-filter:
- paths:
- - src/*
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 7bb8d7ee..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-language: php
-dist: trusty
-sudo: false
-
-matrix:
- include:
- - php: 5.3
- dist: precise
- env: COMPOSER_FLAGS="--prefer-lowest"
- - php: 5.4
- - php: 5.5
- - php: 5.6
- env: SCRUTINIZER=1 PHPUNIT_FLAGS="--coverage-clover=coverage.xml"
- - php: 7.0
- env: COMPOSER_FLAGS="--prefer-lowest"
- - php: 7.1
- env: CS_FIXER=1
- - php: 7.2
- - php: 7.3
- env: BENCHMARK=1
- - php: 7.4snapshot
- fast_finish: true
-
-cache:
- directories:
- - $HOME/.composer/cache
-
-before_script:
- - if [[ SCRUTINIZER != '1' ]]; then phpenv config-rm xdebug.ini || true; fi
- - composer self-update
- - composer update $COMPOSER_FLAGS --prefer-dist
-
-script:
- - stty cols 120
- - if [ "$CS_FIXER" == 1 ]; then wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.13.1/php-cs-fixer.phar && php php-cs-fixer.phar fix --dry-run --diff; fi
- - mkdir -p build/logs
- - ./vendor/bin/phpunit $PHPUNIT_FLAGS
-
-after_script:
- - if [ "$SCRUTINIZER" == 1 ]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.xml; fi
- - if [ "$BENCHMARK" == 1 ]; then php test/benchmark/run.php 10; fi
-
-notifications:
- irc: "irc.freenode.net#masterminds"
diff --git a/README.md b/README.md
index 546d3e24..fb6e5625 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,19 @@
+> # UKRAINE NEEDS YOUR HELP NOW!
+>
+> On 24 February 2022, Russian [President Vladimir Putin ordered an invasion of Ukraine by Russian Armed Forces](https://www.bbc.com/news/world-europe-60504334).
+>
+> Your support is urgently needed.
+>
+> - Donate to the volunteers. Here is the volunteer fund helping the Ukrainian army to provide all the necessary equipment:
+> https://bank.gov.ua/en/news/all/natsionalniy-bank-vidkriv-spetsrahunok-dlya-zboru-koshtiv-na-potrebi-armiyi or https://savelife.in.ua/en/donate/
+> - Triple-check social media sources. Russian disinformation is attempting to coverup and distort the reality in Ukraine.
+> - Help Ukrainian refugees who are fleeing Russian attacks and shellings: https://www.globalcitizen.org/en/content/ways-to-help-ukraine-conflict/
+> - Put pressure on your political representatives to provide help to Ukraine.
+> - Believe in the Ukrainian people, they will not surrender, they don't have another Ukraine.
+>
+> THANK YOU!
+----
+
# HTML5-PHP
HTML5 is a standards-compliant HTML5 parser and writer written entirely in PHP.
@@ -14,7 +30,7 @@ HTML5 provides the following features.
- Interoperability with [QueryPath](https://github.com/technosophos/querypath)
- Runs on **PHP** 5.3.0 or newer
-[](https://travis-ci.org/Masterminds/html5-php)
+[](https://github.com/Masterminds/html5-php/actions/workflows/ci.yaml)
[](https://packagist.org/packages/masterminds/html5)
[](https://scrutinizer-ci.com/g/Masterminds/html5-php/?branch=master)
[](https://scrutinizer-ci.com/g/Masterminds/html5-php/?branch=master)
@@ -173,7 +189,7 @@ issues known issues that are not presently on the roadmap:
- Scripts: This parser does not contain a JavaScript or a CSS
interpreter. While one may be supplied, not all features will be
supported.
-- Rentrance: The current parser is not re-entrant. (Thus you can't pause
+- Reentrance: The current parser is not re-entrant. (Thus you can't pause
the parser to modify the HTML string mid-parse.)
- Validation: The current tree builder is **not** a validating parser.
While it will correct some HTML, it does not check that the HTML
diff --git a/RELEASE.md b/RELEASE.md
index 386c12f1..33007ed6 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -1,5 +1,14 @@
# Release Notes
+2.7.6 (2021-08-18)
+
+- #218: Address comment handling issues
+
+2.7.5 (2021-07-01)
+
+- #204: Travis: Enable tests on PHP 8.0
+- #207: Fix PHP 8.1 deprecations
+
2.7.4 (2020-10-01)
- #191: Fix travisci build
diff --git a/composer.json b/composer.json
index 6e5dddbb..35b4a573 100644
--- a/composer.json
+++ b/composer.json
@@ -20,13 +20,11 @@
}
],
"require" : {
- "ext-ctype": "*",
"ext-dom": "*",
- "ext-libxml" : "*",
"php" : ">=5.3.0"
},
"require-dev": {
- "phpunit/phpunit" : "^4.8.35"
+ "phpunit/phpunit" : "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
},
"autoload": {
"psr-4": {"Masterminds\\": "src"}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index f36cf110..8e7750d5 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -5,13 +5,4 @@
test/HTML5/
-
-
- systemlib.phpreflection_hni
- src/HTML5/Parser/InputStream.php
- src/HTML5/Serializer/RulesInterface.php
- src/HTML5/Entities.php
- src/HTML5/Serializer/HTML5Entities.php
-
-
diff --git a/src/HTML5.php b/src/HTML5.php
index c857145f..49a90daf 100644
--- a/src/HTML5.php
+++ b/src/HTML5.php
@@ -146,7 +146,6 @@ public function hasErrors()
* Parse an input string.
*
* @param string $input
- * @param array $options
*
* @return \DOMDocument
*/
diff --git a/src/HTML5/Elements.php b/src/HTML5/Elements.php
index 8fe79878..5d8cfd44 100644
--- a/src/HTML5/Elements.php
+++ b/src/HTML5/Elements.php
@@ -71,6 +71,24 @@ class Elements
*/
const BLOCK_ONLY_INLINE = 128;
+ /**
+ * Elements with optional end tags that cause auto-closing of previous and parent tags,
+ * as example most of the table related tags, see https://www.w3.org/TR/html401/struct/tables.html
+ * Structure is as follows:
+ * TAG-NAME => [PARENT-TAG-NAME-TO-CLOSE1, PARENT-TAG-NAME-TO-CLOSE2, ...].
+ *
+ * Order is important, after auto-closing one parent with might have to close also their parent.
+ *
+ * @var array
+ */
+ public static $optionalEndElementsParentsToClose = array(
+ 'tr' => array('td', 'tr'),
+ 'td' => array('td', 'th'),
+ 'th' => array('td', 'th'),
+ 'tfoot' => array('td', 'th', 'tr', 'tbody', 'thead'),
+ 'tbody' => array('td', 'th', 'tr', 'thead'),
+ );
+
/**
* The HTML5 elements as defined in http://dev.w3.org/html5/markup/elements.html.
*
@@ -185,7 +203,7 @@ class Elements
'u' => 1,
'ul' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
'var' => 1,
- 'video' => 65, // NORMAL | BLOCK_TAG
+ 'video' => 1,
'wbr' => 9, // NORMAL | VOID_TAG
// Legacy?
diff --git a/src/HTML5/Parser/CharacterReference.php b/src/HTML5/Parser/CharacterReference.php
index 490b5487..56a206cb 100644
--- a/src/HTML5/Parser/CharacterReference.php
+++ b/src/HTML5/Parser/CharacterReference.php
@@ -48,7 +48,7 @@ public static function lookupDecimal($int)
}
/**
- * Given a hexidecimal number, return the UTF-8 character.
+ * Given a hexadecimal number, return the UTF-8 character.
*
* @param $hexdec
*
diff --git a/src/HTML5/Parser/DOMTreeBuilder.php b/src/HTML5/Parser/DOMTreeBuilder.php
index 24bdce85..85651179 100644
--- a/src/HTML5/Parser/DOMTreeBuilder.php
+++ b/src/HTML5/Parser/DOMTreeBuilder.php
@@ -175,7 +175,7 @@ public function __construct($isFragment = false, array $options = array())
// documents, and attempting to up-convert any older DTDs to HTML5.
$dt = $impl->createDocumentType('html');
// $this->doc = \DOMImplementation::createDocument(NULL, 'html', $dt);
- $this->doc = $impl->createDocument(null, null, $dt);
+ $this->doc = $impl->createDocument(null, '', $dt);
$this->doc->encoding = !empty($options['encoding']) ? $options['encoding'] : 'UTF-8';
}
@@ -231,8 +231,6 @@ public function fragment()
*
* This is used for handling Processor Instructions as they are
* inserted. If omitted, PI's are inserted directly into the DOM tree.
- *
- * @param InstructionProcessor $proc
*/
public function setInstructionProcessor(InstructionProcessor $proc)
{
@@ -359,6 +357,16 @@ public function startTag($name, $attributes = array(), $selfClosing = false)
$this->onlyInline = null;
}
+ // some elements as table related tags might have optional end tags that force us to auto close multiple tags
+ // https://www.w3.org/TR/html401/struct/tables.html
+ if ($this->current instanceof \DOMElement && isset(Elements::$optionalEndElementsParentsToClose[$lname])) {
+ foreach (Elements::$optionalEndElementsParentsToClose[$lname] as $parentElName) {
+ if ($this->current instanceof \DOMElement && $this->current->tagName === $parentElName) {
+ $this->autoclose($parentElName);
+ }
+ }
+ }
+
try {
$prefix = ($pos = strpos($lname, ':')) ? substr($lname, 0, $pos) : '';
@@ -406,6 +414,8 @@ public function startTag($name, $attributes = array(), $selfClosing = false)
$aName = Elements::normalizeMathMlAttribute($aName);
}
+ $aVal = (string) $aVal;
+
try {
$prefix = ($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : false;
diff --git a/src/HTML5/Parser/Scanner.php b/src/HTML5/Parser/Scanner.php
index 1b52983e..1b25888b 100644
--- a/src/HTML5/Parser/Scanner.php
+++ b/src/HTML5/Parser/Scanner.php
@@ -104,7 +104,7 @@ public function position()
*/
public function peek()
{
- if (($this->char + 1) <= $this->EOF) {
+ if (($this->char + 1) < $this->EOF) {
return $this->data[$this->char + 1];
}
diff --git a/src/HTML5/Parser/StringInputStream.php b/src/HTML5/Parser/StringInputStream.php
index 0c213feb..75b08861 100644
--- a/src/HTML5/Parser/StringInputStream.php
+++ b/src/HTML5/Parser/StringInputStream.php
@@ -183,6 +183,7 @@ public function getColumnOffset()
*
* @return string The current character.
*/
+ #[\ReturnTypeWillChange]
public function current()
{
return $this->data[$this->char];
@@ -192,6 +193,7 @@ public function current()
* Advance the pointer.
* This is part of the Iterator interface.
*/
+ #[\ReturnTypeWillChange]
public function next()
{
++$this->char;
@@ -200,6 +202,7 @@ public function next()
/**
* Rewind to the start of the string.
*/
+ #[\ReturnTypeWillChange]
public function rewind()
{
$this->char = 0;
@@ -210,6 +213,7 @@ public function rewind()
*
* @return bool Whether the current pointer location is valid.
*/
+ #[\ReturnTypeWillChange]
public function valid()
{
return $this->char < $this->EOF;
@@ -324,6 +328,7 @@ public function peek()
return false;
}
+ #[\ReturnTypeWillChange]
public function key()
{
return $this->char;
diff --git a/src/HTML5/Parser/Tokenizer.php b/src/HTML5/Parser/Tokenizer.php
index 300a4462..77e268fb 100644
--- a/src/HTML5/Parser/Tokenizer.php
+++ b/src/HTML5/Parser/Tokenizer.php
@@ -131,13 +131,16 @@ protected function consumeData()
$tok = $this->scanner->next();
- if ('!' === $tok) {
+ if (false === $tok) {
+ // end of string
+ $this->parseError('Illegal tag opening');
+ } elseif ('!' === $tok) {
$this->markupDeclaration();
} elseif ('/' === $tok) {
$this->endTag();
} elseif ('?' === $tok) {
$this->processingInstruction();
- } elseif (ctype_alpha($tok)) {
+ } elseif ($this->is_alpha($tok)) {
$this->tagName();
} else {
$this->parseError('Illegal tag opening');
@@ -347,7 +350,7 @@ protected function endTag()
// > -> parse error
// EOF -> parse error
// -> parse error
- if (!ctype_alpha($tok)) {
+ if (!$this->is_alpha($tok)) {
$this->parseError("Expected tag name, got '%s'", $tok);
if ("\0" == $tok || false === $tok) {
return false;
@@ -504,7 +507,7 @@ protected function attribute(&$attributes)
$this->scanner->whitespace();
$val = $this->attributeValue();
- if ($isValidAttribute) {
+ if ($isValidAttribute && !array_key_exists($name, $attributes)) {
$attributes[$name] = $val;
}
@@ -712,18 +715,25 @@ protected function isCommentEnd()
return true;
}
- // If it doesn't start with -, not the end.
- if ('-' != $tok) {
+ // If next two tokens are not '--', not the end.
+ if ('-' != $tok || '-' != $this->scanner->peek()) {
return false;
}
- // Advance one, and test for '->'
- if ('-' == $this->scanner->next() && '>' == $this->scanner->peek()) {
+ $this->scanner->consume(2); // Consume '-' and one of '!' or '>'
+
+ // Test for '>'
+ if ('>' == $this->scanner->current()) {
+ return true;
+ }
+ // Test for '!>'
+ if ('!' == $this->scanner->current() && '>' == $this->scanner->peek()) {
$this->scanner->consume(); // Consume the last '>'
+
return true;
}
- // Unread '-';
- $this->scanner->unconsume(1);
+ // Unread '-' and one of '!' or '>';
+ $this->scanner->unconsume(2);
return false;
}
@@ -1118,7 +1128,7 @@ protected function decodeCharacterReference($inAttribute = false)
return '&';
}
- // Hexidecimal encoding.
+ // Hexadecimal encoding.
// X[0-9a-fA-F]+;
// x[0-9a-fA-F]+;
if ('x' === $tok || 'X' === $tok) {
@@ -1188,4 +1198,18 @@ protected function decodeCharacterReference($inAttribute = false)
return '&';
}
+
+ /**
+ * Checks whether a (single-byte) character is an ASCII letter or not.
+ *
+ * @param string $input A single-byte string
+ *
+ * @return bool True if it is a letter, False otherwise
+ */
+ protected function is_alpha($input)
+ {
+ $code = ord($input);
+
+ return ($code >= 97 && $code <= 122) || ($code >= 65 && $code <= 90);
+ }
}
diff --git a/src/HTML5/Parser/TreeBuildingRules.php b/src/HTML5/Parser/TreeBuildingRules.php
index 00d3951f..4c6983b2 100644
--- a/src/HTML5/Parser/TreeBuildingRules.php
+++ b/src/HTML5/Parser/TreeBuildingRules.php
@@ -80,7 +80,6 @@ public function evaluate($new, $current)
case 'thead':
case 'tfoot':
case 'table': // Spec isn't explicit about this, but it's necessary.
-
return $this->closeIfCurrentMatches($new, $current, array(
'thead',
'tfoot',
diff --git a/src/HTML5/Parser/UTF8Utils.php b/src/HTML5/Parser/UTF8Utils.php
index f6a70bfa..4405e4cc 100644
--- a/src/HTML5/Parser/UTF8Utils.php
+++ b/src/HTML5/Parser/UTF8Utils.php
@@ -38,7 +38,7 @@ class UTF8Utils
/**
* Count the number of characters in a string.
- * UTF-8 aware. This will try (in order) iconv, MB, libxml, and finally a custom counter.
+ * UTF-8 aware. This will try (in order) iconv, MB, and finally a custom counter.
*
* @param string $string
*
@@ -55,12 +55,6 @@ public static function countChars($string)
return iconv_strlen($string, 'utf-8');
}
- if (function_exists('utf8_decode')) {
- // MPB: Will this work? Won't certain decodes lead to two chars
- // extrapolated out of 2-byte chars?
- return strlen(utf8_decode($string));
- }
-
$count = count_chars($string);
// 0x80 = 0x7F - 0 + 1 (one added to get inclusive range)
diff --git a/test/HTML5/ElementsTest.php b/test/HTML5/ElementsTest.php
index 08b5ee42..846d5726 100644
--- a/test/HTML5/ElementsTest.php
+++ b/test/HTML5/ElementsTest.php
@@ -421,7 +421,6 @@ public function testIsA()
'table',
'tfoot',
'ul',
- 'video',
);
foreach ($blockTags as $tag) {
@@ -432,6 +431,7 @@ public function testIsA()
'span',
'img',
'label',
+ 'video',
);
foreach ($nonBlockTags as $tag) {
$this->assertFalse(Elements::isA($tag, Elements::BLOCK_TAG), 'Block tag test failed on: ' . $tag);
diff --git a/test/HTML5/Html5Test.php b/test/HTML5/Html5Test.php
index ed66d8af..7c2f4850 100644
--- a/test/HTML5/Html5Test.php
+++ b/test/HTML5/Html5Test.php
@@ -11,7 +11,10 @@ class Html5Test extends TestCase
*/
private $html5;
- public function setUp()
+ /**
+ * @before
+ */
+ public function before()
{
$this->html5 = $this->getInstance();
}
@@ -53,6 +56,60 @@ public function testImageTagsInSvg()
$this->assertEmpty($this->html5->getErrors());
}
+ public function testSelfClosingTableHierarchyElements()
+ {
+ $html = '
+
+
+
+ 0
+ |
+
+ A
+ |
+ B1
+ | B2
+ |
+ C
+ |
+
+ 1
+ | 2
+ |
';
+
+ $expected = '
+
+
+ 0 |
+
+
+
+
+ A |
+
+
+ B1 |
+ B2 |
+
+
+ C |
+
+
+
+
+ 1 |
+ 2 |
+
+
+
';
+
+ $doc = $this->html5->loadHTMLFragment($html);
+ $this->assertSame(
+ preg_replace('/\s+/', '', $expected),
+ preg_replace('/\s+/', '', $this->html5->saveHTML($doc))
+ );
+ }
+
public function testLoadOptions()
{
// doc
@@ -90,7 +147,12 @@ public function testEncodingUtf8()
$this->assertEmpty($this->html5->getErrors());
$this->assertFalse($this->html5->hasErrors());
- $this->assertContains('Žťčýů', $dom->saveHTML());
+ // phpunit 9
+ if (method_exists($this, 'assertStringContainsString')) {
+ $this->assertStringContainsString('Žťčýů', $dom->saveHTML());
+ } else {
+ $this->assertContains('Žťčýů', $dom->saveHTML());
+ }
}
public function testEncodingWindows1252()
@@ -370,7 +432,13 @@ public function testElements()
public function testAttributes()
{
$res = $this->cycle('');
- $this->assertContains('', $res);
+
+ // phpunit 9
+ if (method_exists($this, 'assertStringContainsString')) {
+ $this->assertStringContainsString('', $res);
+ } else {
+ $this->assertContains('', $res);
+ }
$res = $this->cycle('FOO
');
$this->assertRegExp('|FOO
|', $res);
@@ -484,9 +552,12 @@ public function testCDATA()
public function testAnchorTargetQueryParam()
{
$res = $this->cycle('https://domain.com/page.php?foo=bar&target=baz');
- $this->assertContains(
- 'https://domain.com/page.php?foo=bar&target=baz',
- $res
- );
+
+ // phpunit 9
+ if (method_exists($this, 'assertStringContainsString')) {
+ $this->assertStringContainsString('https://domain.com/page.php?foo=bar&target=baz', $res);
+ } else {
+ $this->assertContains('https://domain.com/page.php?foo=bar&target=baz', $res);
+ }
}
}
diff --git a/test/HTML5/Parser/DOMTreeBuilderTest.php b/test/HTML5/Parser/DOMTreeBuilderTest.php
index 659378c6..1e1898c9 100644
--- a/test/HTML5/Parser/DOMTreeBuilderTest.php
+++ b/test/HTML5/Parser/DOMTreeBuilderTest.php
@@ -6,9 +6,9 @@
namespace Masterminds\HTML5\Tests\Parser;
+use Masterminds\HTML5\Parser\DOMTreeBuilder;
use Masterminds\HTML5\Parser\Scanner;
use Masterminds\HTML5\Parser\Tokenizer;
-use Masterminds\HTML5\Parser\DOMTreeBuilder;
/**
* These tests are functional, not necessarily unit tests.
diff --git a/test/HTML5/Parser/ScannerTest.php b/test/HTML5/Parser/ScannerTest.php
index 9f75c4db..a181c1ba 100644
--- a/test/HTML5/Parser/ScannerTest.php
+++ b/test/HTML5/Parser/ScannerTest.php
@@ -6,8 +6,8 @@
namespace Masterminds\HTML5\Tests\Parser;
-use Masterminds\HTML5\Parser\StringInputStream;
use Masterminds\HTML5\Parser\Scanner;
+use Masterminds\HTML5\Parser\StringInputStream;
class ScannerTest extends \Masterminds\HTML5\Tests\TestCase
{
diff --git a/test/HTML5/Parser/TokenizerTest.php b/test/HTML5/Parser/TokenizerTest.php
index 6c906864..86401c73 100644
--- a/test/HTML5/Parser/TokenizerTest.php
+++ b/test/HTML5/Parser/TokenizerTest.php
@@ -2,9 +2,9 @@
namespace Masterminds\HTML5\Tests\Parser;
-use Masterminds\HTML5\Parser\UTF8Utils;
use Masterminds\HTML5\Parser\Scanner;
use Masterminds\HTML5\Parser\Tokenizer;
+use Masterminds\HTML5\Parser\UTF8Utils;
class TokenizerTest extends \Masterminds\HTML5\Tests\TestCase
{
@@ -197,11 +197,17 @@ public function testComment()
{
$good = array(
'' => 'easy',
+ '' => 'easy',
'' => ' 1 > 0 ',
+ '' => ' 1 > 0 ',
'' => ' --$i ',
+ '' => ' --$i ',
'' => '--$i',
+ '' => '--$i',
"" => "\nHello World.\na",
+ "" => "\nHello World.\na",
'' => ' ' => '