adamjakab /
SuiteCRM
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | class parseCSV { |
||
| 4 | |||
| 5 | /* |
||
| 6 | |||
| 7 | Class: parseCSV v0.4.3 beta |
||
| 8 | http://code.google.com/p/parsecsv-for-php/ |
||
| 9 | |||
| 10 | |||
| 11 | Fully conforms to the specifications lined out on wikipedia: |
||
| 12 | - http://en.wikipedia.org/wiki/Comma-separated_values |
||
| 13 | |||
| 14 | Based on the concept of Ming Hong Ng's CsvFileParser class: |
||
| 15 | - http://minghong.blogspot.com/2006/07/csv-parser-for-php.html |
||
| 16 | |||
| 17 | |||
| 18 | |||
| 19 | Copyright (c) 2007 Jim Myhrberg ([email protected]). |
||
| 20 | |||
| 21 | Permission is hereby granted, free of charge, to any person obtaining a copy |
||
| 22 | of this software and associated documentation files (the "Software"), to deal |
||
| 23 | in the Software without restriction, including without limitation the rights |
||
| 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||
| 25 | copies of the Software, and to permit persons to whom the Software is |
||
| 26 | furnished to do so, subject to the following conditions: |
||
| 27 | |||
| 28 | The above copyright notice and this permission notice shall be included in |
||
| 29 | all copies or substantial portions of the Software. |
||
| 30 | |||
| 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
| 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
| 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||
| 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
| 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||
| 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||
| 37 | THE SOFTWARE. |
||
| 38 | |||
| 39 | |||
| 40 | |||
| 41 | Code Examples |
||
| 42 | ---------------- |
||
| 43 | # general usage |
||
| 44 | $csv = new parseCSV('data.csv'); |
||
| 45 | print_r($csv->data); |
||
| 46 | ---------------- |
||
| 47 | # tab delimited, and encoding conversion |
||
| 48 | $csv = new parseCSV(); |
||
| 49 | $csv->encoding('UTF-16', 'UTF-8'); |
||
| 50 | $csv->delimiter = "\t"; |
||
| 51 | $csv->parse('data.tsv'); |
||
| 52 | print_r($csv->data); |
||
| 53 | ---------------- |
||
| 54 | # auto-detect delimiter character |
||
| 55 | $csv = new parseCSV(); |
||
| 56 | $csv->auto('data.csv'); |
||
| 57 | print_r($csv->data); |
||
| 58 | ---------------- |
||
| 59 | # modify data in a csv file |
||
| 60 | $csv = new parseCSV(); |
||
| 61 | $csv->sort_by = 'id'; |
||
| 62 | $csv->parse('data.csv'); |
||
| 63 | # "4" is the value of the "id" column of the CSV row |
||
| 64 | $csv->data[4] = array('firstname' => 'John', 'lastname' => 'Doe', 'email' => '[email protected]'); |
||
| 65 | $csv->save(); |
||
| 66 | ---------------- |
||
| 67 | # add row/entry to end of CSV file |
||
| 68 | # - only recommended when you know the extact sctructure of the file |
||
| 69 | $csv = new parseCSV(); |
||
| 70 | $csv->save('data.csv', array('1986', 'Home', 'Nowhere', ''), true); |
||
| 71 | ---------------- |
||
| 72 | # convert 2D array to csv data and send headers |
||
| 73 | # to browser to treat output as a file and download it |
||
| 74 | $csv = new parseCSV(); |
||
| 75 | $csv->output (true, 'movies.csv', $array); |
||
| 76 | ---------------- |
||
| 77 | |||
| 78 | |||
| 79 | */ |
||
| 80 | |||
| 81 | |||
| 82 | /** |
||
| 83 | * Configuration |
||
| 84 | * - set these options with $object->var_name = 'value'; |
||
| 85 | */ |
||
| 86 | |||
| 87 | # use first line/entry as field names |
||
| 88 | var $heading = true; |
||
| 89 | |||
| 90 | # override field names |
||
| 91 | var $fields = array(); |
||
| 92 | |||
| 93 | # sort entries by this field |
||
| 94 | var $sort_by = null; |
||
| 95 | var $sort_reverse = false; |
||
| 96 | |||
| 97 | # sort behavior passed to ksort/krsort functions |
||
| 98 | # regular = SORT_REGULAR |
||
| 99 | # numeric = SORT_NUMERIC |
||
| 100 | # string = SORT_STRING |
||
| 101 | var $sort_type = null; |
||
| 102 | |||
| 103 | # delimiter (comma) and enclosure (double quote) |
||
| 104 | var $delimiter = ','; |
||
| 105 | var $enclosure = '"'; |
||
| 106 | |||
| 107 | # basic SQL-like conditions for row matching |
||
| 108 | var $conditions = null; |
||
| 109 | |||
| 110 | # number of rows to ignore from beginning of data |
||
| 111 | var $offset = null; |
||
| 112 | |||
| 113 | # limits the number of returned rows to specified amount |
||
| 114 | var $limit = null; |
||
| 115 | |||
| 116 | # number of rows to analyze when attempting to auto-detect delimiter |
||
| 117 | var $auto_depth = 15; |
||
| 118 | |||
| 119 | # characters to ignore when attempting to auto-detect delimiter |
||
| 120 | var $auto_non_chars = "a-zA-Z0-9\n\r"; |
||
| 121 | |||
| 122 | # preferred delimiter characters, only used when all filtering method |
||
| 123 | # returns multiple possible delimiters (happens very rarely) |
||
| 124 | var $auto_preferred = ",;\t.:|"; |
||
| 125 | |||
| 126 | # character encoding options |
||
| 127 | var $convert_encoding = false; |
||
| 128 | var $input_encoding = 'ISO-8859-1'; |
||
| 129 | var $output_encoding = 'ISO-8859-1'; |
||
| 130 | |||
| 131 | # used by unparse(), save(), and output() functions |
||
| 132 | var $linefeed = "\r\n"; |
||
| 133 | |||
| 134 | # only used by output() function |
||
| 135 | var $output_delimiter = ','; |
||
| 136 | var $output_filename = 'data.csv'; |
||
| 137 | |||
| 138 | # keep raw file data in memory after successful parsing (useful for debugging) |
||
| 139 | var $keep_file_data = false; |
||
| 140 | |||
| 141 | /** |
||
| 142 | * Internal variables |
||
| 143 | */ |
||
| 144 | |||
| 145 | # current file |
||
| 146 | var $file; |
||
| 147 | |||
| 148 | # loaded file contents |
||
| 149 | var $file_data; |
||
| 150 | |||
| 151 | # error while parsing input data |
||
| 152 | # 0 = No errors found. Everything should be fine :) |
||
| 153 | # 1 = Hopefully correctable syntax error was found. |
||
| 154 | # 2 = Enclosure character (double quote by default) |
||
| 155 | # was found in non-enclosed field. This means |
||
| 156 | # the file is either corrupt, or does not |
||
| 157 | # standard CSV formatting. Please validate |
||
| 158 | # the parsed data yourself. |
||
| 159 | var $error = 0; |
||
| 160 | |||
| 161 | # detailed error info |
||
| 162 | var $error_info = array(); |
||
| 163 | |||
| 164 | # array of field values in data parsed |
||
| 165 | var $titles = array(); |
||
| 166 | |||
| 167 | # two dimensional array of CSV data |
||
| 168 | var $data = array(); |
||
| 169 | |||
| 170 | |||
| 171 | /** |
||
| 172 | * Constructor |
||
| 173 | * @param input CSV file or string |
||
| 174 | */ |
||
| 175 | function __construct ($input = null, $offset = null, $limit = null, $conditions = null) { |
||
| 176 | if ( $offset !== null ) $this->offset = $offset; |
||
| 177 | if ( $limit !== null ) $this->limit = $limit; |
||
| 178 | if ( count($conditions) > 0 ) $this->conditions = $conditions; |
||
| 179 | if ( !empty($input) ) $this->parse($input); |
||
| 180 | } |
||
| 181 | |||
| 182 | /** |
||
| 183 | * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead |
||
| 184 | */ |
||
| 185 | function parseCSV($input = null, $offset = null, $limit = null, $conditions = null){ |
||
| 186 | $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code'; |
||
| 187 | if(isset($GLOBALS['log'])) { |
||
| 188 | $GLOBALS['log']->deprecated($deprecatedMessage); |
||
| 189 | } |
||
| 190 | else { |
||
| 191 | trigger_error($deprecatedMessage, E_USER_DEPRECATED); |
||
| 192 | } |
||
| 193 | self::__construct($input, $offset, $limit, $conditions); |
||
| 194 | } |
||
| 195 | |||
| 196 | |||
| 197 | |||
| 198 | // ============================================== |
||
| 199 | // ----- [ Main Functions ] --------------------- |
||
| 200 | // ============================================== |
||
| 201 | |||
| 202 | /** |
||
| 203 | * Parse CSV file or string |
||
| 204 | * @param input CSV file or string |
||
| 205 | * @return nothing |
||
| 206 | */ |
||
| 207 | function parse ($input = null, $offset = null, $limit = null, $conditions = null) { |
||
| 208 | if ( $input === null ) $input = $this->file; |
||
| 209 | if ( !empty($input) ) { |
||
| 210 | if ( $offset !== null ) $this->offset = $offset; |
||
| 211 | if ( $limit !== null ) $this->limit = $limit; |
||
| 212 | if ( count($conditions) > 0 ) $this->conditions = $conditions; |
||
| 213 | if ( is_readable($input) ) { |
||
| 214 | $this->data = $this->parse_file($input); |
||
| 215 | } else { |
||
| 216 | $this->file_data = &$input; |
||
| 217 | $this->data = $this->parse_string(); |
||
| 218 | } |
||
| 219 | if ( $this->data === false ) return false; |
||
| 220 | } |
||
| 221 | return true; |
||
| 222 | } |
||
| 223 | |||
| 224 | /** |
||
| 225 | * Save changes, or new file and/or data |
||
| 226 | * @param file file to save to |
||
| 227 | * @param data 2D array with data |
||
| 228 | * @param append append current data to end of target CSV if exists |
||
| 229 | * @param fields field names |
||
| 230 | * @return true or false |
||
| 231 | */ |
||
| 232 | function save ($file = null, $data = array(), $append = false, $fields = array()) { |
||
| 233 | if ( empty($file) ) $file = &$this->file; |
||
| 234 | $mode = ( $append ) ? 'at' : 'wt' ; |
||
| 235 | $is_php = ( preg_match('/\.php$/i', $file) ) ? true : false ; |
||
| 236 | return $this->_wfile($file, $this->unparse($data, $fields, $append, $is_php), $mode); |
||
| 237 | } |
||
| 238 | |||
| 239 | /** |
||
| 240 | * Generate CSV based string for output |
||
| 241 | * @param filename if specified, headers and data will be output directly to browser as a downloable file |
||
| 242 | * @param data 2D array with data |
||
| 243 | * @param fields field names |
||
| 244 | * @param delimiter delimiter used to separate data |
||
| 245 | * @return CSV data using delimiter of choice, or default |
||
| 246 | */ |
||
| 247 | function output ($filename = null, $data = array(), $fields = array(), $delimiter = null) { |
||
| 248 | if ( empty($filename) ) $filename = $this->output_filename; |
||
| 249 | if ( $delimiter === null ) $delimiter = $this->output_delimiter; |
||
| 250 | $data = $this->unparse($data, $fields, null, null, $delimiter); |
||
| 251 | if ( $filename !== null ) { |
||
| 252 | header('Content-type: application/csv'); |
||
| 253 | header('Content-Disposition: attachment; filename="'.$filename.'"'); |
||
| 254 | echo $data; |
||
| 255 | } |
||
| 256 | return $data; |
||
| 257 | } |
||
| 258 | |||
| 259 | /** |
||
| 260 | * Convert character encoding |
||
| 261 | * @param input input character encoding, uses default if left blank |
||
| 262 | * @param output output character encoding, uses default if left blank |
||
| 263 | * @return nothing |
||
| 264 | */ |
||
| 265 | function encoding ($input = null, $output = null) { |
||
| 266 | $this->convert_encoding = true; |
||
| 267 | if ( $input !== null ) $this->input_encoding = $input; |
||
| 268 | if ( $output !== null ) $this->output_encoding = $output; |
||
| 269 | } |
||
| 270 | |||
| 271 | /** |
||
| 272 | * Auto-Detect Delimiter: Find delimiter by analyzing a specific number of |
||
| 273 | * rows to determine most probable delimiter character |
||
| 274 | * @param file local CSV file |
||
| 275 | * @param parse true/false parse file directly |
||
| 276 | * @param search_depth number of rows to analyze |
||
| 277 | * @param preferred preferred delimiter characters |
||
| 278 | * @param enclosure enclosure character, default is double quote ("). |
||
| 279 | * @return delimiter character |
||
| 280 | */ |
||
| 281 | function auto ($file = null, $parse = true, $search_depth = null, $preferred = null, $enclosure = null) { |
||
| 282 | |||
| 283 | if ( $file === null ) $file = $this->file; |
||
| 284 | if ( empty($search_depth) ) $search_depth = $this->auto_depth; |
||
| 285 | if ( $enclosure === null ) $enclosure = $this->enclosure; |
||
| 286 | else $this->enclosure = $enclosure; |
||
| 287 | |||
| 288 | if ( $preferred === null ) $preferred = $this->auto_preferred; |
||
| 289 | |||
| 290 | if ( empty($this->file_data) ) { |
||
| 291 | if ( $this->_check_data($file) ) { |
||
| 292 | $data = &$this->file_data; |
||
| 293 | } else return false; |
||
| 294 | } else { |
||
| 295 | $data = &$this->file_data; |
||
| 296 | } |
||
| 297 | |||
| 298 | $chars = array(); |
||
| 299 | $strlen = strlen($data); |
||
| 300 | $enclosed = false; |
||
| 301 | $n = 1; |
||
| 302 | $to_end = true; |
||
| 303 | |||
| 304 | // walk specific depth finding possible delimiter characters |
||
| 305 | for ( $i=0; $i < $strlen; $i++ ) { |
||
| 306 | $ch = $data{$i}; |
||
| 307 | $nch = ( isset($data{$i+1}) ) ? $data{$i+1} : false ; |
||
| 308 | $pch = ( isset($data{$i-1}) ) ? $data{$i-1} : false ; |
||
| 309 | |||
| 310 | // open and closing quotes |
||
| 311 | if ( $ch == $enclosure ) { |
||
| 312 | if ( !$enclosed || $nch != $enclosure ) { |
||
| 313 | $enclosed = ( $enclosed ) ? false : true ; |
||
| 314 | } elseif ( $enclosed ) { |
||
| 315 | $i++; |
||
| 316 | } |
||
| 317 | |||
| 318 | // end of row |
||
| 319 | } elseif ( ($ch == "\n" && $pch != "\r" || $ch == "\r") && !$enclosed ) { |
||
| 320 | if ( $n >= $search_depth ) { |
||
| 321 | $strlen = 0; |
||
| 322 | $to_end = false; |
||
| 323 | } else { |
||
| 324 | $n++; |
||
| 325 | } |
||
| 326 | |||
| 327 | // count character |
||
| 328 | } elseif (!$enclosed) { |
||
| 329 | if ( !preg_match('/['.preg_quote($this->auto_non_chars, '/').']/i', $ch) ) { |
||
| 330 | if ( !isset($chars[$ch][$n]) ) { |
||
| 331 | $chars[$ch][$n] = 1; |
||
| 332 | } else { |
||
| 333 | $chars[$ch][$n]++; |
||
| 334 | } |
||
| 335 | } |
||
| 336 | } |
||
| 337 | } |
||
| 338 | |||
| 339 | // filtering |
||
| 340 | $depth = ( $to_end ) ? $n-1 : $n ; |
||
| 341 | $filtered = array(); |
||
| 342 | foreach( $chars as $char => $value ) { |
||
| 343 | if ( $match = $this->_check_count($char, $value, $depth, $preferred) ) { |
||
| 344 | $filtered[$match] = $char; |
||
| 345 | } |
||
| 346 | } |
||
| 347 | |||
| 348 | // capture most probable delimiter |
||
| 349 | ksort($filtered); |
||
| 350 | $this->delimiter = reset($filtered); |
||
| 351 | |||
| 352 | // parse data |
||
| 353 | if ( $parse ) $this->data = $this->parse_string(); |
||
| 354 | |||
| 355 | return $this->delimiter; |
||
| 356 | |||
| 357 | } |
||
| 358 | |||
| 359 | |||
| 360 | // ============================================== |
||
| 361 | // ----- [ Core Functions ] --------------------- |
||
| 362 | // ============================================== |
||
| 363 | |||
| 364 | /** |
||
| 365 | * Read file to string and call parse_string() |
||
| 366 | * @param file local CSV file |
||
| 367 | * @return 2D array with CSV data, or false on failure |
||
| 368 | */ |
||
| 369 | function parse_file ($file = null) { |
||
| 370 | if ( $file === null ) $file = $this->file; |
||
| 371 | if ( empty($this->file_data) ) $this->load_data($file); |
||
| 372 | return ( !empty($this->file_data) ) ? $this->parse_string() : false ; |
||
| 373 | } |
||
| 374 | |||
| 375 | /** |
||
| 376 | * Parse CSV strings to arrays |
||
| 377 | * @param data CSV string |
||
| 378 | * @return 2D array with CSV data, or false on failure |
||
| 379 | */ |
||
| 380 | function parse_string ($data = null) { |
||
| 381 | if ( empty($data) ) { |
||
| 382 | if ( $this->_check_data() ) { |
||
| 383 | $data = &$this->file_data; |
||
| 384 | } else return false; |
||
| 385 | } |
||
| 386 | |||
| 387 | $white_spaces = str_replace($this->delimiter, '', " \t\x0B\0"); |
||
| 388 | |||
| 389 | $rows = array(); |
||
| 390 | $row = array(); |
||
| 391 | $row_count = 0; |
||
| 392 | $current = ''; |
||
| 393 | $head = ( !empty($this->fields) ) ? $this->fields : array() ; |
||
| 394 | $col = 0; |
||
| 395 | $enclosed = false; |
||
| 396 | $was_enclosed = false; |
||
| 397 | $strlen = strlen($data); |
||
| 398 | |||
| 399 | // walk through each character |
||
| 400 | for ( $i=0; $i < $strlen; $i++ ) { |
||
| 401 | $ch = $data{$i}; |
||
| 402 | $nch = ( isset($data{$i+1}) ) ? $data{$i+1} : false ; |
||
| 403 | $pch = ( isset($data{$i-1}) ) ? $data{$i-1} : false ; |
||
| 404 | |||
| 405 | // open/close quotes, and inline quotes |
||
| 406 | if ( $ch == $this->enclosure ) { |
||
| 407 | if ( !$enclosed ) { |
||
| 408 | if ( ltrim($current, $white_spaces) == '' ) { |
||
| 409 | $enclosed = true; |
||
| 410 | $was_enclosed = true; |
||
| 411 | } else { |
||
| 412 | $this->error = 2; |
||
| 413 | $error_row = count($rows) + 1; |
||
| 414 | $error_col = $col + 1; |
||
| 415 | if ( !isset($this->error_info[$error_row.'-'.$error_col]) ) { |
||
| 416 | $this->error_info[$error_row.'-'.$error_col] = array( |
||
| 417 | 'type' => 2, |
||
| 418 | 'info' => 'Syntax error found on row '.$error_row.'. Non-enclosed fields can not contain double-quotes.', |
||
| 419 | 'row' => $error_row, |
||
| 420 | 'field' => $error_col, |
||
| 421 | 'field_name' => (!empty($head[$col])) ? $head[$col] : null, |
||
| 422 | ); |
||
| 423 | } |
||
| 424 | $current .= $ch; |
||
| 425 | } |
||
| 426 | } elseif ($nch == $this->enclosure) { |
||
| 427 | $current .= $ch; |
||
| 428 | $i++; |
||
| 429 | } elseif ( $nch != $this->delimiter && $nch != "\r" && $nch != "\n" ) { |
||
| 430 | for ( $x=($i+1); isset($data{$x}) && ltrim($data{$x}, $white_spaces) == ''; $x++ ) {} |
||
| 431 | if ( $data{$x} == $this->delimiter ) { |
||
| 432 | $enclosed = false; |
||
| 433 | $i = $x; |
||
| 434 | } else { |
||
| 435 | if ( $this->error < 1 ) { |
||
| 436 | $this->error = 1; |
||
| 437 | } |
||
| 438 | $error_row = count($rows) + 1; |
||
| 439 | $error_col = $col + 1; |
||
| 440 | if ( !isset($this->error_info[$error_row.'-'.$error_col]) ) { |
||
| 441 | $this->error_info[$error_row.'-'.$error_col] = array( |
||
| 442 | 'type' => 1, |
||
| 443 | 'info' => |
||
| 444 | 'Syntax error found on row '.(count($rows) + 1).'. '. |
||
| 445 | 'A single double-quote was found within an enclosed string. '. |
||
| 446 | 'Enclosed double-quotes must be escaped with a second double-quote.', |
||
| 447 | 'row' => count($rows) + 1, |
||
| 448 | 'field' => $col + 1, |
||
| 449 | 'field_name' => (!empty($head[$col])) ? $head[$col] : null, |
||
| 450 | ); |
||
| 451 | } |
||
| 452 | $current .= $ch; |
||
| 453 | $enclosed = false; |
||
| 454 | } |
||
| 455 | } else { |
||
| 456 | $enclosed = false; |
||
| 457 | } |
||
| 458 | |||
| 459 | // end of field/row |
||
| 460 | } elseif ( ($ch == $this->delimiter || $ch == "\n" || $ch == "\r") && !$enclosed ) { |
||
| 461 | $key = ( !empty($head[$col]) ) ? $head[$col] : $col ; |
||
| 462 | $row[$key] = ( $was_enclosed ) ? $current : trim($current) ; |
||
| 463 | $current = ''; |
||
| 464 | $was_enclosed = false; |
||
| 465 | $col++; |
||
| 466 | |||
| 467 | // end of row |
||
| 468 | if ( $ch == "\n" || $ch == "\r" ) { |
||
| 469 | if ( $this->_validate_offset($row_count) && $this->_validate_row_conditions($row, $this->conditions) ) { |
||
| 470 | if ( $this->heading && empty($head) ) { |
||
| 471 | $head = $row; |
||
| 472 | } elseif ( empty($this->fields) || (!empty($this->fields) && (($this->heading && $row_count > 0) || !$this->heading)) ) { |
||
| 473 | if ( !empty($this->sort_by) && !empty($row[$this->sort_by]) ) { |
||
| 474 | if ( isset($rows[$row[$this->sort_by]]) ) { |
||
| 475 | $rows[$row[$this->sort_by].'_0'] = &$rows[$row[$this->sort_by]]; |
||
| 476 | unset($rows[$row[$this->sort_by]]); |
||
| 477 | for ( $sn=1; isset($rows[$row[$this->sort_by].'_'.$sn]); $sn++ ) {} |
||
| 478 | $rows[$row[$this->sort_by].'_'.$sn] = $row; |
||
| 479 | } else $rows[$row[$this->sort_by]] = $row; |
||
| 480 | } else $rows[] = $row; |
||
| 481 | } |
||
| 482 | } |
||
| 483 | $row = array(); |
||
| 484 | $col = 0; |
||
| 485 | $row_count++; |
||
| 486 | if ( $this->sort_by === null && $this->limit !== null && count($rows) == $this->limit ) { |
||
| 487 | $i = $strlen; |
||
| 488 | } |
||
| 489 | if ( $ch == "\r" && $nch == "\n" ) $i++; |
||
| 490 | } |
||
| 491 | |||
| 492 | // append character to current field |
||
| 493 | } else { |
||
| 494 | $current .= $ch; |
||
| 495 | } |
||
| 496 | } |
||
| 497 | $this->titles = $head; |
||
| 498 | if ( !empty($this->sort_by) ) { |
||
| 499 | $sort_type = SORT_REGULAR; |
||
| 500 | if ( $this->sort_type == 'numeric' ) { |
||
| 501 | $sort_type = SORT_NUMERIC; |
||
| 502 | } elseif ( $this->sort_type == 'string' ) { |
||
| 503 | $sort_type = SORT_STRING; |
||
| 504 | } |
||
| 505 | ( $this->sort_reverse ) ? krsort($rows, $sort_type) : ksort($rows, $sort_type) ; |
||
| 506 | if ( $this->offset !== null || $this->limit !== null ) { |
||
| 507 | $rows = array_slice($rows, ($this->offset === null ? 0 : $this->offset) , $this->limit, true); |
||
| 508 | } |
||
| 509 | } |
||
| 510 | if ( !$this->keep_file_data ) { |
||
| 511 | $this->file_data = null; |
||
| 512 | } |
||
| 513 | return $rows; |
||
| 514 | } |
||
| 515 | |||
| 516 | /** |
||
| 517 | * Create CSV data from array |
||
| 518 | * @param data 2D array with data |
||
| 519 | * @param fields field names |
||
| 520 | * @param append if true, field names will not be output |
||
| 521 | * @param is_php if a php die() call should be put on the first |
||
| 522 | * line of the file, this is later ignored when read. |
||
| 523 | * @param delimiter field delimiter to use |
||
| 524 | * @return CSV data (text string) |
||
| 525 | */ |
||
| 526 | function unparse ( $data = array(), $fields = array(), $append = false , $is_php = false, $delimiter = null) { |
||
| 527 | if ( !is_array($data) || empty($data) ) $data = &$this->data; |
||
| 528 | if ( !is_array($fields) || empty($fields) ) $fields = &$this->titles; |
||
| 529 | if ( $delimiter === null ) $delimiter = $this->delimiter; |
||
| 530 | |||
| 531 | $string = ( $is_php ) ? "<?php header('Status: 403'); die(' '); ?>".$this->linefeed : '' ; |
||
| 532 | $entry = array(); |
||
| 533 | |||
| 534 | // create heading |
||
| 535 | if ( $this->heading && !$append && !empty($fields) ) { |
||
| 536 | foreach( $fields as $key => $value ) { |
||
| 537 | $entry[] = $this->_enclose_value($value); |
||
| 538 | } |
||
| 539 | $string .= implode($delimiter, $entry).$this->linefeed; |
||
| 540 | $entry = array(); |
||
| 541 | } |
||
| 542 | |||
| 543 | // create data |
||
| 544 | foreach( $data as $key => $row ) { |
||
| 545 | foreach( $row as $field => $value ) { |
||
| 546 | $entry[] = $this->_enclose_value($value); |
||
| 547 | } |
||
| 548 | $string .= implode($delimiter, $entry).$this->linefeed; |
||
| 549 | $entry = array(); |
||
| 550 | } |
||
| 551 | |||
| 552 | return $string; |
||
| 553 | } |
||
| 554 | |||
| 555 | /** |
||
| 556 | * Load local file or string |
||
| 557 | * @param input local CSV file |
||
| 558 | * @return true or false |
||
| 559 | */ |
||
| 560 | function load_data ($input = null) { |
||
| 561 | $data = null; |
||
| 562 | $file = null; |
||
| 563 | if ( $input === null ) { |
||
| 564 | $file = $this->file; |
||
| 565 | } elseif ( file_exists($input) ) { |
||
| 566 | $file = $input; |
||
| 567 | } else { |
||
| 568 | $data = $input; |
||
| 569 | } |
||
| 570 | if ( !empty($data) || $data = $this->_rfile($file) ) { |
||
| 571 | if ( $this->file != $file ) $this->file = $file; |
||
| 572 | if ( preg_match('/\.php$/i', $file) && preg_match('/<\?.*?\?>(.*)/ims', $data, $strip) ) { |
||
| 573 | $data = ltrim($strip[1]); |
||
| 574 | } |
||
| 575 | if ( $this->convert_encoding ) $data = iconv($this->input_encoding, $this->output_encoding, $data); |
||
| 576 | if ( substr($data, -1) != "\n" ) $data .= "\n"; |
||
| 577 | $this->file_data = &$data; |
||
| 578 | return true; |
||
| 579 | } |
||
| 580 | return false; |
||
| 581 | } |
||
| 582 | |||
| 583 | |||
| 584 | // ============================================== |
||
| 585 | // ----- [ Internal Functions ] ----------------- |
||
| 586 | // ============================================== |
||
| 587 | |||
| 588 | /** |
||
| 589 | * Validate a row against specified conditions |
||
| 590 | * @param row array with values from a row |
||
| 591 | * @param conditions specified conditions that the row must match |
||
| 592 | * @return true of false |
||
| 593 | */ |
||
| 594 | function _validate_row_conditions ($row = array(), $conditions = null) { |
||
| 595 | if ( !empty($row) ) { |
||
| 596 | if ( !empty($conditions) ) { |
||
| 597 | $conditions = (strpos($conditions, ' OR ') !== false) ? explode(' OR ', $conditions) : array($conditions) ; |
||
| 598 | $or = ''; |
||
| 599 | foreach( $conditions as $key => $value ) { |
||
| 600 | if ( strpos($value, ' AND ') !== false ) { |
||
| 601 | $value = explode(' AND ', $value); |
||
| 602 | $and = ''; |
||
| 603 | foreach( $value as $k => $v ) { |
||
| 604 | $and .= $this->_validate_row_condition($row, $v); |
||
| 605 | } |
||
| 606 | $or .= (strpos($and, '0') !== false) ? '0' : '1' ; |
||
| 607 | } else { |
||
| 608 | $or .= $this->_validate_row_condition($row, $value); |
||
| 609 | } |
||
| 610 | } |
||
| 611 | return (strpos($or, '1') !== false) ? true : false ; |
||
| 612 | } |
||
| 613 | return true; |
||
| 614 | } |
||
| 615 | return false; |
||
| 616 | } |
||
| 617 | |||
| 618 | /** |
||
| 619 | * Validate a row against a single condition |
||
| 620 | * @param row array with values from a row |
||
| 621 | * @param condition specified condition that the row must match |
||
| 622 | * @return true of false |
||
| 623 | */ |
||
| 624 | function _validate_row_condition ($row, $condition) { |
||
| 625 | $operators = array( |
||
| 626 | '=', 'equals', 'is', |
||
| 627 | '!=', 'is not', |
||
| 628 | '<', 'is less than', |
||
| 629 | '>', 'is greater than', |
||
| 630 | '<=', 'is less than or equals', |
||
| 631 | '>=', 'is greater than or equals', |
||
| 632 | 'contains', |
||
| 633 | 'does not contain', |
||
| 634 | ); |
||
| 635 | $operators_regex = array(); |
||
| 636 | foreach( $operators as $value ) { |
||
| 637 | $operators_regex[] = preg_quote($value, '/'); |
||
| 638 | } |
||
| 639 | $operators_regex = implode('|', $operators_regex); |
||
| 640 | if ( preg_match('/^(.+) ('.$operators_regex.') (.+)$/i', trim($condition), $capture) ) { |
||
| 641 | $field = $capture[1]; |
||
| 642 | $op = $capture[2]; |
||
| 643 | $value = $capture[3]; |
||
| 644 | if ( preg_match('/^([\'\"]{1})(.*)([\'\"]{1})$/i', $value, $capture) ) { |
||
| 645 | if ( $capture[1] == $capture[3] ) { |
||
| 646 | $value = $capture[2]; |
||
| 647 | $value = str_replace("\\n", "\n", $value); |
||
| 648 | $value = str_replace("\\r", "\r", $value); |
||
| 649 | $value = str_replace("\\t", "\t", $value); |
||
| 650 | $value = stripslashes($value); |
||
| 651 | } |
||
| 652 | } |
||
| 653 | if ( array_key_exists($field, $row) ) { |
||
| 654 | if ( ($op == '=' || $op == 'equals' || $op == 'is') && $row[$field] == $value ) { |
||
| 655 | return '1'; |
||
| 656 | } elseif ( ($op == '!=' || $op == 'is not') && $row[$field] != $value ) { |
||
| 657 | return '1'; |
||
| 658 | } elseif ( ($op == '<' || $op == 'is less than' ) && $row[$field] < $value ) { |
||
| 659 | return '1'; |
||
| 660 | } elseif ( ($op == '>' || $op == 'is greater than') && $row[$field] > $value ) { |
||
| 661 | return '1'; |
||
| 662 | } elseif ( ($op == '<=' || $op == 'is less than or equals' ) && $row[$field] <= $value ) { |
||
| 663 | return '1'; |
||
| 664 | } elseif ( ($op == '>=' || $op == 'is greater than or equals') && $row[$field] >= $value ) { |
||
| 665 | return '1'; |
||
| 666 | } elseif ( $op == 'contains' && preg_match('/'.preg_quote($value, '/').'/i', $row[$field]) ) { |
||
| 667 | return '1'; |
||
| 668 | } elseif ( $op == 'does not contain' && !preg_match('/'.preg_quote($value, '/').'/i', $row[$field]) ) { |
||
| 669 | return '1'; |
||
| 670 | } else { |
||
| 671 | return '0'; |
||
| 672 | } |
||
| 673 | } |
||
| 674 | } |
||
| 675 | return '1'; |
||
| 676 | } |
||
| 677 | |||
| 678 | /** |
||
| 679 | * Validates if the row is within the offset or not if sorting is disabled |
||
| 680 | * @param current_row the current row number being processed |
||
| 681 | * @return true of false |
||
| 682 | */ |
||
| 683 | function _validate_offset ($current_row) { |
||
| 684 | if ( $this->sort_by === null && $this->offset !== null && $current_row < $this->offset ) return false; |
||
| 685 | return true; |
||
| 686 | } |
||
| 687 | |||
| 688 | /** |
||
| 689 | * Enclose values if needed |
||
| 690 | * - only used by unparse() |
||
| 691 | * @param value string to process |
||
| 692 | * @return Processed value |
||
| 693 | */ |
||
| 694 | function _enclose_value ($value = null) { |
||
| 695 | if ( $value !== null && $value != '' ) { |
||
| 696 | $delimiter = preg_quote($this->delimiter, '/'); |
||
| 697 | $enclosure = preg_quote($this->enclosure, '/'); |
||
| 698 | if ( preg_match("/".$delimiter."|".$enclosure."|\n|\r/i", $value) || ($value{0} == ' ' || substr($value, -1) == ' ') ) { |
||
| 699 | $value = str_replace($this->enclosure, $this->enclosure.$this->enclosure, $value); |
||
| 700 | $value = $this->enclosure.$value.$this->enclosure; |
||
| 701 | } |
||
| 702 | } |
||
| 703 | return $value; |
||
| 704 | } |
||
| 705 | |||
| 706 | /** |
||
| 707 | * Check file data |
||
| 708 | * @param file local filename |
||
| 709 | * @return true or false |
||
| 710 | */ |
||
| 711 | function _check_data ($file = null) { |
||
| 712 | if ( empty($this->file_data) ) { |
||
| 713 | if ( $file === null ) $file = $this->file; |
||
| 714 | return $this->load_data($file); |
||
| 715 | } |
||
| 716 | return true; |
||
| 717 | } |
||
| 718 | |||
| 719 | |||
| 720 | /** |
||
| 721 | * Check if passed info might be delimiter |
||
| 722 | * - only used by find_delimiter() |
||
| 723 | * @return special string used for delimiter selection, or false |
||
| 724 | */ |
||
| 725 | function _check_count ($char, $array, $depth, $preferred) { |
||
| 726 | if ( $depth == count($array) ) { |
||
| 727 | $first = null; |
||
| 728 | $equal = null; |
||
| 729 | $almost = false; |
||
| 730 | foreach( $array as $key => $value ) { |
||
| 731 | if ( $first == null ) { |
||
| 732 | $first = $value; |
||
| 733 | } elseif ( $value == $first && $equal !== false) { |
||
| 734 | $equal = true; |
||
| 735 | } elseif ( $value == $first+1 && $equal !== false ) { |
||
| 736 | $equal = true; |
||
| 737 | $almost = true; |
||
| 738 | } else { |
||
| 739 | $equal = false; |
||
| 740 | } |
||
| 741 | } |
||
| 742 | if ( $equal ) { |
||
| 743 | $match = ( $almost ) ? 2 : 1 ; |
||
| 744 | $pref = strpos($preferred, $char); |
||
| 745 | $pref = ( $pref !== false ) ? str_pad($pref, 3, '0', STR_PAD_LEFT) : '999' ; |
||
| 746 | return $pref.$match.'.'.(99999 - str_pad($first, 5, '0', STR_PAD_LEFT)); |
||
| 747 | } else return false; |
||
| 748 | } |
||
| 749 | } |
||
| 750 | |||
| 751 | /** |
||
| 752 | * Read local file |
||
| 753 | * @param file local filename |
||
| 754 | * @return Data from file, or false on failure |
||
| 755 | */ |
||
| 756 | function _rfile ($file = null) { |
||
| 757 | if ( is_readable($file) ) { |
||
| 758 | if ( !($fh = fopen($file, 'r')) ) return false; |
||
| 759 | $data = fread($fh, filesize($file)); |
||
| 760 | fclose($fh); |
||
| 761 | return $data; |
||
| 762 | } |
||
| 763 | return false; |
||
| 764 | } |
||
| 765 | |||
| 766 | /** |
||
| 767 | * Write to local file |
||
| 768 | * @param file local filename |
||
| 769 | * @param string data to write to file |
||
| 770 | * @param mode fopen() mode |
||
| 771 | * @param lock flock() mode |
||
| 772 | * @return true or false |
||
| 773 | */ |
||
| 774 | function _wfile ($file, $string = '', $mode = 'wb', $lock = 2) { |
||
| 775 | if ( $fp = fopen($file, $mode) ) { |
||
| 776 | flock($fp, $lock); |
||
| 777 | $re = fwrite($fp, $string); |
||
| 778 | $re2 = fclose($fp); |
||
| 779 | if ( $re != false && $re2 != false ) return true; |
||
|
0 ignored issues
–
show
|
|||
| 780 | } |
||
| 781 | return false; |
||
| 782 | } |
||
| 783 | |||
| 784 | } |
||
| 785 | |||
| 786 | ?> |
||
| 787 |
When comparing two booleans, it is generally considered safer to use the strict comparison operator.