Completed
Push — develop ( 782b4e...557e80 )
by Adrien
43:38
created

Gnumeric   F

Complexity

Total Complexity 196

Size/Duplication

Total Lines 868
Duplicated Lines 3.92 %

Coupling/Cohesion

Components 1
Dependencies 17

Test Coverage

Coverage 76.27%

Importance

Changes 0
Metric Value
dl 34
loc 868
ccs 405
cts 531
cp 0.7627
rs 1.263
c 0
b 0
f 0
wmc 196
lcom 1
cbo 17

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A canRead() 0 20 3
B listWorksheetNames() 7 21 5
C listWorksheetInfo() 5 41 11
A gzfileGetContents() 0 13 3
A load() 0 8 1
F loadIntoExisting() 22 607 154
C parseBorderAttributes() 0 68 16
A parseRichText() 0 7 1
A parseGnumericColour() 0 9 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Gnumeric often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Gnumeric, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader;
4
5
use PhpOffice\PhpSpreadsheet\Cell\Cell;
6
use PhpOffice\PhpSpreadsheet\Cell\DataType;
7
use PhpOffice\PhpSpreadsheet\NamedRange;
8
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
9
use PhpOffice\PhpSpreadsheet\RichText\RichText;
10
use PhpOffice\PhpSpreadsheet\Settings;
11
use PhpOffice\PhpSpreadsheet\Shared\Date;
12
use PhpOffice\PhpSpreadsheet\Shared\File;
13
use PhpOffice\PhpSpreadsheet\Spreadsheet;
14
use PhpOffice\PhpSpreadsheet\Style\Alignment;
15
use PhpOffice\PhpSpreadsheet\Style\Border;
16
use PhpOffice\PhpSpreadsheet\Style\Borders;
17
use PhpOffice\PhpSpreadsheet\Style\Fill;
18
use PhpOffice\PhpSpreadsheet\Style\Font;
19
use XMLReader;
20
21
class Gnumeric extends BaseReader implements IReader
22
{
23
    /**
24
     * Shared Expressions.
25
     *
26
     * @var array
27
     */
28
    private $expressions = [];
29
30
    private $referenceHelper;
31
32
    /**
33
     * Create a new Gnumeric.
34
     */
35 4
    public function __construct()
36
    {
37 4
        $this->readFilter = new DefaultReadFilter();
38 4
        $this->referenceHelper = ReferenceHelper::getInstance();
39 4
    }
40
41
    /**
42
     * Can the current IReader read the file?
43
     *
44
     * @param string $pFilename
45
     *
46
     * @throws Exception
47
     *
48
     * @return bool
49
     */
50 3
    public function canRead($pFilename)
51
    {
52 3
        File::assertFile($pFilename);
53
54
        // Check if gzlib functions are available
55 3
        if (!function_exists('gzread')) {
56
            throw new Exception('gzlib library is not enabled');
57
        }
58
59
        // Read signature data (first 3 bytes)
60 3
        $fh = fopen($pFilename, 'r');
61 3
        $data = fread($fh, 2);
62 3
        fclose($fh);
63
64 3
        if ($data != chr(0x1F) . chr(0x8B)) {
65
            return false;
66
        }
67
68 3
        return true;
69
    }
70
71
    /**
72
     * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
73
     *
74
     * @param string $pFilename
75
     *
76
     * @throws Exception
77
     */
78
    public function listWorksheetNames($pFilename)
79
    {
80
        File::assertFile($pFilename);
81
82
        $xml = new XMLReader();
83
        $xml->xml($this->securityScanFile('compress.zlib://' . realpath($pFilename)), null, Settings::getLibXmlLoaderOptions());
84
        $xml->setParserProperty(2, true);
85
86
        $worksheetNames = [];
87
        while ($xml->read()) {
88 View Code Duplication
            if ($xml->name == 'gnm:SheetName' && $xml->nodeType == XMLReader::ELEMENT) {
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...
89
                $xml->read(); //    Move onto the value node
90
                $worksheetNames[] = (string) $xml->value;
91
            } elseif ($xml->name == 'gnm:Sheets') {
92
                //    break out of the loop once we've got our sheet names rather than parse the entire file
93
                break;
94
            }
95
        }
96
97
        return $worksheetNames;
98
    }
99
100
    /**
101
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
102
     *
103
     * @param string $pFilename
104
     *
105
     * @throws Exception
106
     */
107
    public function listWorksheetInfo($pFilename)
108
    {
109
        File::assertFile($pFilename);
110
111
        $xml = new XMLReader();
112
        $xml->xml($this->securityScanFile('compress.zlib://' . realpath($pFilename)), null, Settings::getLibXmlLoaderOptions());
113
        $xml->setParserProperty(2, true);
114
115
        $worksheetInfo = [];
116
        while ($xml->read()) {
117
            if ($xml->name == 'gnm:Sheet' && $xml->nodeType == XMLReader::ELEMENT) {
118
                $tmpInfo = [
119
                    'worksheetName' => '',
120
                    'lastColumnLetter' => 'A',
121
                    'lastColumnIndex' => 0,
122
                    'totalRows' => 0,
123
                    'totalColumns' => 0,
124
                ];
125
126
                while ($xml->read()) {
127
                    if ($xml->name == 'gnm:Name' && $xml->nodeType == XMLReader::ELEMENT) {
128
                        $xml->read(); //    Move onto the value node
129
                        $tmpInfo['worksheetName'] = (string) $xml->value;
130
                    } elseif ($xml->name == 'gnm:MaxCol' && $xml->nodeType == XMLReader::ELEMENT) {
131
                        $xml->read(); //    Move onto the value node
132
                        $tmpInfo['lastColumnIndex'] = (int) $xml->value;
133
                        $tmpInfo['totalColumns'] = (int) $xml->value + 1;
134 View Code Duplication
                    } elseif ($xml->name == 'gnm:MaxRow' && $xml->nodeType == XMLReader::ELEMENT) {
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...
135
                        $xml->read(); //    Move onto the value node
136
                        $tmpInfo['totalRows'] = (int) $xml->value + 1;
137
138
                        break;
139
                    }
140
                }
141
                $tmpInfo['lastColumnLetter'] = Cell::stringFromColumnIndex($tmpInfo['lastColumnIndex']);
142
                $worksheetInfo[] = $tmpInfo;
143
            }
144
        }
145
146
        return $worksheetInfo;
147
    }
148
149
    /**
150
     * @param string $filename
151
     */
152 1
    private function gzfileGetContents($filename)
153
    {
154 1
        $file = @gzopen($filename, 'rb');
155 1
        if ($file !== false) {
156 1
            $data = '';
157 1
            while (!gzeof($file)) {
158 1
                $data .= gzread($file, 1024);
159
            }
160 1
            gzclose($file);
161
        }
162
163 1
        return $data;
0 ignored issues
show
Bug introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
164
    }
165
166
    /**
167
     * Loads Spreadsheet from file.
168
     *
169
     * @param string $pFilename
170
     *
171
     * @throws Exception
172
     *
173
     * @return Spreadsheet
174
     */
175 1
    public function load($pFilename)
176
    {
177
        // Create new Spreadsheet
178 1
        $spreadsheet = new Spreadsheet();
179
180
        // Load into this instance
181 1
        return $this->loadIntoExisting($pFilename, $spreadsheet);
182
    }
183
184
    /**
185
     * Loads from file into Spreadsheet instance.
186
     *
187
     * @param string $pFilename
188
     * @param Spreadsheet $spreadsheet
189
     *
190
     * @throws Exception
191
     *
192
     * @return Spreadsheet
193
     */
194 1
    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
195
    {
196 1
        File::assertFile($pFilename);
197
198 1
        $gFileData = $this->gzfileGetContents($pFilename);
199
200 1
        $xml = simplexml_load_string($this->securityScan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
201 1
        $namespacesMeta = $xml->getNamespaces(true);
202
203 1
        $gnmXML = $xml->children($namespacesMeta['gnm']);
204
205 1
        $docProps = $spreadsheet->getProperties();
206
        //    Document Properties are held differently, depending on the version of Gnumeric
207 1
        if (isset($namespacesMeta['office'])) {
208 1
            $officeXML = $xml->children($namespacesMeta['office']);
209 1
            $officeDocXML = $officeXML->{'document-meta'};
210 1
            $officeDocMetaXML = $officeDocXML->meta;
211
212 1
            foreach ($officeDocMetaXML as $officePropertyData) {
213 1
                $officePropertyDC = [];
214 1
                if (isset($namespacesMeta['dc'])) {
215 1
                    $officePropertyDC = $officePropertyData->children($namespacesMeta['dc']);
216
                }
217 1
                foreach ($officePropertyDC as $propertyName => $propertyValue) {
218 1
                    $propertyValue = (string) $propertyValue;
219
                    switch ($propertyName) {
220 1
                        case 'title':
221 1
                            $docProps->setTitle(trim($propertyValue));
222
223 1
                            break;
224 1
                        case 'subject':
225 1
                            $docProps->setSubject(trim($propertyValue));
226
227 1
                            break;
228 1
                        case 'creator':
229 1
                            $docProps->setCreator(trim($propertyValue));
230 1
                            $docProps->setLastModifiedBy(trim($propertyValue));
231
232 1
                            break;
233 1 View Code Duplication
                        case 'date':
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...
234 1
                            $creationDate = strtotime(trim($propertyValue));
235 1
                            $docProps->setCreated($creationDate);
0 ignored issues
show
Documentation introduced by
$creationDate is of type integer, but the function expects a object<PhpOffice\PhpSpre...heet\Document\datetime>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
236 1
                            $docProps->setModified($creationDate);
0 ignored issues
show
Documentation introduced by
$creationDate is of type integer, but the function expects a object<PhpOffice\PhpSpre...heet\Document\datetime>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
237
238 1
                            break;
239 1
                        case 'description':
240 1
                            $docProps->setDescription(trim($propertyValue));
241
242 1
                            break;
243
                    }
244
                }
245 1
                $officePropertyMeta = [];
246 1
                if (isset($namespacesMeta['meta'])) {
247 1
                    $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
248
                }
249 1
                foreach ($officePropertyMeta as $propertyName => $propertyValue) {
250 1
                    $attributes = $propertyValue->attributes($namespacesMeta['meta']);
251 1
                    $propertyValue = (string) $propertyValue;
252
                    switch ($propertyName) {
253 1
                        case 'keyword':
254 1
                            $docProps->setKeywords(trim($propertyValue));
255
256 1
                            break;
257 1
                        case 'initial-creator':
258 1
                            $docProps->setCreator(trim($propertyValue));
259 1
                            $docProps->setLastModifiedBy(trim($propertyValue));
260
261 1
                            break;
262 1 View Code Duplication
                        case 'creation-date':
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...
263 1
                            $creationDate = strtotime(trim($propertyValue));
264 1
                            $docProps->setCreated($creationDate);
0 ignored issues
show
Documentation introduced by
$creationDate is of type integer, but the function expects a object<PhpOffice\PhpSpre...heet\Document\datetime>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
265 1
                            $docProps->setModified($creationDate);
0 ignored issues
show
Documentation introduced by
$creationDate is of type integer, but the function expects a object<PhpOffice\PhpSpre...heet\Document\datetime>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
266
267 1
                            break;
268 1
                        case 'user-defined':
269 1
                            list(, $attrName) = explode(':', $attributes['name']);
270
                            switch ($attrName) {
271 1
                                case 'publisher':
272 1
                                    $docProps->setCompany(trim($propertyValue));
273
274 1
                                    break;
275 1
                                case 'category':
276 1
                                    $docProps->setCategory(trim($propertyValue));
277
278 1
                                    break;
279 1
                                case 'manager':
280 1
                                    $docProps->setManager(trim($propertyValue));
281
282 1
                                    break;
283
                            }
284
285 1
                            break;
286
                    }
287
                }
288
            }
289
        } elseif (isset($gnmXML->Summary)) {
290
            foreach ($gnmXML->Summary->Item as $summaryItem) {
291
                $propertyName = $summaryItem->name;
292
                $propertyValue = $summaryItem->{'val-string'};
293
                switch ($propertyName) {
294
                    case 'title':
295
                        $docProps->setTitle(trim($propertyValue));
296
297
                        break;
298
                    case 'comments':
299
                        $docProps->setDescription(trim($propertyValue));
300
301
                        break;
302
                    case 'keywords':
303
                        $docProps->setKeywords(trim($propertyValue));
304
305
                        break;
306
                    case 'category':
307
                        $docProps->setCategory(trim($propertyValue));
308
309
                        break;
310
                    case 'manager':
311
                        $docProps->setManager(trim($propertyValue));
312
313
                        break;
314
                    case 'author':
315
                        $docProps->setCreator(trim($propertyValue));
316
                        $docProps->setLastModifiedBy(trim($propertyValue));
317
318
                        break;
319
                    case 'company':
320
                        $docProps->setCompany(trim($propertyValue));
321
322
                        break;
323
                }
324
            }
325
        }
326
327 1
        $worksheetID = 0;
328 1
        foreach ($gnmXML->Sheets->Sheet as $sheet) {
329 1
            $worksheetName = (string) $sheet->Name;
330 1
            if ((isset($this->loadSheetsOnly)) && (!in_array($worksheetName, $this->loadSheetsOnly))) {
331
                continue;
332
            }
333
334 1
            $maxRow = $maxCol = 0;
335
336
            // Create new Worksheet
337 1
            $spreadsheet->createSheet();
338 1
            $spreadsheet->setActiveSheetIndex($worksheetID);
339
            //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
340
            //        cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
341
            //        name in line with the formula, not the reverse
342 1
            $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
343
344 1
            if ((!$this->readDataOnly) && (isset($sheet->PrintInformation))) {
345 1
                if (isset($sheet->PrintInformation->Margins)) {
346 1
                    foreach ($sheet->PrintInformation->Margins->children('gnm', true) as $key => $margin) {
347 1
                        $marginAttributes = $margin->attributes();
348 1
                        $marginSize = 72 / 100; //    Default
349 1
                        switch ($marginAttributes['PrefUnit']) {
350 1
                            case 'mm':
351 1
                                $marginSize = (int) ($marginAttributes['Points']) / 100;
352
353 1
                                break;
354
                        }
355
                        switch ($key) {
356 1
                            case 'top':
357 1
                                $spreadsheet->getActiveSheet()->getPageMargins()->setTop($marginSize);
358
359 1
                                break;
360 1
                            case 'bottom':
361 1
                                $spreadsheet->getActiveSheet()->getPageMargins()->setBottom($marginSize);
362
363 1
                                break;
364 1
                            case 'left':
365 1
                                $spreadsheet->getActiveSheet()->getPageMargins()->setLeft($marginSize);
366
367 1
                                break;
368 1
                            case 'right':
369 1
                                $spreadsheet->getActiveSheet()->getPageMargins()->setRight($marginSize);
370
371 1
                                break;
372 1
                            case 'header':
373 1
                                $spreadsheet->getActiveSheet()->getPageMargins()->setHeader($marginSize);
374
375 1
                                break;
376 1
                            case 'footer':
377 1
                                $spreadsheet->getActiveSheet()->getPageMargins()->setFooter($marginSize);
378
379 1
                                break;
380
                        }
381
                    }
382
                }
383
            }
384
385 1
            foreach ($sheet->Cells->Cell as $cell) {
386 1
                $cellAttributes = $cell->attributes();
387 1
                $row = (int) $cellAttributes->Row + 1;
388 1
                $column = (int) $cellAttributes->Col;
389
390 1
                if ($row > $maxRow) {
391 1
                    $maxRow = $row;
392
                }
393 1
                if ($column > $maxCol) {
394 1
                    $maxCol = $column;
395
                }
396
397 1
                $column = Cell::stringFromColumnIndex($column);
398
399
                // Read cell?
400 1
                if ($this->getReadFilter() !== null) {
401 1
                    if (!$this->getReadFilter()->readCell($column, $row, $worksheetName)) {
402
                        continue;
403
                    }
404
                }
405
406 1
                $ValueType = $cellAttributes->ValueType;
407 1
                $ExprID = (string) $cellAttributes->ExprID;
408 1
                $type = DataType::TYPE_FORMULA;
409 1
                if ($ExprID > '') {
410 1
                    if (((string) $cell) > '') {
411 1
                        $this->expressions[$ExprID] = [
412 1
                            'column' => $cellAttributes->Col,
413 1
                            'row' => $cellAttributes->Row,
414 1
                            'formula' => (string) $cell,
415
                        ];
416
                    } else {
417 1
                        $expression = $this->expressions[$ExprID];
418
419 1
                        $cell = $this->referenceHelper->updateFormulaReferences(
420 1
                            $expression['formula'],
421 1
                            'A1',
422 1
                            $cellAttributes->Col - $expression['column'],
423 1
                            $cellAttributes->Row - $expression['row'],
424 1
                            $worksheetName
425
                        );
426
                    }
427 1
                    $type = DataType::TYPE_FORMULA;
428
                } else {
429
                    switch ($ValueType) {
430 1
                        case '10':        //    NULL
431
                            $type = DataType::TYPE_NULL;
432
433
                            break;
434 1
                        case '20':        //    Boolean
435 1
                            $type = DataType::TYPE_BOOL;
436 1
                            $cell = ($cell == 'TRUE') ? true : false;
437
438 1
                            break;
439 1
                        case '30':        //    Integer
440
                            $cell = (int) $cell;
441
                            // Excel 2007+ doesn't differentiate between integer and float, so set the value and dropthru to the next (numeric) case
442
                            // no break
443 1
                        case '40':        //    Float
444 1
                            $type = DataType::TYPE_NUMERIC;
445
446 1
                            break;
447 1
                        case '50':        //    Error
448
                            $type = DataType::TYPE_ERROR;
449
450
                            break;
451 1
                        case '60':        //    String
452 1
                            $type = DataType::TYPE_STRING;
453
454 1
                            break;
455 1
                        case '70':        //    Cell Range
456 1
                        case '80':        //    Array
457
                    }
458
                }
459 1
                $spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit($cell, $type);
460
            }
461
462 1
            if ((!$this->readDataOnly) && (isset($sheet->Objects))) {
463 1
                foreach ($sheet->Objects->children('gnm', true) as $key => $comment) {
464 1
                    $commentAttributes = $comment->attributes();
465
                    //    Only comment objects are handled at the moment
466 1
                    if ($commentAttributes->Text) {
467 1
                        $spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound)->setAuthor((string) $commentAttributes->Author)->setText($this->parseRichText((string) $commentAttributes->Text));
468
                    }
469
                }
470
            }
471 1
            foreach ($sheet->Styles->StyleRegion as $styleRegion) {
472 1
                $styleAttributes = $styleRegion->attributes();
473 1
                if (($styleAttributes['startRow'] <= $maxRow) &&
474 1
                    ($styleAttributes['startCol'] <= $maxCol)) {
475 1
                    $startColumn = Cell::stringFromColumnIndex((int) $styleAttributes['startCol']);
476 1
                    $startRow = $styleAttributes['startRow'] + 1;
477
478 1
                    $endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol'];
479 1
                    $endColumn = Cell::stringFromColumnIndex($endColumn);
480 1
                    $endRow = ($styleAttributes['endRow'] > $maxRow) ? $maxRow : $styleAttributes['endRow'];
481 1
                    $endRow += 1;
482 1
                    $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow;
483
484 1
                    $styleAttributes = $styleRegion->Style->attributes();
485
486
                    //    We still set the number format mask for date/time values, even if readDataOnly is true
487 1
                    if ((!$this->readDataOnly) ||
488 1
                        (Date::isDateTimeFormatCode((string) $styleAttributes['Format']))) {
489 1
                        $styleArray = [];
490 1
                        $styleArray['numberFormat']['formatCode'] = (string) $styleAttributes['Format'];
491
                        //    If readDataOnly is false, we set all formatting information
492 1
                        if (!$this->readDataOnly) {
493 1
                            switch ($styleAttributes['HAlign']) {
494 1
                                case '1':
495 1
                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_GENERAL;
496
497 1
                                    break;
498 1
                                case '2':
499 1
                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_LEFT;
500
501 1
                                    break;
502 1
                                case '4':
503 1
                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_RIGHT;
504
505 1
                                    break;
506 1
                                case '8':
507 1
                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_CENTER;
508
509 1
                                    break;
510
                                case '16':
511
                                case '64':
512
                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_CENTER_CONTINUOUS;
513
514
                                    break;
515
                                case '32':
516
                                    $styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_JUSTIFY;
517
518
                                    break;
519
                            }
520
521 1
                            switch ($styleAttributes['VAlign']) {
522 1
                                case '1':
523 1
                                    $styleArray['alignment']['vertical'] = Alignment::VERTICAL_TOP;
524
525 1
                                    break;
526 1
                                case '2':
527 1
                                    $styleArray['alignment']['vertical'] = Alignment::VERTICAL_BOTTOM;
528
529 1
                                    break;
530 1
                                case '4':
531 1
                                    $styleArray['alignment']['vertical'] = Alignment::VERTICAL_CENTER;
532
533 1
                                    break;
534
                                case '8':
535
                                    $styleArray['alignment']['vertical'] = Alignment::VERTICAL_JUSTIFY;
536
537
                                    break;
538
                            }
539
540 1
                            $styleArray['alignment']['wrapText'] = ($styleAttributes['WrapText'] == '1') ? true : false;
541 1
                            $styleArray['alignment']['shrinkToFit'] = ($styleAttributes['ShrinkToFit'] == '1') ? true : false;
542 1
                            $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0;
543
544 1
                            $RGB = self::parseGnumericColour($styleAttributes['Fore']);
545 1
                            $styleArray['font']['color']['rgb'] = $RGB;
546 1
                            $RGB = self::parseGnumericColour($styleAttributes['Back']);
547 1
                            $shade = $styleAttributes['Shade'];
548 1
                            if (($RGB != '000000') || ($shade != '0')) {
549 1
                                $styleArray['fill']['color']['rgb'] = $styleArray['fill']['startColor']['rgb'] = $RGB;
550 1
                                $RGB2 = self::parseGnumericColour($styleAttributes['PatternColor']);
551 1
                                $styleArray['fill']['endColor']['rgb'] = $RGB2;
552
                                switch ($shade) {
553 1
                                    case '1':
554 1
                                        $styleArray['fill']['fillType'] = Fill::FILL_SOLID;
555
556 1
                                        break;
557 1
                                    case '2':
558
                                        $styleArray['fill']['fillType'] = Fill::FILL_GRADIENT_LINEAR;
559
560
                                        break;
561 1
                                    case '3':
562
                                        $styleArray['fill']['fillType'] = Fill::FILL_GRADIENT_PATH;
563
564
                                        break;
565 1
                                    case '4':
566
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKDOWN;
567
568
                                        break;
569 1
                                    case '5':
570
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKGRAY;
571
572
                                        break;
573 1
                                    case '6':
574 1
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKGRID;
575
576 1
                                        break;
577 1
                                    case '7':
578 1
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKHORIZONTAL;
579
580 1
                                        break;
581 1
                                    case '8':
582
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKTRELLIS;
583
584
                                        break;
585 1
                                    case '9':
586
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKUP;
587
588
                                        break;
589 1
                                    case '10':
590
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKVERTICAL;
591
592
                                        break;
593 1
                                    case '11':
594
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_GRAY0625;
595
596
                                        break;
597 1
                                    case '12':
598
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_GRAY125;
599
600
                                        break;
601 1
                                    case '13':
602
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTDOWN;
603
604
                                        break;
605 1
                                    case '14':
606
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTGRAY;
607
608
                                        break;
609 1
                                    case '15':
610
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTGRID;
611
612
                                        break;
613 1
                                    case '16':
614
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTHORIZONTAL;
615
616
                                        break;
617 1
                                    case '17':
618
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTTRELLIS;
619
620
                                        break;
621 1
                                    case '18':
622
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTUP;
623
624
                                        break;
625 1
                                    case '19':
626
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTVERTICAL;
627
628
                                        break;
629 1
                                    case '20':
630
                                        $styleArray['fill']['fillType'] = Fill::FILL_PATTERN_MEDIUMGRAY;
631
632
                                        break;
633
                                }
634
                            }
635
636 1
                            $fontAttributes = $styleRegion->Style->Font->attributes();
637 1
                            $styleArray['font']['name'] = (string) $styleRegion->Style->Font;
638 1
                            $styleArray['font']['size'] = (int) ($fontAttributes['Unit']);
639 1
                            $styleArray['font']['bold'] = ($fontAttributes['Bold'] == '1') ? true : false;
640 1
                            $styleArray['font']['italic'] = ($fontAttributes['Italic'] == '1') ? true : false;
641 1
                            $styleArray['font']['strikethrough'] = ($fontAttributes['StrikeThrough'] == '1') ? true : false;
642 1
                            switch ($fontAttributes['Underline']) {
643 1
                                case '1':
644 1
                                    $styleArray['font']['underline'] = Font::UNDERLINE_SINGLE;
645
646 1
                                    break;
647 1
                                case '2':
648 1
                                    $styleArray['font']['underline'] = Font::UNDERLINE_DOUBLE;
649
650 1
                                    break;
651 1
                                case '3':
652 1
                                    $styleArray['font']['underline'] = Font::UNDERLINE_SINGLEACCOUNTING;
653
654 1
                                    break;
655 1
                                case '4':
656 1
                                    $styleArray['font']['underline'] = Font::UNDERLINE_DOUBLEACCOUNTING;
657
658 1
                                    break;
659
                                default:
660 1
                                    $styleArray['font']['underline'] = Font::UNDERLINE_NONE;
661
662 1
                                    break;
663
                            }
664 1
                            switch ($fontAttributes['Script']) {
665 1
                                case '1':
666 1
                                    $styleArray['font']['superscript'] = true;
667
668 1
                                    break;
669 1
                                case '-1':
670 1
                                    $styleArray['font']['subscript'] = true;
671
672 1
                                    break;
673
                            }
674
675 1
                            if (isset($styleRegion->Style->StyleBorder)) {
676 1 View Code Duplication
                                if (isset($styleRegion->Style->StyleBorder->Top)) {
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...
677 1
                                    $styleArray['borders']['top'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Top->attributes());
678
                                }
679 1 View Code Duplication
                                if (isset($styleRegion->Style->StyleBorder->Bottom)) {
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...
680 1
                                    $styleArray['borders']['bottom'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Bottom->attributes());
681
                                }
682 1 View Code Duplication
                                if (isset($styleRegion->Style->StyleBorder->Left)) {
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...
683 1
                                    $styleArray['borders']['left'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Left->attributes());
684
                                }
685 1 View Code Duplication
                                if (isset($styleRegion->Style->StyleBorder->Right)) {
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...
686 1
                                    $styleArray['borders']['right'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Right->attributes());
687
                                }
688 1
                                if ((isset($styleRegion->Style->StyleBorder->Diagonal)) && (isset($styleRegion->Style->StyleBorder->{'Rev-Diagonal'}))) {
689 1
                                    $styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Diagonal->attributes());
690 1
                                    $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH;
691 1
                                } elseif (isset($styleRegion->Style->StyleBorder->Diagonal)) {
692 1
                                    $styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Diagonal->attributes());
693 1
                                    $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP;
694 1
                                } elseif (isset($styleRegion->Style->StyleBorder->{'Rev-Diagonal'})) {
695 1
                                    $styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->{'Rev-Diagonal'}->attributes());
696 1
                                    $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN;
697
                                }
698
                            }
699 1
                            if (isset($styleRegion->Style->HyperLink)) {
700
                                //    TO DO
701 1
                                $hyperlink = $styleRegion->Style->HyperLink->attributes();
0 ignored issues
show
Unused Code introduced by
$hyperlink is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
702
                            }
703
                        }
704 1
                        $spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray);
705
                    }
706
                }
