Completed
Push — develop ( 3cc80b...4b4831 )
by Adrien
20:56
created

OLERead::readPropertySets()   B

Complexity

Conditions 8
Paths 13

Size

Total Lines 55
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 25
c 0
b 0
f 0
nc 13
nop 0
dl 0
loc 55
ccs 26
cts 26
cp 1
crap 8
rs 7.4033

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 30.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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
 * @version    ##VERSION##, ##DATE##
28
 */
29
30 4
defined('IDENTIFIER_OLE') ||
31 4
    define('IDENTIFIER_OLE', pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1));
32
33
class OLERead
34
{
35
    private $data = '';
36
37
    // OLE identifier
38
    const IDENTIFIER_OLE = IDENTIFIER_OLE;
39
40
    // Size of a sector = 512 bytes
41
    const BIG_BLOCK_SIZE = 0x200;
42
43
    // Size of a short sector = 64 bytes
44
    const SMALL_BLOCK_SIZE = 0x40;
45
46
    // Size of a directory entry always = 128 bytes
47
    const PROPERTY_STORAGE_BLOCK_SIZE = 0x80;
48
49
    // Minimum size of a standard stream = 4096 bytes, streams smaller than this are stored as short streams
50
    const SMALL_BLOCK_THRESHOLD = 0x1000;
51
52
    // header offsets
53
    const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c;
54
    const ROOT_START_BLOCK_POS = 0x30;
55
    const SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c;
56
    const EXTENSION_BLOCK_POS = 0x44;
57
    const NUM_EXTENSION_BLOCK_POS = 0x48;
58
    const BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c;
59
60
    // property storage offsets (directory offsets)
61
    const SIZE_OF_NAME_POS = 0x40;
62
    const TYPE_POS = 0x42;
63
    const START_BLOCK_POS = 0x74;
64
    const SIZE_POS = 0x78;
65
66
    public $wrkbook = null;
67
    public $summaryInformation = null;
68
    public $documentSummaryInformation = null;
69
70
    /**
71
     * Read the file
72
     *
73
     * @param $sFileName string Filename
74
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
75
     */
76 4
    public function read($sFileName)
77
    {
78
        // Check if file exists and is readable
79 4
        if (!is_readable($sFileName)) {
80
            throw new \PhpOffice\PhpSpreadsheet\Reader\Exception('Could not open ' . $sFileName . ' for reading! File does not exist, or it is not readable.');
81
        }
82
83
        // Get the file identifier
84
        // Don't bother reading the whole file until we know it's a valid OLE file
85 4
        $this->data = file_get_contents($sFileName, false, null, 0, 8);
86
87
        // Check OLE identifier
88 4
        if ($this->data != self::IDENTIFIER_OLE) {
89
            throw new \PhpOffice\PhpSpreadsheet\Reader\Exception('The filename ' . $sFileName . ' is not recognised as an OLE file');
90
        }
91
92
        // Get the file data
93 4
        $this->data = file_get_contents($sFileName);
94
95
        // Total number of sectors used for the SAT
96 4
        $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...
97
98
        // SecID of the first sector of the directory stream
99 4
        $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...
100
101
        // SecID of the first sector of the SSAT (or -2 if not extant)
102 4
        $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...
103
104
        // SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
105 4
        $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...
106
107
        // Total number of sectors used by MSAT
108 4
        $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...
109
110 4
        $bigBlockDepotBlocks = [];
111 4
        $pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
112
113 4
        $bbdBlocks = $this->numBigBlockDepotBlocks;
114
115 4
        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...
116
            $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS) / 4;
117
        }
118
119 4 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...
120 4
            $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
121 4
            $pos += 4;
122
        }
123
124 4
        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...
125
            $pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
126
            $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
127
128 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...
129
                $bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
130
                $pos += 4;
131
            }
132
133
            $bbdBlocks += $blocksToRead;
134
            if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
135
                $this->extensionBlock = self::getInt4d($this->data, $pos);
136
            }
137
        }
138
139 4
        $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...
140 4
        $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...
141 4
        $bbs = self::BIG_BLOCK_SIZE / 4;
142 4
        for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) {
143 4
            $pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
144
145 4
            $this->bigBlockChain .= substr($this->data, $pos, 4 * $bbs);
146 4
            $pos += 4 * $bbs;
147
        }
148
149 4
        $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...
150 4
        $sbdBlock = $this->sbdStartBlock;
151 4
        $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...
152 4
        while ($sbdBlock != -2) {
153 4
            $pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
154
155 4
            $this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
156 4
            $pos += 4 * $bbs;
157
158 4
            $sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock * 4);
159
        }
160
161
        // read the directory stream
162 4
        $block = $this->rootStartBlock;
163 4
        $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...
164
165 4
        $this->readPropertySets();
166 4
    }
167
168
    /**
169
     * Extract binary stream data
170
     *
171
     * @param int $stream
172
     * @return string
173
     */
174 4
    public function getStream($stream)
