diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index f173b58..eaa454f 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -18,6 +18,9 @@
tests/Unit
+
+ tests/Integration
+
diff --git a/src/Runner/EntityExpansionMarker.php b/src/Runner/EntityExpansionMarker.php
new file mode 100644
index 0000000..ded0b3b
--- /dev/null
+++ b/src/Runner/EntityExpansionMarker.php
@@ -0,0 +1,61 @@
+%s', self::START, $content, self::END);
+ }
+
+ public static function contains(\DOMNode $node): bool
+ {
+ for ($current = $node; $current->parentNode !== null; $current = $current->parentNode) {
+ if (self::isBetweenMarkers($current)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static function isBetweenMarkers(\DOMNode $node): bool
+ {
+ $nestedMarkers = 0;
+
+ for ($sibling = $node->previousSibling; $sibling !== null; $sibling = $sibling->previousSibling) {
+ if (self::isEnd($sibling)) {
+ $nestedMarkers++;
+ continue;
+ }
+
+ if (!self::isStart($sibling)) {
+ continue;
+ }
+
+ if ($nestedMarkers === 0) {
+ return true;
+ }
+
+ $nestedMarkers--;
+ }
+
+ return false;
+ }
+
+ private static function isStart(\DOMNode $node): bool
+ {
+ return $node instanceof \DOMComment && $node->textContent === self::START;
+ }
+
+ private static function isEnd(\DOMNode $node): bool
+ {
+ return $node instanceof \DOMComment && $node->textContent === self::END;
+ }
+}
diff --git a/src/Runner/EntityPreprocessor.php b/src/Runner/EntityPreprocessor.php
index a603684..f7d80b8 100644
--- a/src/Runner/EntityPreprocessor.php
+++ b/src/Runner/EntityPreprocessor.php
@@ -25,7 +25,14 @@ public function process(string $xml): string
return $this->expandEntities($xml);
}
- private function expandEntities(string $content): string
+ public function processForParsing(string $xml): string
+ {
+ $xml = $this->stripDoctype($xml);
+
+ return $this->expandEntities($xml, markXmlExpansions: true);
+ }
+
+ private function expandEntities(string $content, bool $markXmlExpansions = false): string
{
$maxDepth = 20;
@@ -33,10 +40,13 @@ private function expandEntities(string $content): string
$changed = false;
$content = preg_replace_callback(
- '/|' . self::ENTITY_PATTERN . '/',
- function (array $matches) use (&$changed): string {
- // If this is a comment, return as is
- if (str_starts_with($matches[0], '||<\?[\s\S]*?\?>|' . self::ENTITY_PATTERN . '/',
+ function (array $matches) use (&$changed, $markXmlExpansions): string {
+ if (
+ str_starts_with($matches[0], '&value;';
+
+ self::assertSame(
+ 'expanded',
+ $preprocessor->process($source),
+ );
+ }
+
+ private function parse(string $xml): \DOMDocument
+ {
+ $document = new \DOMDocument();
+ $document->loadXML($xml);
+
+ return $document;
+ }
+}
diff --git a/tests/Integration/Sniff/EntityExpandedSniffTest.php b/tests/Integration/Sniff/EntityExpandedSniffTest.php
new file mode 100644
index 0000000..a6b1925
--- /dev/null
+++ b/tests/Integration/Sniff/EntityExpandedSniffTest.php
@@ -0,0 +1,62 @@
+Source&expanded;';
+ $document = $this->processedDocument($source, 'Expanded');
+
+ $violations = new SimparaSniff()->process($document, $source, 'file.xml');
+
+ self::assertCount(1, $violations);
+ self::assertSame(1, $violations[0]->line);
+ }
+
+ #[Test]
+ public function exceptionNameIgnoresExpandedElements(): void
+ {
+ $source = 'RuntimeException&expanded;';
+ $document = $this->processedDocument(
+ $source,
+ 'ExpandedException',
+ );
+
+ $violations = new ExceptionNameSniff()->process($document, $source, 'file.xml');
+
+ self::assertCount(1, $violations);
+ self::assertSame(1, $violations[0]->line);
+ }
+
+ private function processedDocument(string $source, string $expanded): \DOMDocument
+ {
+ $content = new EntityPreprocessor(['expanded' => $expanded])->processForParsing($source);
+ $document = new \DOMDocument();
+ $document->loadXML($content);
+
+ return $document;
+ }
+}
diff --git a/tests/Unit/Sniff/ExceptionNameSniffTest.php b/tests/Unit/Sniff/ExceptionNameSniffTest.php
index 99ecd73..5e749c6 100644
--- a/tests/Unit/Sniff/ExceptionNameSniffTest.php
+++ b/tests/Unit/Sniff/ExceptionNameSniffTest.php
@@ -5,14 +5,18 @@
namespace DocbookCS\Tests\Unit\Sniff;
use DocbookCS\Report\Violation;
+use DocbookCS\Runner\EntityExpansionMarker;
use DocbookCS\Sniff\ExceptionNameSniff;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[
CoversClass(ExceptionNameSniff::class),
CoversClass(Violation::class),
+ //
+ UsesClass(EntityExpansionMarker::class),
]
final class ExceptionNameSniffTest extends TestCase
{
diff --git a/tests/Unit/Sniff/SimparaSniffTest.php b/tests/Unit/Sniff/SimparaSniffTest.php
index d6c616a..036d374 100644
--- a/tests/Unit/Sniff/SimparaSniffTest.php
+++ b/tests/Unit/Sniff/SimparaSniffTest.php
@@ -5,14 +5,18 @@
namespace DocbookCS\Tests\Unit\Sniff;
use DocbookCS\Report\Violation;
+use DocbookCS\Runner\EntityExpansionMarker;
use DocbookCS\Sniff\SimparaSniff;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[
CoversClass(SimparaSniff::class),
CoversClass(Violation::class),
+ //
+ UsesClass(EntityExpansionMarker::class),
]
final class SimparaSniffTest extends TestCase
{