707
            }
708
709 1
            if ((!$this->readDataOnly) && (isset($sheet->Cols))) {
710
                //    Column Widths
711 1
                $columnAttributes = $sheet->Cols->attributes();
712 1
                $defaultWidth = $columnAttributes['DefaultSizePts'] / 5.4;
713 1
                $c = 0;
714 1
                foreach ($sheet->Cols->ColInfo as $columnOverride) {
715 1
                    $columnAttributes = $columnOverride->attributes();
716 1
                    $column = $columnAttributes['No'];
717 1
                    $columnWidth = $columnAttributes['Unit'] / 5.4;
718 1
                    $hidden = ((isset($columnAttributes['Hidden'])) && ($columnAttributes['Hidden'] == '1')) ? true : false;
719 1
                    $columnCount = (isset($columnAttributes['Count'])) ? $columnAttributes['Count'] : 1;
720 1
                    while ($c < $column) {
721 1
                        $spreadsheet->getActiveSheet()->getColumnDimension(Cell::stringFromColumnIndex($c))->setWidth($defaultWidth);
722 1
                        ++$c;
723
                    }
724 1
                    while (($c < ($column + $columnCount)) && ($c <= $maxCol)) {
725 1
                        $spreadsheet->getActiveSheet()->getColumnDimension(Cell::stringFromColumnIndex($c))->setWidth($columnWidth);
726 1
                        if ($hidden) {
727 1
                            $spreadsheet->getActiveSheet()->getColumnDimension(Cell::stringFromColumnIndex($c))->setVisible(false);
728
                        }
729 1
                        ++$c;
730
                    }
731
                }
732 1
                while ($c <= $maxCol) {
733
                    $spreadsheet->getActiveSheet()->getColumnDimension(Cell::stringFromColumnIndex($c))->setWidth($defaultWidth);
734
                    ++$c;
735
                }
736
            }
