Question

I have a function which convert user input to various services like youtube and opengraph

function link_to_opengraph($content) {
    // patterns author https://github.com/LeonardoCardoso/Facebook-Link-Preview/blob/master/php/classes/Regex.php
    // overall code inspiration https://github.com/LeonardoCardoso/Facebook-Link-Preview/
    $urlRegex = "/(https?\:\/\/|\s)[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})(\/+[a-z0-9_.\:\;-]*)*(\?[\&\%\|\+a-z0-9_=,\.\:\;-]*)?([\&\%\|\+&a-z0-9_=,\:\;\.-]*)([\!\#\/\&\%\|\+a-z0-9_=,\:\;\.-]*)}*/i";
    $imageRegex = "/<img(.*?)src=(\"|\')(.+?)(gif|jpg|png|bmp)(\"|\')(.*?)(\/)?>(<\/img>)?/";
    $imagePrefixRegex = "/\.(jpg|png|gif|bmp)$/i";
    $pattern_youtube = '#\b(?:https?://)?(?:www\.)?(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})[^\s,]*#x';

    preg_match_all( $urlRegex, $content, $allStars );
    if($allStars) {
        require_once(APFPATH . '/lib/OpenGraph.php');
        foreach( $allStars[0] as $key => $star ) {
            if (preg_match($imagePrefixRegex, $star)) {
                // first check if its image
                $markup = '![img]('.$star.'){.class}';
                $content = str_replace( $star, $markup, $content );
            } else if (preg_match($pattern_youtube, $star, $match)) {
                // else if test youtube link
                $graph = OpenGraph::fetch($star);
                $markup = '[![img](http://img.youtube.com/vi/'.$match[1].'/0.jpg)](http://youtu.be/'.$match[1].' "'.$graph->title.'"){#'.$match[1].' .ytmarkdown}';
                $content = str_replace( $star, $markup, $content );
            } else if(2 == 4) {
                // reserved for local links
            } else {
                // else check if this is external link
                $graph = OpenGraph::fetch($star);
                $markup = '[![img]('.$graph->image.')**'.$graph->title.'** '.$graph->description.']('.$star.' "'.$star.'"){.graphmarkdown}';
                $content = str_replace( $star, $markup, $content );
            }
        }
    }
    //return json_encode($allStars);
    return $content;
}

The idea is to convert services to markdown before parsing it with markdown extra.
Now problem is str_replace. For example consider case when user input is:

https://www.youtube.com/watch?v=wxsV37GY1IU&feature=share

https://www.youtube.com/watch?v=wxsV37GY1IU&feature=share

http://youtu.be/wxsV37GY1IU

Since first I replace $content the oput will be:

"[![img](http://img.youtube.com/vi/wxsV37GY1IU/0.jpg)]([![img](http://img.youtube.com/vi/wxsV37GY1IU/0.jpg)](http://youtu.be/wxsV37GY1IU "Project Gooseberry - Why should you contribute to it?"){#wxsV37GY1IU .ytmarkdown} "Project Gooseberry - Why should you contribute to it?"){#wxsV37GY1IU .ytmarkdown}

