8000 - · symfony/symfony@3c6e292 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3c6e292

Browse files
fancywebnicolas-grekas
authored andcommitted
-
1 parent 274edfc commit 3c6e292

File tree

4 files changed

+83
-129
lines changed

4 files changed

+83
-129
lines changed

src/Symfony/Component/String/AbstractString.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -647,10 +647,8 @@ public function truncate(int $length, string $ellipsis = ''): self
647647
abstract public function upper(): self;
648648

649649
/**
650-
* Returns the string printable length on a terminal.
650+
* Returns the printable length on a terminal.
651651
*/
652-
abstract public function wcswidth(): int;
653-
654652
abstract public function width(bool $ignoreAnsiDecoration = true): int;
655653

656654
/**

src/Symfony/Component/String/AbstractUnicodeString.php

Lines changed: 79 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,6 @@ public function replaceMatches(string $fromRegexp, $to): parent
342342
return $str;
343343
}
344344

345-
/**
346-
* {@inheritdoc}
347-
*/
348345
public function reverse(): parent
349346
{
350347
$str = clone $this;
@@ -425,91 +422,9 @@ public function upper(): parent
425422
return $str;
426423
}
427424

428-
/**
429-
* {@inheritdoc}
430-
*
431-
* If the string contains a non-printable character, -1 is returned.
432-
*
433-
* Based on https://github.com/jquast/wcwidth that is a Python implementation of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c.
434-
*/
435-
public function wcswidth(): int
436-
{
437-
$width = 0;
438-
439-
$length = $this->length();
440-
for ($i = 0; $i <= $length; ++$i) {
441-
foreach ($this->codePointsAt($i) as $codePoint) {
442-
if (
443-
0 === $codePoint || // NULL
444-
0x034F === $codePoint || // COMBINING GRAPHEME JOINER
445-
(0x200B <= $codePoint && 0x200F >= $codePoint) || // ZERO WIDTH SPACE to RIGHT-TO-LEFT MARK
446-
0x2028 === $codePoint || // LINE SEPARATOR
447-
0x2029 === $codePoint || // PARAGRAPH SEPARATOR
448-
(0x202A <= $codePoint && 0x202E >= $codePoint) || // LEFT-TO-RIGHT EMBEDDING to RIGHT-TO-LEFT OVERRIDE
449-
(0x2060 <= $codePoint && 0x2063 >= $codePoint) // WORD JOINER to INVISIBLE SEPARATOR
450-
) {
451-
continue;
452-
}
453-
454-
if (
455-
32 > $codePoint || // C0 control characters
456-
(0x07F <= $codePoint && 0x0A0 > $codePoint) // C1 control characters and DEL
457-
) {
458-
return -1;
459-
}
460-
461-
static $tableZero;
462-
if (null === $tableZero) {
463-
$tableZero = require __DIR__.'/Resources/data/wcswidth_table_zero.php';
464-
}
465-
466-
if ($codePoint >= $tableZero[0][0] && $codePoint <= $tableZero[$ubound = \count($tableZero) - 1][1]) {
467-
$lbound = 0;
468-
while ($ubound >= $lbound) {
469-
$mid = floor(($lbound + $ubound) / 2);
470-
471-
if ($codePoint > $tableZero[$mid][1]) {
472-
$lbound = $mid + 1;
473-
} elseif ($codePoint < $tableZero[$mid][0]) {
474-
$ubound = $mid - 1;
475-
} else {
476-
continue 2;
477-
}
478-
}
479-
}
480-
481-
static $tableWide;
482-
if (null === $tableWide) {
483-
$tableWide = require __DIR__.'/Resources/data/wcswidth_table_wide.php';
484-
}
485-
486-
if ($codePoint >= $tableWide[0][0] && $codePoint <= $tableWide[$ubound = \count($tableWide) - 1][1]) {
487-
$lbound = 0;
488-
while ($ubound >= $lbound) {
489-
$mid = floor(($lbound + $ubound) / 2);
490-
491-
if ($codePoint > $tableWide[$mid][1]) {
492-
$lbound = $mid + 1;
493-
} elseif ($codePoint < $tableWide[$mid][0]) {
494-
$ubound = $mid - 1;
495-
} else {
496-
$width += 2;
497-
498-
continue 2;
499-
}
500-
}
501-
}
502-
503-
++$width;
504-
}
505-
}
506-
507-
return $width;
508-
}
509-
510425
public function width(bool $ignoreAnsiDecoration = true): int
511426
{
512-
$width = 0;
427+
$width = -1;
513428
$s = str_replace(["\x00", "\x05", "\x07"], '', $this->string);
514429

515430
if (false !== strpos($s, "\r")) {
@@ -525,11 +440,7 @@ public function width(bool $ignoreAnsiDecoration = true): int
525440
)/x', '', $s);
526441
}
527442

