PHP Get the word replaced by preg_replace - php

How can I get the replaced word by preg_replace() function.
preg_replace('/[#]+([A-Za-z0-9-_]+)/', '$0', $post );
I want to get $1 variable so that I can user it further.

Capture it before you replace the expression:
// This is where the match will be kept
$matches = array();
$pattern = '/[#]+([A-Za-z0-9-_]+)/';
// Check if there are matches and capture the user (first group)
if (preg_match($pattern, $post, $matches)) {
// First match is the user
$user = $matches[1];
// Do the replace
preg_replace($pattern, '$0', $post );
}

This isn't possible with preg_replace() as it returns the finished string/array, but does not preserve the replaced phrases. You can use preg_replace_callback() to manually achieve this.
$pattern = '/[#]+([A-Za-z0-9-_]+)/';
$subject = '#jurgemaister foo #hynner';
$tokens = array();
$result = preg_replace_callback(
$pattern,
function($matches) use(&$tokens) {
$tokens[] = $matches[1];
return ''.$matches[0].'';
},
$subject
);
echo $result;
// #jurgemaister foo #hynner
print_r($tokens);
// Array
// (
// [0] => jurgemaister
// [1] => hynner
// )

You should use preg_match in addition to preg_replace. preg_replace is just for replacing.
$regex = '/[#]+([A-Za-z0-9-_]+)/';
preg_match($regex, $post, $matches);
preg_replace($regex, '$0', $post );

You can't do that with preg_replace, but you can do it with preg_replace_callback:
preg_replace_callback($regex, function($matches){
notify_user($matches[1]);
return "<a href='/$matches[1]' target='_blank'>$matches[0]</a>";
}, $post);
replace notify_user with whatever you would call to notify the user.
This can also be modified to check whether the user exists and replace only valid mentions.

Related

Find URL in string and turn into a link

I'm using the code given on this page to look through a string and turn the URL into an HTML link.
It works quite well, but there is a little issue with the "replace" part of it.
The problem occurs when I have almost identical links. For example:
https://example.com/page.php?goto=200
and
https://example.com/page.php
Everything will be fine with the first link, but the second will create a <a> tag in the first <a> tag.
First run
https://example.com/page.php?goto=200
Second
https://example.com/page.php?goto=200">https://example.com/page.php?goto=200</a>
Because it's also replacing the html link just created.
How do I avoid this?
<?php
function turnUrlIntoHyperlink($string){
//The Regular Expression filter
$reg_exUrl = "/(?i)\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))/";
// Check if there is a url in the text
if(preg_match_all($reg_exUrl, $string, $url)) {
// Loop through all matches
foreach($url[0] as $newLinks){
if(strstr( $newLinks, ":" ) === false){
$link = 'http://'.$newLinks;
}else{
$link = $newLinks;
}
// Create Search and Replace strings
$search = $newLinks;
$replace = ''.$link.'';
$string = str_replace($search, $replace, $string);
}
}
//Return result
return $string;
}
?>
You need to add a whitespace identifier \s in your regex at the start, also remove \b because \b only returns the last match.
You regex can written as:
$reg_exUrl = "/(?i)\s((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))/"
check this one: https://regex101.com/r/YFQPlZ/1
I have change the replace part a bit, since I couldn't get the suggested regex to work.
Maybe it can be done better, but I'm still learning :)
function turnUrlIntoHyperlink($string){
//The Regular Expression filter
$reg_exUrl = "/(?i)\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))/";
// Check if there is a url in the text
if(preg_match_all($reg_exUrl, $string, $url)) {
// Loop through all matches
foreach($url[0] as $key => $newLinks){
if(strstr( $newLinks, ":" ) === false){
$url = 'https://'.$newLinks;
}else{
$url = $newLinks;
}
// Create Search and Replace strings
$replace .= ''.$url.',';
$newLinks = '/'.preg_quote($newLinks, '/').'/';
$string = preg_replace($newLinks, '{'.$key.'}', $string, 1);
}
$arr_replace = explode(',', $replace);
foreach ($arr_replace as $key => $link) {
$string = str_replace('{'.$key.'}', $link, $string);
}
}
//Return result
return $string;
}

"preg_replace()" not working properly.

