Proper way to replace string with callback - php

I'm working on implementation of a CMS and want to include functionality similar to how word press uses short codes, but I am having trouble replacing the "shortcode" with the function callback.
I'm using the below regex to find all "shortcodes" in the code and it works, I just can't figure out how to go about replacing it with function callbacks.
Regex: /[([^]]*)]/
What I have so far(not working)
function runShortcodes($input){
return preg_replace_callback(
'/\[([^\]]*)\]/', function ($matches)
{
$function = $matches[1];
ob_start();
$function();
$return = ob_get_contents();
ob_end_clean();
return $return;
}, $input
);
}
function event(){
return 'it worked';
}
echo runShortcodes('test [event]');
Right now i'm just trying to replace the [event] with the return data of the event function.

As you are using output buffering to capture the value from the short code function, you will need to actually output something from the event() function...
function event(){
return 'it worked';
}
just passes the value back, try...
function event(){
echo 'it worked';
}
Or remove the output buffering and just return the value from the short code...
function runShortcodes($input){
return preg_replace_callback(
'/\[([^\]]*)\]/', function ($matches)
{
$function = $matches[1];
return $function();
}, $input
);
}
function event(){
return 'it worked';
}
echo runShortcodes('test [event]');

Related

PHP Using preg_replace_callback shortcodes function returns wrong elements order

I'm defining a function which can handle shortcodes for users wisiwyg made posts.
Using a function based on preg_replace_callback works great, but the returned replaced values prints before the initial string
This is the handler function
function shortcodify($string){
return preg_replace_callback('#\[\[(.*?)\]\]#', function($matches){
$parts = explode(':',$matches[1]);
$fnName = array_shift($parts);
if(function_exists($fnName)){
return call_user_func_array($fnName,$parts);
} else {
return $matches[0];
}
},$string);
}
This is the function which will replace the shortcode
function slider($tag){
//search $tag in DB
echo '<div>...'.$sliderContentFromDB.'...</div>';
}
The usage:
$postContent = "<h1>Super Slider</h1> [[slider:super-slider]] <p>Slider Description</p>";
shortcodify($postContent);
The expected result is:
<h1>Super Slider</h1>
<div>...super slider content...</div>
<p>Slider Description</p>
The actual result is:
<div>...super slider content...</div>
<h1>Super Slider</h1>
<p>Slider Description</p>
What could I be doing wrong?
You should return the value rather than echoing it. What you're seeing is correct, in that the callback function should evaluate before the preg_replace_callback() result is returned to your variable.
function slider($tag){
//search $tag in DB
return '<div>...'.$sliderContentFromDB.'...</div>';
}
Returning will ensure it gets aggregated into the rest the results from preg_replace_callback(), and returned in the correct order. Example.
This is how I would do it:
function shortcodify($string){
return preg_replace_callback('#\[\[(.*?)\]\]#', function($matches){
//start output buffering
ob_start();
$parts = explode(':',$matches[1]);
$fnName = array_shift($parts);
//echo values in case they return instead of echo
if(function_exists($fnName)){
echo call_user_func_array($fnName,$parts);
} else {
echo $matches[0];
}
//return contents of buffer
return ob_get_clean();
},$string);
}
Now if you return or echo from the shortcode it makes no difference, because in either case it will get swept up by the buffer.

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;
}

preg_replace_callback: passing in replacement callback as a variable not working

This does not work
$check["pattern"] = "/correct/";
$callback = "function ($m) { return ucfirst($m[0]);}";
echo preg_replace_callback($check["pattern"],$callback,"correct" );
output: correct
This works
$check["pattern"] = "/correct/";
echo preg_replace_callback($check["pattern"],function ($m) { return ucfirst($m[0]);},"correct" );
output: Correct
Why, and how to make it work with the function stored inside a var? :)
Why would you want to do that? I see no reason to store the function inside a variable, to be honest. Nevertheless, if you really want to do this, take a look at create_function:
<?php
$check["pattern"] = "/correct/";
$callback = create_function('$m', 'return ucfirst($m[0]);');
echo preg_replace_callback( $check['pattern'], $callback, "correct" );
// Output: "Correct"
If you do a var_dump on $callback = "function ($m) { return ucfirst($m[0]);}"; the result is a string. In the working situation you pass a Closure (anonymous function) as callback.
The manual is clear: a Closure is allowed, if you pass a string, it must be the name of a function.

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);

PHP Str_replace for echo function

The following function is part of code written into the core of a plugin I am reverse engineering. The problem with it is that I need to do an str_replace on it and I cannot because it is already set to echo.
The function is.
function similar_posts($args = '') {
echo SimilarPosts::execute($args);
}
I call it in my pages using similar_posts(), but what I really need to do in my theme is call $related = similar_posts(), however that function is set to echo. How do I change that.
I tried this.
function get_similar_posts($args = '') {
SimilarPosts::execute($args);
}
But that did not produce any results.
function get_similar_posts($args = '') {
return (SimilarPosts::execute($args));
}
If you want to use the value SimilarPosts::execute ($args) returns, you'll need to use the keyword 'return' inside your get_similar_posts.
function get_similar_posts ($args = '') {
return SimilarPosts::execute($args);
}
If you are unable to change the definition of get_similar_posts there are ways to snatch the content printed by similar_posts even though it's "set to echo".
This can be accompished by using the Output Control Functions available in PHP.
function echo_hello_world () {
echo "hello world";
}
$printed_data = "";
ob_start ();
{
echo_hello_world ();
$printed_data = ob_get_contents ();
}
ob_end_clean ();
echo "echo_hello_world () printed '$printed_data'\n";
output
echo_hello_world () printed 'hello world'
Use return instead of echo.
So that you have:
return SimilarPosts::execute($args);
instead of:
echo SimilarPosts::execute($args);
Wrap the function inside another in which you use output buffering.
Done it..
function get_similar_posts($args = '') {
return SimilarPosts::execute($args);
}
and on the page get_similar_posts();
Should have thought of that.
return from the function:
function get_similar_posts($args = '') {
return SimilarPosts::execute($args);
}

Categories