ExportXml.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Set of functions used to build XML dumps of tables
  5. *
  6. * @package PhpMyAdmin-Export
  7. * @subpackage XML
  8. */
  9. declare(strict_types=1);
  10. namespace PhpMyAdmin\Plugins\Export;
  11. use PhpMyAdmin\DatabaseInterface;
  12. use PhpMyAdmin\Export;
  13. use PhpMyAdmin\Plugins\ExportPlugin;
  14. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
  15. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
  16. use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
  17. use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
  18. use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
  19. use PhpMyAdmin\Util;
  20. /* Can't do server export */
  21. if (! isset($GLOBALS['db']) || strlen($GLOBALS['db']) === 0) {
  22. $GLOBALS['skip_import'] = true;
  23. return;
  24. }
  25. /**
  26. * Handles the export for the XML class
  27. *
  28. * @package PhpMyAdmin-Export
  29. * @subpackage XML
  30. */
  31. class ExportXml extends ExportPlugin
  32. {
  33. /**
  34. * Table name
  35. *
  36. * @var string
  37. */
  38. private $_table;
  39. /**
  40. * Table names
  41. *
  42. * @var array
  43. */
  44. private $_tables;
  45. /**
  46. * Constructor
  47. */
  48. public function __construct()
  49. {
  50. parent::__construct();
  51. $this->setProperties();
  52. }
  53. /**
  54. * Initialize the local variables that are used for export XML
  55. *
  56. * @return void
  57. */
  58. protected function initSpecificVariables()
  59. {
  60. global $table, $tables;
  61. $this->_setTable($table);
  62. if (is_array($tables)) {
  63. $this->_setTables($tables);
  64. }
  65. }
  66. /**
  67. * Sets the export XML properties
  68. *
  69. * @return void
  70. */
  71. protected function setProperties()
  72. {
  73. // create the export plugin property item
  74. $exportPluginProperties = new ExportPluginProperties();
  75. $exportPluginProperties->setText('XML');
  76. $exportPluginProperties->setExtension('xml');
  77. $exportPluginProperties->setMimeType('text/xml');
  78. $exportPluginProperties->setOptionsText(__('Options'));
  79. // create the root group that will be the options field for
  80. // $exportPluginProperties
  81. // this will be shown as "Format specific options"
  82. $exportSpecificOptions = new OptionsPropertyRootGroup(
  83. "Format Specific Options"
  84. );
  85. // general options main group
  86. $generalOptions = new OptionsPropertyMainGroup("general_opts");
  87. // create primary items and add them to the group
  88. $leaf = new HiddenPropertyItem("structure_or_data");
  89. $generalOptions->addProperty($leaf);
  90. // add the main group to the root group
  91. $exportSpecificOptions->addProperty($generalOptions);
  92. // export structure main group
  93. $structure = new OptionsPropertyMainGroup(
  94. "structure",
  95. __('Object creation options (all are recommended)')
  96. );
  97. // create primary items and add them to the group
  98. $leaf = new BoolPropertyItem(
  99. "export_events",
  100. __('Events')
  101. );
  102. $structure->addProperty($leaf);
  103. $leaf = new BoolPropertyItem(
  104. "export_functions",
  105. __('Functions')
  106. );
  107. $structure->addProperty($leaf);
  108. $leaf = new BoolPropertyItem(
  109. "export_procedures",
  110. __('Procedures')
  111. );
  112. $structure->addProperty($leaf);
  113. $leaf = new BoolPropertyItem(
  114. "export_tables",
  115. __('Tables')
  116. );
  117. $structure->addProperty($leaf);
  118. $leaf = new BoolPropertyItem(
  119. "export_triggers",
  120. __('Triggers')
  121. );
  122. $structure->addProperty($leaf);
  123. $leaf = new BoolPropertyItem(
  124. "export_views",
  125. __('Views')
  126. );
  127. $structure->addProperty($leaf);
  128. $exportSpecificOptions->addProperty($structure);
  129. // data main group
  130. $data = new OptionsPropertyMainGroup(
  131. "data",
  132. __('Data dump options')
  133. );
  134. // create primary items and add them to the group
  135. $leaf = new BoolPropertyItem(
  136. "export_contents",
  137. __('Export contents')
  138. );
  139. $data->addProperty($leaf);
  140. $exportSpecificOptions->addProperty($data);
  141. // set the options for the export plugin property item
  142. $exportPluginProperties->setOptions($exportSpecificOptions);
  143. $this->properties = $exportPluginProperties;
  144. }
  145. /**
  146. * Generates output for SQL defintions of routines
  147. *
  148. * @param string $db Database name
  149. * @param string $type Item type to be used in XML output
  150. * @param string $dbitype Item type used in DBI qieries
  151. *
  152. * @return string XML with definitions
  153. */
  154. private function _exportRoutines($db, $type, $dbitype)
  155. {
  156. // Export routines
  157. $routines = $GLOBALS['dbi']->getProceduresOrFunctions(
  158. $db,
  159. $dbitype
  160. );
  161. return $this->_exportDefinitions($db, $type, $dbitype, $routines);
  162. }
  163. /**
  164. * Generates output for SQL defintions
  165. *
  166. * @param string $db Database name
  167. * @param string $type Item type to be used in XML output
  168. * @param string $dbitype Item type used in DBI qieries
  169. * @param array $names Names of items to export
  170. *
  171. * @return string XML with definitions
  172. */
  173. private function _exportDefinitions($db, $type, $dbitype, array $names)
  174. {
  175. global $crlf;
  176. $head = '';
  177. if ($names) {
  178. foreach ($names as $name) {
  179. $head .= ' <pma:' . $type . ' name="'
  180. . htmlspecialchars($name) . '">' . $crlf;
  181. // Do some formatting
  182. $sql = $GLOBALS['dbi']->getDefinition($db, $dbitype, $name);
  183. $sql = htmlspecialchars(rtrim($sql));
  184. $sql = str_replace("\n", "\n ", $sql);
  185. $head .= " " . $sql . $crlf;
  186. $head .= ' </pma:' . $type . '>' . $crlf;
  187. }
  188. }
  189. return $head;
  190. }
  191. /**
  192. * Outputs export header. It is the first method to be called, so all
  193. * the required variables are initialized here.
  194. *
  195. * @return bool Whether it succeeded
  196. */
  197. public function exportHeader()
  198. {
  199. $this->initSpecificVariables();
  200. global $crlf, $cfg, $db;
  201. $table = $this->_getTable();
  202. $tables = $this->_getTables();
  203. $export_struct = isset($GLOBALS['xml_export_functions'])
  204. || isset($GLOBALS['xml_export_procedures'])
  205. || isset($GLOBALS['xml_export_tables'])
  206. || isset($GLOBALS['xml_export_triggers'])
  207. || isset($GLOBALS['xml_export_views']);
  208. $export_data = isset($GLOBALS['xml_export_contents']) ? true : false;
  209. if ($GLOBALS['output_charset_conversion']) {
  210. $charset = $GLOBALS['charset'];
  211. } else {
  212. $charset = 'utf-8';
  213. }
  214. $head = '<?xml version="1.0" encoding="' . $charset . '"?>' . $crlf
  215. . '<!--' . $crlf
  216. . '- phpMyAdmin XML Dump' . $crlf
  217. . '- version ' . PMA_VERSION . $crlf
  218. . '- https://www.phpmyadmin.net' . $crlf
  219. . '-' . $crlf
  220. . '- ' . __('Host:') . ' ' . htmlspecialchars($cfg['Server']['host']);
  221. if (! empty($cfg['Server']['port'])) {
  222. $head .= ':' . $cfg['Server']['port'];
  223. }
  224. $head .= $crlf
  225. . '- ' . __('Generation Time:') . ' '
  226. . Util::localisedDate() . $crlf
  227. . '- ' . __('Server version:') . ' ' . $GLOBALS['dbi']->getVersionString() . $crlf
  228. . '- ' . __('PHP Version:') . ' ' . PHP_VERSION . $crlf
  229. . '-->' . $crlf . $crlf;
  230. $head .= '<pma_xml_export version="1.0"'
  231. . ($export_struct
  232. ? ' xmlns:pma="https://www.phpmyadmin.net/some_doc_url/"'
  233. : '')
  234. . '>' . $crlf;
  235. if ($export_struct) {
  236. $result = $GLOBALS['dbi']->fetchResult(
  237. 'SELECT `DEFAULT_CHARACTER_SET_NAME`, `DEFAULT_COLLATION_NAME`'
  238. . ' FROM `information_schema`.`SCHEMATA` WHERE `SCHEMA_NAME`'
  239. . ' = \'' . $GLOBALS['dbi']->escapeString($db) . '\' LIMIT 1'
  240. );
  241. $db_collation = $result[0]['DEFAULT_COLLATION_NAME'];
  242. $db_charset = $result[0]['DEFAULT_CHARACTER_SET_NAME'];
  243. $head .= ' <!--' . $crlf;
  244. $head .= ' - Structure schemas' . $crlf;
  245. $head .= ' -->' . $crlf;
  246. $head .= ' <pma:structure_schemas>' . $crlf;
  247. $head .= ' <pma:database name="' . htmlspecialchars($db)
  248. . '" collation="' . htmlspecialchars($db_collation) . '" charset="' . htmlspecialchars($db_charset)
  249. . '">' . $crlf;
  250. if ($tables === null) {
  251. $tables = [];
  252. }
  253. if (count($tables) === 0) {
  254. $tables[] = $table;
  255. }
  256. foreach ($tables as $table) {
  257. // Export tables and views
  258. $result = $GLOBALS['dbi']->fetchResult(
  259. 'SHOW CREATE TABLE ' . Util::backquote($db) . '.'
  260. . Util::backquote($table),
  261. 0
  262. );
  263. $tbl = $result[$table][1];
  264. $is_view = $GLOBALS['dbi']->getTable($db, $table)
  265. ->isView();
  266. if ($is_view) {
  267. $type = 'view';
  268. } else {
  269. $type = 'table';
  270. }
  271. if ($is_view && ! isset($GLOBALS['xml_export_views'])) {
  272. continue;
  273. }
  274. if (! $is_view && ! isset($GLOBALS['xml_export_tables'])) {
  275. continue;
  276. }
  277. $head .= ' <pma:' . $type . ' name="' . htmlspecialchars($table) . '">'
  278. . $crlf;
  279. $tbl = " " . htmlspecialchars($tbl);
  280. $tbl = str_replace("\n", "\n ", $tbl);
  281. $head .= $tbl . ';' . $crlf;
  282. $head .= ' </pma:' . $type . '>' . $crlf;
  283. if (isset($GLOBALS['xml_export_triggers'])
  284. && $GLOBALS['xml_export_triggers']
  285. ) {
  286. // Export triggers
  287. $triggers = $GLOBALS['dbi']->getTriggers($db, $table);
  288. if ($triggers) {
  289. foreach ($triggers as $trigger) {
  290. $code = $trigger['create'];
  291. $head .= ' <pma:trigger name="'
  292. . htmlspecialchars($trigger['name']) . '">' . $crlf;
  293. // Do some formatting
  294. $code = mb_substr(rtrim($code), 0, -3);
  295. $code = " " . htmlspecialchars($code);
  296. $code = str_replace("\n", "\n ", $code);
  297. $head .= $code . $crlf;
  298. $head .= ' </pma:trigger>' . $crlf;
  299. }
  300. unset($trigger);
  301. unset($triggers);
  302. }
  303. }
  304. }
  305. if (isset($GLOBALS['xml_export_functions'])
  306. && $GLOBALS['xml_export_functions']
  307. ) {
  308. $head .= $this->_exportRoutines($db, 'function', 'FUNCTION');
  309. }
  310. if (isset($GLOBALS['xml_export_procedures'])
  311. && $GLOBALS['xml_export_procedures']
  312. ) {
  313. $head .= $this->_exportRoutines($db, 'procedure', 'PROCEDURE');
  314. }
  315. if (isset($GLOBALS['xml_export_events'])
  316. && $GLOBALS['xml_export_events']
  317. ) {
  318. // Export events
  319. $events = $GLOBALS['dbi']->fetchResult(
  320. "SELECT EVENT_NAME FROM information_schema.EVENTS "
  321. . "WHERE EVENT_SCHEMA='" . $GLOBALS['dbi']->escapeString($db)
  322. . "'"
  323. );
  324. $head .= $this->_exportDefinitions(
  325. $db,
  326. 'event',
  327. 'EVENT',
  328. $events
  329. );
  330. }
  331. unset($result);
  332. $head .= ' </pma:database>' . $crlf;
  333. $head .= ' </pma:structure_schemas>' . $crlf;
  334. if ($export_data) {
  335. $head .= $crlf;
  336. }
  337. }
  338. return $this->export->outputHandler($head);
  339. }
  340. /**
  341. * Outputs export footer
  342. *
  343. * @return bool Whether it succeeded
  344. */
  345. public function exportFooter()
  346. {
  347. $foot = '</pma_xml_export>';
  348. return $this->export->outputHandler($foot);
  349. }
  350. /**
  351. * Outputs database header
  352. *
  353. * @param string $db Database name
  354. * @param string $db_alias Aliases of db
  355. *
  356. * @return bool Whether it succeeded
  357. */
  358. public function exportDBHeader($db, $db_alias = '')
  359. {
  360. global $crlf;
  361. if (empty($db_alias)) {
  362. $db_alias = $db;
  363. }
  364. if (isset($GLOBALS['xml_export_contents'])
  365. && $GLOBALS['xml_export_contents']
  366. ) {
  367. $head = ' <!--' . $crlf
  368. . ' - ' . __('Database:') . ' \''
  369. . htmlspecialchars($db_alias) . '\'' . $crlf
  370. . ' -->' . $crlf . ' <database name="'
  371. . htmlspecialchars($db_alias) . '">' . $crlf;
  372. return $this->export->outputHandler($head);
  373. }
  374. return true;
  375. }
  376. /**
  377. * Outputs database footer
  378. *
  379. * @param string $db Database name
  380. *
  381. * @return bool Whether it succeeded
  382. */
  383. public function exportDBFooter($db)
  384. {
  385. global $crlf;
  386. if (isset($GLOBALS['xml_export_contents'])
  387. && $GLOBALS['xml_export_contents']
  388. ) {
  389. return $this->export->outputHandler(' </database>' . $crlf);
  390. }
  391. return true;
  392. }
  393. /**
  394. * Outputs CREATE DATABASE statement
  395. *
  396. * @param string $db Database name
  397. * @param string $export_type 'server', 'database', 'table'
  398. * @param string $db_alias Aliases of db
  399. *
  400. * @return bool Whether it succeeded
  401. */
  402. public function exportDBCreate($db, $export_type, $db_alias = '')
  403. {
  404. return true;
  405. }
  406. /**
  407. * Outputs the content of a table in XML format
  408. *
  409. * @param string $db database name
  410. * @param string $table table name
  411. * @param string $crlf the end of line sequence
  412. * @param string $error_url the url to go back in case of error
  413. * @param string $sql_query SQL query for obtaining data
  414. * @param array $aliases Aliases of db/table/columns
  415. *
  416. * @return bool Whether it succeeded
  417. */
  418. public function exportData(
  419. $db,
  420. $table,
  421. $crlf,
  422. $error_url,
  423. $sql_query,
  424. array $aliases = []
  425. ) {
  426. // Do not export data for merge tables
  427. if ($GLOBALS['dbi']->getTable($db, $table)->isMerge()) {
  428. return true;
  429. }
  430. $db_alias = $db;
  431. $table_alias = $table;
  432. $this->initAlias($aliases, $db_alias, $table_alias);
  433. if (isset($GLOBALS['xml_export_contents'])
  434. && $GLOBALS['xml_export_contents']
  435. ) {
  436. $result = $GLOBALS['dbi']->query(
  437. $sql_query,
  438. DatabaseInterface::CONNECT_USER,
  439. DatabaseInterface::QUERY_UNBUFFERED
  440. );
  441. $columns_cnt = $GLOBALS['dbi']->numFields($result);
  442. $columns = [];
  443. for ($i = 0; $i < $columns_cnt; $i++) {
  444. $columns[$i] = stripslashes($GLOBALS['dbi']->fieldName($result, $i));
  445. }
  446. unset($i);
  447. $buffer = ' <!-- ' . __('Table') . ' '
  448. . htmlspecialchars($table_alias) . ' -->' . $crlf;
  449. if (! $this->export->outputHandler($buffer)) {
  450. return false;
  451. }
  452. while ($record = $GLOBALS['dbi']->fetchRow($result)) {
  453. $buffer = ' <table name="'
  454. . htmlspecialchars($table_alias) . '">' . $crlf;
  455. for ($i = 0; $i < $columns_cnt; $i++) {
  456. $col_as = $columns[$i];
  457. if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])
  458. ) {
  459. $col_as
  460. = $aliases[$db]['tables'][$table]['columns'][$col_as];
  461. }
  462. // If a cell is NULL, still export it to preserve
  463. // the XML structure
  464. if (! isset($record[$i]) || $record[$i] === null) {
  465. $record[$i] = 'NULL';
  466. }
  467. $buffer .= ' <column name="'
  468. . htmlspecialchars($col_as) . '">'
  469. . htmlspecialchars((string) $record[$i])
  470. . '</column>' . $crlf;
  471. }
  472. $buffer .= ' </table>' . $crlf;
  473. if (! $this->export->outputHandler($buffer)) {
  474. return false;
  475. }
  476. }
  477. $GLOBALS['dbi']->freeResult($result);
  478. }
  479. return true;
  480. }
  481. /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */
  482. /**
  483. * Gets the table name
  484. *
  485. * @return string
  486. */
  487. private function _getTable()
  488. {
  489. return $this->_table;
  490. }
  491. /**
  492. * Sets the table name
  493. *
  494. * @param string $table table name
  495. *
  496. * @return void
  497. */
  498. private function _setTable($table)
  499. {
  500. $this->_table = $table;
  501. }
  502. /**
  503. * Gets the table names
  504. *
  505. * @return array
  506. */
  507. private function _getTables()
  508. {
  509. return $this->_tables;
  510. }
  511. /**
  512. * Sets the table names
  513. *
  514. * @param array $tables table names
  515. *
  516. * @return void
  517. */
  518. private function _setTables(array $tables)
  519. {
  520. $this->_tables = $tables;
  521. }
  522. }