I've a text file that I extracted all the domain addresses strting with http://
now I want to replace all the http://. in my matches array with "" but nothing is happening im not even getting an an error
$list = file_get_contents( 'file.txt' );
preg_match_all( "/http:\/\/.([a-z]{1,24}).([a-z^0-9-]{1,23}).([a-z]{1,3})/", $list, $matches );
for ($i=0; $i>=50; $i++) {
$pattern = array();
$replacement = array();
$pattern[0][$i] = "/http:\/\/.[w-w]{1,3}/";
$replacement[0][$i] = '';
preg_replace( $pattern[0][$i], $replacement[0][$i], $matches[0][$i] );
}
print_r($matches);
Your loop never runs because 0 >= 50 yields false. That said, what you're looking for is a map operation:
$matches = array_map(function($match) {
return preg_replace('~^http://w{1,3}~', '', $match);
}, $matches[0]);
print_r($matches);
preg_match_all has also problem. The period in regular expression matches any character.
$list = file_get_contents( 'file.txt' );
preg_match_all( "/http:\/\/([a-z]{1,24})\.([a-z^0-9-]{1,23})\.([a-z]{1,3})/", $list, $matches );
$pattern = "/http:\/\/(.[w-w]{1,3})/";
$replacement = '$1';
$matches[0] = preg_replace( $pattern, $replacement, $matches[0] );
print_r($matches);

Use a regex match as an array pointer

I want to replace some numbers in a string with the content of the array in the position which this number points to.
For example, replace "Hello 1 you are great", with "Hello myarray[1] you are great"
I was doing the next: preg_replace('/(\d+)/','VALUE: ' . $array[$1],$string);
But it does not work. How could I do it?
You should use a callback.
<?php
$str = 'Hello, 1!';
$replacements = array(
1 => 'world'
);
$str = preg_replace_callback('/(\d+)/', function($matches) use($replacements) {
if (array_key_exists($matches[0], $replacements)) {
return $replacements[$matches[0]];
} else {
return $matches[0];
}
}, $str);
var_dump($str); // 'Hello, world!'
Since you are using a callback, in the event that you actually want to use a number, you might want to encode your strings as {1} or something instead of 1. You can use a modified match pattern:
<?php
// added braces to match
$str = 'Hello, {1}!';
$replacements = array(
1 => 'world'
);
// added braces to regex
$str = preg_replace_callback('/\{(\d+)\}/', function($matches) use($replacements) {
if (array_key_exists($matches[1], $replacements)) {
return $replacements[$matches[1]];
} else {
// leave string as-is, with braces
return $matches[0];
}
}, $str);
var_dump($str); // 'Hello, world!'
However, if you are always matching known strings, you may want to use #ChrisCooney's solution because it offers less opportunity to screw up the logic.
The other answer is perfectly fine. I managed it this way:
$val = "Chris is 0";
// Initialise with index.
$adj = array("Fun", "Awesome", "Stupid");
// Create array of replacements.
$pattern = '!\d+!';
// Create regular expression.
preg_match($pattern, $val, $matches);
// Get matches with the regular expression.
echo preg_replace($pattern, $adj[$matches[0]], $val);
// Replace number with first match found.
Just offering another solution to the problem :)
$string = "Hello 1 you are great";
$replacements = array(1 => 'I think');
preg_match('/\s(\d)\s/', $string, $matches);
foreach($matches as $key => $match) {
// skip full pattern match
if(!$key) {
continue;
}
$string = str_replace($match, $replacements[$match], $string);
}
<?php
$array = array( 2 => '**', 3 => '***');
$string = 'lets test for number 2 and see 3 the result';
echo preg_replace_callback('/(\d+)/', 'replaceNumber', $string);
function replaceNumber($matches){
global $array;
return $array[$matches[0]];
}
?>
output
lets test for number ** and see *** the result

How do I use a part of preg_replace pattern as a variable?

function anchor($text)
{
return preg_replace('#\>\>([0-9]+)#','<span class=anchor>>>$1</span>', $text);
}
This piece of code is used to render page anchor.
I need to use the
([0-9]+)
part as a variable to do some maths to define the exact url for the href tag.
Thanks.
Use preg_replace_callback instead.
In php 5.3 +:
$matches = array();
$text = preg_replace_callback(
$pattern,
function($match) use (&$matches){
$matches[] = $match[1];
return '<span class=anchor><a href="#$1">'.$match[1].'</span>';
}
);
In php <5.3 :
global $matches;
$matches = array();
$text = preg_replace_callback(
$pattern,
create_function('$match','global $matches; $matches[] = $match[1]; return \'<span class=anchor><a href="#$1">\'.$match[1].\'</span>\';')
);

PHP preg_quote in replacement string

