Completed
Push — develop ( 5e03e2...c8a8fd )
by Adrien
28:49
created

OLERead::read()   C

Complexity

Conditions 9
Paths 81

Size

Total Lines 88
Code Lines 46

Duplication

Lines 8
Ratio 9.09 %

Code Coverage

Tests 37
CRAP Score 9.7805

Importance

Changes 0
Metric Value
cc 9
eloc 46
nc 81
nop 1
dl 8
loc 88
ccs 37
cts 47
cp 0.7872
crap 9.7805
rs 5.2636
c 0
b 0
f 0

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 30 and the first side effect is on line 29.

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