Search code examples
phphashtaglowercasepreg-replace-callback

STRTOLOWER in PREG_REPLACE_CALLBACK not working with #hashtag link


For some reason, I can't, for the life of me, get strtolower to work properly with an anchor tag that is linking to a #Hashtag...even using preg_replace_callback().

public static function convertHashtags($str) {
    $str = preg_replace_callback(
                    '/(\#([a-z0-9_]+))/ix', 
                    function( $matches ) {
                        $uri = strtolower($matches[2]);
                        // return $uri;
                        return '<a href="'. SITE_URL .'/hashtag/'. $uri .'">'. $matches[1] .'</a>';
                    }, $str, -1);
    return $str;
}

All this needs to do is grab the #hashtag and turn it into a link. The URL needs to be lowercased while the #HashTag retains it's original formatting.

Example:

#Palladia turns into:
<a href="/hashtag/palladia">#Palladia</a>

However, I am noticing something wonky...if I put a # in the return, right before $matches[1] it works fine, but obviously displays 2 #'s. So I thought, ok then, I'll just use $matches[2] with a # in front of it. Nope, doesn't work. For whatever reason it needs that extra # in front of the #Palladia...this results in a not so ideal result:

<a href="/hashtag/palladia">##Palladia</a>

Oddly enough, if I simply return strotolower($matches[2]), it does lowercase the string...it just doesn't want to work inside of the anchor tag. Does anyone have any idea how to make it so I do not need that extra # there?


Solution

  • I think the confusion is coming from what is in $matches -- you have two sets of brackets, but you really only need one to capture the text after the hashtag.

    I've simplified the code slightly:

    public static function convertHashtags($str) {
    
        return preg_replace_callback(
            '/#([\w]+)/',  // all "word" characters, all digits, and underscore
                           // brackets around the string AFTER the hashtag
            function( $matches ) {
                // $matches[0] is the complete match (including the hashtag)
                // $matches[1] is the match for the subpattern enclosed in brackets
                return '<a href="'. SITE_URL .'/hashtag/'
                    . strtolower($matches[1]) .'">'
                    . $matches[0] .'</a>';
            }, $str);
    }
    
    convertHashtags('#Palladium')
    // output: <a href="SITE_URL/hashtag/palladium">#Palladium</a>
    

    Also works with text containing multiple hashtags:

    convertHashtags("I love #StackOverflow and #Hashtags. They're awesome! #Awesomesauce");
    // output:  I love <a href="SITE_URL/hashtag/stackoverflow">#StackOverflow</a> and
    //          <a href="SITE_URL/hashtag/hashtags">#Hashtags</a>. They're awesome! <a
    //          href="SITE_URL/hashtag/awesomesauce">#Awesomesauce</a>