416 lines
15 KiB
PHP
416 lines
15 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace PhpMyAdmin\Tests\Plugins\Export;
|
|
|
|
use PhpMyAdmin\Config;
|
|
use PhpMyAdmin\Config\Settings\Export;
|
|
use PhpMyAdmin\ConfigStorage\Relation;
|
|
use PhpMyAdmin\Current;
|
|
use PhpMyAdmin\Dbal\DatabaseInterface;
|
|
use PhpMyAdmin\Export\OutputHandler;
|
|
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
|
|
use PhpMyAdmin\Plugins\Export\ExportXml;
|
|
use PhpMyAdmin\Plugins\ExportPlugin;
|
|
use PhpMyAdmin\Plugins\ExportType;
|
|
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
|
|
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
|
|
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
|
|
use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
|
|
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
|
|
use PhpMyAdmin\Tests\AbstractTestCase;
|
|
use PhpMyAdmin\Transformations;
|
|
use PHPUnit\Framework\Attributes\CoversClass;
|
|
use PHPUnit\Framework\Attributes\Medium;
|
|
use ReflectionMethod;
|
|
use ReflectionProperty;
|
|
|
|
use function ob_get_clean;
|
|
use function ob_start;
|
|
|
|
#[CoversClass(ExportXml::class)]
|
|
#[Medium]
|
|
final class ExportXmlTest extends AbstractTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
OutputHandler::$asFile = false;
|
|
ExportPlugin::$exportType = ExportType::Table;
|
|
ExportPlugin::$singleTable = false;
|
|
Current::$database = 'db';
|
|
}
|
|
|
|
public function testSetProperties(): void
|
|
{
|
|
$exportXml = $this->getExportXml();
|
|
|
|
$method = new ReflectionMethod(ExportXml::class, 'setProperties');
|
|
$method->invoke($exportXml, null);
|
|
|
|
$attrProperties = new ReflectionProperty(ExportXml::class, 'properties');
|
|
$properties = $attrProperties->getValue($exportXml);
|
|
|
|
self::assertInstanceOf(ExportPluginProperties::class, $properties);
|
|
|
|
self::assertSame(
|
|
'XML',
|
|
$properties->getText(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'xml',
|
|
$properties->getExtension(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'text/xml',
|
|
$properties->getMimeType(),
|
|
);
|
|
|
|
$options = $properties->getOptions();
|
|
|
|
self::assertInstanceOf(OptionsPropertyRootGroup::class, $options);
|
|
|
|
self::assertSame(
|
|
'Format Specific Options',
|
|
$options->getName(),
|
|
);
|
|
|
|
$generalOptionsArray = $options->getProperties();
|
|
|
|
$generalOptions = $generalOptionsArray->current();
|
|
$generalOptionsArray->next();
|
|
|
|
self::assertInstanceOf(OptionsPropertyMainGroup::class, $generalOptions);
|
|
|
|
self::assertSame(
|
|
'xml_general_opts',
|
|
$generalOptions->getName(),
|
|
);
|
|
|
|
$generalProperties = $generalOptions->getProperties();
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(HiddenPropertyItem::class, $property);
|
|
|
|
$generalOptions = $generalOptionsArray->current();
|
|
$generalOptionsArray->next();
|
|
|
|
self::assertInstanceOf(OptionsPropertyMainGroup::class, $generalOptions);
|
|
|
|
self::assertSame(
|
|
'xml_structure',
|
|
$generalOptions->getName(),
|
|
);
|
|
|
|
$generalProperties = $generalOptions->getProperties();
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
|
|
$generalOptions = $generalOptionsArray->current();
|
|
|
|
self::assertInstanceOf(OptionsPropertyMainGroup::class, $generalOptions);
|
|
|
|
self::assertSame(
|
|
'xml_data',
|
|
$generalOptions->getName(),
|
|
);
|
|
|
|
$generalProperties = $generalOptions->getProperties();
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
}
|
|
|
|
public function testExportHeader(): void
|
|
{
|
|
Config::getInstance()->selectedServer['DisableIS'] = false;
|
|
Current::$charset = 'iso-8859-1';
|
|
Current::$database = 'd<"b';
|
|
|
|
$functions = [['fn']];
|
|
$procedures = [['pr']];
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$dbi = $this->createDatabaseInterface($dbiDummy);
|
|
|
|
$dbiDummy->addResult(
|
|
'SELECT `DEFAULT_CHARACTER_SET_NAME`, `DEFAULT_COLLATION_NAME`'
|
|
. " FROM `information_schema`.`SCHEMATA` WHERE `SCHEMA_NAME` = 'd<\\\"b' LIMIT 1",
|
|
[['utf-8', 'utf8_general_ci']],
|
|
['DEFAULT_CHARACTER_SET_NAME', 'DEFAULT_COLLATION_NAME'],
|
|
);
|
|
$dbiDummy->addResult(
|
|
'SELECT SPECIFIC_NAME FROM information_schema.ROUTINES '
|
|
. "WHERE ROUTINE_SCHEMA = 'd<\\\"b' AND ROUTINE_TYPE = 'FUNCTION' AND SPECIFIC_NAME != ''",
|
|
$functions,
|
|
['Name'],
|
|
);
|
|
$dbiDummy->addResult(
|
|
'SELECT SPECIFIC_NAME FROM information_schema.ROUTINES '
|
|
. "WHERE ROUTINE_SCHEMA = 'd<\\\"b' AND ROUTINE_TYPE = 'PROCEDURE' AND SPECIFIC_NAME != ''",
|
|
$procedures,
|
|
['Name'],
|
|
);
|
|
$dbiDummy->addResult('SHOW CREATE TABLE `d<"b`.`table`', [['table', '"tbl"']]);
|
|
$dbiDummy->addResult(
|
|
'SELECT 1 FROM information_schema.VIEWS WHERE TABLE_SCHEMA = \'d<\"b\' AND TABLE_NAME = \'table\'',
|
|
[],
|
|
);
|
|
$dbiDummy->addResult(
|
|
'SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE,'
|
|
. ' ACTION_TIMING, ACTION_STATEMENT, EVENT_OBJECT_SCHEMA, EVENT_OBJECT_TABLE, DEFINER FROM'
|
|
. ' information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA COLLATE utf8_bin= \'d<\"b\' AND'
|
|
. ' EVENT_OBJECT_TABLE COLLATE utf8_bin = \'table\';',
|
|
[
|
|
[
|
|
'd<"b',
|
|
'trname',
|
|
'INSERT',
|
|
'table',
|
|
'AFTER',
|
|
'BEGIN END',
|
|
'd<"b',
|
|
'test_user@localhost',
|
|
],
|
|
],
|
|
[
|
|
'TRIGGER_SCHEMA',
|
|
'TRIGGER_NAME',
|
|
'EVENT_MANIPULATION',
|
|
'EVENT_OBJECT_TABLE',
|
|
'ACTION_TIMING',
|
|
'ACTION_STATEMENT',
|
|
'EVENT_OBJECT_SCHEMA',
|
|
'DEFINER',
|
|
],
|
|
);
|
|
$dbiDummy->addResult('SHOW CREATE FUNCTION `d<"b`.`fn`', [['fn', 'fndef']], ['name', 'Create Function']);
|
|
$dbiDummy->addResult('SHOW CREATE PROCEDURE `d<"b`.`pr`', [['pr', 'prdef']], ['name', 'Create Procedure']);
|
|
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody([
|
|
'xml_export_contents' => 'On',
|
|
'xml_export_functions' => 'On',
|
|
'xml_export_procedures' => 'On',
|
|
'xml_export_tables' => 'On',
|
|
'xml_export_triggers' => 'On',
|
|
]);
|
|
|
|
$exportXml = $this->getExportXml($dbi);
|
|
$exportXml->setExportOptions($request, new Export());
|
|
|
|
$exportXml->setTables([]);
|
|
Current::$table = 'table';
|
|
|
|
ob_start();
|
|
$exportXml->exportHeader();
|
|
$result = ob_get_clean();
|
|
|
|
self::assertIsString($result);
|
|
|
|
self::assertStringContainsString(
|
|
'<pma_xml_export version="1.0" xmlns:pma="' .
|
|
'https://www.phpmyadmin.net/some_doc_url/">',
|
|
$result,
|
|
);
|
|
|
|
self::assertStringContainsString(
|
|
'<pma:structure_schemas>' . "\n" .
|
|
' <pma:database name="d&lt;&quot;b" collat' .
|
|
'ion="utf8_general_ci" charset="utf-8">' . "\n" .
|
|
' <pma:table name="table">' . "\n" .
|
|
' &quot;tbl&quot;;' . "\n" .
|
|
' </pma:table>' . "\n" .
|
|
' <pma:trigger name="trname">' . "\n" .
|
|
' CREATE TRIGGER `trname` AFTER INSERT ON `table`' . "\n" .
|
|
' FOR EACH ROW BEGIN END' . "\n" .
|
|
' </pma:trigger>' . "\n" .
|
|
' <pma:function name="fn">' . "\n" .
|
|
' fndef' . "\n" .
|
|
' </pma:function>' . "\n" .
|
|
' <pma:procedure name="pr">' . "\n" .
|
|
' prdef' . "\n" .
|
|
' </pma:procedure>' . "\n" .
|
|
' </pma:database>' . "\n" .
|
|
' </pma:structure_schemas>',
|
|
$result,
|
|
);
|
|
|
|
// case 2 with isView as true and false
|
|
|
|
$dbiDummy->addResult(
|
|
'SELECT `DEFAULT_CHARACTER_SET_NAME`, `DEFAULT_COLLATION_NAME`'
|
|
. " FROM `information_schema`.`SCHEMATA` WHERE `SCHEMA_NAME` = 'd<\\\"b' LIMIT 1",
|
|
[['utf-8', 'utf8_general_ci']],
|
|
['DEFAULT_CHARACTER_SET_NAME', 'DEFAULT_COLLATION_NAME'],
|
|
);
|
|
$dbiDummy->addResult('SHOW CREATE TABLE `d<"b`.`t1`', [['t1', '"tbl"']]);
|
|
$dbiDummy->addResult(
|
|
'SELECT 1 FROM information_schema.VIEWS WHERE TABLE_SCHEMA = \'d<\"b\' AND TABLE_NAME = \'t1\'',
|
|
[],
|
|
);
|
|
$dbiDummy->addResult('SHOW CREATE TABLE `d<"b`.`t2`', [['t2', '"tbl"']]);
|
|
$dbiDummy->addResult(
|
|
'SELECT 1 FROM information_schema.VIEWS WHERE TABLE_SCHEMA = \'d<\"b\' AND TABLE_NAME = \'t2\'',
|
|
[],
|
|
);
|
|
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody(['xml_export_triggers' => 'On']);
|
|
|
|
$exportXml = $this->getExportXml($dbi);
|
|
$exportXml->setExportOptions($request, new Export());
|
|
|
|
$exportXml->setTables(['t1', 't2']);
|
|
|
|
ob_start();
|
|
$exportXml->exportHeader();
|
|
$result = ob_get_clean();
|
|
|
|
self::assertIsString($result);
|
|
|
|
self::assertStringContainsString(
|
|
'<pma:structure_schemas>' . "\n" .
|
|
' <pma:database name="d&lt;&quot;b" collat' .
|
|
'ion="utf8_general_ci" charset="utf-8">' . "\n" .
|
|
' </pma:database>' . "\n" .
|
|
' </pma:structure_schemas>',
|
|
$result,
|
|
);
|
|
$dbiDummy->assertAllQueriesConsumed();
|
|
}
|
|
|
|
public function testExportFooter(): void
|
|
{
|
|
$exportXml = $this->getExportXml();
|
|
$this->expectOutputString('</pma_xml_export>');
|
|
$exportXml->exportFooter();
|
|
}
|
|
|
|
public function testExportDBHeader(): void
|
|
{
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody(['xml_export_contents' => 'On']);
|
|
|
|
$exportXml = $this->getExportXml();
|
|
$exportXml->setExportOptions($request, new Export());
|
|
|
|
ob_start();
|
|
$exportXml->exportDBHeader('&db');
|
|
$result = ob_get_clean();
|
|
|
|
self::assertIsString($result);
|
|
|
|
self::assertStringContainsString('<database name="&amp;db">', $result);
|
|
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody([]);
|
|
|
|
$exportXml->setExportOptions($request, new Export());
|
|
|
|
$exportXml->exportDBHeader('&db');
|
|
}
|
|
|
|
public function testExportDBFooter(): void
|
|
{
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody(['xml_export_contents' => 'On']);
|
|
|
|
$exportXml = $this->getExportXml();
|
|
$exportXml->setExportOptions($request, new Export());
|
|
|
|
ob_start();
|
|
$exportXml->exportDBFooter('&db');
|
|
$result = ob_get_clean();
|
|
|
|
self::assertIsString($result);
|
|
|
|
self::assertStringContainsString('</database>', $result);
|
|
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody([]);
|
|
|
|
$exportXml->setExportOptions($request, new Export());
|
|
|
|
$exportXml->exportDBFooter('&db');
|
|
}
|
|
|
|
public function testExportDBCreate(): void
|
|
{
|
|
$exportXml = $this->getExportXml();
|
|
$this->expectNotToPerformAssertions();
|
|
$exportXml->exportDBCreate('testDB');
|
|
}
|
|
|
|
public function testExportData(): void
|
|
{
|
|
Config::getInstance()->selectedServer['DisableIS'] = true;
|
|
|
|
OutputHandler::$asFile = true;
|
|
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody(['xml_export_contents' => 'On']);
|
|
|
|
$exportXml = $this->getExportXml();
|
|
$exportXml->setExportOptions($request, new Export());
|
|
|
|
ob_start();
|
|
$exportXml->exportData('test_db', 'test_table', 'SELECT * FROM `test_db`.`test_table`;');
|
|
$result = ob_get_clean();
|
|
|
|
self::assertIsString($result);
|
|
self::assertSame(
|
|
' <!-- Table test_table -->' . "\n"
|
|
. ' <table name="test_table">' . "\n"
|
|
. ' <column name="id">1</column>' . "\n"
|
|
. ' <column name="name">abcd</column>' . "\n"
|
|
. ' <column name="datetimefield">2011-01-20 02:00:02</column>' . "\n"
|
|
. ' </table>' . "\n"
|
|
. ' <table name="test_table">' . "\n"
|
|
. ' <column name="id">2</column>' . "\n"
|
|
. ' <column name="name">foo</column>' . "\n"
|
|
. ' <column name="datetimefield">2010-01-20 02:00:02</column>' . "\n"
|
|
. ' </table>' . "\n"
|
|
. ' <table name="test_table">' . "\n"
|
|
. ' <column name="id">3</column>' . "\n"
|
|
. ' <column name="name">Abcd</column>' . "\n"
|
|
. ' <column name="datetimefield">2012-01-20 02:00:02</column>' . "\n"
|
|
. ' </table>' . "\n",
|
|
$result,
|
|
);
|
|
}
|
|
|
|
private function getExportXml(DatabaseInterface|null $dbi = null): ExportXml
|
|
{
|
|
$dbi ??= $this->createDatabaseInterface();
|
|
$config = new Config();
|
|
$relation = new Relation($dbi, $config);
|
|
|
|
return new ExportXml($relation, new OutputHandler(), new Transformations($dbi, $relation), $dbi, $config);
|
|
}
|
|
}
|