How to call a function in an preg_replace? - php

How can I call a function within preg_replace()?
$template = '<div> [BILD="123"][BILD="246"] </div>';
$pat = '/\[BILD="(.*?)"\]\[BILD ="(.*?)"\]/';
$ret = getImageArea($id, $config, '$1', '$2');
return preg_replace($pat, $rep, $template);
It fails opening the function and doesn't sends 123 or 246.
It just sends $1 and $2.

As I (and many others) told in comments, you have to use preg_replace_callback in this case. One possible approach:
$template = '<div> [BILD="123"][BILD="246"] </div>';
$pat = '/\[BILD="(.*?)"\]\[BILD="(.*?)"\]/';
return preg_replace_callback($pat, function($matches) use($id, $config) {
return getImageArea($id, $config, $matches[1], $matches[2]);
}, $template);
Demo. As you see, it's quite straight-forward; the only catch is making $id and $config variables available inside the callback (which is done with use op).

Related

How php read local variable in temp function?

I noticed that PHP now can use temp function definition (like js and other scripts language), but how can I get local variable in the temp function ?
ex:
function my_func($text, $prefix){
$regex = '/xxxx/';
return preg_replace_callback($regex, function($matches){
// how to read $prefix here?
}, $text);
}
The use will do the job..
function my_func($text, $prefix){
$regex = '/xxxx/';
return preg_replace_callback($regex, function($matches) use(&$prefix){
//^^^^^^^^^^^^^
}, $text);
}
I find it ..
http://www.php.net/manual/en/functions.anonymous.php
function my_func($text, $prefix){
$regex = '/xxxx/';
return preg_replace_callback($regex, function($matches) use ($prefix){
// how to read $prefix here?
}, $text);
}

Improving speed of regex and using preg_replace for slugs

Previously I have been echoing $obj->html but a current project requires that the HTML be examined for slugs like {whatever} and replacing these with other content.
I have two problems. The first is that this code is slower than I would like:
class Foo {
function draw_content() {
$slug = "/(?<=\{).*(?=\})/";
if (preg_match($slug, $this->html, $matches)) {
foreach ($matches as $match) {
if (method_exists($this,$match)) {
$replacement = $this->$match();
$this->html = preg_replace("/\{$match\}/", $replacement, $this->html);
}
}
}
return $this->html;
} // fn
function new_releases() {
echo "new release book covers";
} // fn
} // class
Is there a better way to get the slug contents? I presume the regex is what is slowing this down?
The second issue is stranger to me. Given this $obj->html:
<p class="headline">New Releases</p>
<p>xxx{new_releases}xxxx</p>
The processed output of $obj->draw_content() is drawn by <?=$obj->draw_content()?>
new release book covers<p class="headline">New Releases</p>
<p>xxxxxxx</p>
Why is the new_releases() output prepended? The slug is gone but the replacement is not in it's place!
you can replace your pattern by:
$slug = '~{\K[^}]*+(?=})~';
IMHO, you should replace your preg_match test and your preg_replace by an only preg_replace_callback function, try something like this (and correct the bugs :).
function draw_content() {
$slug = '~{([^}]*+)}~';
$that = $this;
$this->html = preg_replace_callback( $slug, function ($m) use ($that) {
if (method_exists($that, $m[1]))
return $that->$m[1]();
return $m[0]; }, $this->html);
return $this->html;
}

Replace in each paragraph

