Test Failed
Push — develop ( 90366f...812a46 )
by Adrien
28:16
created

OLE::_readInt4()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 0
cts 3
cp 0
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Shared;
4
5
// vim: set expandtab tabstop=4 shiftwidth=4:
6
// +----------------------------------------------------------------------+
7
// | PHP Version 4                                                        |
8
// +----------------------------------------------------------------------+
9
// | Copyright (c) 1997-2002 The PHP Group                                |
10
// +----------------------------------------------------------------------+
11
// | This source file is subject to version 2.02 of the PHP license,      |
12
// | that is bundled with this package in the file LICENSE, and is        |
13
// | available at through the world-wide-web at                           |
14
// | http://www.php.net/license/2_02.txt.                                 |
15
// | If you did not receive a copy of the PHP license and are unable to   |
16
// | obtain it through the world-wide-web, please send a note to          |
17
// | [email protected] so we can mail you a copy immediately.               |
18
// +----------------------------------------------------------------------+
19
// | Author: Xavier Noguer <[email protected]>                              |
20
// | Based on OLE::Storage_Lite by Kawai, Takanori                        |
21
// +----------------------------------------------------------------------+
22
//
23
24
/*
25
* Array for storing OLE instances that are accessed from
26
* OLE_ChainedBlockStream::stream_open().
27
* @var  array
28
*/
29
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
30
use PhpOffice\PhpSpreadsheet\Shared\OLE\ChainedBlockStream;
31
32 54
$GLOBALS['_OLE_INSTANCES'] = [];
33
34
/**
35
 * OLE package base class.
36
 *
37
 * @author   Xavier Noguer <[email protected]>
38
 * @author   Christian Schmidt <[email protected]>
39
 *
40
 * @category   PhpSpreadsheet
41
 */
