Mb_str_pad and escape sequences

Hello all;

When padding a string for display in a CLI script, I’ve found that escape sequences seem to confuse the mb_str_pad() function.

Specifically, say I want a list of results for the word time: pretend the results are:

Now is the time for all good men                           Record A
What time is the party?                                    Record B
The bus was actually on time yesterday                     Record C

I’d like to display results in a list with padded columns, highlighting the match with color, something like

Now is the **YLW** time **WHT** for all good men
What **YLW** time **WHT** is the party?
The bus was actually on **YLW** time **WHT** yesterday

where YLW and WHT are the escape sequences for yellow and white foregrounds respectively. The padding works fine when I omit the escape sequences, but the columns are truncated when they are included:

Now is the time for all good men   Record A
What time is the party?  Record B
The bus was actually on time yesterday Record C

To confuse matters, a var_dump() says the strings are the expected length (120). But copy/paste them from the terminal to mousepad or some such and they aren’t; they’re 100.

PHP version 8.3.6, xubuntu 24.04

Any thoughts?

Here are two solutions. See what this does for you. See if this helps you answer your own questions. If you get stuck, I will explain.

<?php
// OPTION ONE
// 1. Pad the plain text to the desired width
$plain = "Now is the time for all good men";
$padded = mb_str_pad($plain, 50);

// 2. Now replace "time" with the colored version (length doesn't change visually)
$highlighted = str_replace('time', "\033[33mtime\033[37m", $padded);
echo $highlighted . "Record A\n";

// OPTION TWO
function mb_str_pad_ansi(string $input, int $pad_length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT): string
{
    // 1. Remove all ANSI escape sequences to get the "visible" text
    $strip_ansi = preg_replace('/\033\[[0-9;]*m/', '', $input);
    
    // 2. Get the visible length (multibyte-safe)
    $visible_len = mb_strlen($strip_ansi);
    
    // 3. Calculate how many padding characters are actually needed
    $needed_padding = $pad_length - $visible_len;
    
    if ($needed_padding <= 0) {
        return $input;
    }
    
    // 4. Apply padding ONLY to the visible length by repeating the pad string
    return $input . str_repeat($pad_string, $needed_padding);
}

// Usage:
$highlighted = "\033[33mtime\033[37m"; // Your yellow/white example
$line = "Now is the " . $highlighted . " for all good men";
echo mb_str_pad_ansi($line, 50) . "Record A\n";

Sorry for the late reply - just saw this in my spam folder having rebuilt a system. I’ll give this a whirl and let you know.

Sponsor our Newsletter | Privacy Policy | Terms of Service