Completed
Push — develop ( b7b106...b01671 )
by Adrien
16:54
created

Csv::getEnclosure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 2
cp 0
crap 2
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader;
4
5
use PhpOffice\PhpSpreadsheet\Spreadsheet;
6
7
/**
8
 * Copyright (c) 2006 - 2016 PhpSpreadsheet.
9
 *
10
 * This library is free software; you can redistribute it and/or
11
 * modify it under the terms of the GNU Lesser General Public
12
 * License as published by the Free Software Foundation; either
13
 * version 2.1 of the License, or (at your option) any later version.
14
 *
15
 * This library is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
 * Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public
21
 * License along with this library; if not, write to the Free Software
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
 *
24
 * @category   PhpSpreadsheet
25
 *
26
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
27
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
28
 */
29
class Csv extends BaseReader implements IReader
30
{
31
    /**
32
     * Input encoding.
33
     *
34
     * @var string
35
     */
36
    private $inputEncoding = 'UTF-8';
37
38
    /**
39
     * Delimiter.
40
     *
41
     * @var string
42
     */
43
    private $delimiter = ',';
44
45
    /**
46
     * Enclosure.
47
     *
48
     * @var string
49
     */
50
    private $enclosure = '"';
51
52
    /**
53
     * Sheet index to read.
54
     *
55
     * @var int
56
     */
57
    private $sheetIndex = 0;
58
59
    /**
60
     * Load rows contiguously.
61
     *
62
     * @var bool
63
     */
64
    private $contiguous = false;
65
66
    /**
67
     * Row counter for loading rows contiguously.
68
     *
69
     * @var int
70
     */
71
    private $contiguousRow = -1;
72
73
    /**
74
     * Create a new CSV Reader instance.
75
     */
76 2
    public function __construct()
77
    {
78 2
        $this->readFilter = new DefaultReadFilter();
79 2
    }
80
81
    /**
82
     * Set input encoding.
83
     *
84
     * @param string $pValue Input encoding
85
     */
86
    public function setInputEncoding($pValue = 'UTF-8')
87
    {
88
        $this->inputEncoding = $pValue;
89
90
        return $this;
91
    }
92
93
    /**
94
     * Get input encoding.
95
     *
96
     * @return string
97
     */
98
    public function getInputEncoding()
99
    {
100
        return $this->inputEncoding;
101
    }
102
103
    /**
104
     * Move filepointer past any BOM marker.
105
     */
106 2
    protected function skipBOM()
107
    {
108 2
        rewind($this->fileHandle);
109
110 2
        switch ($this->inputEncoding) {
111 2
            case 'UTF-8':
112 2
                fgets($this->fileHandle, 4) == "\xEF\xBB\xBF" ?
113 2
                    fseek($this->fileHandle, 3) : fseek($this->fileHandle, 0);
114 2
                break;
115 View Code Duplication
            case 'UTF-16LE':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
116
                fgets($this->fileHandle, 3) == "\xFF\xFE" ?
117
                    fseek($this->fileHandle, 2) : fseek($this->fileHandle, 0);
118
                break;
119 View Code Duplication
            case 'UTF-16BE':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
120
                fgets($this->fileHandle, 3) == "\xFE\xFF" ?
121
                    fseek($this->fileHandle, 2) : fseek($this->fileHandle, 0);
122
                break;
123 View Code Duplication
            case 'UTF-32LE':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
                fgets($this->fileHandle, 5) == "\xFF\xFE\x00\x00" ?
125
                    fseek($this->fileHandle, 4) : fseek($this->fileHandle, 0);
126
                break;
127 View Code Duplication
            case 'UTF-32BE':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128
                fgets($this->fileHandle, 5) == "\x00\x00\xFE\xFF" ?
129
                    fseek($this->fileHandle, 4) : fseek($this->fileHandle, 0);
130
                break;
131
            default:
132
                break;
133
        }
134 2
    }
135
136
    /**
137
     * Identify any separator that is explicitly set in the file.
138
     */
139 2
    protected function checkSeparator()
140
    {
141 2
        $line = fgets($this->fileHandle);
142 2
        if ($line === false) {
143
            return;
144
        }
145
146 2
        if ((strlen(trim($line, "\r\n")) == 5) && (stripos($line, 'sep=') === 0)) {
147
            $this->delimiter = substr($line, 4, 1);
148
149
            return;
150
        }
151
152 2
        return $this->skipBOM();
153
    }
154
155
    /**
156
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
157
     *
158
     * @param string $pFilename
159
     *
160
     * @throws Exception
161
     */
162
    public function listWorksheetInfo($pFilename)
163
    {
164
        // Open file
165
        if (!$this->canRead($pFilename)) {
166
            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
167
        }
168
        $this->openFile($pFilename);
169
        $fileHandle = $this->fileHandle;
170
171
        // Skip BOM, if any
172
        $this->skipBOM();
173
        $this->checkSeparator();
174
175
        $worksheetInfo = [];
176
        $worksheetInfo[0]['worksheetName'] = 'Worksheet';
177
        $worksheetInfo[0]['lastColumnLetter'] = 'A';
178
        $worksheetInfo[0]['lastColumnIndex'] = 0;
179
        $worksheetInfo[0]['totalRows'] = 0;
180
        $worksheetInfo[0]['totalColumns'] = 0;
181
182
        // Loop through each line of the file in turn
183
        while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure)) !== false) {
184
            ++$worksheetInfo[0]['totalRows'];
185
            $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], count($rowData) - 1);
186
        }
187
188
        $worksheetInfo[0]['lastColumnLetter'] = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex']);
189
        $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
190
191
        // Close file
192
        fclose($fileHandle);
193
194
        return $worksheetInfo;
