Completed
Pull Request — master (#387)
by
unknown
06:30
created

SheetHelper::getActiveSheetIndex()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 12
cts 12
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 9
nc 2
nop 0
crap 4
1
<?php
2
3
namespace Box\Spout\Reader\XLSX\Helper;
4
5
use Box\Spout\Reader\Wrapper\XMLReader;
6
use Box\Spout\Reader\XLSX\Sheet;
7
8
/**
9
 * Class SheetHelper
10
 * This class provides helper functions related to XLSX sheets
11
 *
12
 * @package Box\Spout\Reader\XLSX\Helper
13
 */
14
class SheetHelper
15
{
16
    /** Paths of XML files relative to the XLSX file root */
17
    const WORKBOOK_XML_RELS_FILE_PATH = 'xl/_rels/workbook.xml.rels';
18
    const WORKBOOK_XML_FILE_PATH = 'xl/workbook.xml';
19
20
    /** @var string Path of the XLSX file being read */
21
    protected $filePath;
22
23
    /** @var \Box\Spout\Reader\XLSX\ReaderOptions Reader's current options */
24
    protected $options;
25
26
    /** @var \Box\Spout\Reader\XLSX\Helper\SharedStringsHelper Helper to work with shared strings */
27
    protected $sharedStringsHelper;
28
29
    /** @var \Box\Spout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */
30
    protected $globalFunctionsHelper;
31
32
    /**
33
     * @param string $filePath Path of the XLSX file being read
34
     * @param \Box\Spout\Reader\XLSX\ReaderOptions $options Reader's current options
35
     * @param \Box\Spout\Reader\XLSX\Helper\SharedStringsHelper Helper to work with shared strings
36
     * @param \Box\Spout\Common\Helper\GlobalFunctionsHelper $globalFunctionsHelper
37
     */
38 99
    public function __construct($filePath, $options, $sharedStringsHelper, $globalFunctionsHelper)
39
    {
40 99
        $this->filePath = $filePath;
41 99
        $this->options = $options;
42 99
        $this->sharedStringsHelper = $sharedStringsHelper;
43 99
        $this->globalFunctionsHelper = $globalFunctionsHelper;
44 99
    }
45
46
    /**
47
     * Returns the sheets metadata of the file located at the previously given file path.
48
     * The paths to the sheets' data are read from the [Content_Types].xml file.
49
     *
50
     * @return Sheet[] Sheets within the XLSX file
51
     */
52 99
    public function getSheets()
53
    {
54 99
        $sheets = [];
55 99
        $sheetIndex = 0;
56
57 99
        $xmlReader = new XMLReader();
58 99
        if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_XML_FILE_PATH)) {
59 99
            while ($xmlReader->read()) {
60 99
                if ($xmlReader->isPositionedOnStartingNode('sheet')) {
61 96
                    $sheets[] = $this->getSheetFromSheetXMLNode($xmlReader, $sheetIndex);
62 96
                    $sheetIndex++;
63 99
                } else if ($xmlReader->isPositionedOnEndingNode('sheets')) {
64
                    // stop reading once all sheets have been read
65 99
                    break;
66
                }
67 99
            }
68
69 99
            $xmlReader->close();
70 99
        }
71
72 99
        return $sheets;
73
    }
74
75
    /**
76
     * Get active sheet index
77
     *
78
     * @return int index of active sheet
79
     */
80 99
    public function getActiveSheetIndex()
81
    {
82 99
        $activeSheetIndex = 0;
83
84 99
        $xmlReader = new XMLReader();
85 99
        if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_XML_FILE_PATH)) {
86 99
            while ($xmlReader->read()) {
87 99
                if ($xmlReader->isPositionedOnStartingNode('workbookView')) {
88 33
                    $activeSheetIndex = (int) $xmlReader->getAttribute('activeTab');
89 33
                }
90 99
            }
91 99
            $xmlReader->close();
92 99
        }
93
94 99
        return $activeSheetIndex;
95
    }
96
97
    /**
98
     * Returns an instance of a sheet, given the XML node describing the sheet - from "workbook.xml".
99
     * We can find the XML file path describing the sheet inside "workbook.xml.res", by mapping with the sheet ID
100
     * ("r:id" in "workbook.xml", "Id" in "workbook.xml.res").
101
     *
102
     * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReaderOnSheetNode XML Reader instance, pointing on the node describing the sheet, as defined in "workbook.xml"
103
     * @param int $sheetIndexZeroBased Index of the sheet, based on order of appearance in the workbook (zero-based)
104
     * @return \Box\Spout\Reader\XLSX\Sheet Sheet instance
105
     */
106 96
    protected function getSheetFromSheetXMLNode($xmlReaderOnSheetNode, $sheetIndexZeroBased)
107
    {
108 96
        $sheetId = $xmlReaderOnSheetNode->getAttribute('r:id');
109 96
        $escapedSheetName = $xmlReaderOnSheetNode->getAttribute('name');
110
111
        /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
112 96
        $escaper = \Box\Spout\Common\Escaper\XLSX::getInstance();
113 96
        $sheetName = $escaper->unescape($escapedSheetName);
114
115 96
        $sheetDataXMLFilePath = $this->getSheetDataXMLFilePathForSheetId($sheetId);
116
117 96
        return new Sheet($this->filePath, $sheetDataXMLFilePath, $sheetIndexZeroBased, $sheetName, $this->options, $this->sharedStringsHelper);
118
    }
119
120
    /**
121
     * @param string $sheetId The sheet ID, as defined in "workbook.xml"
122
     * @return string The XML file path describing the sheet inside "workbook.xml.res", for the given sheet ID
123
     */
124 96
    protected function getSheetDataXMLFilePathForSheetId($sheetId)
125
    {
126 96
        $sheetDataXMLFilePath = '';
127
128
        // find the file path of the sheet, by looking at the "workbook.xml.res" file
129 96
        $xmlReader = new XMLReader();
130 96
        if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_XML_RELS_FILE_PATH)) {
131 96
            while ($xmlReader->read()) {
132 96
                if ($xmlReader->isPositionedOnStartingNode('Relationship')) {
133 96
                    $relationshipSheetId = $xmlReader->getAttribute('Id');
134
135 96
                    if ($relationshipSheetId === $sheetId) {
136
                        // In workbook.xml.rels, it is only "worksheets/sheet1.xml"
137
                        // In [Content_Types].xml, the path is "/xl/worksheets/sheet1.xml"
138 96
                        $sheetDataXMLFilePath = $xmlReader->getAttribute('Target');
139
140
                        // sometimes, the sheet data file path already contains "/xl/"...
141 96
                        if (strpos($sheetDataXMLFilePath, '/xl/') !== 0) {
142 93
                            $sheetDataXMLFilePath = '/xl/' . $sheetDataXMLFilePath;
143 93
                            break;
144
                        }
145 3
                    }
146 81
                }
147 96
            }
148
149 96
            $xmlReader->close();
150 96
        }
151
152 96
        return $sheetDataXMLFilePath;
153
    }
154
}
155