Completed
Push — develop ( 685e29...09d456 )
by Adrien
14:14
created

OLE::getStream()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 14
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 24
ccs 0
cts 17
cp 0
crap 12
rs 8.9713
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 39 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 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
// $Id: OLE.php,v 1.13 2007/03/07 14:38:25 schmidt Exp $
24
25
/*
26
* Array for storing OLE instances that are accessed from
27
* OLE_ChainedBlockStream::stream_open().
28
* @var  array
29
*/
30
$GLOBALS['_OLE_INSTANCES'] = [];
31
32
/**
33
 * OLE package base class.
34
 *
35
 * @author   Xavier Noguer <[email protected]>
36
 * @author   Christian Schmidt <[email protected]>
37
 * @category   PhpSpreadsheet
38
 */
39
class OLE
40
{
41
    const OLE_PPS_TYPE_ROOT = 5;
42
    const OLE_PPS_TYPE_DIR = 1;
43
    const OLE_PPS_TYPE_FILE = 2;
44
    const OLE_DATA_SIZE_SMALL = 0x1000;
45
    const OLE_LONG_INT_SIZE = 4;
46
    const OLE_PPS_SIZE = 0x80;
47
48
    /**
49
     * The file handle for reading an OLE container
50
     * @var resource
51
     */
52
    public $_file_handle;
53
54
    /**
55
     * Array of PPS's found on the OLE container
56
     * @var array
57
     */
58
    public $_list = [];
59
60
    /**
61
     * Root directory of OLE container
62
     * @var OLE_PPS_Root
63
     */
64
    public $root;
65
66
    /**
67
     * Big Block Allocation Table
68
     * @var array  (blockId => nextBlockId)
69
     */
70
    public $bbat;
71
72
    /**
73
     * Short Block Allocation Table
74
     * @var array  (blockId => nextBlockId)
75
     */
76
    public $sbat;
77
78
    /**
79
     * Size of big blocks. This is usually 512.
80
     * @var  int  number of octets per block.
81
     */
82
    public $bigBlockSize;
83
84
    /**
85
     * Size of small blocks. This is usually 64.
86
     * @var  int  number of octets per block
87
     */
88
    public $smallBlockSize;
89
90
    /**
91
     * Reads an OLE container from the contents of the file given.
92
     *
93
     * @acces public
94
     * @param string $file
95
     * @throws \PhpSpreadsheet\Reader\Exception
96
     * @return mixed true on success, PEAR_Error on failure
97
     */
98
    public function read($file)
99
    {
100
        $fh = fopen($file, 'r');
101
        if (!$fh) {
102
            throw new \PhpSpreadsheet\Reader\Exception("Can't open file $file");
103
        }
104
        $this->_file_handle = $fh;
105
106
        $signature = fread($fh, 8);
107
        if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
108
            throw new \PhpSpreadsheet\Reader\Exception("File doesn't seem to be an OLE container.");
109
        }
110
        fseek($fh, 28);
111
        if (fread($fh, 2) != "\xFE\xFF") {
112
            // This shouldn't be a problem in practice
113
            throw new \PhpSpreadsheet\Reader\Exception('Only Little-Endian encoding is supported.');
114
        }
115
        // Size of blocks and short blocks in bytes
116
        $this->bigBlockSize = pow(2, self::_readInt2($fh));
0 ignored issues
show
Documentation Bug introduced by
It seems like pow(2, self::_readInt2($fh)) can also be of type double. However, the property $bigBlockSize is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
117
        $this->smallBlockSize = pow(2, self::_readInt2($fh));
0 ignored issues
show
Documentation Bug introduced by
It seems like pow(2, self::_readInt2($fh)) can also be of type double. However, the property $smallBlockSize is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
118
119
        // Skip UID, revision number and version number
120
        fseek($fh, 44);
121
        // Number of blocks in Big Block Allocation Table
122
        $bbatBlockCount = self::_readInt4($fh);
123
124
        // Root chain 1st block
125
        $directoryFirstBlockId = self::_readInt4($fh);
126
127
        // Skip unused bytes
128
        fseek($fh, 56);
129
        // Streams shorter than this are stored using small blocks
130
        $this->bigBlockThreshold = self::_readInt4($fh);
0 ignored issues
show
Bug introduced by
The property bigBlockThreshold 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...
131
        // Block id of first sector in Short Block Allocation Table
132
        $sbatFirstBlockId = self::_readInt4($fh);
133
        // Number of blocks in Short Block Allocation Table
134
        $sbbatBlockCount = self::_readInt4($fh);
135
        // Block id of first sector in Master Block Allocation Table
136
        $mbatFirstBlockId = self::_readInt4($fh);
137
        // Number of blocks in Master Block Allocation Table
138
        $mbbatBlockCount = self::_readInt4($fh);
139
        $this->bbat = [];
140
141
        // Remaining 4 * 109 bytes of current block is beginning of Master
142
        // Block Allocation Table
143
        $mbatBlocks = [];
144
        for ($i = 0; $i < 109; ++$i) {
145
            $mbatBlocks[] = self::_readInt4($fh);
146
        }
147
148
        // Read rest of Master Block Allocation Table (if any is left)
149
        $pos = $this->_getBlockOffset($mbatFirstBlockId);
150 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...
151
            fseek($fh, $pos);
152
            for ($j = 0; $j < $this->bigBlockSize / 4 - 1; ++$j) {
153
                $mbatBlocks[] = self::_readInt4($fh);
154
            }
155
            // Last block id in each block points to next block
156
            $pos = $this->_getBlockOffset(self::_readInt4($fh));
157
        }
