PluginProbe
ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce / 1.3.13
ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce v1.3.13
1.17.9 1.17.8 1.17.7 1.17.6 1.17.5 1.17.4 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.3.0 1.3.1 1.3.11 1.3.12 1.3.13 1.3.14 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 All 143 releases
erp / includes / lib / parsecsv.lib.php

parsecsv.lib.php in ERP: Complete HR, Accounting & CRM Suite Built for WooCommerce 1.3.13, at includes/lib/parsecsv.lib.php

1,164 lines 34.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class parseCSV {
4
5 /*
6 Class: parseCSV v0.4.3 beta
7 https://github.com/parsecsv/parsecsv-for-php
8
9 Fully conforms to the specifications lined out on wikipedia:
10 - http://en.wikipedia.org/wiki/Comma-separated_values
11
12 Based on the concept of Ming Hong Ng's CsvFileParser class:
13 - http://minghong.blogspot.com/2006/07/csv-parser-for-php.html
14
15
16 (The MIT license)
17
18 Copyright (c) 2014 Jim Myhrberg.
19
20 Permission is hereby granted, free of charge, to any person obtaining a copy
21 of this software and associated documentation files (the "Software"), to deal
22 in the Software without restriction, including without limitation the rights
23 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24 copies of the Software, and to permit persons to whom the Software is
25 furnished to do so, subject to the following conditions:
26
27 The above copyright notice and this permission notice shall be included in
28 all copies or substantial portions of the Software.
29
30 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
36 THE SOFTWARE.
37
38
39 Code Examples
40 ----------------
41 # general usage
42 $csv = new parseCSV('data.csv');
43 print_r($csv->data);
44 ----------------
45 # tab delimited, and encoding conversion
46 $csv = new parseCSV();
47 $csv->encoding('UTF-16', 'UTF-8');
48 $csv->delimiter = "\t";
49 $csv->parse('data.tsv');
50 print_r($csv->data);
51 ----------------
52 # auto-detect delimiter character
53 $csv = new parseCSV();
54 $csv->auto('data.csv');
55 print_r($csv->data);
56 ----------------
57 # modify data in a csv file
58 $csv = new parseCSV();
59 $csv->sort_by = 'id';
60 $csv->parse('data.csv');
61 # "4" is the value of the "id" column of the CSV row
62 $csv->data[4] = array('firstname' => 'John', 'lastname' => 'Doe', 'email' => 'john@doe.com');
63 $csv->save();
64 ----------------
65 # add row/entry to end of CSV file
66 # - only recommended when you know the extact sctructure of the file
67 $csv = new parseCSV();
68 $csv->save('data.csv', array(array('1986', 'Home', 'Nowhere', '')), true);
69 ----------------
70 # convert 2D array to csv data and send headers
71 # to browser to treat output as a file and download it
72 $csv = new parseCSV();
73 $csv->output('movies.csv', $array, array('field 1', 'field 2'), ',');
74 ----------------
75 */
76
77 /**
78 * Configuration
79 * - set these options with $object->var_name = 'value';
80 */
81
82 /**
83 * Heading
84 * Use first line/entry as field names
85 *
86 * @access public
87 * @var bool
88 */
89 public $heading = true;
90
91 /**
92 * Fields
93 * Override field names
94 *
95 * @access public
96 * @var array
97 */
98 public $fields = array();
99
100 /**
101 * Sort By
102 * Sort csv by this field
103 *
104 * @access public
105 * @var string
106 */
107 public $sort_by = null;
108
109 /**
110 * Sort Reverse
111 * Reverse the sort function
112 *
113 * @access public
114 * @var bool
115 */
116 public $sort_reverse = false;
117
118 /**
119 * Sort Type
120 * Sort behavior passed to sort methods
121 *
122 * regular = SORT_REGULAR
123 * numeric = SORT_NUMERIC
124 * string = SORT_STRING
125 *
126 * @access public
127 * @var string
128 */
129 public $sort_type = null;
130
131 /**
132 * Delimiter
133 * Delimiter character
134 *
135 * @access public
136 * @var string
137 */
138 public $delimiter = ',';
139
140 /**
141 * Enclosure
142 * Enclosure character
143 *
144 * @access public
145 * @var string
146 */
147 public $enclosure = '"';
148
149 /**
150 * Enclose All
151 * Force enclosing all columns
152 *
153 * @access public
154 * @var bool
155 */
156 public $enclose_all = false;
157
158 /**
159 * Conditions
160 * Basic SQL-Like conditions for row matching
161 *
162 * @access public
163 * @var string
164 */
165 public $conditions = null;
166
167 /**
168 * Offset
169 * Number of rows to ignore from beginning of data
170 *
171 * @access public
172 * @var int
173 */
174 public $offset = null;
175
176 /**
177 * Limit
178 * Limits the number of returned rows to the specified amount
179 *
180 * @access public
181 * @var int
182 */
183 public $limit = null;
184
185 /**
186 * Auto Depth
187 * Number of rows to analyze when attempting to auto-detect delimiter
188 *
189 * @access public
190 * @var int
191 */
192 public $auto_depth = 15;
193
194 /**
195 * Auto Non Charts
196 * Characters that should be ignored when attempting to auto-detect delimiter
197 *
198 * @access public
199 * @var string
200 */
201 public $auto_non_chars = "a-zA-Z0-9\n\r";
202
203 /**
204 * Auto Preferred
205 * preferred delimiter characters, only used when all filtering method
206 * returns multiple possible delimiters (happens very rarely)
207 *
208 * @access public
209 * @var string
210 */
211 public $auto_preferred = ",;\t.:|";
212
213 /**
214 * Convert Encoding
215 * Should we convert the csv encoding?
216 *
217 * @access public
218 * @var bool
219 */
220 public $convert_encoding = false;
221
222 /**
223 * Input Encoding
224 * Set the input encoding
225 *
226 * @access public
227 * @var string
228 */
229 public $input_encoding = 'ISO-8859-1';
230
231 /**
232 * Output Encoding
233 * Set the output encoding
234 *
235 * @access public
236 * @var string
237 */
238 public $output_encoding = 'ISO-8859-1';
239
240 /**
241 * Linefeed
242 * Line feed characters used by unparse, save, and output methods
243 *
244 * @access public
245 * @var string
246 */
247 public $linefeed = "\r";
248
249 /**
250 * Output Delimiter
251 * Sets the output delimiter used by the output method
252 *
253 * @access public
254 * @var string
255 */
256 public $output_delimiter = ',';
257
258 /**
259 * Output filename
260 * Sets the output filename
261 *
262 * @access public
263 * @var string
264 */
265 public $output_filename = 'data.csv';
266
267 /**
268 * Keep File Data
269 * keep raw file data in memory after successful parsing (useful for debugging)
270 *
271 * @access public
272 * @var bool
273 */
274 public $keep_file_data = false;
275
276 /**
277 * Internal variables
278 */
279
280 /**
281 * File
282 * Current Filename
283 *
284 * @access public
285 * @var string
286 */
287 public $file;
288
289 /**
290 * File Data
291 * Current file data
292 *
293 * @access public
294 * @var string
295 */
296 public $file_data;
297
298 /**
299 * Error
300 * Contains the error code if one occured
301 *
302 * 0 = No errors found. Everything should be fine :)
303 * 1 = Hopefully correctable syntax error was found.
304 * 2 = Enclosure character (double quote by default)
305 * was found in non-enclosed field. This means
306 * the file is either corrupt, or does not
307 * standard CSV formatting. Please validate
308 * the parsed data yourself.
309 *
310 * @access public
311 * @var int
312 */
313 public $error = 0;
314
315 /**
316 * Error Information
317 * Detailed error information
318 *
319 * @access public
320 * @var array
321 */
322 public $error_info = array();
323
324 /**
325 * Titles
326 * CSV titles if they exists
327 *
328 * @access public
329 * @var array
330 */
331 public $titles = array();
332
333 /**
334 * Data
335 * Two dimensional array of CSV data
336 *
337 * @access public
338 * @var array
339 */
340 public $data = array();
341
342 /**
343 * Constructor
344 * Class constructor
345 *
346 * @access public
347 * @param [string] input The CSV string or a direct filepath
348 * @param [integer] offset Number of rows to ignore from the beginning of the data
349 * @param [integer] limit Limits the number of returned rows to specified amount
350 * @param [string] conditions Basic SQL-like conditions for row matching
351 */
352 public function __construct($input = null, $offset = null, $limit = null, $conditions = null, $keep_file_data = null) {
353 if (!is_null($offset)) {
354 $this->offset = $offset;
355 }
356
357 if (!is_null($limit)) {
358 $this->limit = $limit;
359 }
360
361 if (!is_null($conditions)) {
362 $this->conditions = $conditions;
363 }
364
365 if (!is_null($keep_file_data)) {
366 $this->keep_file_data = $keep_file_data;
367 }
368
369 if (!empty($input)) {
370 $this->parse($input);
371 }
372 }
373
374 // ==============================================
375 // ----- [ Main Functions ] ---------------------
376 // ==============================================
377
378 /**
379 * Parse
380 * Parse a CSV file or string
381 *
382 * @access public
383 * @param [string] input The CSV string or a direct filepath
384 * @param [integer] offset Number of rows to ignore from the beginning of the data
385 * @param [integer] limit Limits the number of returned rows to specified amount
386 * @param [string] conditions Basic SQL-like conditions for row matching
387 *
388 * @return [bool]
389 */
390 public function parse($input = null, $offset = null, $limit = null, $conditions = null) {
391 if (is_null($input)) {
392 $input = $this->file;
393 }
394
395 if (!empty($input)) {
396 if (!is_null($offset)) {
397 $this->offset = $offset;
398 }
399
400 if (!is_null($limit)) {
401 $this->limit = $limit;
402 }
403
404 if (!is_null($conditions)) {
405 $this->conditions = $conditions;
406 }
407
408 if (is_readable($input)) {
409 $this->data = $this->parse_file($input);
410 } else {
411 $this->file_data = &$input;
412 $this->data = $this->parse_string();
413 }
414
415 if ($this->data === false) {
416 return false;
417 }
418 }
419
420 return true;
421 }
422
423 /**
424 * Save
425 * Save changes, or write a new file and/or data
426 *
427 * @access public
428 * @param [string] $file File location to save to
429 * @param [array] $data 2D array of data
430 * @param [bool] $append Append current data to end of target CSV, if file exists
431 * @param [array] $fields Field names
432 *
433 * @return [bool]
434 */
435 public function save($file = null, $data = array(), $append = false, $fields = array()) {
436 if (empty($file)) {
437 $file = &$this->file;
438 }
439
440 $mode = ($append) ? 'at' : 'wt';
441 $is_php = (preg_match('/\.php$/i', $file)) ? true : false;
442
443 return $this->_wfile($file, $this->unparse($data, $fields, $append, $is_php), $mode);
444 }
445
446 /**
447 * Output
448 * Generate a CSV based string for output.
449 *
450 * @access public
451 * @param [string] $filename If specified, headers and data will be output directly to browser as a downloable file
452 * @param [array] $data 2D array with data
453 * @param [array] $fields Field names
454 * @param [type] $delimiter delimiter used to separate data
455 *
456 * @return [string]
457 */
458 public function output($filename = null, $data = array(), $fields = array(), $delimiter = null) {
459 if (empty($filename)) {
460 $filename = $this->output_filename;
461 }
462
463 if ($delimiter === null) {
464 $delimiter = $this->output_delimiter;
465 }
466
467 $data = $this->unparse($data, $fields, null, null, $delimiter);
468
469 if (!is_null($filename)) {
470 header('Content-type: application/csv');
471 header('Content-Length: ' . strlen($data));
472 header('Cache-Control: no-cache, must-revalidate');
473 header('Pragma: no-cache');
474 header('Expires: 0');
475 header('Content-Disposition: attachment; filename="' . $filename . '"; modification-date="' . date('r') . '";');
476
477 echo $data;
478 }
479
480 return $data;
481 }
482
483 /**
484 * Encoding
485 * Convert character encoding
486 *
487 * @access public
488 * @param [string] $input Input character encoding, uses default if left blank
489 * @param [string] $output Output character encoding, uses default if left blank
490 */
491 public function encoding($input = null, $output = null) {
492 $this->convert_encoding = true;
493 if (!is_null($input)) {
494 $this->input_encoding = $input;
495 }
496
497 if (!is_null($output)) {
498 $this->output_encoding = $output;
499 }
500 }
501
502 /**
503 * Auto
504 * Auto-Detect Delimiter: Find delimiter by analyzing a specific number of
505 * rows to determine most probable delimiter character
506 *
507 * @access public
508 * @param [string] $file Local CSV file
509 * @param [bool] $parse True/false parse file directly
510 * @param [int] $search_depth Number of rows to analyze
511 * @param [string] $preferred Preferred delimiter characters
512 * @param [string] $enclosure Enclosure character, default is double quote (").
513 *
514 * @return [string]
515 */
516 public function auto($file = null, $parse = true, $search_depth = null, $preferred = null, $enclosure = null) {
517 if (is_null($file)) {
518 $file = $this->file;
519 }
520
521 if (empty($search_depth)) {
522 $search_depth = $this->auto_depth;
523 }
524
525 if (is_null($enclosure)) {
526 $enclosure = $this->enclosure;
527 }
528
529 if (is_null($preferred)) {
530 $preferred = $this->auto_preferred;
531 }
532
533 if (empty($this->file_data)) {
534 if ($this->_check_data($file)) {
535 $data = &$this->file_data;
536 } else {
537 return false;
538 }
539 } else {
540 $data = &$this->file_data;
541 }
542
543 $chars = array();
544 $strlen = strlen($data);
545 $enclosed = false;
546 $n = 1;
547 $to_end = true;
548
549 // walk specific depth finding posssible delimiter characters
550 for ($i = 0; $i < $strlen; $i++) {
551 $ch = $data{$i};
552 $nch = (isset($data{$i + 1})) ? $data{$i + 1} : false;
553 $pch = (isset($data{$i - 1})) ? $data{$i - 1} : false;
554
555 // open and closing quotes
556 if ($ch == $enclosure) {
557 if (!$enclosed || $nch != $enclosure) {
558 $enclosed = ($enclosed) ? false : true;
559 } elseif ($enclosed) {
560 $i++;
561 }
562
563 // end of row
564 } elseif (($ch == "\n" && $pch != "\r" || $ch == "\r") && !$enclosed) {
565 if ($n >= $search_depth) {
566 $strlen = 0;
567 $to_end = false;
568 } else {
569 $n++;
570 }
571
572 // count character
573 } elseif (!$enclosed) {
574 if (!preg_match('/[' . preg_quote($this->auto_non_chars, '/') . ']/i', $ch)) {
575 if (!isset($chars[$ch][$n])) {
576 $chars[$ch][$n] = 1;
577 } else {
578 $chars[$ch][$n]++;
579 }
580 }
581 }
582 }
583
584 // filtering
585 $depth = ($to_end) ? $n - 1 : $n;
586 $filtered = array();
587 foreach ($chars as $char => $value) {
588 if ($match = $this->_check_count($char, $value, $depth, $preferred)) {
589 $filtered[$match] = $char;
590 }
591 }
592
593 // capture most probable delimiter
594 ksort($filtered);
595 $this->delimiter = reset($filtered);
596
597 // parse data
598 if ($parse) {
599 $this->data = $this->parse_string();
600 }
601
602 return $this->delimiter;
603 }
604
605 // ==============================================
606 // ----- [ Core Functions ] ---------------------
607 // ==============================================
608
609 /**
610 * Parse File
611 * Read file to string and call parse_string()
612 *
613 * @access public
614 *
615 * @param [string] $file Local CSV file
616 *
617 * @return [array|bool]
618 */
619 public function parse_file($file = null) {
620 if (is_null($file)) {
621 $file = $this->file;
622 }
623
624 if (empty($this->file_data)) {
625 $this->load_data($file);
626 }
627
628 return (!empty($this->file_data)) ? $this->parse_string() : false;
629 }
630
631 /**
632 * Parse CSV strings to arrays
633 *
634 * @access public
635 * @param data CSV string
636 *
637 * @return 2D array with CSV data, or false on failure
638 */
639 public function parse_string($data = null) {
640 if (empty($data)) {
641 if ($this->_check_data()) {
642 $data = &$this->file_data;
643 } else {
644 return false;
645 }
646 }
647
648 $white_spaces = str_replace($this->delimiter, '', " \t\x0B\0");
649
650 $rows = array();
651 $row = array();
652 $row_count = 0;
653 $current = '';
654 $head = (!empty($this->fields)) ? $this->fields : array();
655 $col = 0;
656 $enclosed = false;
657 $was_enclosed = false;
658 $strlen = strlen($data);
659
660 // force the parser to process end of data as a character (false) when
661 // data does not end with a line feed or carriage return character.
662 $lch = $data{$strlen - 1};
663 if ($lch != "\n" && $lch != "\r") {
664 $strlen++;
665 }
666
667 // walk through each character
668 for ($i = 0; $i < $strlen; $i++) {
669 $ch = (isset($data{$i})) ? $data{$i} : false;
670 $nch = (isset($data{$i + 1})) ? $data{$i + 1} : false;
671 $pch = (isset($data{$i - 1})) ? $data{$i - 1} : false;
672
673 // open/close quotes, and inline quotes
674 if ($ch == $this->enclosure) {
675 if (!$enclosed) {
676 if (ltrim($current, $white_spaces) == '') {
677 $enclosed = true;
678 $was_enclosed = true;
679 } else {
680 $this->error = 2;
681 $error_row = count($rows) + 1;
682 $error_col = $col + 1;
683 if (!isset($this->error_info[$error_row . '-' . $error_col])) {
684 $this->error_info[$error_row . '-' . $error_col] = array(
685 'type' => 2,
686 'info' => 'Syntax error found on row ' . $error_row . '. Non-enclosed fields can not contain double-quotes.',
687 'row' => $error_row,
688 'field' => $error_col,
689 'field_name' => (!empty($head[$col])) ? $head[$col] : null,
690 );
691 }
692
693 $current .= $ch;
694 }
695 } elseif ($nch == $this->enclosure) {
696 $current .= $ch;
697 $i++;
698 } elseif ($nch != $this->delimiter && $nch != "\r" && $nch != "\n") {
699 for ($x = ($i + 1);isset($data{$x}) && ltrim($data{$x}, $white_spaces) == ''; $x++) {}
700 if ($data{$x} == $this->delimiter) {
701 $enclosed = false;
702 $i = $x;
703 } else {
704 if ($this->error < 1) {
705 $this->error = 1;
706 }
707
708 $error_row = count($rows) + 1;
709 $error_col = $col + 1;
710 if (!isset($this->error_info[$error_row . '-' . $error_col])) {
711 $this->error_info[$error_row . '-' . $error_col] = array(
712 'type' => 1,
713 'info' =>
714 'Syntax error found on row ' . (count($rows) + 1) . '. ' .
715 'A single double-quote was found within an enclosed string. ' .
716 'Enclosed double-quotes must be escaped with a second double-quote.',
717 'row' => count($rows) + 1,
718 'field' => $col + 1,
719 'field_name' => (!empty($head[$col])) ? $head[$col] : null,
720 );
721 }
722
723 $current .= $ch;
724 $enclosed = false;
725 }
726 } else {
727 $enclosed = false;
728 }
729
730 // end of field/row/csv
731 } elseif (($ch == $this->delimiter || $ch == "\n" || $ch == "\r" || $ch === false) && !$enclosed) {
732 $key = (!empty($head[$col])) ? $head[$col] : $col;
733 $row[$key] = ($was_enclosed) ? $current : trim($current);
734 $current = '';
735 $was_enclosed = false;
736 $col++;
737
738 // end of row
739 if ($ch == "\n" || $ch == "\r" || $ch === false) {
740 if ($this->_validate_offset($row_count) && $this->_validate_row_conditions($row, $this->conditions)) {
741 if ($this->heading && empty($head)) {
742 $head = $row;
743 } elseif (empty($this->fields) || (!empty($this->fields) && (($this->heading && $row_count > 0) || !$this->heading))) {
744 if (!empty($this->sort_by) && !empty($row[$this->sort_by])) {
745 if (isset($rows[$row[$this->sort_by]])) {
746 $rows[$row[$this->sort_by] . '_0'] = &$rows[$row[$this->sort_by]];
747 unset($rows[$row[$this->sort_by]]);
748 for ($sn = 1;isset($rows[$row[$this->sort_by] . '_' . $sn]); $sn++) {}
749 $rows[$row[$this->sort_by] . '_' . $sn] = $row;
750 } else {
751 $rows[$row[$this->sort_by]] = $row;
752 }
753
754 } else {
755 $rows[] = $row;
756 }
757 }
758 }
759
760 $row = array();
761 $col = 0;
762 $row_count++;
763
764 if ($this->sort_by === null && $this->limit !== null && count($rows) == $this->limit) {
765 $i = $strlen;
766 }
767
768 if ($ch == "\r" && $nch == "\n") {
769 $i++;
770 }
771 }
772
773 // append character to current field
774 } else {
775 $current .= $ch;
776 }
777 }
778
779 $this->titles = $head;
780 if (!empty($this->sort_by)) {
781 $sort_type = SORT_REGULAR;
782 if ($this->sort_type == 'numeric') {
783 $sort_type = SORT_NUMERIC;
784 } elseif ($this->sort_type == 'string') {
785 $sort_type = SORT_STRING;
786 }
787
788 ($this->sort_reverse) ? krsort($rows, $sort_type) : ksort($rows, $sort_type);
789
790 if ($this->offset !== null || $this->limit !== null) {
791 $rows = array_slice($rows, ($this->offset === null ? 0 : $this->offset), $this->limit, true);
792 }
793 }
794
795 if (!$this->keep_file_data) {
796 $this->file_data = null;
797 }
798
799 return $rows;
800 }
801
802 /**
803 * Create CSV data from array
804 *
805 * @access public
806 * @param data 2D array with data
807 * @param fields field names
808 * @param append if true, field names will not be output
809 * @param is_php if a php die() call should be put on the first
810 * line of the file, this is later ignored when read.
811 * @param delimiter field delimiter to use
812 *
813 * @return CSV data (text string)
814 */
815 public function unparse($data = array(), $fields = array(), $append = false, $is_php = false, $delimiter = null) {
816 if (!is_array($data) || empty($data)) {
817 $data = &$this->data;
818 }
819
820 if (!is_array($fields) || empty($fields)) {
821 $fields = &$this->titles;
822 }
823
824 if ($delimiter === null) {
825 $delimiter = $this->delimiter;
826 }
827
828 $string = ($is_php) ? "<?php header('Status: 403'); die(' '); ?>" . $this->linefeed : '';
829 $entry = array();
830
831 // create heading
832 if ($this->heading && !$append && !empty($fields)) {
833 foreach ($fields as $key => $value) {
834 $entry[] = $this->_enclose_value($value, $delimiter);
835 }
836
837 $string .= implode($delimiter, $entry) . $this->linefeed;
838 $entry = array();
839 }
840
841 // create data
842 foreach ($data as $key => $row) {
843 foreach ($row as $field => $value) {
844 $entry[] = $this->_enclose_value($value, $delimiter);
845 }
846
847 $string .= implode($delimiter, $entry) . $this->linefeed;
848 $entry = array();
849 }
850
851 if ($this->convert_encoding) {
852 $string = iconv($this->input_encoding, $this->output_encoding, $string);
853 }
854
855 return $string;
856 }
857
858 /**
859 * Load local file or string
860 *
861 * @access public
862 * @param input local CSV file
863 *
864 * @return true or false
865 */
866 public function load_data($input = null) {
867 $data = null;
868 $file = null;
869
870 if (is_null($input)) {
871 $file = $this->file;
872 } elseif (file_exists($input)) {
873 $file = $input;
874 } else {
875 $data = $input;
876 }
877
878 if (!empty($data) || $data = $this->_rfile($file)) {
879 if ($this->file != $file) {
880 $this->file = $file;
881 }
882
883 if (preg_match('/\.php$/i', $file) && preg_match('/<\?.*?\?>(.*)/ims', $data, $strip)) {
884 $data = ltrim($strip[1]);
885 }
886
887 if ($this->convert_encoding) {
888 $data = iconv($this->input_encoding, $this->output_encoding, $data);
889 }
890
891 if (substr($data, -1) != "\n") {
892 $data .= "\n";
893 }
894
895 $this->file_data = &$data;
896 return true;
897 }
898
899 return false;
900 }
901
902 // ==============================================
903 // ----- [ Internal Functions ] -----------------
904 // ==============================================
905
906 /**
907 * Validate a row against specified conditions
908 *
909 * @access protected
910 * @param row array with values from a row
911 * @param conditions specified conditions that the row must match
912 *
913 * @return true of false
914 */
915 protected function _validate_row_conditions($row = array(), $conditions = null) {
916 if (!empty($row)) {
917 if (!empty($conditions)) {
918 $conditions = (strpos($conditions, ' OR ') !== false) ? explode(' OR ', $conditions) : array($conditions);
919 $or = '';
920 foreach ($conditions as $key => $value) {
921 if (strpos($value, ' AND ') !== false) {
922 $value = explode(' AND ', $value);
923 $and = '';
924
925 foreach ($value as $k => $v) {
926 $and .= $this->_validate_row_condition($row, $v);
927 }
928
929 $or .= (strpos($and, '0') !== false) ? '0' : '1';
930 } else {
931 $or .= $this->_validate_row_condition($row, $value);
932 }
933 }
934
935 return (strpos($or, '1') !== false) ? true : false;
936 }
937
938 return true;
939 }
940
941 return false;
942 }
943
944 /**
945 * Validate a row against a single condition
946 *
947 * @access protected
948 * @param row array with values from a row
949 * @param condition specified condition that the row must match
950 *
951 * @return true of false
952 */
953 protected function _validate_row_condition($row, $condition) {
954 $operators = array(
955 '=', 'equals', 'is',
956 '!=', 'is not',
957 '<', 'is less than',
958 '>', 'is greater than',
959 '<=', 'is less than or equals',
960 '>=', 'is greater than or equals',
961 'contains',
962 'does not contain',
963 );
964
965 $operators_regex = array();
966
967 foreach ($operators as $value) {
968 $operators_regex[] = preg_quote($value, '/');
969 }
970
971 $operators_regex = implode('|', $operators_regex);
972
973 if (preg_match('/^(.+) (' . $operators_regex . ') (.+)$/i', trim($condition), $capture)) {
974 $field = $capture[1];
975 $op = $capture[2];
976 $value = $capture[3];
977
978 if (preg_match('/^([\'\"]{1})(.*)([\'\"]{1})$/i', $value, $capture)) {
979 if ($capture[1] == $capture[3]) {
980 $value = $capture[2];
981 $value = str_replace("\\n", "\n", $value);
982 $value = str_replace("\\r", "\r", $value);
983 $value = str_replace("\\t", "\t", $value);
984 $value = stripslashes($value);
985 }
986 }
987
988 if (array_key_exists($field, $row)) {
989 if (($op == '=' || $op == 'equals' || $op == 'is') && $row[$field] == $value) {
990 return '1';
991 } elseif (($op == '!=' || $op == 'is not') && $row[$field] != $value) {
992 return '1';
993 } elseif (($op == '<' || $op == 'is less than') && $row[$field] < $value) {
994 return '1';
995 } elseif (($op == '>' || $op == 'is greater than') && $row[$field] > $value) {
996 return '1';
997 } elseif (($op == '<=' || $op == 'is less than or equals') && $row[$field] <= $value) {
998 return '1';
999 } elseif (($op == '>=' || $op == 'is greater than or equals') && $row[$field] >= $value) {
1000 return '1';
1001 } elseif ($op == 'contains' && preg_match('/' . preg_quote($value, '/') . '/i', $row[$field])) {
1002 return '1';
1003 } elseif ($op == 'does not contain' && !preg_match('/' . preg_quote($value, '/') . '/i', $row[$field])) {
1004 return '1';
1005 } else {
1006 return '0';
1007 }
1008 }
1009 }
1010
1011 return '1';
1012 }
1013
1014 /**
1015 * Validates if the row is within the offset or not if sorting is disabled
1016 *
1017 * @access protected
1018 * @param current_row the current row number being processed
1019 *
1020 * @return true of false
1021 */
1022 protected function _validate_offset($current_row) {
1023 if ($this->sort_by === null && $this->offset !== null && $current_row < $this->offset) {
1024 return false;
1025 }
1026
1027 return true;
1028 }
1029
1030 /**
1031 * Enclose values if needed
1032 * - only used by unparse()
1033 *
1034 * @access protected
1035 * @param value string to process
1036 *
1037 * @return Processed value
1038 */
1039 protected function _enclose_value($value = null, $delimiter = null) {
1040 if (is_null($delimiter)) {
1041 $delimiter = $this->delimiter;
1042 }
1043 if ($value !== null && $value != '') {
1044 $delimiter_quoted = preg_quote($delimiter, '/');
1045 $enclosure_quoted = preg_quote($this->enclosure, '/');
1046 if (preg_match("/" . $delimiter_quoted . "|" . $enclosure_quoted . "|\n|\r/i", $value) || ($value{0} == ' ' || substr($value, -1) == ' ') || $this->enclose_all) {
1047 $value = str_replace($this->enclosure, $this->enclosure . $this->enclosure, $value);
1048 $value = $this->enclosure . $value . $this->enclosure;
1049 }
1050 }
1051
1052 return $value;
1053 }
1054
1055 /**
1056 * Check file data
1057 *
1058 * @access protected
1059 * @param file local filename
1060 *
1061 * @return true or false
1062 */
1063 protected function _check_data($file = null) {
1064 if (empty($this->file_data)) {
1065 if (is_null($file)) {
1066 $file = $this->file;
1067 }
1068
1069 return $this->load_data($file);
1070 }
1071
1072 return true;
1073 }
1074
1075 /**
1076 * Check if passed info might be delimiter
1077 * Only used by find_delimiter
1078 *
1079 * @access protected
1080 * @param [type] $char [description]
1081 * @param [type] $array [description]
1082 * @param [type] $depth [description]
1083 * @param [type] $preferred [description]
1084 *
1085 * @return special string used for delimiter selection, or false
1086 */
1087 protected function _check_count($char, $array, $depth, $preferred) {
1088 if ($depth == count($array)) {
1089 $first = null;
1090 $equal = null;
1091 $almost = false;
1092 foreach ($array as $key => $value) {
1093 if ($first == null) {
1094 $first = $value;
1095 } elseif ($value == $first && $equal !== false) {
1096 $equal = true;
1097 } elseif ($value == $first + 1 && $equal !== false) {
1098 $equal = true;
1099 $almost = true;
1100 } else {
1101 $equal = false;
1102 }
1103 }
1104
1105 if ($equal) {
1106 $match = ($almost) ? 2 : 1;
1107 $pref = strpos($preferred, $char);
1108 $pref = ($pref !== false) ? str_pad($pref, 3, '0', STR_PAD_LEFT) : '999';
1109
1110 return $pref . $match . '.' . (99999 - str_pad($first, 5, '0', STR_PAD_LEFT));
1111 } else {
1112 return false;
1113 }
1114 }
1115 }
1116
1117 /**
1118 * Read local file
1119 *
1120 * @access protected
1121 * @param file local filename
1122 *
1123 * @return Data from file, or false on failure
1124 */
1125 protected function _rfile($file = null) {
1126 if (is_readable($file)) {
1127 if (!($fh = fopen($file, 'r'))) {
1128 return false;
1129 }
1130
1131 $data = fread($fh, filesize($file));
1132 fclose($fh);
1133 return $data;
1134 }
1135
1136 return false;
1137 }
1138
1139 /**
1140 * Write to local file
1141 *
1142 * @access protected
1143 * @param file local filename
1144 * @param string data to write to file
1145 * @param mode fopen() mode
1146 * @param lock flock() mode
1147 *
1148 * @return true or false
1149 */
1150 protected function _wfile($file, $string = '', $mode = 'wb', $lock = 2) {
1151 if ($fp = fopen($file, $mode)) {
1152 flock($fp, $lock);
1153 $re = fwrite($fp, $string);
1154 $re2 = fclose($fp);
1155
1156 if ($re != false && $re2 != false) {
1157 return true;
1158 }
1159 }
1160
1161 return false;
1162 }
1163 }
1164