Passed
Push — master ( 3c7afe...11f0b5 )
by
unknown
14:12 queued 14s
created

ArrayArgumentHelper   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Test Coverage

Coverage 97.65%

Importance

Changes 0
Metric Value
wmc 45
eloc 69
dl 0
loc 209
ccs 83
cts 85
cp 0.9765
rs 8.8
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A columnCount() 0 3 1
A getMatrixPair() 0 11 4
A isColumnVector() 0 3 1
A rowCount() 0 3 1
A isVector() 0 3 2
A isRowVector() 0 3 1
A arguments() 0 3 1
A hasArrayArgument() 0 3 1
A getSingleColumnVector() 0 5 2
A getRowVectors() 0 10 4
A flattenSingleCellArrays() 0 12 5
A rows() 0 5 2
A filterArray() 0 5 1
A arrayArguments() 0 10 4
A initialise() 0 15 2
A getColumnVectors() 0 10 4
A getSingleRowVector() 0 5 2
A getFirstArrayArgumentNumber() 0 12 4
A columns() 0 7 3

How to fix   Complexity   

Complex Class

Complex classes like ArrayArgumentHelper 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.

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 ArrayArgumentHelper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
6
7
class ArrayArgumentHelper
8
{
9
    protected int $indexStart = 0;
10
11
    /** @var mixed[] */
12
    protected array $arguments;
13
14
    protected int $argumentCount;
15
16
    /** @var int[] */
17
    protected array $rows;
18
19
    /** @var int[] */
20
    protected array $columns;
21
22
    /** @param mixed[] $arguments */
23 3787
    public function initialise(array $arguments): void
24
    {
25 3787
        $keys = array_keys($arguments);
26 3787
        $this->indexStart = (int) array_shift($keys);
27 3787
        $this->rows = $this->rows($arguments);
28 3787
        $this->columns = $this->columns($arguments);
29
30 3787
        $this->argumentCount = count($arguments);
31 3787
        $this->arguments = $this->flattenSingleCellArrays($arguments, $this->rows, $this->columns);
32
33 3787
        $this->rows = $this->rows($arguments);
34 3787
        $this->columns = $this->columns($arguments);
35
36 3787
        if ($this->arrayArguments() > 2) {
37 2
            throw new Exception('Formulae with more than two array arguments are not supported');
38
        }
39
    }
40
41
    /** @return mixed[] */
42 3785
    public function arguments(): array
43
    {
44 3785
        return $this->arguments;
45
    }
46
47 3785
    public function hasArrayArgument(): bool
48
    {
49 3785
        return $this->arrayArguments() > 0;
50
    }
51
52 218
    public function getFirstArrayArgumentNumber(): int
53
    {
54 218
        $rowArrays = $this->filterArray($this->rows);
55 218
        $columnArrays = $this->filterArray($this->columns);
56
57 218
        for ($index = $this->indexStart; $index < $this->argumentCount; ++$index) {
58 218
            if (isset($rowArrays[$index]) || isset($columnArrays[$index])) {
59 218
                return ++$index;
60
            }
61
        }
62
63
        return 0;
64
    }
65
66 99
    public function getSingleRowVector(): ?int
67
    {
68 99
        $rowVectors = $this->getRowVectors();
69
70 99
        return count($rowVectors) === 1 ? array_pop($rowVectors) : null;
71
    }
72
73
    /** @return int[] */
74 99
    private function getRowVectors(): array
75
    {
76 99
        $rowVectors = [];
77 99
        for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
78 99
            if ($this->rows[$index] === 1 && $this->columns[$index] > 1) {
79 81
                $rowVectors[] = $index;
80
            }
81
        }
82
83 99
        return $rowVectors;
84
    }
85
86 99
    public function getSingleColumnVector(): ?int
87
    {
88 99
        $columnVectors = $this->getColumnVectors();
89
90 99
        return count($columnVectors) === 1 ? array_pop($columnVectors) : null;
91
    }
92
93
    /** @return int[] */
94 99
    private function getColumnVectors(): array
95
    {
96 99
        $columnVectors = [];
97 99
        for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
98 99
            if ($this->rows[$index] > 1 && $this->columns[$index] === 1) {
99 78
                $columnVectors[] = $index;
100
            }
101
        }
102
103 99
        return $columnVectors;
104
    }
105
106
    /** @return int[] */
107 33
    public function getMatrixPair(): array
108
    {
109 33
        for ($i = $this->indexStart; $i < ($this->indexStart + $this->argumentCount - 1); ++$i) {
110 33
            for ($j = $i + 1; $j < $this->argumentCount; ++$j) {
111 33
                if (isset($this->rows[$i], $this->rows[$j])) {
112 33
                    return [$i, $j];
113
                }
114
            }
115
        }
116
117
        return [];
118
    }
119
120 33
    public function isVector(int $argument): bool
121
    {
122 33
        return $this->rows[$argument] === 1 || $this->columns[$argument] === 1;
123
    }
124
125 7
    public function isRowVector(int $argument): bool
126
    {
127 7
        return $this->rows[$argument] === 1;
128
    }
129
130 7
    public function isColumnVector(int $argument): bool
131
    {
132 7
        return $this->columns[$argument] === 1;
133
    }
134
135 7
    public function rowCount(int $argument): int
136
    {
137 7
        return $this->rows[$argument];
138
    }
139
140 7
    public function columnCount(int $argument): int
141
    {
142 7
        return $this->columns[$argument];
143
    }
144
145
    /**
146
     * @param mixed[] $arguments
147
     *
148
     * @return int[]
149
     */
150 3787
    private function rows(array $arguments): array
151
    {
152 3787
        return array_map(
153 3787
            fn ($argument): int => is_countable($argument) ? count($argument) : 1,
154 3787
            $arguments
155 3787
        );
156
    }
157
158
    /**
159
     * @param mixed[] $arguments
160
     *
161
     * @return int[]
162
     */
163 3787
    private function columns(array $arguments): array
164
    {
165 3787
        return array_map(
166 3787
            fn (mixed $argument): int => is_array($argument) && is_array($argument[array_keys($argument)[0]])
167 3785
                    ? count($argument[array_keys($argument)[0]])
168 3787
                    : 1,
169 3787
            $arguments
170 3787
        );
171
    }
172
173 3787
    public function arrayArguments(): int
174
    {
175 3787
        $count = 0;
176 3787
        foreach (array_keys($this->arguments) as $argument) {
177 3787
            if ($this->rows[$argument] > 1 || $this->columns[$argument] > 1) {
178 319
                ++$count;
179
            }
180
        }
181
182 3787
        return $count;
183
    }
184
185
    /**
186
     * @param mixed[] $arguments
187
     * @param int[] $rows
188
     * @param int[] $columns
189
     *
190
     * @return mixed[]
191
     */
192 3787
    private function flattenSingleCellArrays(array $arguments, array $rows, array $columns): array
193
    {
194 3787
        foreach ($arguments as $index => $argument) {
195 3787
            if ($rows[$index] === 1 && $columns[$index] === 1) {
196 3714
                while (is_array($argument)) {
197 3536
                    $argument = array_pop($argument);
198
                }
199 3714
                $arguments[$index] = $argument;
200
            }
201
        }
202
203 3787
        return $arguments;
204
    }
205
206
    /**
207
     * @param mixed[] $array
208
     *
209
     * @return mixed[]
210
     */
211 218
    private function filterArray(array $array): array
212
    {
213 218
        return array_filter(
214 218
            $array,
215 218
            fn ($value): bool => $value > 1
216 218
        );
217
    }
218
}
219