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);
}