CSV::schema()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * CSV
5
 *
6
 * Comma Separated Values Tools.
7
 *
8
 * @package core
9
 * @author [email protected]
10
 * @copyright Caffeina srl - 2015 - http://caffeina.com
11
 */
12
13
class CSV {
14
  use Module;
15
16
  const AUTO      = null,
17
        STANDARD  = ',',
18
        EXCEL     = ';',
19
        TAB       = "\t",
20
        READ      = 'r',
21
        WRITE     = 'w';
22
23
  protected $file,
24
            $headers      = [],
25
            $template     = [],
26
            $mode         = self::WRITE,
27
            $format       = self::STANDARD,
28
            $savedheaders = false;
29
30
  public static function open($file, $format=self::AUTO){
31
    return new static($file,self::READ,$format);
32
  }
33
34
  public static function create($file, $format=self::STANDARD){
35
    return new static($file,self::WRITE, $format);
36
  }
37
38
  public function SQL($sql){
39
    $csv = $this;
40
    SQL::each($sql,function($row) use (&$csv){
41
      $csv->write($row);
42
    });
43
    return $this;
44
  }
45
  
46
  public static function fromSQL($sql, $format=self::AUTO){
47
    return static::create(tempnam(sys_get_temp_dir(), 'CSVx'), $format)->SQL($sql);
48
  }
49
50
  public static function fromTable($table, $format=self::AUTO){
51
    $csv = static::create(tempnam(sys_get_temp_dir(), 'CSVx'), $format);
52
    foreach($table as $row){
53
      $csv->write($row);
54
    }
55
    return $csv;
56
  }
57
58
  public function __construct($file, $mode=self::READ, $format=self::AUTO){
59
    $this->mode = $mode;
60
    $this->file = new \SplFileObject($file,'c+');
61
    if (!$this->file->valid()) throw new Exception("Error opening CSV file [$file]", 1);
62
    $this->file->setFlags(
63
      \SplFileObject::READ_CSV |     // set file reading mode to csv
64
      \SplFileObject::SKIP_EMPTY |   // ignore empty lines
65
      \SplFileObject::DROP_NEW_LINE  // drop new line from last column in record
66
    );
67
    $this->format = ($format==self::AUTO ? $this->guessSeparator() : $format);
68
    $this->file->setCsvControl($this->format,'"',"\\");
69
  }
70
71
  private function guessSeparator($checkLines = 2){
72
    if ($this->mode == self::WRITE) return self::STANDARD;
73
    $delimiters = [",","\t",";"];
74
    $results = [];
75
    $this->file->rewind();
76
    while ($checkLines--) {
77
        $line = $this->file->fgets();
78
        foreach ($delimiters as $delimiter){
79
            $fields = preg_split('/['.$delimiter.']/', $line);
80
            if(count($fields) > 1){
81
                if(empty($results[$delimiter])){
82
                  $results[$delimiter] = 1;
83
                } else {
84
                  $results[$delimiter]++;
85
                }
86
            }
87
        }
88
    }
89
    $this->file->rewind();
90
    $results = array_keys($results, max($results));
91
    return $results[0];
92
  }
93
94
  public function write($row){
95
    if ($this->mode != self::WRITE) return;
96
    $row = (array)$row;
97
    if (false === $this->savedheaders) {
98
      $this->schema(array_keys($row));
99
    }
100
    $row_t = $this->template;
101
    foreach ($this->headers as $key) {
102
      if (isset($row[$key])) $row_t[$key] = $row[$key];
103
    }
104
    $this->file->fputcsv($row_t);
105
  }
106
107
  public function read(){
108
    if ($this->mode != self::READ) return;
109
    foreach($this->file as $row){
110
      if ($row){
111
        if(!$this->headers) {
112
          $this->headers = $row;
113
          continue;
114
        }
115
        yield array_combine($this->headers, array_map('trim', $row));
116
      }
117
    }
118
    return;
119
  }
120
121
  public function each(callable $looper = null){
122
    if ($looper) {
123
      foreach($this->read() as $k => $row) $looper($row, (int)$k);
124
      return $this;
125
    } else {
126
      $results = [];
127
      foreach($this->read() as $row) $results[] = $row;
128
      return $results;
129
    }
130
  }
131
132
  public function convert($filename, $format=self::STANDARD){
133
    if ($this->mode != self::READ) return;
134
    if ($format == self::AUTO) $format = self::STANDARD;
135
    $csv = CSV::create($filename, CSV::EXCEL);
136
    $this->each(function($row) use ($csv) {
137
      $csv->write($row);
138
    });
139
    return $csv;
140
  }
141
142
  public function flush(){
143
    if ($this->mode == self::WRITE) {
144
      $this->file->fflush();
145
    }
146
  }
147
148
  public function schema($schema=null){
149
    if($schema){
150
      $this->headers = array_values((array)$schema);
151
      if ($this->mode == self::WRITE) {
152
        $this->savedheaders = true;
153
        $this->template = array_combine($this->headers, array_pad([],count($this->headers),''));
154
        $this->file->fputcsv($this->headers);
155
      }
156
      return $this;
157
    } else {
158
      return $this->headers;
159
    }
160
  }
161
162
  public function asString(){
163
    $this->flush();
164
    return file_get_contents($this->file->getPathname());
165
  }
166
167
  public function __toString(){
168
    try { return $this->asString(); } catch(\Exception $e) { return ''; }
169
  }
170
171
}
172