ExportXml.php 18 KB

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