158
159
        // Read Big Block Allocation Table according to chain specified by
160
        // $mbatBlocks
161 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...
162
            $pos = $this->_getBlockOffset($mbatBlocks[$i]);
163
            fseek($fh, $pos);
164
            for ($j = 0; $j < $this->bigBlockSize / 4; ++$j) {
165
                $this->bbat[] = self::_readInt4($fh);
166
            }
167
        }
168
169
        // Read short block allocation table (SBAT)
170
        $this->sbat = [];
171
        $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4;
172
        $sbatFh = $this->getStream($sbatFirstBlockId);
173
        for ($blockId = 0; $blockId < $shortBlockCount; ++$blockId) {
174
            $this->sbat[$blockId] = self::_readInt4($sbatFh);
175
        }
176
        fclose($sbatFh);
177
178
        $this->_readPpsWks($directoryFirstBlockId);
179
180
        return true;
181
    }
182
183
    /**
184
     * @param  int  block id
185
     * @param  int  byte offset from beginning of file
186
     */
187
    public function _getBlockOffset($blockId)
188
    {
189
        return 512 + $blockId * $this->bigBlockSize;
190
    }
191
192
    /**
193
     * Returns a stream for use with fread() etc. External callers should
194
     * use \PhpSpreadsheet\Shared\OLE\PPS\File::getStream().
195
     * @param   int|PPS   block id or PPS
196
     * @return  resource  read-only stream
197
     */
198
    public function getStream($blockIdOrPps)
0 ignored issues
show
Coding Style introduced by
getStream uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
199
    {
200
        static $isRegistered = false;
201
        if (!$isRegistered) {
202
            stream_wrapper_register('ole-chainedblockstream', '\\PhpSpreadsheet\\Shared\\OLE\\ChainedBlockStream');
203
            $isRegistered = true;
204
        }
205
206
        // Store current instance in global array, so that it can be accessed
207
        // in OLE_ChainedBlockStream::stream_open().
208
        // Object is removed from self::$instances in OLE_Stream::close().
209
        $GLOBALS['_OLE_INSTANCES'][] = $this;
210
        $instanceId = end(array_keys($GLOBALS['_OLE_INSTANCES']));
0 ignored issues
show
Bug introduced by
array_keys($GLOBALS['_OLE_INSTANCES']) cannot be passed to end() as the parameter $array expects a reference.
Loading history...
211
212
        $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId;
213
        if ($blockIdOrPps instanceof OLE\PPS) {
214
            $path .= '&blockId=' . $blockIdOrPps->startBlock;
215
            $path .= '&size=' . $blockIdOrPps->Size;
216
        } else {
217
            $path .= '&blockId=' . $blockIdOrPps;
218
        }
219
220
        return fopen($path, 'r');
221
    }
222
223
    /**
224
     * Reads a signed char.
225
     * @param   resource  file handle
226
     * @return  int
227
     */
228
    private static function _readInt1($fh)
229
    {
230
        list(, $tmp) = unpack('c', fread($fh, 1));
231
232
        return $tmp;
233
    }
234
235
    /**
236
     * Reads an unsigned short (2 octets).
237
     * @param   resource  file handle
238
     * @return  int
239
     */
240
    private static function _readInt2($fh)
241
    {
242
        list(, $tmp) = unpack('v', fread($fh, 2));
243
244
        return $tmp;
245
    }
246
247
    /**
248
     * Reads an unsigned long (4 octets).
249
     * @param   resource  file handle
250
     * @return  int
251
     */
252
    private static function _readInt4($fh)
253
    {
254
        list(, $tmp) = unpack('V', fread($fh, 4));
255
256
        return $tmp;
257
    }
