PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.12
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.12
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmCSVExportHelper.php

FrmCSVExportHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.12, at classes/helpers/FrmCSVExportHelper.php

730 lines 21.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmCSVExportHelper {
7
8 /**
9 * @var string
10 */
11 protected static $separator = ', ';
12
13 /**
14 * @var string
15 */
16 protected static $column_separator = ',';
17
18 /**
19 * @var string
20 */
21 protected static $line_break = 'return';
22
23 /**
24 * @var string
25 */
26 protected static $charset = 'UTF-8';
27
28 /**
29 * @var string
30 */
31 protected static $to_encoding = 'UTF-8';
32
33 /**
34 * @var string
35 */
36 protected static $wp_date_format = 'Y-m-d H:i:s';
37
38 /**
39 * @var int
40 */
41 protected static $comment_count = 0;
42
43 /**
44 * @var int
45 */
46 protected static $form_id = 0;
47
48 /**
49 * @var array
50 */
51 protected static $headings = array();
52
53 /**
54 * @var array
55 */
56 protected static $fields = array();
57
58 /**
59 * @var stdClass|null
60 */
61 protected static $entry;
62
63 /**
64 * @var bool|null
65 */
66 protected static $has_parent_id;
67
68 /**
69 * @var array|null
70 */
71 protected static $fields_by_repeater_id;
72
73 /**
74 * @var string either 'echo' or 'file' are supported.
75 */
76 protected static $mode = 'echo';
77
78 /**
79 * @var resource|null used to write a CSV file in file mode.
80 */
81 protected static $fp;
82
83 /**
84 * @var string the context of the CSV being generated. Possible values include 'email' when used as an email attachment, or 'default'.
85 */
86 protected static $context = 'default';
87
88 /**
89 * @var array
90 */
91 protected static $meta = array();
92
93 public static function csv_format_options() {
94 $formats = array( 'UTF-8', 'ISO-8859-1', 'windows-1256', 'windows-1251', 'macintosh' );
95 $formats = apply_filters( 'frm_csv_format_options', $formats );
96
97 return $formats;
98 }
99
100 /**
101 * @param array $atts
102 * @return false|string|null returns a string file path or false if $atts['mode'] is set to 'file'.
103 */
104 public static function generate_csv( $atts ) {
105 global $frm_vars;
106 $frm_vars['prevent_caching'] = true;
107
108 self::$fields = $atts['form_cols'];
109 self::$form_id = $atts['form']->id;
110 self::$mode = ! empty( $atts['mode'] ) && 'file' === $atts['mode'] ? 'file' : 'echo';
111 self::$context = ! empty( $atts['context'] ) ? $atts['context'] : 'default';
112 self::$meta = ! empty( $atts['meta'] ) ? $atts['meta'] : array();
113
114 self::set_class_parameters();
115 self::set_has_parent_id( $atts['form'] );
116
117 $filename = self::generate_csv_filename( $atts['form'] );
118
119 if ( 'file' === self::$mode ) {
120 $filepath = get_temp_dir() . $filename;
121 self::$fp = @fopen( $filepath, 'w' );
122 if ( ! self::$fp ) {
123 return false;
124 }
125 } elseif ( 'echo' === self::$mode ) {
126 self::print_file_headers( $filename );
127 }
128
129 unset( $filename );
130
131 $comment_count = FrmDb::get_count(
132 'frm_item_metas',
133 array(
134 'item_id' => $atts['entry_ids'],
135 'field_id' => 0,
136 'meta_value like' => '{',
137 ),
138 array(
139 'group_by' => 'item_id',
140 'order_by' => 'count(*) DESC',
141 'limit' => 1,
142 )
143 );
144 self::$comment_count = $comment_count;
145
146 self::prepare_csv_headings();
147
148 // fetch 20 posts at a time rather than loading the entire table into memory
149 while ( $next_set = array_splice( $atts['entry_ids'], 0, 20 ) ) {
150 self::prepare_next_csv_rows( $next_set );
151 }
152
153 self::after_generate_csv( $atts );
154
155 unset( $atts['form'], $atts['form_cols'] );
156
157 if ( 'file' === self::$mode ) {
158 fclose( self::$fp );
159 return $filepath;
160 }
161
162 return null;
163 }
164
165 /**
166 * @since 6.8.4
167 *
168 * @param array $atts
169 * @return void
170 */
171 private static function after_generate_csv( $atts ) {
172 /**
173 * @since 6.8.4
174 *
175 * @param array $atts {
176 * @type object $form
177 * @type array $entry_ids
178 * @type array $form_cols
179 * }
180 */
181 do_action( 'frm_after_generate_csv', $atts );
182 }
183
184 /**
185 * @since 5.0.16
186 *
187 * @param stdClass $form
188 * @return string
189 */
190 private static function generate_csv_filename( $form ) {
191 $filename = gmdate( 'ymdHis', time() ) . '_' . sanitize_title_with_dashes( $form->name ) . '_formidable_entries.csv';
192 return apply_filters( 'frm_csv_filename', $filename, $form, self::get_standard_filter_args() );
193 }
194
195 /**
196 * @since 5.0.16
197 *
198 * @return array
199 */
200 private static function get_standard_filter_args() {
201 return array(
202 'context' => self::$context,
203 'meta' => self::$meta,
204 );
205 }
206
207 private static function set_class_parameters() {
208 $args = self::get_standard_filter_args();
209 self::$separator = apply_filters( 'frm_csv_sep', self::$separator, $args );
210 self::$line_break = apply_filters( 'frm_csv_line_break', self::$line_break, $args );
211 self::$wp_date_format = apply_filters( 'frm_csv_date_format', self::$wp_date_format, $args );
212 self::get_csv_format();
213 self::$charset = get_option( 'blog_charset' );
214
215 $col_sep = ! empty( $_POST['csv_col_sep'] ) ? sanitize_text_field( wp_unslash( $_POST['csv_col_sep'] ) ) : self::$column_separator; // phpcs:ignore WordPress.Security.NonceVerification.Missing
216
217 self::$column_separator = apply_filters( 'frm_csv_column_sep', $col_sep, $args );
218 }
219
220 private static function set_has_parent_id( $form ) {
221 self::$has_parent_id = $form->parent_form_id > 0;
222 }
223
224 private static function print_file_headers( $filename ) {
225 header( 'Content-Description: File Transfer' );
226 header( 'Content-Disposition: attachment; filename="' . esc_attr( $filename ) . '"' );
227 header( 'Content-Type: text/csv; charset=' . self::$charset, true );
228 header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', mktime( gmdate( 'H' ) + 2, gmdate( 'i' ), gmdate( 's' ), gmdate( 'm' ), gmdate( 'd' ), gmdate( 'Y' ) ) ) . ' GMT' );
229 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
230 header( 'Cache-Control: no-cache, must-revalidate' );
231 header( 'Pragma: no-cache' );
232
233 do_action(
234 'frm_csv_headers',
235 array(
236 'form_id' => self::$form_id,
237 'fields' => self::$fields,
238 )
239 );
240 }
241
242 public static function get_csv_format() {
243 $csv_format = FrmAppHelper::get_post_param( 'csv_format', 'UTF-8', 'sanitize_text_field' );
244 $csv_format = apply_filters( 'frm_csv_format', $csv_format, self::get_standard_filter_args() );
245 self::$to_encoding = $csv_format;
246 }
247
248 private static function prepare_csv_headings() {
249 $headings = array();
250 self::csv_headings( $headings );
251 $headings = apply_filters(
252 'frm_csv_columns',
253 $headings,
254 self::$form_id,
255 array_merge(
256 self::get_standard_filter_args(),
257 array( 'fields' => self::$fields )
258 )
259 );
260 self::$headings = $headings;
261
262 self::print_csv_row( $headings );
263 }
264
265 private static function field_headings( $col ) {
266 $field_type_obj = FrmFieldFactory::get_field_factory( $col );
267 if ( ! empty( $field_type_obj->is_combo_field ) ) {
268 // This is combo field.
269 return $field_type_obj->get_export_headings();
270 }
271
272 $field_headings = array();
273 $separate_values = array( 'user_id', 'file', 'data', 'date' );
274 if ( ! empty( $col->field_options['separate_value'] ) && ! in_array( $col->type, $separate_values, true ) ) {
275 $field_headings[ $col->id . '_label' ] = strip_tags( $col->name . ' ' . __( '(label)', 'formidable' ) );
276 }
277
278 $field_headings[ $col->id ] = strip_tags( $col->name );
279 $field_headings = apply_filters(
280 'frm_csv_field_columns',
281 $field_headings,
282 array_merge(
283 self::get_standard_filter_args(),
284 array( 'field' => $col )
285 )
286 );
287
288 return $field_headings;
289 }
290
291 private static function csv_headings( &$headings ) {
292 $fields_by_repeater_id = array();
293 $repeater_ids = array();
294
295 foreach ( self::$fields as $col ) {
296 if ( self::is_the_child_of_a_repeater( $col ) ) {
297 $repeater_id = $col->field_options['in_section'];
298 // Set a placeholder to maintain order for repeater fields.
299 $headings[ 'repeater' . $repeater_id ] = array();
300
301 if ( ! isset( $fields_by_repeater_id[ $repeater_id ] ) ) {
302 $fields_by_repeater_id[ $repeater_id ] = array();
303 $repeater_ids[] = $repeater_id;
304 }
305
306 $fields_by_repeater_id[ $repeater_id ][] = $col;
307
308 continue;
309 }
310
311 $headings += self::field_headings( $col );
312 }
313 unset( $repeater_id, $col );
314
315 if ( $repeater_ids ) {
316 $where = array( 'field_id' => $repeater_ids );
317 $repeater_meta = FrmDb::get_results( 'frm_item_metas', $where, 'field_id, meta_value' );
318 $max = array_fill_keys( $repeater_ids, 0 );
319
320 foreach ( $repeater_meta as $row ) {
321 $start = strpos( $row->meta_value, 'a:' ) + 2;
322 $end = strpos( $row->meta_value, ':{' );
323 $length = substr( $row->meta_value, $start, $end - $start );
324
325 if ( $length > $max[ $row->field_id ] ) {
326 $max[ $row->field_id ] = $length;
327 }
328 }
329 unset( $start, $end, $length, $row, $repeater_meta, $where );
330
331 $flat = array();
332 foreach ( $headings as $key => $heading ) {
333 if ( is_array( $heading ) ) {
334 $repeater_id = str_replace( 'repeater', '', $key );
335
336 $repeater_headings = array();
337 foreach ( $fields_by_repeater_id[ $repeater_id ] as $col ) {
338 $repeater_headings += self::field_headings( $col );
339 }
340
341 for ( $i = 0; $i < $max[ $repeater_id ]; $i++ ) {
342 foreach ( $repeater_headings as $repeater_key => $repeater_name ) {
343 $flat[ $repeater_key . '[' . $i . ']' ] = $repeater_name;
344 }
345 }
346 } else {
347 $flat[ $key ] = $heading;
348 }
349 }
350
351 self::$fields_by_repeater_id = $fields_by_repeater_id;
352
353 unset( $key, $heading, $max, $repeater_headings, $repeater_id, $fields_by_repeater_id );
354
355 $headings = $flat;
356 unset( $flat );
357 }//end if
358
359 if ( self::$comment_count ) {
360 for ( $i = 0; $i < self::$comment_count; $i++ ) {
361 $headings[ 'comment' . $i ] = __( 'Comment', 'formidable' );
362 $headings[ 'comment_user_id' . $i ] = __( 'Comment User', 'formidable' );
363 $headings[ 'comment_created_at' . $i ] = __( 'Comment Date', 'formidable' );
364 }
365 unset( $i );
366 }
367
368 $headings['created_at'] = __( 'Timestamp', 'formidable' );
369 $headings['updated_at'] = __( 'Last Updated', 'formidable' );
370 $headings['user_id'] = __( 'Created By', 'formidable' );
371 $headings['updated_by'] = __( 'Updated By', 'formidable' );
372 $headings['is_draft'] = __( 'Entry Status', 'formidable' );
373 $headings['ip'] = __( 'IP', 'formidable' );
374 $headings['id'] = __( 'ID', 'formidable' );
375 $headings['item_key'] = __( 'Key', 'formidable' );
376 if ( self::has_parent_id() ) {
377 $headings['parent_id'] = __( 'Parent ID', 'formidable' );
378 }
379
380 $headings = apply_filters( 'frm_export_csv_headings', $headings );
381 }
382
383 /**
384 * @param object $field
385 * @return bool
386 */
387 private static function is_the_child_of_a_repeater( $field ) {
388 if ( $field->form_id === self::$form_id || empty( $field->field_options['in_section'] ) ) {
389 return false;
390 }
391
392 $section_id = $field->field_options['in_section'];
393 $section = FrmField::getOne( $section_id );
394
395 if ( ! $section ) {
396 return false;
397 }
398
399 return FrmField::is_repeating_field( $section );
400 }
401
402 private static function has_parent_id() {
403 return self::$has_parent_id;
404 }
405
406 private static function prepare_next_csv_rows( $next_set ) {
407 // order by parent_item_id so children will be first
408 $where = array(
409 'or' => 1,
410 'id' => $next_set,
411 'parent_item_id' => $next_set,
412 );
413 $entries = FrmEntry::getAll( $where, ' ORDER BY parent_item_id DESC', '', true, false );
414
415 foreach ( $entries as $entry ) {
416 self::$entry = $entry;
417 unset( $entry );
418
419 if ( self::$entry->form_id !== self::$form_id ) {
420 self::add_repeat_field_values_to_csv( $entries );
421 } else {
422 self::prepare_csv_row();
423 }
424 }
425 }
426
427 private static function prepare_csv_row() {
428 $row = array();
429 self::add_field_values_to_csv( $row );
430 self::add_entry_data_to_csv( $row );
431 $row = apply_filters(
432 'frm_csv_row',
433 $row,
434 array(
435 'entry' => self::$entry,
436 'date_format' => self::$wp_date_format,
437 'comment_count' => self::$comment_count,
438 'context' => self::$context,
439 )
440 );
441 self::print_csv_row( $row );
442 }
443
444 private static function add_repeat_field_values_to_csv( &$entries ) {
445 if ( isset( self::$entry->metas ) ) {
446 // add child entries to the parent
447 foreach ( self::$entry->metas as $meta_id => $meta_value ) {
448 if ( ! is_numeric( $meta_id ) || '' === $meta_value ) {
449 // if the hook is being used to include field keys in the metas array,
450 // we need to skip the keys and only process field ids
451 continue;
452 }
453
454 if ( ! isset( $entries[ self::$entry->parent_item_id ] ) ) {
455 $entries[ self::$entry->parent_item_id ] = new stdClass();
456 $entries[ self::$entry->parent_item_id ]->metas = array();
457 }
458
459 if ( ! isset( $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] ) ) {
460 $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] = array();
461 } elseif ( ! is_array( $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] ) ) {
462 // if the data is here, it should be an array but if this field has collected data
463 // both while inside and outside of the repeating section, it's possible this is a string.
464 $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] = (array) $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ];
465 }
466
467 // Add the repeated values.
468 $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ][] = $meta_value;
469 }//end foreach
470
471 self::$entry->metas = self::fill_missing_repeater_metas( self::$entry->metas, $entries );
472 $entries[ self::$entry->parent_item_id ]->metas += self::$entry->metas;
473 }//end if
474
475 // add the embedded form id
476 if ( ! isset( $entries[ self::$entry->parent_item_id ]->embedded_fields ) ) {
477 $entries[ self::$entry->parent_item_id ]->embedded_fields = array();
478 }
479 $entries[ self::$entry->parent_item_id ]->embedded_fields[ self::$entry->id ] = self::$entry->form_id;
480 }
481
482 /**
483 * When an empty field is saved, it isn't saved as a meta value
484 * The export needs all of the meta to be filled in, so we put blank strings for every missing repeater child
485 *
486 * @param array $metas
487 * @param array $entries
488 * @return array
489 */
490 private static function fill_missing_repeater_metas( $metas, &$entries ) {
491 $field_ids = array_keys( $metas );
492 $field_id = end( $field_ids );
493 $field = self::get_field( $field_id );
494
495 if ( ! $field || empty( $field->field_options['in_section'] ) ) {
496 return $metas;
497 }
498
499 $repeater_id = $field->field_options['in_section'];
500 if ( ! isset( self::$fields_by_repeater_id[ $repeater_id ] ) ) {
501 return $metas;
502 }
503
504 foreach ( self::$fields_by_repeater_id[ $repeater_id ] as $repeater_child ) {
505 if ( ! isset( $metas[ $repeater_child->id ] ) ) {
506 $metas[ $repeater_child->id ] = '';
507
508 if ( ! isset( $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ] ) || ! is_array( $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ] ) ) {
509 $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ] = array();
510 }
511
512 $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ][] = '';
513 }
514 }
515
516 return $metas;
517 }
518
519 private static function get_field( $field_id ) {
520 $field_id = (int) $field_id;
521 foreach ( self::$fields as $field ) {
522 if ( $field_id === (int) $field->id ) {
523 return $field;
524 }
525 }
526 return false;
527 }
528
529 private static function add_field_values_to_csv( &$row ) {
530 foreach ( self::$fields as $col ) {
531 $field_value = isset( self::$entry->metas[ $col->id ] ) ? self::$entry->metas[ $col->id ] : false;
532
533 FrmFieldsHelper::prepare_field_value( $field_value, $col->type );
534 self::add_array_values_to_columns( $row, compact( 'col', 'field_value' ) );
535
536 $field_value = apply_filters(
537 'frm_csv_value',
538 $field_value,
539 array(
540 'field' => $col,
541 'entry' => self::$entry,
542 'separator' => self::$separator,
543 'context' => self::$context,
544 )
545 );
546
547 if ( ! empty( $col->field_options['separate_value'] ) ) {
548 $label_key = $col->id . '_label';
549 if ( self::is_the_child_of_a_repeater( $col ) ) {
550 $row[ $label_key ] = array();
551
552 if ( is_array( $field_value ) ) {
553 foreach ( $field_value as $value ) {
554 $row[ $label_key ][] = self::get_separate_value_label( $value, $col );
555 }
556 }
557 } else {
558 $row[ $label_key ] = self::get_separate_value_label( $field_value, $col );
559 }
560 unset( $label_key );
561 }
562
563 $row[ $col->id ] = $field_value;
564
565 unset( $col, $field_value );
566 }//end foreach
567 }
568
569 /**
570 * @since 5.0.06
571 *
572 * @param mixed $field_value
573 * @param stdClass $field
574 * @return string
575 */
576 private static function get_separate_value_label( $field_value, $field ) {
577 return FrmEntriesHelper::display_value(
578 $field_value,
579 $field,
580 array(
581 'type' => $field->type,
582 'post_id' => self::$entry->post_id,
583 'show_icon' => false,
584 'entry_id' => self::$entry->id,
585 'sep' => self::$separator,
586 'embedded_field_id' => isset( self::$entry->embedded_fields ) && isset( self::$entry->embedded_fields[ self::$entry->id ] ) ? 'form' . self::$entry->embedded_fields[ self::$entry->id ] : 0,
587 )
588 );
589 }
590
591 /**
592 * @since 2.0.23
593 */
594 private static function add_array_values_to_columns( &$row, $atts ) {
595 if ( is_array( $atts['field_value'] ) ) {
596 foreach ( $atts['field_value'] as $key => $sub_value ) {
597 if ( is_array( $sub_value ) ) {
598 // This is combo field inside repeater. The heading key has this format: [86_first[0]].
599 foreach ( $sub_value as $sub_key => $sub_sub_value ) {
600 $column_key = $atts['col']->id . '_' . $sub_key . '[' . $key . ']';
601 if ( ! is_numeric( $sub_key ) && isset( self::$headings[ $column_key ] ) ) {
602 $row[ $column_key ] = $sub_sub_value;
603 }
604 }
605
606 continue;
607 }
608
609 $column_key = $atts['col']->id . '_' . $key;
610 if ( ! is_numeric( $key ) && isset( self::$headings[ $column_key ] ) ) {
611 $row[ $column_key ] = $sub_value;
612 }
613 }
614 }
615 }
616
617 private static function add_entry_data_to_csv( &$row ) {
618 $row['created_at'] = FrmAppHelper::get_formatted_time( self::$entry->created_at, self::$wp_date_format, ' ' );
619 $row['updated_at'] = FrmAppHelper::get_formatted_time( self::$entry->updated_at, self::$wp_date_format, ' ' );
620 $row['user_id'] = self::$entry->user_id;
621 $row['updated_by'] = self::$entry->updated_by;
622 $row['is_draft'] = self::$entry->is_draft;
623 $row['ip'] = self::$entry->ip;
624 $row['id'] = self::$entry->id;
625 $row['item_key'] = self::$entry->item_key;
626 if ( self::has_parent_id() ) {
627 $row['parent_id'] = self::$entry->parent_item_id;
628 }
629 }
630
631 private static function print_csv_row( $rows ) {
632 $sep = '';
633 $echo = 'echo' === self::$mode;
634
635 foreach ( self::$headings as $k => $heading ) {
636 if ( isset( $rows[ $k ] ) ) {
637 $row = $rows[ $k ];
638 } else {
639 $row = '';
640 // array indexed data is not at $rows[ $k ]
641 if ( $k[ strlen( $k ) - 1 ] === ']' ) {
642 $start = strrpos( $k, '[' );
643 $key = substr( $k, 0, $start++ );
644 $index = substr( $k, $start, strlen( $k ) - 1 - $start );
645
646 if ( isset( $rows[ $key ] ) && isset( $rows[ $key ][ $index ] ) ) {
647 $row = $rows[ $key ][ $index ];
648 }
649
650 unset( $start, $key, $index );
651 }
652 }
653
654 if ( is_array( $row ) ) {
655 // implode the repeated field values
656 $row = implode( self::$separator, FrmAppHelper::array_flatten( $row, 'reset' ) );
657 }
658
659 $val = self::encode_value( $row );
660 if ( 'return' !== self::$line_break ) {
661 $val = str_replace( array( "\r\n", "\r", "\n" ), self::$line_break, $val );
662 }
663
664 if ( $echo ) {
665 echo $sep . '"' . $val . '"'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
666 } else {
667 fwrite( self::$fp, $sep . '"' . $val . '"' );
668 }
669 $sep = self::$column_separator;
670
671 unset( $k, $row );
672 }//end foreach
673 if ( $echo ) {
674 echo "\n";
675 } else {
676 fwrite( self::$fp, "\n" );
677 }
678 }
679
680 public static function encode_value( $line ) {
681 if ( '' === $line ) {
682 return $line;
683 }
684
685 $convmap = false;
686
687 switch ( self::$to_encoding ) {
688 case 'macintosh':
689 // this map was derived from the differences between the MacRoman and UTF-8 Charsets
690 // Reference:
691 // http://www.alanwood.net/demos/macroman.html.
692 $convmap = array( 256, 304, 0, 0xffff, 306, 337, 0, 0xffff, 340, 375, 0, 0xffff, 377, 401, 0, 0xffff, 403, 709, 0, 0xffff, 712, 727, 0, 0xffff, 734, 936, 0, 0xffff, 938, 959, 0, 0xffff, 961, 8210, 0, 0xffff, 8213, 8215, 0, 0xffff, 8219, 8219, 0, 0xffff, 8227, 8229, 0, 0xffff, 8231, 8239, 0, 0xffff, 8241, 8248, 0, 0xffff, 8251, 8259, 0, 0xffff, 8261, 8363, 0, 0xffff, 8365, 8481, 0, 0xffff, 8483, 8705, 0, 0xffff, 8707, 8709, 0, 0xffff, 8711, 8718, 0, 0xffff, 8720, 8720, 0, 0xffff, 8722, 8729, 0, 0xffff, 8731, 8733, 0, 0xffff, 8735, 8746, 0, 0xffff, 8748, 8775, 0, 0xffff, 8777, 8799, 0, 0xffff, 8801, 8803, 0, 0xffff, 8806, 9673, 0, 0xffff, 9675, 63742, 0, 0xffff, 63744, 64256, 0, 0xffff );
693 break;
694 case 'ISO-8859-1':
695 $convmap = array( 256, 10000, 0, 0xffff );
696 }
697
698 if ( is_array( $convmap ) ) {
699 $line = mb_encode_numericentity( $line, $convmap, self::$charset );
700 }
701
702 if ( self::$to_encoding !== self::$charset ) {
703 $line = iconv( self::$charset, self::$to_encoding . '//IGNORE', $line );
704 }
705
706 return self::escape_csv( $line );
707 }
708
709 /**
710 * Escape a " in a csv with another "
711 *
712 * @since 2.0
713 * @param mixed $value
714 * @return mixed
715 */
716 public static function escape_csv( $value ) {
717 if ( ! is_string( $value ) ) {
718 return $value;
719 }
720
721 if ( '=' === $value[0] ) {
722 // escape the = to prevent vulnerability
723 $value = "'" . $value;
724 }
725 $value = str_replace( '"', '""', $value );
726
727 return $value;
728 }
729 }
730