AbstractFormatter   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 334
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 41
eloc 141
dl 0
loc 334
ccs 167
cts 167
cp 1
rs 9.1199
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A addRows() 0 3 1
A __construct() 0 2 1
A getParam() 0 2 1
A format() 0 8 1
A addRow() 0 15 4
A addTopBorder() 0 7 2
A addHeader() 0 8 2
A addHeaderBorder() 0 6 1
A getAbcRange() 0 15 5
B initSidebar() 0 30 7
A initHeader() 0 27 6
A setParams() 0 6 2
A init() 0 12 2
A getNumRange() 0 7 3
A addBottomBorder() 0 7 2
A addDataRow() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like AbstractFormatter 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 AbstractFormatter, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace ToolkitLab\ASCII;
4
5
use ToolkitLab\ASCII\Table;
6
7
abstract class AbstractFormatter implements FormatterInterface {
8
    
9
    const
10
        DEFAULT_MODE              = 0x00,
11
12
        HEADER_FIRST_ROW_MODE     = 0X01,
13
        HEADER_NUMERIC_MODE       = 0x02,
14
        HEADER_ABC_MODE           = 0X04,
15
            
16
        SIDEBAR_NUMERIC_MODE      = 0X10,
17
        SIDEBAR_ABC_MODE          = 0x20,
18
            
19
        SPREADSHEET_MODE          = 0X14;
20
21
    /**
22
     * @var Table
23
     */
24
    protected $table;
25
26
    /**
27
     * @var array
28
     */
29
    protected $metadata = [];
30
31
    /**
32
     * @var string
33
     */
34
    protected $output = '';
35
36
    /**
37
     * The data to be formatted
38
     * @var array
39
     */
40
    protected $data = [];
41
    
42
    /**
43
     * @var boolean
44
     */
45
    protected $useHeader = false;
46
    
47
    /**
48
     * @var boolean
49
     */
50
    protected $useSidebar = false;
51
52
    /**
53
     * Formatting parameters
54
     * @var array
55
     */
56
    protected $params = [
57
        'mode' => self::DEFAULT_MODE,
58
        'rotate' => false,
59
        'max_cell_length' => 100,
60
        'max_cell_ending' => '...',
61
    ];
62
63
    /**
64
     * Constructor
65
     * @param array $params
66
     */
67 44
    public function __construct($params = []) {
68 44
        $this->setParams($params);
69 42
    }
70
71
    /**
72
     * Converts an array into ASCII-formatted string (table)
73
     * @param array $data
74
     * @param array $params
75
     * @return string
76
     */
77 42
    public function format(array $data, $params = []) {
78 42
        $table = new Table($data);
79 42
        $this->init($table, $params);
80 38
        $this->addTopBorder();
81 38
        $this->addHeader();
82 38
        $this->addRows();
83 38
        $this->addBottomBorder();
84 38
        return $this->output;
85
    }
86
87
    /**
88
     * Initializes the data/parameters before formatting
89
     * @param Table $table
90
     * @param array $params
91
     * @return void
92
     */
93 42
    protected function init(Table $table, $params = []) {
94 42
        $this->output = '';
95 42
        $this->setParams($params);
96 42
        $this->table = $table;
97 42
        if ($this->getParam('rotate') !== false) {
98 8
            $this->table->rotate($this->getParam('rotate'));
99 8
        }
100 42
        $this->initSidebar();
101 40
        $this->initHeader();
102 38
        $maxLength = $this->getParam('max_cell_length');
103 38
        $ending = $this->getParam('max_cell_ending');
104 38
        $this->table->truncate($maxLength, $ending);
105 38
    }
106
    
107
    /**
108
     * Initializes the header
109
     * @throws \LogicException
110
     */
111 40
    protected function initHeader() {
112 40
        $count = 0;
113 40
        $mode = $this->getParam('mode');
114 40
        $data = $this->table->getData();
115 40
        $x = $this->table->getDimensionX();
116
        
117 40
        if (($mode & self::HEADER_FIRST_ROW_MODE) === self::HEADER_FIRST_ROW_MODE) {
118 10
            $count++;
119 10
        }
120
        
121 40
        if (($mode & self::HEADER_ABC_MODE) === self::HEADER_ABC_MODE) {
122 8
            $data = array_merge([$this->getAbcRange($x, $this->useSidebar)], $data);
123 8
            $count++;
124 8
        }
125
        
126 40
        if (($mode & self::HEADER_NUMERIC_MODE) === self::HEADER_NUMERIC_MODE) {
127 6
            $data = array_merge([$this->getNumRange($x, $this->useSidebar)], $data);
128 6
            $count++;
129 6
        }
130
        
131 40
        if ($count > 1) {
132 2
            throw new \LogicException('There should be only one header.');
133
        }
134
        
135 38
        if ($count) {
136 20
            $this->useHeader = true;
137 20
            $this->table->setData($data);
138 20
        }
139 38
    }
140
    
141
    /**
142
     * Initializes the sidebar
143
     * @throws \LogicException
144
     */
145 42
    protected function initSidebar() {
146 42
        $count = 0;
147 42
        $mode = $this->getParam('mode');
148 42
        $data = $this->table->getData();
149 42
        $headerMode = ($mode & self::HEADER_FIRST_ROW_MODE) === self::HEADER_FIRST_ROW_MODE;
150 42
        $y = $this->table->getDimensionY();
151
        
152 42
        if (($mode & self::SIDEBAR_ABC_MODE) === self::SIDEBAR_ABC_MODE) {
153 4
            $abc = $this->getAbcRange($y, $headerMode);
154 4
            foreach ($data as $key => $val) {
155 4
                $data[$key] = array_merge([array_shift($abc)], $val);
156 4
            }
157 4
            $count++;
158 4
        }
159
        
160 42
        if (($mode & self::SIDEBAR_NUMERIC_MODE) === self::SIDEBAR_NUMERIC_MODE) {
161 8
            $num = $this->getNumRange($y, $headerMode);
162 8
            foreach ($data as $key => $val) {
163 8
                $data[$key] = array_merge([array_shift($num)], $val);
164 8
            }
165 8
            $count++;
166 8
        }
167
        
168 42
        if ($count > 1) {
169 2
            throw new \LogicException('There should be only one sidebar.');
170
        }
171
        
172 40
        if ($count) {
173 8
            $this->useSidebar = true;
174 8
            $this->table->setData($data);
175 8
        }
176 40
    }
177
    
178
    /**
179
     * Updates the parameters with new values
180
     * @param array $params
181
     * @throws \InvalidArgumentException
182
     */
183 44
    protected function setParams($params) {
184 44
        $unknownParams = array_diff(array_keys($params), array_keys($this->params));
185 44
        if (count($unknownParams)) {
186 2
            throw new \InvalidArgumentException('Unknown parameter(-s): ' . implode(', ', $unknownParams));
187
        }
188 42
        $this->params = array_merge($this->params, $params);
189 42
    }
190
191
    /**
192
     * Get the specified parameter
193
     * @param string $key
194
     * @return mixed
195
     */
196 42
    protected function getParam($key) {
197 42
        return $this->params[$key];
198
    }
199
    
200
    /**
201
     * Returns an array of letters for a spreadsheet header
202
     * @param int $length
203
     * @return array
204
     */
205 12
    final protected function getAbcRange($length, $firstEmpty = false) {
206 12
        $length = $firstEmpty ? $length - 1 : $length;
207 12
        $baseRange = range('A', 'Z');
208 12
        $range = range('A', 'Z');
209 12
        while (count($range) < $length && count($baseRange) > 1) {
210 2
            $letter = array_shift($baseRange);
211 2
            $tmpRange = range('A', 'Z');
212
            array_walk($tmpRange, function(&$e) use ($letter) { $e = $letter . $e; });
213 2
            $range = array_merge($range, $tmpRange);
214 2
        }
215 12
        $range = array_slice($range, 0, $length);
216 12
        if ($firstEmpty) {
217 4
            $range = array_merge([''], $range);
218 4
        }
219 12
        return $range;
220
    }
221
    
222
    /**
223
     * Returns an array of digits for a spreadsheet header
224
     * @param int $length
225
     * @return array
226
     */
227 12
    final protected function getNumRange($length, $firstEmpty = false) {
228 12
        $length = $firstEmpty ? $length - 1 : $length;
229 12
        $range = range(1, $length);
230 12
        if ($firstEmpty) {
231 2
            $range = array_merge([''], $range);
232 2
        }
233 12
        return $range;
234
    }
235
236
    /**
237
     * Adds the top border to the output
238
     * @return void
239
     */
240 38
    protected function addTopBorder() {
241 38
        if ($this->metadata['top_border']) {
242 34
            $this->addRow(
243 34
                $this->metadata['top_border']['left'],
244 34
                $this->metadata['top_border']['middle'],
245 34
                $this->metadata['top_border']['right'],
246 34
                $this->metadata['top_border']['pad']
247 34
            );
248 34
        }
249 38
    }
250
251
    /**
252
     * Adds the header row to the output
253
     * @return void
254
     */
255 38
    protected function addHeader() {
256 38
        if ($this->useHeader) {
257 20
            $data = $this->table->getData();
258 20
            $x = $this->table->getDimensionX();
259 20
            $y = $this->table->getDimensionY();
260 20
            $this->addDataRow(array_shift($data));
261 20
            $this->addHeaderBorder();
262 20
            $this->table->setData($data, $x, $y);
263 20
        }
264 38
    }
265
266
    /**
267
     * Adds the rows to the output
268
     */
269 38
    protected function addRows() {
270 38
        $data = $this->table->getData();
271 38
        array_walk($data, [$this, 'addDataRow']);
272 38
    }
273
274
    /**
275
     * Adds the row with the specified data to the output
276
     * @param array $row
277
     * @return void
278
     */
279 38
    protected function addDataRow($row) {
280 38
        $this->addRow(
281 38
            $this->metadata['content']['left'],
282 38
            $this->metadata['content']['middle'],
283 38
            $this->metadata['content']['right'],
284 38
            $this->metadata['content']['pad'],
285
            $row
286 38
        );
287 38
    }
288
289
    /**
290
     * Adds the bottom border of the header to the output
291
     * @return void
292
     */
293 20
    protected function addHeaderBorder() {
294 20
        $this->addRow(
295 20
            $this->metadata['header']['left'],
296 20
            $this->metadata['header']['middle'],
297 20
            $this->metadata['header']['right'],
298 20
            $this->metadata['header']['pad']
299 20
        );
300 20
    }
301
302
    /**
303
     * Adds the bottom border to the output
304
     * @return void
305
     */
306 38
    protected function addBottomBorder() {
307 38
        if ($this->metadata['bottom_border']) {
308 34
            $this->addRow(
309 34
                $this->metadata['bottom_border']['left'],
310 34
                $this->metadata['bottom_border']['middle'],
311 34
                $this->metadata['bottom_border']['right'],
312 34
                $this->metadata['bottom_border']['pad']
313 34
            );
314 34
        }
315 38
    }
316
317
    /**
318
     * Adds the row to the output
319
     * @param string $lb
320
     * @param string $bm
321
     * @param string $br
322
     * @param string $pad
323
     * @param array $row
324
     * @return void
325
     */
326 38
    protected function addRow($lb, $bm, $br, $pad, $row = []) {
327 38
        $delimiter = $lb;
328 38
        for ($i = 0; $i < $this->table->getDimensionX(); $i++) {
329 38
            $maxLength = $this->table->getColumnsMaxLength($i);
330 38
            $this->output .= $delimiter;
331 38
            if (count($row)) {
332 38
                $cell = isset($row[$i]) ? $row[$i] : '';
333 38
                $spaces = str_repeat($pad, $maxLength - strlen($cell));
334 38
                $this->output .= " {$cell}{$spaces} ";
335 38
            } else {
336 36
                $this->output .= str_repeat($pad, $maxLength + 2);
337
            }
338 38
            $delimiter = $bm;
339 38
        }
340 38
        $this->output .= $br . PHP_EOL;
341 38
    }
342
343
}
344