Completed
Push — develop ( a5c21a...5d3fdf )
by Adrien
229:01 queued 221:37
created

Excel2003XML::listWorksheetNames()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 15
nc 3
nop 1
dl 0
loc 24
ccs 0
cts 15
cp 0
crap 12
rs 8.9713
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader;
4
5
use PhpOffice\PhpSpreadsheet\Shared\File;
6
use PhpOffice\PhpSpreadsheet\Spreadsheet;
7
8
/**
9
 * Copyright (c) 2006 - 2016 PhpSpreadsheet
10
 *
11
 * This library is free software; you can redistribute it and/or
12
 * modify it under the terms of the GNU Lesser General Public
13
 * License as published by the Free Software Foundation; either
14
 * version 2.1 of the License, or (at your option) any later version.
15
 *
16
 * This library is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
 * Lesser General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public
22
 * License along with this library; if not, write to the Free Software
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24
 *
25
 * @category   PhpSpreadsheet
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 Excel2003XML extends BaseReader implements IReader
30
{
31
    /**
32
     * Formats
33
     *
34
     * @var array
35
     */
36
    protected $styles = [];
37
38
    /**
39
     * Character set used in the file
40
     *
41
     * @var string
42
     */
43
    protected $charSet = 'UTF-8';
44
45
    /**
46
     * Create a new Excel2003XML Reader instance
47
     */
48 2
    public function __construct()
49
    {
50 2
        $this->readFilter = new DefaultReadFilter();
51 2
    }
52
53
    /**
54
     * Can the current IReader read the file?
55
     *
56
     * @param     string         $pFilename
57
     * @throws Exception
58
     * @return     bool
59
     */
60 2
    public function canRead($pFilename)
61
    {
62
63
        //    Office                    xmlns:o="urn:schemas-microsoft-com:office:office"
64
        //    Excel                    xmlns:x="urn:schemas-microsoft-com:office:excel"
65
        //    XML Spreadsheet            xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
66
        //    Spreadsheet component    xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
67
        //    XML schema                 xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
68
        //    XML data type            xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
69
        //    MS-persist recordset    xmlns:rs="urn:schemas-microsoft-com:rowset"
70
        //    Rowset                    xmlns:z="#RowsetSchema"
71
        //
72
73
        $signature = [
74 2
                '<?xml version="1.0"',
75
                '<?mso-application progid="Excel.Sheet"?>',
76
            ];
77
78
        // Open file
79 2
        $this->openFile($pFilename);
80 2
        $fileHandle = $this->fileHandle;
81
82
        // Read sample data (first 2 KB will do)
83 2
        $data = fread($fileHandle, 2048);
84 2
        fclose($fileHandle);
85 2
        $data = strtr($data, "'", '"'); // fix headers with single quote
86
87 2
        $valid = true;
88 2
        foreach ($signature as $match) {
89
            // every part of the signature must be present
90 2
            if (strpos($data, $match) === false) {
91
                $valid = false;
92 2
                break;
93
            }
94
        }
95
96
        //    Retrieve charset encoding
97 2
        if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/um', $data, $matches)) {
98 2
            $this->charSet = strtoupper($matches[1]);
99
        }
100
101 2
        return $valid;
102
    }
103
104
    /**
105
     * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object
106
     *
107
     * @param     string         $pFilename
108
     * @throws     Exception
109
     */
110
    public function listWorksheetNames($pFilename)
111
    {
112
        File::assertFile($pFilename);
113
        if (!$this->canRead($pFilename)) {
114
            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
115
        }
116
117
        $worksheetNames = [];
118
119
        $xml = simplexml_load_string(
120
            $this->securityScan(file_get_contents($pFilename)),
121
            'SimpleXMLElement',
122
            \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions()
123
        );
124
        $namespaces = $xml->getNamespaces(true);
125
126
        $xml_ss = $xml->children($namespaces['ss']);
127
        foreach ($xml_ss->Worksheet as $worksheet) {
128
            $worksheet_ss = $worksheet->attributes($namespaces['ss']);
129
            $worksheetNames[] = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet);
130
        }
