Browse Source

Fix width calculation on formatted cells.

Calculate widths of cell contents by stripping their formatting tags
first.

Fixes #13079
Mark Story 7 years ago
parent
commit
9fc0363079

+ 1 - 1
src/Console/ConsoleIo.php

@@ -398,7 +398,7 @@ class ConsoleIo
      */
     public function styles($style = null, $definition = null)
     {
-        $this->_out->styles($style, $definition);
+        return $this->_out->styles($style, $definition);
     }
 
     /**

+ 20 - 2
src/Shell/Helper/TableHelper.php

@@ -44,7 +44,7 @@ class TableHelper extends Helper
         $widths = [];
         foreach ($rows as $line) {
             foreach (array_values($line) as $k => $v) {
-                $columnLength = mb_strwidth($v);
+                $columnLength = $this->_cellWidth($v);
                 if ($columnLength >= (isset($widths[$k]) ? $widths[$k] : 0)) {
                     $widths[$k] = $columnLength;
                 }
@@ -55,6 +55,24 @@ class TableHelper extends Helper
     }
 
     /**
+     * Get the width of a cell exclusive of style tags.
+     *
+     * @param string $text The text to calculate a width for.
+     * @return int The width of the textual content in visible characters.
+     */
+    protected function _cellWidth($text)
+    {
+        if (strpos($text, '<') === false && strpos($text, '>') === false) {
+            return mb_strwidth($text);
+        }
+        $styles = array_keys($this->_io->styles());
+        $tags = implode('|', $styles);
+        $text = preg_replace('#</?(?:' . $tags . ')>#', '', $text);
+
+        return mb_strwidth($text);
+    }
+
+    /**
      * Output a row separator.
      *
      * @param array $widths The widths of each column to output.
@@ -86,7 +104,7 @@ class TableHelper extends Helper
 
         $out = '';
         foreach (array_values($row) as $i => $column) {
-            $pad = $widths[$i] - mb_strwidth($column);
+            $pad = $widths[$i] - $this->_cellWidth($column);
             if (!empty($options['style'])) {
                 $column = $this->_addStyle($column, $options['style']);
             }

+ 25 - 2
tests/TestCase/Shell/Helper/TableHelperTest.php

@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * CakePHP :  Rapid Development Framework (https://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
@@ -271,6 +272,28 @@ class TableHelperTest extends TestCase
     }
 
     /**
+     * Test output with formatted cells
+     *
+     * @return void
+     */
+    public function testOutputWithFormattedCells()
+    {
+        $data = [
+            ['short', 'Longish thing', '<info>short</info>'],
+            ['Longer thing', 'short', '<warning>Longest</warning> <error>Value</error>'],
+        ];
+        $this->helper->setConfig(['headers' => false]);
+        $this->helper->output($data);
+        $expected = [
+            '+--------------+---------------+---------------+',
+            '| short        | Longish thing | <info>short</info>         |',
+            '| Longer thing | short         | <warning>Longest</warning> <error>Value</error> |',
+            '+--------------+---------------+---------------+',
+        ];
+        $this->assertEquals($expected, $this->stub->messages());
+    }
+
+    /**
      * Test output with row separator
      *
      * @return void
@@ -280,7 +303,7 @@ class TableHelperTest extends TestCase
         $data = [
             ['Header 1', 'Header', 'Long Header'],
             ['short', 'Longish thing', 'short'],
-            ['Longer thing', 'short', 'Longest Value']
+            ['Longer thing', 'short', 'Longest Value'],
         ];
         $this->helper->setConfig(['rowSeparator' => true]);
         $this->helper->output($data);
@@ -337,7 +360,7 @@ class TableHelperTest extends TestCase
     public function testOutputWithHeaderAndNoData()
     {
         $data = [
-            ['Header 1', 'Header', 'Long Header']
+            ['Header 1', 'Header', 'Long Header'],
         ];
         $this->helper->output($data);
         $expected = [