[![img](http://img.youtube.com/vi/wxsV37GY1IU/0.jpg)]([![img](http://img.youtube.com/vi/wxsV37GY1IU/0.jpg)](http://youtu.be/wxsV37GY1IU "Project Gooseberry - Why should you contribute to it?"){#wxsV37GY1IU .ytmarkdown} "Project Gooseberry - Why should you contribute to it?"){#wxsV37GY1IU .ytmarkdown}

[![img](http://img.youtube.com/vi/wxsV37GY1IU/0.jpg)](http://youtu.be/wxsV37GY1IU "Project Gooseberry - Why should you contribute to it?"){#wxsV37GY1IU .ytmarkdown} "

As you can see it replaced strings I inserted previously.

When it should return string like this:

"[![img](http://img.youtube.com/vi/wxsV37GY1IU/0.jpg)](http://youtu.be/wxsV37GY1IU "Project Gooseberry - Why should you contribute to it?"){#wxsV37GY1IU .ytmarkdown}

[![img](http://img.youtube.com/vi/wxsV37GY1IU/0.jpg)](http://youtu.be/wxsV37GY1IU "Project Gooseberry - Why should you contribute to it?"){#wxsV37GY1IU .ytmarkdown}

[![img](http://img.youtube.com/vi/wxsV37GY1IU/0.jpg)](http://youtu.be/wxsV37GY1IU "Project Gooseberry - Why should you contribute to it?"){#wxsV37GY1IU .ytmarkdown} "

I dont want to disallow entering same link multiple times.
So question is: Is there any way to tell where I want to replace this $star (link) exactly?

Was it helpful?

Solution

Sorry for the delay, but I do have an answer for you that will hopefully be what you are looking for. Basically what I am using is PHP's built in function preg_replace_callback instead of str_replace. The main difference is that it iterates over each match and applies a function to the match, then replaces it in the string. So you can manipulate different parts of the string (like the 11-digit youtube id) and then replace the entire match with whatever you want.

In order to apply this, though, I had to make some structural changes to your code. First, I moved the variables containing the various REGEX definitions outside of the link_to_opengraph function. I will include them as parameters to the function later.

The first part of the script looks like this:

<?php

// DEFINE SOME CONTENT TO TEST ON
$content = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna https://www.youtube.com/watch?v=wxsV37GY1IU&feature=share aliqua. Ut enim ad minim veniam, <img src="http://www.google.com/images/logo.gif"> quis nostrud exercitation ullamco https://www.youtube.com/watch?v=wxsV37GY1IU&feature=share laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui http://youtu.be/wxsV37GY1IU officia deserunt mollit anim id est laborum.';

// DEFINE THE REGEX EXPRESSIONS - WE DON'T APPEAR TO NEED THE urlRegex ANY MORE
$urlRegex = "~(?:https?://|\s)[-a-z0-9]+(?:\.[-a-z0-9]+)*(?:\.[a-z]{2,4})(?:/+[-a-z0-9_.:;]*)*(?:\?[-&%|+a-z0-9_=,.:;]*)?(?:[-&%|+&a-z0-9_=,:;.]*)(?:[-!#/&%|+a-z0-9_=,:;.]*)}*~i";
$imageRegex = '<img(.*?)src=("|\')(.+?)(gif|jpg|png|bmp)("|\')(.*?)(/)?>(</img>)?';
$imagePrefixRegex = "/\.(jpg|png|gif|bmp)$/i";
$pattern_youtube = '\bhttps?://(?:www\.)?(?:youtube\.com/[-A-Z0-9]*?(?:\?|&)v=|youtu\.be/)([-A-Z0-9]{11})[-A-Z0-9&=]*?(?=\s|$)';

Note: I ended up having to rewrite the $pattern_youtube expression just because for whatever reason I couldn't get the one you had to work properly with my code.

Okay, next I defined a callback function and assigned it into a variable named $callback_function. This is the function that will actually handle the if/else blocks, opposed to how it is currently done in the link_to_opengraph function. Notice that the parameter $m is passed automatically to the function and it will contain each match. I also attach the $imageRegex and $pattern_youtube variables to the function with the USE keyword. Now those variables are available in the function scope.

$callback_function = function($m) use ($imageRegex, $pattern_youtube) {

    if (preg_match('~'.$pattern_youtube.'~i', $m[0])) {
        //$graph = OpenGraph::fetch($m[10]);
        $graph = '<FONT COLOR=RED>'.$m[10].'</FONT>'; // MAKE A DUMMY GRAPH VARIABLE
        $replacement = '<BR><BR>[![img](http://img.youtube.com/vi/'.$m[10].'/0.jpg)](http://youtu.be/'.$m[10].' "'.$graph.'"){#'.$m[10].' .ytmarkdown}';
    }
    else {
        $replacement = '<BR><BR><FONT COLOR=BLUE>BUBBA GUMP</FONT>';
    }

    return $replacement;

};

Next, I just print out the $content as it is returned from the link_to_opengraph function. This function now takes additional parameters for the REGEX expressions and the callback function.

print link_to_opengraph($content, $callback_function, $imageRegex, $pattern_youtube);

Finally, we have the link_to_opengraph function. All this function ends up doing is calling preg_replace_callback and returning the results.

function link_to_opengraph($content, $callback_function, $imageRegex, $pattern_youtube) {

    $content = preg_replace_callback('~('.$imageRegex.'|'.$pattern_youtube.')~i', $callback_function, $content);

    return $content;

}

Another thing that is different here is that I evaluate both of your patterns here at once. The callback function will apply the markup on its own.

Here is a working demo of the whole thing

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top