258
259
    /**
260
     * Gets information about all PPS's on the OLE container from the PPS WK's
261
     * creates an OLE_PPS object for each one.
262
     *
263
     * @param  int  the block id of the first block
264
     * @return mixed true on success, PEAR_Error on failure
265
     */
266
    public function _readPpsWks($blockId)
267
    {
268
        $fh = $this->getStream($blockId);
269
        for ($pos = 0;; $pos += 128) {
270
            fseek($fh, $pos, SEEK_SET);
271
            $nameUtf16 = fread($fh, 64);
272
            $nameLength = self::_readInt2($fh);
273
            $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2);
274
            // Simple conversion from UTF-16LE to ISO-8859-1
275
            $name = str_replace("\x00", '', $nameUtf16);
276
            $type = self::_readInt1($fh);
277
            switch ($type) {
278
                case self::OLE_PPS_TYPE_ROOT:
279
                    $pps = new OLE\PPS_Root(null, null, []);
280
                    $this->root = $pps;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pps of type object<PhpSpreadsheet\Shared\OLE\PPS_Root> is incompatible with the declared type object<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...
281
                    break;
282
                case self::OLE_PPS_TYPE_DIR:
283
                    $pps = new OLE\PPS(null, null, null, null, null, null, null, null, null, []);
284
                    break;
285
                case self::OLE_PPS_TYPE_FILE:
286
                    $pps = new OLE\PPS\File($name);
287
                    break;
288
                default:
289
                    continue;
290
            }
291
            fseek($fh, 1, SEEK_CUR);
292
            $pps->Type = $type;
0 ignored issues
show
Bug introduced by
The variable $pps does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
293
            $pps->Name = $name;
294
            $pps->PrevPps = self::_readInt4($fh);
295
            $pps->NextPps = self::_readInt4($fh);
296
            $pps->DirPps = self::_readInt4($fh);
297
            fseek($fh, 20, SEEK_CUR);
298
            $pps->Time1st = self::OLE2LocalDate(fread($fh, 8));
299
            $pps->Time2nd = self::OLE2LocalDate(fread($fh, 8));
300
            $pps->startBlock = self::_readInt4($fh);
301
            $pps->Size = self::_readInt4($fh);
302
            $pps->No = count($this->_list);
303
            $this->_list[] = $pps;
304
305
            // check if the PPS tree (starting from root) is complete
306
            if (isset($this->root) && $this->_ppsTreeComplete($this->root->No)) {
307
                break;
308
            }
309
        }
310
        fclose($fh);
311
312
        // Initialize $pps->children on directories
313
        foreach ($this->_list as $pps) {
314
            if ($pps->Type == self::OLE_PPS_TYPE_DIR || $pps->Type == self::OLE_PPS_TYPE_ROOT) {
315
                $nos = [$pps->DirPps];
316
                $pps->children = [];
317
                while ($nos) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nos of type array 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...
318
                    $no = array_pop($nos);
319
                    if ($no != -1) {
320
                        $childPps = $this->_list[$no];
321
                        $nos[] = $childPps->PrevPps;
322
                        $nos[] = $childPps->NextPps;
323
                        $pps->children[] = $childPps;
324
                    }
325
                }
326
            }
327
        }
328
329
        return true;
330
    }
331
332
    /**
333
     * It checks whether the PPS tree is complete (all PPS's read)
334
     * starting with the given PPS (not necessarily root)
335
     *
336
     * @param int $index The index of the PPS from which we are checking
337
     * @return bool Whether the PPS tree for the given PPS is complete
338
     */
339
    public function _ppsTreeComplete($index)
340
    {
341
        return isset($this->_list[$index]) &&
342
               ($pps = $this->_list[$index]) &&
343
               ($pps->PrevPps == -1 ||
344
                $this->_ppsTreeComplete($pps->PrevPps)) &&
345
               ($pps->NextPps == -1 ||
346
                $this->_ppsTreeComplete($pps->NextPps)) &&
347
               ($pps->DirPps == -1 ||
348
                $this->_ppsTreeComplete($pps->DirPps));
349
    }
350
351
    /**
352
     * Checks whether a PPS is a File PPS or not.
353
     * If there is no PPS for the index given, it will return false.
354
     *
355
     * @param int $index The index for the PPS
356
     * @return bool true if it's a File PPS, false otherwise
357
     */
358 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...
359
    {
360
        if (isset($this->_list[$index])) {
361
            return $this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE;
362
        }
363
364
        return false;
365
    }
366
367
    /**
368
     * Checks whether a PPS is a Root PPS or not.
369
     * If there is no PPS for the index given, it will return false.
370
     *
371
     * @param int $index The index for the PPS.
372
     * @return bool true if it's a Root PPS, false otherwise
373
     */
