1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Behat Gherkin. |
5
|
|
|
* (c) Konstantin Kudryashov <[email protected]> |
6
|
|
|
* |
7
|
|
|
* For the full copyright and license information, please view the LICENSE |
8
|
|
|
* file that was distributed with this source code. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Behat\Gherkin\Node; |
12
|
|
|
|
13
|
|
|
use ArrayIterator; |
14
|
|
|
use Behat\Gherkin\Exception\NodeException; |
15
|
|
|
use Iterator; |
16
|
|
|
use IteratorAggregate; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Represents Gherkin Table argument. |
20
|
|
|
* |
21
|
|
|
* @author Konstantin Kudryashov <[email protected]> |
22
|
|
|
*/ |
23
|
|
|
class TableNode implements ArgumentInterface, IteratorAggregate |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* @var array |
27
|
|
|
*/ |
28
|
|
|
private $table; |
29
|
|
|
/** |
30
|
|
|
* @var integer |
31
|
|
|
*/ |
32
|
|
|
private $maxLineLength = array(); |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Initializes table. |
36
|
|
|
* |
37
|
|
|
* @param array $table Table in form of [$rowLineNumber => [$val1, $val2, $val3]] |
38
|
|
|
* |
39
|
|
|
* @throws NodeException If the given table is invalid |
40
|
|
|
*/ |
41
|
52 |
|
public function __construct(array $table) |
42
|
|
|
{ |
43
|
52 |
|
$this->table = $table; |
44
|
52 |
|
$columnCount = null; |
45
|
|
|
|
46
|
52 |
|
foreach ($this->getRows() as $row) { |
47
|
39 |
|
if ($columnCount === null) { |
48
|
39 |
|
$columnCount = count($row); |
49
|
39 |
|
} |
50
|
|
|
|
51
|
39 |
|
if (count($row) !== $columnCount) { |
52
|
1 |
|
throw new NodeException('Table does not have same number of columns in every row.'); |
53
|
|
|
} |
54
|
|
|
|
55
|
39 |
|
if (!is_array($row)) { |
56
|
39 |
|
throw new NodeException('Table is not two-dimensional.'); |
57
|
39 |
|
} |
58
|
39 |
|
|
59
|
|
|
foreach ($row as $column => $string) { |
60
|
39 |
|
if (!isset($this->maxLineLength[$column])) { |
61
|
39 |
|
$this->maxLineLength[$column] = 0; |
62
|
52 |
|
} |
63
|
51 |
|
|
64
|
|
|
if (!is_scalar($string)) { |
65
|
|
|
throw new NodeException('Table is not two-dimensional.'); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
$this->maxLineLength[$column] = max($this->maxLineLength[$column], mb_strlen($string, 'utf8')); |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
2 |
|
* Creates a table from a given list. |
75
|
|
|
* |
76
|
2 |
|
* @param array $list One-dimensional array |
77
|
1 |
|
* |
78
|
|
|
* @return TableNode |
79
|
|
|
* |
80
|
1 |
|
* @throws NodeException If the given list is not a one-dimensional array |
81
|
1 |
|
*/ |
82
|
1 |
|
public static function fromList(array $list) |
83
|
1 |
|
{ |
84
|
|
|
if (count($list) !== count($list, COUNT_RECURSIVE)) { |
85
|
|
|
throw new NodeException('List is not a one-dimensional array.'); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
array_walk($list, function (&$item) { |
89
|
|
|
$item = array($item); |
90
|
|
|
}); |
91
|
|
|
return new self($list); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Returns node type. |
96
|
|
|
* |
97
|
|
|
* @return string |
98
|
|
|
*/ |
99
|
|
|
public function getNodeType() |
100
|
|
|
{ |
101
|
4 |
|
return 'Table'; |
102
|
|
|
} |
103
|
4 |
|
|
104
|
|
|
/** |
105
|
|
|
* Returns table hash, formed by columns (ColumnsHash). |
106
|
|
|
* |
107
|
|
|
* @return array |
108
|
|
|
*/ |
109
|
|
|
public function getHash() |
110
|
|
|
{ |
111
|
19 |
|
return $this->getColumnsHash(); |
112
|
|
|
} |
113
|
19 |
|
|
114
|
19 |
|
/** |
115
|
|
|
* Returns table hash, formed by columns. |
116
|
19 |
|
* |
117
|
19 |
|
* @return array |
118
|
11 |
|
*/ |
119
|
19 |
|
public function getColumnsHash() |
120
|
|
|
{ |
121
|
19 |
|
$rows = $this->getRows(); |
122
|
|
|
$keys = array_shift($rows); |
123
|
|
|
|
124
|
|
|
$hash = array(); |
125
|
|
|
foreach ($rows as $row) { |
126
|
|
|
$hash[] = array_combine($keys, $row); |
127
|
|
|
} |
128
|
|
|
|
129
|
2 |
|
return $hash; |
130
|
|
|
} |
131
|
2 |
|
|
132
|
|
|
/** |
133
|
2 |
|
* Returns table hash, formed by rows. |
134
|
2 |
|
* |
135
|
2 |
|
* @return array |
136
|
|
|
*/ |
137
|
2 |
|
public function getRowsHash() |
138
|
|
|
{ |
139
|
|
|
$hash = array(); |
140
|
|
|
|
141
|
|
|
foreach ($this->getRows() as $row) { |
142
|
|
|
$hash[array_shift($row)] = (1 == count($row)) ? $row[0] : $row; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
return $hash; |
146
|
4 |
|
} |
147
|
|
|
|
148
|
4 |
|
/** |
149
|
|
|
* Returns numerated table lines. |
150
|
|
|
* Line numbers are keys, lines are values. |
151
|
|
|
* |
152
|
|
|
* @return array |
153
|
|
|
*/ |
154
|
|
|
public function getTable() |
155
|
|
|
{ |
156
|
52 |
|
return $this->table; |
157
|
|
|
} |
158
|
52 |
|
|
159
|
|
|
/** |
160
|
|
|
* Returns table rows. |
161
|
|
|
* |
162
|
|
|
* @return array |
163
|
|
|
*/ |
164
|
|
|
public function getRows() |
165
|
|
|
{ |
166
|
5 |
|
return array_values($this->table); |
167
|
|
|
} |
168
|
5 |
|
|
169
|
|
|
/** |
170
|
|
|
* Returns table definition lines. |
171
|
|
|
* |
172
|
|
|
* @return array |
173
|
|
|
*/ |
174
|
|
|
public function getLines() |
175
|
|
|
{ |
176
|
|
|
return array_keys($this->table); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
8 |
|
* Returns specific row in a table. |
181
|
|
|
* |
182
|
8 |
|
* @param integer $index Row number |
183
|
|
|
* |
184
|
8 |
|
* @return array |
185
|
|
|
* |
186
|
|
|
* @throws NodeException If row with specified index does not exist |
187
|
|
|
*/ |
188
|
8 |
View Code Duplication |
public function getRow($index) |
|
|
|
|
189
|
|
|
{ |
190
|
|
|
$rows = $this->getRows(); |
191
|
|
|
|
192
|
|
|
if (!isset($rows[$index])) { |
193
|
|
|
throw new NodeException(sprintf('Rows #%d does not exist in table.', $index)); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
return $rows[$index]; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
1 |
|
* Returns specific column in a table. |
201
|
|
|
* |
202
|
1 |
|
* @param integer $index Column number |
203
|
|
|
* |
204
|
|
|
* @return array |
205
|
|
|
* |
206
|
1 |
|
* @throws NodeException If column with specified index does not exist |
207
|
1 |
|
*/ |
208
|
|
|
public function getColumn($index) |
209
|
1 |
|
{ |
210
|
1 |
|
if ($index >= count($this->getRow(0))) { |
211
|
1 |
|
throw new NodeException(sprintf('Column #%d does not exist in table.', $index)); |
212
|
|
|
} |
213
|
1 |
|
|
214
|
|
|
$rows = $this->getRows(); |
215
|
|
|
$column = array(); |
216
|
|
|
|
217
|
|
|
foreach ($rows as $row) { |
218
|
|
|
$column[] = $row[$index]; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
return $column; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
4 |
|
* Returns line number at which specific row was defined. |
226
|
|
|
* |
227
|
4 |
|
* @param integer $index |
228
|
|
|
* |
229
|
4 |
|
* @return integer |
230
|
|
|
* |
231
|
|
|
* @throws NodeException If row with specified index does not exist |
232
|
|
|
*/ |
233
|
4 |
View Code Duplication |
public function getRowLine($index) |
|
|
|
|
234
|
|
|
{ |
235
|
|
|
$lines = array_keys($this->table); |
236
|
|
|
|
237
|
|
|
if (!isset($lines[$index])) { |
238
|
|
|
throw new NodeException(sprintf('Rows #%d does not exist in table.', $index)); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
return $lines[$index]; |
242
|
|
|
} |
243
|
5 |
|
|
244
|
|
|
/** |
245
|
5 |
|
* Converts row into delimited string. |
246
|
5 |
|
* |
247
|
5 |
|
* @param integer $rowNum Row number |
248
|
5 |
|
* |
249
|
|
|
* @return string |
250
|
5 |
|
*/ |
251
|
|
|
public function getRowAsString($rowNum) |
252
|
|
|
{ |
253
|
|
|
$values = array(); |
254
|
|
|
foreach ($this->getRow($rowNum) as $column => $value) { |
255
|
|
|
$values[] = $this->padRight(' ' . $value . ' ', $this->maxLineLength[$column] + 2); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
return sprintf('|%s|', implode('|', $values)); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Converts row into delimited string. |
263
|
|
|
* |
264
|
|
|
* @param integer $rowNum Row number |
265
|
|
|
* @param callable $wrapper Wrapper function |
266
|
|
|
* |
267
|
|
|
* @return string |
268
|
|
|
*/ |
269
|
|
|
public function getRowAsStringWithWrappedValues($rowNum, $wrapper) |
270
|
|
|
{ |
271
|
|
|
$values = array(); |
272
|
|
|
foreach ($this->getRow($rowNum) as $column => $value) { |
273
|
|
|
$value = $this->padRight(' ' . $value . ' ', $this->maxLineLength[$column] + 2); |
274
|
|
|
|
275
|
|
|
$values[] = call_user_func($wrapper, $value, $column); |
276
|
|
|
} |
277
|
|
|
|
278
|
2 |
|
return sprintf('|%s|', implode('|', $values)); |
279
|
|
|
} |
280
|
2 |
|
|
281
|
2 |
|
/** |
282
|
2 |
|
* Converts entire table into string |
283
|
2 |
|
* |
284
|
|
|
* @return string |
285
|
2 |
|
*/ |
286
|
|
|
public function getTableAsString() |
287
|
|
|
{ |
288
|
|
|
$lines = array(); |
289
|
|
|
for ($i = 0; $i < count($this->getRows()); $i++) { |
|
|
|
|
290
|
|
|
$lines[] = $this->getRowAsString($i); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
return implode("\n", $lines); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* Returns line number at which table was started. |
298
|
|
|
* |
299
|
|
|
* @return integer |
300
|
|
|
*/ |
301
|
|
|
public function getLine() |
302
|
|
|
{ |
303
|
|
|
return $this->getRowLine(0); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Converts table into string |
308
|
|
|
* |
309
|
|
|
* @return string |
310
|
|
|
*/ |
311
|
|
|
public function __toString() |
312
|
|
|
{ |
313
|
1 |
|
return $this->getTableAsString(); |
314
|
|
|
} |
315
|
1 |
|
|
316
|
|
|
/** |
317
|
|
|
* Retrieves a hash iterator. |
318
|
|
|
* |
319
|
|
|
* @return Iterator |
320
|
|
|
*/ |
321
|
|
|
public function getIterator() |
322
|
|
|
{ |
323
|
|
|
return new ArrayIterator($this->getHash()); |
324
|
|
|
} |
325
|
|
|
|
326
|
5 |
|
/** |
327
|
|
|
* Pads string right. |
328
|
5 |
|
* |
329
|
5 |
|
* @param string $text Text to pad |
330
|
5 |
|
* @param integer $length Length |
331
|
|
|
* |
332
|
5 |
|
* @return string |
333
|
|
|
*/ |
334
|
|
|
protected function padRight($text, $length) |
335
|
|
|
{ |
336
|
|
|
while ($length > mb_strlen($text, 'utf8')) { |
337
|
|
|
$text = $text . ' '; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
return $text; |
341
|
|
|
} |
342
|
|
|
} |
343
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.