Completed
Push — master ( 6c4086...1bbfd4 )
by Adrien
02:13
created

WorkbookRelationshipsManager::hasStylesXMLFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
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 47
    public function __construct($filePath, $entityFactory)
44
    {
45 47
        $this->filePath = $filePath;
46 47
        $this->entityFactory = $entityFactory;
47 47
    }
48
49
    /**
50
     * @return string The path of the shared string XML file
51
     */
52 40
    public function getSharedStringsXMLFilePath()
53
    {
54 40
        $workbookRelationships = $this->getWorkbookRelationships();
55 40
        $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 40
        $doesContainBasePath = (strpos($sharedStringsXMLFilePath, self::BASE_PATH) !== false);
59 40
        if (!$doesContainBasePath) {
60
            // make sure we return an absolute file path
61 40
            $sharedStringsXMLFilePath = self::BASE_PATH . $sharedStringsXMLFilePath;
62
        }
63
64 40
        return $sharedStringsXMLFilePath;
65
    }
66
67
    /**
68
     * @return bool Whether the XLSX file contains a shared string XML file
69
     */
70 41
    public function hasSharedStringsXMLFile()
71
    {
72 41
        $workbookRelationships = $this->getWorkbookRelationships();
73
74 41
        return isset($workbookRelationships[self::RELATIONSHIP_TYPE_SHARED_STRINGS]);
75
    }
76
77
    /**
78
     * @return bool Whether the XLSX file contains a styles XML file
79
     */
80 39
    public function hasStylesXMLFile()
81
    {
82 39
        $workbookRelationships = $this->getWorkbookRelationships();
83
84 39
        return isset($workbookRelationships[self::RELATIONSHIP_TYPE_STYLES]);
85
    }
86
87
    /**
88
     * @return string The path of the styles XML file
89
     */
90 38
    public function getStylesXMLFilePath()
91
    {
92 38
        $workbookRelationships = $this->getWorkbookRelationships();
93 38
        $stylesXMLFilePath = $workbookRelationships[self::RELATIONSHIP_TYPE_STYLES];
94
95
        // the file path can be relative (e.g. "styles.xml") or absolute (e.g. "/xl/styles.xml")
96 38
        $doesContainBasePath = (strpos($stylesXMLFilePath, self::BASE_PATH) !== false);
97 38
        if (!$doesContainBasePath) {
98
            // make sure we return a full path
99 37
            $stylesXMLFilePath = self::BASE_PATH . $stylesXMLFilePath;
100
        }
101
102 38
        return $stylesXMLFilePath;
103
    }
104
105
    /**
106
     * Reads the workbook.xml.rels and extracts the filename associated to the different types.
107
     * It caches the result so that the file is read only once.
108
     *
109
     * @throws \Box\Spout\Common\Exception\IOException If workbook.xml.rels can't be read
110
     * @return array
111
     */
112 47
    private function getWorkbookRelationships()
113
    {
114 47
        if (!isset($this->cachedWorkbookRelationships)) {
115 47
            $xmlReader = $this->entityFactory->createXMLReader();
116
117 47
            if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_RELS_XML_FILE_PATH) === false) {
118
                throw new IOException('Could not open "' . self::WORKBOOK_RELS_XML_FILE_PATH . '".');
119
            }
120
121 47
            $this->cachedWorkbookRelationships = [];
122
123 47
            while ($xmlReader->readUntilNodeFound(self::XML_NODE_RELATIONSHIP)) {
124 47
                $this->processWorkbookRelationship($xmlReader);
125
            }
126
        }
127
128 47
        return $this->cachedWorkbookRelationships;
129
    }
130
131
    /**
132
     * Extracts and store the data of the current workbook relationship.
133
     *
134
     * @param XMLReader $xmlReader
135
     * @return void
136
     */
137 47
    private function processWorkbookRelationship($xmlReader)
138
    {
139 47
        $type = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TYPE);
140 47
        $target = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TARGET);
141
142
        // @NOTE: if a type is defined more than once, we overwrite the previous value
143
        // To be changed if we want to get the file paths of sheet XML files for instance.
144 47
        $this->cachedWorkbookRelationships[$type] = $target;
145 47
    }
146
}
147