Katie Katie - 1 year ago 60
PHP Question

PHP wordwrap and non-printing strings

I am stuck, stuck, stuck.

I am writing a class that needs a method and I can’t find a solution that completely passes all the unit tests I wrote, there are small gotchas with each solution.

The method is wrapping text (but with a catch) and has three parameters:

  • $text (a string that needs to be word wrapped)

  • $width (the width to wrap)

  • $markup - an array of markup strings that could be randomly placed inside of $text at various points and will be used to stylize the text later (and hence deleted).

For example, if there is a set of markup strings
(they can be anything), and the text
“This is a !!word!!! and this is some <lime>fruit<lime>.”
Wordwrap it to a width of let’s say 25 characters:

This is a word and this
is some fruit.

That would be the proper wrapping, but in order for wordwrap to work, I have to remove the markup and run PHP's wordwrap (which places \n's into the text string appropriately), then return the markup for later processing.

So, here is the problem, I would much rather use php’s wordwrap function than roll my own, as best practice. Here are the solutions that I have tried so far, but failed the unit tests in small ways:

  • Replace the $markup strings with ‘\a’ (escape sequence for beep -
    using a character that I think would not be used), keep track of
    which $markup strings go where in a separate array, run word wrap,
    then put the $markup strings back in the now wordwrapped string

    • Fails because wordwrap counts the \a as one character, thus the more
      markup, the worse wordwrap works

  • Keep an array of the markups and their positions in a separate array and try to rebuild the string

    • Getting better, but the positions I am storing are relative, meaning as I start to restore the markup values, the string is stretching and the position values aren’t valid

I was starting down the road of keeping two strings, the stripped and original, then marching down each string to rebuild the original, but that seems...ugly. Which leads me to believe that I must be missing something.

Any ideas?

Here is the second solution helper functions for stripping and replacing:

* Takes the text and replaces all markup and store the markup and
* their positions in markupInText for later use
* @param $text
* @return string - the text with all the markup switched to /a
public function stripMarkup($text) {

// if there are any markups
if ($this->markupStyles) {

// get all the markups
$markups = array_keys($this->markupStyles);

// go through each markup
foreach ($markups AS $nextMarkup) {

// search through the string
$offset = 0;

// search for the next markup
while (($pos = strpos($text, $nextMarkup, $offset)) !== false) {

// add the position to the array of position for the markup
$this->markupInText[$nextMarkup][] = $pos;

// move the offset as far as the next markup
$offset = $pos + strlen($nextMarkup);

// strip out the markup
foreach ($markups AS $nextMarkup) {

// replace each string with blank
$text = str_replace($nextMarkup,"",$text);

return $text;


* Return the markup that was stripped of markup to its former glory
* @param $text
* @return string - the restored text

public function returnMarkup($text) {

// go through each markup
foreach ($this->markupInText AS $markup => $posArray) {

// go through the range of positions
foreach ($posArray AS $pos) {

// put back the markup
$text = substr($text,0,$pos) . $markup . substr($text,$pos);



// reset the markup in text
$this->markupInText = [];

// return the restored text
return $text;


// part of the suite of test cases that fails:

$original = "This<red> is<red> !s!o!m!e text.";
$answer = $markup->stripMarkup($original);
$this->assertSame("This is some text.",$answer);
$answer = $markup->returnMarkup($answer);
$this->assertSame($original, $answer);

// phpunit failure
Failed asserting that two strings are identical.
Expected :This<red> is<red> !s!o!m!e text.
Actual :This <red>is <red>some text.!!!!

Answer Source

Getting better, but the positions I am storing are relative, meaning as I start to restore the markup values, the string is stretching and the position values aren’t valid

If you store them in their absolute positions in the original string, then you can recursively (i.e. after updating new string each time) add them back in their original positions. The only thing you'd have to account for would be the newly added \n characters by wordwrap. Assuming that the original string didn't contain any newline characters, while inserting, you would also count the number of \ns occurrences up to the positions you're inserting in and recalculate rest of the insertion points by adding that number.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download