Completed
Push — develop ( f99eb8...c5339b )
by Adrien
31:45
created

CacheBase   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 351
Duplicated Lines 5.13 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 74.29%

Importance

Changes 0
Metric Value
dl 18
loc 351
ccs 78
cts 105
cp 0.7429
rs 8.2769
c 0
b 0
f 0
wmc 41
lcom 1
cbo 1

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getParent() 0 4 1
A __construct() 0 7 1
A isDataSet() 0 8 2
A moveCell() 0 13 3
A updateCacheData() 0 4 1
A deleteCacheData() 0 13 4
A getCellList() 0 4 1
A getSortedCellList() 0 11 2
A getHighestRowAndColumn() 0 21 3
A getCurrentAddress() 0 4 1
A getCurrentColumn() 0 6 1
A getCurrentRow() 0 6 1
A getHighestColumn() 0 19 4
A getHighestRow() 0 19 4
A getUniqueID() 0 10 2
A copyCellCollection() 0 10 3
A removeColumn() 9 9 3
A cacheMethodIsAvailable() 0 4 1
A removeRow() 9 9 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CacheBase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CacheBase, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
4
5
/**
6
 * Copyright (c) 2006 - 2016 PhpSpreadsheet.
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 *
22
 * @category   PhpSpreadsheet
23
 *
24
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
25
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
26
 */
27
abstract class CacheBase
28
{
29
    /**
30
     * Parent worksheet.
31
     *
32
     * @var \PhpOffice\PhpSpreadsheet\Worksheet
33
     */
34
    protected $parent;
35
36
    /**
37
     * The currently active Cell.
38
     *
39
     * @var \PhpOffice\PhpSpreadsheet\Cell
40
     */
41
    protected $currentObject = null;
42
43
    /**
44
     * Coordinate address of the currently active Cell.
45
     *
46
     * @var string
47
     */
48
    protected $currentObjectID = null;
49
50
    /**
51
     * Flag indicating whether the currently active Cell requires saving.
52
     *
53
     * @var bool
54
     */
55
    protected $currentCellIsDirty = true;
56
57
    /**
58
     * An array of cells or cell pointers for the worksheet cells held in this cache,
59
     * and indexed by their coordinate address within the worksheet.
60
     *
61
     * @var array of mixed
62
     */
63
    protected $cellCache = [];
64
65
    /**
66
     * Initialise this new cell collection.
67
     *
68
     * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection
69
     */
70 81
    public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent)
71
    {
72
        //    Set our parent worksheet.
73
        //    This is maintained within the cache controller to facilitate re-attaching it to \PhpOffice\PhpSpreadsheet\Cell objects when
74
        //        they are woken from a serialized state
75 81
        $this->parent = $parent;
76 81
    }
77
78
    /**
79
     * Return the parent worksheet for this cell collection.
80
     *
81
     * @return \PhpOffice\PhpSpreadsheet\Worksheet
82
     */
83 46
    public function getParent()
84
    {
85 46
        return $this->parent;
86
    }
87
88
    /**
89
     * Is a value set in the current \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell?
90
     *
91
     * @param string $pCoord Coordinate address of the cell to check
92
     *
93
     * @return bool
94
     */
95 73
    public function isDataSet($pCoord)
96
    {
97 73
        if ($pCoord === $this->currentObjectID) {
98 67
            return true;
99
        }
100
        //    Check if the requested entry exists in the cache
101 73
        return isset($this->cellCache[$pCoord]);
102
    }
103
104
    /**
105
     * Move a cell object from one address to another.
106
     *
107
     * @param string $fromAddress Current address of the cell to move
108
     * @param string $toAddress Destination address of the cell to move
109
     *
110
     * @return bool
111
     */
112
    public function moveCell($fromAddress, $toAddress)
113
    {
114
        if ($fromAddress === $this->currentObjectID) {
115
            $this->currentObjectID = $toAddress;
116
        }
117
        $this->currentCellIsDirty = true;
118
        if (isset($this->cellCache[$fromAddress])) {
119
            $this->cellCache[$toAddress] = &$this->cellCache[$fromAddress];
120
            unset($this->cellCache[$fromAddress]);
121
        }
122
123
        return true;
124
    }
