CSVWriter   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 96.83%

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 9
dl 0
loc 175
ccs 61
cts 63
cp 0.9683
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
C getData() 0 85 14
A escapeLineDelimiter() 0 5 1
A escapeTabDelimiter() 0 4 1
A escapeFieldDelimiter() 0 4 1
A addEnclosure() 0 10 2
A getHttpHeaders() 0 10 2
1
<?php
2
3
/*
4
 * soluble-flexstore library
5
 *
6
 * @author    Vanvelthem Sébastien
7
 * @link      https://github.com/belgattitude/soluble-flexstore
8
 * @copyright Copyright (c) 2016-2017 Vanvelthem Sébastien
9
 * @license   MIT License https://github.com/belgattitude/soluble-flexstore/blob/master/LICENSE.md
10
 *
11
 */
12
13
namespace Soluble\FlexStore\Writer;
14
15
use Soluble\FlexStore\Writer\Http\SimpleHeaders;
16
use Soluble\FlexStore\Options;
17
18
class CSVWriter extends AbstractSendableWriter
19
{
20
    const SEPARATOR_TAB = "\t";
21
    const SEPARATOR_COMMA = ',';
22
    const SEPARATOR_SEMICOLON = ';';
23
    const SEPARATOR_NEWLINE_UNIX = "\n";
24
    const SEPARATOR_NEWLINE_WIN = "\r\n";
25
26
    /**
27
     * @var SimpleHeaders
28
     */
29
    protected $headers;
30
31
    /**
32
     * @var array
33
     */
34
    protected $options = [
35
        'field_separator' => ';',
36
        'line_separator' => "\n",
37
        'enclosure' => '',
38
        'charset' => 'UTF-8',
39
        'escape' => '\\',
40
        // ignore charset transliteration errors
41
        'ignore_translit_error' => false
42
    ];
43
44
    /**
45
     * @throws Exception\CharsetConversionException
46
     *
47
     * @param Options $options
48
     *
49
     * @return string csv encoded data
50
     */
51 8
    public function getData(Options $options = null)
52
    {
53 8
        if ($options === null) {
54
            // Take store global/default options
55 7
            $options = $this->store->getOptions();
56
        }
57
58
        // TODO - TEST database connection charset !!!
59
        //
60
61 8
        ini_set('default_charset', 'UTF-8');
62
63 8
        if (PHP_VERSION_ID < 50600) {
64
            iconv_set_encoding('internal_encoding', 'UTF-8');
65
        }
66
67
        /*
68
          $backup_encoding = iconv_get_encoding("internal_encoding");
69
          iconv_set_encoding("internal_encoding", "UTF-8");
70
          iconv_set_encoding("input_encoding", "UTF-8");
71
          iconv_set_encoding("output_encoding", "UTF-8");
72
          mb_internal_encoding("UTF-8");
73
         */
74
75 8
        $internal_encoding = strtoupper(ini_get('default_charset'));
76 8
        $charset = strtoupper($this->options['charset']);
77
78 8
        $csv = '';
79
80
        // Get unformatted data when using csv writer
81 8
        $options->getHydrationOptions()->disableFormatters();
82 8
        $data = $this->store->getData($options)->toArray();
83
84 8
        if (strtoupper($this->options['charset']) !== $charset && !function_exists('iconv')) {
85
            throw new Exception\RuntimeException('CSV writer requires iconv extension');
86
        }
87
88 8
        $iconv_output_charset = $this->options['charset'];
89 8
        if ($this->options['ignore_translit_error']) {
90 1
            $iconv_output_charset .= '//TRANSLIT//IGNORE';
91
        }
92
93 8
        if (count($data) === 0) {
94 1
            $columns = $this->store->getColumnModel()->getColumns();
95 1
            $header_line = implode($this->options['field_separator'], array_keys((array) $columns));
96 1
            $csv .= $header_line . $this->options['line_separator'];
97
        } else {
98 7
            $header_line = implode($this->options['field_separator'], array_keys($data[0]));
99 7
            $csv .= $header_line . $this->options['line_separator'];
100
101 7
            foreach ($data as $row) {
102 7
                switch ($this->options['field_separator']) {
103 7
                    case self::SEPARATOR_TAB:
104 2
                        array_walk($row, [$this, 'escapeTabDelimiter']);
105 2
                        break;
106
                    default:
107 5
                        array_walk($row, [$this, 'escapeFieldDelimiter']);
108
                }
109
110 7
                array_walk($row, [$this, 'escapeLineDelimiter']);
111
112 7
                if ($this->options['enclosure'] !== '') {
113 5
                    array_walk($row, [$this, 'addEnclosure']);
114
                }
115
116 7
                $line = implode($this->options['field_separator'], $row);
117
118 7
                if ($charset !== $internal_encoding) {
119 5
                    $l = (string) $line;
120 5
                    if ($l !== '') {
121 5
                        $l = @iconv($internal_encoding, $iconv_output_charset, $l);
122 5
                        if ($l === false || strlen($l) === 0) {
123 2
                            throw new Exception\CharsetConversionException("Cannot convert the charset to '" . $this->options['charset'] . "' from charset '$internal_encoding', using '$iconv_output_charset' value: '$line'.");
124
                        } else {
125 4
                            $line = $l;
126
                        }
127
                    }
128
                }
129
130 6
                $csv .= $line . $this->options['line_separator'];
131
            }
132
        }
133
134 7
        return $csv;
135
    }
136
137
    /**
138
     * @param string $item
139
     */
140 7
    protected function escapeLineDelimiter(&$item)
141
    {
142 7
        $item = str_replace(self::SEPARATOR_NEWLINE_WIN, ' ', $item);
143 7
        $item = str_replace(self::SEPARATOR_NEWLINE_UNIX, ' ', $item);
144 7
    }
145
146
    /**
147
     * @param string $item
148
     */
149 2
    protected function escapeTabDelimiter(&$item)
150
    {
151 2
        $item = str_replace("\t", ' ', $item);
152 2
    }
153
154
    /**
155
     * @param string $item
156
     */
157 5
    protected function escapeFieldDelimiter(&$item)
158
    {
159 5
        $item = str_replace($this->options['field_separator'], $this->options['escape'] . $this->options['field_separator'], $item);
160 5
    }
161
162
    /**
163
     * @param string $item
164
     * @param string $key
165
     */
166 5
    protected function addEnclosure(&$item, $key)
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
167
    {
168 5
        $enc = $this->options['enclosure'];
169 5
        $escape = $this->options['escape'];
170 5
        if ($escape === '') {
171 1
            $item = $enc . str_replace($enc, '', $item) . $enc;
172
        } else {
173 4
            $item = $enc . str_replace($enc, $escape . $enc, $item) . $enc;
174
        }
175 5
    }
176
177
    /**
178
     * Return default headers for sending store data via http.
179
     *
180
     * @return SimpleHeaders
181
     */
182 3
    public function getHttpHeaders()
183
    {
184 3
        if ($this->headers === null) {
185 3
            $this->headers = new SimpleHeaders();
186 3
            $this->headers->setContentType('text/csv', $this->options['charset']);
187
            //$this->headers->setContentDispositionType(SimpleHeaders::DIPOSITION_ATTACHEMENT);
188
        }
189
190 3
        return $this->headers;
191
    }
192
}
193