ExportCsv.php 11 KB

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