ColumnWidths::averageWidth()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
namespace Consolidation\OutputFormatters\Transformations\Wrap;
3
4
use Symfony\Component\Console\Helper\TableStyle;
5
6
/**
7
 * Calculate the width of data in table cells in preparation for word wrapping.
8
 */
9
class ColumnWidths
10
{
11
    protected $widths;
12
13
    public function __construct($widths = [])
14
    {
15
        $this->widths = $widths;
16
    }
17
18
    public function paddingSpace(
19
        $paddingInEachCell,
20
        $extraPaddingAtEndOfLine = 0,
21
        $extraPaddingAtBeginningOfLine = 0
22
    ) {
23
        return ($extraPaddingAtBeginningOfLine + $extraPaddingAtEndOfLine + (count($this->widths) * $paddingInEachCell));
24
    }
25
26
    /**
27
     * Find all of the columns that are shorter than the specified threshold.
28
     */
29
    public function findShortColumns($thresholdWidth)
30
    {
31
        $thresholdWidths = array_fill_keys(array_keys($this->widths), $thresholdWidth);
32
33
        return $this->findColumnsUnderThreshold($thresholdWidths);
34
    }
35
36
    /**
37
     * Find all of the columns that are shorter than the corresponding minimum widths.
38
     */
39
    public function findUndersizedColumns($minimumWidths)
40
    {
41
        return $this->findColumnsUnderThreshold($minimumWidths->widths());
42
    }
43
44
    protected function findColumnsUnderThreshold(array $thresholdWidths)
45
    {
46
        $shortColWidths = [];
47
        foreach ($this->widths as $key => $maxLength) {
48
            if (isset($thresholdWidths[$key]) && ($maxLength <= $thresholdWidths[$key])) {
49
                $shortColWidths[$key] = $maxLength;
50
            }
51
        }
52
53
        return new ColumnWidths($shortColWidths);
54
    }
55
56
    /**
57
     * If the widths specified by this object do not fit within the
58
     * provided avaiable width, then reduce them all proportionally.
59
     */
60
    public function adjustMinimumWidths($availableWidth, $dataCellWidths)
61
    {
62
        $result = $this->selectColumns($dataCellWidths->keys());
63
        if ($result->isEmpty()) {
64
            return $result;
65
        }
66
        $numberOfColumns = $dataCellWidths->count();
67
68
        // How many unspecified columns are there?
69
        $unspecifiedColumns = $numberOfColumns - $result->count();
70
        $averageWidth = $this->averageWidth($availableWidth);
71
72
        // Reserve some space for the columns that have no minimum.
73
        // Make sure they collectively get at least half of the average
74
        // width for each column. Or should it be a quarter?
75
        $reservedSpacePerColumn = ($averageWidth / 2);
76
        $reservedSpace = $reservedSpacePerColumn * $unspecifiedColumns;
77
78
        // Calculate how much of the available space is remaining for use by
79
        // the minimum column widths after the reserved space is accounted for.
80
        $remainingAvailable = $availableWidth - $reservedSpace;
81
82
        // Don't do anything if our widths fit inside the available widths.
83
        if ($result->totalWidth() <= $remainingAvailable) {
84
            return $result;
85
        }
86
87
        // Shrink the minimum widths if the table is too compressed.
88
        return $result->distribute($remainingAvailable);
89
    }
90
91
    /**
92
     * Return proportional weights
93
     */
94
    public function distribute($availableWidth)
95
    {
96
        $result = [];
97
        $totalWidth = $this->totalWidth();
98
        $lastColumn = $this->lastColumn();
99
        $widths = $this->widths();
100
101
        // Take off the last column, and calculate proportional weights
102
        // for the first N-1 columns.
103
        array_pop($widths);
104
        foreach ($widths as $key => $width) {
105
            $result[$key] = round(($width / $totalWidth) * $availableWidth);
106
        }
107
108
        // Give the last column the rest of the available width
109
        $usedWidth = $this->sumWidth($result);
110
        $result[$lastColumn] = $availableWidth - $usedWidth;
111
112
        return new ColumnWidths($result);
113
    }
114
115
    public function lastColumn()
116
    {
117
        $keys = $this->keys();
118
        return array_pop($keys);
119
    }
120
121
    /**
122
     * Return the number of columns.
123
     */
124
    public function count()
125
    {
126
        return count($this->widths);
127
    }
128
129
    /**
130
     * Calculate how much space is available on average for all columns.
131
     */
132
    public function averageWidth($availableWidth)
133
    {
134
        if ($this->isEmpty()) {
135
            debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
136
        }
137
        return $availableWidth / $this->count();
138
    }
139
140
    /**
141
     * Return the available keys (column identifiers) from the calculated
142
     * data set.
143
     */
144
    public function keys()
145
    {
146
        return array_keys($this->widths);
147
    }
148
149
    /**
150
     * Set the length of the specified column.
151
     */
152
    public function setWidth($key, $width)
153
    {
154
        $this->widths[$key] = $width;
155
    }
156
157
    /**
158
     * Return the length of the specified column.
159
     */
160
    public function width($key)
161
    {
162
        return isset($this->widths[$key]) ? $this->widths[$key] : 0;
163
    }
164
165
    /**
166
     * Return all of the lengths
167
     */
168
    public function widths()
169
    {
170
        return $this->widths;
171
    }
172
173
    /**
174
     * Return true if there is no data in this object
175
     */
176
    public function isEmpty()
177
    {
178
        return empty($this->widths);
179
    }
180
181
    /**
182
     * Return the sum of the lengths of the provided widths.
183
     */
184
    public function totalWidth()
185
    {
186
        return static::sumWidth($this->widths());
187
    }
188
189
    /**
190
     * Return the sum of the lengths of the provided widths.
191
     */
192
    public static function sumWidth($widths)
193
    {
194
        return array_reduce(
195
            $widths,
196
            function ($carry, $item) {
197
                return $carry + $item;
198
            }
199
        );
200
    }
201
202
    /**
203
     * Ensure that every item in $widths that has a corresponding entry
204
     * in $minimumWidths is as least as large as the minimum value held there.
205
     */
206
    public function enforceMinimums($minimumWidths)
207
    {
208
        $result = [];
209
        if ($minimumWidths instanceof ColumnWidths) {
210
            $minimumWidths = $minimumWidths->widths();
211
        }
212
        $minimumWidths += $this->widths;
213
214
        foreach ($this->widths as $key => $value) {
215
            $result[$key] = max($value, $minimumWidths[$key]);
216
        }
217
218
        return new ColumnWidths($result);
219
    }
220
221
    /**
222
     * Remove all of the specified columns from this data structure.
223
     */
224
    public function removeColumns($columnKeys)
225
    {
226
        $widths = $this->widths();
227
228
        foreach ($columnKeys as $key) {
229
            unset($widths[$key]);
230
        }
231
232
        return new ColumnWidths($widths);
233
    }
234
235
    /**
236
     * Select all columns that exist in the provided list of keys.
237
     */
238
    public function selectColumns($columnKeys)
239
    {
240
        $widths = [];
241
242
        foreach ($columnKeys as $key) {
243
            if (isset($this->widths[$key])) {
244
                $widths[$key] = $this->width($key);
245
            }
246
        }
247
248
        return new ColumnWidths($widths);
249
    }
250
251
    /**
252
     * Combine this set of widths with another set, and return
253
     * a new set that contains the entries from both.
254
     */
255
    public function combine(ColumnWidths $combineWith)
256
    {
257
        // Danger: array_merge renumbers numeric keys; that must not happen here.
258
        $combined = $combineWith->widths();
259
        foreach ($this->widths() as $key => $value) {
260
            $combined[$key] = $value;
261
        }
262
        return new ColumnWidths($combined);
263
    }
264
}
265