Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a conditional preg_replace in PHP?

I have some code like this, which replaces some shortcodes with a link:

$search = array(
    '#\{r\|([^|]+)\|([^}]+)\}#',
    '#\{t\|([^|]+)\|([^}]+)\}#',
    ...,
);

$replace = array(
    '<a href="/ref/$1">$2</a>',
    '<a href="/type/$1">$2</a>',
    ...,
);

$content = preg_replace( $search, $replace, $content );

I have lots more that are similar so I was wondering, is there some way to reduce this to one simple preg_replace with a conditional?

For example, use the regex #\{([a-z])\|([^|]+)\|([^}]+)\}# and replace the first match with something different (r=ref, t=type) based on its letter? (If it helps, the shortcodes are like {r|url-slug|LinkTitle}.)

like image 565
DisgruntledGoat Avatar asked Mar 07 '12 14:03

DisgruntledGoat


2 Answers

This calls for preg_replace_callback (or possibly just the /e eval modifier), which would allow you to put the mapping t = type and r = ref in the replacement logic:

= preg_replace_callback('#\{([rt])\|([^|]+)\|([^}]+)\}#', "cb_123", ...

function cb_123($m) {

    $map = array("t" => "type",  "r" => "ref");
    $what = $map[  $m[1]  ];

    return "<a href=\"/$what/$m[2]\">$m[3]</a>";
}
like image 98
mario Avatar answered Sep 27 '22 22:09

mario


DISCLAIMER: What follows below is terrible advice, and suggests the use of a PHP feature that is quite rightly now deprecated. I'm only leaving it here for a historical reference.

Use the technique suggested in the accepted answer.


The alternative to the (perfectly valid) preg_replace_callback() method suggested by @mario is the e modifier, which is available only with preg_replace() and allows your replacement string to be evaluated as PHP code:

<?php

   $shortCodes = array (
     'r' => 'ref',
     't' => 'type'
   );

   $expr = '#\{([a-z])\|([^|]+)\|([^}]+)\}#e';
   $replace = '"<a href=\"/{$shortCodes[\'$1\']}/$2\">$3</a>"';
   $string = 'Some text as a ref {r|link1.php|link} and a type {r|link2.php|link}';

   echo preg_replace($expr, $replace, $string);

The only problem I can think of is if your LinkTitle contains a single quote it will be escaped and appear as \' in the output.

See it working

EDIT

After a little trial and error, here is a version that works with anything you can throw at it, and also passes all the data through urlencode()/htmlspecialchars() where appropriate:

<?php

  $shortCodes = array (
    'r' => 'ref',
    't' => 'type'
  );

  $expr = array(
    '#\{([a-z])\|([^|]+)\|([^}]*"[^}]*)\}#e',
    '#\{([a-z])\|([^|]+)\|([^}]+)\}#e'
  );
  $replace = array(
    '"<a href=\"/{$shortCodes[\'$1\']}/".htmlspecialchars(urlencode(\'$2\'))."\">".htmlspecialchars(str_replace(\'\"\', \'"\', \'$3\'))."</a>"',
    '"<a href=\"/{$shortCodes[\'$1\']}/".htmlspecialchars(urlencode(\'$2\'))."\">".htmlspecialchars(\'$3\')."</a>"'
  );
  $string = 'Some text as a ref {r|link &1.php|link&\' with some bad characters in it} and a type {r|link2.php|link with some "quotes" in it}';

  echo preg_replace($expr, $replace, $string);

Outputs:

Some text as a ref <a href="/ref/link+%261.php">link&amp;' with some bad characters in it</a> and a type <a href="/ref/link2.php">link with some &quot;quotes&quot; in it</a>

See it working

like image 22
DaveRandom Avatar answered Sep 27 '22 20:09

DaveRandom