PluginProbe
TablePress – Tables in WordPress made easy / 3.0
TablePress – Tables in WordPress made easy v3.0
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 2.4.4 All 44 releases
← All changes | libraries/csv-parser.class.php +38 -34 1.123.0 View file →
@@ -12,8 +12,9 @@
12 12 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
13 13
14 14 /**
15 15 * CSV Parsing class
16 + *
16 17 * @package TablePress
17 18 * @subpackage Import
18 19 * @author Tobias Bäthge
19 20 * @since 1.0.0
@@ -23,43 +24,39 @@
23 24 /**
24 25 * The used character for the enclosure of a cell. Defaults to quotation mark ".
25 26 *
26 27 * @since 1.0.0
27 - * @var string
28 28 */
29 - protected $enclosure = '"';
29 + protected string $enclosure = '"';
30 30
31 31 /**
32 32 * Number of rows to analyze when attempting to auto-detect the CSV delimiter.
33 33 *
34 34 * @since 1.0.0
35 - * @var int
36 35 */
37 - protected $delimiter_search_max_lines = 15;
36 + protected int $delimiter_search_max_lines = 15;
38 37
39 38 /**
40 39 * Characters to ignore when attempting to auto-detect delimiter.
41 40 *
42 41 * @since 1.0.0
43 - * @var string
44 42 */
45 - protected $non_delimiter_chars = "a-zA-Z0-9\n\r";
43 + protected string $non_delimiter_chars = "a-zA-Z0-9\n\r";
46 44
47 45 /**
48 46 * The preferred delimiter characters, only used when all filtering method return multiple possible delimiters (happens very rarely).
47 + * There must not be more than 9 characters in the preferred delimiter character list, see `_check_delimiter_count()`.
49 48 *
50 49 * @since 1.0.0
51 - * @var string
52 50 */
53 - protected $preferred_delimiter_chars = ";,\t";
51 + protected string $preferred_delimiter_chars = ";,\t";
54 52
55 53 /**
56 54 * The CSV data string that shall be parsed to an array.
57 55 *
58 56 * @since 1.0.0
59 - * @var string
60 57 */
61 - protected $import_data;
58 + protected string $import_data = '';
62 59
63 60 /**
64 61 * The error state while parsing input data.
65 62 *
@@ -68,19 +65,18 @@
68 65 * 2 = The enclosure character was found in a non-enclosed field. This means the file is either corrupt,
69 66 * or does not follow the common CSV standard. Please validate the parsed data manually.
70 67 *
71 68 * @since 1.0.0
72 - * @var int
73 69 */
74 - public $error = 0;
70 + public int $error = 0;
75 71
76 72 /**
77 73 * Detailed error information.
78 74 *
79 75 * @since 1.0.0
80 - * @var array
76 + * @var array<string, array<string, int|string>>
81 77 */
82 - public $error_info = array();
78 + public array $error_info = array();
83 79
84 80 /**
85 81 * Class Constructor.
86 82 *
@@ -96,11 +92,11 @@
96 92 * @since 1.0.0
97 93 *
98 94 * @param string $data Data to be parsed.
99 95 */
100 - public function load_data( $data ) {
96 + public function load_data( string $data ): void {
101 97 // Check for mandatory trailing line break.
102 - if ( "\n" !== substr( $data, -1 ) ) {
98 + if ( ! str_ends_with( $data, "\n" ) ) {
103 99 $data .= "\n";
104 100 }
105 101 $this->import_data = $data;
106 102 }
@@ -111,9 +107,9 @@
111 107 * @since 1.0.0
112 108 *
113 109 * @return string Most probable delimiter character.
114 110 */
115 - public function find_delimiter() {
111 + public function find_delimiter(): string {
116 112 $data = &$this->import_data;
117 113
118 114 $delimiter_count = array();
119 115 $enclosed = false;
@@ -130,24 +126,24 @@
130 126 // Open and closing quotes.
131 127 if ( ! $enclosed || $next_char !== $this->enclosure ) {
132 128 $enclosed = ! $enclosed; // Flip bool.
133 129 } elseif ( $enclosed ) {
134 - $i++; // Skip next character.
130 + ++$i; // Skip next character.
135 131 }
136 - } elseif ( ( "\n" === $curr_char && "\r" !== $prev_char || "\r" === $curr_char ) && ! $enclosed ) {
132 + } elseif ( ( ( "\n" === $curr_char && "\r" !== $prev_char ) || "\r" === $curr_char ) && ! $enclosed ) {
137 133 // Reached end of a line.
138 - $current_line++;
134 + ++$current_line;
139 135 if ( $current_line >= $this->delimiter_search_max_lines ) {
140 136 break;
141 137 }
142 138 } elseif ( ! $enclosed ) {
143 139 // At this point, $curr_char seems to be used as a delimiter, as it is not enclosed.
144 - // Count $curr_char if it is not in the $this->non_delimiter_chars list
140 + // Count $curr_char if it is not in the $this->non_delimiter_chars list.
145 141 if ( 0 === preg_match( '#[' . $this->non_delimiter_chars . ']#i', $curr_char ) ) {
146 142 if ( ! isset( $delimiter_count[ $curr_char ][ $current_line ] ) ) {
147 - $delimiter_count[ $curr_char ][ $current_line ] = 0; // Initialize empty
143 + $delimiter_count[ $curr_char ][ $current_line ] = 0; // Initialize empty.
148 144 }
149 - $delimiter_count[ $curr_char ][ $current_line ]++;
145 + ++$delimiter_count[ $curr_char ][ $current_line ];
150 146 }
151 147 }
152 148 }
153 149
@@ -167,8 +163,13 @@
167 163 arsort( $delimiter_counts, SORT_NUMERIC );
168 164 $potential_delimiters = array_keys( $delimiter_counts );
169 165 }
170 166
167 + // If still no delimiter was found, fall back to a comma.
168 + if ( empty( $potential_delimiters ) ) {
169 + $potential_delimiters = array( ',' );
170 + }
171 +
171 172 // Return first array element, as that has the highest count.
172 173 return array_shift( $potential_delimiters );
173 174 }
174 175
@@ -177,13 +178,13 @@
177 178 *
178 179 * @since 1.0.0
179 180 *
180 181 * @param string $char Character to check.
181 - * @param array $line_counts Counts for the characters in the lines.
182 + * @param int[] $line_counts Counts for the characters in the lines.
182 183 * @param int $number_lines Number of lines.
183 184 * @return bool|string False if delimiter is not possible, string to be used as a sort key if character could be a delimiter.
184 185 */
185 - protected function _check_delimiter_count( $char, array $line_counts, $number_lines ) {
186 + protected function _check_delimiter_count( string $char, array $line_counts, int $number_lines ) /* : bool|string */ {
186 187 // Was the potential delimiter found in every line?
187 188 if ( count( $line_counts ) !== $number_lines ) {
188 189 return false;
189 190 }
@@ -191,9 +192,9 @@
191 192 // Check if the count in every line is the same (or one higher for an "almost").
192 193 $first = null;
193 194 $equal = null;
194 195 $almost = false;
195 - foreach ( $line_counts as $line => $count ) {
196 + foreach ( $line_counts as $count ) {
196 197 if ( is_null( $first ) ) {
197 198 $first = $count;
198 199 } elseif ( $count === $first && false !== $equal ) {
199 200 $equal = true;
@@ -210,11 +211,14 @@
210 211 }
211 212
212 213 // At this point, count is equal in all lines, so determine a string to sort priority.
213 214 $match = ( $almost ) ? 2 : 1;
215 + // There must not be more than 9 characters in the preferred delimiter character list.
214 216 $pref = strpos( $this->preferred_delimiter_chars, $char );
215 - $pref = ( false !== $pref ) ? str_pad( $pref, 3, '0', STR_PAD_LEFT ) : '999';
216 - return $pref . $match . '.' . ( 99999 - str_pad( $first, 5, '0', STR_PAD_LEFT ) );
217 + if ( false === $pref ) {
218 + $pref = 9;
219 + }
220 + return $pref . $match . '.' . ( 99999 - $first );
217 221 }
218 222
219 223 /**
220 224 * Parse CSV string into a two-dimensional array.
@@ -221,11 +225,11 @@
221 225 *
222 226 * @since 1.0.0
223 227 *
224 228 * @param string $delimiter Delimiter character for the CSV parsing.
225 - * @return array Two-dimensional array with the data from the CSV string.
229 + * @return array<int, array<int, string>> Two-dimensional array with the data from the CSV string.
226 230 */
227 - public function parse( $delimiter ) {
231 + public function parse( string $delimiter ): array {
228 232 $data = &$this->import_data;
229 233
230 234 // Filter delimiter from the list, if it is a whitespace character.
231 235 $white_spaces = str_replace( $delimiter, '', " \t\x0B\0" );
@@ -265,12 +269,12 @@
265 269 }
266 270 } elseif ( $next_char === $this->enclosure ) {
267 271 // Enclosure character within enclosed cell (" encoded as "").
268 272 $cell_content .= $curr_char;
269 - $i++; // Skip next character
273 + ++$i; // Skip next character.
270 274 } elseif ( $next_char !== $delimiter && "\r" !== $next_char && "\n" !== $next_char ) {
271 275 // for-loop (instead of while-loop) that skips whitespace.
272 - for ( $x = ( $i + 1 ); isset( $data[ $x ] ) && '' === ltrim( $data[ $x ], $white_spaces ); $x++ ) {
276 + for ( $x = ( $i + 1 ); isset( $data[ $x ] ) && '' === ltrim( $data[ $x ], $white_spaces ); $x++ ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed,Generic.CodeAnalysis.EmptyStatement.DetectedFor
273 277 // Action is in iterator check.
274 278 }
275 279 if ( $data[ $x ] === $delimiter ) {
276 280 $enclosed = false;
@@ -301,9 +305,9 @@
301 305
302 306 $row[ $column ] = ( $was_enclosed ) ? $cell_content : trim( $cell_content );
303 307 $cell_content = '';
304 308 $was_enclosed = false;
305 - $column++;
309 + ++$column;
306 310
307 311 // End of line.
308 312 if ( "\n" === $curr_char || "\r" === $curr_char ) {
309 313 // Append completed row.
@@ -311,9 +315,9 @@
311 315 $row = array();
312 316 $column = 0;
313 317 if ( "\r" === $curr_char && "\n" === $next_char ) {
314 318 // Skip next character in \r\n line breaks.
315 - $i++;
319 + ++$i;
316 320 }
317 321 }
318 322 } else {
319 323 // Append character to current cell.