Completed
Push — master ( f79b8d...f9bba4 )
by Andy
02:08
created

CsvFileObject::fwriteCsv()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 0
cts 6
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 3
crap 6
1
<?php
2
3
namespace Palmtree\Csv;
4
5
class CsvFileObject extends \SplFileObject
6
{
7
    protected $bytesWritten = 0;
8
    protected $lineEnding = "\r\n";
9
10
    public function fwriteCsv($fields, $delimiter = null, $enclosure = null)
11
    {
12
        $bytes = $this->fwrite($this->getCsvString($fields, $delimiter, $enclosure));
13
14
        if ($bytes === false) {
15
            return false;
16
        }
17
18
        $this->bytesWritten += $bytes;
19
20
        return $bytes;
21
    }
22
23
    public function __destruct()
24
    {
25
        $this->trimFinalLineEnding();
26
    }
27
28
    /**
29
     * Returns a string representation of a row to be written
30
     * as a line in a CSV file.
31
     *
32
     * @param array $row
33
     *
34
     * @param       $delimiter
35
     * @param       $enclosure
36
     *
37
     * @return string
38
     */
39
    protected function getCsvString($row, $delimiter, $enclosure)
40
    {
41
        $csvControl = $this->getCsvControl();
42
43
        $delimiter = $delimiter ? : $csvControl[0];
44
        $enclosure = $enclosure ? : $csvControl[1];
45
46
        $result = $enclosure;
47
        $result .= implode($enclosure . $delimiter . $enclosure, self::escapeEnclosure($row, $enclosure));
48
        $result .= $enclosure;
49
50
        $result .= $this->getLineEnding();
51
52
        return $result;
53
    }
54
55
    /**
56
     * @return int
57
     */
58
    public function getBytesWritten()
59
    {
60
        return (int)$this->bytesWritten;
61
    }
62
63
    /**
64
     * @return string
65
     */
66
    public function getLineEnding()
67
    {
68
        return $this->lineEnding;
69
    }
70
71
    /**
72
     * @param string $lineEnding
73
     *
74
     * @return CsvFileObject
75
     */
76
    public function setLineEnding($lineEnding)
77
    {
78
        $this->lineEnding = $lineEnding;
79
80
        return $this;
81
    }
82
83
    /**
84
     * Trims the line ending delimiter from the end of the CSV file.
85
     * RFC-4180 states CSV files should not contain a trailing new line.
86
     */
87
    public function trimFinalLineEnding()
88
    {
89
        if ($this->getBytesWritten() > 0) {
90
            // Only trim the file if it ends with the line ending delimiter.
91
            $length = mb_strlen($this->getLineEnding());
92
93
            $this->fseek(-$length, SEEK_END);
94
            $chunk = $this->fread($length);
95
96
            if ($chunk === $this->getLineEnding()) {
97
                $this->ftruncate($this->getBytesWritten() - $length);
98
            }
99
        }
100
    }
101
102
    /**
103
     * Escapes the enclosure character recursively.
104
     * RFC-4180 states the enclosure character (usually double quotes) should be
105
     * escaped by itself, so " becomes "".
106
     *
107
     * @param mixed $data Array or string of data to escape.
108
     *
109
     * @param       $enclosure
110
     *
111
     * @return mixed Escaped data
112
     */
113
    protected static function escapeEnclosure($data, $enclosure)
114
    {
115
        if (is_array($data)) {
116
            foreach ($data as $key => $value) {
117
                $data[$key] = self::escapeEnclosure($value, $enclosure);
118
            }
119
        } else {
120
            $data = str_replace($enclosure, str_repeat($enclosure, 2), $data);
121
        }
122
123
        return $data;
124
    }
125
}
126