Completed
Push — master ( bf0dce...7bf51d )
by Greg
9s
created

WordWrapper::setPaddingFromStyle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 1
eloc 6
nc 1
nop 1
1
<?php
2
namespace Consolidation\OutputFormatters\Transformations;
3
4
use Symfony\Component\Console\Helper\TableStyle;
5
6
class WordWrapper
7
{
8
    protected $width;
9
10
    // For now, hardcode these to match what the Symfony Table helper does.
11
    // Note that these might actually need to be adjusted depending on the
12
    // table style.
13
    protected $extraPaddingAtBeginningOfLine = 0;
14
    protected $extraPaddingAtEndOfLine = 0;
15
    protected $paddingInEachCell = 3;
16
17
    public function __construct($width)
18
    {
19
        $this->width = $width;
20
    }
21
22
    /**
23
     * Calculate our padding widths from the specified table style.
24
     * @param TableStyle $style
25
     */
26
    public function setPaddingFromStyle(TableStyle $style)
27
    {
28
        $verticalBorderLen = strlen($style->getVerticalBorderChar());
29
        $paddingLen = strlen($style->getPaddingChar());
30
31
        $this->extraPaddingAtBeginningOfLine = 0;
32
        $this->extraPaddingAtEndOfLine = $verticalBorderLen;
33
        $this->paddingInEachCell = $verticalBorderLen + $paddingLen;
34
    }
35
36
    /**
37
     * Wrap the cells in each part of the provided data table
38
     * @param array $rows
39
     * @return array
40
     */
41
    public function wrap($rows, $widths = [])
42
    {
43
        // If the width was not set, then disable wordwrap.
44
        if (!$this->width) {
45
            return $rows;
46
        }
47
48
        // Calculate the column widths to use based on the content.
49
        $auto_widths = $this->columnAutowidth($rows, $widths);
50
51
        // Do wordwrap on all cells.
52
        $newrows = array();
53
        foreach ($rows as $rowkey => $row) {
54
            foreach ($row as $colkey => $cell) {
55
                $newrows[$rowkey][$colkey] = $this->wrapCell($cell, $auto_widths[$colkey]);
56
            }
57
        }
58
59
        return $newrows;
60
    }
61
62
    /**
63
     * Wrap one cell.  Guard against modifying non-strings and
64
     * then call through to wordwrap().
65
     *
66
     * @param mixed $cell
67
     * @param string $cellWidth
68
     * @return mixed
69
     */
70
    protected function wrapCell($cell, $cellWidth)
71
    {
72
        if (!is_string($cell)) {
73
            return $cell;
74
        }
75
        return wordwrap($cell, $cellWidth, "\n", true);
76
    }
77
78
    /**
79
     * Determine the best fit for column widths. Ported from Drush.
80
     *
81
     * @param array $rows The rows to use for calculations.
82
     * @param array $widths Manually specified widths of each column
83
     *   (in characters) - these will be left as is.
84
     */
85
    protected function columnAutowidth($rows, $widths)
86
    {
87
        $auto_widths = $widths;
88
89
        // First we determine the distribution of row lengths in each column.
90
        // This is an array of descending character length keys (i.e. starting at
91
        // the rightmost character column), with the value indicating the number
92
        // of rows where that character column is present.
93
        $col_dist = array();
94
        foreach ($rows as $rowkey => $row) {
95
            foreach ($row as $col_id => $cell) {
96
                if (empty($widths[$col_id])) {
97
                    $length = strlen($cell);
98
                    if ($length == 0) {
99
                        $col_dist[$col_id][0] = 0;
100
                    }
101
                    while ($length > 0) {
102
                        if (!isset($col_dist[$col_id][$length])) {
103
                            $col_dist[$col_id][$length] = 0;
104
                        }
105
                        $col_dist[$col_id][$length]++;
106
                        $length--;
107
                    }
108
                }
109
            }
110
        }
111
        foreach ($col_dist as $col_id => $count) {
112
            // Sort the distribution in decending key order.
113
            krsort($col_dist[$col_id]);
114
            // Initially we set all columns to their "ideal" longest width
115
            // - i.e. the width of their longest column.
116
            $auto_widths[$col_id] = max(array_keys($col_dist[$col_id]));
117
        }
118
119
        // We determine what width we have available to use, and what width the
120
        // above "ideal" columns take up.
121
        $available_width = $this->width - ($this->extraPaddingAtBeginningOfLine + $this->extraPaddingAtEndOfLine + (count($auto_widths) * $this->paddingInEachCell));
122
        $auto_width_current = array_sum($auto_widths);
123
124
        // If we need to reduce a column so that we can fit the space we use this
125
        // loop to figure out which column will cause the "least wrapping",
126
        // (relative to the other columns) and reduce the width of that column.
127
        while ($auto_width_current > $available_width) {
128
            $count = 0;
129
            $width = 0;
130
            foreach ($col_dist as $col_id => $counts) {
131
                // If we are just starting out, select the first column.
132
                if ($count == 0 ||
133
                 // OR: if this column would cause less wrapping than the currently
134
                 // selected column, then select it.
135
                 (current($counts) < $count) ||
136
                 // OR: if this column would cause the same amount of wrapping, but is
137
                 // longer, then we choose to wrap the longer column (proportionally
138
                 // less wrapping, and helps avoid triple line wraps).
139
                 (current($counts) == $count && key($counts) > $width)) {
140
                    // Select the column number, and record the count and current width
141
                    // for later comparisons.
142
                    $column = $col_id;
143
                    $count = current($counts);
144
                    $width = key($counts);
145
                }
146
            }
147
            if ($width <= 1) {
148
                // If we have reached a width of 1 then give up, so wordwrap can still progress.
149
                break;
150
            }
151
            // Reduce the width of the selected column.
152
            $auto_widths[$column]--;
0 ignored issues
show
Bug introduced by
The variable $column 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...
153
            // Reduce our overall table width counter.
154
            $auto_width_current--;
155
            // Remove the corresponding data from the disctribution, so next time
156
            // around we use the data for the row to the left.
157
            unset($col_dist[$column][$width]);
158
        }
159
        return $auto_widths;
160
    }
161
}
162