131
132
        return $worksheetNames;
133
    }
134
135
    /**
136
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)
137
     *
138
     * @param   string     $pFilename
139
     * @throws   Exception
140
     */
141
    public function listWorksheetInfo($pFilename)
142
    {
143
        File::assertFile($pFilename);
144
145
        $worksheetInfo = [];
146
147
        $xml = simplexml_load_string(
148
            $this->securityScan(file_get_contents($pFilename)),
149
            'SimpleXMLElement',
150
            \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions()
151
        );
152
        $namespaces = $xml->getNamespaces(true);
153
154
        $worksheetID = 1;
155
        $xml_ss = $xml->children($namespaces['ss']);
156
        foreach ($xml_ss->Worksheet as $worksheet) {
157
            $worksheet_ss = $worksheet->attributes($namespaces['ss']);
158
159
            $tmpInfo = [];
160
            $tmpInfo['worksheetName'] = '';
161
            $tmpInfo['lastColumnLetter'] = 'A';
162
            $tmpInfo['lastColumnIndex'] = 0;
163
            $tmpInfo['totalRows'] = 0;
164
            $tmpInfo['totalColumns'] = 0;
165
166
            if (isset($worksheet_ss['Name'])) {
167
                $tmpInfo['worksheetName'] = (string) $worksheet_ss['Name'];
168
            } else {
169
                $tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}";
170
            }
171
172
            if (isset($worksheet->Table->Row)) {
173
                $rowIndex = 0;
174
175
                foreach ($worksheet->Table->Row as $rowData) {
176
                    $columnIndex = 0;
177
                    $rowHasData = false;
178
179
                    foreach ($rowData->Cell as $cell) {
180
                        if (isset($cell->Data)) {
181
                            $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
182
                            $rowHasData = true;
183
                        }
184
185
                        ++$columnIndex;
186
                    }
187
188
                    ++$rowIndex;
189
190
                    if ($rowHasData) {
191
                        $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
192
                    }
193
                }
194
            }
195
196
            $tmpInfo['lastColumnLetter'] = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($tmpInfo['lastColumnIndex']);
197
            $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
198
199
            $worksheetInfo[] = $tmpInfo;
200
            ++$worksheetID;
201
        }
202
203
        return $worksheetInfo;
204
    }
205
206
    /**
207
     * Loads Spreadsheet from file
208
     *
209
     * @param     string         $pFilename
210
     * @throws     Exception
211
     * @return     \PhpOffice\PhpSpreadsheet\Spreadsheet
212
     */
213 1
    public function load($pFilename)
214
    {
215
        // Create new Spreadsheet
216 1
        $spreadsheet = new Spreadsheet();
217 1
        $spreadsheet->removeSheetByIndex(0);
218
219
        // Load into this instance
220 1
        return $this->loadIntoExisting($pFilename, $spreadsheet);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->loadIntoEx...ilename, $spreadsheet); (PhpOffice\PhpSpreadsheet\Spreadsheet) is incompatible with the return type declared by the interface PhpOffice\PhpSpreadsheet\Reader\IReader::load of type PhpOffice\PhpSpreadsheet\Reader\PhpSpreadsheet.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
221
    }
222
223 1 View Code Duplication
    protected static function identifyFixedStyleValue($styleList, &$styleAttributeValue)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
224
    {
225 1
        $styleAttributeValue = strtolower($styleAttributeValue);
226 1
        foreach ($styleList as $style) {
227 1
            if ($styleAttributeValue == strtolower($style)) {
228 1
                $styleAttributeValue = $style;
229
230 1
                return true;
231
            }
232
        }
233
234
        return false;
235
    }
236
237
    /**
238
     * pixel units to excel width units(units of 1/256th of a character width)
239
     * @param pxs
240
     * @return
241
     */
