Completed
Pull Request — master (#220)
by Adrien
02:51
created

SheetHelper::getSheetFromSheetXMLNode()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 27
ccs 7
cts 7
cp 1
rs 8.8571
cc 1
eloc 17
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Box\Spout\Reader\XLSX\Helper;
4
5
use Box\Spout\Reader\Wrapper\SimpleXMLElement;
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
    /** Namespaces for the XML files */
21
    const MAIN_NAMESPACE_FOR_WORKBOOK_XML_RELS = 'http://schemas.openxmlformats.org/package/2006/relationships';
22
    const MAIN_NAMESPACE_FOR_WORKBOOK_XML = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
23
24
    /** @var string Path of the XLSX file being read */
25
    protected $filePath;
26
27
    /** @var \Box\Spout\Reader\XLSX\Helper\SharedStringsHelper Helper to work with shared strings */
28
    protected $sharedStringsHelper;
29
30
    /** @var \Box\Spout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */
31
    protected $globalFunctionsHelper;
32
33
    /** @var \Box\Spout\Reader\Wrapper\SimpleXMLElement XML element representing the workbook.xml.rels file */
34
    protected $workbookXMLRelsAsXMLElement;
35
36
    /** @var \Box\Spout\Reader\Wrapper\SimpleXMLElement XML element representing the workbook.xml file */
37
    protected $workbookXMLAsXMLElement;
38
39
    /**
40
     * @param string $filePath Path of the XLSX file being read
41
     * @param \Box\Spout\Reader\XLSX\Helper\SharedStringsHelper Helper to work with shared strings
42
     * @param \Box\Spout\Common\Helper\GlobalFunctionsHelper $globalFunctionsHelper
43
     */
44
    public function __construct($filePath, $sharedStringsHelper, $globalFunctionsHelper)
45
    {
46
        $this->filePath = $filePath;
47
        $this->sharedStringsHelper = $sharedStringsHelper;
48
        $this->globalFunctionsHelper = $globalFunctionsHelper;
49 75
    }
50
51 75
    /**
52 75
     * Returns the sheets metadata of the file located at the previously given file path.
53 75
     * The paths to the sheets' data are read from the [Content_Types].xml file.
54 75
     *
55
     * @return Sheet[] Sheets within the XLSX file
56
     */
57
    public function getSheets()
58
    {
59
        $sheets = [];
60
61
        // Starting from "workbook.xml" as this file is the source of truth for the sheets order
62 75
        $workbookXMLElement = $this->getWorkbookXMLAsXMLElement();
63
        $sheetNodes = $workbookXMLElement->xpath('//ns:sheet');
64 75
65
        foreach ($sheetNodes as $sheetIndex => $sheetNode) {
66 75
            $sheets[] = $this->getSheetFromSheetXMLNode($sheetNode, $sheetIndex);
67 75
        }
68
69 75
        return $sheets;
70
    }
71
72 75
    /**
73 75
     * Returns an instance of a sheet, given the XML node describing the sheet - from "workbook.xml".
74
     * We can find the XML file path describing the sheet inside "workbook.xml.res", by mapping with the sheet ID
75 75
     * ("r:id" in "workbook.xml", "Id" in "workbook.xml.res").
76 72
     *
77 72
     * @param \Box\Spout\Reader\Wrapper\SimpleXMLElement $sheetNode XML Node describing the sheet, as defined in "workbook.xml"
78
     * @param int $sheetIndexZeroBased Index of the sheet, based on order of appearance in the workbook (zero-based)
79 72
     * @return \Box\Spout\Reader\XLSX\Sheet Sheet instance
80 72
     */
81
    protected function getSheetFromSheetXMLNode($sheetNode, $sheetIndexZeroBased)
82
    {
83
        $sheetId = $sheetNode->getAttribute('r:id');
84 75
        $escapedSheetName = $sheetNode->getAttribute('name');
85 15
86 75
        /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
87
        $escaper = new \Box\Spout\Common\Escaper\XLSX();
88 75
        $sheetName = $escaper->unescape($escapedSheetName);
89
90
        // find the file path of the sheet, by looking at the "workbook.xml.res" file
91
        $workbookXMLResElement = $this->getWorkbookXMLRelsAsXMLElement();
92
        $relationshipNodes = $workbookXMLResElement->xpath('//ns:Relationship[@Id="' . $sheetId . '"]');
93
        var_dump($sheetNode);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($sheetNode); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
94
        var_dump($sheetName);
95
        var_dump($sheetId);
96
        var_dump($sheetNode->getAttribute('id'));
97
        var_dump($sheetNode->getAttribute('id', 'r'));
98
        var_dump($workbookXMLResElement);
99
        var_dump($relationshipNodes);
100 72
        $relationshipNode = $relationshipNodes[0];
101
102
        // In workbook.xml.rels, it is only "worksheets/sheet1.xml"
103
        // In [Content_Types].xml, the path is "/xl/worksheets/sheet1.xml"
104 72
        $sheetDataXMLFilePath = '/xl/' . $relationshipNode->getAttribute('Target');
105
106
        return new Sheet($this->filePath, $sheetDataXMLFilePath, $this->sharedStringsHelper, $sheetIndexZeroBased, $sheetName);
107 72
    }
108 72
109 72
    /**
110
     * Returns a representation of the workbook.xml.rels file, ready to be parsed.
111 72
     * The returned value is cached.
112
     *
113 72
     * @return \Box\Spout\Reader\Wrapper\SimpleXMLElement XML element representating the workbook.xml.rels file
114 72
     */
115 72
    protected function getWorkbookXMLRelsAsXMLElement()
116
    {
117 72
        if (!$this->workbookXMLRelsAsXMLElement) {
118 72
            $this->workbookXMLRelsAsXMLElement = $this->getFileAsXMLElementWithNamespace(
119 72
                self::WORKBOOK_XML_RELS_FILE_PATH,
120
                self::MAIN_NAMESPACE_FOR_WORKBOOK_XML_RELS
121
            );
122 72
        }
123 72
124
        return $this->workbookXMLRelsAsXMLElement;
125 72
    }
126
127
    /**
128
     * Returns a representation of the workbook.xml file, ready to be parsed.
129
     * The returned value is cached.
130
     *
131
     * @return \Box\Spout\Reader\Wrapper\SimpleXMLElement XML element representating the workbook.xml.rels file
132
     */
133
    protected function getWorkbookXMLAsXMLElement()
134 72
    {
135
        if (!$this->workbookXMLAsXMLElement) {
136 72
            $this->workbookXMLAsXMLElement = $this->getFileAsXMLElementWithNamespace(
137 72
                self::WORKBOOK_XML_FILE_PATH,
138 72
                self::MAIN_NAMESPACE_FOR_WORKBOOK_XML
139
            );
140 72
141 72
            $this->workbookXMLAsXMLElement->registerXPathNamespace('r', self::MAIN_NAMESPACE_FOR_WORKBOOK_XML_RELS);
142
        }
143 72
144
        return $this->workbookXMLAsXMLElement;
145
    }
146
147
    /**
148
     * Loads the contents of the given file in an XML parser and register the given XPath namespace.
149
     *
150
     * @param string $xmlFilePath The path of the XML file inside the XLSX file
151
     * @param string $mainNamespace The main XPath namespace to register
152 72
     * @return \Box\Spout\Reader\Wrapper\SimpleXMLElement The XML element representing the file
153
     */
154 72
    protected function getFileAsXMLElementWithNamespace($xmlFilePath, $mainNamespace)
155 72
    {
156 72
        $xmlContents = $this->globalFunctionsHelper->file_get_contents('zip://' . $this->filePath . '#' . $xmlFilePath);
157
158 72
        $xmlElement = new SimpleXMLElement($xmlContents);
159 72
        $xmlElement->registerXPathNamespace('ns', $mainNamespace);
160
161 72
        return $xmlElement;
162
    }
163
}
164