1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PhpOffice\PhpSpreadsheet\Reader; |
4
|
|
|
|
5
|
|
|
use PhpOffice\PhpSpreadsheet\Calculation\Calculation; |
6
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\Cell; |
7
|
|
|
use PhpOffice\PhpSpreadsheet\Shared\StringHelper; |
8
|
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet; |
9
|
|
|
use PhpOffice\PhpSpreadsheet\Style\Border; |
10
|
|
|
|
11
|
|
|
class Slk extends BaseReader implements IReader |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* Input encoding. |
15
|
|
|
* |
16
|
|
|
* @var string |
17
|
|
|
*/ |
18
|
|
|
private $inputEncoding = 'ANSI'; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Sheet index to read. |
22
|
|
|
* |
23
|
|
|
* @var int |
24
|
|
|
*/ |
25
|
|
|
private $sheetIndex = 0; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Formats. |
29
|
|
|
* |
30
|
|
|
* @var array |
31
|
|
|
*/ |
32
|
|
|
private $formats = []; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Format Count. |
36
|
|
|
* |
37
|
|
|
* @var int |
38
|
|
|
*/ |
39
|
|
|
private $format = 0; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Create a new SYLK Reader instance. |
43
|
|
|
*/ |
44
|
4 |
|
public function __construct() |
45
|
|
|
{ |
46
|
4 |
|
$this->readFilter = new DefaultReadFilter(); |
47
|
4 |
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Validate that the current file is a SYLK file. |
51
|
|
|
* |
52
|
|
|
* @param string $pFilename |
53
|
|
|
* |
54
|
|
|
* @throws Exception |
55
|
|
|
* |
56
|
|
|
* @return bool |
57
|
|
|
*/ |
58
|
3 |
|
public function canRead($pFilename) |
59
|
|
|
{ |
60
|
|
|
// Check if file exists |
61
|
|
|
try { |
62
|
3 |
|
$this->openFile($pFilename); |
63
|
|
|
} catch (Exception $e) { |
64
|
|
|
return false; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
// Read sample data (first 2 KB will do) |
68
|
3 |
|
$data = fread($this->fileHandle, 2048); |
69
|
|
|
|
70
|
|
|
// Count delimiters in file |
71
|
3 |
|
$delimiterCount = substr_count($data, ';'); |
72
|
3 |
|
$hasDelimiter = $delimiterCount > 0; |
73
|
|
|
|
74
|
|
|
// Analyze first line looking for ID; signature |
75
|
3 |
|
$lines = explode("\n", $data); |
76
|
3 |
|
$hasId = substr($lines[0], 0, 4) === 'ID;P'; |
77
|
|
|
|
78
|
3 |
|
fclose($this->fileHandle); |
79
|
|
|
|
80
|
3 |
|
return $hasDelimiter && $hasId; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Set input encoding. |
85
|
|
|
* |
86
|
|
|
* @param string $pValue Input encoding, eg: 'ANSI' |
87
|
|
|
*/ |
88
|
|
|
public function setInputEncoding($pValue) |
89
|
|
|
{ |
90
|
|
|
$this->inputEncoding = $pValue; |
91
|
|
|
|
92
|
|
|
return $this; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Get input encoding. |
97
|
|
|
* |
98
|
|
|
* @return string |
99
|
|
|
*/ |
100
|
|
|
public function getInputEncoding() |
101
|
|
|
{ |
102
|
|
|
return $this->inputEncoding; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns). |
107
|
|
|
* |
108
|
|
|
* @param string $pFilename |
109
|
|
|
* |
110
|
|
|
* @throws Exception |
111
|
|
|
*/ |
112
|
|
|
public function listWorksheetInfo($pFilename) |
113
|
|
|
{ |
114
|
|
|
// Open file |
115
|
|
|
if (!$this->canRead($pFilename)) { |
116
|
|
|
throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); |
117
|
|
|
} |
118
|
|
|
$this->openFile($pFilename); |
119
|
|
|
$fileHandle = $this->fileHandle; |
120
|
|
|
rewind($fileHandle); |
121
|
|
|
|
122
|
|
|
$worksheetInfo = []; |
123
|
|
|
$worksheetInfo[0]['worksheetName'] = 'Worksheet'; |
124
|
|
|
$worksheetInfo[0]['lastColumnLetter'] = 'A'; |
125
|
|
|
$worksheetInfo[0]['lastColumnIndex'] = 0; |
126
|
|
|
$worksheetInfo[0]['totalRows'] = 0; |
127
|
|
|
$worksheetInfo[0]['totalColumns'] = 0; |
128
|
|
|
|
129
|
|
|
// loop through one row (line) at a time in the file |
130
|
|
|
$rowIndex = 0; |
131
|
|
|
while (($rowData = fgets($fileHandle)) !== false) { |
132
|
|
|
$columnIndex = 0; |
133
|
|
|
|
134
|
|
|
// convert SYLK encoded $rowData to UTF-8 |
135
|
|
|
$rowData = StringHelper::SYLKtoUTF8($rowData); |
136
|
|
|
|
137
|
|
|
// explode each row at semicolons while taking into account that literal semicolon (;) |
138
|
|
|
// is escaped like this (;;) |
139
|
|
|
$rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData))))); |
140
|
|
|
|
141
|
|
|
$dataType = array_shift($rowData); |
142
|
|
|
if ($dataType == 'C') { |
143
|
|
|
// Read cell value data |
144
|
|
|
foreach ($rowData as $rowDatum) { |
145
|
|
View Code Duplication |
switch ($rowDatum[0]) { |
|
|
|
|
146
|
|
|
case 'C': |
147
|
|
|
case 'X': |
148
|
|
|
$columnIndex = substr($rowDatum, 1) - 1; |
149
|
|
|
|
150
|
|
|
break; |
151
|
|
|
case 'R': |
152
|
|
|
case 'Y': |
153
|
|
|
$rowIndex = substr($rowDatum, 1); |
154
|
|
|
|
155
|
|
|
break; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
$worksheetInfo[0]['totalRows'] = max($worksheetInfo[0]['totalRows'], $rowIndex); |
159
|
|
|
$worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], $columnIndex); |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
$worksheetInfo[0]['lastColumnLetter'] = Cell::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex']); |
165
|
|
|
$worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1; |
166
|
|
|
|
167
|
|
|
// Close file |
168
|
|
|
fclose($fileHandle); |
169
|
|
|
|
170
|
|
|
return $worksheetInfo; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Loads PhpSpreadsheet from file. |
175
|
|
|
* |
176
|
|
|
* @param string $pFilename |
177
|
|
|
* |
178
|
|
|
* @throws Exception |
179
|
|
|
* |
180
|
|
|
* @return Spreadsheet |
181
|
|
|
*/ |
182
|
1 |
|
public function load($pFilename) |
183
|
|
|
{ |
184
|
|
|
// Create new Spreadsheet |
185
|
1 |
|
$spreadsheet = new Spreadsheet(); |
186
|
|
|
|
187
|
|
|
// Load into this instance |
188
|
1 |
|
return $this->loadIntoExisting($pFilename, $spreadsheet); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance. |
193
|
|
|
* |
194
|
|
|
* @param string $pFilename |
195
|
|
|
* @param Spreadsheet $spreadsheet |
196
|
|
|
* |
197
|
|
|
* @throws Exception |
198
|
|
|
* |
199
|
|
|
* @return Spreadsheet |
200
|
|
|
*/ |
201
|
1 |
|
public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet) |
202
|
|
|
{ |
203
|
|
|
// Open file |
204
|
1 |
|
if (!$this->canRead($pFilename)) { |
205
|
|
|
throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); |
206
|
|
|
} |
207
|
1 |
|
$this->openFile($pFilename); |
208
|
1 |
|
$fileHandle = $this->fileHandle; |
209
|
1 |
|
rewind($fileHandle); |
210
|
|
|
|
211
|
|
|
// Create new Worksheets |
212
|
1 |
|
while ($spreadsheet->getSheetCount() <= $this->sheetIndex) { |
213
|
|
|
$spreadsheet->createSheet(); |
214
|
|
|
} |
215
|
1 |
|
$spreadsheet->setActiveSheetIndex($this->sheetIndex); |
216
|
|
|
|
217
|
1 |
|
$fromFormats = ['\-', '\ ']; |
218
|
1 |
|
$toFormats = ['-', ' ']; |
219
|
|
|
|
220
|
|
|
// Loop through file |
221
|
1 |
|
$column = $row = ''; |
222
|
|
|
|
223
|
|
|
// loop through one row (line) at a time in the file |
224
|
1 |
|
while (($rowData = fgets($fileHandle)) !== false) { |
225
|
|
|
// convert SYLK encoded $rowData to UTF-8 |
226
|
1 |
|
$rowData = StringHelper::SYLKtoUTF8($rowData); |
227
|
|
|
|
228
|
|
|
// explode each row at semicolons while taking into account that literal semicolon (;) |
229
|
|
|
// is escaped like this (;;) |
230
|
1 |
|
$rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData))))); |
231
|
|
|
|
232
|
1 |
|
$dataType = array_shift($rowData); |
233
|
|
|
// Read shared styles |
234
|
1 |
|
if ($dataType == 'P') { |
235
|
1 |
|
$formatArray = []; |
236
|
1 |
|
foreach ($rowData as $rowDatum) { |
237
|
1 |
|
switch ($rowDatum[0]) { |
238
|
1 |
|
case 'P': |
239
|
1 |
|
$formatArray['numberFormat']['formatCode'] = str_replace($fromFormats, $toFormats, substr($rowDatum, 1)); |
240
|
|
|
|
241
|
1 |
|
break; |
242
|
1 |
|
case 'E': |
243
|
1 |
|
case 'F': |
244
|
1 |
|
$formatArray['font']['name'] = substr($rowDatum, 1); |
245
|
|
|
|
246
|
1 |
|
break; |
247
|
1 |
|
case 'L': |
248
|
1 |
|
$formatArray['font']['size'] = substr($rowDatum, 1); |
249
|
|
|
|
250
|
1 |
|
break; |
251
|
1 |
View Code Duplication |
case 'S': |
|
|
|
|
252
|
1 |
|
$styleSettings = substr($rowDatum, 1); |
253
|
1 |
|
for ($i = 0; $i < strlen($styleSettings); ++$i) { |
254
|
1 |
|
switch ($styleSettings[$i]) { |
255
|
1 |
|
case 'I': |
256
|
1 |
|
$formatArray['font']['italic'] = true; |
257
|
|
|
|
258
|
1 |
|
break; |
259
|
1 |
|
case 'D': |
260
|
|
|
$formatArray['font']['bold'] = true; |
261
|
|
|
|
262
|
|
|
break; |
263
|
1 |
|
case 'T': |
264
|
|
|
$formatArray['borders']['top']['borderStyle'] = Border::BORDER_THIN; |
265
|
|
|
|
266
|
|
|
break; |
267
|
1 |
|
case 'B': |
268
|
1 |
|
$formatArray['borders']['bottom']['borderStyle'] = Border::BORDER_THIN; |
269
|
|
|
|
270
|
1 |
|
break; |
271
|
1 |
|
case 'L': |
272
|
|
|
$formatArray['borders']['left']['borderStyle'] = Border::BORDER_THIN; |
273
|
|
|
|
274
|
|
|
break; |
275
|
1 |
|
case 'R': |
276
|
|
|
$formatArray['borders']['right']['borderStyle'] = Border::BORDER_THIN; |
277
|
|
|
|
278
|
|
|
break; |
279
|
|
|
} |
280
|
|
|
} |
281
|
|
|
|
282
|
1 |
|
break; |
283
|
|
|
} |
284
|
|
|
} |
285
|
1 |
|
$this->formats['P' . $this->format++] = $formatArray; |
286
|
|
|
// Read cell value data |
287
|
1 |
|
} elseif ($dataType == 'C') { |
288
|
1 |
|
$hasCalculatedValue = false; |
289
|
1 |
|
$cellData = $cellDataFormula = ''; |
290
|
1 |
|
foreach ($rowData as $rowDatum) { |
291
|
1 |
|
switch ($rowDatum[0]) { |
292
|
1 |
|
case 'C': |
293
|
1 |
|
case 'X': |
294
|
1 |
|
$column = substr($rowDatum, 1); |
295
|
|
|
|
296
|
1 |
|
break; |
297
|
1 |
|
case 'R': |
298
|
1 |
|
case 'Y': |
299
|
1 |
|
$row = substr($rowDatum, 1); |
300
|
|
|
|
301
|
1 |
|
break; |
302
|
1 |
|
case 'K': |
303
|
1 |
|
$cellData = substr($rowDatum, 1); |
304
|
|
|
|
305
|
1 |
|
break; |
306
|
1 |
|
case 'E': |
307
|
1 |
|
$cellDataFormula = '=' . substr($rowDatum, 1); |
308
|
|
|
// Convert R1C1 style references to A1 style references (but only when not quoted) |
309
|
1 |
|
$temp = explode('"', $cellDataFormula); |
310
|
1 |
|
$key = false; |
311
|
1 |
View Code Duplication |
foreach ($temp as &$value) { |
|
|
|
|
312
|
|
|
// Only count/replace in alternate array entries |
313
|
1 |
|
if ($key = !$key) { |
314
|
1 |
|
preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); |
315
|
|
|
// Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way |
316
|
|
|
// through the formula from left to right. Reversing means that we work right to left.through |
317
|
|
|
// the formula |
318
|
1 |
|
$cellReferences = array_reverse($cellReferences); |
319
|
|
|
// Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent, |
320
|
|
|
// then modify the formula to use that new reference |
321
|
1 |
|
foreach ($cellReferences as $cellReference) { |
322
|
1 |
|
$rowReference = $cellReference[2][0]; |
323
|
|
|
// Empty R reference is the current row |
324
|
1 |
|
if ($rowReference == '') { |
325
|
1 |
|
$rowReference = $row; |
326
|
|
|
} |
327
|
|
|
// Bracketed R references are relative to the current row |
328
|
1 |
|
if ($rowReference[0] == '[') { |
329
|
1 |
|
$rowReference = $row + trim($rowReference, '[]'); |
330
|
|
|
} |
331
|
1 |
|
$columnReference = $cellReference[4][0]; |
332
|
|
|
// Empty C reference is the current column |
333
|
1 |
|
if ($columnReference == '') { |
334
|
|
|
$columnReference = $column; |
335
|
|
|
} |
336
|
|
|
// Bracketed C references are relative to the current column |
337
|
1 |
|
if ($columnReference[0] == '[') { |
338
|
1 |
|
$columnReference = $column + trim($columnReference, '[]'); |
339
|
|
|
} |
340
|
1 |
|
$A1CellReference = Cell::stringFromColumnIndex($columnReference - 1) . $rowReference; |
341
|
|
|
|
342
|
1 |
|
$value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0])); |
343
|
|
|
} |
344
|
|
|
} |
345
|
|
|
} |
346
|
1 |
|
unset($value); |
347
|
|
|
// Then rebuild the formula string |
348
|
1 |
|
$cellDataFormula = implode('"', $temp); |
349
|
1 |
|
$hasCalculatedValue = true; |
350
|
|
|
|
351
|
1 |
|
break; |
352
|
|
|
} |
353
|
|
|
} |
354
|
1 |
|
$columnLetter = Cell::stringFromColumnIndex($column - 1); |
355
|
1 |
|
$cellData = Calculation::unwrapResult($cellData); |
356
|
|
|
|
357
|
|
|
// Set cell value |
358
|
1 |
|
$spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData); |
359
|
1 |
|
if ($hasCalculatedValue) { |
360
|
1 |
|
$cellData = Calculation::unwrapResult($cellData); |
361
|
1 |
|
$spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setCalculatedValue($cellData); |
362
|
|
|
} |
363
|
|
|
// Read cell formatting |
364
|
1 |
|
} elseif ($dataType == 'F') { |
365
|
1 |
|
$formatStyle = $columnWidth = $styleSettings = ''; |
|
|
|
|
366
|
1 |
|
$styleData = []; |
367
|
1 |
|
foreach ($rowData as $rowDatum) { |
368
|
1 |
|
switch ($rowDatum[0]) { |
369
|
1 |
|
case 'C': |
370
|
1 |
|
case 'X': |
371
|
1 |
|
$column = substr($rowDatum, 1); |
372
|
|
|
|
373
|
1 |
|
break; |
374
|
1 |
|
case 'R': |
375
|
1 |
|
case 'Y': |
376
|
1 |
|
$row = substr($rowDatum, 1); |
377
|
|
|
|
378
|
1 |
|
break; |
379
|
1 |
|
case 'P': |
380
|
1 |
|
$formatStyle = $rowDatum; |
381
|
|
|
|
382
|
1 |
|
break; |
383
|
1 |
|
case 'W': |
384
|
1 |
|
list($startCol, $endCol, $columnWidth) = explode(' ', substr($rowDatum, 1)); |
385
|
|
|
|
386
|
1 |
|
break; |
387
|
1 |
View Code Duplication |
case 'S': |
|
|
|
|
388
|
1 |
|
$styleSettings = substr($rowDatum, 1); |
389
|
1 |
|
for ($i = 0; $i < strlen($styleSettings); ++$i) { |
390
|
1 |
|
switch ($styleSettings[$i]) { |
391
|
1 |
|
case 'I': |
392
|
1 |
|
$styleData['font']['italic'] = true; |
393
|
|
|
|
394
|
1 |
|
break; |
395
|
1 |
|
case 'D': |
396
|
1 |
|
$styleData['font']['bold'] = true; |
397
|
|
|
|
398
|
1 |
|
break; |
399
|
1 |
|
case 'T': |
400
|
1 |
|
$styleData['borders']['top']['borderStyle'] = Border::BORDER_THIN; |
401
|
|
|
|
402
|
1 |
|
break; |
403
|
1 |
|
case 'B': |
404
|
1 |
|
$styleData['borders']['bottom']['borderStyle'] = Border::BORDER_THIN; |
405
|
|
|
|
406
|
1 |
|
break; |
407
|
1 |
|
case 'L': |
408
|
1 |
|
$styleData['borders']['left']['borderStyle'] = Border::BORDER_THIN; |
409
|
|
|
|
410
|
1 |
|
break; |
411
|
1 |
|
case 'R': |
412
|
1 |
|
$styleData['borders']['right']['borderStyle'] = Border::BORDER_THIN; |
413
|
|
|
|
414
|
1 |
|
break; |
415
|
|
|
} |
416
|
|
|
} |
417
|
|
|
|
418
|
1 |
|
break; |
419
|
|
|
} |
420
|
|
|
} |
421
|
1 |
|
if (($formatStyle > '') && ($column > '') && ($row > '')) { |
422
|
1 |
|
$columnLetter = Cell::stringFromColumnIndex($column - 1); |
423
|
1 |
View Code Duplication |
if (isset($this->formats[$formatStyle])) { |
|
|
|
|
424
|
1 |
|
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->formats[$formatStyle]); |
425
|
|
|
} |
426
|
|
|
} |
427
|
1 |
|
if ((!empty($styleData)) && ($column > '') && ($row > '')) { |
428
|
1 |
|
$columnLetter = Cell::stringFromColumnIndex($column - 1); |
429
|
1 |
|
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData); |
430
|
|
|
} |
431
|
1 |
|
if ($columnWidth > '') { |
432
|
1 |
|
if ($startCol == $endCol) { |
433
|
1 |
|
$startCol = Cell::stringFromColumnIndex($startCol - 1); |
|
|
|
|
434
|
1 |
|
$spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth); |
435
|
|
|
} else { |
436
|
1 |
|
$startCol = Cell::stringFromColumnIndex($startCol - 1); |
437
|
1 |
|
$endCol = Cell::stringFromColumnIndex($endCol - 1); |
|
|
|
|
438
|
1 |
|
$spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth); |
439
|
|
|
do { |
440
|
1 |
|
$spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth($columnWidth); |
441
|
1 |
|
} while ($startCol != $endCol); |
442
|
|
|
} |
443
|
|
|
} |
444
|
|
|
} else { |
445
|
1 |
|
foreach ($rowData as $rowDatum) { |
446
|
1 |
View Code Duplication |
switch ($rowDatum[0]) { |
|
|
|
|
447
|
1 |
|
case 'C': |
448
|
1 |
|
case 'X': |
449
|
1 |
|
$column = substr($rowDatum, 1); |
450
|
|
|
|
451
|
1 |
|
break; |
452
|
1 |
|
case 'R': |
453
|
1 |
|
case 'Y': |
454
|
1 |
|
$row = substr($rowDatum, 1); |
455
|
|
|
|
456
|
1 |
|
break; |
457
|
|
|
} |
458
|
|
|
} |
459
|
|
|
} |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
// Close file |
463
|
1 |
|
fclose($fileHandle); |
464
|
|
|
|
465
|
|
|
// Return |
466
|
1 |
|
return $spreadsheet; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
/** |
470
|
|
|
* Get sheet index. |
471
|
|
|
* |
472
|
|
|
* @return int |
473
|
|
|
*/ |
474
|
|
|
public function getSheetIndex() |
475
|
|
|
{ |
476
|
|
|
return $this->sheetIndex; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
/** |
480
|
|
|
* Set sheet index. |
481
|
|
|
* |
482
|
|
|
* @param int $pValue Sheet index |
483
|
|
|
* |
484
|
|
|
* @return Slk |
485
|
|
|
*/ |
486
|
|
|
public function setSheetIndex($pValue) |
487
|
|
|
{ |
488
|
|
|
$this->sheetIndex = $pValue; |
489
|
|
|
|
490
|
|
|
return $this; |
491
|
|
|
} |
492
|
|
|
} |
493
|
|
|
|
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.