Completed
Pull Request — master (#557)
by Adrien
03:10
created

getStylesXMLFilePath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 0
crap 2
1
<?php
2
3
namespace Box\Spout\Reader\XLSX\Manager;
4
5
use Box\Spout\Common\Exception\IOException;
6
use Box\Spout\Reader\Wrapper\XMLReader;
7
use Box\Spout\Reader\XLSX\Creator\InternalEntityFactory;
8
9
/**
10
 * Class WorkbookRelationshipsManager
11
 * This class manages the workbook relationships defined in the associated XML file
12
 */
13
class WorkbookRelationshipsManager
14
{
15
    const BASE_PATH = 'xl/';
16
17
    /** Path of workbook relationships XML file inside the XLSX file */
18
    const WORKBOOK_RELS_XML_FILE_PATH = 'xl/_rels/workbook.xml.rels';
19
20
    /** Relationships types */
21
    const RELATIONSHIP_TYPE_SHARED_STRINGS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings';
22
    const RELATIONSHIP_TYPE_STYLES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles';
23
    const RELATIONSHIP_TYPE_WORKSHEET = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet';
24
25
    /** Nodes and attributes used to find relevant information in the workbook relationships XML file */
26
    const XML_NODE_RELATIONSHIP = 'Relationship';
27
    const XML_ATTRIBUTE_TYPE = 'Type';
28
    const XML_ATTRIBUTE_TARGET = 'Target';
29
30
    /** @var string Path of the XLSX file being read */
31
    private $filePath;
32
33
    /** @var InternalEntityFactory Factory to create entities */
34
    private $entityFactory;
35
36
    /** @var array Cache of the already read workbook relationships: [TYPE] => [FILE_NAME] */
37
    private $cachedWorkbookRelationships;
38
39
    /**
40
     * @param string $filePath Path of the XLSX file being read
41
     * @param InternalEntityFactory $entityFactory Factory to create entities
42
     */
43 46
    public function __construct($filePath, $entityFactory)
44
    {
45 46
        $this->filePath = $filePath;
46 46
        $this->entityFactory = $entityFactory;
47 46
    }
48
49
    /**
50
     * @return string The path of the shared string XML file
51
     */
52 39
    public function getSharedStringsXMLFilePath()
53
    {
54 39
        $workbookRelationships = $this->getWorkbookRelationships();
55 39
        $sharedStringsXMLFilePath = $workbookRelationships[self::RELATIONSHIP_TYPE_SHARED_STRINGS];
56
57
        // the file path can be relative (e.g. "styles.xml") or absolute (e.g. "/xl/styles.xml")
58 39
        $doesContainBasePath = (strpos($sharedStringsXMLFilePath, self::BASE_PATH) !== false);
59 39
        if (!$doesContainBasePath) {
60
            // make sure we return an absolute file path
61 39
            $sharedStringsXMLFilePath = self::BASE_PATH . $sharedStringsXMLFilePath;
62
        }
63
64 39
        return $sharedStringsXMLFilePath;
65
    }
66
67
    /**
68
     * @return bool Whether the XLSX file contains a shared string XML file
69
     */
70 40
    public function hasSharedStringsXMLFile()
71
    {
72 40
        $workbookRelationships = $this->getWorkbookRelationships();
73
74 40
        return isset($workbookRelationships[self::RELATIONSHIP_TYPE_SHARED_STRINGS]);
75
    }
76
77
    /**
78
     * @return string|null The path of the styles XML file
79
     */
80 38
    public function getStylesXMLFilePath()
81
    {
82 38
        $workbookRelationships = $this->getWorkbookRelationships();
83 38
        $stylesXMLFilePath = $workbookRelationships[self::RELATIONSHIP_TYPE_STYLES];
84
85
        // the file path can be relative (e.g. "styles.xml") or absolute (e.g. "/xl/styles.xml")
86 38
        $doesContainBasePath = (strpos($stylesXMLFilePath, self::BASE_PATH) !== false);
87 38
        if (!$doesContainBasePath) {
88
            // make sure we return a full path
89 37
            $stylesXMLFilePath = self::BASE_PATH . $stylesXMLFilePath;
90
        }
91
92 38
        return $stylesXMLFilePath;
93
    }
94
95
    /**
96
     * Reads the workbook.xml.rels and extracts the filename associated to the different types.
97
     * It caches the result so that the file is read only once.
98
     *
99
     * @throws \Box\Spout\Common\Exception\IOException If workbook.xml.rels can't be read
100
     * @return array
101
     */
102 46
    private function getWorkbookRelationships()
103
    {
104 46
        if (!isset($this->cachedWorkbookRelationships)) {
105 46
            $xmlReader = $this->entityFactory->createXMLReader();
106
107 46
            if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_RELS_XML_FILE_PATH) === false) {
108
                throw new IOException('Could not open "' . self::WORKBOOK_RELS_XML_FILE_PATH . '".');
109
            }
110
111 46
            $this->cachedWorkbookRelationships = [];
112
113 46
            while ($xmlReader->readUntilNodeFound(self::XML_NODE_RELATIONSHIP)) {
114 46
                $this->processWorkbookRelationship($xmlReader);
115
            }
116
        }
117
118 46
        return $this->cachedWorkbookRelationships;
119
    }
120
121
    /**
122
     * Extracts and store the data of the current workbook relationship.
123
     *
124
     * @param XMLReader $xmlReader
125
     * @return void
126
     */
127 46
    private function processWorkbookRelationship($xmlReader)
128
    {
129 46
        $type = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TYPE);
130 46
        $target = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TARGET);
131
132
        // @NOTE: if a type is defined more than once, we overwrite the previous value
133
        // To be changed if we want to get the file paths of sheet XML files for instance.
134 46
        $this->cachedWorkbookRelationships[$type] = $target;
135 46
    }
136
}
137