242
    protected static function pixel2WidthUnits($pxs)
243
    {
244
        $UNIT_OFFSET_MAP = [0, 36, 73, 109, 146, 182, 219];
245
246
        $widthUnits = 256 * ($pxs / 7);
247
        $widthUnits += $UNIT_OFFSET_MAP[($pxs % 7)];
248
249
        return $widthUnits;
250
    }
251
252
    /**
253
     * excel width units(units of 1/256th of a character width) to pixel units
254
     * @param widthUnits
255
     * @return
256
     */
257
    protected static function widthUnits2Pixel($widthUnits)
258
    {
259
        $pixels = ($widthUnits / 256) * 7;
260
        $offsetWidthUnits = $widthUnits % 256;
261
        $pixels += round($offsetWidthUnits / (256 / 7));
262
263
        return $pixels;
264
    }
265
266
    protected static function hex2str($hex)
267
    {
268
        return chr(hexdec($hex[1]));
269
    }
270
271
    /**
272
     * Loads from file into Spreadsheet instance
273
     *
274
     * @param     string         $pFilename
275
     * @param     \PhpOffice\PhpSpreadsheet\Spreadsheet    $spreadsheet
276
     * @throws    Exception
277
     * @return    \PhpOffice\PhpSpreadsheet\Spreadsheet
278
     */
279 1
    public function loadIntoExisting($pFilename, \PhpOffice\PhpSpreadsheet\Spreadsheet $spreadsheet)
280
    {
281 1
        $fromFormats = ['\-', '\ '];
282 1
        $toFormats = ['-', ' '];
283
284
        $underlineStyles = [
285 1
            \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_NONE,
286 1
            \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE,
287 1
            \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING,
288 1
            \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE,
289 1
            \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING,
290
        ];
291
        $verticalAlignmentStyles = [
292 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_BOTTOM,
293 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_TOP,
294 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
295 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_JUSTIFY,
296
        ];
297
        $horizontalAlignmentStyles = [
298 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_GENERAL,
299 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT,
300 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT,
301 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
302 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS,
303 1
            \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_JUSTIFY,
304
        ];
305
306 1
        $timezoneObj = new \DateTimeZone('Europe/London');
0 ignored issues
show
Unused Code introduced by
$timezoneObj 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...
307 1
        $GMT = new \DateTimeZone('UTC');
0 ignored issues
show
Unused Code introduced by
$GMT 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...
308
309 1
        File::assertFile($pFilename);
310 1
        if (!$this->canRead($pFilename)) {
311
            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
312
        }
313
314 1
        $xml = simplexml_load_string(
315 1
            $this->securityScan(file_get_contents($pFilename)),
316 1
            'SimpleXMLElement',
317 1
            \PhpOffice\PhpSpreadsheet\Settings::getLibXmlLoaderOptions()
318
        );
319 1
        $namespaces = $xml->getNamespaces(true);
320
321 1
        $docProps = $spreadsheet->getProperties();
322 1
        if (isset($xml->DocumentProperties[0])) {
323
            foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
324
                switch ($propertyName) {
325
                    case 'Title':
326
                        $docProps->setTitle(self::convertStringEncoding($propertyValue, $this->charSet));
327
                        break;
328
                    case 'Subject':
329
                        $docProps->setSubject(self::convertStringEncoding($propertyValue, $this->charSet));
330
                        break;
331
                    case 'Author':
332
                        $docProps->setCreator(self::convertStringEncoding($propertyValue, $this->charSet));
333
                        break;
334
                    case 'Created':
335
                        $creationDate = strtotime($propertyValue);
336
                        $docProps->setCreated($creationDate);
0 ignored issues
show
Documentation introduced by
$creationDate is of type integer, but the function expects a object<PhpOffice\PhpSpre...Document\datetime>|null.

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...
337
                        break;
338
                    case 'LastAuthor':
339
                        $docProps->setLastModifiedBy(self::convertStringEncoding($propertyValue, $this->charSet));
340
                        break;
341
                    case 'LastSaved':
342
                        $lastSaveDate = strtotime($propertyValue);
343
                        $docProps->setModified($lastSaveDate);
0 ignored issues
show
Documentation introduced by
$lastSaveDate is of type integer, but the function expects a object<PhpOffice\PhpSpre...Document\datetime>|null.

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...
344
                        break;
345
                    case 'Company':
346
                        $docProps->setCompany(self::convertStringEncoding($propertyValue, $this->charSet));
347
                        break;
348
                    case 'Category':
349
                        $docProps->setCategory(self::convertStringEncoding($propertyValue, $this->charSet));
350
                        break;
351
                    case 'Manager':
352
                        $docProps->setManager(self::convertStringEncoding($propertyValue, $this->charSet));
353
                        break;
354
                    case 'Keywords':
355
                        $docProps->setKeywords(self::convertStringEncoding($propertyValue, $this->charSet));
356
                        break;
357
                    case 'Description':
358
                        $docProps->setDescription(self::convertStringEncoding($propertyValue, $this->charSet));
359
                        break;
360
                }
361
            }
362
        }