175
    {
176 4
        if ($stream === null) {
177
            return null;
178
        }
179
180 4
        $streamData = '';
181
182 4
        if ($this->props[$stream]['size'] < self::SMALL_BLOCK_THRESHOLD) {
183 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...
184
185 4
            $block = $this->props[$stream]['startBlock'];
186
187 4
            while ($block != -2) {
188 4
                $pos = $block * self::SMALL_BLOCK_SIZE;
189 4
                $streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
190
191 4
                $block = self::getInt4d($this->smallBlockChain, $block * 4);
192
            }
193
194 4
            return $streamData;
195
        } else {
196 4
            $numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE;
197 4
            if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) {
198 4
                ++$numBlocks;
199
            }
200
201 4
            if ($numBlocks == 0) {
202
                return '';
203
            }
204
205 4
            $block = $this->props[$stream]['startBlock'];
206
207 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...
208 4
                $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
209 4
                $streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
210 4
                $block = self::getInt4d($this->bigBlockChain, $block * 4);
211
            }
212
213 4
            return $streamData;
214
        }
215
    }
216
217
    /**
218
     * Read a standard stream (by joining sectors using information from SAT)
219
     *
220
     * @param int $bl Sector ID where the stream starts
221
     * @return string Data for standard stream
222
     */
223 4
    private function _readData($bl)
224
    {
225 4
        $block = $bl;
226 4
        $data = '';
227
228 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...
229 4
            $pos = ($block + 1) * self::BIG_BLOCK_SIZE;
230 4
            $data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
231 4
            $block = self::getInt4d($this->bigBlockChain, $block * 4);
232
        }
233
234 4
        return $data;
235
    }
236
237
    /**
238
     * Read entries in the directory stream.
239
     */
240 4
    private function readPropertySets()
241
    {
242 4
        $offset = 0;
243
244
        // loop through entires, each entry is 128 bytes
245 4
        $entryLen = strlen($this->entry);
246 4
        while ($offset < $entryLen) {
247
            // entry data (128 bytes)
248 4
            $d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
249
250
            // size in bytes of name
251 4
            $nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS + 1]) << 8);
252
253
            // type of entry
254 4
            $type = ord($d[self::TYPE_POS]);
255
256
            // sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
257
            // sectorID of first sector of the short-stream container stream, if this entry is root entry
258 4
            $startBlock = self::getInt4d($d, self::START_BLOCK_POS);
259
260 4
            $size = self::getInt4d($d, self::SIZE_POS);
261
262 4
            $name = str_replace("\x00", '', substr($d, 0, $nameSize));
263
264 4
            $this->props[] = [
265 4
                'name' => $name,
266 4
                'type' => $type,
267 4
                'startBlock' => $startBlock,
268 4
                'size' => $size,
269
            ];
270
271
            // tmp helper to simplify checks
272 4
            $upName = strtoupper($name);
273
274
            // Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
275 4
            if (($upName === 'WORKBOOK') || ($upName === 'BOOK')) {
276 4
                $this->wrkbook = count($this->props) - 1;
277 4
            } elseif ($upName === 'ROOT ENTRY' || $upName === 'R') {
278
                // Root entry
279 4
                $this->rootentry = count($this->props) - 1;
280
            }
281
282
            // Summary information
283 4
            if ($name == chr(5) . 'SummaryInformation') {
284 4
                $this->summaryInformation = count($this->props) - 1;
285
            }
286
287
            // Additional Document Summary information
288 4
            if ($name == chr(5) . 'DocumentSummaryInformation') {
289 4
                $this->documentSummaryInformation = count($this->props) - 1;
290
            }
291
292 4
            $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
293
        }
294 4
    }
295
296
    /**
297
     * Read 4 bytes of data at specified position
298
     *
299
     * @param string $data
300
     * @param int $pos
301
     * @return int
302
     */
303 4
    private static function getInt4d($data, $pos)
304
    {
305 4
        if (trim($data) == '') {
306
            // No data provided
307
            throw new \PhpOffice\PhpSpreadsheet\Reader\Exception('Parameter data is empty.');
308 4
        } elseif ($pos < 0) {
309
            // Invalid position
310
            throw new \PhpOffice\PhpSpreadsheet\Reader\Exception('Parameter pos=' . $pos . ' is invalid.');
311
        }
312
313 4
        $len = strlen($data);
314 4
        if ($len < $pos + 4) {
315
            $data .= str_repeat("\0", $pos + 4 - $len);
316
        }
317
318
        // FIX: represent numbers correctly on 64-bit system
319
        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
320
        // Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
321 4
        $_or_24 = ord($data[$pos + 3]);
322 4
        if ($_or_24 >= 128) {
323
            // negative number
324 4
            $_ord_24 = -abs((256 - $_or_24) << 24);
325
        } else {
326 4
            $_ord_24 = ($_or_24 & 127) << 24;
327
        }
328
329 4
        return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
330
    }
331
}
332