737
738 1
            if ((!$this->readDataOnly) && (isset($sheet->Rows))) {
739
                //    Row Heights
740 1
                $rowAttributes = $sheet->Rows->attributes();
741 1
                $defaultHeight = $rowAttributes['DefaultSizePts'];
742 1
                $r = 0;
743
744 1
                foreach ($sheet->Rows->RowInfo as $rowOverride) {
745 1
                    $rowAttributes = $rowOverride->attributes();
746 1
                    $row = $rowAttributes['No'];
747 1
                    $rowHeight = $rowAttributes['Unit'];
748 1
                    $hidden = ((isset($rowAttributes['Hidden'])) && ($rowAttributes['Hidden'] == '1')) ? true : false;
749 1
                    $rowCount = (isset($rowAttributes['Count'])) ? $rowAttributes['Count'] : 1;
750 1
                    while ($r < $row) {
751 1
                        ++$r;
752 1
                        $spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight);
753
                    }
754 1
                    while (($r < ($row + $rowCount)) && ($r < $maxRow)) {
755 1
                        ++$r;
756 1
                        $spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($rowHeight);
757 1
                        if ($hidden) {
758
                            $spreadsheet->getActiveSheet()->getRowDimension($r)->setVisible(false);
759
                        }
760
                    }
761
                }
