Completed
Push — develop ( 07455d...44e246 )
by Adrien
37:28
created

OLERead   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 297
Duplicated Lines 6.06 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 87.29%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 18
loc 297
ccs 103
cts 118
cp 0.8729
rs 9.8
c 2
b 0
f 0
wmc 31
lcom 1
cbo 2

5 Methods

Rating   Name   Duplication   Size   Complexity  
B getInt4d() 0 28 5
C read() 8 89 9
C getStream() 5 41 7
A _readData() 5 13 2
B readPropertySets() 0 55 8

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Shared;
4
5
/*
6
 * PhpSpreadsheet
7
 *
8
 * Copyright (c) 2006 - 2016 PhpSpreadsheet
9
 *
10
 * This library is free software; you can redistribute it and/or
11
 * modify it under the terms of the GNU Lesser General Public
12
 * License as published by the Free Software Foundation; either
13
 * version 2.1 of the License, or (at your option) any later version.
14
 *
15
 * This library is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
 * Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public
21
 * License along with this library; if not, write to the Free Software
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
 *
24
 * @category   PhpSpreadsheet
25
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
26
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
27
 */
28
29
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
30
31
class OLERead
32
{
33
    private $data = '';
34
35
    // Size of a sector = 512 bytes
36
    const BIG_BLOCK_SIZE = 0x200;
37
38
    // Size of a short sector = 64 bytes
39
    const SMALL_BLOCK_SIZE = 0x40;
40
41
    // Size of a directory entry always = 128 bytes
42
    const PROPERTY_STORAGE_BLOCK_SIZE = 0x80;
43
44
    // Minimum size of a standard stream = 4096 bytes, streams smaller than this are stored as short streams
45
    const SMALL_BLOCK_THRESHOLD = 0x1000;
46
47
    // header offsets
48
    const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c;
49
    const ROOT_START_BLOCK_POS = 0x30;
50
    const SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c;
51
    const EXTENSION_BLOCK_POS = 0x44;
52
    const NUM_EXTENSION_BLOCK_POS = 0x48;
53
    const BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c;
54
55
    // property storage offsets (directory offsets)
56
    const SIZE_OF_NAME_POS = 0x40;
57
    const TYPE_POS = 0x42;
58
    const START_BLOCK_POS = 0x74;
59
    const SIZE_POS = 0x78;
60
61
    public $wrkbook = null;
62
    public $summaryInformation = null;
63
    public $documentSummaryInformation = null;
64
65
    /**
66
     * Read the file.
67
     *
68
     * @param $pFilename string Filename
69
     *
70
     * @throws ReaderException
71
     */
72 5
    public function read($pFilename)
73
    {
74 5
        File::assertFile($pFilename);
75
76
        // Get the file identifier
77
        // Don't bother reading the whole file until we know it's a valid OLE file
78 5
        $this->data = file_get_contents($pFilename, false, null, 0, 8);
79
80
        // Check OLE identifier
81 5
        $identifierOle = pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1);
82 5
        if ($this->data != $identifierOle) {
83
            throw new ReaderException('The filename ' . $pFilename . ' is not recognised as an OLE file');
84
        }
85
86
        // Get the file data
87 5
        $this->data = file_get_contents($pFilename);
88
89
        // Total number of sectors used for the SAT
90 5
        $this->numBigBlockDepotBlocks = self::getInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
0 ignored issues
show
Bug introduced by
The property numBigBlockDepotBlocks does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
91
92
        // SecID of the first sector of the directory stream
93 5
        $this->rootStartBlock = self::getInt4d($this->data, self::ROOT_START_BLOCK_POS);
0 ignored issues
show
Bug introduced by
The property rootStartBlock does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
94
95
        // SecID of the first sector of the SSAT (or -2 if not extant)
96 5
        $this->sbdStartBlock = self::getInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS);
0 ignored issues
show
Bug introduced by
The property sbdStartBlock does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
97
98
        // SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