363 1
        if (isset($xml->CustomDocumentProperties)) {
364
            foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
365
                $propertyAttributes = $propertyValue->attributes($namespaces['dt']);
366
                $propertyName = preg_replace_callback('/_x([0-9a-z]{4})_/', ['self', 'hex2str'], $propertyName);
367
                $propertyType = \PhpOffice\PhpSpreadsheet\Document\Properties::PROPERTY_TYPE_UNKNOWN;
368
                switch ((string) $propertyAttributes) {
369
                    case 'string':
370
                        $propertyType = \PhpOffice\PhpSpreadsheet\Document\Properties::PROPERTY_TYPE_STRING;
371
                        $propertyValue = trim($propertyValue);
372
                        break;
373
                    case 'boolean':
374
                        $propertyType = \PhpOffice\PhpSpreadsheet\Document\Properties::PROPERTY_TYPE_BOOLEAN;
375
                        $propertyValue = (bool) $propertyValue;
376
                        break;
377
                    case 'integer':
378
                        $propertyType = \PhpOffice\PhpSpreadsheet\Document\Properties::PROPERTY_TYPE_INTEGER;
379
                        $propertyValue = intval($propertyValue);
380
                        break;
381
                    case 'float':
382
                        $propertyType = \PhpOffice\PhpSpreadsheet\Document\Properties::PROPERTY_TYPE_FLOAT;
383
                        $propertyValue = floatval($propertyValue);
384
                        break;
385
                    case 'dateTime.tz':
386
                        $propertyType = \PhpOffice\PhpSpreadsheet\Document\Properties::PROPERTY_TYPE_DATE;
387
                        $propertyValue = strtotime(trim($propertyValue));
388
                        break;
389
                }
390
                $docProps->setCustomProperty($propertyName, $propertyValue, $propertyType);
391
            }
392
        }
