diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index 80e0228..c3477af 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -1,7 +1,9 @@
ignoreVCSIgnored(true)
- ->in(realpath(__DIR__));
+ ->exclude('Tests/Functional/Converter/Fixtures/Xml/')
+ ->in(realpath(__DIR__))
+;
return (new \PhpCsFixer\Config())
->setRiskyAllowed(true)
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..6e3c279
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,13 @@
+# Changelog
+
+## v1.1.0 - 2023-04-12
+
+### Added
+
+- Add bin `typo3-php-datasets`.
+
+- Add command `convert-from-xml`.
+
+## v1.0.0 - 2023-04-11
+
+_Initial release._
diff --git a/Classes/Command/ConvertFromXml.php b/Classes/Command/ConvertFromXml.php
new file mode 100644
index 0000000..4cf2a38
--- /dev/null
+++ b/Classes/Command/ConvertFromXml.php
@@ -0,0 +1,62 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+namespace Codappix\Typo3PhpDatasets\Command;
+
+use Codappix\Typo3PhpDatasets\Converter\Xml;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ConvertFromXml extends Command
+{
+ protected static $defaultName = 'convert-from-xml';
+ protected static $defaultDescription = 'Converts XML data-sets to PHP data-sets.';
+
+ protected function configure(): void
+ {
+ $this->addArgument('file', InputArgument::IS_ARRAY, 'The file(s) to convert.');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $files = $input->getArgument('file');
+ if (is_array($files) === false) {
+ $output->writeln('File needs to be an array.');
+ return Command::INVALID;
+ }
+
+ $converter = new Xml();
+ foreach ($files as $file) {
+ try {
+ $converter->convert(realpath($file) ?: $file);
+ } catch (\Exception $e) {
+ $output->writeln($e->getMessage());
+ return Command::INVALID;
+ }
+ }
+
+ return Command::SUCCESS;
+ }
+}
diff --git a/Classes/Converter/Converter.php b/Classes/Converter/Converter.php
new file mode 100644
index 0000000..7adc18f
--- /dev/null
+++ b/Classes/Converter/Converter.php
@@ -0,0 +1,32 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+namespace Codappix\Typo3PhpDatasets\Converter;
+
+interface Converter
+{
+ /**
+ * @return string The absolute file path to converted file.
+ */
+ public function convert(string $fileName): string;
+}
diff --git a/Classes/Converter/Xml.php b/Classes/Converter/Xml.php
new file mode 100644
index 0000000..2f4baa7
--- /dev/null
+++ b/Classes/Converter/Xml.php
@@ -0,0 +1,104 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+namespace Codappix\Typo3PhpDatasets\Converter;
+
+use InvalidArgumentException;
+use SimpleXMLElement;
+use SplFileObject;
+
+class Xml implements Converter
+{
+ public function convert(string $xmlFileName): string
+ {
+ if (file_exists($xmlFileName) === false) {
+ throw new InvalidArgumentException('Given file "' . $xmlFileName . '" does not exist.', 1681283739);
+ }
+
+ $phpFileName = $this->getNewFileName($xmlFileName);
+ $xmlContent = file_get_contents($xmlFileName);
+ if ($xmlContent === false) {
+ throw new \Exception('Could not read content of file "' . $xmlFileName . '".', 1681287782);
+ }
+
+ try {
+ file_put_contents($phpFileName, $this->buildContent($xmlContent));
+ } catch (\Exception $e) {
+ throw new \Exception('Could not generate new file.', 1681287881, $e);
+ }
+
+ return $phpFileName;
+ }
+
+ private function getNewFileName(string $xmlFileName): string
+ {
+ $file = new SplFileObject($xmlFileName);
+ return str_replace(
+ $file->getBasename(),
+ $file->getBasename($file->getExtension()) . 'php',
+ $file->getRealPath()
+ );
+ }
+
+ /**
+ * Adapted from TYPO3 testing framework XML import.
+ */
+ private function buildContent(string $xmlContent): string
+ {
+ $phpArray = [];
+ $xml = simplexml_load_string($xmlContent);
+ if ($xml === false) {
+ throw new \Exception('Could not parse XML content.', 1681287859);
+ }
+ foreach ($xml->children() as $table) {
+ if (!$table instanceof SimpleXMLElement) {
+ continue;
+ }
+
+ $insertArray = [];
+ foreach ($table->children() as $column) {
+ if (!$column instanceof SimpleXMLElement) {
+ continue;
+ }
+
+ $columnName = $column->getName();
+ $columnValue = (string)$table->$columnName;
+
+ if ((string)($column['is-NULL'] ?? '') === 'yes') {
+ $columnValue = null;
+ }
+
+ $insertArray[$columnName] = $columnValue;
+ }
+
+ $phpArray[$table->getName()][] = $insertArray;
+ }
+
+ return implode(PHP_EOL, [
+ 'assertPHPDataSet(__DIR__ . '/Fixtures/SimpleSet.php');
+Converter
+=========
+
+The package provides a single bin with command to convert existing data-sets.
+The bin is available as ``typo3-php-datasets``.
+Available commands:
+
+- ``convert-from-xml``
+
+Each command will convert the existing file(s) and place a new php variant next to it.
+Existing files are only read, not changed.
+
TODO
====
diff --git a/Tests/Functional/Converter/Fixtures/Xml/MultipleRecordsInSingleTableAssert.php b/Tests/Functional/Converter/Fixtures/Xml/MultipleRecordsInSingleTableAssert.php
new file mode 100644
index 0000000..d627869
--- /dev/null
+++ b/Tests/Functional/Converter/Fixtures/Xml/MultipleRecordsInSingleTableAssert.php
@@ -0,0 +1,21 @@
+
+ array (
+ 0 =>
+ array (
+ 'uid' => '1',
+ 'pid' => '0',
+ 'title' => 'Page with uid 1 below 0',
+ 'deleted' => '0',
+ ),
+ 1 =>
+ array (
+ 'uid' => '2',
+ 'pid' => '1',
+ 'title' => 'Page with uid 2 below 1',
+ 'deleted' => '0',
+ ),
+ ),
+);
diff --git a/Tests/Functional/Converter/Fixtures/Xml/MultipleRecordsInSingleTableIncoming.xml b/Tests/Functional/Converter/Fixtures/Xml/MultipleRecordsInSingleTableIncoming.xml
new file mode 100644
index 0000000..168d475
--- /dev/null
+++ b/Tests/Functional/Converter/Fixtures/Xml/MultipleRecordsInSingleTableIncoming.xml
@@ -0,0 +1,15 @@
+
+
+
+ 1
+ 0
+ Page with uid 1 below 0
+ 0
+
+
+ 2
+ 1
+ Page with uid 2 below 1
+ 0
+
+
diff --git a/Tests/Functional/Converter/Fixtures/Xml/RecordsInDifferentTablesAssert.php b/Tests/Functional/Converter/Fixtures/Xml/RecordsInDifferentTablesAssert.php
new file mode 100644
index 0000000..bcd7071
--- /dev/null
+++ b/Tests/Functional/Converter/Fixtures/Xml/RecordsInDifferentTablesAssert.php
@@ -0,0 +1,24 @@
+
+ array (
+ 0 =>
+ array (
+ 'uid' => '1',
+ 'pid' => '0',
+ 'title' => 'Page with uid 1 below 0',
+ 'deleted' => '0',
+ ),
+ ),
+ 'tt_content' =>
+ array (
+ 0 =>
+ array (
+ 'uid' => '1',
+ 'pid' => '1',
+ 'header' => 'Content with uid 1 on page 1',
+ 'deleted' => '0',
+ ),
+ ),
+);
diff --git a/Tests/Functional/Converter/Fixtures/Xml/RecordsInDifferentTablesIncoming.xml b/Tests/Functional/Converter/Fixtures/Xml/RecordsInDifferentTablesIncoming.xml
new file mode 100644
index 0000000..d0bbf0b
--- /dev/null
+++ b/Tests/Functional/Converter/Fixtures/Xml/RecordsInDifferentTablesIncoming.xml
@@ -0,0 +1,15 @@
+
+
+
+ 1
+ 0
+ Page with uid 1 below 0
+ 0
+
+
+ 1
+ 1
+ Content with uid 1 on page 1
+ 0
+
+
diff --git a/Tests/Functional/Converter/Fixtures/Xml/SimpleAssert.php b/Tests/Functional/Converter/Fixtures/Xml/SimpleAssert.php
new file mode 100644
index 0000000..78d597e
--- /dev/null
+++ b/Tests/Functional/Converter/Fixtures/Xml/SimpleAssert.php
@@ -0,0 +1,14 @@
+
+ array (
+ 0 =>
+ array (
+ 'uid' => '1',
+ 'pid' => '0',
+ 'title' => 'Page with uid 1 below 0',
+ 'deleted' => '0',
+ ),
+ ),
+);
diff --git a/Tests/Functional/Converter/Fixtures/Xml/SimpleIncoming.xml b/Tests/Functional/Converter/Fixtures/Xml/SimpleIncoming.xml
new file mode 100644
index 0000000..628c6b0
--- /dev/null
+++ b/Tests/Functional/Converter/Fixtures/Xml/SimpleIncoming.xml
@@ -0,0 +1,9 @@
+
+
+
+ 1
+ 0
+ Page with uid 1 below 0
+ 0
+
+
diff --git a/Tests/Functional/Converter/XmlTest.php b/Tests/Functional/Converter/XmlTest.php
new file mode 100644
index 0000000..769ef1f
--- /dev/null
+++ b/Tests/Functional/Converter/XmlTest.php
@@ -0,0 +1,104 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+namespace Codappix\Typo3PhpDatasets\Tests\Functional\Converter;
+
+use Codappix\Typo3PhpDatasets\Converter\Xml;
+use GlobIterator;
+use InvalidArgumentException;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \Codappix\Typo3PhpDatasets\Converter\Xml
+ * @testdox The XML converter
+ */
+class XmlTest extends TestCase
+{
+ protected function tearDown(): void
+ {
+ $filesToDelete = new GlobIterator(__DIR__ . '/Fixtures/Xml/*Incoming.php');
+ foreach ($filesToDelete as $file) {
+ unlink((string)$file);
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * @test
+ */
+ public function canBeCreated(): void
+ {
+ $subject = new Xml();
+
+ self::assertInstanceOf(
+ Xml::class,
+ $subject
+ );
+ }
+
+ /**
+ * @test
+ */
+ public function throwsExceptionForNoneExistingFile(): void
+ {
+ $subject = new Xml();
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionCode(1681283739);
+ $this->expectExceptionMessage('Given file "NoneExistingFile.xml" does not exist.');
+ $subject->convert('NoneExistingFile.xml');
+ }
+
+ /**
+ * @test
+ * @dataProvider possibleXmlFiles
+ * @testdox Converts $_dataName XML to PHP
+ */
+ public function convertsXmlFileToPhpFile(
+ string $incomingXmlFile,
+ string $expectedResultFile
+ ): void {
+ $subject = new Xml();
+ $result = $subject->convert($incomingXmlFile);
+
+ self::assertFileEquals($expectedResultFile, $result);
+ }
+
+ public static function possibleXmlFiles(): array
+ {
+ return [
+ 'Simple' => [
+ 'incomingXmlFile' => __DIR__ . '/Fixtures/Xml/SimpleIncoming.xml',
+ 'expectedResultFile' => __DIR__ . '/Fixtures/Xml/SimpleAssert.php',
+ ],
+ 'Multiple records in single table' => [
+ 'incomingXmlFile' => __DIR__ . '/Fixtures/Xml/MultipleRecordsInSingleTableIncoming.xml',
+ 'expectedResultFile' => __DIR__ . '/Fixtures/Xml/MultipleRecordsInSingleTableAssert.php',
+ ],
+ 'Records in different tables' => [
+ 'incomingXmlFile' => __DIR__ . '/Fixtures/Xml/RecordsInDifferentTablesIncoming.xml',
+ 'expectedResultFile' => __DIR__ . '/Fixtures/Xml/RecordsInDifferentTablesAssert.php',
+ ],
+ ];
+ }
+}
diff --git a/bin/typo3-php-datasets b/bin/typo3-php-datasets
new file mode 100755
index 0000000..c3829a1
--- /dev/null
+++ b/bin/typo3-php-datasets
@@ -0,0 +1,13 @@
+#!/usr/bin/env php
+add(new ConvertFromXml());
+
+$application->run();
diff --git a/composer.json b/composer.json
index 41b2e20..61da55d 100644
--- a/composer.json
+++ b/composer.json
@@ -19,9 +19,14 @@
"email": "coding@daniel-siepmann.de"
}
],
+ "bin": [
+ "bin/typo3-php-datasets"
+ ],
"require": {
"php": "^7.3 || ^7.4 || ^8.0 || ^8.1 || ^8.2",
+ "composer-runtime-api": "^2.2",
"doctrine/dbal": "^2.13",
+ "symfony/console": "^5.4",
"typo3/cms-core": "^10.4 || ^11.5"
},
"require-dev": {
@@ -41,6 +46,8 @@
"allow-plugins": {
"typo3/class-alias-loader": true,
"typo3/cms-composer-installers": true
- }
+ },
+ "lock": false,
+ "sort-packages": true
}
}
diff --git a/shell.nix b/shell.nix
index 94b9cde..30cfa01 100644
--- a/shell.nix
+++ b/shell.nix
@@ -27,19 +27,26 @@ let
composer install --prefer-dist --no-progress --working-dir="$PROJECT_ROOT"
'';
};
+ projectCgl = pkgs.writeShellApplication {
+ name = "project-cgl";
+ runtimeInputs = [
+ php
+ ];
+ text = ''
+ ./vendor/bin/php-cs-fixer fix --diff
+ '';
+ };
in pkgs.mkShell {
name = "TYPO3 PHP Datasets";
buildInputs = [
projectInstall
+ projectCgl
phpWithXdebug
composer
- pkgs.parallel
];
shellHook = ''
- export PROJECT_ROOT="$(pwd)"
-
export typo3DatabaseDriver=pdo_sqlite
'';
}