42
class OLE
43
{
44
    const OLE_PPS_TYPE_ROOT = 5;
45
    const OLE_PPS_TYPE_DIR = 1;
46
    const OLE_PPS_TYPE_FILE = 2;
47
    const OLE_DATA_SIZE_SMALL = 0x1000;
48
    const OLE_LONG_INT_SIZE = 4;
49
    const OLE_PPS_SIZE = 0x80;
50
51
    /**
52
     * The file handle for reading an OLE container.
53
     *
54
     * @var resource
55
     */
56
    public $_file_handle;
57
58
    /**
59
     * Array of PPS's found on the OLE container.
60
     *
61
     * @var array
62
     */
63
    public $_list = [];
64
65
    /**
66
     * Root directory of OLE container.
67
     *
68
     * @var OLE_PPS_Root
0 ignored issues
show
Bug introduced by
The type PhpOffice\PhpSpreadsheet\Shared\OLE_PPS_Root was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
69
     */
70
    public $root;
71
72
    /**
73
     * Big Block Allocation Table.
74
     *
75
     * @var array (blockId => nextBlockId)
76
     */
77
    public $bbat;
78
79
    /**
80
     * Short Block Allocation Table.
81
     *
82
     * @var array (blockId => nextBlockId)
83
     */
84
    public $sbat;
85
86
    /**
87
     * Size of big blocks. This is usually 512.
88
     *
89
     * @var int number of octets per block
90
     */
91
    public $bigBlockSize;
92
93
    /**
94
     * Size of small blocks. This is usually 64.
95
     *
96
     * @var int number of octets per block
97
     */
98
    public $smallBlockSize;
99
100
    /**
101
     * Threshold for big blocks.
102
     *
103
     * @var int
104
     */
105
    private $bigBlockThreshold;
106
107
    /**
108
     * Reads an OLE container from the contents of the file given.
109
     *
110
     * @acces public
111
     *
112
     * @param string $file
113
     *
114
     * @throws ReaderException
115
     *
116
     * @return bool true on success, PEAR_Error on failure
117
     */
118
    public function read($file)
119
    {
120
        $fh = fopen($file, 'r');
121
        if (!$fh) {
122
            throw new ReaderException("Can't open file $file");
123
        }
124
        $this->_file_handle = $fh;
125
126
        $signature = fread($fh, 8);
127
        if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
128
            throw new ReaderException("File doesn't seem to be an OLE container.");
129
        }
130
        fseek($fh, 28);
131
        if (fread($fh, 2) != "\xFE\xFF") {
132
            // This shouldn't be a problem in practice
133
            throw new ReaderException('Only Little-Endian encoding is supported.');
134
        }
135
        // Size of blocks and short blocks in bytes
136
        $this->bigBlockSize = pow(2, self::_readInt2($fh));
137
        $this->smallBlockSize = pow(2, self::_readInt2($fh));
138
139
        // Skip UID, revision number and version number
140
        fseek($fh, 44);
141
        // Number of blocks in Big Block Allocation Table
142
        $bbatBlockCount = self::_readInt4($fh);
143
144
        // Root chain 1st block
145
        $directoryFirstBlockId = self::_readInt4($fh);
146
147
        // Skip unused bytes
148
        fseek($fh, 56);
149
        // Streams shorter than this are stored using small blocks
150
        $this->bigBlockThreshold = self::_readInt4($fh);
151
        // Block id of first sector in Short Block Allocation Table
152
        $sbatFirstBlockId = self::_readInt4($fh);
153
        // Number of blocks in Short Block Allocation Table
154
        $sbbatBlockCount = self::_readInt4($fh);
155
        // Block id of first sector in Master Block Allocation Table
156
        $mbatFirstBlockId = self::_readInt4($fh);
157
        // Number of blocks in Master Block Allocation Table
158
        $mbbatBlockCount = self::_readInt4($fh);
159
        $this->bbat = [];
160
161
        // Remaining 4 * 109 bytes of current block is beginning of Master
162
        // Block Allocation Table
163
        $mbatBlocks = [];
164
        for ($i = 0; $i < 109; ++$i) {
165
            $mbatBlocks[] = self::_readInt4($fh);
166
        }
167
168
        // Read rest of Master Block Allocation Table (if any is left)
169
        $pos = $this->_getBlockOffset($mbatFirstBlockId);
170 View Code Duplication
        for ($i = 0; $i < $mbbatBlockCount; ++$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...
171
            fseek($fh, $pos);
172
            for ($j = 0; $j < $this->bigBlockSize / 4 - 1; ++$j) {
173
                $mbatBlocks[] = self::_readInt4($fh);
174
            }
175
            // Last block id in each block points to next block
176
            $pos = $this->_getBlockOffset(self::_readInt4($fh));
177
        }
178
179
        // Read Big Block Allocation Table according to chain specified by $mbatBlocks
180 View Code Duplication
        for ($i = 0; $i < $bbatBlockCount; ++$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...
181
            $pos = $this->_getBlockOffset($mbatBlocks[$i]);
182
            fseek($fh, $pos);
183
            for ($j = 0; $j < $this->bigBlockSize / 4; ++$j) {
184
                $this->bbat[] = self::_readInt4($fh);
185
            }
186
        }
187
188
        // Read short block allocation table (SBAT)
189
        $this->sbat = [];
190
        $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4;
191
        $sbatFh = $this->getStream($sbatFirstBlockId);
192
        for ($blockId = 0; $blockId < $shortBlockCount; ++$blockId) {
193
            $this->sbat[$blockId] = self::_readInt4($sbatFh);
194
        }
195
        fclose($sbatFh);
196
197
        $this->_readPpsWks($directoryFirstBlockId);
198
199
        return true;
200
    }
201
202
    /**
203
     * @param int block id
204
     * @param int byte offset from beginning of file
0 ignored issues
show
Bug introduced by
The type PhpOffice\PhpSpreadsheet\Shared\byte was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
205
     * @param mixed $blockId
206
     */
207
    public function _getBlockOffset($blockId)
208
    {
209
        return 512 + $blockId * $this->bigBlockSize;
210
    }
211
212
    /**
213
     * Returns a stream for use with fread() etc. External callers should
214
     * use \PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\File::getStream().
215
     *
216
     * @param int|PPS block id or PPS
0 ignored issues
show
Bug introduced by
The type PhpOffice\PhpSpreadsheet\Shared\block was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
217
     * @param mixed $blockIdOrPps
218
     *
219
     * @return resource read-only stream
220
     */
221
    public function getStream($blockIdOrPps)
