How to use preg_replace regardless of the input? - php

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.

Related

Replace all domain including subdomain name using PHP with Regex

I am using preg_replace or str_replace in PHP to replace all the domain name including www
$string = ' https://example.com, https://www.example.com, https://subdomain.example.com ';
$olddomain= "example.com";
$newdomain = "stackoverflow.com";
$output = str_replace($olddomain, $newdomain, $string);
$output = preg_replace('#(www[.])?[.]' . $olddomain. '#', $newdomain, $body);
echo $output;
My expectation:
https://example.com -> https://stackoverflow.com
https://www.example.com -> https://stackoverflow.com
https://subdomain.example.com -> https://subdomain.example.com
preg_replace with regex.
$string = ' https://example.com, https://www.example.com, https://subdomain.example.com ';
$olddomain= "example.com";
$newdomain = "stackoverflow.com";
$output = preg_replace('#(https://(www\.)?)' . $olddomain. '#', '$1' . $newdomain, $string);
echo $output;
Output :
https://stackoverflow.com, https://www.stackoverflow.com, https://subdomain.example.com
No need for a regex, just pass the www. with the other change in into str_replace(). Remember str_replace() accepts an array of things to "change from" and "change to".
$string = ' https://example.com, https://www.example.com, https://subdomain.example.com ';
$olddomain = ["example.com",'www.'];
$newdomain = ["stackoverflow.com", ''];
$output = str_replace($olddomain, $newdomain, $string);
echo $output;
RESULT
https://stackoverflow.com, https://stackoverflow.com, https://subdomain.stackoverflow.com
Without using Regex
$string = ' https://example.com, https://www.example.com, https://subdomain.example.com ';
$olddomain= "example.com";
$newdomain = "stackoverflow.com";
$parts = explode(",", $string);
$new_parts = [];
foreach ($parts as $part) {
$new_parts[] = str_replace(['https://', 'http://', $olddomain], ['https://', 'http://', $newdomain], $part);
}
$output = implode(",", $new_parts);
echo $output;
Return
https://stackoverflow.com, https://www.stackoverflow.com, https://subdomain.example.com
With Array and Regex
$string = ' https://example.com, https://www.example.com, https://subdomain.example.com ';
$olddomain = ["example.com", 'www\.'];
$newdomain = ["stackoverflow.com", ''];
$output = preg_replace('#https://(' . implode('|', $olddomain) . ')#', 'https://' . $newdomain[0], $string);
$output = preg_replace('#(' . $olddomain[1] . ')#', $newdomain[1], $output);
echo $output;

ignore url in PHP regex

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

Adding custom masks to phone numbers

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 . '})[- ]/';

Why this portion of PHP code doesn't work?

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

remove word from string by position from input

EXAMPLE:
input = 2
text = aa bb cc
Will become: aa cc
The input for position is $_POST['position']
i have
$words = explode(" ", $_POST['string']);
for ($i=0; $i<count($words); $i++){
echo $words[$i] . " ";
}
$to_remove = 2;
$text = "aa bb cc";
$words = explode(' ', $text);
if(isset($words[$to_remove -1])) unset($words[$to_remove -1]);
$text = implode(' ', $words);
Pulling out the big guns REGEX !!!
$string = 'aa bb cc dd';
$input = 2;
$regex = $input - 1;
echo preg_replace('#^((?:\S+\s+){'.$regex.'})(\S+\s*)#', '$1', $string);
Output: aa cc dd
Foreach loops tend to make it a little easier to understand (IMO). Looks cleaner too.
$pos = 2;
$sentence = explode(" ", $_POST['string']);
foreach ($sentence as $index => $word) {
if ($index != $pos - 1) {
$result .= $word . ' ';
}
}
echo $result;
This sounds like a homework question. I'll take a stab at it though:
Code:
<?php
$string = trim($_POST['string']);
$parts = explode(" ", string);
$newString = "";
$position = intval($_POST['position']);
for($a = 0; $a < count($parts); $a++) {
if($a != $position) { // or $a != ($position - 1) depending on if you passed in zero based position
$newString = $newString . $parts[$a] . " ";
}
}
$newString = trim($newString);
echo "Old String: " . $string . "<br />";
echo "New String: " . $newString;
?>
Output:
Old String: aa bb cc
New String: aa cc
$input = 2;
$words = explode(" ", $_POST['string']);
unset($words[$input-1]);
$words = implode(" ", $words);
echo $words;

Categories