Add command to convert from XML to PHP (#2)

A bin was added with a single command.
That command allows to convert multiple XML files to PHP files in place.
This commit is contained in:
Daniel Siepmann 2023-04-12 12:05:53 +02:00 committed by GitHub
parent 5887cb87fe
commit ae33eeddc9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 459 additions and 5 deletions

View file

@ -1,7 +1,9 @@
<?php <?php
$finder = (new PhpCsFixer\Finder()) $finder = (new PhpCsFixer\Finder())
->ignoreVCSIgnored(true) ->ignoreVCSIgnored(true)
->in(realpath(__DIR__)); ->exclude('Tests/Functional/Converter/Fixtures/Xml/')
->in(realpath(__DIR__))
;
return (new \PhpCsFixer\Config()) return (new \PhpCsFixer\Config())
->setRiskyAllowed(true) ->setRiskyAllowed(true)

13
CHANGELOG.md Normal file
View file

@ -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._

View file

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 Daniel Siepmann <coding@daniel-siepmann.de>
*
* 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;
}
}

View file

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 Daniel Siepmann <coding@daniel-siepmann.de>
*
* 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;
}

104
Classes/Converter/Xml.php Normal file
View file

@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 Daniel Siepmann <coding@daniel-siepmann.de>
*
* 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, [
'<?php',
'',
'return ' . var_export($phpArray, true) . ';',
'',
]);
}
}

View file

@ -71,6 +71,18 @@ Within testing framework
$this->assertPHPDataSet(__DIR__ . '/Fixtures/SimpleSet.php'); $this->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 TODO
==== ====

View file

@ -0,0 +1,21 @@
<?php
return array (
'pages' =>
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',
),
),
);

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<pages>
<uid>1</uid>
<pid>0</pid>
<title>Page with uid 1 below 0</title>
<deleted>0</deleted>
</pages>
<pages>
<uid>2</uid>
<pid>1</pid>
<title>Page with uid 2 below 1</title>
<deleted>0</deleted>
</pages>
</dataset>

View file

@ -0,0 +1,24 @@
<?php
return array (
'pages' =>
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',
),
),
);

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<pages>
<uid>1</uid>
<pid>0</pid>
<title>Page with uid 1 below 0</title>
<deleted>0</deleted>
</pages>
<tt_content>
<uid>1</uid>
<pid>1</pid>
<header>Content with uid 1 on page 1</header>
<deleted>0</deleted>
</tt_content>
</dataset>

View file

@ -0,0 +1,14 @@
<?php
return array (
'pages' =>
array (
0 =>
array (
'uid' => '1',
'pid' => '0',
'title' => 'Page with uid 1 below 0',
'deleted' => '0',
),
),
);

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<pages>
<uid>1</uid>
<pid>0</pid>
<title>Page with uid 1 below 0</title>
<deleted>0</deleted>
</pages>
</dataset>

View file

@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
/*
* Copyright (C) 2023 Daniel Siepmann <coding@daniel-siepmann.de>
*
* 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',
],
];
}
}

13
bin/typo3-php-datasets Executable file
View file

@ -0,0 +1,13 @@
#!/usr/bin/env php
<?php
require $_composer_autoload_path ?? __DIR__ . '/../vendor/autoload.php';
use Codappix\Typo3PhpDatasets\Command\ConvertFromXml;
use Symfony\Component\Console\Application;
$application = new Application();
$application->add(new ConvertFromXml());
$application->run();

View file

@ -19,9 +19,14 @@
"email": "coding@daniel-siepmann.de" "email": "coding@daniel-siepmann.de"
} }
], ],
"bin": [
"bin/typo3-php-datasets"
],
"require": { "require": {
"php": "^7.3 || ^7.4 || ^8.0 || ^8.1 || ^8.2", "php": "^7.3 || ^7.4 || ^8.0 || ^8.1 || ^8.2",
"composer-runtime-api": "^2.2",
"doctrine/dbal": "^2.13", "doctrine/dbal": "^2.13",
"symfony/console": "^5.4",
"typo3/cms-core": "^10.4 || ^11.5" "typo3/cms-core": "^10.4 || ^11.5"
}, },
"require-dev": { "require-dev": {
@ -41,6 +46,8 @@
"allow-plugins": { "allow-plugins": {
"typo3/class-alias-loader": true, "typo3/class-alias-loader": true,
"typo3/cms-composer-installers": true "typo3/cms-composer-installers": true
} },
"lock": false,
"sort-packages": true
} }
} }

View file

@ -27,19 +27,26 @@ let
composer install --prefer-dist --no-progress --working-dir="$PROJECT_ROOT" 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 { in pkgs.mkShell {
name = "TYPO3 PHP Datasets"; name = "TYPO3 PHP Datasets";
buildInputs = [ buildInputs = [
projectInstall projectInstall
projectCgl
phpWithXdebug phpWithXdebug
composer composer
pkgs.parallel
]; ];
shellHook = '' shellHook = ''
export PROJECT_ROOT="$(pwd)"
export typo3DatabaseDriver=pdo_sqlite export typo3DatabaseDriver=pdo_sqlite
''; '';
} }