In $var there is some code. I'm trying to operate a nl2br() on text inside each <p></p>.
echo preg_replace('/<p>(.*?)</p>/i', nl2br('${1}'), $var);
This code doesn't work.
How do I fix this?
You probably need to escape <\/p>
It looks like preg_replace_callback might be what you're looking for: http://us3.php.net/manual/en/function.preg-replace-callback.php
ETA: In your specific example, you could either use an anonymous function (only if you're doing this once or twice, otherwise it eats up memory) or define a helper function
function nl2br_wrapper($matches)
{
return "<p>".nl2br($matches[1])."</p>";
}
preg_replace_callback('/<p>(.*?)<\/p>/si', "nl2br_wrapper", $var);
Like kernal mentions you could use an anonymous function(PHP 5)
function replaceText($data, $html)
{
$callback = function ($matches) use ($data){
return ( isset($data[$matches[1]]) )
? nl2br($data[$matches[1]])
: $matches[0];
};
return preg_replace_callback(
'/\<p>(.*?)\</p>',
$callback,
$html);
}
echo replaceText($replace_with, $html);

mustache i18n with parameters

I'm trying to use Mustache together with i18n (php, within Wordpress). I've got the basic __ functionality working nicely, something like this
class my_i18n {
public function __trans($string) {
return __($string, 'theme-name');
}
}
class mytache {
public function __()
{
return array('my_i18n', '__trans');
}
}
Then to output a template with an i18n string, I can simply do this
$context = new mytache;
$template = "<div>{{#__}}String to translate{{/__}}</div>";
$m = new Mustache;
echo $m->render($template, $context);
So far everything is fine. However, I want to be able to translate strings with parameters. i.e. the equivalent of sprint_f(__('Account Balance: %s'), $balance);.
It seems that if I do something like {{#__}}Account Balance: {{balance}}{{/__}} it doesn't work. I'm guessing because the inner tag gets converted first and therefore the translation cannot be found for the phrase.
Any ideas how to achieve this cleanly with Mustache?
UPDATE: here's the end-result snippet (with massive help from bobthecow):
class I18nMapper {
public static function translate($str) {
$matches = array();
// searching for all {{tags}} in the string
if (preg_match_all('/{{\s*.*?\s*}}/',$str, &$matches)) {
// first we remove ALL tags and replace with %s and retrieve the translated version
$result = __(preg_replace('/{{\s*.*?\s*}}/','%s', $str), 'theme-name');
// then replace %s back to {{tag}} with the matches
return vsprintf($result, $matches[0]);
}
else
return __($str, 'theme-name');
}
}
class mytache {
public function __()
{
return array('I18nMapper', 'trans');
}
}
I added an i18n example here... it's pretty cheesy, but the test passes. It looks like that's almost the same as what you're doing. Is it possible that you're using an outdated version of Mustache? The spec used to specify different variable interpolation rules, which would make this use case not work as expected.
On my behalf I would suggest using normal, fully functional template engine. I understand, that small is great and everything, but for example Twig is much more advanced. So I would recommend it.
About mustache. Can't you just extend you translation method! For example you pass {{#__}}Account Balance: #balance#{{/__}}
function __( $string, $replacement )
{
$replaceWith = '';
if ( 'balance' == $replacement )
{
$replaceWith = 234.56;
}
return str_replace( '#' . $replacement . '#', $replaceWith, $string );
}
class my_i18n
{
public function __trans( $string )
{
$matches = array();
$replacement = '';
preg_match( '~(\#[a-zA-Z0-9]+\#)~', $string, $matches );
if ( ! empty( $matches ) )
{
$replacement = trim( $matches[0], '#' );
}
return __( $string, $replacement );
}
}
$Mustache = new Mustache();
$template = '{{#__}}Some lime #tag#{{/__}}';
$MyTache = new mytache();
echo $Mustache->render( $template, $MyTache );
This is a very ugly example, but you can make it fancy yourself. As I see Mustache on it's own will not be able to do what you want.
Hope that helped.

Named backreferences with preg_replace

Pretty straightforward; I can't seem to find anything definitive regarding PHP's preg_replace() supporting named backreferences:
// should match, replace, and output: user/profile/foo
$string = 'user/foo';
echo preg_replace('#^user/(?P<id>[^/]+)$#Di', 'user/profile/(?P=id)', $string);
This is a trivial example, but I'm wondering if this syntax, (?P=name) is simply not supported. Syntactical issue, or non-existent functionality?
They exist:
http://www.php.net/manual/en/regexp.reference.back-references.php
With preg_replace_callback:
function my_replace($matches) {
return '/user/profile/' . $matches['id'];
}
$newandimproved = preg_replace_callback('#^user/(?P<id>[^/]+)$#Di', 'my_replace', $string);
Or even quicker
$newandimproved = preg_replace('#^user/([^/]+)$#Di', '/user/profile/$1', $string);
preg_replace does not support named backreferences.
preg_replace_callback supports named backreferences, but after PHP 5.3, so expect it to fail on PHP 5.2 and below.
preg_replace does not supported named subpatterns yet.
you can use this :
class oreg_replace_helper {
const REGEXP = '~
(?<!\x5C)(\x5C\x5C)*+
(?:
(?:
\x5C(?P<num>\d++)
)
|
(?:
\$\+?{(?P<name1>\w++)}
)
|
(?:
\x5Cg\<(?P<name2>\w++)\>
)
)?
~xs';
protected $replace;
protected $matches;
public function __construct($replace) {
$this->replace = $replace;
}
public function replace($matches) {
var_dump($matches);
$this->matches = $matches;
return preg_replace_callback(self::REGEXP, array($this, 'map'), $this->replace);
}
public function map($matches) {
foreach (array('num', 'name1', 'name2') as $name) {
if (isset($this->matches[$matches[$name]])) {
return stripslashes($matches[1]) . $this->matches[$matches[$name]];
}
}
return stripslashes($matches[1]);
}
}
function oreg_replace($pattern, $replace, $subject) {
return preg_replace_callback($pattern, array(new oreg_replace_helper($replace), 'replace'), $subject);
}
then you can use either \g<name> ${name} or $+{name} as reference in your replace statement.
cf (http://www.rexegg.com/regex-disambiguation.html#namedcapture)

Categories