Passed
Branch feature/php7 (3e8a2f)
by Andy
04:54
created

CsvFileObject::getCsvString()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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