[ Index ]

MailPress 7.1

[ Index ]     [ Classes ]     [ Functions ]     [ Variables ]     [ Constants ]     [ Statistics ]    

title

Body

[close]

/mp-includes/class/options/import/importers/parsecsv/ -> parsecsv.lib.php (source)

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


Generated: Mon Mar 11 18:33:33 2019 Cross-referenced by PHPXref 0.7.1