762 1
                while ($r < $maxRow) {
763
                    ++$r;
764
                    $spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight);
765
                }
766
            }
767
768
            //    Handle Merged Cells in this worksheet
769 1
            if (isset($sheet->MergedRegions)) {
770 1
                foreach ($sheet->MergedRegions->Merge as $mergeCells) {
771 1
                    if (strpos($mergeCells, ':') !== false) {
772 1
                        $spreadsheet->getActiveSheet()->mergeCells($mergeCells);
773
                    }
774
                }
775
            }
776
777 1
            ++$worksheetID;
778
        }
779
780
        //    Loop through definedNames (global named ranges)
781 1
        if (isset($gnmXML->Names)) {
782 1
            foreach ($gnmXML->Names->Name as $namedRange) {
783 1
                $name = (string) $namedRange->name;
784 1
                $range = (string) $namedRange->value;
785 1
                if (stripos($range, '#REF!') !== false) {
786
                    continue;
787
                }
788
789 1
                $range = explode('!', $range);
790 1
                $range[0] = trim($range[0], "'");
791 1
                if ($worksheet = $spreadsheet->getSheetByName($range[0])) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $worksheet is correct as $spreadsheet->getSheetByName($range[0]) (which targets PhpOffice\PhpSpreadsheet...sheet::getSheetByName()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
792 1
                    $extractedRange = str_replace('$', '', $range[1]);
793 1
                    $spreadsheet->addNamedRange(new NamedRange($name, $worksheet, $extractedRange));
794
                }
795
            }
796
        }