125
126
    /**
127
     * Add or Update a cell in cache.
128
     *
129
     * @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
130
     *
131
     * @throws \PhpOffice\PhpSpreadsheet\Exception
132
     *
133
     * @return \PhpOffice\PhpSpreadsheet\Cell
134
     */
135 72
    public function updateCacheData(\PhpOffice\PhpSpreadsheet\Cell $cell)
136
    {
137 72
        return $this->addCacheData($cell->getCoordinate(), $cell);
138
    }
139
140
    /**
141
     * Delete a cell in cache identified by coordinate address.
142
     *
143
     * @param string $pCoord Coordinate address of the cell to delete
144
     *
145
     * @throws \PhpOffice\PhpSpreadsheet\Exception
146
     */
147 15
    public function deleteCacheData($pCoord)
148
    {
149 15
        if ($pCoord === $this->currentObjectID && !is_null($this->currentObject)) {
150
            $this->currentObject->detach();
151
            $this->currentObjectID = $this->currentObject = null;
152
        }
153
154 15
        if (is_object($this->cellCache[$pCoord])) {
155 15
            $this->cellCache[$pCoord]->detach();
156
        }
157 15
        unset($this->cellCache[$pCoord]);
158 15
        $this->currentCellIsDirty = false;
159 15
    }
160
161
    /**
162
     * Get a list of all cell addresses currently held in cache.
163
     *
164
     * @return string[]
165
     */
166 64
    public function getCellList()
167
    {
168 64
        return array_keys($this->cellCache);
169
    }
170
171
    /**
172
     * Sort the list of all cell addresses currently held in cache by row and column.
173
     *
174
     * @return string[]
175
     */
176 63
    public function getSortedCellList()
177
    {
178 63
        $sortKeys = [];
179 63
        foreach ($this->getCellList() as $coord) {
180 63
            sscanf($coord, '%[A-Z]%d', $column, $row);
0 ignored issues
show
Bug introduced by
The variable $row does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
181 63
            $sortKeys[sprintf('%09d%3s', $row, $column)] = $coord;
182
        }
183 63
        ksort($sortKeys);
184
185 63
        return array_values($sortKeys);
186
    }
187
188
    /**
189
     * Get highest worksheet column and highest row that have cell records.
190
     *
191
     * @return array Highest column name and highest row number
192
     */
193 62
    public function getHighestRowAndColumn()
194
    {
195
        // Lookup highest column and highest row
196 62
        $col = ['A' => '1A'];
197 62
        $row = [1];
198 62
        foreach ($this->getCellList() as $coord) {
199 62
            sscanf($coord, '%[A-Z]%d', $c, $r);
0 ignored issues
show
Bug introduced by
The variable $r does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
200 62
            $row[$r] = $r;
201 62
            $col[$c] = strlen($c) . $c;
202
        }
203 62
        if (!empty($row)) {
204
            // Determine highest column and row
205 62
            $highestRow = max($row);
206 62
            $highestColumn = substr(max($col), 1);
207
        }
208
209
        return [
210 62
            'row' => $highestRow,
0 ignored issues
show
Bug introduced by
The variable $highestRow 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...
211 62
            'column' => $highestColumn,
0 ignored issues
show
Bug introduced by
The variable $highestColumn 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...
212
        ];
213
    }
214
215
    /**
216
     * Return the cell address of the currently active cell object.
217
     *
218
     * @return string
219
     */
220 73
    public function getCurrentAddress()
221
    {
222 73
        return $this->currentObjectID;
223
    }
224
225
    /**
226
     * Return the column address of the currently active cell object.
227
     *
228
     * @return string
229
     */
230 47
    public function getCurrentColumn()
231
    {
232 47
        sscanf($this->currentObjectID, '%[A-Z]%d', $column, $row);
0 ignored issues
show
Bug introduced by
The variable $row does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
233
234 47
        return $column;
235
    }
236
237
    /**
238
     * Return the row address of the currently active cell object.
239
     *
240
     * @return int
241
     */
242 45
    public function getCurrentRow()
