PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.9
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.9
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.9, at classes/helpers/FrmCSVExportHelper.php

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