Home > Back-end >  PHP: Regex matching and replacing multiple instances of multiple matches being duplicated
PHP: Regex matching and replacing multiple instances of multiple matches being duplicated

Time:01-11

I'm looking to write a shortcode system for a gaming community/database, where users can add things like ((Magical Sword)) to their content, and it'll be parsed into a nice link to relevant item with an inline thumbnail image.

Here's the code I'm using so far:

function inlineItems($text) {
    $re = "/\(\(([^)] )\)\)/m";
    preg_match_all($re, $text, $matches, PREG_SET_ORDER, 0);
    foreach($matches as $match) {
        $slug = makeSlug($match[1]);
        $item = getItem($slug);
        if($item) {
            $text = preg_replace($match[0], '<a  data-tooltip="tooltip-item-' . $item->slug . '" href="/items/' . $item->slug .'"><img src="/images/items/' . $item->slug .'.png">' . $item->name .'</a>', $text);
        }
    }
    $text = str_replace("((", "", $text);
    $text = str_replace("))", "", $text);
    return $text;
}

Example output, if a user entered ((Crystal Sword)) would be:

<a  data-tooltip="tooltip-item-crystal-sword" href="/items/crystal-sword"><img src="/images/items/crystal-sword.png">Crystal Sword</a>

So far so good, everything works great.

However, an issue occurs when a particular match is repeated multiple times in one text string.

If a user enters something like: A ((Crystal Sword)) is essential for farming, get a ((Crystal Sword)) as soon as you can. ((Crystal Sword)) is the best! then the replacement matches the item name multiple times, and ends up with a mess like this:

<a  data-tooltip="tooltip-item-crystal-sword" href="/items/crystal-sword"><img src="/images/items/crystal-sword.png"></a><a  data-tooltip="tooltip-item-crystal-sword" href="/items/crystal-sword"><img src="/images/items/crystal-sword.png"></a><a  data-tooltip="tooltip-item-crystal-sword" href="/items/crystal-sword"><img src="/images/items/crystal-sword.png">Crystal Sword</a>

How do I prevent it from overlapping matches like this?

CodePudding user response:

Your code is pretty messy. You don't need all those replaces all around, one is enough. Follow the KISS principle:

<?php

function inlineItems($text) {

    $re = "/\(\((. ?)\)\)/m";

    return preg_replace_callback($re, function($matches){

        $item = getItem( makeSlug($matches[1]) );

        return "<a class='text-item' data-tooltip='tooltip-item-{$item->slug}' href='/items/{$item->slug}'>
            <img src='images/items/{$item->slug}.png'>
            {$item->name}
        </a>";

    }, $text);

}


print inlineItems('A ((Crystal Sword)) is essential for farming, get a ((Crystal Sword)) as soon as you can. ((Crystal Sword)) is the best!');
  • Related