I'm getting stuck at this problem, which is
I have an array like this:
$array = [
'name' => 'John',
'email' => john#gmail.com
];
And a string sample like this:
$string = 'Hi [[name]], your email is [[email]]';
The problem is obvious, replace name with John and email with john#gmail.com.
What i attempted:
//check if $string has [[ ]] pattern
$stringHasBrackets = preg_match_all('/\[\[(.*?)\]\]/i', $string, $matchOutput);
if ($stringHasBrackets) {
foreach ($matchOutput[1] as $matchOutputKey => $stringToBeReplaced) {
if (array_key_exists($stringToBeReplaced, $array)) {
$newString = preg_replace("/\[\[(.+?)\]\]/i",
$array[$stringToBeReplaced],
$string);
}
}
}
Which led me to a new string like this:
Hi john#gmail.com, your email is john#gmail.com
Makes sense because that's what the pattern is for, but not what I wanted.
How can I solve this? I thought of using a variable in the pattern but don't know how to do it. I've read about preg_replace_callback but also don't really know how to implement it.
Thanks!
You may use preg_replace_callback like this:
$array = ['name' => 'John', 'email' => 'john#gmail.com'];
$string = 'Hi [[name]], your email is [[email]]';
echo preg_replace_callback('/\[\[(.*?)]]/', function ($m) use ($array) {
return isset($array[$m[1]]) ? $array[$m[1]] : $m[0];
}, $string);
See PHP demo.
Details
'/\[\[(.*?)]]/' matches [[...]] substrings putting what is inside the brackets into Group 1
$m holds the match object
use ($array) allows the callback to access $array variable
isset($array[$m[1]]) checks if there is a value corresponding to the found key in the $array variable. If it is found, the value is returned, else, the found match is pasted back.
preg_replace accepts arrays as regex and replacement so you can use this simpler approach:
$array = ['name' => 'John', 'email' => 'john#gmail.com'];
$string = 'Hi [[name]], your email is [[email]]';
// create array of regex using array keys
$rearr = array_map(function($k) { return '/\[\[' . $k . ']]/'; },
array_keys($array));
# pass 2 arrays to preg_replace
echo preg_replace($rearr, $array, $string) . '\n';
Output:
Hi John, your email is john#gmail.com
PHP Code Demo
You can try this,
$array = ['name' => 'John', 'email' => 'john#gmail.com'];
$string = 'Hi [[name]], your email is [[email]]';
$stringHasBrackets = preg_match_all('/\[\[(.*?)\]\]/i', $string, $matchOutput);
if ($stringHasBrackets) {
$newString = $string;
foreach ($matchOutput[1] as $matchOutputKey => $stringToBeReplaced) {
if (array_key_exists($stringToBeReplaced, $array)) {
$newString = preg_replace("/\[\[$stringToBeReplaced\]\]/i", $array[$stringToBeReplaced], $newString);
}
}
echo $newString;
}
I think here more simply would be to use str_replace function, like:
$array = [
'name' => 'John',
'email' => 'john#gmail.com'
];
$string = 'Hi [[name]], your email is [[email]]';
$string = str_replace(array_map(function ($v) {return "[[{$v}]]";},
array_keys($array)), $array, $string);
echo $string;
Updated for $array to be "untouchable"
Related
There is a string like this:
$string = 'connector:rtp-monthly direction:outbound message:error writing data: xxxx yyyy zzzz date:2015-11-02 10:20:30';
This string is from user Input. So it will never have the same order. It's an input field which I need to split to build a DB query.
Now I would like to split the string based on words given in a array() which is like a mapper containing the words I need to find in the string. Looking like so:
$mapper = array(
'connector' => array('type' => 'string'),
'direction' => array('type' => 'string'),
'message' => array('type' => 'string'),
'date' => array('type' => 'date'),
);
Only the keys of the $mapper will be relevant. I've tried with foreach and explode like:
$parts = explode(':', $string);
But the problem is: There can be colons somewhere in the string so I don't need to explode there. I only need to explode if a colon is followed right after the mapper key. The mapper keys in this case are:
connector // in this case split if "connector:" is found
direction // untill "direction:" is found
message // untill "message:" is found
date // untill "date:" is found
But remember also, the user input can varey. So the string will always change ant the order of the string and the mapper array() will never be in the same order. So I'm not sure if explode is the right way to go, or if I should use a regex. And if so how to do it.
The desired result should be an array looking something like this:
$desired_result = array(
'connector' => 'rtp-monthly',
'direction' => 'outbound',
'message' => 'error writing data: xxxx yyyy zzzz',
'date' => '2015-11-02 10:20:30',
);
Help is much appreciated.
The trickier part of this is matching the original string. You can do it with Regex with the help of lookahead positive assertions:
$pattern = "/(connector|direction|message|date):(.+?)(?= connector:| direction:| message:| date:|$)/";
$subject = 'connector:rtp-monthly direction:outbound message:error writing data: xxxx yyyy zzzz date:2015-11-02 10:20:30';
preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER );
$returnArray = array();
foreach($matches as $item)
{
$returnArray[$item[1]] = $item[2];
}
In this Regex /(connector|direction|message|date):(.+?)(?= connector:| direction:| message:| date:|$)/, you're matching:
(connector|direction|message|date) - find a keyword and capture it;
: - followed by a colon;
(.+?) - followed by any character many times non greedy, and capture it;
(?= connector:| direction:| message:| date:|$) - up until the next keyword or the end of the string, using a non-capturing look-ahead positive assertion.
The result is:
Array
(
[connector] => rtp-monthly
[direction] => outbound
[message] => error writing data: xxxx yyyy zzzz
[date] => 2015-11-02 10:20:30
)
I didn't use the mapper array just to make the example clear, but you could use implode to put the keywords together.
Our aim isto make one array that contains the values of two arrays that we would extract from the string. It is neccesary to have two arrays since there are two string delimeters we wish to consider.
Try this:
$parts = array();
$large_parts = explode(" ", $string);
for($i=0; $i<count($large_parts); $i++){
$small_parts = explode(":", $large_parts[$i]);
$parts[$small_parts[0]] = $small_parts[1];
}
$parts should now contain the desired array
Hope you get sorted out.
Here you are. The regex is there to "catch" the key (any sequence of characters, excluding blank space and ":"). Starting from there, I use "explode" to "recursively" split the string. Tested ad works good
$string = 'connector:rtp-monthly direction:outbound message:error writing data date:2015-11-02';
$element = "(.*?):";
preg_match_all( "/([^\s:]*?):/", $string, $matches);
$result = array();
$keys = array();
$values = array();
$counter = 0;
foreach( $matches[0] as $id => $match ) {
$exploded = explode( $matches[ 0 ][ $id ], $string );
$keys[ $counter ] = $matches[ 1 ][ $id ];
if( $counter > 0 ) {
$values[ $counter - 1 ] = $exploded[ 0 ];
}
$string = $exploded[ 1 ];
$counter++;
}
$values[] = $string;
$result = array();
foreach( $keys as $id => $key ) {
$result[ $key ] = $values[ $id ];
}
print_r( $result );
You could use a combination of a regular expression and explode(). Consider the following code:
$str = "connector:rtp-monthly direction:outbound message:error writing data date:2015-11-02";
$regex = "/([^:\s]+):(\S+)/i";
// first group: match any character except ':' and whitespaces
// delimiter: ':'
// second group: match any character which is not a whitespace
// will not match writing and data
preg_match_all($regex, $str, $matches);
$mapper = array();
foreach ($matches[0] as $match) {
list($key, $value) = explode(':', $match);
$mapper[$key][] = $value;
}
Additionally, you might want to think of a better way to store the strings in the first place (JSON? XML?).
Using preg_split() to explode() by multiple delimiters in PHP
Just a quick note here. To explode() a string using multiple delimiters in PHP you will have to make use of the regular expressions. Use pipe character to separate your delimiters.
$string = 'connector:rtp-monthly direction:outbound message:error writing data: xxxx yyyy zzzz date:2015-11-02 10:20:30';
$chunks = preg_split('/(connector|direction|message)/',$string,-1, PREG_SPLIT_NO_EMPTY);
// Print_r to check response output.
echo '<pre>';
print_r($chunks);
echo '</pre>';
PREG_SPLIT_NO_EMPTY – To return only non-empty pieces.
Is it possible to using a regular expression, or otherwise create a function that will convert a string containing the php array of any form, in the real array, which can operate?
For Example:
$str = "array(1, array(2), array('key' => 'value'))";
$arr = stringArrayToArray($str);
Maybe there is already such an implementation of task?
Or do not bother, but simply to use eval()?
$arr = eval("return $str;");
You can try explode() function.
Try it.
<?php
$str = "do you love me";
$arr = explode(' ', $str);
print_r($arr);
?>
You can just replace the array( with a [ and ) with ]
as shown below:
$str = "array(1, array(2), array('key' => 'value'))";
$str = str_replace(" ","",str_replace(",","[",$str));
$str = str_replace(")","",str_replace("array(","[",$str));
echo $str;
$arr = explode("[",$str);
var_dump($arr);
?>
this the best I could get.
maybe this is duplicate but i did't find good solution.
I have array
$list = Array
(
[hi] => 0
[man] => 1
);
$string="hi man, how are you? man is here. hi again."
It should produce $final_string = "0 1, how are you? 1 is here. 0 again."
How can I achieve it with smart way? Many thanks.
Off of the top of my head:
$find = array_keys($list);
$replace = array_values($list);
$new_string = str_ireplace($find, $replace, $string);
Can be done in one line using strtr().
Quoting the documentation:
If given two arguments, the second should be an array in the form array('from' => 'to', ...). The return value is a string where all the occurrences of the array keys have been replaced by the corresponding values. The longest keys will be tried first. Once a substring has been replaced, its new value will not be searched again.
To get the modified string, you'd just do:
$newString = strtr($string, $list);
This would output:
0 1, how are you? 1 is here. 0 again.
Demo.
$text = strtr($text, $array_from_to)
See http://php.net/manual/en/function.strtr.php
preg_replace may be helpful.
<?php
$list = Array
(
'hi' => 0,
'man' => 1
);
$string="hi man, how are you? Man is here. Hi again.";
$patterns = array();
$replacements = array();
foreach ($list as $k => $v)
{
$patterns[] = '/' . $k . '/i'; // use i to ignore case
$replacements[] = $v;
}
echo preg_replace($patterns, $replacements, $string);
Just finished making this function. Basically it is suppose to look through a string and try to find any placeholder variables, which would be place between two curly brackets {}. It grabs the value between the curly brackets and uses it to look through an array where it should match the key. Then it replaces the curly bracket variable in the string with the value in the array of the matching key.
It has a few problems though. First is when I var_dump($matches) it puts puts the results in an array, inside an array. So I have to use two foreach() just the reach the correct data.
I also feel like its heavy and I've been looking over it trying to make it better but I'm somewhat stumped. Any optimizations I missed?
function dynStr($str,$vars) {
preg_match_all("/\{[A-Z0-9_]+\}+/", $str, $matches);
foreach($matches as $match_group) {
foreach($match_group as $match) {
$match = str_replace("}", "", $match);
$match = str_replace("{", "", $match);
$match = strtolower($match);
$allowed = array_keys($vars);
$match_up = strtoupper($match);
$str = (in_array($match, $allowed)) ? str_replace("{".$match_up."}", $vars[$match], $str) : str_replace("{".$match_up."}", '', $str);
}
}
return $str;
}
$variables = array("first_name"=>"John","last_name"=>"Smith","status"=>"won");
$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.';
echo dynStr($string,$variables);
//Would output: 'Dear John Smith, we wanted to tell you that you won the competition.'
I think for such a simple task you don't need to use RegEx:
$variables = array("first_name"=>"John","last_name"=>"Smith","status"=>"won");
$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.';
foreach($variables as $key => $value){
$string = str_replace('{'.strtoupper($key).'}', $value, $string);
}
echo $string; // Dear John Smith, we wanted to tell you that you won the competition.
I hope I'm not too late to join the party — here is how I would do it:
function template_substitution($template, $data)
{
$placeholders = array_map(function ($placeholder) {
return strtoupper("{{$placeholder}}");
}, array_keys($data));
return strtr($template, array_combine($placeholders, $data));
}
$variables = array(
'first_name' => 'John',
'last_name' => 'Smith',
'status' => 'won',
);
$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you have {STATUS} the competition.';
echo template_substitution($string, $variables);
And, if by any chance you could make your $variables keys to match your placeholders exactly, the solution becomes ridiculously simple:
$variables = array(
'{FIRST_NAME}' => 'John',
'{LAST_NAME}' => 'Smith',
'{STATUS}' => 'won',
);
$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you have {STATUS} the competition.';
echo strtr($string, $variables);
(See strtr() in PHP manual.)
Taking in account the nature of the PHP language, I believe that this approach should yield the best performance from all listed in this thread.
EDIT: After revisiting this answer 7 years later, I noticed a potentially dangerous oversight on my side, which was also pointed out by another user. Be sure to give them a pat on the back in the form of an upvote!
If you are interested in what this answer looked like before this edit, check out the revision history
I think you can greatly simplify your code, with this (unless I'm misinterpreting some of the requirements):
$allowed = array("first_name"=>"John","last_name"=>"Smith","status"=>"won");
$resultString = preg_replace_callback(
// the pattern, no need to escape curly brackets
// uses a group (the parentheses) that will be captured in $matches[ 1 ]
'/{([A-Z0-9_]+)}/',
// the callback, uses $allowed array of possible variables
function( $matches ) use ( $allowed )
{
$key = strtolower( $matches[ 1 ] );
// return the complete match (captures in $matches[ 0 ]) if no allowed value is found
return array_key_exists( $key, $allowed ) ? $allowed[ $key ] : $matches[ 0 ];
},
// the input string
$yourString
);
PS.: if you want to remove placeholders that are not allowed from the input string, replace
return array_key_exists( $key, $allowed ) ? $allowed[ $key ] : $matches[ 0 ];
with
return array_key_exists( $key, $allowed ) ? $allowed[ $key ] : '';
Just a heads up for future people who land on this page: All the answers (including the accepted answer) using foreach loops and/or the str_replace method are susceptible to replacing good ol' Johnny {STATUS}'s name with Johnny won.
Decent Dabbler's preg_replace_callback approach and U-D13's second option (but not the first) are the only ones currently posted I see that aren't vulnerable to this, but since I don't have enough reputation to add a comment I'll just write up a whole different answer I guess.
If your replacement values contain user-input, a safer solution is to use the strtr function instead of str_replace to avoid re-replacing any placeholders that may show up in your values.
$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.';
$variables = array(
"first_name"=>"John",
// Note the value here
"last_name"=>"{STATUS}",
"status"=>"won"
);
// bonus one-liner for transforming the placeholders
// but it's ugly enough I broke it up into multiple lines anyway :)
$replacement = array_combine(
array_map(function($k) { return '{'.strtoupper($k).'}'; }, array_keys($variables)),
array_values($variables)
);
echo strtr($string, $replacement);
Outputs: Dear John {STATUS}, we wanted to tell you that you won the competition.
Whereas str_replace outputs: Dear John won, we wanted to tell you that you won the competition.
This is the function that I use:
function searchAndReplace($search, $replace){
preg_match_all("/\{(.+?)\}/", $search, $matches);
if (isset($matches[1]) && count($matches[1]) > 0){
foreach ($matches[1] as $key => $value) {
if (array_key_exists($value, $replace)){
$search = preg_replace("/\{$value\}/", $replace[$value], $search);
}
}
}
return $search;
}
$array = array(
'FIRST_NAME' => 'John',
'LAST_NAME' => 'Smith',
'STATUS' => 'won'
);
$paragraph = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.';
// outputs: Dear John Smith, we wanted to tell you that you won the competition.
Just pass it some text to search for, and an array with the replacements in.
/**
replace placeholders with object
**/
$user = new stdClass();
$user->first_name = 'Nick';
$user->last_name = 'Trom';
$message = 'This is a {{first_name}} of a user. The user\'s {{first_name}} is replaced as well as the user\'s {{last_name}}.';
preg_match_all('/{{([0-9A-Za-z_]+)}}/', $message, $matches);
foreach($matches[1] as $match)
{
if(isset($user->$match))
$rep = $user->$match;
else
$rep = '';
$message = str_replace('{{'.$match.'}}', $rep, $message);
}
echo $message;
I have string like
$string = "string_key::%foo%:%bar%";
, and array of params
$params = array("foo" => 1, "bar" => 2);
How can I replace this params in $string pattern?
Expected result is
string_key::1:2
First, you need to rewrite the $params array:
$string = "string_key::%foo%:%bar%";
$params = array("foo" => 1, "bar" => 2);
foreach($params as $key => $value) {
$search[] = "%" . $key . "%";
$replace[] = $value;
}
After that, you can simply pass the arrays to str_replace():
$output = str_replace($search, $replace, $string);
View output on Codepad
On a personal note, I did this one:
$string = "string_key::%foo%:%bar%";
$params = array("%foo%" => 1, "%bar%" => 2);
$output = strtr($string, $params);
You do not have to do anything else because if there is some value in the array or the string is not replaced and overlooked.
Fast and simple method for pattern replacement.
I'm not sure what will be the fastest solution for you (it depends on the string size and the number of replacement values you will use).
I normally use this kind of function to perform parameterized replacements. It uses preg_replace_callback and a closure to perform the replacement for each percent enclosed word.
function replaceVariables($str, $vars)
{
// the expression '/%([a-z]+)%/i' could be used as well
// (might be better in some cases)
return preg_replace_callback('/%([^%]+)%/', function($m) use ($vars) {
// $m[1] contains the word inside the percent signs
return isset($vars[$m[1]]) ? $vars[$m[1]] : '';
}, $str);
}
echo replaceVariables("string_key::%foo%:%bar%", array(
"foo" => 1,
"bar" => 2
));
// string_key::1:2
Update
It's different from using str_replace() in cases whereby a percent enclosed value is found without a corresponding replacement.
This line determines the behaviour:
return isset($vars[$m[1]]) ? $vars[$m[1]] : '';
It will replace '%baz%' with an empty string if it's not part of the $vars. But this:
return isset($vars[$m[1]]) ? $vars[$m[1]] : $m[0];
Will leave '%baz%' in your final string.