@@ -942,6 +942,127 @@ public static function dataEscapeCsvValue() {
942942 ];
943943 }
944944
945+ public function testWriteCsv () {
946+ // Create a temporary file
947+ $ temp_file = tmpfile ();
948+
949+ // Test data with various cases that need escaping
950+ $ headers = [ 'name ' , 'formula ' , 'quoted ' , 'comma ' , 'backslash ' ];
951+ $ rows = [
952+ [
953+ 'name ' => 'John Doe ' ,
954+ 'formula ' => '=SUM(A1:A2) ' ,
955+ 'quoted ' => 'Contains "quotes" ' ,
956+ 'comma ' => 'Item 1, Item 2 ' ,
957+ 'backslash ' => 'C: \\path \\to \\file ' ,
958+ ],
959+ [
960+ 'name ' => '@username ' ,
961+ 'formula ' => '+1234 ' ,
962+ 'quoted ' => "'Single quotes' " ,
963+ 'comma ' => '-123,45 ' ,
964+ 'backslash ' => 'Escape \\this ' ,
965+ ],
966+ ];
967+
968+ // Write to CSV
969+ Utils \write_csv ( $ temp_file , $ rows , $ headers );
970+
971+ // Rewind file and read contents
972+ rewind ( $ temp_file );
973+ $ csv_content = stream_get_contents ( $ temp_file );
974+
975+ // Normalize line endings for cross-platform testing
976+ $ csv_content = str_replace ( "\r\n" , "\n" , $ csv_content );
977+
978+ // Check individual components instead of the exact string
979+ $ this ->assertStringContainsString ( 'name,formula,quoted,comma,backslash ' , $ csv_content );
980+ $ this ->assertStringContainsString ( '"John Doe" ' , $ csv_content );
981+ $ this ->assertStringContainsString ( '\'=SUM(A1:A2) ' , $ csv_content );
982+ $ this ->assertStringContainsString ( '"Contains ""quotes""" ' , $ csv_content );
983+ $ this ->assertStringContainsString ( '"Item 1, Item 2" ' , $ csv_content );
984+ $ this ->assertStringContainsString ( '\'@username ' , $ csv_content );
985+ $ this ->assertStringContainsString ( '\'Single quotes \'' , $ csv_content );
986+ $ this ->assertStringContainsString ( '\'+1234 ' , $ csv_content );
987+ $ this ->assertStringContainsString ( '\'-123,45 ' , $ csv_content );
988+ }
989+
990+ public function testWriteCsvWithoutHeaders () {
991+ // Create a temporary file
992+ $ temp_file = tmpfile ();
993+
994+ // Test data without using headers
995+ $ rows = [
996+ [ 'John Doe ' , '=SUM(A1:A2) ' , 'Contains "quotes" ' ],
997+ [ '@username ' , '+1234 ' , '-amount ' ],
998+ ];
999+
1000+ // Write to CSV without headers
1001+ Utils \write_csv ( $ temp_file , $ rows );
1002+
1003+ // Rewind file and read contents
1004+ rewind ( $ temp_file );
1005+ $ csv_content = stream_get_contents ( $ temp_file );
1006+
1007+ // Normalize line endings for cross-platform testing
1008+ $ csv_content = str_replace ( "\r\n" , "\n" , $ csv_content );
1009+
1010+ // Check individual components instead of the exact string
1011+ $ this ->assertStringContainsString ( '"John Doe" ' , $ csv_content );
1012+ $ this ->assertStringContainsString ( '\'=SUM(A1:A2) ' , $ csv_content );
1013+ $ this ->assertStringContainsString ( '"Contains ""quotes""" ' , $ csv_content );
1014+ $ this ->assertStringContainsString ( '\'@username ' , $ csv_content );
1015+ $ this ->assertStringContainsString ( '\'+1234 ' , $ csv_content );
1016+ $ this ->assertStringContainsString ( '\'-amount ' , $ csv_content );
1017+ }
1018+
1019+ public function testWriteCsvWithFieldPicking () {
1020+ // Create a temporary file
1021+ $ temp_file = tmpfile ();
1022+
1023+ // Test data with additional fields that should be filtered out
1024+ $ rows = [
1025+ [
1026+ 'id ' => 1 ,
1027+ 'name ' => 'John Doe ' ,
1028+ 'email ' => 'john@example.com ' ,
1029+ 'formula ' => '=HYPERLINK("http://malicious.com") ' ,
1030+ 'extra ' => 'Should not appear ' ,
1031+ ],
1032+ [
1033+ 'id ' => 2 ,
1034+ 'name ' => '@username ' ,
1035+ 'email ' => 'user@example.com ' ,
1036+ 'formula ' => '+1234 ' ,
1037+ 'extra ' => 'Should not appear ' ,
1038+ ],
1039+ ];
1040+
1041+ // Only include these headers (should filter the rows accordingly)
1042+ $ headers = [ 'id ' , 'name ' , 'email ' , 'formula ' ];
1043+
1044+ // Write to CSV, which should filter fields based on headers
1045+ Utils \write_csv ( $ temp_file , $ rows , $ headers );
1046+
1047+ // Rewind file and read contents
1048+ rewind ( $ temp_file );
1049+ $ csv_content = stream_get_contents ( $ temp_file );
1050+
1051+ // Normalize line endings for cross-platform testing
1052+ $ csv_content = str_replace ( "\r\n" , "\n" , $ csv_content );
1053+
1054+ // Check individual components instead of the exact string
1055+ $ this ->assertStringContainsString ( 'id,name,email,formula ' , $ csv_content );
1056+ $ this ->assertStringContainsString ( '1,"John Doe",john@example.com ' , $ csv_content );
1057+ $ this ->assertStringContainsString ( '\'=HYPERLINK ' , $ csv_content );
1058+ $ this ->assertStringContainsString ( '2, \'@username,user@example.com ' , $ csv_content );
1059+ $ this ->assertStringContainsString ( '\'+1234 ' , $ csv_content );
1060+
1061+ // Make sure 'extra' field is not in the output
1062+ $ this ->assertStringNotContainsString ( 'extra ' , $ csv_content );
1063+ $ this ->assertStringNotContainsString ( 'Should not appear ' , $ csv_content );
1064+ }
1065+
9451066 public function testReplacePathConstsAddSlashes () {
9461067 $ expected = "define( 'ABSPATH', dirname( 'C: \\\\Users \\\\test\'s \\\\site' ) . '/' ); " ;
9471068 $ source = "define( 'ABSPATH', dirname( __FILE__ ) . '/' ); " ;
0 commit comments