I'm trying to change color of a substring, like this:
$str = 'abcd<efgh>lmno';
preg_match_all('/<[\S]*?>/m', $str, $matches, PREG_PATTERN_ORDER);
$replacements = $needles = [];
foreach ($matches[0] as $match) {
$needles[] = $match;
$replacements[] = '<span style="color:red;">' . $match . '</span>';
}
echo str_replace($needles, $replacements, $str);
I expect this result: abcd<span style="color:red;"><efgh></span>lmno
But I obtain: abcdlmno
Try this:
$str = 'abcd<efgh>lmno';
preg_match_all('/<[\S]*?>/m', $str, $matches, PREG_PATTERN_ORDER);
foreach ($matches[0] as $match) {
$str = str_replace($match, '<span style="color:red;">' . $match . '</span>', $str);
}
echo $str;
//abcd<span style="color:red;"><efgh></span>lmnoC
Related
I'm trying to replace the matches with numbers by sorting them but couldn't do it. I have a string that has {} in between words. I'd like to change them to 1, 2, 3, etc. without foreach. could be with preg matches all?
$string = 'sample {} test {} string {}';
This string must have seen like this:
sample (0) test (1) string (2)
This is my code:
$string_split = explode('{}', $string);
foreach($string as $string_word){
$i ++;
echo $string_word . $i . ' ';
}
Another option using preg_replace_callback, incrementing the count for every replacement.
$string = 'sample {} test {} string {}';
$count = 0;
echo preg_replace_callback("~{}~", function($m) use (&$count) {
return '(' . $count++ . ')';
}, $string);
Output
sample (0) test (1) string (2)
Php demo
You could iterate over your string using preg_match, keeping a counter along the way:
$string = 'sample {} test {} string {}';
$counter = 0;
while (preg_match("/\{\}/", $string)) {
$string = preg_replace("/\{\}/", "(" . $counter . ")", $string, 1);
$counter = $counter + 1;
}
echo "\n" . $string;
This prints:
sample {} test {} string {}
sample (0) test (1) string (2)
I was thinking the regex can be useful.
$re = '/(.*?)\s{}(\s?)/m';
$str = 'sample {} test {} string {}';
preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
// Print the entire match result
$new_string = null;
$i = 0;
foreach($matches as $item){
$new_string .= $item[1] . ' ('. $i . ') ';
$i++;
}
echo $new_string;
Output:
sample (0) test (1) string (2)
I have an input field in which users can specify a pattern for replacements.
But I want to allow them to leave it empty, and treat it like "pattern can be anything". Please remember the string ($subject) can be empty or not empty!
I tried using ".*" for this but it writes a double output. What can I use instead?
An empty pattern is just one of many possible patterns so I prefer to have a regex for it even though I can just do if (empty($pattern)) echo $replacement;.
<?php
echo '<ol>';
echo '<li><ol>';
$pattern = '';
$replacement = 'Sample output';
$subject = 'Foo bar';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
$pattern = '';
$replacement = 'Sample output';
$subject = '';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
echo '</ol><li><ol>';
$pattern = '';
if (empty($pattern))
$pattern = '.*';
$replacement = 'Sample output';
$subject = 'Foo bar';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
$pattern = '';
if (empty($pattern))
$pattern = '.*';
$replacement = 'Sample output';
$subject = '';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
echo '</ol><li><ol>';
$pattern = '';
if (empty($pattern))
$pattern = '.+';
$replacement = 'Sample output';
$subject = 'Foo bar';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
$pattern = '';
if (empty($pattern))
$pattern = '.+';
$replacement = 'Sample output';
$subject = '';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
echo '</ol><li><ol>';
$pattern = '';
if (empty($pattern))
$pattern = '$^';
$replacement = 'Sample output';
$subject = 'Foo bar';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
$pattern = '';
if (empty($pattern))
$pattern = '$^';
$replacement = 'Sample output';
$subject = '';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
echo '</ol><li><ol>';
$pattern = '';
if (empty($pattern))
$pattern = '(?:)';
$replacement = 'Sample output';
$subject = 'Foo bar';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
$pattern = '';
if (empty($pattern))
$pattern = '(?:)';
$replacement = 'Sample output';
$subject = '';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
echo '</ol><li><ol>';
$pattern = '';
if (empty($pattern))
$pattern = '(?=a)o';
$replacement = 'Sample output';
$subject = 'Foo bar';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
$pattern = '';
if (empty($pattern))
$pattern = '(?=a)o';
$replacement = 'Sample output';
$subject = '';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
echo '</ol><li><ol>';
$pattern = '';
if (empty($pattern))
$pattern = '.\A';
$replacement = 'Sample output';
$subject = 'Foo bar';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
$pattern = '';
if (empty($pattern))
$pattern = '.\A';
$replacement = 'Sample output';
$subject = '';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
echo '</ol><li><ol>';
$pattern = '';
if (empty($pattern))
$pattern = '\z.';
$replacement = 'Sample output';
$subject = 'Foo bar';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
$pattern = '';
if (empty($pattern))
$pattern = '\z.';
$replacement = 'Sample output';
$subject = '';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
echo '</ol><li><ol>';
$pattern = '';
if (empty($pattern))
$pattern = '(?!)';
$replacement = 'Sample output';
$subject = 'Foo bar';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
$pattern = '';
if (empty($pattern))
$pattern = '(?!)';
$replacement = 'Sample output';
$subject = '';
echo '<li>' . preg_replace('/' . $pattern . '/', $replacement, $subject);
?>
The needed output (for empty and non empty strings) should be:
Sample Output
Sample Output
As you can see below, the output is never like that:
Sample outputFSample outputoSample outputoSample output Sample outputbSample outputaSample outputrSample outputSample outputSample outputSample outputSample outputSample outputFoo barSample outputSample outputFSample outputoSample outputoSample output Sample outputbSample outputaSample outputrSample outputSample outputFoo barFoo barFoo barFoo bar
The replacement gets doubled because preg_replace replaces all match occurrences in the string, and .* pattern is capable of matching empty strings, and it does match the whole string first, and then - it is PCRE feature - allows the next match right at the end of the string. See preg_replace double replacement.
In your case, the solution is using both start of string (^ or \A) and end of string ($ or \z) anchors:
'/\A.*\z/s'
'/^.*$/s'
This way, you will always match a string, be it empty or not, once.
I've got a utility where I'm trying to enforce brand standards in an application where the function will wrap brand words in a span with a class.
public function filterBrandWords($text)
{
// look up the brand words from the config settings
$filter_terms = ['brandword1', 'brandword2', 'brandword3'];
$filtered_text = $text;
foreach ($filter_terms as $word) {
$match_count = preg_match_all('/' . $word . '/i', $text, $matches);
for ($i = 0; $i < $match_count; $i++) {
$brand_string = trim($matches[0][$i]);
$lower = strtolower($brand_string);
$new = '<span class="font-semibold">' . substr($lower, 0, 3) . '</span>' . substr($lower, 3);
$filtered_text = preg_replace('/\b' . $brand_string . '\b/', $new, $filtered_text);
}
}
return $filtered_text;
}
This works but noticed that it's also filtering text that contains the brand URL when applied.
I tried amending $match_count = preg_match_all('/' . $word . '/i', $text, $matches); to $match_count = preg_match_all('/' . $word . 'com$' . '/i', $text, $matches); in the hope it would ignore matches with com in them.
What have I gotten wrong here the regex?
If I do
echo filterBrandWords('brandword1');
the output is
<span class="font-semibold">bra</span>ndword1
with a URL, the output is
<span class="font-semibold">bra</span>ndword1.com
In those instances, I want to ignore the filter and just give it straight.
If you want to ignore anything like a URL you can use something like this as your regex:
(?|.*\.(com|net|org))
which is a Negative Lookahead assertion that matches URL's (broadly). Insert that into your function as I have done here:
function filterBrandWords($text)
{
// look up the brand words from the config settings
$filter_terms = ['brandword1', 'brandword2', 'brandword3'];
$filtered_text = $text;
if(!preg_match('/(?|.*\.(com|net|org))/', $filtered_text)) { // if it resembles a URL, skip it
foreach ($filter_terms as $word) {
$match_count = preg_match_all('/' . $word . '/i', $text, $matches);
for ($i = 0; $i < $match_count; $i++) {
$brand_string = trim($matches[0][$i]);
$lower = strtolower($brand_string);
$new = '<span class="font-semibold">' . substr($lower, 0, 3) . '</span>' . substr($lower, 3);
$filtered_text = preg_replace('/\b' . $brand_string . '\b/', $new, $filtered_text);
}
}
}
return $filtered_text;
}
Now call the function with something resembling a URL:
echo filterBrandWords('brandword1.com');
And the entire URL is just returned:
brandword1.com
EXAMPLE
So i'm creating a simple function to mask phone numbers. My phone numbers have a 9 digits and i want preg_replace them with a given mask like 2-2-2-1-2 or 3-2-2-2 and etc.
I tried this:
$mask = explode('-', '3-2-2-2');
$pattern = '';
$replace = '';
foreach ($mask as $key => $value) {
if ($key == 0) {
$pattern = '/\(?(\d{' . $value . '})\)?[- ]';
$replace = '$' . ++$key . '-';
continue;
}
if ($key == count($mask) - 1) {
$pattern .= '?(\d{' . $value . '})/';
$replace .= '$' . ++$key;
break;
}
$pattern .= '?(\d{' . $value . '})[- ]';
$replace .= '$' . ++$key . '-';
}
return preg_replace($pattern, $replace, '902000810');
and the result is 902-00-08-10. Sometimes getting error preg_replace(): No ending delimiter '/' found. How can i refactor this to not getting errors?
Assuming:
$num = '902000810';
$mask = explode('-', '3-2-2-2');
There're other ways than using regex to format a phone number from the mask.
using formatted strings:
$maskPH = array_map(fn($i) => "%{$i}s", $mask);
$formatI = implode('', $maskPH);
$formatO = implode('-', $maskPH);
$result = vsprintf($formatO, sscanf($num, $formatI));
using unpack:
$format = array_reduce($mask, function ($c, $i) {
static $j = 0;
return "{$c}A{$i}_" . $j++ . "/";
});
$result = implode('-', unpack($format, $num));
preg_replace(): No ending delimiter '/' found
means that your pattern does not terminate with a / as last character.
But all three patterns lack proper formatting:
You should modify them accordingly.
From:
$pattern = '/\(?(\d{' . $value . '})\)?[- ]';
$pattern .= '?(\d{' . $value . '})/';
$pattern .= '?(\d{' . $value . '})[- ]';
To:
$pattern = '/\(?(\d{' . $value . '})\)?[- ]/';
$pattern .= '/?(\d{' . $value . '})/';
$pattern .= '/?(\d{' . $value . '})[- ]/';
i have some big string, and some array of words that must be replaced with some changes, like wrapping in link. First issue is wrap whole words or combinations words. And the second issue is do previous step minimum every 1000 characters.
$string="lalala word lalala blah, blah lalala combination of words lalala lalala...";
$patterns=array('word','combination of words');
$replacements=array('word','combination of words');
For an example, what i must to do with snippet before?
It sounds to me like you're looking for wordwrap(). You can then use preg_replace_callback() to apply it to your search patterns and make the replacements:
foreach ($patterns as $pattern) {
$regex = '/' . preg_quote($pattern, '/') . '/';
$string = preg_replace_callback($regex, function($match) {
return '<a href="#">'
. wordwrap(htmlspecialchars($match), 1000, '<br />')
. '</a>';
}, $string);
}
SOLUTION:
<?php
function set_keys_by_words($content, $key, $words,$before,$after) {
$positions = array();
$string = '';
for ($i = 0; $i < count($words); $i++) {
$string = preg_replace('/\b' . $words[$i] . '\b/ui', $key . $words[$i], $content);
$position = mb_strpos($string, $key);
if ($position != '') {
$positions[(int) $position] = $words[$i];
}
}
ksort($positions);
$word = '';
$number = '';
$i = 0;
foreach ($positions as $k => $v) {
$i++;
if ($i == 1) {
$number = $k;
$word = $v;
}
}
if ((int) $number) {
$word_len = strlen($word);
$part_after = preg_replace('/\b' . $word . '\b/ui', $before . $word . $after, mb_substr($content, 0, $number + $word_len));
echo $part_after . mb_substr($content, $number + $word_len, 1000);
$content = mb_substr($content, $number + $word_len + 1000);
if ($content != '') {
set_keys_by_words($content, $key, $words);
}
} else if ($number == '' && $content != '') {
echo $content;
}
}
?>