797
798
        // Return
799 1
        return $spreadsheet;
800
    }
801
802 1
    private static function parseBorderAttributes($borderAttributes)
803
    {
804 1
        $styleArray = [];
805 1
        if (isset($borderAttributes['Color'])) {
806 1
            $styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']);
807
        }
808
809 1
        switch ($borderAttributes['Style']) {
810 1
            case '0':
811
                $styleArray['borderStyle'] = Border::BORDER_NONE;
812
813
                break;
814 1
            case '1':
815 1
                $styleArray['borderStyle'] = Border::BORDER_THIN;
816
817 1
                break;
818 1
            case '2':
819 1
                $styleArray['borderStyle'] = Border::BORDER_MEDIUM;
820
821 1
                break;
822 1
            case '3':
823
                $styleArray['borderStyle'] = Border::BORDER_SLANTDASHDOT;
824
825
                break;
826 1
            case '4':
827 1
                $styleArray['borderStyle'] = Border::BORDER_DASHED;
828
829 1
                break;
830 1
            case '5':
831 1
                $styleArray['borderStyle'] = Border::BORDER_THICK;
832
833 1
                break;
834 1
            case '6':
835 1
                $styleArray['borderStyle'] = Border::BORDER_DOUBLE;
836
837 1
                break;
838 1
            case '7':
839 1
                $styleArray['borderStyle'] = Border::BORDER_DOTTED;
840
841 1
                break;
842 1
            case '8':
843 1
                $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHED;
844
845 1
                break;
846 1
            case '9':
847 1
                $styleArray['borderStyle'] = Border::BORDER_DASHDOT;
848
849 1
                break;
850 1
            case '10':
851 1
                $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOT;
852
853 1
                break;
854 1
            case '11':
855 1
                $styleArray['borderStyle'] = Border::BORDER_DASHDOTDOT;
856
857 1
                break;
858 1
            case '12':
859 1
                $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOTDOT;
860
861 1
                break;
862 1
            case '13':
863 1
                $styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOTDOT;
864
865 1
                break;
866
        }
867
868 1
        return $styleArray;
869
    }
870
871 1
    private function parseRichText($is)
872
    {
873 1
        $value = new RichText();
874 1
        $value->createText($is);
875
876 1
        return $value;
877
    }
878
879 1
    private static function parseGnumericColour($gnmColour)
880
    {
881 1
        list($gnmR, $gnmG, $gnmB) = explode(':', $gnmColour);
882 1
        $gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2);
883 1
        $gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2);
884 1
        $gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2);
885
886 1
        return $gnmR . $gnmG . $gnmB;
887
    }
888
}
889