222
    {
223
        static $isRegistered = false;
224
        if (!$isRegistered) {
225
            stream_wrapper_register('ole-chainedblockstream', ChainedBlockStream::class);
226
            $isRegistered = true;
227
        }
228
229
        // Store current instance in global array, so that it can be accessed
230
        // in OLE_ChainedBlockStream::stream_open().
231
        // Object is removed from self::$instances in OLE_Stream::close().
232
        $GLOBALS['_OLE_INSTANCES'][] = $this;
233
        $instanceId = end(array_keys($GLOBALS['_OLE_INSTANCES']));
234
235
        $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId;
236
        if ($blockIdOrPps instanceof OLE\PPS) {
237
            $path .= '&blockId=' . $blockIdOrPps->startBlock;
238
            $path .= '&size=' . $blockIdOrPps->Size;
239
        } else {
240
            $path .= '&blockId=' . $blockIdOrPps;
241
        }
242
243
        return fopen($path, 'r');
0 ignored issues
show
Bug Best Practice introduced by
The expression return fopen($path, 'r') could also return false which is incompatible with the documented return type resource. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
244
    }
245
246
    /**
247
     * Reads a signed char.
248
     *
249
     * @param resource $fh file handle
250
     *
251
     * @return int
252
     */
253
    private static function _readInt1($fh)
254
    {
255
        list(, $tmp) = unpack('c', fread($fh, 1));
1 ignored issue
show
Bug introduced by
The call to unpack() has too few arguments starting with offset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

255
        list(, $tmp) = /** @scrutinizer ignore-call */ unpack('c', fread($fh, 1));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
It seems like fread($fh, 1) can also be of type false; however, parameter $data of unpack() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

255
        list(, $tmp) = unpack('c', /** @scrutinizer ignore-type */ fread($fh, 1));
Loading history...
256
257
        return $tmp;
258
    }
259
260
    /**
261
     * Reads an unsigned short (2 octets).
262
     *
263
     * @param resource $fh file handle
264
     *
265
     * @return int
266
     */
267
    private static function _readInt2($fh)
268
    {
269
        list(, $tmp) = unpack('v', fread($fh, 2));
1 ignored issue
show
Bug introduced by
It seems like fread($fh, 2) can also be of type false; however, parameter $data of unpack() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

269
        list(, $tmp) = unpack('v', /** @scrutinizer ignore-type */ fread($fh, 2));
Loading history...
Bug introduced by
The call to unpack() has too few arguments starting with offset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

269
        list(, $tmp) = /** @scrutinizer ignore-call */ unpack('v', fread($fh, 2));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
270
271
        return $tmp;
272
    }
273
274
    /**
275
     * Reads an unsigned long (4 octets).
276
     *
277
     * @param resource $fh file handle
278
     *
279
     * @return int
280
     */
281
    private static function _readInt4($fh)
282
    {
283
        list(, $tmp) = unpack('V', fread($fh, 4));
1 ignored issue
show
Bug introduced by
The call to unpack() has too few arguments starting with offset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

283
        list(, $tmp) = /** @scrutinizer ignore-call */ unpack('V', fread($fh, 4));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
It seems like fread($fh, 4) can also be of type false; however, parameter $data of unpack() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

283
        list(, $tmp) = unpack('V', /** @scrutinizer ignore-type */ fread($fh, 4));
Loading history...
284
285
        return $tmp;
286
    }
287
288
    /**
289
     * Gets information about all PPS's on the OLE container from the PPS WK's
290
     * creates an OLE_PPS object for each one.
291
     *
292
     * @param int $blockId the block id of the first block
293
     *
294
     * @return bool true on success, PEAR_Error on failure
295
     */
296
    public function _readPpsWks($blockId)
