This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the webmozart/console package. |
||
5 | * |
||
6 | * (c) Bernhard Schussek <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | namespace Webmozart\Console\UI\Component; |
||
13 | |||
14 | use Webmozart\Console\Api\Formatter\Formatter; |
||
15 | use Webmozart\Console\Util\StringUtil; |
||
16 | |||
17 | /** |
||
18 | * Wraps cells to fit a given screen width with a given number of columns. |
||
19 | * |
||
20 | * You can add data cells with {@link addCell()}. Call {@link fit()} to fit |
||
21 | * the cells into a given maximum width and number of columns. |
||
22 | * |
||
23 | * You can access the rows with the wrapped cells with {@link getWrappedRows()}. |
||
24 | * |
||
25 | * @since 1.0 |
||
26 | * |
||
27 | * @author Bernhard Schussek <[email protected]> |
||
28 | */ |
||
29 | class CellWrapper |
||
30 | { |
||
31 | /** |
||
32 | * @var string[] |
||
33 | */ |
||
34 | private $cells = array(); |
||
35 | |||
36 | /** |
||
37 | * @var int[][] |
||
38 | */ |
||
39 | private $cellLengths = array(); |
||
40 | |||
41 | /** |
||
42 | * @var string[][] |
||
43 | */ |
||
44 | private $wrappedRows = array(); |
||
45 | |||
46 | /** |
||
47 | * @var int |
||
48 | */ |
||
49 | private $nbColumns = 0; |
||
50 | |||
51 | /** |
||
52 | * @var int[] |
||
53 | */ |
||
54 | private $columnLengths; |
||
55 | |||
56 | /** |
||
57 | * @var bool |
||
58 | */ |
||
59 | private $wordWraps = false; |
||
60 | |||
61 | /** |
||
62 | * @var bool |
||
63 | */ |
||
64 | private $wordCuts = false; |
||
65 | |||
66 | /** |
||
67 | * @var int |
||
68 | */ |
||
69 | private $maxTotalWidth = 0; |
||
70 | |||
71 | /** |
||
72 | * @var int |
||
73 | */ |
||
74 | private $totalWidth = 0; |
||
75 | |||
76 | /** |
||
77 | * Adds a cell to the wrapper. |
||
78 | * |
||
79 | * @param string $cell The data cell. |
||
80 | */ |
||
81 | 21 | public function addCell($cell) |
|
82 | { |
||
83 | 21 | $this->cells[] = rtrim($cell); |
|
84 | 21 | } |
|
85 | |||
86 | /** |
||
87 | * Adds cells to the wrapper. |
||
88 | * |
||
89 | * @param string[] $cells The data cells. |
||
90 | */ |
||
91 | public function addCells(array $cells) |
||
92 | { |
||
93 | foreach ($cells as $cell) { |
||
94 | $this->cells[] = rtrim($cell); |
||
95 | } |
||
96 | } |
||
97 | |||
98 | /** |
||
99 | * Sets the data cells in the wrapper. |
||
100 | * |
||
101 | * @param string[] $cells The data cells. |
||
102 | */ |
||
103 | public function setCells(array $cells) |
||
104 | { |
||
105 | $this->cells = array(); |
||
106 | |||
107 | $this->addCells($cells); |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Returns the data cells in the wrapper. |
||
112 | * |
||
113 | * @return string[] The data cells. |
||
114 | */ |
||
115 | public function getCells() |
||
116 | { |
||
117 | return $this->cells; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Returns the wrapped cells organized by rows and columns. |
||
122 | * |
||
123 | * The method {@link fit()} should be called before accessing this method. |
||
124 | * Otherwise, an empty array is returned. |
||
125 | * |
||
126 | * @return string[][] An array of arrays. The first level represents rows, |
||
127 | * the second level the cells in each row. |
||
128 | */ |
||
129 | 21 | public function getWrappedRows() |
|
130 | { |
||
131 | 21 | return $this->wrappedRows; |
|
132 | } |
||
133 | |||
134 | /** |
||
135 | * Returns the lengths of the wrapped columns. |
||
136 | * |
||
137 | * The method {@link fit()} should be called before accessing this method. |
||
138 | * Otherwise, an empty array is returned. |
||
139 | * |
||
140 | * @return int[] The lengths of each column. |
||
141 | */ |
||
142 | 21 | public function getColumnLengths() |
|
143 | { |
||
144 | 21 | return $this->columnLengths; |
|
145 | } |
||
146 | |||
147 | /** |
||
148 | * Returns the number of wrapped columns. |
||
149 | * |
||
150 | * The method {@link fit()} should be called before accessing this method. |
||
151 | * Otherwise this method returns zero. |
||
152 | * |
||
153 | * @return int The number of columns. |
||
154 | */ |
||
155 | public function getNbColumns() |
||
156 | { |
||
157 | return $this->nbColumns; |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * Returns an estimated number of columns for the given maximum width. |
||
162 | * |
||
163 | * @param int $maxTotalWidth The maximum total width of the columns. |
||
164 | * |
||
165 | * @return int The estimated number of columns. |
||
166 | */ |
||
167 | 10 | public function getEstimatedNbColumns($maxTotalWidth) |
|
168 | { |
||
169 | 10 | $i = 0; |
|
170 | 10 | $rowWidth = 0; |
|
171 | |||
172 | 10 | while (isset($this->cells[$i])) { |
|
173 | 10 | $rowWidth += StringUtil::getLength($this->cells[$i]); |
|
174 | |||
175 | 10 | if ($rowWidth > $maxTotalWidth) { |
|
176 | // Return previous number of columns |
||
177 | 9 | return $i; |
|
178 | } |
||
179 | |||
180 | 10 | ++$i; |
|
181 | } |
||
182 | |||
183 | 1 | return $i; |
|
184 | } |
||
185 | |||
186 | /** |
||
187 | * Returns the maximum allowed total width of the columns. |
||
188 | * |
||
189 | * The method {@link fit()} should be called before accessing this method. |
||
190 | * Otherwise this method returns zero. |
||
191 | * |
||
192 | * @return int The maximum allowed total width. |
||
193 | */ |
||
194 | public function getMaxTotalWidth() |
||
195 | { |
||
196 | return $this->maxTotalWidth; |
||
197 | } |
||
198 | |||
199 | /** |
||
200 | * Returns the actual total column width. |
||
201 | * |
||
202 | * The method {@link fit()} should be called before accessing this method. |
||
203 | * Otherwise this method returns zero. |
||
204 | * |
||
205 | * @return int The actual total column width. |
||
206 | */ |
||
207 | public function getTotalWidth() |
||
208 | { |
||
209 | return $this->totalWidth; |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Returns whether any of the cells needed to be wrapped into multiple |
||
214 | * lines. |
||
215 | * |
||
216 | * The method {@link fit()} should be called before accessing this method. |
||
217 | * Otherwise this method returns `false`. |
||
218 | * |
||
219 | * @return bool Returns `true` if a cell was wrapped into multiple lines |
||
220 | * and `false` otherwise. |
||
221 | */ |
||
222 | public function hasWordWraps() |
||
223 | { |
||
224 | return $this->wordWraps; |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * Returns whether any of the cells contains words cut in two. |
||
229 | * |
||
230 | * The method {@link fit()} should be called before accessing this method. |
||
231 | * Otherwise this method returns `false`. |
||
232 | * |
||
233 | * @return bool Returns `true` if a cell contains words cut in two and |
||
234 | * `false` otherwise. |
||
235 | */ |
||
236 | 10 | public function hasWordCuts() |
|
237 | { |
||
238 | 10 | return $this->wordCuts; |
|
239 | } |
||
240 | |||
241 | /** |
||
242 | * Fits the added cells into the given maximum total width with the given |
||
243 | * number of columns. |
||
244 | * |
||
245 | * @param int $maxTotalWidth The maximum total width of the columns. |
||
246 | * @param int $nbColumns The number of columns to use. |
||
247 | * @param Formatter $formatter The formatter used to remove style tags. |
||
248 | */ |
||
249 | 21 | public function fit($maxTotalWidth, $nbColumns, Formatter $formatter) |
|
250 | { |
||
251 | 21 | $this->resetState($maxTotalWidth, $nbColumns); |
|
252 | 21 | $this->initRows($formatter); |
|
253 | |||
254 | // If the cells fit within the max width we're good |
||
255 | 21 | if ($this->totalWidth <= $maxTotalWidth) { |
|
256 | 7 | return; |
|
257 | } |
||
258 | |||
259 | 14 | $this->wrapColumns($formatter); |
|
260 | 14 | } |
|
261 | |||
262 | 21 | private function resetState($maxTotalWidth, $nbColumns) |
|
263 | { |
||
264 | 21 | $this->wrappedRows = array(); |
|
265 | 21 | $this->nbColumns = $nbColumns; |
|
266 | 21 | $this->cellLengths = array(); |
|
267 | 21 | $this->columnLengths = array_fill(0, $nbColumns, 0); |
|
268 | 21 | $this->wordWraps = false; |
|
269 | 21 | $this->wordCuts = false; |
|
270 | 21 | $this->maxTotalWidth = $maxTotalWidth; |
|
271 | 21 | $this->totalWidth = 0; |
|
272 | 21 | } |
|
273 | |||
274 | 21 | private function initRows(Formatter $formatter) |
|
275 | { |
||
276 | 21 | $row = null; |
|
277 | 21 | $col = 0; |
|
278 | |||
279 | 21 | foreach ($this->cells as $i => $cell) { |
|
280 | 21 | if (0 === $col) { |
|
281 | 21 | $this->wrappedRows[] = array(); |
|
282 | 21 | $this->cellLengths[] = array(); |
|
283 | |||
284 | 21 | $row = &$this->wrappedRows[count($this->wrappedRows) - 1]; |
|
285 | 21 | $cellLengths = &$this->cellLengths[count($this->cellLengths) - 1]; |
|
286 | } |
||
287 | |||
288 | 21 | $row[$col] = $cell; |
|
289 | 21 | $cellLengths[$col] = StringUtil::getLength($cell, $formatter); |
|
0 ignored issues
–
show
|
|||
290 | 21 | $this->columnLengths[$col] = max($this->columnLengths[$col], $cellLengths[$col]); |
|
291 | |||
292 | 21 | $col = ($col + 1) % $this->nbColumns; |
|
293 | } |
||
294 | |||
295 | // Fill last row up |
||
296 | 21 | if ($col > 0) { |
|
297 | 7 | while ($col < $this->nbColumns) { |
|
298 | 7 | $row[$col] = ''; |
|
299 | 7 | $cellLengths[$col] = 0; |
|
300 | 7 | ++$col; |
|
301 | } |
||
302 | } |
||
303 | |||
304 | 21 | $this->totalWidth = array_sum($this->columnLengths); |
|
0 ignored issues
–
show
It seems like
array_sum($this->columnLengths) can also be of type double . However, the property $totalWidth 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 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;
}
![]() |
|||
305 | 21 | } |
|
306 | |||
307 | 14 | private function wrapColumns(Formatter $formatter) |
|
308 | { |
||
309 | 14 | $availableWidth = $this->maxTotalWidth; |
|
310 | 14 | $longColumnLengths = $this->columnLengths; |
|
311 | |||
312 | // Filter "short" column, i.e. columns that are not wrapped |
||
313 | // We distribute the available screen width by the number of columns |
||
314 | // and decide that all columns that are shorter than their share are |
||
315 | // "short". |
||
316 | // This process is repeated until no more "short" columns are found. |
||
317 | do { |
||
318 | 14 | $threshold = $availableWidth / count($longColumnLengths); |
|
319 | 14 | $repeat = false; |
|
320 | |||
321 | 14 | foreach ($longColumnLengths as $col => $length) { |
|
322 | 14 | if ($length <= $threshold) { |
|
323 | 12 | $availableWidth -= $length; |
|
324 | 12 | unset($longColumnLengths[$col]); |
|
325 | 14 | $repeat = true; |
|
326 | } |
||
327 | } |
||
328 | 14 | } while ($repeat); |
|
329 | |||
330 | // Calculate actual and available width |
||
331 | 14 | $actualWidth = 0; |
|
332 | 14 | $lastAdaptedCol = 0; |
|
333 | |||
334 | // "Long" columns, i.e. columns that need to be wrapped, are added to |
||
335 | // the actual width |
||
336 | 14 | foreach ($longColumnLengths as $col => $length) { |
|
337 | 14 | $actualWidth += $length; |
|
338 | 14 | $lastAdaptedCol = $col; |
|
339 | } |
||
340 | |||
341 | // Fit columns into available width |
||
342 | 14 | foreach ($longColumnLengths as $col => $length) { |
|
343 | // Keep ratios of column lengths and distribute them among the |
||
344 | // available width |
||
345 | 14 | $this->columnLengths[$col] = round(($length / $actualWidth) * $availableWidth); |
|
346 | |||
347 | 14 | if ($col === $lastAdaptedCol) { |
|
348 | // Fix rounding errors |
||
349 | 14 | $this->columnLengths[$col] += $this->maxTotalWidth - array_sum($this->columnLengths); |
|
350 | } |
||
351 | |||
352 | 14 | $this->wrapColumn($col, $this->columnLengths[$col], $formatter); |
|
353 | |||
354 | // Recalculate the column length based on the actual wrapped length |
||
355 | 14 | $this->refreshColumnLength($col); |
|
356 | |||
357 | // Recalculate the actual width based on the changed length. |
||
358 | 14 | $actualWidth = $actualWidth - $length + $this->columnLengths[$col]; |
|
359 | } |
||
360 | |||
361 | 14 | $this->totalWidth = array_sum($this->columnLengths); |
|
0 ignored issues
–
show
It seems like
array_sum($this->columnLengths) can also be of type double . However, the property $totalWidth 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 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;
}
![]() |
|||
362 | 14 | } |
|
363 | |||
364 | 14 | private function wrapColumn($col, $columnLength, Formatter $formatter) |
|
365 | { |
||
366 | 14 | foreach ($this->wrappedRows as $i => $row) { |
|
367 | 14 | $cell = $row[$col]; |
|
368 | 14 | $cellLength = $this->cellLengths[$i][$col]; |
|
369 | |||
370 | 14 | if ($cellLength > $columnLength) { |
|
371 | 14 | $this->wordWraps = true; |
|
372 | |||
373 | 14 | if (!$this->wordCuts) { |
|
374 | 14 | $minLengthWithoutCut = StringUtil::getMaxWordLength($cell, $formatter); |
|
375 | |||
376 | 14 | if ($minLengthWithoutCut > $columnLength) { |
|
377 | 7 | $this->wordCuts = true; |
|
378 | } |
||
379 | } |
||
380 | |||
381 | // TODO use format aware wrapper |
||
382 | // true: Words may be cut in two |
||
383 | 14 | $wrappedCell = wordwrap($cell, $columnLength, "\n", true); |
|
384 | |||
385 | 14 | $this->wrappedRows[$i][$col] = $wrappedCell; |
|
386 | |||
387 | // Refresh cell length |
||
388 | 14 | $this->cellLengths[$i][$col] = StringUtil::getMaxLineLength($wrappedCell, $formatter); |
|
389 | } |
||
390 | } |
||
391 | 14 | } |
|
392 | |||
393 | 14 | private function refreshColumnLength($col) |
|
394 | { |
||
395 | 14 | $this->columnLengths[$col] = 0; |
|
396 | |||
397 | 14 | foreach ($this->wrappedRows as $i => $row) { |
|
398 | 14 | $this->columnLengths[$col] = max($this->columnLengths[$col], $this->cellLengths[$i][$col]); |
|
399 | } |
||
400 | 14 | } |
|
401 | } |
||
402 |
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:
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
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: