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