528-
$w = substr_count($s, "\xAD") - substr_count($s, "\x08");
529-
$s = preg_replace('/[\x00\x05\x07\p{Mn}\p{Me}\p{Cf}\x{1160}-\x{11FF}\x{200B}]+/u', '', $s);
530-
$s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
531-
532-
if ($width < $w += mb_strlen($s, 'UTF-8') + ($wide << 1)) {
443+
if ($width < $w = $this->wcwidth($s)) {
533444
$width = $w;
534445
}
535446
}
@@ -575,4 +486,81 @@ private function pad(int $len, self $pad, int $type): parent
575486
throw new InvalidArgumentException('Invalid padding type.');
576487
}
577488
}
489+
490+
/**
491+
* Based on https://github.com/jquast/wcwidth, a Python implementation of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c.
492+
*
493+
* If the string contains a non-printable character, -1 is returned.
494+
*/
495+
private function wcwidth(string $string): int
496+
{
497+
$width = 0;
498+
499+
foreach (preg_split('/./u', $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $codePoint) {
500+
$codePoint = mb_ord($codePoint, 'UTF-8');
501+
502+
if (0 === $codePoint // NULL
503+
|| 0x034F === $codePoint // COMBINING GRAPHEME JOINER
504+
|| (0x200B <= $codePoint && 0x200F >= $codePoint) // ZERO WIDTH SPACE to RIGHT-TO-LEFT MARK
505+
|| 0x2028 === $codePoint // LINE SEPARATOR
506+
|| 0x2029 === $codePoint // PARAGRAPH SEPARATOR
507+
|| (0x202A <= $codePoint && 0x202E >= $codePoint) // LEFT-TO-RIGHT EMBEDDING to RIGHT-TO-LEFT OVERRIDE
508+
|| (0x2060 <= $codePoint && 0x2063 >= $codePoint) // WORD JOINER to INVISIBLE SEPARATOR
509+
) {
510+
continue;
511+
}
512+
513+
if (32 > $codePoint // C0 control characters
514+
|| (0x07F <= $codePoint && 0x0A0 > $codePoint) // C1 control characters and DEL
515+
) {
516+
return -1;
517+
}
518+
519+
static $tableZero;
520+
if (null === $tableZero) {
521+
$tableZero = require __DIR__.'/Resources/data/wcwidth_table_zero.php';
522+
}
523+
524+
if ($codePoint >= $tableZero[0][0] && $codePoint <= $tableZero[$ubound = \count($tableZero) - 1][1]) {
525+
$lbound = 0;
526+
while ($ubound >= $lbound) {
527+
$mid = floor(($lbound + $ubound) / 2);
528+
529+
if ($codePoint > $tableZero[$mid][1]) {
530+
$lbound = $mid + 1;
531+
} elseif ($codePoint < $tableZero[$mid][0]) {
532+
$ubound = $mid - 1;
533+
} else {
534+
continue 2;
535+
}
536+
}
537+
}
538+
539+
static $tableWide;
540+
if (null === $tableWide) {
541+
$tableWide = require __DIR__.'/Resources/data/wcwidth_table_wide.php';
542+
}
543+
544+
if ($codePoint >= $tableWide[0][0] && $codePoint <= $tableWide[$ubound = \count($tableWide) - 1][1]) {
545+
$lbound = 0;
546+
while ($ubound >= $lbound) {
547+
$mid = floor(($lbound + $ubound) / 2);
548+
549+
if ($codePoint > $tableWide[$mid][1]) {
550+
$lbound = $mid + 1;
551+
} elseif ($codePoint < $tableWide[$mid][0]) {
552+
$ubound = $mid - 1;
553+
} else {
554+
$width += 2;
555+
556+
continue 2;
557+
}
558+
}
559+
}
560+
561+
++$width;
562+
}
563+
564+
return $width;
565+
}
578566
}

src/Symfony/Component/String/ByteString.php

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,6 @@ public function replaceMatches(string $fromRegexp, $to): parent
303303
return $str;
304304
}
305305

306-
/**
307-
* {@inheritdoc}
308-
*/
309306
public function reverse(): parent
310307
{
311308
$str = clone $this;
@@ -458,39 +455,10 @@ public function upper(): parent
458455
return $str;
459456
}
460457

461-
/**
462-
* {@inheritdoc}
463-
*/
464-
public function wcswidth(): int
465-
{
466-
return $this->width();
467-
}
468-
469458
public function width(bool $ignoreAnsiDecoration = true): int
470459
{
471-
$width = 0;
472-
$s = str_replace(["\x00", "\x05", "\x07"], '', $this->string);
473-
474-
if (false !== strpos($s, "\r")) {
475-
$s = str_replace(["\r\n", "\r"], "\n", $s);
476-
}
477-
478-
foreach (explode("\n", $s) as $s) {
479-
if ($ignoreAnsiDecoration) {
480-
$s = preg_replace('/\x1B(?:
481-
\[ [\x30-\x3F]*+ [\x20-\x2F]*+ [0x40-\x7E]
482-
| [P\]X^_] .*? \x1B\\\\
483-
| [\x41-\x7E]
484-
)/x', '', $s);
485-
}
486-
487-
$w = substr_count($s, "\xAD") - substr_count($s, "\x08");
488-
489-
if ($width < $w += \strlen($s)) {
490-
$width = $w;
491-
}
492-
}
460+
$string = preg_match('//u', $this->string) ? $this->string : preg_replace('/[\x80-\xFF\]/', '?', $this->string);
493461

494-
return $width;
462+
return (new CodePointString($string))->width($ignoreAnsiDecoration);
495463
}
496464
}

src/Symfony/Component/String/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ CHANGELOG
55
-----
66

77
* Added the `AbstractString::reverse()` method.
8-
* Added the `AbstractString::wcswidth()` method.
8+
* Made `AbstractString::width()` follow POSIX.1-2001
99

1010
5.0.0
1111
-----

0 commit comments

Comments
 (0)
0