297
    {
298
        $fh = $this->getStream($blockId);
299
        for ($pos = 0; true; $pos += 128) {
300
            fseek($fh, $pos, SEEK_SET);
301
            $nameUtf16 = fread($fh, 64);
302
            $nameLength = self::_readInt2($fh);
303
            $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2);
1 ignored issue
show
Bug introduced by
It seems like $nameUtf16 can also be of type false; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

303
            $nameUtf16 = substr(/** @scrutinizer ignore-type */ $nameUtf16, 0, $nameLength - 2);
Loading history...
304
            // Simple conversion from UTF-16LE to ISO-8859-1
305
            $name = str_replace("\x00", '', $nameUtf16);
306
            $type = self::_readInt1($fh);
307
            switch ($type) {
308
                case self::OLE_PPS_TYPE_ROOT:
309
                    $pps = new OLE\PPS\Root(null, null, []);
310
                    $this->root = $pps;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pps of type PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\Root is incompatible with the declared type PhpOffice\PhpSpreadsheet\Shared\OLE_PPS_Root of property $root.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
311
312
                    break;
313
                case self::OLE_PPS_TYPE_DIR:
314
                    $pps = new OLE\PPS(null, null, null, null, null, null, null, null, null, []);
315
316
                    break;
317
                case self::OLE_PPS_TYPE_FILE:
318
                    $pps = new OLE\PPS\File($name);
319
320
                    break;
321
                default:
322
                    continue;
323
            }
324
            fseek($fh, 1, SEEK_CUR);
325
            $pps->Type = $type;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $pps does not seem to be defined for all execution paths leading up to this point.
Loading history...
326
            $pps->Name = $name;
327
            $pps->PrevPps = self::_readInt4($fh);
328
            $pps->NextPps = self::_readInt4($fh);
329
            $pps->DirPps = self::_readInt4($fh);
330
            fseek($fh, 20, SEEK_CUR);
331
            $pps->Time1st = self::OLE2LocalDate(fread($fh, 8));
0 ignored issues
show
Bug introduced by
fread($fh, 8) of type false|string is incompatible with the type integer expected by parameter $string of PhpOffice\PhpSpreadsheet...ed\OLE::OLE2LocalDate(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

331
            $pps->Time1st = self::OLE2LocalDate(/** @scrutinizer ignore-type */ fread($fh, 8));
Loading history...
Documentation Bug introduced by
The property $Time1st was declared of type integer, but self::OLE2LocalDate(fread($fh, 8)) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
332
            $pps->Time2nd = self::OLE2LocalDate(fread($fh, 8));
0 ignored issues
show
Documentation Bug introduced by
The property $Time2nd was declared of type integer, but self::OLE2LocalDate(fread($fh, 8)) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
333
            $pps->startBlock = self::_readInt4($fh);
334
            $pps->Size = self::_readInt4($fh);
335
            $pps->No = count($this->_list);
336
            $this->_list[] = $pps;
337
338
            // check if the PPS tree (starting from root) is complete
339
            if (isset($this->root) && $this->_ppsTreeComplete($this->root->No)) {
340
                break;
341
            }
342
        }
343
        fclose($fh);
344
345
        // Initialize $pps->children on directories
346
        foreach ($this->_list as $pps) {
347
            if ($pps->Type == self::OLE_PPS_TYPE_DIR || $pps->Type == self::OLE_PPS_TYPE_ROOT) {
348
                $nos = [$pps->DirPps];
349
                $pps->children = [];
350
                while ($nos) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nos of type array<integer,integer> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
351
                    $no = array_pop($nos);
352
                    if ($no != -1) {
353
                        $childPps = $this->_list[$no];
354
                        $nos[] = $childPps->PrevPps;
355
                        $nos[] = $childPps->NextPps;
356
                        $pps->children[] = $childPps;
357
                    }
358
                }
359
            }
360
        }
361
362
        return true;
363
    }
364
365
    /**
366
     * It checks whether the PPS tree is complete (all PPS's read)
367
     * starting with the given PPS (not necessarily root).
368
     *
369
     * @param int $index The index of the PPS from which we are checking
370
     *
371
     * @return bool Whether the PPS tree for the given PPS is complete
372
     */
373
    public function _ppsTreeComplete($index)
374
    {
375
        return isset($this->_list[$index]) &&
376
               ($pps = $this->_list[$index]) &&
377
               ($pps->PrevPps == -1 ||
378
                $this->_ppsTreeComplete($pps->PrevPps)) &&
379
               ($pps->NextPps == -1 ||
380
                $this->_ppsTreeComplete($pps->NextPps)) &&
381
               ($pps->DirPps == -1 ||
382
                $this->_ppsTreeComplete($pps->DirPps));
383
    }