393
394 1
        foreach ($xml->Styles[0] as $style) {
395 1
            $style_ss = $style->attributes($namespaces['ss']);
396 1
            $styleID = (string) $style_ss['ID'];
397 1
            $this->styles[$styleID] = (isset($this->styles['Default'])) ? $this->styles['Default'] : [];
398 1
            foreach ($style as $styleType => $styleData) {
399 1
                $styleAttributes = $styleData->attributes($namespaces['ss']);
400
                switch ($styleType) {
401 1
                    case 'Alignment':
402 1
                        foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
403 1
                            $styleAttributeValue = (string) $styleAttributeValue;
404
                            switch ($styleAttributeKey) {
405 1
                                case 'Vertical':
406 1
                                    if (self::identifyFixedStyleValue($verticalAlignmentStyles, $styleAttributeValue)) {
407 1
                                        $this->styles[$styleID]['alignment']['vertical'] = $styleAttributeValue;
408
                                    }
409 1
                                    break;
410 1
                                case 'Horizontal':
411 1
                                    if (self::identifyFixedStyleValue($horizontalAlignmentStyles, $styleAttributeValue)) {
412 1
                                        $this->styles[$styleID]['alignment']['horizontal'] = $styleAttributeValue;
413
                                    }
414 1
                                    break;
415 1
                                case 'WrapText':
416 1
                                    $this->styles[$styleID]['alignment']['wrap'] = true;
417 1
                                    break;
418
                            }
419
                        }
420 1
                        break;
421 1
                    case 'Borders':
422 1
                        foreach ($styleData->Border as $borderStyle) {
423 1
                            $borderAttributes = $borderStyle->attributes($namespaces['ss']);
424 1
                            $thisBorder = [];
425 1
                            foreach ($borderAttributes as $borderStyleKey => $borderStyleValue) {
426
                                switch ($borderStyleKey) {
427 1
                                    case 'LineStyle':
428 1
                                        $thisBorder['style'] = \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_MEDIUM;
429 1
                                        break;
430 1
                                    case 'Weight':
431 1
                                        break;
432 1
                                    case 'Position':
433 1
                                        $borderPosition = strtolower($borderStyleValue);
434 1
                                        break;
435 1
                                    case 'Color':
436 1
                                        $borderColour = substr($borderStyleValue, 1);
437 1
                                        $thisBorder['color']['rgb'] = $borderColour;
438 1
                                        break;
439
                                }
440
                            }
441 1
                            if (!empty($thisBorder)) {
442 1
                                if (($borderPosition == 'left') || ($borderPosition == 'right') || ($borderPosition == 'top') || ($borderPosition == 'bottom')) {
443 1
                                    $this->styles[$styleID]['borders'][$borderPosition] = $thisBorder;
0 ignored issues
show
Bug introduced by
The variable $borderPosition 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...
444
                                }
445
                            }
446
                        }
447 1
                        break;
448 1
                    case 'Font':
449 1
                        foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
450 1
                            $styleAttributeValue = (string) $styleAttributeValue;
451
                            switch ($styleAttributeKey) {
452 1
                                case 'FontName':
453 1
                                    $this->styles[$styleID]['font']['name'] = $styleAttributeValue;
454 1
                                    break;
455 1
                                case 'Size':
456 1
                                    $this->styles[$styleID]['font']['size'] = $styleAttributeValue;
457 1
                                    break;
458 1 View Code Duplication
                                case 'Color':
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...
459 1
                                    $this->styles[$styleID]['font']['color']['rgb'] = substr($styleAttributeValue, 1);
460 1
                                    break;
461 1
                                case 'Bold':
462 1
                                    $this->styles[$styleID]['font']['bold'] = true;
463 1
                                    break;
464 1
                                case 'Italic':
465 1
                                    $this->styles[$styleID]['font']['italic'] = true;
466 1
                                    break;
467 1
                                case 'Underline':
468 1
                                    if (self::identifyFixedStyleValue($underlineStyles, $styleAttributeValue)) {
469 1
                                        $this->styles[$styleID]['font']['underline'] = $styleAttributeValue;
470
                                    }
471 1
                                    break;
472
                            }
473
                        }
474 1
                        break;
475 1
                    case 'Interior':
476 1
                        foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
477
                            switch ($styleAttributeKey) {
478 1 View Code Duplication
                                case 'Color':
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...
479 1
                                    $this->styles[$styleID]['fill']['color']['rgb'] = substr($styleAttributeValue, 1);
480 1
                                    break;
481 1
                                case 'Pattern':
482 1
                                    $this->styles[$styleID]['fill']['type'] = strtolower($styleAttributeValue);
483 1
                                    break;
484
                            }
485
                        }
486 1
                        break;
487 1
                    case 'NumberFormat':
488 1
                        foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
489 1
                            $styleAttributeValue = str_replace($fromFormats, $toFormats, $styleAttributeValue);
