I am trying to use preg_replace_callback() to call any function with its parameter(s) embedded in a string.
$string = "some text ucfirst('asd')";
$pattern = "~ucfirst([a-z]+)\(\)~";
$string = preg_replace_callback($pattern, "ucasef", $string);
echo $string; // some text Asd
I need some help with the pattern but also with how to use it to accomplish the example output.
This is how you may use it, I've added some comments to clarify the code:
$input = "some text ucfirst('name') and strtoupper (\"shout\" ). Maybe also make it strtolower( 'LOWER') or do('nothing').";
$pattern = '~
(\w+) # Match the function name and put it in group 1
\s*\(\s* # Some optional whitespaces around (
("|\') # Match either a double or single quote and put it in group 2
(.*?) # Match anything, ungreedy until ...
\2 # Match what was matched in group 2
\s*\) # Some optional whitespaces before )
~xs'; # XS modifiers, x to make this fancy formatting/commenting and s to match newlines with the dot "."
$output = preg_replace_callback($pattern, function($v){
$allowed = array('strtolower', 'strtoupper', 'ucfirst', 'ucwords'); // Allowed functions
if(in_array(strtolower($v[1]), $allowed)){ // Check if the function used is allowed
return call_user_func($v[1], $v[3]); // Use it
}else{
return $v[0]; // return the original value, you might use something else
}
}, $input);
echo $output;
Output: some text Name and SHOUT. Maybe also make it lower or do('nothing').
Related
Here is the sample PHP code:
<?php
$str = '10,000.1 $100,000.1';
$pattern = '/(?!\$)\d+(,\d{3})*\.?\d*/';
$replacement_str = 'Without$sign';
echo preg_replace($pattern, $replacement_str, $str);?>
Target is to replace numbers only (i.e. "$100,000.1" should not be replaced). But the above code replaces both 10,000.1 and $100,000.1. How to achieve the exclusion?
This assertion is always true (?!\$)\d+ as you match a digit which can not be a $
As the . and the digits at the end of the pattern are optional, it could also match ending on a dot like for example 0,000.
Instead you can assert a whitespace boundary to the left, and optionally match a dot followed by 1 or more digits:
(?<!\S)\d+(?:,\d{3})*(?:\.\d+)?\b
Regex demo
Example:
$str = '10,000.1 $100,000.1';
$pattern = '/(?<!\S)\d+(?:,\d{3})*(?:\.\d+)?\b/';
$replacement_str = 'Without$sign';
echo preg_replace($pattern, $replacement_str, $str);
Output (If you remove the numbers, the text "Without$sign" is not correct)
Without$sign $100,000.1
i made this function to find specific sets of characters in a text string and convert them to html tags:
function ccfc($content)
{
$reg_exUrl = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/";
// $code_block = preg_replace($reg_exUrl, "{$url[0]} ", $content);
if(preg_match($reg_exUrl, $content, $url)) {
// make the urls hyper links
$content = preg_replace($reg_exUrl, "{$url[0]} ", $content);
} else {
// if no urls in the text just return the text
$content = $content;
}
$code_block = preg_replace_callback(
'/([\`]{3})(.*?)([\`]{3})/s',
function($matches) {
$matches[2] = htmlentities($matches[2]);
return '<pre><code>'. $matches[2] .'</code></pre>';
},
$content);
$bold = preg_replace_callback(
'/([\*]{2})(.*?)([\*]{2})/s',
function($matches) {
$matches[2] = htmlentities($matches[2]);
return '<b>'. $matches[2] .'</b>';
},
$code_block);
$italic = preg_replace_callback(
'/([\*]{1})(.*?)([\*]{1})/s',
function($matches) {
$matches[2] = htmlentities($matches[2]);
return '<i>'. $matches[2] .'</i>';
},
$bold);
return $italic;
}
This function will find the urls like http://www.google.com and convert them to links
the second will find the ``` code content ``` and convert it to <pre><code> code content </code></pre>
The third will find the ** content ** and convert to <b> content </b>
The fourth will find the * content * and convert it to <i> content </i>
but if the code is written outside the ``` ``` it is executed. How can I make the remaining text use htmlentities()?
Rather than calling htmlentities after running the text through your converter functions, call it before you do the converting:
function ccfc($content) {
$content = htmlentities($content);
This won't affect the entities involved in the markup (* and `), and you can also set the double_encode flag to false ensure that already-encoded content (e.g. & characters in links) does not get encoded twice -- see the PHP manual for the settings:
$content = htmlentities($content, ENT_QUOTES, UTF-8, false);
This setting will treat the text as UTF-8, encode all quotes, but will not double encode a link like http://example.com?p=1&q=2.
On another note, you don't need to use preg_replace_callback for your substitutions; you can use the captured text in the replacement expression. Here's an example from the code formatting regex:
$code_block = preg_replace(
'/`{3}(.*?)`{3}/s',
"<pre><code>$1</code></pre>",
$content);
As noted in my comment, <b> and <i> are deprecated; if you are using them to place emphasis on text, you could replace them with <strong> and <em> respectively; if the markup is solely for presentation, it is better to enclose the text in a <span> element and give it a class which has bold or italic formatting.
Here is the full code with the htmlentities moved and preg_replace replacements:
function ccfc($content)
{ $content = htmlentities($content, ENT_QUOTES, NULL, false);
echo $content . PHP_EOL;
$reg_exUrl = "/((http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/?\S*)?)/";
// $code_block = preg_replace($reg_exUrl, "{$url[0]} ", $content);
// make the urls hyperlinks
$content = preg_replace($reg_exUrl, "<a href='$1'>$1</a>", $content);
# replace ``` with code blocks
$content = preg_replace(
'/`{3}(.*?)`{3}/s',
"<pre><code>$1</code></pre>",
$content);
# replace **text** with strong text
$content = preg_replace(
'/\*{2}([^\*].*?)\*{2}/s',
"<strong>$1</strong>",
$content);
# replace *text* with em text
$content = preg_replace(
'/\*(.*?)\*/s',
"<em>$1</em>",
$content);
return $content;
}
A quick explanation of how preg_replace works: when you use parentheses in a regular expression, you capture the matter within those parentheses to the special variables $1, $2, $3, etc. The contents of the first set of parentheses are in $1, the contents of the second set in $2, and so on. For example, take this regular expression:
/(\w+) and (\w+)/
and the input string bread and butter, bread matches the expression in the first set of parens and butter matches the expression in the second set; $1 would be set to bread and $2 to butter. This becomes useful when we do preg_replace, as we can use $1 and $2 in the replacement string:
$str = preg_replace("/(\w+) and (\w+)/", "I love $2 on $1", "bread and butter");
echo $str;
Output:
I love butter on bread
Anything that is in the match string but isn't captured will disappear -- like the and in this example.
In the replacements in your code, the text between the delimiters (* and `) needs to be kept, so it is captured in parentheses; the delimiters themselves are not needed, so they aren't in parentheses.
Explanation of the other characters in the regexs:
?, *, +, {2} : these are quantifiers - they dictate the number of times the preceding pattern should appear. ? means 0 or 1 times; * is 0 or more times; + is one or more times; {2} means twice; {500} would mean 500 times.
\w represents any number, letter, or _
. matches any character
.*? matches a string of any length, including length 0.
\** would match 0 or more * characters; to match *, you have to escape it (i.e. \*) so that the regex engine doesn't interpret it as a quantifier
I've not yet mastered regex, so would appreciate your help with the code.
I need to replace all lines that:
start with brackets or parentheses;
which may contain either a regular number of up to 3 digits or a combination of up to 3 letters;
which may be followed by a period;
which digis or the numbers may or may not be inside or tags.
Here's the example of what it needs to be replaced with:
(1)blahblah => %%(1)|blahblah
(<i>iv</i>.) blahblah => %%(<i>iv</i>.)|blahblah
[b] some stuff => %%[b]| some stuff
So the regex will need to recognize if it needs to be applied to the particular string, and if yes, put %% in the beginning of the line, then put the stuff inside the brackets, then put a pipe | (if there is a space between the brackets and the rest of the text, delete the space), and finally place the rest of the line.
So, let's assume I have an array that I'm trying to run through the function that will either process the string (if it matches the criteria), or return it unchanged.
I only need to know how to write the function.
Thanks
function my_replace ($str) {
$expr = '~
(
# opening bracket or paren
(?:\(|\[)
# optional opening tag
(?:<([a-z])>)?
# either up to 3 digits or up to 3 alphas
(?:[a-z]{1,3}|[0-9]{1,3})
# optional closing tag
(?:</\2>)?
# optional dot
\.?
# closing bracket or paren
(?:\)|])
)
# optional whitespace
\s*
# grab the rest of the string
(.+)
~ix';
return preg_replace($expr, '%%$1|$3', $str);
}
See it working
Here is my version.
It uses the fallback regular expression if the first one doesn't match (as agreed upon previously).
Demo
Code:
<?php
function do_replace($string) {
$regex = '/^(\((?:<([a-z])>)?(\d{0,3}|[a-z]{1,3})(?:<\/\2>)?(\.)?\)|\[(?:<([a-z])>)?(\d{0,3}|[a-z]{1,3})(?:<\/\2>)?(\.)?\])\s*(.*)/i';
$result = preg_match($regex, $string);
if($result) {
return preg_replace($regex, '%%$1|$8', $string);
} else {
$regex = '/^(\d{0,3}|[a-z]{1,3})\.\s*(.+)$/i';
$result = preg_match($regex, $string);
if($result) {
return preg_replace($regex, '%%$1.|$2', $string);
} else {
return $string;
}
}
}
$strings = array(
'(1)blahblah',
'(<i>iv</i>.) blahblah',
'[b] some stuff',
'25. blahblah',
'A. some other stuff. one',
'blah. some other stuff',
'text (1) text',
'2008. blah',
'[123) <-- mismatch'
);
foreach($strings as $string) echo do_replace($string) . PHP_EOL;
?>
First regular expression expanded:
$regex = '
/
^(
\(
(?:<([a-z])>)?
(
\d{0,3}
|
[a-z]{1,3}
)
(?:<\/\2>)?
(\.)?
\)
|
\[
(?:<([a-z])>)?
(
\d{0,3}
|
[a-z]{1,3}
)
(?:<\/\2>)?
(\.)?
\]
)
\s*
(.*)
/ix';
function replaceString($string){
return preg_replace('/^\s*([\{,\[,\(]+?)/', "%%$1", $string);
}
I want to display the most recent posts from my phpbb3 forum on my website, but without the bbcode.
so I'm trying to strip the bbcode but without succes
one of the posts for example could be:
[quote="SimonLeBon":3pwalcod]bladie bla bla[/quote:3pwalcod]bla bla bladie bla blaffsd
fsdjhgfd dgfgdffgdfg
to strip bbcodes i use the function i found via google, I've tried several other similiar functions aswell:
<?php
function stripBBCode($text_to_search) {
$pattern = '|[[\/\!]*?[^\[\]]*?]|si';
$replace = '';
return preg_replace($pattern, $replace, $text_to_search);
}
?>
This however doesn't really have any effect.
This will strip bbcode, that is valid (i.e. opening tags matching closing tags).
$str = preg_replace('/\[(\w+)=.*?:(.*?)\](.*?)\[\/\1:\2\]/', '$3', $str);
CodePad.
Reusable Function
function stripBBCode($str) {
return preg_replace('/\[(\w+)=.*?:(.*?)\](.*?)\[\/\1:\2\]/', '$3', $str);
}
Explanation
\[ match literal [.
(\w+) Match 1 or more word characters and save in capturing group 1.
= Match literal =.
.*? Match ungreedily every character except \n between = and :.
: Match literal :.
(.*?) Match ungreedily every character except \n between : and ] and save in capturing group 2.
\] Match literal ].
(.*?) Match ungreedily every character except \n between : and ] and save in capturing group 3.
\[ Match literal [.
/\1\2 Match previous capturing groups again.
\] Match literal ].
Here's the one from phpBB (slightly adjusted to be standalone):
/**
* Strips all bbcode from a text and returns the plain content
*/
function strip_bbcode(&$text, $uid = '')
{
if (!$uid)
{
$uid = '[0-9a-z]{5,}';
}
$text = preg_replace("#\[\/?[a-z0-9\*\+\-]+(?:=(?:".*"|[^\]]*))?(?::[a-z])?(\:$uid)\]#", ' ', $text);
$match = return array(
'#<!\-\- e \-\->.*?<!\-\- e \-\->#',
'#<!\-\- l \-\-><a (?:class="[\w-]+" )?href="(.*?)(?:(&|\?)sid=[0-9a-f]{32})?">.*?</a><!\-\- l \-\->#',
'#<!\-\- ([mw]) \-\-><a (?:class="[\w-]+" )?href="(.*?)">.*?</a><!\-\- \1 \-\->#',
'#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#',
'#<!\-\- .*? \-\->#s',
'#<.*?>#s',
);
$replace = array('\1', '\1', '\2', '\1', '', '');
$text = preg_replace($match, $replace, $text);
}
Why don't you use the BBCode parsing facilities that are built in to PHP?
http://php.net/manual/en/book.bbcode.php
Nowadays, use phpbb's own function https://wiki.phpbb.com/Strip_bbcode
I'm trying to use regular expressions (preg_match and preg_replace) to do the following:
Find a string like this:
{%title=append me to the title%}
Then extract out the title part and the append me to the title part. Which I can then use to perform a str_replace(), etc.
Given that I'm terrible at regular expressions, my code is failing...
preg_match('/\{\%title\=(\w+.)\%\}/', $string, $matches);
What pattern do I need? :/
I think it's because the \w operator doesn't match spaces. Because everything after the equal sign is required to fit in before your closing %, it all has to match whatever is inside those brackets (or else the entire expression fails to match).
This bit of code worked for me:
$str = '{%title=append me to the title%}';
preg_match('/{%title=([\w ]+)%}/', $str, $matches);
print_r($matches);
//gives:
//Array ([0] => {%title=append me to the title%} [1] => append me to the title )
Note that the use of the + (one or more) means that an empty expression, ie. {%title=%} won't match. Depending on what you expect for white space, you might want to use the \s after the \w character class instead of an actual space character. \s will match tabs, newlines, etc.
You can try:
$str = '{%title=append me to the title%}';
// capture the thing between % and = as title
// and between = and % as the other part.
if(preg_match('#{%(\w+)\s*=\s*(.*?)%}#',$str,$matches)) {
$title = $matches[1]; // extract the title.
$append = $matches[2]; // extract the appending part.
}
// find these.
$find = array("/$append/","/$title/");
// replace the found things with these.
$replace = array('IS GOOD','TITLE');
// use preg_replace for replacement.
$str = preg_replace($find,$replace,$str);
var_dump($str);
Output:
string(17) "{%TITLE=IS GOOD%}"
Note:
In your regex: /\{\%title\=(\w+.)\%\}/
There is no need to escape % as its
not a meta char.
There is no need to escape { and }.
These are meta char but only when
used as a quantifier in the form of
{min,max} or {,max} or {min,}
or {num}. So in your case they are treated literally.
Try this:
preg_match('/(title)\=(.*?)([%}])/s', $string, $matches);
The match[1] has your title and match[2] has the other part.