PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / classes / exporters / class-exporter-csv.php

class-exporter-csv.php in TablePress – Tables in WordPress made easy 3.4, at classes/exporters/class-exporter-csv.php

116 lines 3.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * TablePress CSV Exporter Class
4 *
5 * @package TablePress
6 * @subpackage Export/Import
7 * @author Tobias Bäthge
8 * @since 3.4.0
9 */
10
11 declare(strict_types=1);
12
13 namespace TablePress\Export;
14
15 use TablePress\Export\Exporter_Interface;
16
17 // Prohibit direct script loading.
18 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
19
20 /**
21 * TablePress CSV Exporter Class
22 *
23 * @package TablePress
24 * @subpackage Export/Import
25 * @since 3.4.0
26 */
27 class CSV_Exporter implements Exporter_Interface {
28
29 /**
30 * Exports a table to CSV format.
31 *
32 * @since 3.4.0
33 *
34 * @param array<string, mixed> $table Table to be exported.
35 * @param array<string, mixed> $options Format-specific options for the export.
36 * @return string Exported table.
37 */
38 public function export( array $table, array $options = array() ): string {
39 $delimiter = $options['csv_delimiter'] ?? ',';
40 if ( 'tab' === $delimiter ) {
41 $delimiter = "\t";
42 }
43
44 $output = '';
45
46 foreach ( $table['data'] as $row ) {
47 $csv_row = array();
48 foreach ( $row as $cell ) {
49 $csv_row[] = $this->wrap_and_escape( $cell, $delimiter );
50 }
51 $output .= implode( $delimiter, $csv_row );
52 $output .= "\n";
53 }
54
55 return $output;
56 }
57
58 /**
59 * Wraps and escapes a cell for CSV export.
60 *
61 * @since 3.4.0
62 *
63 * @param string $cell_content Content of a cell.
64 * @param string $delimiter CSV delimiter character.
65 * @return string Wrapped string for CSV export.
66 */
67 protected function wrap_and_escape( string $cell_content, string $delimiter ): string {
68 // Return early if the cell is empty. No escaping or wrapping is needed then.
69 if ( '' === $cell_content ) {
70 return $cell_content;
71 }
72
73 // Escape potentially dangerous functions that could be used for CSV injection attacks in external spreadsheet software.
74 $active_content_triggers = array( '=', '+', '-', '@' );
75 if ( in_array( $cell_content[0], $active_content_triggers, true ) ) {
76 // phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found -- Avoid concatenation of function names to prevent false positives in code scanners.
77 $functions_to_escape = array(
78 'cmd|',
79 'FOR' . 'FILES|',
80 'rund' . 'll32',
81 'DD' . 'E(',
82 'IMPORT' . 'XML(',
83 'IMPORT' . 'FEED(',
84 'IMPORT' . 'HTML(',
85 'IMPORT' . 'RANGE(',
86 'IMPORT' . 'DATA(',
87 'IMAGE(',
88 'HYPERLINK(',
89 'WEBSERVICE(',
90 );
91 // phpcs:enable
92
93 $fn_stripos = function_exists( 'mb_stripos' ) ? 'mb_stripos' : 'stripos';
94
95 foreach ( $functions_to_escape as $function ) {
96 if ( false !== $fn_stripos( $cell_content, $function ) ) {
97 $cell_content = "'" . $cell_content; // Prepend a ' to indicate that the cell format is a text string.
98 break;
99 }
100 }
101 }
102
103 // Escape CSV delimiter for RegExp (e.g. '|').
104 $delimiter = preg_quote( $delimiter, '#' );
105 if ( 1 === preg_match( '#' . $delimiter . '|"|\n|\r#i', $cell_content ) || str_starts_with( $cell_content, ' ' ) || str_ends_with( $cell_content, ' ' ) ) {
106 // Escape single " as double "".
107 $cell_content = str_replace( '"', '""', $cell_content );
108 // Wrap string in "".
109 $cell_content = '"' . $cell_content . '"';
110 }
111
112 return $cell_content;
113 }
114
115 } // class CSV_Exporter
116