18
18
*
19
19
* @author Fabien Potencier <fabien@symfony.com>
20
20
* @author Саша Стаменковић <umpirsky@gmail.com>
21
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
21
22
*/
22
23
class Table
23
24
{
@@ -139,7 +140,12 @@ public function getStyle()
139
140
140
141
public function setHeaders (array $ headers )
141
142
{
142
- $ this ->headers = array_values ($ headers );
143
+ $ headers = array_values ($ headers );
144
+ if (!empty ($ headers ) && !is_array ($ headers [0 ])) {
145
+ $ headers = array ($ headers );
146
+ }
147
+
148
+ $ this ->headers = $ headers ;
143
149
144
150
return $ this ;
145
151
}
@@ -174,30 +180,6 @@ public function addRow($row)
174
180
175
181
$ this ->rows [] = array_values ($ row );
176
182
177
- end ($ this ->rows );
178
- $ rowKey = key ($ this ->rows );
179
- reset ($ this ->rows );
180
-
181
- foreach ($ row as $ key => $ cellValue ) {
182
- if (!strstr ($ cellValue , "\n" )) {
183
- continue ;
184
- }
185
-
186
- $ lines = explode ("\n" , $ cellValue );
187
- $ this ->rows [$ rowKey ][$ key ] = $ lines [0 ];
188
- unset($ lines [0 ]);
189
-
190
- foreach ($ lines as $ lineKey => $ line ) {
191
- $ nextRowKey = $ rowKey + $ lineKey + 1 ;
192
-
193
- if (isset ($ this ->rows [$ nextRowKey ])) {
194
- $ this ->rows [$ nextRowKey ][$ key ] = $ line ;
195
- } else {
196
- $ this ->rows [$ nextRowKey ] = array ($ key => $ line );
197
- }
198
- }
199
- }
200
-
201
183
return $ this ;
202
184
}
203
185
@@ -222,10 +204,16 @@ public function setRow($column, array $row)
222
204
*/
223
205
public function render ()
224
206
{
207
+ $ this ->calculateNumberOfColumns ();
208
+ $ this ->rows = $ this ->buildTableRows ($ this ->rows );
209
+ $ this ->headers = $ this ->buildTableRows ($ this ->headers );
210
+
225
211
$ this ->renderRowSeparator ();
226
- $ this ->renderRow ($ this ->headers , $ this ->style ->getCellHeaderFormat ());
227
212
if (!empty ($ this ->headers )) {
228
- $ this ->renderRowSeparator ();
213
+ foreach ($ this ->headers as $ header ) {
214
+ $ this ->renderRow ($ header , $ this ->style ->getCellHeaderFormat ());
215
+ $ this ->renderRowSeparator ();
216
+ }
229
217
}
230
218
foreach ($ this ->rows as $ row ) {
231
219
if ($ row instanceof TableSeparator) {
@@ -248,7 +236,7 @@ public function render()
248
236
*/
249
237
private function renderRowSeparator ()
250
238
{
251
- if (0 === $ count = $ this ->getNumberOfColumns () ) {
239
+ if (0 === $ count = $ this ->numberOfColumns ) {
252
240
return ;
253
241
}
254
242
@@ -287,7 +275,7 @@ private function renderRow(array $row, $cellFormat)
287
275
}
288
276
289
277
$ this ->renderColumnSeparator ();
290
- for ($ column = 0 , $ count = $ this ->getNumberOfColumns (); $ column < $ count ; $ column++ ) {
278
+ foreach ($ this ->getRowColumns ( $ row ) as $ column ) {
291
279
$ this ->renderCell ($ row , $ column , $ cellFormat );
292
280
$ this ->renderColumnSeparator ();
293
281
}
@@ -305,36 +293,213 @@ private function renderCell(array $row, $column, $cellFormat)
305
293
{
306
294
$ cell = isset ($ row [$ column ]) ? $ row [$ column ] : '' ;
307
295
$ width = $ this ->getColumnWidth ($ column );
296
+ if ($ cell instanceof TableCell && $ cell ->getColspan () > 1 ) {
297
+ // add the width of the following columns(numbers of colspan).
298
+ foreach (range ($ column + 1 , $ column + $ cell ->getColspan () - 1 ) as $ nextColumn ) {
299
+ $ width += $ this ->getColumnSeparatorWidth () + $ this ->getColumnWidth ($ nextColumn );
300
+ }
301
+ }
308
302
309
303
// str_pad won't work properly with multi-byte strings, we need to fix the padding
310
304
if (function_exists ('mb_strwidth ' ) && false !== $ encoding = mb_detect_encoding ($ cell )) {
311
305
$ width += strlen ($ cell ) - mb_strwidth ($ cell , $ encoding );
312
306
}
313
307
314
- $ width += Helper::strlen ($ cell ) - Helper::strlenWithoutDecoration ($ this ->output ->getFormatter (), $ cell );
308
+ if ($ cell instanceof TableSeparator) {
309
+ $ this ->output ->write (sprintf ($ this ->style ->getBorderFormat (), str_repeat ($ this ->style ->getHorizontalBorderChar (), $ width )));
310
+ } else {
311
+ $ width += Helper::strlen ($ cell ) - Helper::strlenWithoutDecoration ($ this ->output ->getFormatter (), $ cell );
312
+ $ content = sprintf ($ this ->style ->getCellRowContentFormat (), $ cell );
313
+ $ this ->output ->write (sprintf ($ cellFormat , str_pad ($ content , $ width , $ this ->style ->getPaddingChar (), $ this ->style ->getPadType ())));
314
+ }
315
+ }
315
316
316
- $ content = sprintf ($ this ->style ->getCellRowContentFormat (), $ cell );
317
+ /**
318
+ * Calculate number of columns for this table.
319
+ */
320
+ private function calculateNumberOfColumns ()
321
+ {
322
+ if (null !== $ this ->numberOfColumns ) {
323
+ return ;
324
+ }
317
325
318
- $ this ->output ->write (sprintf ($ cellFormat , str_pad ($ content , $ width , $ this ->style ->getPaddingChar (), $ this ->style ->getPadType ())));
326
+ $ columns = array (0 );
327
+ foreach (array_merge ($ this ->headers , $ this ->rows ) as $ row ) {
328
+ if ($ row instanceof TableSeparator) {
329
+ continue ;
330
+ }
331
+
332
+ $ columns [] = $ this ->getNumberOfColumns ($ row );
333
+ }
334
+
335
+ return $ this ->numberOfColumns = max ($ columns );
336
+ }
337
+
338
+ private function buildTableRows ($ rows )
339
+ {
340
+ $ unmergedRows = array ();
341
+ for ($ rowKey = 0 ; $ rowKey < count ($ rows ); $ rowKey ++) {
342
+ $ rows = $ this ->fillNextRows ($ rows , $ rowKey );
343
+
344
+ // Remove any new line breaks and replace it with a new line
345
+ foreach ($ rows [$ rowKey ] as $ column => $ cell ) {
346
+ $ rows [$ rowKey ] = $ this ->fillCells ($ rows [$ rowKey ], $ column );
347
+ if (!strstr ($ cell , "\n" )) {
348
+ continue ;
349
+ }
350
+ $ lines = explode ("\n" , $ cell );
351
+ foreach ($ lines as $ lineKey => $ line ) {
352
+ if ($ cell instanceof TableCell) {
353
+ $ line = new TableCell ($ line , array ('colspan ' => $ cell ->getColspan ()));
354
+ }
355
+ if (0 === $ lineKey ) {
356
+ $ rows [$ rowKey ][$ column ] = $ line ;
357
+ } else {
358
+ $ unmergedRows [$ rowKey ][$ lineKey ][$ column ] = $ line ;
359
+ }
360
+ }
361
+ }
362
+ }
363
+
364
+ $ tableRows = array ();
365
+ foreach ($ rows as $ rowKey => $ row ) {
366
+ $ tableRows [] = $ row ;
367
+ if (isset ($ unmergedRows [$ rowKey ])) {
368
+ $ tableRows = array_merge ($ tableRows , $ unmergedRows [$ rowKey ]);
369
+ }
370
+ }
371
+
372
+ return $ tableRows ;
319
373
}
320
374
321
375
/**
322
- * Gets number of columns for this table.
376
+ * fill rows that contains rowspan > 1.
377
+ *
378
+ * @param array $rows
379
+ * @param array $line
380
+ *
381
+ * @return array
382
+ */
383
+ private function fillNextRows ($ rows , $ line )
384
+ {
385
+ $ unmergedRows = array ();
386
+ foreach ($ rows [$ line ] as $ column => $ cell ) {
387
+ if ($ cell instanceof TableCell && $ cell ->getRowspan () > 1 ) {
388
+ $ nbLines = $ cell ->getRowspan ()-1 ;
389
+ $ lines = array ($ cell );
390
+ if (strstr ($ cell , "\n" )) {
391
+ $ lines = explode ("\n" , $ cell );
392
+ $ nbLines = count ($ lines ) > $ nbLines ? substr_count ($ cell , "\n" ) : $ nbLines ;
393
+
394
+ $ rows [$ line ][$ column ] = new TableCell ($ lines [0 ], array ('colspan ' => $ cell ->getColspan ()));
395
+ unset($ lines [0 ]);
396
+ }
397
+
398
+ // create a two dimensional array (rowspan x colspan)
399
+ $ unmergedRows = array_replace_recursive (array_fill ($ line + 1 , $ nbLines , '' ), $ unmergedRows );
400
+ foreach ($ unmergedRows as $ unmergedRowKey => $ unmergedRow ) {
401
+ $ value = isset ($ lines [$ unmergedRowKey - $ line ]) ? $ lines [$ unmergedRowKey - $ line ] : '' ;
402
+ $ unmergedRows [$ unmergedRowKey ][$ column ] = new TableCell ($ value , array ('colspan ' => $ cell ->getColspan ()));
403
+ }
404
+ }
405
+ }
406
+
407
+ foreach ($ unmergedRows as $ unmergedRowKey => $ unmergedRow ) {
408
+ // we need to know if $unmergedRow will be merged or inserted into $rows
409
+ if (isset ($ rows [$ unmergedRowKey ]) && is_array ($ rows [$ unmergedRowKey ]) && ($ this ->getNumberOfColumns ($ rows [$ unmergedRowKey ]) + $ this ->getNumberOfColumns ($ unmergedRows [$ unmergedRowKey ]) <= $ this ->numberOfColumns )) {
410
+ foreach ($ unmergedRow as $ cellKey => $ cell ) {
411
+ // insert cell into row at cellKey position
412
+ array_splice ($ rows [$ unmergedRowKey ], $ cellKey , 0 , array ($ cell ));
413
+ }
414
+ } else {
415
+ $ row = $ this ->copyRow ($ rows , $ unmergedRowKey -1 );
416
+ foreach ($ unmergedRow as $ column => $ cell ) {
417
+ if (!empty ($ cell )) {
418
+ $ row [$ column ] = $ unmergedRow [$ column ];
419
+ }
420
+ }
421
+ array_splice ($ rows , $ unmergedRowKey , 0 , array ($ row ));
422
+ }
423
+ }
424
+
425
+ return $ rows ;
426
+ }
427
+
428
+ /**
429
+ * fill cells for a row that contains colspan > 1.
430
+ *
431
+ * @param array $row
432
+ * @param array $column
433
+ *
434
+ * @return array
435
+ */
436
+ private function fillCells ($ row , $ column )
437
+ {
438
+ $ cell = $ row [$ column ];
439
+ if ($ cell instanceof TableCell && $ cell ->getColspan () > 1 ) {
440
+ foreach (range ($ column + 1 , $ column + $ cell ->getColspan () - 1 ) as $ position ) {
441
+ // insert empty value into rows at column position
442
+ array_splice ($ row , $ position , 0 , '' );
443
+ }
444
+ }
445
+
446
+ return $ row ;
447
+ }
448
+
449
+ /**
450
+ * @param array $rows
451
+ * @param int $line
452
+ *
453
+ * @return array
454
+ */
455
+ private function copyRow ($ rows , $ line )
456
+ {
457
+ $ row = $ rows [$ line ];
458
+ foreach ($ row as $ cellKey => $ cellValue ) {
459
+ $ row [$ cellKey ] = '' ;
460
+ if ($ cellValue instanceof TableCell) {
461
+ $ row [$ cellKey ] = new TableCell ('' , array ('colspan ' => $ cellValue ->getColspan ()));
462
+ }
463
+ }
464
+
465
+ return $ row ;
466
+ }
467
+
468
+ /**
469
+ * Gets number of columns by row.
470
+ *
471
+ * @param array $row
323
472
*
324
473
* @return int
325
474
*/
326
- private function getNumberOfColumns ()
475
+ private function getNumberOfColumns (array $ row )
327
476
{
328
- if (null !== $ this ->numberOfColumns ) {
329
- return $ this ->numberOfColumns ;
477
+ $ columns = count ($ row );
478
+ foreach ($ row as $ column ) {
479
+ $ columns += $ column instanceof TableCell ? ($ column ->getColspan ()-1 ) : 0 ;
330
480
}
331
481
332
- $ columns = array (count ($ this ->headers ));
333
- foreach ($ this ->rows as $ row ) {
334
- $ columns [] = count ($ row );
482
+ return $ columns ;
483
+ }
484
+
485
+ /**
486
+ * Gets list of columns for the given row.
487
+ *
488
+ * @param array $row
489
+ *
490
+ * @return array()
491
+ */
492
+ private function getRowColumns ($ row )
493
+ {
494
+ $ columns = range (0 , $ this ->numberOfColumns -1 );
495
+ foreach ($ row as $ cellKey => $ cell ) {
496
+ if ($ cell instanceof TableCell && $ cell ->getColspan () > 1 ) {
497
+ // exclude grouped columns.
498
+ $ columns = array_diff ($ columns , range ($ cellKey +1 , $ cellKey + $ cell ->getColspan ()-1 ));
499
+ }
335
500
}
336
501
337
- return $ this -> numberOfColumns = max ( $ columns) ;
502
+ return $ columns ;
338
503
}
339
504
340
505
/**
@@ -350,8 +515,7 @@ private function getColumnWidth($column)
350
515
return $ this ->columnWidths [$ column ];
351
516
}
352
517
353
- $ lengths = array ($ this ->getCellWidth ($ this ->headers , $ column ));
354
- foreach ($ this ->rows as $ row ) {
518
+ foreach (array_merge ($ this ->headers , $ this ->rows ) as $ row ) {
355
519
if ($ row instanceof TableSeparator) {
356
520
continue ;
357
521
}
@@ -362,6 +526,18 @@ private function getColumnWidth($column)
362
526
return $ this ->columnWidths [$ column ] = max ($ lengths ) + strlen ($ this ->style ->getCellRowContentFormat ()) - 2 ;
363
527
}
364
528
529
+ /**
530
+ * Gets column width.
531
+ *
532
+ * @param int $column
533
+ *
534
+ * @return int
535
+ */
536
+ private function getColumnSeparatorWidth ()
537
+ {
538
+ return strlen (sprintf ($ this ->style ->getBorderFormat (), $ this ->style ->getVerticalBorderChar ()));
539
+ }
540
+
365
541
/**
366
542
* Gets cell width.
367
543
*
@@ -372,7 +548,17 @@ private function getColumnWidth($column)
372
548
*/
373
549
private function getCellWidth (array $ row , $ column )
374
550
{
375
- return isset ($ row [$ column ]) ? Helper::strlenWithoutDecoration ($ this ->output ->getFormatter (), $ row [$ column ]) : 0 ;
551
+ if (isset ($ row [$ column ])) {
552
+ $ cell = $ row [$ column ];
553
+ if ($ cell instanceof TableCell && $ cell ->getColspan () > 1 ) {
554
+ // we assume that cell value will be across more than one column.
555
+ $ cell = substr ($ cell , 0 , strlen ($ cell )/$ cell ->getColspan ());
556
+ }
557
+
558
+ return Helper::strlenWithoutDecoration ($ this ->output ->getFormatter (), $ cell );
559
+ }
560
+
561
+ return 0 ;
376
562
}
377
563
378
564
/**
0 commit comments