$value) { $fields[$key] = trim($fields[$key]); $fields[$key] = str_replace("\"", "\"\"", $fields[$key]); } return $fields; } ## format parameters for the (:csvtemplate:) directive function CSVTemplate($m) { extract($GLOBALS['MarkupToHTML']); $opt = ParseArgs($m[1]); if (isset($_GET['sort']) && isset($_GET['csvname']) && $_GET['csvname'] == $opt['name']) $opt['sort'] = trim($_GET['sort']); # Parse any standalone argument (without '=') foreach ((array)@$opt[''] as $a) if (!isset($opt[$a])) $opt[$a] = TRUE; return CSVTemplateGenerateContent($pagename, $opt); } ## format parameters for the (:csvtemplate:) directive from an URL Query function CSVTemplateQueryURL($pagename) { $opt = array(); // Extract only the parameters that we know are valid one $opt['source'] = trim($_GET['source']); $opt['template'] = str_replace(",", "#", trim($_GET['template'])); $opt['header'] = trim($_GET['header']); $opt['sep'] = trim($_GET['sep']); $opt['filter'] = trim($_GET['filter']); $opt['sort'] = trim($_GET['sort']); $opt['limit'] = trim($_GET['limit']); $result = CSVTemplateGenerateContent($pagename, $opt); PrintFmt($pagename,$result); return $result; } function CSVTemplateGenerateContent($pagename, $opt) { global $FmtPV, $parsedCSV; $templatesArray = array(); # Check if source file exist $sourcefile = MakePageName($pagename, $opt['source']); if (!PageExists($sourcefile)) return "%red%csvtemplate: Invalid source File!"; # Create a list of templates $templateslist = explode(',', $opt['template']); # Create a new list of templates with template filename as key on a list of (repeat, count, string) while (count($templateslist) > 0) { # Get the first template file $tempfilename = array_shift($templateslist); list($tempfilename, $tempanchor) = explode('#', $tempfilename, 2); # Check if template file exist $templatefile = MakePageName($pagename, $tempfilename); if (!PageExists($templatefile)) return "%red%csvtemplate: Invalid template File: ".$tempfilename."!"; $templatestring = ReadPage($templatefile, READPAGE_CURRENT); $templatestring = $templatestring['text']; if ($tempanchor) $templatestring = TextSection($templatestring, "#".$tempanchor); # Fill the other values depending if the repetition is specified or not (default to 1) $templrepeat = $templateslist[0]; if (is_numeric($templrepeat) && $templrepeat > 0) $templatesArray[$templatefile."#".$tempanchor] = array(array_shift($templateslist), 1, $templatestring); else $templatesArray[$templatefile."#".$tempanchor] = array(1, 1, $templatestring); } # Read the source file $sourceCSV = ReadPage($sourcefile, READPAGE_CURRENT); $sourceCSV = trim($sourceCSV['text'])."\n"; # Parse the CSV content if it was not already if (!$parsedCSV[$sourcefile]) $parsedCSV[$sourcefile] = CSVTemplateParseCVSContent($sourceCSV, $opt['header'], $opt['sep']); $filteredCSV = array(); $filteredCSV = $parsedCSV[$sourcefile]; # If a filter is present, filter the entries if ($opt['filter']) { $filteredresult = array(); $filteredresult = $parsedCSV[$sourcefile]; $filteredCSV = array(); $filterstring = $opt['filter']; # Replace {name}s with $parsedCSV[x]['name'] in the filter $filterstring = "return (".preg_replace_callback('/\{([^[$]*?)\}/', function($m) { return "\$entry['".$m[1]."']"; }, $filterstring).");"; foreach ($filteredresult as $entry) if (eval($filterstring)) $filteredCSV[] = $entry; } $limit = count($filteredCSV); # Determine the number of rows we will return if ($opt['limit'] && is_numeric($opt['limit']) && $opt['limit'] > 0) $limit = min($opt['limit'], $limit); if ($limit == 0) return ""; # If a sort argument is present, sort the entries if ($opt['sort'] and count($filteredCSV) > 1) { if ($opt['sort'] == 'random') { $lastIndex = count($filteredCSV) - 1; $draws = $limit; $newArr = array(); while($draws > 0) { $rndIndex = rand(0, $lastIndex); $test = array_splice($filteredCSV, $rndIndex, 1); $newArr[] = $test[0]; $draws--; $lastIndex--; } $filteredCSV = $newArr; } else { $sortarray = explode(',', $opt['sort']); $sortarray = array_map('trim',$sortarray); $sortcommand = "CSVTemplateArrayColumnSort("; foreach ($sortarray as $sortoption) { $type = "SORT_LOCALE_STRING"; $order = "SORT_ASC"; if ($sortoption[0] == "#") { $type = "SORT_NUMERIC"; $sortoption = substr ($sortoption, 1); } if ($sortoption[0] == "-") { $order = "SORT_DESC"; $sortoption = substr ($sortoption, 1); } $sortcommand = $sortcommand."'".$sortoption."', ".$order.", ".$type.","; } $sortcommand = "return ".$sortcommand." \$filteredCSV);"; $filteredCSV = eval($sortcommand); } } # Create a string using one template per row $count = 1; foreach ($filteredCSV as $rowvalues) { # Grab the string that will be used as index $templ = key($templatesArray); # Append the filled template to the the resulting string $templ1 = str_replace("{CSVTemplateCount}", $count, $templatesArray[$templ][2]); $result .= CSVTemplateGetFilledTemplate($templ1, array_merge($rowvalues, $opt)); # Increment the template counter $templatesArray[$templ][1]++; # If the counter exceed the repetition factor, reset the counter and switch to the next template if ($templatesArray[$templ][1] > $templatesArray[$templ][0]) { $templatesArray[$templ][1] = 1; if (!next($templatesArray)) reset($templatesArray); } $count++; if ($count > $limit) break; } return $result; } // Fill the template with the field values function CSVTemplateGetFilledTemplate($template, $fields) { $string = $template; $string = preg_replace("/\n\n\n*/", "\n<:vspace>\n", $string); $string = preg_replace_callback('/\{([^[\(\){}$]*?)\}/', function($m) use($fields) { if (array_key_exists($m[1], $fields)) { // ensure no more then 2 consecutive new line chars $ret = preg_replace("/(\n|\r\n|\r){2,}/", "$1$1", $fields[$m[1]]); $ret = stripmagic(str_replace(array(":)", "(:", "\n", "\r", "\r\n"), array(":)", "(:", "
", "
", "
"), $ret)); return $ret; } return ""; }, $string); # replace {name} fields return $string; } function CSVTemplateParseCVSContent($content, $header=TRUE, $separator=";") { $headerA = array(); $result = array(); $flineval = array(); if ($separator == "") $separator=";"; if (strlen($content) == 0) return $result; $csv = new CSVTemplateParseCSV(); if ($header) { $tok = strtok($content,"\r\n"); $headerA = explode($separator, $tok); } $csv->heading = $header; $csv->delimiter = $separator; $csv->parse($content); $result = $csv->data; return $result; } # CSVTemplateArrayColumnSort function CSVTemplateArrayColumnSort() { $n = func_num_args(); // Get the array to sort $ar = func_get_arg($n-1); if(!is_array($ar)) return false; // for($i = 0; $i < $n-1; $i++) $col[$i] = func_get_arg($i); foreach($ar as $key => $val) foreach($col as $kkey => $vval) if(is_string($vval)) ${"subar$kkey"}[$key] = $val[$vval]; $arv = array(); foreach($col as $key => $val) $arv[] = (is_string($val) ? ${"subar$key"} : $val); $arv[] = &$ar; call_user_func_array("array_multisort", $arv); return $ar; } ##############################3 class CSVTemplateParseCSV { /* Class: CSVTemplateParseCSV v0.2.0 beta https://github.com/parsecsv/parsecsv-for-php Created by Jim Myhrberg (jim@zhuoqe.org). Fully conforms to the specifications lined out on wikipedia: - http://en.wikipedia.org/wiki/Comma-separated_values Based on the concept of this class: - http://minghong.blogspot.com/2006/07/csv-parser-for-php.html Code Examples ---------------- # general usage $csv = new CSVTemplateParseCSV('data.csv'); print_r($csv->data); ---------------- # tab delimited, and encoding conversion $csv = new CSVTemplateParseCSV(); $csv->encoding('UTF-16', 'UTF-8'); $csv->delimiter = "\t"; $csv->parse('data.tsv'); print_r($csv->data); ---------------- # auto-detect delimiter character $csv = new CSVTemplateParseCSV(); $csv->auto('data.csv'); print_r($csv->data); ---------------- # modify data in a csv file $csv = new CSVTemplateParseCSV(); $csv->sort_by = 'id'; $csv->parse('data.csv'); # "4" is the value of the "id" column of the CSV row $csv->data[4] = array('firstname' => 'John', 'lastname' => 'Doe', 'email' => 'john@doe.com'); $csv->save(); ---------------- # add row/entry to end of CSV file # - only recommended when you know the extact sctructure of the file $csv = new CSVTemplateParseCSV(); $csv->save('data.csv', array('1986', 'Home', 'Nowhere', ''), true); ---------------- # convert 2D array to csv data and send headers # to browser to treat output as a file and download it $csv = new CSVTemplateParseCSV(); $csv->output (true, 'movies.csv', $array); ---------------- ---------- This program is free software; you can redistributeit and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. http://www.gnu.org/licenses/gpl.txt This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---------- */ /** * Configuration */ # 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; # delimiter (comma) and enclosure (double quote) var $delimiter = ','; var $enclosure = '"'; # 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"; # prefered delimiter characters, only used when all filtering method # returns multiple possible delimiters (happens very rarely) var $auto_prefered = ",;\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 = "\n"; # only used by output() function var $output_delimiter = ','; var $output_filename = 'data.csv'; /** * Internal variables */ # current file var $file; # loaded file contents var $file_data; # array of field values in data parsed var $titles = array(); # two dimentional array of CSV data var $data = array(); /** * Constructor * @param input CSV file or string * @return nothing */ function __construct($input = null) { if ( !empty($input) ) $this->parse($input); } // ============================================== // ----- [ Main Functions ] --------------------- // ============================================== /** * Parse CSV file or string * @param input CSV file or string * @return nothing */ function parse ($input = null) { if ( !empty($input) ) { $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 */ 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 output if true, prints headers and strings to browser * @param filename filename sent to browser in headers if output is true * @param data 2D array with data * @param fields field names * @param delimiter delimiter used to seperate data * @return CSV data using delimiter of choice, or default */ function output ($output = true, $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 ( $output ) { header('Content-type: application/csv'); header('Content-Disposition: inline; filename="'.$filename.'"'); echo $data; } return $data; } /** * 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 */ function encoding ($input = null, $output = null) { $this->convert_encoding = true; if ( $input !== null ) $this->input_encoding = $input; if ( $output !== null ) $this->output_encoding = $output; } /** * Auto-Detect Delimiter: Find delimiter by analyzing a specific number of * rows to determin 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 prefered prefered delimiter characters * @param enclosure enclosure character, default is double quote ("). * @return delimiter character */ function auto ($file = null, $parse = true, $search_depth = null, $prefered = 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 ( $prefered === null ) $prefered = $this->auto_prefered; if ( empty($data) ) { if ( $this->check_data() ) { $data = &$this->file_data; } else return false; } $chars = array(); $strlen = strlen($data); $enclosed = false; $n = 1; $to_end = true; // walk specific depth finding posssible delimiter characters for ( $i=0; $i < $strlen; $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 && (!$enclosed || $nch != $enclosure) ) { $enclosed = ( $enclosed ) ? false : true ; // inline quotes } elseif ( $ch == $enclosure && $enclosed ) { $i++; // end of row } elseif ( ($ch == "\n" && $pch != "\r" || $ch == "\r") && !$enclosed ) { if ( $n >= $search_depth ) { $strlen = 0; $to_end = false; } else { $n++; } // count character } elseif (!$enclosed) { if ( !preg_match('/['.preg_quote($this->auto_non_chars, '/').']/i', $ch) ) { if ( !isset($chars[$ch][$n]) ) { $chars[$ch][$n] = 1; } else { $chars[$ch][$n]++; } } } } // filtering $depth = ( $to_end ) ? $n-1 : $n ; $filtered = array(); foreach( $chars as $char => $value ) { if ( $match = $this->check_count($char, $value, $depth, $prefered) ) { $filtered[$match] = $char; } } // capture most probable delimiter ksort($filtered); $delimiter = reset($filtered); $this->delimiter = $delimiter; // parse data if ( $parse ) $this->data = $this->parse_string(); return $delimiter; } // ============================================== // ----- [ Core Functions ] --------------------- // ============================================== /** * Load local file or string * @param input local CSV file * @return true or false */ function load_data ($input = null) { $data = null; $file = null; if ( $input === null ) { $file = $this->file; } elseif ( file_exists($input) ) { $file = $input; } else { $data = $input; } if ( !empty($data) || $data = $this->rfile($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 ( $data[strlen($data)-1] != "\n" ) $data .= "\n"; $this->file_data = &$data; return true; } return false; } /** * Read file to string and call parse_string() * @param file local CSV file * @return 2D array with CSV data, or false on failure */ 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 * @param data CSV string * @return 2D array with CSV data, or false on failure */ function parse_string ($data = null) { if ( empty($data) ) { if ( $this->check_data() ) { $data = &$this->file_data; } else return false; } $rows = array(); $row = array(); $row_count = 0; $current = ''; $head = ( !empty($this->fields) ) ? $this->fields : array() ; $col = 0; $enclosed = false; $strlen = strlen($data); // walk through each character for ( $i=0; $i < $strlen; $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 == $this->enclosure && (!$enclosed || $nch != $this->enclosure) ) { $enclosed = ( $enclosed ) ? false : true ; // inline quotes } elseif ( $ch == $this->enclosure && $enclosed ) { $current .= $ch; $i++; // end of field/row } elseif ( ($ch == $this->delimiter || ($ch == "\n" && $pch != "\r") || $ch == "\r") && !$enclosed ) { $current = trim($current); $key = ( !empty($head[$col]) ) ? $head[$col] : $col; $row[$key] = $current; $current = ''; $col++; // end of row if ( $ch == "\n" || $ch == "\r" ) { if ( $this->heading && empty($head) ) { $head = $row; } 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; } $row = array(); $col = 0; $row_count++; } // append character to current field } else { $current .= $ch; } } $this->titles = $head; if ( !empty($this->sort_by) ) { ( $this->sort_reverse ) ? krsort($rows) : ksort($rows) ; } return $rows; } /** * Create CSV data from array * @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; $string = ( $is_php ) ? "".$this->linefeed : '' ; $entry = array(); // create heading if ( $this->heading && !$append ) { foreach( $fields as $key => $value ) { $entry[] = $this->enclose_value($value); } $string .= implode($delimiter, $entry).$this->linefeed; $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(); } return $string; } // ============================================== // ----- [ Internal Functions ] ----------------- // ============================================== /** * Enclose values if needed * - only used by unparse() * @param value string to process * @return Processed value */ function enclose_value ($value = null) { $delimiter = preg_quote($this->delimiter, '/'); $enclosure = preg_quote($this->enclosure, '/'); if ( preg_match("/".$delimiter."|".$enclosure."|\n|\r/i", $value) ) { $value = str_replace($this->enclosure, $this->enclosure.$this->enclosure, $value); $value = $this->enclosure.$value.$this->enclosure; } return $value; } /** * Check file data * @param file local filename * @return true or false */ 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 */ function check_count ($char, $array, $depth, $prefered) { if ( $depth == count($array) ) { $first = null; $equal = null; $almost = false; foreach( $array as $key => $value ) { if ( $first == null ) { $first = $value; } elseif ( $value == $first && $equal !== false) { $equal = true; } elseif ( $value == $first+1 && $equal !== false ) { $equal = true; $almost = true; } else { $equal = false; } } if ( $equal ) { $match = ( $almost ) ? 2 : 1 ; $pref = strpos($prefered, $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; } } /** * Read local file * @param file local filename * @return Data from file, or false on failure */ function rfile ($file = null){ if ( is_readable($file) ) { if ( !($fh = fopen($file, 'r')) ) return false; $data = fread($fh, filesize($file)); fclose($fh); return $data; } return false; } /** * Write to local file * @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){ if ( $fp = fopen($file, $mode) ) { flock($fp, $lock); $re = fwrite($fp, $string); $re2 = fclose($fp); if ( $re != false && $re2 != false ) return true; } return false; } } ?>