diff --git a/parsecsv.lib.php b/parsecsv.lib.php index 4f1ba89..726136b 100644 --- a/parsecsv.lib.php +++ b/parsecsv.lib.php @@ -1,21 +1,17 @@ output (true, 'movies.csv', $array); ---------------- - - */ - /** * Configuration * - set these options with $object->var_name = 'value'; */ - - # use first line/entry as field names - var $heading = true; - - # override field names - var $fields = array(); - - # sort entries by this field - var $sort_by = null; - var $sort_reverse = false; - - # sort behavior passed to ksort/krsort functions - # regular = SORT_REGULAR - # numeric = SORT_NUMERIC - # string = SORT_STRING - var $sort_type = null; - - # delimiter (comma) and enclosure (double quote) - var $delimiter = ','; - var $enclosure = '"'; - - # basic SQL-like conditions for row matching - var $conditions = null; - - # number of rows to ignore from beginning of data - var $offset = null; - - # limits the number of returned rows to specified amount - var $limit = null; - - # number of rows to analyze when attempting to auto-detect delimiter - var $auto_depth = 15; - - # characters to ignore when attempting to auto-detect delimiter - var $auto_non_chars = "a-zA-Z0-9\n\r"; - - # preferred delimiter characters, only used when all filtering method - # returns multiple possible delimiters (happens very rarely) - var $auto_preferred = ",;\t.:|"; - - # character encoding options - var $convert_encoding = false; - var $input_encoding = 'ISO-8859-1'; - var $output_encoding = 'ISO-8859-1'; - - # used by unparse(), save(), and output() functions - var $linefeed = "\r\n"; - - # only used by output() function - var $output_delimiter = ','; - var $output_filename = 'data.csv'; - - # keep raw file data in memory after successful parsing (useful for debugging) - var $keep_file_data = false; - + + /** + * Heading + * Use first line/entry as field names + * + * @access public + * @var bool + */ + public $heading = true; + + /** + * Fields + * Override field names + * + * @access public + * @var array + */ + public $fields = array(); + + /** + * Sort By + * Sort csv by this field + * + * @access public + * @var string + */ + public $sort_by = null; + + /** + * Sort Reverse + * Reverse the sort function + * + * @access public + * @var bool + */ + public $sort_reverse = false; + + /** + * Sort Type + * Sort behavior passed to sort methods + * + * regular = SORT_REGULAR + * numeric = SORT_NUMERIC + * string = SORT_STRING + * + * @access public + * @var string + */ + public $sort_type = null; + + /** + * Delimiter + * Delimiter character + * + * @access public + * @var string + */ + public $delimiter = ','; + + /** + * Enclosure + * Enclosure character + * + * @access public + * @var string + */ + public $enclosure = '"'; + + /** + * Conditions + * Basic SQL-Like conditions for row matching + * + * @access public + * @var string + */ + public $conditions = null; + + /** + * Offset + * Number of rows to ignore from beginning of data + * + * @access public + * @var int + */ + public $offset = null; + + /** + * Limit + * Limits the number of returned rows to the specified amount + * + * @access public + * @var int + */ + public $limit = null; + + /** + * Auto Depth + * Number of rows to analyze when attempting to auto-detect delimiter + * + * @access public + * @var int + */ + public $auto_depth = 15; + + /** + * Auto Non Charts + * Characters that should be ignored when attempting to auto-detect delimiter + * + * @access public + * @var string + */ + public $auto_non_chars = "a-zA-Z0-9\n\r"; + + /** + * Auto Preferred + * preferred delimiter characters, only used when all filtering method + * returns multiple possible delimiters (happens very rarely) + * + * @access public + * @var string + */ + public $auto_preferred = ",;\t.:|"; + + /** + * Convert Encoding + * Should we convert the csv encoding? + * + * @access public + * @var bool + */ + public $convert_encoding = false; + + /** + * Input Encoding + * Set the input encoding + * + * @access public + * @var string + */ + public $input_encoding = 'ISO-8859-1'; + + /** + * Output Encoding + * Set the output encoding + * + * @access public + * @var string + */ + public $output_encoding = 'ISO-8859-1'; + + /** + * Linefeed + * Line feed characters used by unparse, save, and output methods + * + * @access public + * @var string + */ + public $linefeed = "\r"; + + /** + * Output Delimiter + * Sets the output delimiter used by the output method + * + * @access public + * @var string + */ + public $output_delimiter = ','; + + /** + * Output filename + * Sets the output filename + * + * @access public + * @var string + */ + public $output_filename = 'data.csv'; + + /** + * Keep File Data + * keep raw file data in memory after successful parsing (useful for debugging) + * + * @access public + * @var bool + */ + public $keep_file_data = false; + /** * Internal variables */ - - # current file - var $file; - - # loaded file contents - var $file_data; - - # error while parsing input data - # 0 = No errors found. Everything should be fine :) - # 1 = Hopefully correctable syntax error was found. - # 2 = Enclosure character (double quote by default) - # was found in non-enclosed field. This means - # the file is either corrupt, or does not - # standard CSV formatting. Please validate - # the parsed data yourself. - var $error = 0; - - # detailed error info - var $error_info = array(); - - # array of field values in data parsed - var $titles = array(); - - # two dimentional array of CSV data - var $data = array(); - - + + /** + * File + * Current Filename + * + * @access public + * @var string + */ + public $file; + + /** + * File Data + * Current file data + * + * @access public + * @var string + */ + public $file_data; + + /** + * Error + * Contains the error code if one occured + * + * 0 = No errors found. Everything should be fine :) + * 1 = Hopefully correctable syntax error was found. + * 2 = Enclosure character (double quote by default) + * was found in non-enclosed field. This means + * the file is either corrupt, or does not + * standard CSV formatting. Please validate + * the parsed data yourself. + * + * @access public + * @var int + */ + public $error = 0; + + /** + * Error Information + * Detailed error information + * + * @access public + * @var array + */ + public $error_info = array(); + + /** + * Titles + * CSV titles if they exists + * + * @access public + * @var array + */ + public $titles = array(); + + /** + * Data + * Two dimensional array of CSV data + * + * @access public + * @var array + */ + public $data = array(); + + /** * Constructor - * @param input CSV file or string - * @return nothing + * Class constructor + * + * @access public + * @param [string] input The CSV string or a direct filepath + * @param [integer] offset Number of rows to ignore from the beginning of the data + * @param [integer] limit Limits the number of returned rows to specified amount + * @param [string] conditions Basic SQL-like conditions for row matching */ - function parseCSV ($input = null, $offset = null, $limit = null, $conditions = null) { - if ( $offset !== null ) $this->offset = $offset; - if ( $limit !== null ) $this->limit = $limit; - if ( count($conditions) > 0 ) $this->conditions = $conditions; - if ( !empty($input) ) $this->parse($input); + public function __construct ($input = null, $offset = null, $limit = null, $conditions = null) { + if ( $offset !== null ) { + $this->offset = $offset; + } + + if ( $limit !== null ) { + $this->limit = $limit; + } + + if ( count($conditions) > 0 ) { + $this->conditions = $conditions; + } + + if ( !empty($input) ) { + $this->parse($input); + } } - - + + // ============================================== // ----- [ Main Functions ] --------------------- // ============================================== - + + /** - * Parse CSV file or string - * @param input CSV file or string - * @return nothing + * Parse + * Parse a CSV file or string + * + * @access public + * @param [string] input The CSV string or a direct filepath + * @param [integer] offset Number of rows to ignore from the beginning of the data + * @param [integer] limit Limits the number of returned rows to specified amount + * @param [string] conditions Basic SQL-like conditions for row matching + * + * @return [bool] */ - function parse ($input = null, $offset = null, $limit = null, $conditions = null) { - if ( $input === null ) $input = $this->file; + public function parse ($input = null, $offset = null, $limit = null, $conditions = null) { + if ( $input === null ) { + $input = $this->file; + } + if ( !empty($input) ) { - if ( $offset !== null ) $this->offset = $offset; - if ( $limit !== null ) $this->limit = $limit; - if ( count($conditions) > 0 ) $this->conditions = $conditions; + if ( $offset !== null ) { + $this->offset = $offset; + } + + if ($limit !== null ) { + $this->limit = $limit; + } + + if ( count($conditions) > 0 ) { + $this->conditions = $conditions; + } + if ( is_readable($input) ) { $this->data = $this->parse_file($input); - } else { - $this->file_data = &$input; - $this->data = $this->parse_string(); } - if ( $this->data === false ) return false; + else { + $this->file_data = &$input; + $this->data = $this->parse_string(); + } + + if ( $this->data === false ) { + return false; + } } + return true; } - + /** - * Save changes, or new file and/or data - * @param file file to save to - * @param data 2D array with data - * @param append append current data to end of target CSV if exists - * @param fields field names - * @return true or false + * Save + * Save changes, or write a new file and/or data + * + * @access public + * @param [string] $file File location to save to + * @param [array] $data 2D array of data + * @param [bool] $append Append current data to end of target CSV, if file exists + * @param [array] $fields Field names + * + * @return [bool] */ - function save ($file = null, $data = array(), $append = false, $fields = array()) { - if ( empty($file) ) $file = &$this->file; - $mode = ( $append ) ? 'at' : 'wt' ; - $is_php = ( preg_match('/\.php$/i', $file) ) ? true : false ; + public function save ($file = null, $data = array(), $append = false, $fields = array()) { + if ( empty($file) ) { + $file = &$this->file; + } + + $mode = ($append) ? 'at' : 'wt'; + $is_php = (preg_match('/\.php$/i', $file)) ? true : false; + return $this->_wfile($file, $this->unparse($data, $fields, $append, $is_php), $mode); } - + /** - * Generate CSV based string for output - * @param filename if specified, headers and data will be output directly to browser as a downloable file - * @param data 2D array with data - * @param fields field names - * @param delimiter delimiter used to separate data - * @return CSV data using delimiter of choice, or default + * Output + * Generate a CSV based string for output. + * + * @access public + * @param [string] $filename If specified, headers and data will be output directly to browser as a downloable file + * @param [array] $data 2D array with data + * @param [array] $fields Field names + * @param [type] $delimiter delimiter used to separate data + * + * @return [string] */ - function output ($filename = null, $data = array(), $fields = array(), $delimiter = null) { - if ( empty($filename) ) $filename = $this->output_filename; - if ( $delimiter === null ) $delimiter = $this->output_delimiter; + public function output ($filename = null, $data = array(), $fields = array(), $delimiter = null) { + if ( empty($filename) ) { + $filename = $this->output_filename; + } + + if ( $delimiter === null ) { + $delimiter = $this->output_delimiter; + } + $data = $this->unparse($data, $fields, null, null, $delimiter); + if ( $filename !== null ) { header('Content-type: application/csv'); header('Content-Disposition: attachment; filename="'.$filename.'"'); echo $data; } + return $data; } - + /** + * Encoding * Convert character encoding - * @param input input character encoding, uses default if left blank - * @param output output character encoding, uses default if left blank - * @return nothing + * + * @access public + * @param [string] $input Input character encoding, uses default if left blank + * @param [string] $output Output character encoding, uses default if left blank */ - function encoding ($input = null, $output = null) { + public function encoding ($input = null, $output = null) { $this->convert_encoding = true; - if ( $input !== null ) $this->input_encoding = $input; - if ( $output !== null ) $this->output_encoding = $output; + if ( $input !== null ) { + $this->input_encoding = $input; + } + + if ( $output !== null ) { + $this->output_encoding = $output; + } } - + /** + * Auto * Auto-Detect Delimiter: Find delimiter by analyzing a specific number of * rows to determine most probable delimiter character - * @param file local CSV file - * @param parse true/false parse file directly - * @param search_depth number of rows to analyze - * @param preferred preferred delimiter characters - * @param enclosure enclosure character, default is double quote ("). - * @return delimiter character + * + * @access public + * @param [string] $file Local CSV file + * @param [bool] $parse True/false parse file directly + * @param [int] $search_depth Number of rows to analyze + * @param [string] $preferred Preferred delimiter characters + * @param [string] $enclosure Enclosure character, default is double quote ("). + * + * @return [string] */ - function auto ($file = null, $parse = true, $search_depth = null, $preferred = null, $enclosure = null) { - - if ( $file === null ) $file = $this->file; - if ( empty($search_depth) ) $search_depth = $this->auto_depth; - if ( $enclosure === null ) $enclosure = $this->enclosure; - - if ( $preferred === null ) $preferred = $this->auto_preferred; - + public function auto ($file = null, $parse = true, $search_depth = null, $preferred = null, $enclosure = null) { + if ( $file === null ) { + $file = $this->file; + } + + if ( empty($search_depth) ) { + $search_depth = $this->auto_depth; + } + + if ( $enclosure === null ) { + $enclosure = $this->enclosure; + } + + if ( $preferred === null ) { + $preferred = $this->auto_preferred; + } + if ( empty($this->file_data) ) { if ( $this->_check_data($file) ) { $data = &$this->file_data; - } else return false; - } else { + } + else { + return false; + } + } + else { $data = &$this->file_data; } - - $chars = array(); - $strlen = strlen($data); + + $chars = array(); + $strlen = strlen($data); $enclosed = false; - $n = 1; - $to_end = true; - + $n = 1; + $to_end = true; + // walk specific depth finding posssible delimiter characters for ( $i=0; $i < $strlen; $i++ ) { - $ch = $data{$i}; + $ch = $data{$i}; $nch = ( isset($data{$i+1}) ) ? $data{$i+1} : false ; $pch = ( isset($data{$i-1}) ) ? $data{$i-1} : false ; - + // open and closing quotes if ( $ch == $enclosure ) { if ( !$enclosed || $nch != $enclosure ) { $enclosed = ( $enclosed ) ? false : true ; - } elseif ( $enclosed ) { + } + elseif ( $enclosed ) { $i++; } - + // end of row - } elseif ( ($ch == "\n" && $pch != "\r" || $ch == "\r") && !$enclosed ) { + } + elseif ( ($ch == "\n" && $pch != "\r" || $ch == "\r") && !$enclosed ) { if ( $n >= $search_depth ) { $strlen = 0; $to_end = false; - } else { + } + else { $n++; } - + // count character - } elseif (!$enclosed) { + } + elseif (!$enclosed) { if ( !preg_match('/['.preg_quote($this->auto_non_chars, '/').']/i', $ch) ) { if ( !isset($chars[$ch][$n]) ) { $chars[$ch][$n] = 1; - } else { + } + else { $chars[$ch][$n]++; } } } } - + // filtering - $depth = ( $to_end ) ? $n-1 : $n ; + $depth = ( $to_end ) ? $n-1 : $n ; $filtered = array(); foreach( $chars as $char => $value ) { if ( $match = $this->_check_count($char, $value, $depth, $preferred) ) { $filtered[$match] = $char; } } - + // capture most probable delimiter ksort($filtered); $this->delimiter = reset($filtered); - + // parse data - if ( $parse ) $this->data = $this->parse_string(); - + if ( $parse ) { + $this->data = $this->parse_string(); + } + return $this->delimiter; - } - - + + // ============================================== // ----- [ Core Functions ] --------------------- // ============================================== - + /** + * Parse File * Read file to string and call parse_string() - * @param file local CSV file - * @return 2D array with CSV data, or false on failure + * + * @access public + * + * @param [string] $file Local CSV file + * + * @return [array|bool] */ - function parse_file ($file = null) { - if ( $file === null ) $file = $this->file; - if ( empty($this->file_data) ) $this->load_data($file); + public function parse_file ($file = null) { + if ( $file === null ) { + $file = $this->file; + } + + if ( empty($this->file_data) ) { + $this->load_data($file); + } + return ( !empty($this->file_data) ) ? $this->parse_string() : false ; } - + /** * Parse CSV strings to arrays + * + * @access public * @param data CSV string + * * @return 2D array with CSV data, or false on failure */ - function parse_string ($data = null) { + public function parse_string ($data = null) { if ( empty($data) ) { if ( $this->_check_data() ) { $data = &$this->file_data; - } else return false; + } + else { + return false; + } } - + $white_spaces = str_replace($this->delimiter, '', " \t\x0B\0"); - - $rows = array(); - $row = array(); - $row_count = 0; - $current = ''; - $head = ( !empty($this->fields) ) ? $this->fields : array() ; - $col = 0; - $enclosed = false; + + $rows = array(); + $row = array(); + $row_count = 0; + $current = ''; + $head = ( !empty($this->fields) ) ? $this->fields : array() ; + $col = 0; + $enclosed = false; $was_enclosed = false; - $strlen = strlen($data); - + $strlen = strlen($data); + // walk through each character for ( $i=0; $i < $strlen; $i++ ) { - $ch = $data{$i}; + $ch = $data{$i}; $nch = ( isset($data{$i+1}) ) ? $data{$i+1} : false ; $pch = ( isset($data{$i-1}) ) ? $data{$i-1} : false ; - + // open/close quotes, and inline quotes if ( $ch == $this->enclosure ) { if ( !$enclosed ) { - if ( ltrim($current, $white_spaces) == '' ) { + if ( ltrim($current,$white_spaces) == '' ) { $enclosed = true; $was_enclosed = true; - } else { + } + else { $this->error = 2; - $error_row = count($rows) + 1; - $error_col = $col + 1; + $error_row = count($rows) + 1; + $error_col = $col + 1; if ( !isset($this->error_info[$error_row.'-'.$error_col]) ) { $this->error_info[$error_row.'-'.$error_col] = array( - 'type' => 2, - 'info' => 'Syntax error found on row '.$error_row.'. Non-enclosed fields can not contain double-quotes.', - 'row' => $error_row, - 'field' => $error_col, + 'type' => 2, + 'info' => 'Syntax error found on row '.$error_row.'. Non-enclosed fields can not contain double-quotes.', + 'row' => $error_row, + 'field' => $error_col, 'field_name' => (!empty($head[$col])) ? $head[$col] : null, ); } + $current .= $ch; } - } elseif ($nch == $this->enclosure) { + } + elseif ($nch == $this->enclosure) { $current .= $ch; $i++; - } elseif ( $nch != $this->delimiter && $nch != "\r" && $nch != "\n" ) { + } + elseif ( $nch != $this->delimiter && $nch != "\r" && $nch != "\n" ) { for ( $x=($i+1); isset($data{$x}) && ltrim($data{$x}, $white_spaces) == ''; $x++ ) {} if ( $data{$x} == $this->delimiter ) { $enclosed = false; - $i = $x; - } else { + $i = $x; + } + else { if ( $this->error < 1 ) { $this->error = 1; } + $error_row = count($rows) + 1; $error_col = $col + 1; if ( !isset($this->error_info[$error_row.'-'.$error_col]) ) { @@ -429,154 +702,208 @@ class parseCSV { 'Syntax error found on row '.(count($rows) + 1).'. '. 'A single double-quote was found within an enclosed string. '. 'Enclosed double-quotes must be escaped with a second double-quote.', - 'row' => count($rows) + 1, - 'field' => $col + 1, + 'row' => count($rows) + 1, + 'field' => $col + 1, 'field_name' => (!empty($head[$col])) ? $head[$col] : null, ); } + $current .= $ch; $enclosed = false; } - } else { + } + else { $enclosed = false; } - + // end of field/row - } elseif ( ($ch == $this->delimiter || $ch == "\n" || $ch == "\r") && !$enclosed ) { - $key = ( !empty($head[$col]) ) ? $head[$col] : $col ; - $row[$key] = ( $was_enclosed ) ? $current : trim($current) ; - $current = ''; - $was_enclosed = false; + } + elseif ( ($ch == $this->delimiter || $ch == "\n" || $ch == "\r") && !$enclosed ) { + $key = ( !empty($head[$col]) ) ? $head[$col] : $col ; + $row[$key] = ( $was_enclosed ) ? $current : trim($current) ; + $current = ''; + $was_enclosed = false; $col++; - + // end of row if ( $ch == "\n" || $ch == "\r" ) { if ( $this->_validate_offset($row_count) && $this->_validate_row_conditions($row, $this->conditions) ) { if ( $this->heading && empty($head) ) { $head = $row; - } elseif ( empty($this->fields) || (!empty($this->fields) && (($this->heading && $row_count > 0) || !$this->heading)) ) { + } + elseif ( empty($this->fields) || (!empty($this->fields) && (($this->heading && $row_count > 0) || !$this->heading)) ) { if ( !empty($this->sort_by) && !empty($row[$this->sort_by]) ) { if ( isset($rows[$row[$this->sort_by]]) ) { $rows[$row[$this->sort_by].'_0'] = &$rows[$row[$this->sort_by]]; unset($rows[$row[$this->sort_by]]); for ( $sn=1; isset($rows[$row[$this->sort_by].'_'.$sn]); $sn++ ) {} $rows[$row[$this->sort_by].'_'.$sn] = $row; - } else $rows[$row[$this->sort_by]] = $row; - } else $rows[] = $row; + } + else $rows[$row[$this->sort_by]] = $row; + } + else { + $rows[] = $row; + } } } + $row = array(); $col = 0; $row_count++; + if ( $this->sort_by === null && $this->limit !== null && count($rows) == $this->limit ) { $i = $strlen; } - if ( $ch == "\r" && $nch == "\n" ) $i++; + + if ( $ch == "\r" && $nch == "\n" ) { + $i++; + } } - + // append character to current field - } else { + } + else { $current .= $ch; } } + $this->titles = $head; if ( !empty($this->sort_by) ) { $sort_type = SORT_REGULAR; if ( $this->sort_type == 'numeric' ) { $sort_type = SORT_NUMERIC; - } elseif ( $this->sort_type == 'string' ) { + } + elseif ( $this->sort_type == 'string' ) { $sort_type = SORT_STRING; } + ( $this->sort_reverse ) ? krsort($rows, $sort_type) : ksort($rows, $sort_type) ; + if ( $this->offset !== null || $this->limit !== null ) { $rows = array_slice($rows, ($this->offset === null ? 0 : $this->offset) , $this->limit, true); } } + if ( !$this->keep_file_data ) { $this->file_data = null; } + return $rows; } - + /** * Create CSV data from array + * + * @access public * @param data 2D array with data * @param fields field names * @param append if true, field names will not be output * @param is_php if a php die() call should be put on the first * line of the file, this is later ignored when read. * @param delimiter field delimiter to use + * * @return CSV data (text string) */ - function unparse ( $data = array(), $fields = array(), $append = false , $is_php = false, $delimiter = null) { - if ( !is_array($data) || empty($data) ) $data = &$this->data; - if ( !is_array($fields) || empty($fields) ) $fields = &$this->titles; - if ( $delimiter === null ) $delimiter = $this->delimiter; - + public function unparse ( $data = array(), $fields = array(), $append = false , $is_php = false, $delimiter = null) { + if ( !is_array($data) || empty($data) ) { + $data = &$this->data; + } + + if ( !is_array($fields) || empty($fields) ) { + $fields = &$this->titles; + } + + if ( $delimiter === null ) { + $delimiter = $this->delimiter; + } + $string = ( $is_php ) ? "".$this->linefeed : '' ; - $entry = array(); - + $entry = array(); + // create heading if ( $this->heading && !$append && !empty($fields) ) { foreach( $fields as $key => $value ) { $entry[] = $this->_enclose_value($value); } + $string .= implode($delimiter, $entry).$this->linefeed; - $entry = array(); + $entry = array(); } - + // create data foreach( $data as $key => $row ) { foreach( $row as $field => $value ) { $entry[] = $this->_enclose_value($value); } + $string .= implode($delimiter, $entry).$this->linefeed; - $entry = array(); + $entry = array(); } - + return $string; } - + /** * Load local file or string + * + * @access public * @param input local CSV file + * * @return true or false */ - function load_data ($input = null) { + public function load_data ($input = null) { $data = null; $file = null; + if ( $input === null ) { $file = $this->file; - } elseif ( file_exists($input) ) { + } + elseif ( file_exists($input) ) { $file = $input; - } else { + } + else { $data = $input; } + if ( !empty($data) || $data = $this->_rfile($file) ) { - if ( $this->file != $file ) $this->file = $file; + if ( $this->file != $file ) { + $this->file = $file; + } + if ( preg_match('/\.php$/i', $file) && preg_match('/<\?.*?\?>(.*)/ims', $data, $strip) ) { $data = ltrim($strip[1]); } - if ( $this->convert_encoding ) $data = iconv($this->input_encoding, $this->output_encoding, $data); - if ( substr($data, -1) != "\n" ) $data .= "\n"; + + if ( $this->convert_encoding ) { + $data = iconv($this->input_encoding, $this->output_encoding, $data); + } + + if ( substr($data, -1) != "\n" ) { + $data .= "\n"; + } + $this->file_data = &$data; return true; } + return false; } - - + + // ============================================== // ----- [ Internal Functions ] ----------------- // ============================================== - + /** * Validate a row against specified conditions + * + * @access public * @param row array with values from a row - * @param conditions specified conditions that the row must match + * @param conditions specified conditions that the row must match + * * @return true of false */ - function _validate_row_conditions ($row = array(), $conditions = null) { + public function _validate_row_conditions ($row = array(), $conditions = null) { if ( !empty($row) ) { if ( !empty($conditions) ) { $conditions = (strpos($conditions, ' OR ') !== false) ? explode(' OR ', $conditions) : array($conditions) ; @@ -584,29 +911,38 @@ class parseCSV { foreach( $conditions as $key => $value ) { if ( strpos($value, ' AND ') !== false ) { $value = explode(' AND ', $value); - $and = ''; + $and = ''; + foreach( $value as $k => $v ) { $and .= $this->_validate_row_condition($row, $v); } + $or .= (strpos($and, '0') !== false) ? '0' : '1' ; - } else { + } + else { $or .= $this->_validate_row_condition($row, $value); } } + return (strpos($or, '1') !== false) ? true : false ; } + return true; } + return false; } - + /** * Validate a row against a single condition + * + * @access public * @param row array with values from a row - * @param condition specified condition that the row must match + * @param condition specified condition that the row must match + * * @return true of false */ - function _validate_row_condition ($row, $condition) { + public function _validate_row_condition ($row, $condition) { $operators = array( '=', 'equals', 'is', '!=', 'is not', @@ -617,15 +953,20 @@ class parseCSV { 'contains', 'does not contain', ); + $operators_regex = array(); + foreach( $operators as $value ) { $operators_regex[] = preg_quote($value, '/'); } + $operators_regex = implode('|', $operators_regex); + if ( preg_match('/^(.+) ('.$operators_regex.') (.+)$/i', trim($condition), $capture) ) { $field = $capture[1]; - $op = $capture[2]; + $op = $capture[2]; $value = $capture[3]; + if ( preg_match('/^([\'\"]{1})(.*)([\'\"]{1})$/i', $value, $capture) ) { if ( $capture[1] == $capture[3] ) { $value = $capture[2]; @@ -635,48 +976,67 @@ class parseCSV { $value = stripslashes($value); } } + if ( array_key_exists($field, $row) ) { if ( ($op == '=' || $op == 'equals' || $op == 'is') && $row[$field] == $value ) { return '1'; - } elseif ( ($op == '!=' || $op == 'is not') && $row[$field] != $value ) { + } + elseif ( ($op == '!=' || $op == 'is not') && $row[$field] != $value ) { return '1'; - } elseif ( ($op == '<' || $op == 'is less than' ) && $row[$field] < $value ) { + } + elseif ( ($op == '<' || $op == 'is less than' ) && $row[$field] < $value ) { return '1'; - } elseif ( ($op == '>' || $op == 'is greater than') && $row[$field] > $value ) { + } + elseif ( ($op == '>' || $op == 'is greater than') && $row[$field] > $value ) { return '1'; - } elseif ( ($op == '<=' || $op == 'is less than or equals' ) && $row[$field] <= $value ) { + } + elseif ( ($op == '<=' || $op == 'is less than or equals' ) && $row[$field] <= $value ) { return '1'; - } elseif ( ($op == '>=' || $op == 'is greater than or equals') && $row[$field] >= $value ) { + } + elseif ( ($op == '>=' || $op == 'is greater than or equals') && $row[$field] >= $value ) { return '1'; - } elseif ( $op == 'contains' && preg_match('/'.preg_quote($value, '/').'/i', $row[$field]) ) { + } + elseif ( $op == 'contains' && preg_match('/'.preg_quote($value, '/').'/i', $row[$field]) ) { return '1'; - } elseif ( $op == 'does not contain' && !preg_match('/'.preg_quote($value, '/').'/i', $row[$field]) ) { + } + elseif ( $op == 'does not contain' && !preg_match('/'.preg_quote($value, '/').'/i', $row[$field]) ) { return '1'; - } else { + } + else { return '0'; } } } + return '1'; } - + /** * Validates if the row is within the offset or not if sorting is disabled + * + * @access public * @param current_row the current row number being processed + * * @return true of false */ - function _validate_offset ($current_row) { - if ( $this->sort_by === null && $this->offset !== null && $current_row < $this->offset ) return false; + public function _validate_offset ($current_row) { + if ( $this->sort_by === null && $this->offset !== null && $current_row < $this->offset ) { + return false; + } + return true; } - + /** * Enclose values if needed * - only used by unparse() - * @param value string to process - * @return Processed value + * + * @access public + * @param value string to process + * + * @return Processed value */ - function _enclose_value ($value = null) { + public function _enclose_value ($value = null) { if ( $value !== null && $value != '' ) { $delimiter = preg_quote($this->delimiter, '/'); $enclosure = preg_quote($this->enclosure, '/'); @@ -685,87 +1045,117 @@ class parseCSV { $value = $this->enclosure.$value.$this->enclosure; } } + return $value; } - + /** * Check file data + * + * @access public * @param file local filename + * * @return true or false */ - function _check_data ($file = null) { + public function _check_data ($file = null) { if ( empty($this->file_data) ) { if ( $file === null ) $file = $this->file; + return $this->load_data($file); } + return true; } - - + /** * Check if passed info might be delimiter - * - only used by find_delimiter() - * @return special string used for delimiter selection, or false + * Only used by find_delimiter + * + * @access public + * @param [type] $char [description] + * @param [type] $array [description] + * @param [type] $depth [description] + * @param [type] $preferred [description] + * + * @return special string used for delimiter selection, or false */ - function _check_count ($char, $array, $depth, $preferred) { + public function _check_count ($char, $array, $depth, $preferred) { if ( $depth == count($array) ) { - $first = null; - $equal = null; + $first = null; + $equal = null; $almost = false; foreach( $array as $key => $value ) { if ( $first == null ) { $first = $value; - } elseif ( $value == $first && $equal !== false) { + } + elseif ( $value == $first && $equal !== false) { $equal = true; - } elseif ( $value == $first+1 && $equal !== false ) { + } + elseif ( $value == $first+1 && $equal !== false ) { $equal = true; $almost = true; - } else { + } + else { $equal = false; } } + if ( $equal ) { $match = ( $almost ) ? 2 : 1 ; - $pref = strpos($preferred, $char); - $pref = ( $pref !== false ) ? str_pad($pref, 3, '0', STR_PAD_LEFT) : '999' ; + $pref = strpos($preferred, $char); + $pref = ( $pref !== false ) ? str_pad($pref, 3, '0', STR_PAD_LEFT) : '999' ; + return $pref.$match.'.'.(99999 - str_pad($first, 5, '0', STR_PAD_LEFT)); - } else return false; + } + else { + return false; + } } } - + /** * Read local file + * + * @access public * @param file local filename + * * @return Data from file, or false on failure */ - function _rfile ($file = null) { + public function _rfile ($file = null) { if ( is_readable($file) ) { - if ( !($fh = fopen($file, 'r')) ) return false; + if ( !($fh = fopen($file, 'r')) ) { + return false; + } + $data = fread($fh, filesize($file)); fclose($fh); return $data; } + return false; } /** * Write to local file + * + * @access public * @param file local filename * @param string data to write to file * @param mode fopen() mode * @param lock flock() mode + * * @return true or false */ - function _wfile ($file, $string = '', $mode = 'wb', $lock = 2) { + public function _wfile ($file, $string = '', $mode = 'wb', $lock = 2) { if ( $fp = fopen($file, $mode) ) { flock($fp, $lock); - $re = fwrite($fp, $string); + $re = fwrite($fp, $string); $re2 = fclose($fp); - if ( $re != false && $re2 != false ) return true; + if ( $re != false && $re2 != false ) { + return true; + } } + return false; } - } - -?> \ No newline at end of file