Skip to content

Commit f96cc05

Browse files
authored
Merge pull request #117 from dimtrovich/table-with-unicode-character
Table with unicode character
2 parents 2ca5a50 + fe273ac commit f96cc05

File tree

4 files changed

+75
-10
lines changed

4 files changed

+75
-10
lines changed

src/Helper/InflectsString.php

+28
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
namespace Ahc\Cli\Helper;
1313

1414
use function lcfirst;
15+
use function mb_strwidth;
16+
use function mb_substr;
1517
use function str_replace;
18+
use function strlen;
19+
use function substr;
1620
use function trim;
1721
use function ucwords;
1822

@@ -47,4 +51,28 @@ public function toWords(string $string): string
4751

4852
return ucwords($words);
4953
}
54+
55+
/**
56+
* Return width of string
57+
*/
58+
public function strwidth(string $string): int
59+
{
60+
if (function_exists('mb_strwidth')) {
61+
return mb_strwidth($string);
62+
}
63+
64+
return strlen($string);
65+
}
66+
67+
/**
68+
* Get part of string
69+
*/
70+
public function substr(string $string, int $start, ?int $length = null): string
71+
{
72+
if (function_exists('mb_substr')) {
73+
return mb_substr($string, $start, $length);
74+
}
75+
76+
return substr($string, $start, $length);
77+
}
5078
}

src/Output/Table.php

+17-8
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
use function max;
2626
use function reset;
2727
use function sprintf;
28-
use function str_pad;
2928
use function str_repeat;
30-
use function strlen;
3129
use function trim;
3230

3331
use const PHP_EOL;
@@ -51,7 +49,7 @@ public function render(array $rows, array $styles = []): string
5149
$pos = 0;
5250
foreach ($head as $col => $size) {
5351
$dash[] = str_repeat('-', $size + 2);
54-
$title[] = str_pad($this->toWords($col), $size, ' ');
52+
$title[] = $this->strPad($this->toWords($col), $size, ' ');
5553
$positions[$col] = ++$pos;
5654
}
5755

@@ -62,7 +60,6 @@ public function render(array $rows, array $styles = []): string
6260
$parts = [];
6361
$line++;
6462

65-
[$start, $end] = $styles[['even', 'odd'][(int) $odd]];
6663
foreach ($head as $col => $size) {
6764
$colNumber = $positions[$col];
6865

@@ -85,10 +82,10 @@ public function render(array $rows, array $styles = []): string
8582
$word = str_replace($matches[1], '', $text);
8683
$word = preg_replace('/\\x1b\[0m/', '', $word);
8784

88-
$size += strlen($text) - strlen($word);
85+
$size += $this->strwidth($text) - $this->strwidth($word);
8986
}
9087

91-
$parts[] = "$start " . str_pad($text, $size, ' ') . " $end";
88+
$parts[] = "$start " . $this->strPad($text, $size, ' ') . " $end";
9289
}
9390

9491
$odd = !$odd;
@@ -132,8 +129,8 @@ protected function normalize(array $rows): array
132129
return $col;
133130
}, $cols);
134131

135-
$span = array_map('strlen', $cols);
136-
$span[] = strlen($col);
132+
$span = array_map([$this, 'strwidth'], $cols);
133+
$span[] = $this->strwidth($col);
137134
$value = max($span);
138135
}
139136

@@ -177,4 +174,16 @@ protected function parseStyle(array|callable $style, $val, array $row, array $ta
177174

178175
return ['', ''];
179176
}
177+
178+
/**
179+
* Pad a multibyte string to a certain length with another multibyte string
180+
*/
181+
protected function strPad(string $string, int $length, string $pad_string = ' '): string
182+
{
183+
if (1 > $paddingRequired = $length - $this->strwidth($string)) {
184+
return $string;
185+
}
186+
187+
return $string . $this->substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired);
188+
}
180189
}

src/Output/Writer.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Ahc\Cli\Output;
1313

14+
use Ahc\Cli\Helper\InflectsString;
1415
use Ahc\Cli\Helper\Terminal;
1516

1617
use function fopen;
@@ -19,7 +20,6 @@
1920
use function method_exists;
2021
use function str_repeat;
2122
use function stripos;
22-
use function strlen;
2323
use function strpos;
2424
use function ucfirst;
2525

@@ -190,6 +190,8 @@
190190
*/
191191
class Writer
192192
{
193+
use InflectsString;
194+
193195
/** @var resource Output file handle */
194196
protected $stream;
195197

@@ -339,7 +341,7 @@ public function justify(string $first, ?string $second = null, array $options =
339341

340342
$second = (string) $second;
341343
$terminalWidth = $this->terminal->width() ?? 80;
342-
$dashWidth = $terminalWidth - (strlen($first) + strlen($second));
344+
$dashWidth = $terminalWidth - ($this->strwidth($first) + $this->strwidth($second));
343345
// remove left and right margins because we're going to add 1 space on each side (after/before the text).
344346
// if we don't have a second element, we just remove the left margin
345347
$dashWidth -= $second === '' ? 1 : 2;

tests/Output/TableTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -639,4 +639,30 @@ public function test_render_with_html_like_tags_in_cell_content(): void
639639

640640
$this->assertSame($expectedOutput, trim($result));
641641
}
642+
643+
public function test_render_with_unicode_characters_in_cell_content(): void
644+
{
645+
if (! extension_loaded('mbstring')) {
646+
$this->markTestSkipped('The mbstring extension is not installed. This test will faill without it');
647+
}
648+
649+
$rows = [
650+
['name' => 'François', 'greeting' => 'Bonjour'],
651+
['name' => 'Jürgen', 'greeting' => 'Guten Tag'],
652+
['name' => '北京', 'greeting' => '你好']
653+
];
654+
655+
$expectedOutput =
656+
"+----------+-----------+" . PHP_EOL .
657+
"| Name | Greeting |" . PHP_EOL .
658+
"+----------+-----------+" . PHP_EOL .
659+
"| François | Bonjour |" . PHP_EOL .
660+
"| Jürgen | Guten Tag |" . PHP_EOL .
661+
"| 北京 | 你好 |" . PHP_EOL .
662+
"+----------+-----------+";
663+
664+
$result = $this->table->render($rows);
665+
666+
$this->assertSame($expectedOutput, trim($result));
667+
}
642668
}

0 commit comments

Comments
 (0)