@@ -342,9 +342,6 @@ public function replaceMatches(string $fromRegexp, $to): parent
342
342
return $ str ;
343
343
}
344
344
345
- /**
346
- * {@inheritdoc}
347
- */
348
345
public function reverse (): parent
349
346
{
350
347
$ str = clone $ this ;
@@ -425,91 +422,9 @@ public function upper(): parent
425
422
return $ str ;
426
423
}
427
424
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
-
510
425
public function width (bool $ ignoreAnsiDecoration = true ): int
511
426
{
512
- $ width = 0 ;
427
+ $ width = - 1 ;
513
428
$ s = str_replace (["\x00" , "\x05" , "\x07" ], '' , $ this ->string );
514
429
515
430
if (false !== strpos ($ s , "\r" )) {
@@ -525,11 +440,7 @@ public function width(bool $ignoreAnsiDecoration = true): int
525
440
)/x ' , '' , $ s );
526
441
}
527
442
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 )) {
533
444
$ width = $ w ;
534
445
}
535
446
}
@@ -575,4 +486,81 @@ private function pad(int $len, self $pad, int $type): parent
575
486
throw new InvalidArgumentException ('Invalid padding type. ' );
576
487
}
577
488
}
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
+ }
578
566
}
0 commit comments