ExportCsv.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. <?php
  2. /**
  3. * CSV export code
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin\Plugins\Export;
  7. use PhpMyAdmin\DatabaseInterface;
  8. use PhpMyAdmin\Plugins\ExportPlugin;
  9. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
  10. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
  11. use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
  12. use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
  13. use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
  14. use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
  15. use function mb_strtolower;
  16. use function mb_substr;
  17. use function preg_replace;
  18. use function str_replace;
  19. use function stripslashes;
  20. use function trim;
  21. /**
  22. * Handles the export for the CSV format
  23. */
  24. class ExportCsv extends ExportPlugin
  25. {
  26. public function __construct()
  27. {
  28. parent::__construct();
  29. $this->setProperties();
  30. }
  31. /**
  32. * Sets the export CSV properties
  33. *
  34. * @return void
  35. */
  36. protected function setProperties()
  37. {
  38. $exportPluginProperties = new ExportPluginProperties();
  39. $exportPluginProperties->setText('CSV');
  40. $exportPluginProperties->setExtension('csv');
  41. $exportPluginProperties->setMimeType('text/comma-separated-values');
  42. $exportPluginProperties->setOptionsText(__('Options'));
  43. // create the root group that will be the options field for
  44. // $exportPluginProperties
  45. // this will be shown as "Format specific options"
  46. $exportSpecificOptions = new OptionsPropertyRootGroup(
  47. 'Format Specific Options'
  48. );
  49. // general options main group
  50. $generalOptions = new OptionsPropertyMainGroup('general_opts');
  51. // create leaf items and add them to the group
  52. $leaf = new TextPropertyItem(
  53. 'separator',
  54. __('Columns separated with:')
  55. );
  56. $generalOptions->addProperty($leaf);
  57. $leaf = new TextPropertyItem(
  58. 'enclosed',
  59. __('Columns enclosed with:')
  60. );
  61. $generalOptions->addProperty($leaf);
  62. $leaf = new TextPropertyItem(
  63. 'escaped',
  64. __('Columns escaped with:')
  65. );
  66. $generalOptions->addProperty($leaf);
  67. $leaf = new TextPropertyItem(
  68. 'terminated',
  69. __('Lines terminated with:')
  70. );
  71. $generalOptions->addProperty($leaf);
  72. $leaf = new TextPropertyItem(
  73. 'null',
  74. __('Replace NULL with:')
  75. );
  76. $generalOptions->addProperty($leaf);
  77. $leaf = new BoolPropertyItem(
  78. 'removeCRLF',
  79. __('Remove carriage return/line feed characters within columns')
  80. );
  81. $generalOptions->addProperty($leaf);
  82. $leaf = new BoolPropertyItem(
  83. 'columns',
  84. __('Put columns names in the first row')
  85. );
  86. $generalOptions->addProperty($leaf);
  87. $leaf = new HiddenPropertyItem(
  88. 'structure_or_data'
  89. );
  90. $generalOptions->addProperty($leaf);
  91. // add the main group to the root group
  92. $exportSpecificOptions->addProperty($generalOptions);
  93. // set the options for the export plugin property item
  94. $exportPluginProperties->setOptions($exportSpecificOptions);
  95. $this->properties = $exportPluginProperties;
  96. }
  97. /**
  98. * Outputs export header
  99. *
  100. * @return bool Whether it succeeded
  101. */
  102. public function exportHeader()
  103. {
  104. global $what, $csv_terminated, $csv_separator, $csv_enclosed, $csv_escaped;
  105. //Enable columns names by default for CSV
  106. if ($what === 'csv') {
  107. $GLOBALS['csv_columns'] = 'yes';
  108. }
  109. // Here we just prepare some values for export
  110. if ($what === 'excel') {
  111. $csv_terminated = "\015\012";
  112. switch ($GLOBALS['excel_edition']) {
  113. case 'win':
  114. // as tested on Windows with Excel 2002 and Excel 2007
  115. $csv_separator = ';';
  116. break;
  117. case 'mac_excel2003':
  118. $csv_separator = ';';
  119. break;
  120. case 'mac_excel2008':
  121. $csv_separator = ',';
  122. break;
  123. }
  124. $csv_enclosed = '"';
  125. $csv_escaped = '"';
  126. if (isset($GLOBALS['excel_columns'])) {
  127. $GLOBALS['csv_columns'] = 'yes';
  128. }
  129. } else {
  130. if (empty($csv_terminated)
  131. || mb_strtolower($csv_terminated) === 'auto'
  132. ) {
  133. $csv_terminated = $GLOBALS['crlf'];
  134. } else {
  135. $csv_terminated = str_replace(
  136. [
  137. '\\r',
  138. '\\n',
  139. '\\t',
  140. ],
  141. [
  142. "\015",
  143. "\012",
  144. "\011",
  145. ],
  146. $csv_terminated
  147. );
  148. }
  149. $csv_separator = str_replace('\\t', "\011", $csv_separator);
  150. }
  151. return true;
  152. }
  153. /**
  154. * Outputs export footer
  155. *
  156. * @return bool Whether it succeeded
  157. */
  158. public function exportFooter()
  159. {
  160. return true;
  161. }
  162. /**
  163. * Outputs database header
  164. *
  165. * @param string $db Database name
  166. * @param string $db_alias Alias of db
  167. *
  168. * @return bool Whether it succeeded
  169. */
  170. public function exportDBHeader($db, $db_alias = '')
  171. {
  172. return true;
  173. }
  174. /**
  175. * Outputs database footer
  176. *
  177. * @param string $db Database name
  178. *
  179. * @return bool Whether it succeeded
  180. */
  181. public function exportDBFooter($db)
  182. {
  183. return true;
  184. }
  185. /**
  186. * Outputs CREATE DATABASE statement
  187. *
  188. * @param string $db Database name
  189. * @param string $export_type 'server', 'database', 'table'
  190. * @param string $db_alias Aliases of db
  191. *
  192. * @return bool Whether it succeeded
  193. */
  194. public function exportDBCreate($db, $export_type, $db_alias = '')
  195. {
  196. return true;
  197. }
  198. /**
  199. * Outputs the content of a table in CSV format
  200. *
  201. * @param string $db database name
  202. * @param string $table table name
  203. * @param string $crlf the end of line sequence
  204. * @param string $error_url the url to go back in case of error
  205. * @param string $sql_query SQL query for obtaining data
  206. * @param array $aliases Aliases of db/table/columns
  207. *
  208. * @return bool Whether it succeeded
  209. */
  210. public function exportData(
  211. $db,
  212. $table,
  213. $crlf,
  214. $error_url,
  215. $sql_query,
  216. array $aliases = []
  217. ) {
  218. global $what, $csv_terminated, $csv_separator, $csv_enclosed, $csv_escaped, $dbi;
  219. $db_alias = $db;
  220. $table_alias = $table;
  221. $this->initAlias($aliases, $db_alias, $table_alias);
  222. // Gets the data from the database
  223. $result = $dbi->query(
  224. $sql_query,
  225. DatabaseInterface::CONNECT_USER,
  226. DatabaseInterface::QUERY_UNBUFFERED
  227. );
  228. $fields_cnt = $dbi->numFields($result);
  229. // If required, get fields name at the first line
  230. if (isset($GLOBALS['csv_columns'])) {
  231. $schema_insert = '';
  232. for ($i = 0; $i < $fields_cnt; $i++) {
  233. $col_as = $dbi->fieldName($result, $i);
  234. if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
  235. $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
  236. }
  237. $col_as = stripslashes($col_as);
  238. if ($csv_enclosed == '') {
  239. $schema_insert .= $col_as;
  240. } else {
  241. $schema_insert .= $csv_enclosed
  242. . str_replace(
  243. $csv_enclosed,
  244. $csv_escaped . $csv_enclosed,
  245. $col_as
  246. )
  247. . $csv_enclosed;
  248. }
  249. $schema_insert .= $csv_separator;
  250. }
  251. $schema_insert = trim(mb_substr($schema_insert, 0, -1));
  252. if (! $this->export->outputHandler($schema_insert . $csv_terminated)) {
  253. return false;
  254. }
  255. }
  256. // Format the data
  257. while ($row = $dbi->fetchRow($result)) {
  258. $schema_insert = '';
  259. for ($j = 0; $j < $fields_cnt; $j++) {
  260. if (! isset($row[$j]) || $row[$j] === null) {
  261. $schema_insert .= $GLOBALS[$what . '_null'];
  262. } elseif ($row[$j] == '0' || $row[$j] != '') {
  263. // always enclose fields
  264. if ($what === 'excel') {
  265. $row[$j] = preg_replace("/\015(\012)?/", "\012", $row[$j]);
  266. }
  267. // remove CRLF characters within field
  268. if (isset($GLOBALS[$what . '_removeCRLF'])
  269. && $GLOBALS[$what . '_removeCRLF']
  270. ) {
  271. $row[$j] = str_replace(
  272. [
  273. "\r",
  274. "\n",
  275. ],
  276. '',
  277. $row[$j]
  278. );
  279. }
  280. if ($csv_enclosed == '') {
  281. $schema_insert .= $row[$j];
  282. } else {
  283. // also double the escape string if found in the data
  284. if ($csv_escaped != $csv_enclosed) {
  285. $schema_insert .= $csv_enclosed
  286. . str_replace(
  287. $csv_enclosed,
  288. $csv_escaped . $csv_enclosed,
  289. str_replace(
  290. $csv_escaped,
  291. $csv_escaped . $csv_escaped,
  292. $row[$j]
  293. )
  294. )
  295. . $csv_enclosed;
  296. } else {
  297. // avoid a problem when escape string equals enclose
  298. $schema_insert .= $csv_enclosed
  299. . str_replace(
  300. $csv_enclosed,
  301. $csv_escaped . $csv_enclosed,
  302. $row[$j]
  303. )
  304. . $csv_enclosed;
  305. }
  306. }
  307. } else {
  308. $schema_insert .= '';
  309. }
  310. if ($j >= $fields_cnt - 1) {
  311. continue;
  312. }
  313. $schema_insert .= $csv_separator;
  314. }
  315. if (! $this->export->outputHandler($schema_insert . $csv_terminated)) {
  316. return false;
  317. }
  318. }
  319. $dbi->freeResult($result);
  320. return true;
  321. }
  322. /**
  323. * Outputs result of raw query in CSV format
  324. *
  325. * @param string $err_url the url to go back in case of error
  326. * @param string $sql_query the rawquery to output
  327. * @param string $crlf the end of line sequence
  328. *
  329. * @return bool if succeeded
  330. */
  331. public function exportRawQuery(string $err_url, string $sql_query, string $crlf): bool
  332. {
  333. return $this->exportData('', '', $crlf, $err_url, $sql_query);
  334. }
  335. }