I use preg_replace and I want to include a url inside the replacement string. How do I quote that string? It appears preg_quote is only for the search pattern.
$replace = '\1'.addslashes($url).'\3'.addslashes($title).'\4';
Escapes are needed
addslashes is not sufficient
preg_quote escapes too much
See this demo.
As Mario commented you can use addcslashes($str, "\\$").
Unfortunately there is no generic way to do this, but addslashes should be enough in most cases.
For maximum safety you can use the ${1} syntax. E.g.
$replace = '${1}'.addslashes($url).'${3}'.addslashes($title).'${4}';
If you really want to be totally bulletproof, use a callback replacement function with preg_replace_callback(). The string returned from the callback function is used entirely as-is, so you don't have to worry about mixing replacement syntax with normal text.
Example with preg_replace_callback():
class URLReplacer {
public $pattern = '/my regex/';
public $url;
public $title;
function __construct($url, $title) {
$this->url = $url;
$this->title = $title;
}
function _callback($matches) {
return $matches[1].$url.$matches[3].$title.$matches[4];
}
function replace($subject) {
return preg_replace_callback($this->pattern, array($this, '_callback'), $subject);
}
}
$repl = new URLReplacer($url, $title);
$replaced = $repl->replace($subject);
You did not provide an example, so I compiled one on my own. The working solution I came up with is using a simple callback function:
$url = 'http://example.com/';
$title = 'Make it Complex \4';
$subject = 'Call \\4 " $me an url';
$pattern = '/(.*)an()( )(url)/';
$replace = function($m) use ($url, $title)
{
return "$m[1]$url$m[3]$title$m[4]";
};
$result = preg_replace_callback($pattern, $replace, $subject);
Result:
Call \4 " $me http://example.com/ Make it Complex \4url
The callback function is a so called anonymous function Docs which makes it easy to edit the code in place.
In case you need this more often you can put that into a function of your own, probably to make it more re-useable. You can even go to that far and create yourself your own pattern to replace subgroup matches and variables. For examle {\1} stands for the subpattern 1 match, {$2} for the second variable. Wrapping this into a function of it's own:
$patternf = function()
{
$values = func_get_args();
$mask = $values ? array_shift($values) : NULL;
return function($matches) use ($mask, $values)
{
$parts = preg_split('/({[\\\\\\$][0-9]{1,3}})/', $mask, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
foreach($parts as &$part)
if (preg_match('/^{([\\\\\\$])([0-9]{1,3})}$/', $part, $p))
$part = $p[1] == '\\' ? $matches[(int)$p[2]] : $values[$p[2]-1];
return implode('', $parts);
};
};
Would allow you to make the replacement more handy:
$replace = $patternf('{\1}{$1}{\3}{$2}{\4}', $url, $title);
$result = preg_replace_callback($pattern, $replace, $subject);
Demo. Wrapping this into a function of it's own:
function preg_replace_subst($pattern, $replace, $subject)
{
$values = func_get_args();
$pattern = array_shift($values);
$mask = array_shift($values);
$subject = array_shift($values);
$callback = function($matches) use ($mask, $values)
{
$parts = preg_split('/({[\\\\\\$][0-9]{1,3}})/', $mask, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
foreach($parts as &$part)
if (preg_match('/^{([\\\\\\$])([0-9]{1,3})}$/', $part, $p))
$part = $p[1] == '\\' ? $matches[(int)$p[2]] : $values[$p[2]-1];
return implode('', $parts);
};
return preg_replace_callback($pattern, $callback, $subject);
}
Would give it an easy interface:
$url = 'http://example.com/';
$title = 'Make it Complex \4';
$subject = 'Call \\4 " $me an url';
$pattern = '/(.*)an()( )(url)/';
$replace = '{\1}{$1}{\3}{$2}{\4}';
$result = preg_replace_subst($pattern, $replace, $subject, $url, $title);
But with many substitution variables it should be possible to pass them as an array probably otherwise it gets a bit lengthy.
Using the e modifier with preg_replace (and why it does not work)
When using the e modifier, the matches get replaced in the replace string and it then get's evaluated. As other variables do not get escaped, the matches do interfere with PHP variable substitution, which is dangerous:
$url = 'http://example.com/';
$title = 'Make it Complex \4';
$subject = 'Call me an url.';
$pattern = '/(.*)an()( )(url)/e';
$replace = '"$1{$url}$3{$title}$4"';
$result = preg_replace($pattern, $replace, $subject);
Outputs:
Call me http://example.com/ Make it Complex \4url.
As written, the first e-modifier example is broken because $ won't get escape in $subject, so PHP would have looked for unset variables. That's dangerous, too. I came up with a variant, it solves that issue but it can't handle double-quotes in the subject:
$url = 'http://example.com/';
$title = 'Make it Complex \4';
$subject = 'Call \\4 " $me an url';
$pattern = '/(.*)an()( )(url)/e';
$replace = "'\$1'.\$url.'\$3'.\$title.'$4'";
Output:
Call \4 \" $me http://example.com/ Make it Complex \4url
^ problem, not in input.
So not really fool-proof, that's why it needs the callback function because it gets the matching sub-patterns unquoted.
To make it obvious one is escaping any potential back-references in the $replacement parameter of preg_replace(), use a function:
function preg_quote_replacement($input) {
return addcslashes($input, '\\$');
}
In OP's case:
$subject = preg_replace(
$pattern,
'\1'.preg_quote_replacement($url).'\3'.preg_quote_replacement($title).'\4',
$subject
);
You should use Prepared Patterns. It works like Prepared Statements in SQL:
Pattern::inject("\1#url\2#url\3", ['url' => $input]);

Categories