99 5
        $this->extensionBlock = self::getInt4d($this->data, self::EXTENSION_BLOCK_POS);
0 ignored issues
show
Bug introduced by
The property extensionBlock does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
100
101
        // Total number of sectors used by MSAT
102 5
        $this->numExtensionBlocks = self::getInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS);
0 ignored issues
show
Bug introduced by
The property numExtensionBlocks does not seem to exist. Did you mean extensionBlock?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
103
104 5
        $bigBlockDepotBlocks = [];
105 5
        $pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
106
107 5
        $bbdBlocks = $this->numBigBlockDepotBlocks;
108
109 5
        if ($this->numExtensionBlocks != 0) {
0 ignored issues
show
Bug introduced by
The property numExtensionBlocks does not seem to exist. Did you mean extensionBlock?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
110
            $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS) / 4;
111
        }
112
113 5 View Code Duplication
        for ($i = 0; $i < $bbdBlocks; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
114 5
            $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
115 5
            $pos += 4;
116
        }
117
118 5
        for ($j = 0; $j < $this->numExtensionBlocks; ++$j) {
0 ignored issues
show
Bug introduced by
The property numExtensionBlocks does not seem to exist. Did you mean extensionBlock?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
119
            $pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
120
            $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
121
122 View Code Duplication
            for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123
                $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
124
                $pos += 4;
125
            }
126
127
            $bbdBlocks += $blocksToRead;
128
            if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
129
                $this->extensionBlock = self::getInt4d($this->data, $pos);
130
            }
131
        }
132
133 5
        $pos = 0;
0 ignored issues
show
Unused Code introduced by
$pos is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
134 5
        $this->bigBlockChain = '';
0 ignored issues
show
Bug introduced by
The property bigBlockChain does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
135 5
        $bbs = self::BIG_BLOCK_SIZE / 4;
136 5
        for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) {
137 5
            $pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
138
139 5
            $this->bigBlockChain .= substr($this->data, $pos, 4 * $bbs);
140 5
            $pos += 4 * $bbs;
141
        }
142
143 5
        $pos = 0;
0 ignored issues
show
Unused Code introduced by
$pos is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
144 5
        $sbdBlock = $this->sbdStartBlock;
145 5
        $this->smallBlockChain = '';
0 ignored issues
show
Bug introduced by
The property smallBlockChain does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
146 5
        while ($sbdBlock != -2) {
147 5
            $pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
148
149 5
            $this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
150 5
            $pos += 4 * $bbs;
151
152 5
            $sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock * 4);
153
        }
154
155
        // read the directory stream
156 5
        $block = $this->rootStartBlock;
157 5
        $this->entry = $this->_readData($block);
0 ignored issues
show
Bug introduced by
The property entry does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
158
159 5
        $this->readPropertySets();
160 5
    }
161
162
    /**
163
     * Extract binary stream data.
164
     *
165
     * @param int $stream
166
     *
167
     * @return string
168
     */
169 4
    public function getStream($stream)
170
    {
171 4
        if ($stream === null) {
172
            return null;
173
        }
174
175 4
        $streamData = '';
176
177 4
        if ($this->props[$stream]['size'] < self::SMALL_BLOCK_THRESHOLD) {
178 4
            $rootdata = $this->_readData($this->props[$this->rootentry]['startBlock']);
0 ignored issues
show
Bug introduced by
The property props does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property rootentry does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
179
180 4
            $block = $this->props[$stream]['startBlock'];
181
182 4
            while ($block != -2) {
183 4
                $pos = $block * self::SMALL_BLOCK_SIZE;
184 4
                $streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
185
186 4
                $block = self::getInt4d($this->smallBlockChain, $block * 4);
187
            }
188
189 4
            return $streamData;
190
        }
191 4
        $numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE;
192 4
        if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) {
193 4
            ++$numBlocks;
194
        }
195
196 4
        if ($numBlocks == 0) {
197
            return '';
198
        }
199
200 4
        $block = $this->props[$stream]['startBlock'];
