Completed
Pull Request — master (#220)
by Adrien
03:34
created

SheetHelper::getWorkbookXMLRelsAsXMLElement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 11
ccs 8
cts 8
cp 1
rs 9.4285
cc 2
eloc 6
nc 2
nop 0
crap 2
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($workbookXMLResElement);
96
        var_dump($relationshipNodes);
97
        var_dump($sheetId);
98
        $relationshipNode = $relationshipNodes[0];
99
100 72
        // In workbook.xml.rels, it is only "worksheets/sheet1.xml"
101
        // In [Content_Types].xml, the path is "/xl/worksheets/sheet1.xml"
102
        $sheetDataXMLFilePath = '/xl/' . $relationshipNode->getAttribute('Target');
103
104 72
        return new Sheet($this->filePath, $sheetDataXMLFilePath, $this->sharedStringsHelper, $sheetIndexZeroBased, $sheetName);
105
    }
106
107 72
    /**
108 72
     * Returns a representation of the workbook.xml.rels file, ready to be parsed.
109 72
     * The returned value is cached.
110
     *
111 72
     * @return \Box\Spout\Reader\Wrapper\SimpleXMLElement XML element representating the workbook.xml.rels file
112
     */
113 72
    protected function getWorkbookXMLRelsAsXMLElement()
114 72
    {
115 72
        if (!$this->workbookXMLRelsAsXMLElement) {
116
            $this->workbookXMLRelsAsXMLElement = $this->getFileAsXMLElementWithNamespace(
117 72
                self::WORKBOOK_XML_RELS_FILE_PATH,
118 72
                self::MAIN_NAMESPACE_FOR_WORKBOOK_XML_RELS
119 72
            );
120
        }
121
122 72
        return $this->workbookXMLRelsAsXMLElement;
123 72
    }
124
125 72
    /**
126
     * Returns a representation of the workbook.xml file, ready to be parsed.
127
     * The returned value is cached.
128
     *
129
     * @return \Box\Spout\Reader\Wrapper\SimpleXMLElement XML element representating the workbook.xml.rels file
130
     */
131
    protected function getWorkbookXMLAsXMLElement()
132
    {
133
        if (!$this->workbookXMLAsXMLElement) {
134 72
            $this->workbookXMLAsXMLElement = $this->getFileAsXMLElementWithNamespace(
135
                self::WORKBOOK_XML_FILE_PATH,
136 72
                self::MAIN_NAMESPACE_FOR_WORKBOOK_XML
137 72
            );
138 72
        }
139
140 72
        return $this->workbookXMLAsXMLElement;
141 72
    }
142
143 72
    /**
144
     * Loads the contents of the given file in an XML parser and register the given XPath namespace.
145
     *
146
     * @param string $xmlFilePath The path of the XML file inside the XLSX file
147
     * @param string $mainNamespace The main XPath namespace to register
148
     * @return \Box\Spout\Reader\Wrapper\SimpleXMLElement The XML element representing the file
149
     */
150
    protected function getFileAsXMLElementWithNamespace($xmlFilePath, $mainNamespace)
151
    {
152 72
        $xmlContents = $this->globalFunctionsHelper->file_get_contents('zip://' . $this->filePath . '#' . $xmlFilePath);
153
154 72
        $xmlElement = new SimpleXMLElement($xmlContents);
155 72
        $xmlElement->registerXPathNamespace('ns', $mainNamespace);
156 72
157
        return $xmlElement;
158 72
    }
159
}
160