243
    {
244 45
        sscanf($this->currentObjectID, '%[A-Z]%d', $column, $row);
0 ignored issues
show
Bug introduced by
The variable $row does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
245
246 45
        return (int) $row;
247
    }
248
249
    /**
250
     * Get highest worksheet column.
251
     *
252
     * @param string $row Return the highest column for the specified row,
253
     *                    or the highest column of any row if no row number is passed
254
     *
255
     * @return string Highest column name
256
     */
257 13
    public function getHighestColumn($row = null)
258
    {
259 13
        if ($row == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $row of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
260 13
            $colRow = $this->getHighestRowAndColumn();
261
262 13
            return $colRow['column'];
263
        }
264
265
        $columnList = [1];
266
        foreach ($this->getCellList() as $coord) {
267
            sscanf($coord, '%[A-Z]%d', $c, $r);
0 ignored issues
show
Bug introduced by
The variable $r does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
268
            if ($r != $row) {
269
                continue;
270
            }
271
            $columnList[] = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($c);
272
        }
273
274
        return \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(max($columnList) - 1);
275
    }
276
277
    /**
278
     * Get highest worksheet row.
279
     *
280
     * @param string $column Return the highest row for the specified column,
281
     *                       or the highest row of any column if no column letter is passed
282
     *
283
     * @return int Highest row number
284
     */
285 15
    public function getHighestRow($column = null)
286
    {
287 15
        if ($column == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $column of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
288 15
            $colRow = $this->getHighestRowAndColumn();
289
290 15
            return $colRow['row'];
291
        }
292
293
        $rowList = [0];
294
        foreach ($this->getCellList() as $coord) {
295
            sscanf($coord, '%[A-Z]%d', $c, $r);
0 ignored issues
show
Bug introduced by
The variable $r does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
296
            if ($c != $column) {
297
                continue;
298
            }
299
            $rowList[] = $r;
300
        }
301
302
        return max($rowList);
303
    }
304
305
    /**
306
     * Generate a unique ID for cache referencing.
307
     *
308
     * @return string Unique Reference
309
     */
310 1
    protected function getUniqueID()
311
    {
312 1
        if (function_exists('posix_getpid')) {
313 1
            $baseUnique = posix_getpid();
314
        } else {
315
            $baseUnique = mt_rand();
316
        }
317
318 1
        return uniqid($baseUnique, true);
319
    }
320
321
    /**
322
     * Clone the cell collection.
323
     *
324
     * @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
325
     */
326 1
    public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent)
327
    {
328 1
        $this->currentCellIsDirty;
329 1
        $this->storeData();
330
331 1
        $this->parent = $parent;
332 1
        if (($this->currentObject !== null) && (is_object($this->currentObject))) {
333
            $this->currentObject->attach($this);
334
        }
335 1
    }
336
337
    /**
338
     * Remove a row, deleting all cells in that row.
339
     *
340
     * @param string $row Row number to remove
341
     */
342 15 View Code Duplication
    public function removeRow($row)
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...
343
    {
344 15
        foreach ($this->getCellList() as $coord) {
345 15
            sscanf($coord, '%[A-Z]%d', $c, $r);
0 ignored issues
show
Bug introduced by
The variable $r does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
346 15
            if ($r == $row) {
347
                $this->deleteCacheData($coord);
348
            }
349
        }
350 15
    }
351
352
    /**
353
     * Remove a column, deleting all cells in that column.
354
     *
355
     * @param string $column Column ID to remove
356
     */
357 12 View Code Duplication
    public function removeColumn($column)
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...
358
    {
359 12
        foreach ($this->getCellList() as $coord) {
360 12
            sscanf($coord, '%[A-Z]%d', $c, $r);
0 ignored issues
show
Bug introduced by
The variable $r does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
361 12
            if ($c == $column) {
362 12
                $this->deleteCacheData($coord);
363
            }
364
        }
365 12
    }
366
367
    /**
368
     * Identify whether the caching method is currently available
369
     * Some methods are dependent on the availability of certain extensions being enabled in the PHP build.
370
     *
371
     * @return bool
372
     */
373 67
    public static function cacheMethodIsAvailable()
374
    {
375 67
        return true;
376
    }
377
}
378