490
                            switch ($styleAttributeValue) {
491 1
                                case 'Short Date':
492 1
                                    $styleAttributeValue = 'dd/mm/yyyy';
493 1
                                    break;
494
                            }
495 1
                            if ($styleAttributeValue > '') {
496 1
                                $this->styles[$styleID]['numberformat']['code'] = $styleAttributeValue;
497
                            }
498
                        }
499 1
                        break;
500
                    case 'Protection':
501
                        foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
502
                        }
503 1
                        break;
504
                }
505
            }
506
        }
507
508 1
        $worksheetID = 0;
509 1
        $xml_ss = $xml->children($namespaces['ss']);
510
511 1
        foreach ($xml_ss->Worksheet as $worksheet) {
512 1
            $worksheet_ss = $worksheet->attributes($namespaces['ss']);
513
514 1
            if ((isset($this->loadSheetsOnly)) && (isset($worksheet_ss['Name'])) &&
515 1
                (!in_array($worksheet_ss['Name'], $this->loadSheetsOnly))) {
516
                continue;
517
            }
518
519
            // Create new Worksheet
520 1
            $spreadsheet->createSheet();
521 1
            $spreadsheet->setActiveSheetIndex($worksheetID);
522 1
            if (isset($worksheet_ss['Name'])) {
523 1
                $worksheetName = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet);
524
                //    Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
525
                //        formula cells... during the load, all formulae should be correct, and we're simply bringing
526
                //        the worksheet name in line with the formula, not the reverse
527 1
                $spreadsheet->getActiveSheet()->setTitle($worksheetName, false);
528
            }
529
530 1
            $columnID = 'A';
531 1
            if (isset($worksheet->Table->Column)) {
532 1
                foreach ($worksheet->Table->Column as $columnData) {
533 1
                    $columnData_ss = $columnData->attributes($namespaces['ss']);
534 1
                    if (isset($columnData_ss['Index'])) {
535 1
                        $columnID = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($columnData_ss['Index'] - 1);
536
                    }
537 1
                    if (isset($columnData_ss['Width'])) {
538 1
                        $columnWidth = $columnData_ss['Width'];
539 1
                        $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
540
                    }
541 1
                    ++$columnID;
542
                }
543
            }
544
545 1
            $rowID = 1;
