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