From ea83d53be785543ffa050fdc63181a10c561eac6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 18:56:07 +0000 Subject: [PATCH] Avoid quadratic copying in Table::getDisplayLines() Merging every rendered row into the output with array_merge() copied the whole output array once per row, so rendering a table took quadratic time in the number of rows (about 4 s for 40,000 rows, and ~50 s for a `wp post list` of ~100k posts). Append the lines instead. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01D26yjkN2BiqCXT6p6o1WqS --- lib/cli/Table.php | 7 ++++--- tests/Test_Table.php | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/cli/Table.php b/lib/cli/Table.php index c75f3fa..b3bfed7 100644 --- a/lib/cli/Table.php +++ b/lib/cli/Table.php @@ -235,9 +235,10 @@ public function getDisplayLines() { } foreach ( $this->_rows as $row ) { - $row = $this->_renderer->row( $row ); - $row = explode( PHP_EOL, $row ); - $out = array_merge( $out, $row ); + // Append line by line; array_merge() in this loop copies $out for every row. + foreach ( explode( PHP_EOL, $this->_renderer->row( $row ) ) as $line ) { + $out[] = $line; + } } // Only add final border if there are rows diff --git a/tests/Test_Table.php b/tests/Test_Table.php index 345a756..ab01cbb 100644 --- a/tests/Test_Table.php +++ b/tests/Test_Table.php @@ -50,6 +50,32 @@ public function test_column_value_too_long_ascii() { } } + public function test_display_lines_keep_row_order_with_many_rows() { + $table = new cli\Table(); + $renderer = new cli\Table\Ascii(); + $renderer->setConstraintWidth( 80 ); + $table->setRenderer( $renderer ); + $table->setHeaders( array( 'ID', 'Value' ) ); + + $rows = 5000; + for ( $i = 1; $i <= $rows; $i++ ) { + // Every 100th row contains a line break and renders as two lines. + $table->addRow( array( (string) $i, 0 === $i % 100 ? "first\nsecond" : 'value' ) ); + } + + $out = $table->getDisplayLines(); + + // Border, header, border, one line per row plus one extra line per multi-line row, border. + $this->assertCount( 3 + $rows + $rows / 100 + 1, $out ); + $this->assertSame( '| 1 | value |', $out[3] ); + $this->assertSame( '| 100 | first |', $out[102] ); + $this->assertSame( '| | second |', $out[103] ); + $this->assertSame( '| 101 | value |', $out[104] ); + $this->assertSame( '| 5000 | first |', $out[ count( $out ) - 3 ] ); + $this->assertSame( '| | second |', $out[ count( $out ) - 2 ] ); + $this->assertSame( $out[0], $out[ count( $out ) - 1 ] ); + } + public function test_column_value_too_long_with_multibytes() { $constraint_width = 80;