diff --git a/fractor-xml/src/DomDocumentIterator.php b/fractor-xml/src/DomDocumentIterator.php index dd5e48e8..9a91980a 100644 --- a/fractor-xml/src/DomDocumentIterator.php +++ b/fractor-xml/src/DomDocumentIterator.php @@ -20,7 +20,9 @@ public function traverseDocument(\DOMDocument $document): void $visitor->beforeTraversal($document); } - $this->traverseNode($document->firstChild); + if ($document->firstChild instanceof \DOMNode) { + $this->traverseNode($document->firstChild); + } foreach ($this->visitors as $visitor) { $visitor->afterTraversal($document); @@ -33,6 +35,11 @@ private function traverseNode(\DOMNode $node): void foreach ($this->visitors as $visitor) { $result = $visitor->enterNode($node); + if ($node->parentNode === null) { + // TODO convert into a custom ShouldNotHappenException + throw new \RuntimeException('Node has no parent node'); + } + if ($result === DomDocumentIterator::REMOVE_NODE) { $node->parentNode->removeChild($node); $nodeRemoved = true; diff --git a/fractor-xml/src/XmlFractor.php b/fractor-xml/src/XmlFractor.php index 719bb89f..13f34730 100644 --- a/fractor-xml/src/XmlFractor.php +++ b/fractor-xml/src/XmlFractor.php @@ -2,9 +2,12 @@ namespace a9f\FractorXml; -interface XmlFractor +interface XmlFractor extends DomNodeVisitor { public function canHandle(\DOMNode $node): bool; + /** + * @return \DOMNode|DomDocumentIterator::*|null + */ public function refactor(\DOMNode $node): \DOMNode|int|null; } diff --git a/fractor-xml/tests/DomDocumentIteratorTest.php b/fractor-xml/tests/DomDocumentIteratorTest.php index 9b35aeb0..a552870d 100644 --- a/fractor-xml/tests/DomDocumentIteratorTest.php +++ b/fractor-xml/tests/DomDocumentIteratorTest.php @@ -168,7 +168,7 @@ public function enterNode(\DOMNode $node): \DOMNode|int 'afterTraversal:#document', ], $nodeRemovingVisitor->calls); - self::assertXmlStringEqualsXmlString('', $document->saveXML()); + self::assertXmlStringEqualsXmlString('', $document->saveXML() ?: ''); } #[Test] @@ -179,6 +179,10 @@ public function enterNode(\DOMNode $node): \DOMNode|int { parent::enterNode($node); if ($node->nodeName === 'Child') { + if ($node->ownerDocument === null) { + throw new \RuntimeException('Node does not have an ownerDocument, cannot create element'); + } + return $node->ownerDocument->createElement('NewChild'); } return $node; @@ -201,14 +205,17 @@ public function enterNode(\DOMNode $node): \DOMNode|int 'afterTraversal:#document', ], $nodeRemovingVisitor->calls); - self::assertXmlStringEqualsXmlString('', $document->saveXML()); + self::assertXmlStringEqualsXmlString('', $document->saveXML() ?: ''); } - private function getCollectingDomNodeVisitor(): DomNodeVisitor + private function getCollectingDomNodeVisitor(): CollectingDomNodeVisitor { return new CollectingDomNodeVisitor(); } + /** + * @param list $recorder + */ private function getCallRecordingDomNodeVisitor(string $visitorName, array &$recorder): DomNodeVisitor { return new class($visitorName, $recorder) implements DomNodeVisitor { @@ -217,7 +224,7 @@ private function getCallRecordingDomNodeVisitor(string $visitorName, array &$rec */ public function __construct( private readonly string $visitorName, - private array &$calls + public array &$calls // only public to please PHPStan ) { } diff --git a/fractor/src/DependencyInjection/ContainerBuilder.php b/fractor/src/DependencyInjection/ContainerBuilder.php index 11f202f0..7b176c7d 100644 --- a/fractor/src/DependencyInjection/ContainerBuilder.php +++ b/fractor/src/DependencyInjection/ContainerBuilder.php @@ -30,7 +30,7 @@ public function createDependencyInjectionContainer(?string $fractorConfigFile): $config->addCompilerPass(new CommandsCompilerPass()); $config->addCompilerPass(new FileProcessorCompilerPass()); - if (is_file($fractorConfigFile)) { + if ($fractorConfigFile !== null && is_file($fractorConfigFile)) { $config->import($fractorConfigFile); } @@ -42,7 +42,7 @@ public function createDependencyInjectionContainer(?string $fractorConfigFile): return $config; } - private function registerConfiguredRules(FractorConfig $config) + private function registerConfiguredRules(FractorConfig $config): void { foreach ($config->getRules() as $rule) { $config->registerForAutoconfiguration($rule) @@ -50,7 +50,7 @@ private function registerConfiguredRules(FractorConfig $config) } } - private function registerConfiguredFileProcessors(FractorConfig $config) + private function registerConfiguredFileProcessors(FractorConfig $config): void { foreach ($config->getFileProcessors() as $processor) { $config->registerForAutoconfiguration($processor) diff --git a/fractor/src/FileSystem/FileFinder.php b/fractor/src/FileSystem/FileFinder.php index 7499b804..fa3225ee 100644 --- a/fractor/src/FileSystem/FileFinder.php +++ b/fractor/src/FileSystem/FileFinder.php @@ -7,6 +7,8 @@ final class FileFinder { /** + * @param list $directories + * @param list $fileExtensions * @return list<\SplFileInfo> */ public function findFiles(array $directories, array $fileExtensions): array diff --git a/fractor/tests/Configuration/FractorConfigTest.php b/fractor/tests/Configuration/FractorConfigTest.php index 468ef164..dc28a4f3 100644 --- a/fractor/tests/Configuration/FractorConfigTest.php +++ b/fractor/tests/Configuration/FractorConfigTest.php @@ -48,6 +48,7 @@ public function importFileThrowsExceptionIfNoClosureIsReturned(): void private function placeConfigFileInTemporaryFolderAndImport(FractorConfig $config, string $closure): void { $tempFile = tempnam(sys_get_temp_dir(), 'fractor-test'); + self::assertIsString($tempFile); try { file_put_contents($tempFile, $closure); diff --git a/typo3-fractor/composer.json b/typo3-fractor/composer.json index 6126286f..479d8de9 100644 --- a/typo3-fractor/composer.json +++ b/typo3-fractor/composer.json @@ -14,7 +14,8 @@ "php": "^8.2", "a9f/fractor": "@dev", "a9f/fractor-extension-installer": "@dev", - "a9f/fractor-xml": "@dev" + "a9f/fractor-xml": "@dev", + "thecodingmachine/safe": "^2.5" }, "require-dev": { "ergebnis/composer-normalize": "^2.42", diff --git a/typo3-fractor/src/AbstractFlexformFractor.php b/typo3-fractor/src/AbstractFlexformFractor.php index c0e9012a..92520d01 100644 --- a/typo3-fractor/src/AbstractFlexformFractor.php +++ b/typo3-fractor/src/AbstractFlexformFractor.php @@ -8,6 +8,13 @@ abstract class AbstractFlexformFractor extends AbstractXmlFractor { public function canHandle(\DOMNode $node): bool { - return $node->ownerDocument->firstChild->nodeName === 'T3DataStructure'; + $rootNode = $node->ownerDocument?->firstChild; + + if ($rootNode === null) { + // TODO convert into a custom ShouldNotHappenException + throw new \RuntimeException('Node\'s document does not have a root node'); + } + + return $rootNode->nodeName === 'T3DataStructure'; } } diff --git a/typo3-fractor/src/Rules/FlexForm/AddRenderTypeToFlexFormFractor.php b/typo3-fractor/src/Rules/FlexForm/AddRenderTypeToFlexFormFractor.php index cf2ffd4f..67db2305 100644 --- a/typo3-fractor/src/Rules/FlexForm/AddRenderTypeToFlexFormFractor.php +++ b/typo3-fractor/src/Rules/FlexForm/AddRenderTypeToFlexFormFractor.php @@ -37,7 +37,14 @@ public function refactor(\DOMNode $node): \DOMNode|int|null } if ($isSingleSelect) { - $renderType = $childNode->ownerDocument->createElement('renderType'); + $ownerDocument = $node->ownerDocument; + + if ($ownerDocument === null) { + // TODO convert into a custom ShouldNotHappenException + throw new \RuntimeException('Node does not have an ownerDocument'); + } + + $renderType = $ownerDocument->createElement('renderType'); $renderType->nodeValue = 'selectSingle'; $node->appendChild($renderType); } diff --git a/typo3-fractor/tests/Rules/FlexForm/AddRenderTypeToFlexFormFractorTest.php b/typo3-fractor/tests/Rules/FlexForm/AddRenderTypeToFlexFormFractorTest.php index 287ca224..2a50c2a1 100644 --- a/typo3-fractor/tests/Rules/FlexForm/AddRenderTypeToFlexFormFractorTest.php +++ b/typo3-fractor/tests/Rules/FlexForm/AddRenderTypeToFlexFormFractorTest.php @@ -7,12 +7,14 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; +use function Safe\file_get_contents; + class AddRenderTypeToFlexFormFractorTest extends TestCase { private const FIXTURE_SEPARATOR = '-----'; /** - * @return array + * @return array */ public static function fixtureFilesProvider(): array { @@ -41,7 +43,7 @@ public function test(string $filePath): void $iterator = new DomDocumentIterator([new AddRenderTypeToFlexFormFractor()]); $iterator->traverseDocument($document); - $result = $document->saveXML(); + $result = $document->saveXML() ?: ''; self::assertXmlStringEqualsXmlString($expectedResultXml, $result); }