PpsRoot::saveBigBlockChain()   C
last analyzed

Complexity

Conditions 10
Paths 112

Size

Total Lines 69
Code Lines 45

Duplication

Lines 6
Ratio 8.7 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 6
loc 69
rs 5.8139
cc 10
eloc 45
nc 112
nop 3

How to fix   Long Method    Complexity   

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
2
3
namespace Xls\OLE;
4
5
class PpsRoot extends PPS
6
{
7
    /**
8
     * @var
9
     */
10
    protected $smallBlockSize;
11
12
    /**
13
     * @var
14
     */
15
    protected $bigBlockSize;
16
17
    /**
18
     * @var
19
     */
20
    protected $rootFilePointer;
21
22
    /**
23
     * @param integer $timestamp A timestamp
24
     * @param PpsFile[] $children
25
     */
26
    public function __construct(
27
        $timestamp = null,
28
        $children = array()
29
    ) {
30
        parent::__construct(
31
            null,
32
            OLE::asc2Ucs('Root Entry'),
33
            self::PPS_TYPE_ROOT,
34
            null,
35
            null,
36
            null,
37
            $timestamp,
38
            null,
39
            $children
40
        );
41
    }
42
43
    /**
44
     * Method for saving the whole OLE container (including files).
45
     *
46
     * @param string $filename The name of the file where to save the OLE container
47
     * @throws \Exception
48
     * @return boolean
49
     */
50
    public function save($filename)
51
    {
52
        $this->openFile($filename);
53
54
        $this->setBlockSizes();
55
56
        // Make an array of PPS's (for Save)
57
        $list = array();
58
        self::setPointers($list, array($this));
59
60
        list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->calcSize($list);
61
62
        $this->saveHeader($iSBDcnt, $iBBcnt, $iPPScnt);
63
        $this->saveSmallData($list);
64
        $this->saveBigData($iSBDcnt, $list);
65
        $this->savePps($list);
66
        $this->saveBigBlockChain($iSBDcnt, $iBBcnt, $iPPScnt);
67
68
        fclose($this->rootFilePointer);
69
    }
70
71
    /**
72
     *
73
     */
74
    protected function setBlockSizes()
75
    {
76
        $this->bigBlockSize = pow(2, 9);
77
        $this->smallBlockSize = pow(2, 6);
78
    }
79
80
    /**
81
     * @param $filename
82
     *
83
     * @throws \Exception
84
     */
85
    protected function openFile($filename)
86
    {
87
        $this->rootFilePointer = @fopen($filename, "wb");
88
        if ($this->rootFilePointer === false) {
89
            throw new \Exception("Can't open $filename. It may be in use or protected.");
90
        }
91
    }
92
93
    /**
94
     * @param $size
95
     * @param $blockSize
96
     *
97
     * @return float
98
     */
99
    protected function getBlocksCount($size, $blockSize)
100
    {
101
        return floor($size / $blockSize) + (($size % $blockSize) ? 1 : 0);
102
    }
103
104
    /**
105
     * Calculate some numbers
106
     *
107
     * @param PPS[] $list Reference to an array of PPS's
108
     *
109
     * @return array The array of numbers
110
     */
111
    protected function calcSize($list)
112
    {
113
        $iSBcnt = 0;
114
        $iBBcnt = 0;
115
        foreach ($list as $item) {
116
            if (!$item->isFile()) {
117
                continue;
118
            }
119
120
            $size = $item->getSize();
121
            if ($size < self::DATA_SIZE_SMALL) {
122
                $iSBcnt += $this->getBlocksCount($size, $this->smallBlockSize);
123
            } else {
124
                $iBBcnt += $this->getBlocksCount($size, $this->bigBlockSize);
125
            }
126
        }
127
128
        $iSmallLen = $iSBcnt * $this->smallBlockSize;
129
        $iSlCnt = $this->getPointersPerBlock($this->bigBlockSize);
130
        $iSBDcnt = $this->getBlocksCount($iSBcnt, $iSlCnt);
131
        $iBBcnt += $this->getBlocksCount($iSmallLen, $this->bigBlockSize);
132
        $iBdCnt = $this->getPointersPerBlock($this->bigBlockSize, self::PPS_SIZE);
133
        $iPPScnt = $this->getBlocksCount(count($list), $iBdCnt);
134
135
        return array($iSBDcnt, $iBBcnt, $iPPScnt);
136
    }
137
138
    /**
139
     * Saving big data (PPS's with data bigger than OLE_DATA_SIZE_SMALL)
140
     *
141
     * @param integer $iStBlk
142
     * @param PPS[] &$list Reference to array of PPS's
143
     */
144
    public function saveBigData($iStBlk, &$list)
145
    {
146
        foreach ($list as $item) {
147
            $size = $item->getSize();
148
            if ($size >= self::DATA_SIZE_SMALL
149
                || ($item->isRoot() && $item->hasData())
150
            ) {
151
                $this->write($item->getData());
152
153
                if ($size % $this->bigBlockSize) {
154
                    $zeroByteCount = ($this->bigBlockSize - ($size % $this->bigBlockSize));
155
                    $this->write(str_repeat("\x00", $zeroByteCount));
156
                }
157
158
                // Set For PPS
159
                $item->setStartBlock($iStBlk);
160
                $iStBlk += $this->getBlocksCount($size, $this->bigBlockSize);
161
            }
162
        }
163
    }
164
165
    /**
166
     * get small data (PPS's with data smaller than OLE_DATA_SIZE_SMALL)
167
     *
168
     * @param PPS[] &$list Reference to array of PPS's
169
     * @return string
170
     */
171
    protected function saveSmallData(&$list)
172
    {
173
        $result = '';
174
        $iSmBlk = 0;
175
        foreach ($list as $item) {
176
            if (!$item->isFile()) {
177
                continue;
178
            }
179
180
            $size = $item->getSize();
181
            if ($size <= 0 || $size >= self::DATA_SIZE_SMALL) {
182
                continue;
183
            }
184
185
            $sbCount = $this->getBlocksCount($size, $this->smallBlockSize);
186
            // Add to SBD
187
            for ($j = 0; $j < $sbCount - 1; $j++) {
188
                $this->writeUlong($j + $iSmBlk + 1);
189
            }
190
            $this->writeUlong(-2);
191
192
            $result .= $item->getData();
193
194
            $exp = $size % $this->smallBlockSize;
195
            if ($exp) {
196
                $zeroByteCount = $this->smallBlockSize - $exp;
197
                $result .= str_repeat("\x00", $zeroByteCount);
198
            }
199
200
            $item->setStartBlock($iSmBlk);
201
            $iSmBlk += $sbCount;
202
        }
203
204
        $sbCount = $this->getPointersPerBlock($this->bigBlockSize);
205
        if ($iSmBlk % $sbCount) {
206
            $repeatCount = $sbCount - ($iSmBlk % $sbCount);
207
            $this->writeUlong(-1, $repeatCount);
208
        }
209
210
        $this->data = $result;
211
    }
212
213
    /**
214
     * Saves all the PPS's WKs
215
     *
216
     * @param PPS[] $list Reference to an array with all PPS's
217
     */
218
    protected function savePps(&$list)
219
    {
220
        // Save each PPS WK
221
        $raListCount = count($list);
222
        for ($i = 0; $i < $raListCount; $i++) {
223
            $this->write($list[$i]->getPpsWk());
224
        }
225
226
        // Adjust for Block
227
        $iCnt = count($list);
228
        $iBCnt = $this->getPointersPerBlock($this->bigBlockSize, self::PPS_SIZE);
229
        if ($iCnt % $iBCnt) {
230
            $zeroByteCount = ($iBCnt - ($iCnt % $iBCnt)) * self::PPS_SIZE;
231
            $this->write(str_repeat("\x00", $zeroByteCount));
232
        }
233
    }
234
235
    /**
236
     * @param     $value
237
     * @param int $count
238
     */
239
    protected function writeUlong($value, $count = 1)
240
    {
241
        $packed = pack("V", $value);
242
        $this->write(str_repeat($packed, $count));
243
    }
244
245
    /**
246
     * @param $data
247
     */
248
    protected function write($data)
249
    {
250
        fwrite($this->rootFilePointer, $data);
251
    }
252
253
    /**
254
     * Saving Big Block Depot
255
     *
256
     * @param integer $numSbBlocks - number of Smallblock depot blocks
257
     * @param integer $numBbBlocks - number of Bigblock depot blocks
258
     * @param integer $numPpsBlocks - number of PropertySetStorage blocks
259
     */
260
    protected function saveBigBlockChain($numSbBlocks, $numBbBlocks, $numPpsBlocks)
261
    {
262
        $info = $this->calcBigBlockChain($numSbBlocks, $numBbBlocks, $numPpsBlocks);
263
        $headerEntriesCount = $info["header_list_entries"];
264
        $entriesCount = $info["list_entries"];
265
        $entriesPerBlock = $info["entries_per_block"];
266
267
        if ($numSbBlocks > 0) {
268
            for ($i = 0; $i < $numSbBlocks - 1; $i++) {
269
                $this->writeUlong($i + 1);
270
            }
271
            $this->writeUlong(-2);
272
        }
273
274 View Code Duplication
        for ($i = 0; $i < $numBbBlocks - 1; $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...
275
            $this->writeUlong($i + $numSbBlocks + 1);
276
        }
277
        $this->writeUlong(-2);
278
279 View Code Duplication
        for ($i = 0; $i < $numPpsBlocks - 1; $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...
280
            $this->writeUlong($i + $numSbBlocks + $numBbBlocks + 1);
281
        }
282
        $this->writeUlong(-2);
283
284
        $this->writeUlong(0xFFFFFFFD, $info["FD_entries"]);
285
        $this->writeUlong(0xFFFFFFFC, $info["FC_entries"]);
286
287
        // Adjust for Block
288
        $allEntries = $numSbBlocks
289
            + $numBbBlocks
290
            + $numPpsBlocks
291
            + $info["FD_entries"]
292
            + $info["FC_entries"];
293
        if ($allEntries % $entriesPerBlock) {
294
            $rest = $entriesPerBlock - ($allEntries % $entriesPerBlock);
295
            $this->writeUlong(-1, $rest);
296
        }
297
298
        // Extra BDList
299
        if ($entriesCount > $headerEntriesCount) {
300
            $iN = 0;
301
            $iNb = 0;
302
            $lastEntryIdx = $entriesPerBlock - 1;
303
304
            for ($i = $headerEntriesCount; $i < $entriesCount; $i++, $iN++) {
305
                if ($iN >= $lastEntryIdx) {
306
                    $iN = 0;
307
                    $iNb++;
308
309
                    $val = $numSbBlocks
310
                        + $numBbBlocks
311
                        + $numPpsBlocks
312
                        + $info["FD_entries"]
313
                        + $iNb;
314
                    $this->writeUlong($val);
315
                }
316
317
                $this->writeUlong($numBbBlocks + $numSbBlocks + $numPpsBlocks + $i);
318
            }
319
320
            $allEntries = $entriesCount - $headerEntriesCount;
321
            if ($allEntries % $lastEntryIdx) {
322
                $rest = $lastEntryIdx - ($allEntries % $lastEntryIdx);
323
                $this->writeUlong(-1, $rest);
324
            }
325
326
            $this->writeUlong(-2);
327
        }
328
    }
329
330
    /**
331
     * Save OLE header
332
     *
333
     * @param integer $numSbBlocks - number of Smallblock depot blocks
334
     * @param integer $numBbBlocks - number of Bigblock depot blocks
335
     * @param integer $numPpsBlocks - number of PropertySetStorage blocks
336
     */
337
    public function saveHeader($numSbBlocks, $numBbBlocks, $numPpsBlocks)
338
    {
339
        $info = $this->calcBigBlockChain($numSbBlocks, $numBbBlocks, $numPpsBlocks);
340
        $headerEntriesCount = $info["header_list_entries"];
341
        $entriesCount = $info["list_entries"];
342
343
        $this->write(
344
            "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
345
            . pack("V4", 0, 0, 0, 0)
346
            . pack("v6", 0x3b, 0x03, -2, 9, 6, 0)
347
            . pack("V2", 0, 0)
348
            . pack("V", $entriesCount)
349
            . pack("V", $numSbBlocks + $numBbBlocks) //ROOT START
350
            . pack("V2", 0, 0x1000)
351
        );
352
353
        //Small Block Depot
354
        $value = ($numSbBlocks > 0) ? 0 : -2;
355
        $this->writeUlong($value);
356
        $this->writeUlong($numSbBlocks);
357
358
        // Extra BDList Start, Count
359
        if ($entriesCount < $headerEntriesCount) {
360
            $this->writeUlong(-2); // Extra BDList Start
361
            $this->writeUlong(0); // Extra BDList Count
362
        } else {
363
            $this->writeUlong($numSbBlocks + $numBbBlocks + $numPpsBlocks + $info["FD_entries"]);
364
            $this->writeUlong($info["FC_entries"]);
365
        }
366
367
        // BDList
368
        for ($i = 0; $i < $headerEntriesCount && $i < $entriesCount; $i++) {
369
            $this->writeUlong($numBbBlocks + $numSbBlocks + $numPpsBlocks + $i);
370
        }
371
372
        if ($i < $headerEntriesCount) {
373
            $repeatCount = $headerEntriesCount - $i;
374
            $this->writeUlong(-1, $repeatCount);
375
        }
376
    }
377
378
    /**
379
     * New method to calculate Bigblock chain
380
     *
381
     * @param integer $numSb - number of Smallblock depot blocks
382
     * @param integer $numBb - number of Bigblock depot blocks
383
     * @param integer $numPps - number of PropertySetStorage blocks
384
     * @return array
385
     */
386
    protected function calcBigBlockChain($numSb, $numBb, $numPps)
387
    {
388
        $totalBlocks = $numSb + $numBb + $numPps;
389
        $info = array(
390
            "entries_per_block" => $this->getPointersPerBlock($this->bigBlockSize),
391
            "header_list_entries" => $this->getPointersPerBlock($this->bigBlockSize - 0x4C),
392
            "entries" => $totalBlocks,
393
            "ext_list_entries" => 0,
394
            "FC_entries" => 0,
395
            "FD_entries" => $this->getNumberOfPointerBlocks($totalBlocks)
396
        );
397
398
        $info["list_entries"] = $this->getNumberOfPointerBlocks(
399
            $totalBlocks + $info["FD_entries"]
400
        );
401
402
        if ($info["list_entries"] <= $info["header_list_entries"]) {
403
            return $info;
404
        }
405
406
        return $this->calcBigBlockChainExtra($info);
407
    }
408
409
    /**
410
     * @param array $info
411
     *
412
     * @return array
413
     */
414
    protected function calcBigBlockChainExtra($info)
415
    {
416
        while (true) {
417
            $pointerBlocksCount = $this->getNumberOfPointerBlocks(
418
                $info["entries"] + $info["FD_entries"] + $info["FC_entries"]
419
            );
420
421
            if ($info["list_entries"] >= $pointerBlocksCount) {
422
                break;
423
            }
424
425
            $info["list_entries"] = $pointerBlocksCount;
426
            $info["ext_list_entries"] = $info["list_entries"] - $info["header_list_entries"];
427
            $info["FC_entries"] = $this->getNumberOfPointerBlocks(
428
                $info["ext_list_entries"]
429
            );
430
            $info["FD_entries"] = $this->getNumberOfPointerBlocks(
431
                $info["entries"] + $info["FD_entries"] + $info["FC_entries"]
432
            );
433
        }
434
435
        return $info;
436
    }
437
438
    /**
439
     * Calculates number of pointer blocks
440
     *
441
     * @param integer $numPointers - number of pointers
442
     *
443
     * @return int
444
     */
445
    protected function getNumberOfPointerBlocks($numPointers)
446
    {
447
        return $this->getBlocksCount($numPointers, $this->getPointersPerBlock($this->bigBlockSize));
448
    }
449
450
    /**
451
     * @param int $blockSize
452
     * @param int $pointerSize
453
     *
454
     * @return int
455
     */
456
    protected function getPointersPerBlock($blockSize, $pointerSize = self::LONG_INT_SIZE)
457
    {
458
        return intval(floor($blockSize / $pointerSize));
459
    }
460
}
461