384
385
    /**
386
     * Checks whether a PPS is a File PPS or not.
387
     * If there is no PPS for the index given, it will return false.
388
     *
389
     * @param int $index The index for the PPS
390
     *
391
     * @return bool true if it's a File PPS, false otherwise
392
     */
393 View Code Duplication
    public function isFile($index)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
394
    {
395
        if (isset($this->_list[$index])) {
396
            return $this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE;
397
        }
398
399
        return false;
400
    }
401
402
    /**
403
     * Checks whether a PPS is a Root PPS or not.
404
     * If there is no PPS for the index given, it will return false.
405
     *
406
     * @param int $index the index for the PPS
407
     *
408
     * @return bool true if it's a Root PPS, false otherwise
409
     */
410 View Code Duplication
    public function isRoot($index)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
411
    {
412
        if (isset($this->_list[$index])) {
413
            return $this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT;
414
        }
415
416
        return false;
417
    }
418
419
    /**
420
     * Gives the total number of PPS's found in the OLE container.
421
     *
422
     * @return int The total number of PPS's found in the OLE container
423
     */
424
    public function ppsTotal()
425
    {
426
        return count($this->_list);
427
    }
428
429
    /**
430
     * Gets data from a PPS
431
     * If there is no PPS for the index given, it will return an empty string.
432
     *
433
     * @param int $index The index for the PPS
434
     * @param int $position The position from which to start reading
435
     *                          (relative to the PPS)
436
     * @param int $length The amount of bytes to read (at most)
437
     *
438
     * @return string The binary string containing the data requested
439
     *
440
     * @see OLE_PPS_File::getStream()
441
     */
442
    public function getData($index, $position, $length)
443
    {
444
        // if position is not valid return empty string
445
        if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) {
446
            return '';
447
        }
448
        $fh = $this->getStream($this->_list[$index]);
449
        $data = stream_get_contents($fh, $length, $position);
450
        fclose($fh);
451
452
        return $data;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $data could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
453
    }
454
455
    /**
456
     * Gets the data length from a PPS
457
     * If there is no PPS for the index given, it will return 0.
458
     *
459
     * @param int $index The index for the PPS
460
     *
461
     * @return int The amount of bytes in data the PPS has
462
     */
463
    public function getDataLength($index)
464
    {
465
        if (isset($this->_list[$index])) {
466
            return $this->_list[$index]->Size;
467
        }
468
469
        return 0;
470
    }
471
472
    /**
473
     * Utility function to transform ASCII text to Unicode.
474
     *
475
     * @param string $ascii The ASCII string to transform
476
     *
477
     * @return string The string in Unicode
478
     */
479 39
    public static function ascToUcs($ascii)
480
    {
481 39
        $rawname = '';
482 39
        for ($i = 0; $i < strlen($ascii); ++$i) {
483 39
            $rawname .= $ascii[$i]
484 39
            . "\x00";
485
        }
486
487 39
        return $rawname;
488
    }
489
490
    /**
491
     * Utility function
492
     * Returns a string for the OLE container with the date given.
493
     *
494
     * @param int $date A timestamp
495
     *
496
     * @return string The string for the OLE container
497
     */
498 39
    public static function localDateToOLE($date)