546 1
            if (isset($worksheet->Table->Row)) {
547 1
                $additionalMergedCells = 0;
548 1
                foreach ($worksheet->Table->Row as $rowData) {
549 1
                    $rowHasData = false;
550 1
                    $row_ss = $rowData->attributes($namespaces['ss']);
551 1
                    if (isset($row_ss['Index'])) {
552 1
                        $rowID = (integer) $row_ss['Index'];
553
                    }
554
555 1
                    $columnID = 'A';
556 1
                    foreach ($rowData->Cell as $cell) {
557 1
                        $cell_ss = $cell->attributes($namespaces['ss']);
558 1
                        if (isset($cell_ss['Index'])) {
559 1
                            $columnID = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($cell_ss['Index'] - 1);
560
                        }
561 1
                        $cellRange = $columnID . $rowID;
562
563 1 View Code Duplication
                        if ($this->getReadFilter() !== null) {
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...
564 1
                            if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
0 ignored issues
show
Bug introduced by
The variable $worksheetName 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...
565
                                ++$columnID;
566
                                continue;
567
                            }
568
                        }
569
570 1
                        if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) {
571 1
                            $columnTo = $columnID;
572 1 View Code Duplication
                            if (isset($cell_ss['MergeAcross'])) {
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...
573 1
                                $additionalMergedCells += (int) $cell_ss['MergeAcross'];
574 1
                                $columnTo = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(\PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($columnID) + $cell_ss['MergeAcross'] - 1);
575
                            }
576 1
                            $rowTo = $rowID;
577 1
                            if (isset($cell_ss['MergeDown'])) {
578 1
                                $rowTo = $rowTo + $cell_ss['MergeDown'];
579
                            }
580 1
                            $cellRange .= ':' . $columnTo . $rowTo;
581 1
                            $spreadsheet->getActiveSheet()->mergeCells($cellRange);
582
                        }
583
584 1
                        $cellIsSet = $hasCalculatedValue = false;
585 1
                        $cellDataFormula = '';
586 1
                        if (isset($cell_ss['Formula'])) {
587 1
                            $cellDataFormula = $cell_ss['Formula'];
588
                            // added this as a check for array formulas
589 1
                            if (isset($cell_ss['ArrayRange'])) {
590
                                $cellDataCSEFormula = $cell_ss['ArrayRange'];
0 ignored issues
show
Unused Code introduced by
$cellDataCSEFormula 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...
591
                            }
592 1
                            $hasCalculatedValue = true;
593
                        }
594 1
                        if (isset($cell->Data)) {
595 1
                            $cellValue = $cellData = $cell->Data;
596 1
                            $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NULL;
597 1
                            $cellData_ss = $cellData->attributes($namespaces['ss']);
598 1
                            if (isset($cellData_ss['Type'])) {
599 1
                                $cellDataType = $cellData_ss['Type'];
600
                                switch ($cellDataType) {
601
                                    /*
602
                                    const TYPE_STRING        = 's';
603
                                    const TYPE_FORMULA        = 'f';
604
                                    const TYPE_NUMERIC        = 'n';
605
                                    const TYPE_BOOL            = 'b';
606
                                    const TYPE_NULL            = 'null';
607
                                    const TYPE_INLINE        = 'inlineStr';
608
                                    const TYPE_ERROR        = 'e';
609
                                    */
610 1
                                    case 'String':
611 1
                                        $cellValue = self::convertStringEncoding($cellValue, $this->charSet);
612 1
                                        $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING;
613 1
                                        break;
614 1
                                    case 'Number':
615 1
                                        $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC;
616 1
                                        $cellValue = (float) $cellValue;
617 1
                                        if (floor($cellValue) == $cellValue) {
618 1
                                            $cellValue = (integer) $cellValue;
619
                                        }
620 1
                                        break;
621 1
                                    case 'Boolean':
622 1
                                        $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_BOOL;
623 1
                                        $cellValue = ($cellValue != 0);
624 1
                                        break;
625 1
                                    case 'DateTime':
626 1
                                        $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC;
627 1
                                        $cellValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel(strtotime($cellValue));
628 1
                                        break;
629
                                    case 'Error':
630
                                        $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_ERROR;
631
                                        break;
632
                                }
633
                            }
634
635 1
                            if ($hasCalculatedValue) {
636 1
                                $type = \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_FORMULA;
637 1
                                $columnNumber = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($columnID);
638 1
                                if (substr($cellDataFormula, 0, 3) == 'of:') {
639 1
                                    $cellDataFormula = substr($cellDataFormula, 3);
640 1
                                    $temp = explode('"', $cellDataFormula);
641 1
                                    $key = false;
642 1
                                    foreach ($temp as &$value) {
643
                                        //    Only replace in alternate array entries (i.e. non-quoted blocks)
644 1
                                        if ($key = !$key) {
645 1
                                            $value = str_replace(['[.', '.', ']'], '', $value);
646
                                        }
647
                                    }
648
                                } else {
649
                                    //    Convert R1C1 style references to A1 style references (but only when not quoted)
650
                                    $temp = explode('"', $cellDataFormula);
651
                                    $key = false;
652 View Code Duplication
                                    foreach ($temp as &$value) {
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...
653
                                        //    Only replace in alternate array entries (i.e. non-quoted blocks)
654
                                        if ($key = !$key) {
655
                                            preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
656
                                            //    Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
657
                                            //        through the formula from left to right. Reversing means that we work right to left.through
658
                                            //        the formula
659
                                            $cellReferences = array_reverse($cellReferences);
660
                                            //    Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
661
                                            //        then modify the formula to use that new reference
662
                                            foreach ($cellReferences as $cellReference) {
663
                                                $rowReference = $cellReference[2][0];
664
                                                //    Empty R reference is the current row
665
                                                if ($rowReference == '') {
666
                                                    $rowReference = $rowID;
667
                                                }
668
                                                //    Bracketed R references are relative to the current row
669
                                                if ($rowReference{0} == '[') {
670
                                                    $rowReference = $rowID + trim($rowReference, '[]');
671
                                                }
672
                                                $columnReference = $cellReference[4][0];
673
                                                //    Empty C reference is the current column
674
                                                if ($columnReference == '') {
675
                                                    $columnReference = $columnNumber;
676
                                                }
677
                                                //    Bracketed C references are relative to the current column
678
                                                if ($columnReference{0} == '[') {
679
                                                    $columnReference = $columnNumber + trim($columnReference, '[]');
680
                                                }
681
                                                $A1CellReference = \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex($columnReference - 1) . $rowReference;
682
                                                $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
683
                                            }
684
                                        }
685
                                    }
686
                                }
687 1
                                unset($value);
688
                                //    Then rebuild the formula string
689 1
                                $cellDataFormula = implode('"', $temp);
690
                            }
691
692 1
                            $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type);
693 1
                            if ($hasCalculatedValue) {
694 1
                                $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue);
695
                            }
