Skip to content

Commit d96b792

Browse files
Fix breaking multi-line CSV values on reading (#5939)
* Refactor CSV iterator to use fgetcsv() to account for multiple line comma valued CSVs * Add Unit tests for CSV Iterator
1 parent bba57a7 commit d96b792

File tree

2 files changed

+111
-4
lines changed

2 files changed

+111
-4
lines changed

php/WP_CLI/Iterators/CSV.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,12 @@ public function next() {
5454
$this->current_element = false;
5555

5656
while ( true ) {
57-
$str = fgets( $this->file_pointer );
57+
$row = fgetcsv( $this->file_pointer, self::ROW_SIZE, $this->delimiter );
5858

59-
if ( false === $str ) {
59+
if ( false === $row ) {
6060
break;
6161
}
6262

63-
$row = str_getcsv( $str, $this->delimiter );
64-
6563
$element = [];
6664
foreach ( $this->columns as $i => $key ) {
6765
if ( isset( $row[ $i ] ) ) {

tests/WP_CLI/Iterators/CSVTest.php

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
namespace WP_CLI\Tests\CSV;
4+
5+
use WP_CLI\Tests\TestCase;
6+
use WP_CLI\Iterators\CSV;
7+
8+
class CSVTest extends TestCase {
9+
10+
public function test_it_can_iterate_over_a_csv_file() {
11+
$filename = $this->create_csv_file(
12+
array(
13+
array( 'foo', 'bar' ),
14+
array( 'baz', 'qux' ),
15+
)
16+
);
17+
18+
$expected = array(
19+
0 => array(
20+
'foo' => 'baz',
21+
'bar' => 'qux',
22+
),
23+
);
24+
25+
foreach ( new CSV( $filename ) as $index => $row ) {
26+
$this->assertEquals( $expected[ $index ], $row );
27+
}
28+
}
29+
30+
public function test_it_can_iterate_over_a_csv_file_with_custom_delimiter() {
31+
$filename = $this->create_csv_file(
32+
array(
33+
array( 'foo|bar' ),
34+
array( 'baz|qux' ),
35+
),
36+
'|'
37+
);
38+
39+
$expected = array(
40+
0 => array(
41+
'foo|bar' => 'baz|qux',
42+
),
43+
);
44+
45+
foreach ( new CSV( $filename, '|' ) as $index => $row ) {
46+
$this->assertEquals( $expected[ $index ], $row );
47+
}
48+
}
49+
50+
public function test_it_can_iterate_over_a_csv_file_with_multiple_lines_in_a_value() {
51+
$filename = $this->create_csv_file(
52+
array(
53+
array( 'foo', "bar\nbaz" ),
54+
array( 'qux', "quux\nquuz" ),
55+
)
56+
);
57+
58+
$expected = array(
59+
0 => array(
60+
'foo' => 'qux',
61+
"bar\nbaz" => "quux\nquuz",
62+
),
63+
);
64+
65+
foreach ( new CSV( $filename ) as $index => $row ) {
66+
$this->assertEquals( $expected[ $index ], $row );
67+
}
68+
}
69+
70+
public function test_it_can_iterate_over_a_csv_file_with_multiple_lines_and_comma_in_a_value() {
71+
$filename = $this->create_csv_file(
72+
array(
73+
array( 'foo', "bar\nbaz,qux" ),
74+
array( 'quux', "quuz\ncorge,grault" ),
75+
)
76+
);
77+
78+
$expected = array(
79+
0 => array(
80+
'foo' => 'quux',
81+
"bar\nbaz,qux" => "quuz\ncorge,grault",
82+
),
83+
);
84+
85+
foreach ( new CSV( $filename ) as $index => $row ) {
86+
$this->assertEquals( $expected[ $index ], $row );
87+
}
88+
}
89+
90+
private function create_csv_file( $data, $delimiter = ',' ) {
91+
$filename = tempnam( sys_get_temp_dir(), 'wp-cli-tests-' );
92+
93+
$fp = fopen( $filename, 'wb' );
94+
95+
foreach ( $data as $row ) {
96+
fputcsv( $fp, $row, $delimiter );
97+
}
98+
99+
fclose( $fp );
100+
101+
register_shutdown_function(
102+
function () use ( $filename ) {
103+
unlink( $filename );
104+
}
105+
);
106+
107+
return $filename;
108+
}
109+
}

0 commit comments

Comments
 (0)