1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Issei\StreamedCsvResponse; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Writes the csv row to stdout. |
7
|
|
|
* |
8
|
|
|
* {@internal Don't use this in user-land code }} |
9
|
|
|
* |
10
|
|
|
* @author Issei Murasawa <[email protected]> |
11
|
|
|
*/ |
12
|
|
|
class CsvWriter |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* @var resource |
16
|
|
|
*/ |
17
|
|
|
private $out; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var string|null |
21
|
|
|
*/ |
22
|
|
|
private $encodeTo; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var string[] |
26
|
|
|
*/ |
27
|
|
|
private $charsNeedingEnclosing; |
28
|
|
|
|
29
|
3 |
|
public function __construct($encodeTo = null) |
30
|
|
|
{ |
31
|
3 |
|
$this->out = fopen('php://output', 'wt'); |
32
|
|
|
|
33
|
3 |
|
if (null !== $encodeTo && 'UTF-8' !== strtoupper($encodeTo)) { |
34
|
1 |
|
$this->encodeTo = $encodeTo; |
35
|
1 |
|
} |
36
|
|
|
|
37
|
3 |
|
$this->charsNeedingEnclosing = array( |
38
|
3 |
|
',', // separator |
39
|
3 |
|
'"', // enclosure & escaper |
40
|
3 |
|
"\n", |
41
|
3 |
|
"\r", |
42
|
|
|
); |
43
|
3 |
|
} |
44
|
|
|
|
45
|
3 |
|
public function __destruct() |
46
|
|
|
{ |
47
|
3 |
|
fclose($this->out); |
48
|
3 |
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Writes the csv to stdout. |
52
|
|
|
* |
53
|
|
|
* @param array|\Traversable $row |
54
|
|
|
*/ |
55
|
3 |
|
public function writeRow($row) |
56
|
|
|
{ |
57
|
3 |
|
Assert::isIterable($row, 'Every value of $rows should be an array or an instance of \Traversable.'); |
58
|
|
|
|
59
|
2 |
|
$separator = ''; |
60
|
|
|
|
61
|
2 |
|
foreach ($row as $cell) { |
62
|
2 |
|
fwrite($this->out, $separator . $this->formatCell($cell)); |
63
|
|
|
|
64
|
2 |
|
if ('' === $separator) { |
65
|
2 |
|
$separator = ','; |
66
|
2 |
|
} |
67
|
2 |
|
} |
68
|
|
|
|
69
|
2 |
|
fwrite($this->out, "\r\n"); |
70
|
2 |
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Returns the formatted cell. |
74
|
|
|
* |
75
|
|
|
* @param string $cell |
76
|
|
|
* |
77
|
|
|
* @return string |
78
|
|
|
*/ |
79
|
2 |
|
private function formatCell($cell) |
80
|
|
|
{ |
81
|
2 |
|
if ('' === $cell) { |
82
|
|
|
return $cell; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
// auto encoding |
86
|
2 |
|
if (null !== $this->encodeTo) { |
87
|
1 |
|
$cell = mb_convert_encoding($cell, $this->encodeTo, 'UTF-8'); |
88
|
1 |
|
} |
89
|
|
|
|
90
|
2 |
|
foreach ($this->charsNeedingEnclosing as $charNeedingEnclosing) { |
91
|
2 |
|
if (false !== strpos($cell, $charNeedingEnclosing)) { |
92
|
|
|
// enclosing |
93
|
2 |
|
return '"' . str_replace('"', '""', $cell) . '"'; |
94
|
|
|
} |
95
|
2 |
|
} |
96
|
|
|
|
97
|
2 |
|
return $cell; |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|