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 -[![Build Status](https://travis-ci.org/Masterminds/html5-php.png?branch=master)](https://travis-ci.org/Masterminds/html5-php) +[![CI](https://github.com/Masterminds/html5-php/actions/workflows/ci.yaml/badge.svg)](https://github.com/Masterminds/html5-php/actions/workflows/ci.yaml) [![Latest Stable Version](https://poser.pugx.org/masterminds/html5/v/stable.png)](https://packagist.org/packages/masterminds/html5) [![Code Coverage](https://scrutinizer-ci.com/g/Masterminds/html5-php/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Masterminds/html5-php/?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Masterminds/html5-php/badges/quality-score.png?b=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
B1B2
C
12
'; + + $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", '' => ' ' => '