195
    }
196
197
    /**
198
     * Loads Spreadsheet from file.
199
     *
200
     * @param string $pFilename
201
     *
202
     * @throws Exception
203
     *
204
     * @return \PhpOffice\PhpSpreadsheet\Spreadsheet
205
     */
206 2
    public function load($pFilename)
207
    {
208
        // Create new Spreadsheet
209 2
        $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
210
211
        // Load into this instance
212 2
        return $this->loadIntoExisting($pFilename, $spreadsheet);
213
    }
214
215
    /**
216
     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
217
     *
218
     * @param string $pFilename
219
     * @param Spreadsheet $spreadsheet
220
     *
221
     * @throws Exception
222
     *
223
     * @return Spreadsheet
224
     */
225 2
    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
226
    {
227 2
        $lineEnding = ini_get('auto_detect_line_endings');
228 2
        ini_set('auto_detect_line_endings', true);
229
230
        // Open file
231 2
        if (!$this->canRead($pFilename)) {
232
            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
233
        }
234 2
        $this->openFile($pFilename);
235 2
        $fileHandle = $this->fileHandle;
236
237
        // Skip BOM, if any
238 2
        $this->skipBOM();
239 2
        $this->checkSeparator();
240
241
        // Create new PhpSpreadsheet object
242 2
        while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
243
            $spreadsheet->createSheet();
244
        }
245 2
        $sheet = $spreadsheet->setActiveSheetIndex($this->sheetIndex);
246
247
        // Set our starting row based on whether we're in contiguous mode or not
248 2
        $currentRow = 1;
249 2
        if ($this->contiguous) {
250
            $currentRow = ($this->contiguousRow == -1) ? $sheet->getHighestRow() : $this->contiguousRow;
251
        }
252
253
        // Loop through each line of the file in turn
254 2
        while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure)) !== false) {
255 2
            $columnLetter = 'A';
256 2
            foreach ($rowData as $rowDatum) {
257 2
                if ($rowDatum != '' && $this->readFilter->readCell($columnLetter, $currentRow)) {
258
                    // Convert encoding if necessary
259 2
                    if ($this->inputEncoding !== 'UTF-8') {
260
                        $rowDatum = \PhpOffice\PhpSpreadsheet\Shared\StringHelper::convertEncoding($rowDatum, 'UTF-8', $this->inputEncoding);
261
                    }
262
263
                    // Set cell value
264 2
                    $sheet->getCell($columnLetter . $currentRow)->setValue($rowDatum);
265
                }
266 2
                ++$columnLetter;
267
            }
268 2
            ++$currentRow;
269
        }
270
271
        // Close file
272 2
        fclose($fileHandle);
273
274 2
        if ($this->contiguous) {
275
            $this->contiguousRow = $currentRow;
0 ignored issues
show
Documentation Bug introduced by
It seems like $currentRow can also be of type string. However, the property $contiguousRow is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
276
        }
277
278 2
        ini_set('auto_detect_line_endings', $lineEnding);
279
280
        // Return
281 2
        return $spreadsheet;
282
    }
283
284
    /**
285
     * Get delimiter.
286
     *
287
     * @return string
288
     */
289
    public function getDelimiter()
290
    {
291
        return $this->delimiter;
292
    }
293
294
    /**
295
     * Set delimiter.
296
     *
297
     * @param string $pValue Delimiter, defaults to ,
298
     *
299
     * @return CSV
300
     */
301 1
    public function setDelimiter($pValue = ',')
302
    {
303 1
        $this->delimiter = $pValue;
304
305 1
        return $this;
306
    }
307
308
    /**
309
     * Get enclosure.
310
     *
311
     * @return string
312
     */
313
    public function getEnclosure()
314
    {
315
        return $this->enclosure;
316
    }
317
318
    /**
319
     * Set enclosure.
320
     *
321
     * @param string $pValue Enclosure, defaults to "
322
     *
323
     * @return CSV
324
     */
325 1
    public function setEnclosure($pValue = '"')
326
    {
327 1
        if ($pValue == '') {
328
            $pValue = '"';
329
        }
330 1
        $this->enclosure = $pValue;
331
332 1
        return $this;
333
    }
334
335
    /**
336
     * Get sheet index.
337
     *
338
     * @return int
339
     */
340
    public function getSheetIndex()
341
    {
342
        return $this->sheetIndex;
343
    }
344
345
    /**
346
     * Set sheet index.
347
     *
348
     * @param int $pValue Sheet index
349
     *
350
     * @return CSV
351
     */
352 1
    public function setSheetIndex($pValue = 0)
353
    {
354 1
        $this->sheetIndex = $pValue;
355
356 1
        return $this;
357
    }
358
359
    /**
360
     * Set Contiguous.
361
     *
362
     * @param bool $contiguous
363
     */
364
    public function setContiguous($contiguous = false)
365
    {
366
        $this->contiguous = (bool) $contiguous;
367
        if (!$contiguous) {
368
            $this->contiguousRow = -1;
369
        }
370
371
        return $this;
372
    }
373
374
    /**
375
     * Get Contiguous.
376
     *
377
     * @return bool
378
     */
379
    public function getContiguous()
380
    {
381
        return $this->contiguous;
382
    }
383
384
    /**
385
     * Can the current IReader read the file?
386
     *
387
     * @param string $pFilename
388
     *
389
     * @throws Exception
390
     *
391
     * @return bool
392
     */
393 2
    public function canRead($pFilename)
394
    {
395
        // Check if file exists
396
        try {
397 2
            $this->openFile($pFilename);
398
        } catch (Exception $e) {
399
            return false;
400
        }
401
402 2
        fclose($this->fileHandle);
403
404 2
        return true;
405
    }
406
}
407