696 1
                            $cellIsSet = $rowHasData = true;
697
                        }
698
699 1
                        if (isset($cell->Comment)) {
700 1
                            $commentAttributes = $cell->Comment->attributes($namespaces['ss']);
701 1
                            $author = 'unknown';
702 1
                            if (isset($commentAttributes->Author)) {
703
                                $author = (string) $commentAttributes->Author;
704
                            }
705 1
                            $node = $cell->Comment->Data->asXML();
706 1
                            $annotation = strip_tags($node);
707 1
                            $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setAuthor(self::convertStringEncoding($author, $this->charSet))->setText($this->parseRichText($annotation));
708
                        }
709
710 1
                        if (($cellIsSet) && (isset($cell_ss['StyleID']))) {
711 1
                            $style = (string) $cell_ss['StyleID'];
712 1
                            if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) {
713 1
                                if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) {
714
                                    $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null);
715
                                }
716 1
                                $spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styles[$style]);
717
                            }
718
                        }
719 1
                        ++$columnID;
720 1
                        while ($additionalMergedCells > 0) {
721 1
                            ++$columnID;
722 1
                            --$additionalMergedCells;
723
                        }
724
                    }
725
726 1
                    if ($rowHasData) {
727 1
                        if (isset($row_ss['StyleID'])) {
728
                            $rowStyle = $row_ss['StyleID'];
0 ignored issues
show
Unused Code introduced by
$rowStyle 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...
729
                        }
730 1
                        if (isset($row_ss['Height'])) {
731 1
                            $rowHeight = $row_ss['Height'];
732 1
                            $spreadsheet->getActiveSheet()->getRowDimension($rowID)->setRowHeight($rowHeight);
733
                        }
734
                    }
735
736 1
                    ++$rowID;
737
                }
738
            }
739 1
            ++$worksheetID;
740
        }
741
742
        // Return
743 1
        return $spreadsheet;
744
    }
745
746 1
    protected static function convertStringEncoding($string, $charset)
747
    {
748 1
        if ($charset != 'UTF-8') {
749
            return \PhpOffice\PhpSpreadsheet\Shared\StringHelper::convertEncoding($string, 'UTF-8', $charset);
750
        }
751
752 1
        return $string;
753
    }
754
755 1
    protected function parseRichText($is = '')
756
    {
757 1
        $value = new \PhpOffice\PhpSpreadsheet\RichText();
758
759 1
        $value->createText(self::convertStringEncoding($is, $this->charSet));
760
761 1
        return $value;
762
    }
763
}
764