201
202 4 View Code Duplication
        while ($block != -2) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203 4
            $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
204 4
            $streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
205 4
            $block = self::getInt4d($this->bigBlockChain, $block * 4);
206
        }
207
208 4
        return $streamData;
209
    }
210
211
    /**
212
     * Read a standard stream (by joining sectors using information from SAT).
213
     *
214
     * @param int $bl Sector ID where the stream starts
215
     *
216
     * @return string Data for standard stream
217
     */
218 5
    private function _readData($bl)
219
    {
220 5
        $block = $bl;
221 5
        $data = '';
222
223 5 View Code Duplication
        while ($block != -2) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
224 5
            $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
225 5
            $data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
226 5
            $block = self::getInt4d($this->bigBlockChain, $block * 4);
227
        }
228
229 5
        return $data;
230
    }
231
232
    /**
233
     * Read entries in the directory stream.
234
     */
235 5
    private function readPropertySets()
236
    {
237 5
        $offset = 0;
238
239
        // loop through entires, each entry is 128 bytes
240 5
        $entryLen = strlen($this->entry);
241 5
        while ($offset < $entryLen) {
242
            // entry data (128 bytes)
243 5
            $d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
244
245
            // size in bytes of name
246 5
            $nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS + 1]) << 8);
247
248
            // type of entry
249 5
            $type = ord($d[self::TYPE_POS]);
250
251
            // sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
252
            // sectorID of first sector of the short-stream container stream, if this entry is root entry
253 5
            $startBlock = self::getInt4d($d, self::START_BLOCK_POS);
254
255 5
            $size = self::getInt4d($d, self::SIZE_POS);
256
257 5
            $name = str_replace("\x00", '', substr($d, 0, $nameSize));
258
259 5
            $this->props[] = [
260 5
                'name' => $name,
261 5
                'type' => $type,
262 5
                'startBlock' => $startBlock,
263 5
                'size' => $size,
264
            ];
265
266
            // tmp helper to simplify checks
267 5
            $upName = strtoupper($name);
268
269
            // Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
270 5
            if (($upName === 'WORKBOOK') || ($upName === 'BOOK')) {
271 5
                $this->wrkbook = count($this->props) - 1;
272 5
            } elseif ($upName === 'ROOT ENTRY' || $upName === 'R') {
273
                // Root entry
274 5
                $this->rootentry = count($this->props) - 1;
275
            }
276
277
            // Summary information
278 5
            if ($name == chr(5) . 'SummaryInformation') {
279 5
                $this->summaryInformation = count($this->props) - 1;
280
            }
281
282
            // Additional Document Summary information
283 5
            if ($name == chr(5) . 'DocumentSummaryInformation') {
284 5
                $this->documentSummaryInformation = count($this->props) - 1;
285
            }
286
287 5
            $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
288
        }
289 5
    }
290
291
    /**
292
     * Read 4 bytes of data at specified position.
293
     *
294
     * @param string $data
295
     * @param int $pos
296
     *
297
     * @return int
298
     */
299 5
    private static function getInt4d($data, $pos)
300
    {
301 5
        if (trim($data) == '') {
302
            // No data provided
303
            throw new ReaderException('Parameter data is empty.');
304 5
        } elseif ($pos < 0) {
305
            // Invalid position
306
            throw new ReaderException('Parameter pos=' . $pos . ' is invalid.');
307
        }
308
309 5
        $len = strlen($data);
310 5
        if ($len < $pos + 4) {
311
            $data .= str_repeat("\0", $pos + 4 - $len);
312
        }
313
314
        // FIX: represent numbers correctly on 64-bit system
315
        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
316
        // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
317 5
        $_or_24 = ord($data[$pos + 3]);
318 5
        if ($_or_24 >= 128) {
319
            // negative number
320 5
            $_ord_24 = -abs((256 - $_or_24) << 24);
321
        } else {
322 5
            $_ord_24 = ($_or_24 & 127) << 24;
323
        }
324
325 5
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
326
    }
327
}
328