499
    {
500 39
        if (!isset($date)) {
501 39
            return "\x00\x00\x00\x00\x00\x00\x00\x00";
502
        }
503
504
        // factor used for separating numbers into 4 bytes parts
505 39
        $factor = pow(2, 32);
506
507
        // days from 1-1-1601 until the beggining of UNIX era
508 39
        $days = 134774;
509
        // calculate seconds
510 39
        $big_date = $days * 24 * 3600 + gmmktime(date('H', $date), date('i', $date), date('s', $date), date('m', $date), date('d', $date), date('Y', $date));
0 ignored issues
show
Bug introduced by
date('Y', $date) of type false|string is incompatible with the type integer expected by parameter $year of gmmktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

510
        $big_date = $days * 24 * 3600 + gmmktime(date('H', $date), date('i', $date), date('s', $date), date('m', $date), date('d', $date), /** @scrutinizer ignore-type */ date('Y', $date));
Loading history...
Bug introduced by
date('H', $date) of type false|string is incompatible with the type integer expected by parameter $hour of gmmktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

510
        $big_date = $days * 24 * 3600 + gmmktime(/** @scrutinizer ignore-type */ date('H', $date), date('i', $date), date('s', $date), date('m', $date), date('d', $date), date('Y', $date));
Loading history...
Bug introduced by
date('i', $date) of type false|string is incompatible with the type integer expected by parameter $minute of gmmktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

510
        $big_date = $days * 24 * 3600 + gmmktime(date('H', $date), /** @scrutinizer ignore-type */ date('i', $date), date('s', $date), date('m', $date), date('d', $date), date('Y', $date));
Loading history...
Bug introduced by
date('d', $date) of type false|string is incompatible with the type integer expected by parameter $day of gmmktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

510
        $big_date = $days * 24 * 3600 + gmmktime(date('H', $date), date('i', $date), date('s', $date), date('m', $date), /** @scrutinizer ignore-type */ date('d', $date), date('Y', $date));
Loading history...
Bug introduced by
date('s', $date) of type false|string is incompatible with the type integer expected by parameter $second of gmmktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

510
        $big_date = $days * 24 * 3600 + gmmktime(date('H', $date), date('i', $date), /** @scrutinizer ignore-type */ date('s', $date), date('m', $date), date('d', $date), date('Y', $date));
Loading history...
Bug introduced by
date('m', $date) of type false|string is incompatible with the type integer expected by parameter $month of gmmktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

510
        $big_date = $days * 24 * 3600 + gmmktime(date('H', $date), date('i', $date), date('s', $date), /** @scrutinizer ignore-type */ date('m', $date), date('d', $date), date('Y', $date));
Loading history...
511
        // multiply just to make MS happy
512 39
        $big_date *= 10000000;
513
514 39
        $high_part = floor($big_date / $factor);
515
        // lower 4 bytes
516 39
        $low_part = floor((($big_date / $factor) - $high_part) * $factor);
517
518
        // Make HEX string
519 39
        $res = '';
520
521 39 View Code Duplication
        for ($i = 0; $i < 4; ++$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...
522 39
            $hex = $low_part % 0x100;
523 39
            $res .= pack('c', $hex);
524 39
            $low_part /= 0x100;
525
        }
526 39 View Code Duplication
        for ($i = 0; $i < 4; ++$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...
527 39
            $hex = $high_part % 0x100;
528 39
            $res .= pack('c', $hex);
529 39
            $high_part /= 0x100;
530
        }
531
532 39
        return $res;
533
    }
534
535
    /**
536
     * Returns a timestamp from an OLE container's date.
537
     *
538
     * @param int $string A binary string with the encoded date
539
     *
540
     * @return string The timestamp corresponding to the string
541
     */
542 18
    public static function OLE2LocalDate($string)
543
    {
544 18
        if (strlen($string) != 8) {
545
            throw new ReaderException('Expecting 8 byte string');
546
        }
547
548
        // factor used for separating numbers into 4 bytes parts
549 18
        $factor = pow(2, 32);
550 18
        list(, $high_part) = unpack('V', substr($string, 4, 4));
1 ignored issue
show
Bug introduced by
The call to unpack() has too few arguments starting with offset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

550
        list(, $high_part) = /** @scrutinizer ignore-call */ unpack('V', substr($string, 4, 4));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
It seems like substr($string, 4, 4) can also be of type false; however, parameter $data of unpack() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

550
        list(, $high_part) = unpack('V', /** @scrutinizer ignore-type */ substr($string, 4, 4));
Loading history...
551 18
        list(, $low_part) = unpack('V', substr($string, 0, 4));
552
553 18
        $big_date = ($high_part * $factor) + $low_part;
554
        // translate to seconds
555 18
        $big_date /= 10000000;
556
557
        // days from 1-1-1601 until the beggining of UNIX era
558 18
        $days = 134774;
559
560
        // translate to seconds from beggining of UNIX era
561 18
        $big_date -= $days * 24 * 3600;
562
563 18
        return floor($big_date);
564
    }
565
}
566