374 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...
375
    {
376
        if (isset($this->_list[$index])) {
377
            return $this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT;
378
        }
379
380
        return false;
381
    }
382
383
    /**
384
     * Gives the total number of PPS's found in the OLE container.
385
     *
386
     * @return int The total number of PPS's found in the OLE container
387
     */
388
    public function ppsTotal()
389
    {
390
        return count($this->_list);
391
    }
392
393
    /**
394
     * Gets data from a PPS
395
     * If there is no PPS for the index given, it will return an empty string.
396
     *
397
     * @param int $index    The index for the PPS
398
     * @param int $position The position from which to start reading
399
     *                          (relative to the PPS)
400
     * @param int $length   The amount of bytes to read (at most)
401
     * @return string The binary string containing the data requested
402
     * @see OLE_PPS_File::getStream()
403
     */
404
    public function getData($index, $position, $length)
405
    {
406
        // if position is not valid return empty string
407
        if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) {
408
            return '';
409
        }
410
        $fh = $this->getStream($this->_list[$index]);
411
        $data = stream_get_contents($fh, $length, $position);
412
        fclose($fh);
413
414
        return $data;
415
    }
416
417
    /**
418
     * Gets the data length from a PPS
419
     * If there is no PPS for the index given, it will return 0.
420
     *
421
     * @param int $index    The index for the PPS
422
     * @return int The amount of bytes in data the PPS has
423
     */
424
    public function getDataLength($index)
425
    {
426
        if (isset($this->_list[$index])) {
427
            return $this->_list[$index]->Size;
428
        }
429
430
        return 0;
431
    }
432
433
    /**
434
     * Utility function to transform ASCII text to Unicode
435
     *
436
     * @static
437
     * @param string $ascii The ASCII string to transform
438
     * @return string The string in Unicode
439
     */
440
    public static function ascToUcs($ascii)
441
    {
442
        $rawname = '';
443
        for ($i = 0; $i < strlen($ascii); ++$i) {
444
            $rawname .= $ascii{$i}
445
            . "\x00";
446
        }
447
448
        return $rawname;
449
    }
450
451
    /**
452
     * Utility function
453
     * Returns a string for the OLE container with the date given
454
     *
455
     * @static
456
     * @param int $date A timestamp
457
     * @return string The string for the OLE container
458
     */
459
    public static function localDateToOLE($date = null)
460
    {
461
        if (!isset($date)) {
462
            return "\x00\x00\x00\x00\x00\x00\x00\x00";
463
        }
464
465
        // factor used for separating numbers into 4 bytes parts
466
        $factor = pow(2, 32);
467
468
        // days from 1-1-1601 until the beggining of UNIX era
469
        $days = 134774;
470
        // calculate seconds
471
        $big_date = $days * 24 * 3600 + gmmktime(date('H', $date), date('i', $date), date('s', $date), date('m', $date), date('d', $date), date('Y', $date));
472
        // multiply just to make MS happy
473
        $big_date *= 10000000;
474
475
        $high_part = floor($big_date / $factor);
476
        // lower 4 bytes
477
        $low_part = floor((($big_date / $factor) - $high_part) * $factor);
478
479
        // Make HEX string
480
        $res = '';
481
482 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...
483
            $hex = $low_part % 0x100;
484
            $res .= pack('c', $hex);
485
            $low_part /= 0x100;
486
        }
487 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...
488
            $hex = $high_part % 0x100;
489
            $res .= pack('c', $hex);
490
            $high_part /= 0x100;
491
        }
492
493
        return $res;
494
    }
495
496
    /**
497
     * Returns a timestamp from an OLE container's date
498
     *
499
     * @static
500
     * @param int $string A binary string with the encoded date
501
     * @return string The timestamp corresponding to the string
502
     */
503
    public static function OLE2LocalDate($string)
504
    {
505
        if (strlen($string) != 8) {
506
            return new PEAR_Error('Expecting 8 byte string');
507
        }
508
509
        // factor used for separating numbers into 4 bytes parts
510
        $factor = pow(2, 32);
511
        list(, $high_part) = unpack('V', substr($string, 4, 4));
512
        list(, $low_part) = unpack('V', substr($string, 0, 4));
513
514
        $big_date = ($high_part * $factor) + $low_part;
515
        // translate to seconds
516
        $big_date /= 10000000;
517
518
        // days from 1-1-1601 until the beggining of UNIX era
519
        $days = 134774;
520
521
        // translate to seconds from beggining of UNIX era
522
        $big_date -= $days * 24 * 3600;
523
524
        return floor($big_date);
525
    }
526
}
527