tags:

views:

28

answers:

4

I want to replace

{youtube}Video_ID_Here{/youtube}

with the embed code for a youtube video.

So far I have

preg_replace('/{youtube}(.*){\/youtube}/iU',...)

and it works just fine.

But now I'd like to be able to interpret parameters like height, width, etc. So could I have one regex for this whether is does or doesn't have parameters? It should be able to inperpret all of these below...

{youtube height="200px" width="150px" color1="#eee" color2="rgba(0,0,0,0.5)"}Video_ID_Here{/youtube}

{youtube height="200px"}Video_ID_Here{/youtube}

{youtube}Video_ID_Here{/youtube}

{youtube width="150px" showborder="1"}Video_ID_Here{/youtube}

+1  A: 

You probably want to use preg_replace_callback, as the replacing can get quite convoluted otherwise.

preg_replace_callback('/{youtube(.*)}(.*){\/youtube}/iU',...)

And in your callback, check $match[1] for something like the /(width|showborder|height|color1)="([^"]+)"/i pattern. A simple preg_match_all inside a preg_replace_callback keeps all portions nice & tidy and above all legible.

Wrikken
Your solution appears to be the most light weight but I can't seem to get it working... would you mind going more in depth with this. I'm pretty good at php but have little experience with preg..() functions and very little knowledge of regex. Thanks!
Johnny Freeman
+1  A: 

I would do it something like this:

preg_match_all("/{youtube(.*?)}(.*?){\/youtube}/is", $content, $matches);

for($i=0;$i<count($matches[0]);$i++)
{
  $params = $matches[1][$i];
  $youtubeurl = $matches[2][$i];

  $paramsout = array();

  if(preg_match("/height\s*=\s*('|\")([0-9]+px)('|\")/i", $params, $match)
  {
    $paramsout[] = "height=\"{$match[2]}\"";
  }

  //process others

  //setup new code
  $tagcode = "<object ..." . implode(" ", $paramsout) ."... >"; //I don't know what the code is to display a youtube video

  //replace original tag
  $content = str_replace($matches[0][$i], $tagcode, $content);
}

You could just look for params after "{youtube" and before "}" but you open yourself up to XSS problems. The best way would be look for a specific number of parameters and verify them. Don't allow things like < and > to be passed inside your tags as someone could put do_something_nasty(); or something.

Noodles
+1 for not just right answer, but explaining the security threat involved with bad preg_match queries =)
steven_desu
+1  A: 

Try this:

function createEmbed($videoID, $params)
{
    // $videoID contains the videoID between {youtube}...{/youtube}
    // $params is an array of key value pairs such as height => 200px

    return 'HTML...'; // embed code
}

if (preg_match_all('/\{youtube(.*?)\}(.+?)\{\/youtube\}/', $string, $matches)) {
    foreach ($matches[0] as $index => $youtubeTag) {
        $params = array();

        // break out the attributes
        if (preg_match_all('/\s([a-z0-9]+)="([^\s]+?)"/', $matches[1][$index], $rawParams)) {
            for ($x = 0; $x < count($rawParams[0]); $x++) {
                $params[$rawParams[1][$x]] = $rawParams[2][$x];
            }
        }

        // replace {youtube}...{/youtube} with embed code
        $string = str_replace($youtubeTag, createEmbed($matches[2][$index], $params), $string);
    }
}

this code matches the {youtube}...{/youtube} tags first and then splits out the attributes into an array, passing both them (as key/value pairs) and the video ID to a function. Just fill in the function definition to make it validate the params you want to support and build up the appropriate HTML code.

Tim Fountain
A: 

I'd not use regex at all, since they are notoriously bad at parsing markup.

Since your input format is so close to HTML/XML in the first place, I'd rely on that

$tests = array(
    '{youtube height="200px" width="150px" color1="#eee" color2="rgba(0,0,0,0.5)"}Video_ID_Here{/youtube}'
  , '{youtube height="200px"}Video_ID_Here{/youtube}'
  , '{youtube}Video_ID_Here{/youtube}'
  , '{youtube width="150px" showborder="1"}Video_ID_Here{/youtube}'
  , '{YOUTUBE width="150px" showborder="1"}Video_ID_Here{/youtube}' // deliberately invalid
);

echo '<pre>';
foreach ( $tests as $test )
{
  try {
    $youtube = SimpleXMLYoutubeElement::fromUserInput( $test );

    print_r( $youtube );
  }
  catch ( Exception $e )
  {
    echo $e->getMessage() . PHP_EOL;
  }
}
echo '</pre>';

class SimpleXMLYoutubeElement extends SimpleXMLElement
{
  public static function fromUserInput( $code )
  {
    $xml = @simplexml_load_string(
        str_replace( array( '{', '}' ), array( '<', '>' ), strip_tags( $code ) ), __CLASS__
    );
    if ( !$xml || 'youtube' != $xml->getName() )
    {
      throw new Exception( 'Invalid youtube element' );
    }
    return $xml;
  }

  public function toEmbedCode()
  {
    // write code to convert this to proper embode code
  }
}
Peter Bailey