PluginProbe ʕ •ᴥ•ʔ
WP All Import – Drag & Drop Import for CSV, XML, Excel & Google Sheets / 4.1.1
WP All Import – Drag & Drop Import for CSV, XML, Excel & Google Sheets v4.1.1
4.1.1 3.9.5 3.9.6 4.0.0 4.0.1 4.1.0 trunk 2.12 2.13 2.14 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.4.7 3.4.8 3.4.9 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.5.7 3.5.8 3.5.9 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9 3.7.0 3.7.1 3.7.2 3.7.3 3.7.3-beta-1.0 3.7.4 3.7.4-beta-1.0 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8.0 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4
wp-all-import / libraries / XmlImportCsvParse.php
wp-all-import / libraries Last commit date
ast 1 month ago XmlImportConfig.php 1 month ago XmlImportCsvParse.php 1 month ago XmlImportException.php 1 month ago XmlImportParser.php 1 month ago XmlImportReaderInterface.php 1 month ago XmlImportSQLParse.php 1 month ago XmlImportStringReader.php 1 month ago XmlImportTemplate.php 1 month ago XmlImportTemplateCodeGenerator.php 1 month ago XmlImportTemplateParser.php 1 month ago XmlImportTemplateScanner.php 2 days ago XmlImportToken.php 1 month ago XmlImportXLSParse.php 1 month ago wpaipclzip.lib.php 1 month ago
XmlImportCsvParse.php
1239 lines
1 <?php
2 // phpcs:disable WordPress.WP.AlternativeFunctions, WordPress.PHP.IniSet, WordPress.NamingConventions.PrefixAllGlobals
3 if ( ! defined( 'ABSPATH' ) ) exit;
4
5 class PMXI_CsvParser
6 {
7 public
8
9 /**
10 * csv parsing default-settings
11 *
12 * @var array
13 * @access public
14 */
15 $settings = array(
16 'delimiter' => ",",
17 'eol' => '',
18 'length' => 999999,
19 'enclosure' => '"',
20 'escape' => "\\",
21 ),
22
23 $tmp_files = array(),
24
25 $xpath = '',
26
27 $delimiter = '',
28
29 $htmlentities = false,
30
31 $xml_path = '',
32
33 $iteration = 0,
34
35 $csv_encoding = 'UTF-8',
36
37 $is_csv = false,
38
39 $targetDir = '',
40
41 $auto_encoding = true;
42
43 protected
44
45 /**
46 * imported data from csv
47 *
48 * @var array
49 * @access protected
50 */
51 $rows = array(),
52
53 /**
54 * csv file to parse
55 *
56 * @var string
57 * @access protected
58 */
59 $_filename = '',
60
61 /**
62 * csv headers to parse
63 *
64 * @var array
65 * @access protected
66 */
67 $headers = array();
68
69 /**
70 * data load initialize
71 *
72 * @param mixed $filename please look at the load() method
73 *
74 * @access public
75 * @see load()
76 * @return void
77 */
78 public function __construct( $options = array('filename' => null, 'xpath' => '', 'delimiter' => '', 'encoding' => '', 'xml_path' => '', 'targetDir' => false) ) {
79
80 PMXI_Plugin::$csv_path = $options['filename'];
81
82 // phpcs:ignore WordPress.Security.NonceVerification.Missing
83 $this->xpath = (!empty($options['xpath']) ? $options['xpath'] : ((!empty($_POST['xpath'])) ? sanitize_text_field(wp_unslash($_POST['xpath'])) : '/node'));
84
85 if ( ! empty($options['delimiter']) ){
86 $this->delimiter = $options['delimiter'];
87 } else {
88 $input = new PMXI_Input();
89 $id = $input->get('id', 0);
90 if (!$id){
91 $id = $input->get('import_id', 0);
92 }
93 if ( $id ){
94 $import = new PMXI_Import_Record();
95 $import->getbyId($id);
96 if ( ! $import->isEmpty() ){
97 $this->delimiter = empty($import->options['delimiter']) ? '' : $import->options['delimiter'];
98 }
99 }
100 }
101 if ( ! empty($options['encoding'])){
102 $this->csv_encoding = $options['encoding'];
103 $this->auto_encoding = false;
104 }
105 if (!empty($options['xml_path'])) $this->xml_path = $options['xml_path'];
106
107 // phpcs:ignore WordPress.PHP.IniSet.Risky,Squiz.PHP.DiscouragedFunctions.Discouraged
108 @ini_set( "display_errors", 0);
109 // phpcs:ignore WordPress.PHP.IniSet.Risky,Squiz.PHP.DiscouragedFunctions.Discouraged
110 @ini_set('auto_detect_line_endings', true);
111
112 $file_params = self::analyse_file($options['filename'], 1);
113
114 $this->set_settings(array('delimiter' => $file_params['delimiter']['value'], 'eol' => $file_params['line_ending']['value']));
115
116 unset($file_params);
117
118 $wp_uploads = wp_upload_dir();
119
120 $this->targetDir = (empty($options['targetDir'])) ? wp_all_import_secure_file($wp_uploads['basedir'] . DIRECTORY_SEPARATOR . PMXI_Plugin::UPLOADS_DIRECTORY) : $options['targetDir'];
121
122 $this->load($options['filename']);
123 }
124
125 /**
126 * csv file loader
127 *
128 * indicates the object which file is to be loaded
129 *
130 *
131 * @param string $filename the csv filename to load
132 *
133 * @access public
134 * @return boolean true if file was loaded successfully
135 * @see isSymmetric(), getAsymmetricRows(), symmetrize()
136 */
137 public function load($filename)
138 {
139 $this->_filename = $filename;
140 $this->flush();
141 return $this->parse();
142 }
143
144 /**
145 * settings alterator
146 *
147 * lets you define different settings for scanning
148 *
149 *
150 * @param mixed $array containing settings to use
151 *
152 * @access public
153 * @return boolean true if changes where applyed successfully
154 * @see $settings
155 */
156 public function set_settings($array)
157 {
158 $this->settings = apply_filters('wp_all_import_csv_parser_settings', array_merge($this->settings, $array));
159 }
160
161 /**
162 * header fetcher
163 *
164 * gets csv headers into an array
165 *
166 * @access public
167 * @return array
168 */
169 public function getHeaders()
170 {
171 return $this->headers;
172 }
173
174 /**
175 * header counter
176 *
177 * retrives the total number of loaded headers
178 *
179 * @access public
180 * @return integer gets the length of headers
181 */
182 public function countHeaders()
183 {
184 return count($this->headers);
185 }
186
187 /**
188 * header and row relationship builder
189 *
190 * Attempts to create a relationship for every single cell that
191 * was captured and its corresponding header. The sample below shows
192 * how a connection/relationship is built.
193 *
194 * @param array $columns the columns to connect, if nothing
195 * is given all headers will be used to create a connection
196 *
197 * @access public
198 * @return array If the data is not symmetric an empty array
199 * will be returned instead
200 * @see isSymmetric(), getAsymmetricRows(), symmetrize(), getHeaders()
201 */
202 public function connect($columns = array())
203 {
204 if (!$this->isSymmetric()) {
205 return array();
206 }
207 if (!is_array($columns)) {
208 return array();
209 }
210 if ($columns === array()) {
211 $columns = $this->headers;
212 }
213
214 $ret_arr = array();
215
216 foreach ($this->rows as $record) {
217 $item_array = array();
218 foreach ($record as $column => $value) {
219 $header = $this->headers[$column];
220 if (in_array($header, $columns)) {
221 $item_array[$header] = $value;
222 }
223 }
224
225 // do not append empty results
226 if ($item_array !== array()) {
227 array_push($ret_arr, $item_array);
228 }
229 }
230
231 return $ret_arr;
232 }
233
234 /**
235 * data length/symmetry checker
236 *
237 * tells if the headers and all of the contents length match.
238 * Note: there is a lot of methods that won't work if data is not
239 * symmetric this method is very important!
240 *
241 * @access public
242 * @return boolean
243 * @see symmetrize(), getAsymmetricRows(), isSymmetric()
244 */
245 public function isSymmetric()
246 {
247 $hc = count($this->headers);
248 foreach ($this->rows as $row) {
249 if (count($row) != $hc) {
250 return false;
251 }
252 }
253 return true;
254 }
255
256 /**
257 * asymmetric data fetcher
258 *
259 * finds the rows that do not match the headers length
260 *
261 * lets assume that we add one more row to our csv file.
262 * that has only two values. Something like
263 *
264 * @access public
265 * @return array filled with rows that do not match headers
266 * @see getHeaders(), symmetrize(), isSymmetric(),
267 * getAsymmetricRows()
268 */
269 public function getAsymmetricRows()
270 {
271 $ret_arr = array();
272 $hc = count($this->headers);
273 foreach ($this->rows as $row) {
274 if (count($row) != $hc) {
275 $ret_arr[] = $row;
276 }
277 }
278 return $ret_arr;
279 }
280
281 /**
282 * all rows length equalizer
283 *
284 * makes the length of all rows and headers the same. If no $value is given
285 * all unexistent cells will be filled with empty spaces
286 *
287 * @param mixed $value the value to fill the unexistent cells
288 *
289 * @access public
290 * @return array
291 * @see isSymmetric(), getAsymmetricRows(), symmetrize()
292 */
293 public function symmetrize($value = '')
294 {
295 $max_length = 0;
296 $headers_length = count($this->headers);
297
298 foreach ($this->rows as $row) {
299 $row_length = count($row);
300 if ($max_length < $row_length) {
301 $max_length = $row_length;
302 }
303 }
304
305 if ($max_length < $headers_length) {
306 $max_length = $headers_length;
307 }
308
309 foreach ($this->rows as $key => $row) {
310 $this->rows[$key] = array_pad($row, $max_length, $value);
311 }
312
313 $this->headers = array_pad($this->headers, $max_length, $value);
314 }
315
316 /**
317 * grid walker
318 *
319 * travels through the whole dataset executing a callback per each
320 * cell
321 *
322 * Note: callback functions get the value of the cell as an
323 * argument, and whatever that callback returns will be used to
324 * replace the current value of that cell.
325 *
326 * @param string $callback the callback function to be called per
327 * each cell in the dataset.
328 *
329 * @access public
330 * @return bool
331 * @see walkColumn(), walkRow(), fillColumn(), fillRow(), fillCell()
332 */
333 public function walkGrid($callback)
334 {
335 foreach (array_keys($this->getRows()) as $key) {
336 if (!$this->walkRow($key, $callback)) {
337 return false;
338 }
339 }
340 return true;
341 }
342
343 /**
344 * column fetcher
345 *
346 * gets all the data for a specific column identified by $name
347 *
348 * Note $name is the same as the items returned by getHeaders()
349 *
350 * @param string $name the name of the column to fetch
351 *
352 * @access public
353 * @return array filled with values of a column
354 * @see getHeaders(), fillColumn(), appendColumn(), getCell(), getRows(),
355 * getRow(), hasColumn()
356 */
357 public function getColumn($name)
358 {
359 if (!in_array($name, $this->headers)) {
360 return array();
361 }
362 $ret_arr = array();
363 $key = array_search($name, $this->headers, true);
364 foreach ($this->rows as $data) {
365 $ret_arr[] = $data[$key];
366 }
367 return $ret_arr;
368 }
369
370 /**
371 * column existance checker
372 *
373 * checks if a column exists, columns are identified by their
374 * header name.
375 *
376 * @param string $string an item returned by getHeaders()
377 *
378 * @access public
379 * @return boolean
380 * @see getHeaders()
381 */
382 public function hasColumn($string)
383 {
384 return in_array($string, $this->headers);
385 }
386
387 /**
388 * column appender
389 *
390 * Appends a column and each or all values in it can be
391 *
392 * @param string $column an item returned by getHeaders()
393 * @param mixed $values same as fillColumn()
394 *
395 * @access public
396 * @return boolean
397 * @see getHeaders(), fillColumn(), fillCell(), createHeaders(),
398 * setHeaders()
399 */
400 public function appendColumn($column, $values = null)
401 {
402 if ($this->hasColumn($column)) {
403 return false;
404 }
405 $this->headers[] = $column;
406 $length = $this->countHeaders();
407 $rows = array();
408
409 foreach ($this->rows as $row) {
410 $rows[] = array_pad($row, $length, '');
411 }
412
413 $this->rows = $rows;
414
415 if ($values === null) {
416 $values = '';
417 }
418
419 return $this->fillColumn($column, $values);
420 }
421
422 /**
423 * collumn data injector
424 *
425 * fills alll the data in the given column with $values
426 *
427 * @param mixed $column the column identified by a string
428 * @param mixed $values ither one of the following
429 * - (Number) will fill the whole column with the value of number
430 * - (String) will fill the whole column with the value of string
431 * - (Array) will fill the while column with the values of array
432 * the array gets ignored if it does not match the length of rows
433 *
434 * @access public
435 * @return bool
436 */
437 public function fillColumn($column, $values = null)
438 {
439 if (!$this->hasColumn($column)) {
440 return false;
441 }
442
443 if ($values === null) {
444 return false;
445 }
446
447 if (!$this->isSymmetric()) {
448 return false;
449 }
450
451 $y = array_search($column, $this->headers);
452
453 if (is_numeric($values) || is_string($values)) {
454 foreach (range(0, $this->countRows() -1) as $x) {
455 $this->fillCell($x, $y, $values);
456 }
457 return true;
458 }
459
460 if ($values === array()) {
461 return false;
462 }
463
464 $length = $this->countRows();
465 if (is_array($values) && $length == count($values)) {
466 for ($x = 0; $x < $length; $x++) {
467 $this->fillCell($x, $y, $values[$x]);
468 }
469 return true;
470 }
471
472 return false;
473 }
474
475 /**
476 * column remover
477 *
478 * Completly removes a whole column identified by $name
479 *
480 * @param string $name same as the ones returned by getHeaders();
481 *
482 * @access public
483 * @return boolean
484 * @see hasColumn(), getHeaders(), createHeaders(), setHeaders(),
485 * isSymmetric(), getAsymmetricRows()
486 */
487 public function removeColumn($name)
488 {
489 if (!in_array($name, $this->headers)) {
490 return false;
491 }
492
493 if (!$this->isSymmetric()) {
494 return false;
495 }
496
497 $key = array_search($name, $this->headers);
498 unset($this->headers[$key]);
499 $this->resetKeys($this->headers);
500
501 foreach ($this->rows as $target => $row) {
502 unset($this->rows[$target][$key]);
503 $this->resetKeys($this->rows[$target]);
504 }
505
506 return $this->isSymmetric();
507 }
508
509 /**
510 * column walker
511 *
512 * goes through the whole column and executes a callback for each
513 * one of the cells in it.
514 *
515 * Note: callback functions get the value of the cell as an
516 * argument, and whatever that callback returns will be used to
517 * replace the current value of that cell.
518 *
519 * @param string $name the header name used to identify the column
520 * @param string $callback the callback function to be called per
521 * each cell value
522 *
523 * @access public
524 * @return boolean
525 * @see getHeaders(), fillColumn(), appendColumn()
526 */
527 public function walkColumn($name, $callback)
528 {
529 if (!$this->isSymmetric()) {
530 return false;
531 }
532
533 if (!$this->hasColumn($name)) {
534 return false;
535 }
536
537 if (!function_exists($callback)) {
538 return false;
539 }
540
541 $column = $this->getColumn($name);
542 foreach ($column as $key => $cell) {
543 $column[$key] = $callback($cell);
544 }
545 return $this->fillColumn($name, $column);
546 }
547
548 /**
549 * cell fetcher
550 *
551 * gets the value of a specific cell by given coordinates
552 *
553 * @param integer $x the row to fetch
554 * @param integer $y the column to fetch
555 *
556 * @access public
557 * @return mixed|false the value of the cell or false if the cell does
558 * not exist
559 * @see getHeaders(), hasCell(), getRow(), getRows(), getColumn()
560 */
561 public function getCell($x, $y)
562 {
563 if ($this->hasCell($x, $y)) {
564 $row = $this->getRow($x);
565 return $row[$y];
566 }
567 return false;
568 }
569
570 /**
571 * cell value filler
572 *
573 * replaces the value of a specific cell
574 *
575 * @param integer $x the row to fetch
576 * @param integer $y the column to fetch
577 * @param mixed $value the value to fill the cell with
578 *
579 * @access public
580 * @return boolean
581 * @see hasCell(), getRow(), getRows(), getColumn()
582 */
583 public function fillCell($x, $y, $value)
584 {
585 if (!$this->hasCell($x, $y)) {
586 return false;
587 }
588 $row = $this->getRow($x);
589 $row[$y] = $value;
590 $this->rows[$x] = $row;
591 return true;
592 }
593
594 /**
595 * checks if a coordinate is valid
596 *
597 * @param mixed $x the row to fetch
598 * @param mixed $y the column to fetch
599 *
600 * @access public
601 * @return bool
602 */
603 public function hasCell($x, $y)
604 {
605 $has_x = array_key_exists($x, $this->rows);
606 $has_y = array_key_exists($y, $this->headers);
607 return ($has_x && $has_y);
608 }
609
610 /**
611 * row fetcher
612 *
613 * Note: first row is zero
614 *
615 * @param integer $number the row number to fetch
616 *
617 * @access public
618 * @return array the row identified by number, if $number does
619 * not exist an empty array is returned instead
620 */
621 public function getRow($number)
622 {
623 $raw = $this->rows;
624 if (array_key_exists($number, $raw)) {
625 return $raw[$number];
626 }
627 return array();
628 }
629
630 /**
631 * multiple row fetcher
632 *
633 * Extracts a rows in the following fashion
634 * - all rows if no $range argument is given
635 * - a range of rows identified by their key
636 * - if rows in range are not found nothing is retrived instead
637 * - if no rows were found an empty array is returned
638 *
639 * @param array $range a list of rows to retrive
640 *
641 * @access public
642 * @return array
643 */
644 public function getRows($range = array())
645 {
646 if (is_array($range) && ($range === array())) {
647 return $this->rows;
648 }
649
650 if (!is_array($range)) {
651 return $this->rows;
652 }
653
654 $ret_arr = array();
655 foreach ($this->rows as $key => $row) {
656 if (in_array($key, $range)) {
657 $ret_arr[] = $row;
658 }
659 }
660 return $ret_arr;
661 }
662
663 /**
664 * row counter
665 *
666 * This function will exclude the headers
667 *
668 * @access public
669 * @return integer
670 */
671 public function countRows()
672 {
673 return count($this->rows);
674 }
675
676 /**
677 * row appender
678 *
679 * Aggregates one more row to the currently loaded dataset
680 *
681 * @param array $values the values to be appended to the row
682 *
683 * @access public
684 * @return boolean
685 */
686 public function appendRow($values)
687 {
688 $this->rows[] = array();
689 $this->symmetrize();
690 return $this->fillRow($this->countRows() - 1, $values);
691 }
692
693 /**
694 * fillRow
695 *
696 * Replaces the contents of cells in one given row with $values.
697 *
698 * @param integer $row the row to fill identified by its key
699 * @param mixed $values the value to use, if a string or number
700 * is given the whole row will be replaced with this value.
701 * if an array is given instead the values will be used to fill
702 * the row. Only when the currently loaded dataset is symmetric
703 *
704 * @access public
705 * @return boolean
706 * @see isSymmetric(), getAsymmetricRows(), symmetrize(), fillColumn(),
707 * fillCell(), appendRow()
708 */
709 public function fillRow($row, $values)
710 {
711 if (!$this->hasRow($row)) {
712 return false;
713 }
714
715 if (is_string($values) || is_numeric($values)) {
716 foreach ($this->rows[$row] as $key => $cell) {
717 $this->rows[$row][$key] = $values;
718 }
719 return true;
720 }
721
722 $eql_to_headers = ($this->countHeaders() == count($values));
723 if (is_array($values) && $this->isSymmetric() && $eql_to_headers) {
724 $this->rows[$row] = $values;
725 return true;
726 }
727
728 return false;
729 }
730
731 /**
732 * row existance checker
733 *
734 * Scans currently loaded dataset and
735 * checks if a given row identified by $number exists
736 *
737 * @param mixed $number a numeric value that identifies the row
738 * you are trying to fetch.
739 *
740 * @access public
741 * @return boolean
742 * @see getRow(), getRows(), appendRow(), fillRow()
743 */
744 public function hasRow($number)
745 {
746 return (in_array($number, array_keys($this->rows)));
747 }
748
749 /**
750 * row remover
751 *
752 * removes one row from the current data set.
753 *
754 *
755 * @param mixed $number the key that identifies that row
756 *
757 * @access public
758 * @return boolean
759 * @see hasColumn(), getHeaders(), createHeaders(), setHeaders(),
760 * isSymmetric(), getAsymmetricRows()
761 */
762 public function removeRow($number)
763 {
764 $cnt = $this->countRows();
765 $row = $this->getRow($number);
766 if (is_array($row) && ($row != array())) {
767 unset($this->rows[$number]);
768 } else {
769 return false;
770 }
771 $this->resetKeys($this->rows);
772 return ($cnt == ($this->countRows() + 1));
773 }
774
775 /**
776 * row walker
777 *
778 * goes through one full row of data and executes a callback
779 * function per each cell in that row.
780 *
781 * Note: callback functions get the value of the cell as an
782 * argument, and whatever that callback returns will be used to
783 * replace the current value of that cell.
784 *
785 * @param string|integer $row anything that is numeric is a valid row
786 * identificator. As long as it is within the range of the currently
787 * loaded dataset
788 *
789 * @param string $callback the callback function to be executed
790 * per each cell in a row
791 *
792 * @access public
793 * @return boolean
794 * - false if callback does not exist
795 * - false if row does not exits
796 */
797 public function walkRow($row, $callback)
798 {
799 if (!function_exists($callback)) {
800 return false;
801 }
802 if ($this->hasRow($row)) {
803 foreach ($this->getRow($row) as $key => $value) {
804 $this->rows[$row][$key] = $callback($value);
805 }
806 return true;
807 }
808 return false;
809 }
810
811 /**
812 * raw data as array
813 *
814 * Gets the data that was retrived from the csv file as an array
815 *
816 * Note: that changes and alterations made to rows, columns and
817 * values will also reflect on what this function retrives.
818 *
819 * @access public
820 * @return array
821 * @see connect(), getHeaders(), getRows(), isSymmetric(), getAsymmetricRows(),
822 * symmetrize()
823 */
824 public function getRawArray()
825 {
826 $ret_arr = array();
827
828 foreach ($this->rows as $key => $row) {
829 $item = array();
830 foreach ($this->headers as $col => $value) {
831 $item[$value] = $this->fixEncoding($row[$col]);
832 }
833 array_push($ret_arr, $item);
834 unset($item);
835 }
836 return $ret_arr;
837 }
838
839 // Fixes the encoding to uf8
840 function fixEncoding($in_str)
841 {
842
843 if (function_exists('mb_detect_encoding') and function_exists('mb_check_encoding')){
844
845 $cur_encoding = mb_detect_encoding($in_str) ;
846
847 if ( $cur_encoding == "UTF-8" && mb_check_encoding($in_str,"UTF-8") ){
848 return $in_str;
849 }
850 else
851 return mb_convert_encoding( $in_str, 'UTF-8', 'ISO-8859-1' );
852
853 }
854
855 return $in_str;
856
857 } // fixEncoding
858
859 /**
860 * header creator
861 *
862 * uses prefix and creates a header for each column suffixed by a
863 * numeric value
864 *
865 * @param string $prefix string to use as prefix for each
866 * independent header
867 *
868 * @access public
869 * @return boolean fails if data is not symmetric
870 * @see isSymmetric(), getAsymmetricRows()
871 */
872 public function createHeaders($prefix)
873 {
874 if (!$this->isSymmetric()) {
875 return false;
876 }
877
878 $length = count($this->headers) + 1;
879 $this->moveHeadersToRows();
880
881 $ret_arr = array();
882 for ($i = 1; $i < $length; $i ++) {
883 $ret_arr[] = $prefix . "_$i";
884 }
885 $this->headers = $ret_arr;
886 return $this->isSymmetric();
887 }
888
889 /**
890 * header injector
891 *
892 * uses a $list of values which wil be used to replace current
893 * headers.
894 *
895 *
896 * @param array $list a collection of names to use as headers,
897 *
898 * @access public
899 * @return boolean fails if data is not symmetric
900 * @see isSymmetric(), getAsymmetricRows(), getHeaders(), createHeaders()
901 */
902 public function setHeaders($list)
903 {
904 if (!$this->isSymmetric()) {
905 return false;
906 }
907 if (!is_array($list)) {
908 return false;
909 }
910 if (count($list) != count($this->headers)) {
911 return false;
912 }
913 $this->moveHeadersToRows();
914 $this->headers = $list;
915 return true;
916 }
917
918 /**
919 * csv parser
920 *
921 * reads csv data and transforms it into php-data
922 *
923 * @access protected
924 * @return boolean
925 */
926 protected function parse() {
927 if (!$this->validates()) {
928 return false;
929 }
930
931 $tmpname = wp_unique_filename($this->targetDir, str_replace("csv", "xml", basename($this->_filename)));
932 if ("" == $this->xml_path) {
933 $this->xml_path = $this->targetDir .'/'. wp_all_import_url_title($tmpname);
934 }
935
936 $ignore_special_characters = apply_filters('wp_all_import_csv_to_xml_remove_non_ascii_characters', true);
937 $this->toXML($ignore_special_characters);
938 return true;
939 }
940
941 function toXML( $fixBrokenSymbols = false ){
942
943 $c = 0;
944 $d = ( "" != $this->delimiter ) ? $this->delimiter : $this->settings['delimiter'];
945 $en = $this->settings['enclosure'];
946 $e = $this->settings['escape'];
947 $l = $this->settings['length'];
948
949 $this->is_csv = $d;
950
951 $is_html = false;
952 $f = @fopen($this->_filename, "rb");
953 while (!@feof($f)) {
954 $chunk = @fread($f, 1024);
955 if (strpos($chunk, "<!DOCTYPE") === 0) $is_html = true;
956 break;
957 }
958
959 if ($is_html) return;
960
961 $res = fopen($this->_filename, 'rb');
962
963 $xmlWriter = new XMLWriter();
964 $xmlWriter->openURI($this->xml_path);
965 $xmlWriter->setIndent(true);
966 $xmlWriter->setIndentString("\t");
967 $xmlWriter->startDocument('1.0', $this->csv_encoding);
968 $xmlWriter->startElement('data');
969
970 $import_id = 0;
971
972 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
973 if ( ! empty($_GET['id']) ) $import_id = intval($_GET['id']);
974
975 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
976 if ( ! empty($_GET['import_id']) ) $import_id = intval($_GET['import_id']);
977
978 $create_new_headers = false;
979 $skip_x_rows = apply_filters('wp_all_import_skip_x_csv_rows', false, $import_id);
980 $headers = array();
981 while ($keys = fgetcsv($res, $l, $d, $en, $e)) {
982
983 if ($skip_x_rows !== false && $skip_x_rows > $c) {
984 $c++;
985 continue;
986 }
987 if ($skip_x_rows !== false && $skip_x_rows <= $c) {
988 $skip_x_rows = false;
989 $c = 0;
990 }
991 $empty_columns = 0;
992 foreach ($keys as $key) {
993 if (preg_replace("%\s%", "", $key) == '') $empty_columns++;
994 }
995 // Skip empty lines.
996 if ($empty_columns == count($keys)) continue;
997
998 if ($c == 0) {
999 $buf_keys = $keys;
1000 foreach ($keys as $key => $value) {
1001
1002 if (!$create_new_headers and (preg_match('%\W(http:|https:|ftp:)$%i', $value) or is_numeric($value))) {
1003 $create_new_headers = true;
1004 }
1005
1006 $value = trim(strtolower(preg_replace('/[^a-z0-9_]/i', '', $value)));
1007 if (preg_match('/^[0-9]{1}/', $value)) {
1008 $value = 'el_' . trim(strtolower($value));
1009 }
1010 $value = (!empty($value)) ? $value : 'undefined' . $key;
1011
1012 if (empty($headers[$value])) {
1013 $headers[$value] = 1;
1014 }
1015 else {
1016 $headers[$value]++;
1017 }
1018
1019 $keys[$key] = ($headers[$value] === 1) ? $value : $value . '_' . $headers[$value];
1020 }
1021 $this->headers = $keys;
1022 $create_new_headers = apply_filters('wp_all_import_auto_create_csv_headers', $create_new_headers, $import_id);
1023 if ($create_new_headers) {
1024 $this->createHeaders('column');
1025 $keys = $buf_keys;
1026 }
1027 }
1028 if ( $c or $create_new_headers ) {
1029 if (!empty($keys)) {
1030 $chunk = array();
1031 foreach ($this->headers as $key => $header) {
1032 if(isset($keys[$key])) {
1033 $chunk[ $header ] = $this->fixEncoding( $keys[ $key ] );
1034 }
1035 }
1036 if ( ! empty($chunk) ) {
1037 $xmlWriter->startElement('node');
1038 foreach ($chunk as $header => $value) {
1039 $xmlWriter->startElement($header);
1040 $value = preg_replace('/\]\]>/s', '', preg_replace('/<!\[CDATA\[/s', '', $value ));
1041 if ($fixBrokenSymbols) {
1042 // Remove non ASCII symbols and write CDATA.
1043 $xmlWriter->writeCData(preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $value));
1044 }
1045 else {
1046 $xmlWriter->writeCData($value);
1047 }
1048 $xmlWriter->endElement();
1049 }
1050 $xmlWriter->endElement();
1051 }
1052 }
1053 }
1054 $c++;
1055 }
1056
1057 if ($c === 1 && !$create_new_headers) {
1058 $xmlWriter->startElement('node');
1059 $xmlWriter->endElement();
1060 }
1061 fclose($res);
1062 $xmlWriter->endElement();
1063 $xmlWriter->flush(TRUE);
1064
1065 return TRUE;
1066 }
1067
1068 /**
1069 * empty row remover
1070 *
1071 * removes all records that have been defined but have no data.
1072 *
1073 * @access protected
1074 * @return array containing only the rows that have data
1075 */
1076 protected function removeEmpty() {
1077 $ret_arr = array();
1078 foreach ($this->rows as $row) {
1079 $line = trim(join('', $row));
1080 if (!empty($line)) {
1081 $ret_arr[] = $row;
1082 }
1083 }
1084 $this->rows = $ret_arr;
1085 }
1086
1087 /**
1088 * csv file validator
1089 *
1090 * checks wheather if the given csv file is valid or not
1091 *
1092 * @access protected
1093 * @return boolean
1094 */
1095 protected function validates(){
1096 // file existance
1097 if (!file_exists($this->_filename)) {
1098 return false;
1099 }
1100 // file readability
1101 if (!is_readable($this->_filename)) {
1102 return false;
1103 }
1104 return true;
1105 }
1106
1107 /**
1108 * header relocator
1109 *
1110 * @access protected
1111 * @return void
1112 */
1113 protected function moveHeadersToRows() {
1114 $arr = array();
1115 $arr[] = $this->headers;
1116 foreach ($this->rows as $row) {
1117 $arr[] = $row;
1118 }
1119 $this->rows = $arr;
1120 $this->headers = array();
1121 }
1122
1123 /**
1124 * array key reseter
1125 *
1126 * makes sure that an array's keys are setted in a correct numerical order
1127 *
1128 * Note: that this function does not return anything, all changes
1129 * are made to the original array as a reference
1130 *
1131 * @param array &$array any array, if keys are strings they will
1132 * be replaced with numeric values
1133 *
1134 * @access protected
1135 * @return void
1136 */
1137 protected function resetKeys(&$array)
1138 {
1139 $arr = array();
1140 foreach ($array as $item) {
1141 $arr[] = $item;
1142 }
1143 $array = $arr;
1144 }
1145
1146 /**
1147 * object data flusher
1148 *
1149 * tells this object to forget all data loaded and start from
1150 * scratch
1151 *
1152 * @access protected
1153 * @return void
1154 */
1155 protected function flush()
1156 {
1157 $this->rows = array();
1158 $this->headers = array();
1159 }
1160
1161 function analyse_file($file, $capture_limit_in_kb = 10) {
1162 // capture starting memory usage
1163 $output['peak_mem']['start'] = memory_get_peak_usage(true);
1164
1165 // log the limit how much of the file was sampled (in Kb)
1166 $output['read_kb'] = $capture_limit_in_kb;
1167
1168 // read in file
1169 $fh = fopen($file, 'r');
1170 $contents = fgets($fh);
1171 fclose($fh);
1172
1173 // specify allowed field delimiters
1174 $delimiters = array(
1175 'comma' => ',',
1176 'semicolon' => ';',
1177 'pipe' => '|',
1178 'tabulation' => "\t"
1179 );
1180
1181 $delimiters = apply_filters('wp_all_import_specified_delimiters', $delimiters);
1182
1183 // specify allowed line endings
1184 $line_endings = array(
1185 'rn' => "\r\n",
1186 'n' => "\n",
1187 'r' => "\r",
1188 'nr' => "\n\r"
1189 );
1190
1191 // loop and count each line ending instance
1192 foreach ($line_endings as $key => $value) {
1193 $line_result[$key] = substr_count($contents, $value);
1194 }
1195
1196 // sort by largest array value
1197 asort($line_result);
1198
1199 // log to output array
1200 $output['line_ending']['results'] = $line_result;
1201 $output['line_ending']['count'] = end($line_result);
1202 $output['line_ending']['key'] = key($line_result);
1203 $output['line_ending']['value'] = $line_endings[$output['line_ending']['key']];
1204 $lines = explode($output['line_ending']['value'], $contents);
1205
1206 // remove last line of array, as this maybe incomplete?
1207 array_pop($lines);
1208
1209 // create a string from the legal lines
1210 $complete_lines = implode(' ', $lines);
1211
1212 // log statistics to output array
1213 $output['lines']['count'] = count($lines);
1214 $output['lines']['length'] = strlen($complete_lines);
1215
1216 // loop and count each delimiter instance
1217 foreach ($delimiters as $delimiter_key => $delimiter) {
1218 $delimiter_result[$delimiter_key] = substr_count($complete_lines, $delimiter);
1219 }
1220
1221 // sort by largest array value
1222 asort($delimiter_result);
1223
1224 // log statistics to output array with largest counts as the value
1225 $output['delimiter']['results'] = $delimiter_result;
1226 $output['delimiter']['count'] = end($delimiter_result);
1227 $output['delimiter']['key'] = key($delimiter_result);
1228 $output['delimiter']['value'] = $delimiters[$output['delimiter']['key']];
1229
1230 // capture ending memory usage
1231 $output['peak_mem']['end'] = memory_get_peak_usage(true);
1232
1233 return $output;
1234 }
1235
1236 }
1237
1238 ?>
1239