PluginProbe
M Chart / 1.2.1
M Chart v1.2.1
2.3.2 2.3.1 2.3 2.2.2 2.2.1 2.2 trunk 1.0 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.10 1.10.1 1.11 1.11.1 1.11.2 1.12 1.2 1.2.1 1.3 1.3.1 1.3.2 All 53 releases
m-chart / components / external / parsecsv / parsecsv.lib.php

parsecsv.lib.php in M Chart 1.2.1, at components/external/parsecsv/parsecsv.lib.php

1,198 lines 34.9 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 /**
344 * Constructor
345 * Class constructor
346 *
347 * @access public
348 * @param [string] input The CSV string or a direct filepath
349 * @param [integer] offset Number of rows to ignore from the beginning of the data
350 * @param [integer] limit Limits the number of returned rows to specified amount
351 * @param [string] conditions Basic SQL-like conditions for row matching
352 */
353 public function __construct ($input = null, $offset = null, $limit = null, $conditions = null, $keep_file_data = null) {
354 if (!is_null($offset)) {
355 $this->offset = $offset;
356 }
357
358 if (!is_null($limit)) {
359 $this->limit = $limit;
360 }
361
362 if (!is_null($conditions)) {
363 $this->conditions = $conditions;
364 }
365
366 if (!is_null($keep_file_data)) {
367 $this->keep_file_data = $keep_file_data;
368 }
369
370 if (!empty($input)) {
371 $this->parse($input);
372 }
373 }
374
375
376 // ==============================================
377 // ----- [ Main Functions ] ---------------------
378 // ==============================================
379
380
381 /**
382 * Parse
383 * Parse a CSV file or string
384 *
385 * @access public
386 * @param [string] input The CSV string or a direct filepath
387 * @param [integer] offset Number of rows to ignore from the beginning of the data
388 * @param [integer] limit Limits the number of returned rows to specified amount
389 * @param [string] conditions Basic SQL-like conditions for row matching
390 *
391 * @return [bool]
392 */
393 public function parse ($input = null, $offset = null, $limit = null, $conditions = null) {
394 if (is_null($input)) {
395 $input = $this->file;
396 }
397
398 if (!empty($input)) {
399 if (!is_null($offset)) {
400 $this->offset = $offset;
401 }
402
403 if (!is_null($limit)) {
404 $this->limit = $limit;
405 }
406
407 if (!is_null($conditions)) {
408 $this->conditions = $conditions;
409 }
410
411 if (is_readable($input)) {
412 $this->data = $this->parse_file($input);
413 }
414 else {
415 $this->file_data = &$input;
416 $this->data = $this->parse_string();
417 }
418
419 if ($this->data === false) {
420 return false;
421 }
422 }
423
424 return true;
425 }
426
427 /**
428 * Save
429 * Save changes, or write a new file and/or data
430 *
431 * @access public
432 * @param [string] $file File location to save to
433 * @param [array] $data 2D array of data
434 * @param [bool] $append Append current data to end of target CSV, if file exists
435 * @param [array] $fields Field names
436 *
437 * @return [bool]
438 */
439 public function save ($file = null, $data = array(), $append = false, $fields = array()) {
440 if (empty($file)) {
441 $file = &$this->file;
442 }
443
444 $mode = ($append) ? 'at' : 'wt';
445 $is_php = (preg_match('/\.php$/i', $file)) ? true : false;
446
447 return $this->_wfile($file, $this->unparse($data, $fields, $append, $is_php), $mode);
448 }
449
450 /**
451 * Output
452 * Generate a CSV based string for output.
453 *
454 * @access public
455 * @param [string] $filename If specified, headers and data will be output directly to browser as a downloable file
456 * @param [array] $data 2D array with data
457 * @param [array] $fields Field names
458 * @param [type] $delimiter delimiter used to separate data
459 *
460 * @return [string]
461 */
462 public function output ($filename = null, $data = array(), $fields = array(), $delimiter = null) {
463 if (empty($filename)) {
464 $filename = $this->output_filename;
465 }
466
467 if ($delimiter === null) {
468 $delimiter = $this->output_delimiter;
469 }
470
471 $data = $this->unparse($data, $fields, null, null, $delimiter);
472
473 if (!is_null($filename)) {
474 header('Content-type: application/csv');
475 header('Content-Length: '.strlen($data));
476 header('Cache-Control: no-cache, must-revalidate');
477 header('Pragma: no-cache');
478 header('Expires: 0');
479 header('Content-Disposition: attachment; filename="'.$filename.'"; modification-date="'.date('r').'";');
480
481 echo $data;
482 }
483
484 return $data;
485 }
486
487 /**
488 * Encoding
489 * Convert character encoding
490 *
491 * @access public
492 * @param [string] $input Input character encoding, uses default if left blank
493 * @param [string] $output Output character encoding, uses default if left blank
494 */
495 public function encoding ($input = null, $output = null) {
496 $this->convert_encoding = true;
497 if (!is_null($input)) {
498 $this->input_encoding = $input;
499 }
500
501 if (!is_null($output)) {
502 $this->output_encoding = $output;
503 }
504 }
505
506 /**
507 * Auto
508 * Auto-Detect Delimiter: Find delimiter by analyzing a specific number of
509 * rows to determine most probable delimiter character
510 *
511 * @access public
512 * @param [string] $file Local CSV file
513 * @param [bool] $parse True/false parse file directly
514 * @param [int] $search_depth Number of rows to analyze
515 * @param [string] $preferred Preferred delimiter characters
516 * @param [string] $enclosure Enclosure character, default is double quote (").
517 *
518 * @return [string]
519 */
520 public function auto ($file = null, $parse = true, $search_depth = null, $preferred = null, $enclosure = null) {
521 if (is_null($file)) {
522 $file = $this->file;
523 }
524
525 if (empty($search_depth)) {
526 $search_depth = $this->auto_depth;
527 }
528
529 if (is_null($enclosure)) {
530 $enclosure = $this->enclosure;
531 }
532
533 if (is_null($preferred)) {
534 $preferred = $this->auto_preferred;
535 }
536
537 if (empty($this->file_data)) {
538 if ($this->_check_data($file)) {
539 $data = &$this->file_data;
540 }
541 else {
542 return false;
543 }
544 }
545 else {
546 $data = &$this->file_data;
547 }
548
549 $chars = array();
550 $strlen = strlen($data);
551 $enclosed = false;
552 $n = 1;
553 $to_end = true;
554
555 // walk specific depth finding posssible delimiter characters
556 for ($i=0; $i < $strlen; $i++) {
557 $ch = $data{$i};
558 $nch = (isset($data{$i+1})) ? $data{$i+1} : false ;
559 $pch = (isset($data{$i-1})) ? $data{$i-1} : false ;
560
561 // open and closing quotes
562 if ($ch == $enclosure) {
563 if (!$enclosed || $nch != $enclosure) {
564 $enclosed = ($enclosed) ? false : true ;
565 }
566 elseif ($enclosed) {
567 $i++;
568 }
569
570 // end of row
571 }
572 elseif (($ch == "\n" && $pch != "\r" || $ch == "\r") && !$enclosed) {
573 if ($n >= $search_depth) {
574 $strlen = 0;
575 $to_end = false;
576 }
577 else {
578 $n++;
579 }
580
581 // count character
582 }
583 elseif (!$enclosed) {
584 if (!preg_match('/['.preg_quote($this->auto_non_chars, '/').']/i', $ch)) {
585 if (!isset($chars[$ch][$n])) {
586 $chars[$ch][$n] = 1;
587 }
588 else {
589 $chars[$ch][$n]++;
590 }
591 }
592 }
593 }
594
595 // filtering
596 $depth = ($to_end) ? $n-1 : $n;
597 $filtered = array();
598 foreach ($chars as $char => $value) {
599 if ($match = $this->_check_count($char, $value, $depth, $preferred)) {
600 $filtered[$match] = $char;
601 }
602 }
603
604 // capture most probable delimiter
605 ksort($filtered);
606 $this->delimiter = reset($filtered);
607
608 // parse data
609 if ($parse) {
610 $this->data = $this->parse_string();
611 }
612
613 return $this->delimiter;
614 }
615
616
617 // ==============================================
618 // ----- [ Core Functions ] ---------------------
619 // ==============================================
620
621 /**
622 * Parse File
623 * Read file to string and call parse_string()
624 *
625 * @access public
626 *
627 * @param [string] $file Local CSV file
628 *
629 * @return [array|bool]
630 */
631 public function parse_file ($file = null) {
632 if (is_null($file)) {
633 $file = $this->file;
634 }
635
636 if (empty($this->file_data)) {
637 $this->load_data($file);
638 }
639
640 return (!empty($this->file_data)) ? $this->parse_string() : false;
641 }
642
643 /**
644 * Parse CSV strings to arrays
645 *
646 * @access public
647 * @param data CSV string
648 *
649 * @return 2D array with CSV data, or false on failure
650 */
651 public function parse_string ($data = null) {
652 if (empty($data)) {
653 if ($this->_check_data()) {
654 $data = &$this->file_data;
655 }
656 else {
657 return false;
658 }
659 }
660
661 $white_spaces = str_replace($this->delimiter, '', " \t\x0B\0");
662
663 $rows = array();
664 $row = array();
665 $row_count = 0;
666 $current = '';
667 $head = (!empty($this->fields)) ? $this->fields : array();
668 $col = 0;
669 $enclosed = false;
670 $was_enclosed = false;
671 $strlen = strlen($data);
672
673 // force the parser to process end of data as a character (false) when
674 // data does not end with a line feed or carriage return character.
675 $lch = $data{$strlen-1};
676 if ($lch != "\n" && $lch != "\r") {
677 $strlen++;
678 }
679
680 // walk through each character
681 for ($i=0; $i < $strlen; $i++) {
682 $ch = (isset($data{$i})) ? $data{$i} : false;
683 $nch = (isset($data{$i+1})) ? $data{$i+1} : false;
684 $pch = (isset($data{$i-1})) ? $data{$i-1} : false;
685
686 // open/close quotes, and inline quotes
687 if ($ch == $this->enclosure) {
688 if (!$enclosed) {
689 if (ltrim($current,$white_spaces) == '') {
690 $enclosed = true;
691 $was_enclosed = true;
692 }
693 else {
694 $this->error = 2;
695 $error_row = count($rows) + 1;
696 $error_col = $col + 1;
697 if (!isset($this->error_info[$error_row.'-'.$error_col])) {
698 $this->error_info[$error_row.'-'.$error_col] = array(
699 'type' => 2,
700 'info' => 'Syntax error found on row '.$error_row.'. Non-enclosed fields can not contain double-quotes.',
701 'row' => $error_row,
702 'field' => $error_col,
703 'field_name' => (!empty($head[$col])) ? $head[$col] : null,
704 );
705 }
706
707 $current .= $ch;
708 }
709 }
710 elseif ($nch == $this->enclosure) {
711 $current .= $ch;
712 $i++;
713 }
714 elseif ($nch != $this->delimiter && $nch != "\r" && $nch != "\n") {
715 for ($x=($i+1); isset($data{$x}) && ltrim($data{$x}, $white_spaces) == ''; $x++) {}
716 if ($data{$x} == $this->delimiter) {
717 $enclosed = false;
718 $i = $x;
719 }
720 else {
721 if ($this->error < 1) {
722 $this->error = 1;
723 }
724
725 $error_row = count($rows) + 1;
726 $error_col = $col + 1;
727 if (!isset($this->error_info[$error_row.'-'.$error_col])) {
728 $this->error_info[$error_row.'-'.$error_col] = array(
729 'type' => 1,
730 'info' =>
731 'Syntax error found on row '.(count($rows) + 1).'. '.
732 'A single double-quote was found within an enclosed string. '.
733 'Enclosed double-quotes must be escaped with a second double-quote.',
734 'row' => count($rows) + 1,
735 'field' => $col + 1,
736 'field_name' => (!empty($head[$col])) ? $head[$col] : null,
737 );
738 }
739
740 $current .= $ch;
741 $enclosed = false;
742 }
743 }
744 else {
745 $enclosed = false;
746 }
747
748 // end of field/row/csv
749 }
750 elseif ( ($ch == $this->delimiter || $ch == "\n" || $ch == "\r" || $ch === false) && !$enclosed ) {
751 $key = (!empty($head[$col])) ? $head[$col] : $col;
752 $row[$key] = ($was_enclosed) ? $current : trim($current);
753 $current = '';
754 $was_enclosed = false;
755 $col++;
756
757 // end of row
758 if ($ch == "\n" || $ch == "\r" || $ch === false) {
759 if ($this->_validate_offset($row_count) && $this->_validate_row_conditions($row, $this->conditions)) {
760 if ($this->heading && empty($head)) {
761 $head = $row;
762 }
763 elseif (empty($this->fields) || (!empty($this->fields) && (($this->heading && $row_count > 0) || !$this->heading))) {
764 if (!empty($this->sort_by) && !empty($row[$this->sort_by])) {
765 if (isset($rows[$row[$this->sort_by]])) {
766 $rows[$row[$this->sort_by].'_0'] = &$rows[$row[$this->sort_by]];
767 unset($rows[$row[$this->sort_by]]);
768 for ($sn=1; isset($rows[$row[$this->sort_by].'_'.$sn]); $sn++) {}
769 $rows[$row[$this->sort_by].'_'.$sn] = $row;
770 }
771 else $rows[$row[$this->sort_by]] = $row;
772 }
773 else {
774 $rows[] = $row;
775 }
776 }
777 }
778
779 $row = array();
780 $col = 0;
781 $row_count++;
782
783 if ($this->sort_by === null && $this->limit !== null && count($rows) == $this->limit) {
784 $i = $strlen;
785 }
786
787 if ($ch == "\r" && $nch == "\n") {
788 $i++;
789 }
790 }
791
792 // append character to current field
793 }
794 else {
795 $current .= $ch;
796 }
797 }
798
799 $this->titles = $head;
800 if (!empty($this->sort_by)) {
801 $sort_type = SORT_REGULAR;
802 if ($this->sort_type == 'numeric') {
803 $sort_type = SORT_NUMERIC;
804 }
805 elseif ($this->sort_type == 'string') {
806 $sort_type = SORT_STRING;
807 }
808
809 ($this->sort_reverse) ? krsort($rows, $sort_type) : ksort($rows, $sort_type);
810
811 if ($this->offset !== null || $this->limit !== null) {
812 $rows = array_slice($rows, ($this->offset === null ? 0 : $this->offset) , $this->limit, true);
813 }
814 }
815
816 if (!$this->keep_file_data) {
817 $this->file_data = null;
818 }
819
820 return $rows;
821 }
822
823 /**
824 * Create CSV data from array
825 *
826 * @access public
827 * @param data 2D array with data
828 * @param fields field names
829 * @param append if true, field names will not be output
830 * @param is_php if a php die() call should be put on the first
831 * line of the file, this is later ignored when read.
832 * @param delimiter field delimiter to use
833 *
834 * @return CSV data (text string)
835 */
836 public function unparse ($data = array(), $fields = array(), $append = false , $is_php = false, $delimiter = null) {
837 if (!is_array($data) || empty($data)) {
838 $data = &$this->data;
839 }
840
841 if (!is_array($fields) || empty($fields)) {
842 $fields = &$this->titles;
843 }
844
845 if ($delimiter === null) {
846 $delimiter = $this->delimiter;
847 }
848
849 $string = ($is_php) ? "<?php header('Status: 403'); die(' '); ?>".$this->linefeed : '';
850 $entry = array();
851
852 // create heading
853 if ($this->heading && !$append && !empty($fields)) {
854 foreach ($fields as $key => $value) {
855 $entry[] = $this->_enclose_value($value, $delimiter);
856 }
857
858 $string .= implode($delimiter, $entry).$this->linefeed;
859 $entry = array();
860 }
861
862 // create data
863 foreach ($data as $key => $row) {
864 foreach ($row as $field => $value) {
865 $entry[] = $this->_enclose_value($value, $delimiter);
866 }
867
868 $string .= implode($delimiter, $entry).$this->linefeed;
869 $entry = array();
870 }
871
872 if ($this->convert_encoding) {
873 $string = iconv($this->input_encoding, $this->output_encoding, $string);
874 }
875
876 return $string;
877 }
878
879 /**
880 * Load local file or string
881 *
882 * @access public
883 * @param input local CSV file
884 *
885 * @return true or false
886 */
887 public function load_data ($input = null) {
888 $data = null;
889 $file = null;
890
891 if (is_null($input)) {
892 $file = $this->file;
893 }
894 elseif (file_exists($input)) {
895 $file = $input;
896 }
897 else {
898 $data = $input;
899 }
900
901 if (!empty($data) || $data = $this->_rfile($file)) {
902 if ($this->file != $file) {
903 $this->file = $file;
904 }
905
906 if (preg_match('/\.php$/i', $file) && preg_match('/<\?.*?\?>(.*)/ims', $data, $strip)) {
907 $data = ltrim($strip[1]);
908 }
909
910 if ($this->convert_encoding) {
911 $data = iconv($this->input_encoding, $this->output_encoding, $data);
912 }
913
914 if (substr($data, -1) != "\n") {
915 $data .= "\n";
916 }
917
918 $this->file_data = &$data;
919 return true;
920 }
921
922 return false;
923 }
924
925
926 // ==============================================
927 // ----- [ Internal Functions ] -----------------
928 // ==============================================
929
930 /**
931 * Validate a row against specified conditions
932 *
933 * @access protected
934 * @param row array with values from a row
935 * @param conditions specified conditions that the row must match
936 *
937 * @return true of false
938 */
939 protected function _validate_row_conditions ($row = array(), $conditions = null) {
940 if (!empty($row)) {
941 if (!empty($conditions)) {
942 $conditions = (strpos($conditions, ' OR ') !== false) ? explode(' OR ', $conditions) : array($conditions);
943 $or = '';
944 foreach ($conditions as $key => $value) {
945 if (strpos($value, ' AND ') !== false) {
946 $value = explode(' AND ', $value);
947 $and = '';
948
949 foreach ($value as $k => $v) {
950 $and .= $this->_validate_row_condition($row, $v);
951 }
952
953 $or .= (strpos($and, '0') !== false) ? '0' : '1';
954 }
955 else {
956 $or .= $this->_validate_row_condition($row, $value);
957 }
958 }
959
960 return (strpos($or, '1') !== false) ? true : false;
961 }
962
963 return true;
964 }
965
966 return false;
967 }
968
969 /**
970 * Validate a row against a single condition
971 *
972 * @access protected
973 * @param row array with values from a row
974 * @param condition specified condition that the row must match
975 *
976 * @return true of false
977 */
978 protected function _validate_row_condition ($row, $condition) {
979 $operators = array(
980 '=', 'equals', 'is',
981 '!=', 'is not',
982 '<', 'is less than',
983 '>', 'is greater than',
984 '<=', 'is less than or equals',
985 '>=', 'is greater than or equals',
986 'contains',
987 'does not contain',
988 );
989
990 $operators_regex = array();
991
992 foreach ($operators as $value) {
993 $operators_regex[] = preg_quote($value, '/');
994 }
995
996 $operators_regex = implode('|', $operators_regex);
997
998 if (preg_match('/^(.+) ('.$operators_regex.') (.+)$/i', trim($condition), $capture)) {
999 $field = $capture[1];
1000 $op = $capture[2];
1001 $value = $capture[3];
1002
1003 if (preg_match('/^([\'\"]{1})(.*)([\'\"]{1})$/i', $value, $capture)) {
1004 if ($capture[1] == $capture[3]) {
1005 $value = $capture[2];
1006 $value = str_replace("\\n", "\n", $value);
1007 $value = str_replace("\\r", "\r", $value);
1008 $value = str_replace("\\t", "\t", $value);
1009 $value = stripslashes($value);
1010 }
1011 }
1012
1013 if (array_key_exists($field, $row)) {
1014 if (($op == '=' || $op == 'equals' || $op == 'is') && $row[$field] == $value) {
1015 return '1';
1016 }
1017 elseif (($op == '!=' || $op == 'is not') && $row[$field] != $value) {
1018 return '1';
1019 }
1020 elseif (($op == '<' || $op == 'is less than' ) && $row[$field] < $value) {
1021 return '1';
1022 }
1023 elseif (($op == '>' || $op == 'is greater than') && $row[$field] > $value) {
1024 return '1';
1025 }
1026 elseif (($op == '<=' || $op == 'is less than or equals' ) && $row[$field] <= $value) {
1027 return '1';
1028 }
1029 elseif (($op == '>=' || $op == 'is greater than or equals') && $row[$field] >= $value) {
1030 return '1';
1031 }
1032 elseif ($op == 'contains' && preg_match('/'.preg_quote($value, '/').'/i', $row[$field])) {
1033 return '1';
1034 }
1035 elseif ($op == 'does not contain' && !preg_match('/'.preg_quote($value, '/').'/i', $row[$field])) {
1036 return '1';
1037 }
1038 else {
1039 return '0';
1040 }
1041 }
1042 }
1043
1044 return '1';
1045 }
1046
1047 /**
1048 * Validates if the row is within the offset or not if sorting is disabled
1049 *
1050 * @access protected
1051 * @param current_row the current row number being processed
1052 *
1053 * @return true of false
1054 */
1055 protected function _validate_offset ($current_row) {
1056 if ($this->sort_by === null && $this->offset !== null && $current_row < $this->offset) {
1057 return false;
1058 }
1059
1060 return true;
1061 }
1062
1063 /**
1064 * Enclose values if needed
1065 * - only used by unparse()
1066 *
1067 * @access protected
1068 * @param value string to process
1069 *
1070 * @return Processed value
1071 */
1072 protected function _enclose_value ($value = null, $delimiter = null) {
1073 if (is_null($delimiter)) {
1074 $delimiter = $this->delimiter;
1075 }
1076 if ($value !== null && $value != '') {
1077 $delimiter_quoted = preg_quote($delimiter, '/');
1078 $enclosure_quoted = preg_quote($this->enclosure, '/');
1079 if (preg_match("/".$delimiter_quoted."|".$enclosure_quoted."|\n|\r/i", $value) || ($value{0} == ' ' || substr($value, -1) == ' ') || $this->enclose_all) {
1080 $value = str_replace($this->enclosure, $this->enclosure.$this->enclosure, $value);
1081 $value = $this->enclosure.$value.$this->enclosure;
1082 }
1083 }
1084
1085 return $value;
1086 }
1087
1088 /**
1089 * Check file data
1090 *
1091 * @access protected
1092 * @param file local filename
1093 *
1094 * @return true or false
1095 */
1096 protected function _check_data ($file = null) {
1097 if (empty($this->file_data)) {
1098 if (is_null($file)) $file = $this->file;
1099
1100 return $this->load_data($file);
1101 }
1102
1103 return true;
1104 }
1105
1106 /**
1107 * Check if passed info might be delimiter
1108 * Only used by find_delimiter
1109 *
1110 * @access protected
1111 * @param [type] $char [description]
1112 * @param [type] $array [description]
1113 * @param [type] $depth [description]
1114 * @param [type] $preferred [description]
1115 *
1116 * @return special string used for delimiter selection, or false
1117 */
1118 protected function _check_count ($char, $array, $depth, $preferred) {
1119 if ($depth == count($array)) {
1120 $first = null;
1121 $equal = null;
1122 $almost = false;
1123 foreach ($array as $key => $value) {
1124 if ($first == null) {
1125 $first = $value;
1126 }
1127 elseif ($value == $first && $equal !== false) {
1128 $equal = true;
1129 }
1130 elseif ($value == $first+1 && $equal !== false) {
1131 $equal = true;
1132 $almost = true;
1133 }
1134 else {
1135 $equal = false;
1136 }
1137 }
1138
1139 if ($equal) {
1140 $match = ($almost) ? 2 : 1;
1141 $pref = strpos($preferred, $char);
1142 $pref = ($pref !== false) ? str_pad($pref, 3, '0', STR_PAD_LEFT) : '999';
1143
1144 return $pref.$match.'.'.(99999 - str_pad($first, 5, '0', STR_PAD_LEFT));
1145 }
1146 else {
1147 return false;
1148 }
1149 }
1150 }
1151
1152 /**
1153 * Read local file
1154 *
1155 * @access protected
1156 * @param file local filename
1157 *
1158 * @return Data from file, or false on failure
1159 */
1160 protected function _rfile ($file = null) {
1161 if (is_readable($file)) {
1162 if (!($fh = fopen($file, 'r'))) {
1163 return false;
1164 }
1165
1166 $data = fread($fh, filesize($file));
1167 fclose($fh);
1168 return $data;
1169 }
1170
1171 return false;
1172 }
1173
1174 /**
1175 * Write to local file
1176 *
1177 * @access protected
1178 * @param file local filename
1179 * @param string data to write to file
1180 * @param mode fopen() mode
1181 * @param lock flock() mode
1182 *
1183 * @return true or false
1184 */
1185 protected function _wfile ($file, $string = '', $mode = 'wb', $lock = 2) {
1186 if ($fp = fopen($file, $mode)) {
1187 flock($fp, $lock);
1188 $re = fwrite($fp, $string);
1189 $re2 = fclose($fp);
1190 if ($re != false && $re2 != false) {
1191 return true;
1192 }
1193 }
1194
1195 return false;
1196 }
1197 }
1198