diff --git a/components/XML/Tests/W3CXMLConformanceTest.php b/components/XML/Tests/W3CXMLConformanceTest.php index e5b09101..bb47c268 100644 --- a/components/XML/Tests/W3CXMLConformanceTest.php +++ b/components/XML/Tests/W3CXMLConformanceTest.php @@ -58,15 +58,6 @@ public function test_w3c_xml_test_case($test_id, $test_type, $test_file, $descri return; } - if (in_array($test_id, [ - "not-sa04", - "sa04", - "ibm-valid-P01-ibm01v01.xml", - ])) { - $this->markTestSkipped("Skipping test case: {$test_id} – XMLProcessor does not support custom processing directive targets (e.g. )"); - return; - } - if (in_array($test_id, [ "ibm-1-1-valid-P02-ibm02v01.xml", "ibm-1-1-valid-P02-ibm02v02.xml", diff --git a/components/XML/Tests/XMLProcessorTest.php b/components/XML/Tests/XMLProcessorTest.php index c38a254c..fa0adb26 100644 --- a/components/XML/Tests/XMLProcessorTest.php +++ b/components/XML/Tests/XMLProcessorTest.php @@ -1501,6 +1501,19 @@ public function test_processor_instructions() { 'The modifiable text was not correctly captured.' ); } + public function test_custom_processing_instruction_target() { + $processor = XMLProcessor::create_from_string( '' ); + $this->assertTrue( $processor->next_token(), 'The custom processing instruction was not found.' ); + $this->assertEquals( + '#processing-instructions', + $processor->get_token_type(), + 'The custom processing instruction was not correctly identified.' + ); + $this->assertSame( ' "notes"', $processor->get_modifiable_text() ); + $this->assertTrue( $processor->next_token(), 'The element following the processing instruction was not found.' ); + $this->assertSame( 'post', $processor->get_token_name(), 'The element following the processing instruction was not parsed correctly.' ); + } + /** * Ensures that updates which are enqueued in front of the cursor * are applied before moving forward in the document. diff --git a/components/XML/class-xmlprocessor.php b/components/XML/class-xmlprocessor.php index 9ddea8ae..2509537e 100644 --- a/components/XML/class-xmlprocessor.php +++ b/components/XML/class-xmlprocessor.php @@ -2106,27 +2106,26 @@ private function parse_next_tag() { * `is_closing_tag && - '?' === $xml[ $at + 1 ] - ) { - if ( $at + 4 >= $doc_length ) { + if ( ! $this->is_closing_tag && '?' === $xml[ $at + 1 ] ) { + if ( $at + 2 >= $doc_length ) { $this->mark_incomplete_input(); return false; } - if ( ! ( - ( 'x' === $xml[ $at + 2 ] || 'X' === $xml[ $at + 2 ] ) && - ( 'm' === $xml[ $at + 3 ] || 'M' === $xml[ $at + 3 ] ) && - ( 'l' === $xml[ $at + 4 ] || 'L' === $xml[ $at + 4 ] ) - ) ) { + $target_length = $this->parse_name( $at + 2 ); + if ( false === $target_length ) { + return false; + } + + if ( 0 === $target_length ) { $this->bail( 'Invalid processing instruction target.', self::ERROR_SYNTAX ); } - $at += 5; + $target_ends_at = $at + 2 + $target_length; + $this->bytes_already_parsed = $target_ends_at; - // Skip whitespace. + // Skip whitespace after the target, if any. $this->skip_whitespace(); /* @@ -2141,7 +2140,7 @@ private function parse_next_tag() { * closing ?> is found. Some failures may pass unnoticed. That may not be a problem in practice, * but if it is then this code path will require a stricter implementation. */ - $closer_at = strpos( $xml, '?>', $at ); + $closer_at = strpos( $xml, '?>', $this->bytes_already_parsed ); if ( false === $closer_at ) { $this->mark_incomplete_input(); @@ -2149,8 +2148,8 @@ private function parse_next_tag() { } $this->parser_state = self::STATE_PI_NODE; - $this->token_length = $closer_at + 5 - $this->token_starts_at; - $this->text_starts_at = $this->token_starts_at + 5; + $this->token_length = $closer_at + 2 - $this->token_starts_at; + $this->text_starts_at = $target_ends_at; $this->text_length = $closer_at - $this->text_starts_at; $this->bytes_already_parsed = $closer_at + 2;