Replace variables from text with corresponding values - php

Let say I have the following string which I want to send as an email to a customer.
"Hello Mr/Mrs {{Name}}. You have subscribed for {{Service}} at {{Date}}."
And I have an array with the values that should be replaced
array(
'Name' => $customerName, //or string
'Service' => $serviceName, //or string
'Date' => '2015-06-06'
);
I can find all strings between {{..}} with this:
preg_match_all('/\{{(.*?)\}}/',$a,$match);
where $match is an array with values, But I need to replace every match with a corresponding value from an array with values
Note that the array with values contains a lot more values and the number of items in it or the keys sequence is not relevant to number of matches in string.

You can use preg_replace_callback and pass the array with the help of use to the callback function:
$s = "Hello Mr/Mrs {{Name}}. You have subscribed for {{Service}} at {{Date}} {{I_DONT_KNOW_IT}}.";
$arr = array(
'Name' => "customerName", //or string
'Service' => "serviceName", //or string
'Date' => '2015-06-06'
);
echo $res = preg_replace_callback('/{{(.*?)}}/', function($m) use ($arr) {
return isset($arr[$m[1]]) ? $arr[$m[1]] : $m[0]; // If the key is uknown, just use the match value
}, $s);
// => Hello Mr/Mrs customerName. You have subscribed for serviceName at 2015-06-06.
See IDEONE demo.
The $m[1] refers to what has been captured by the (.*?). I guess this pattern is sufficient for the current scenario (does not need unrolling as the strings it matches are relatively short).

You don't need to use a regex for that, you can do it with a simple replacement function if you change a little the array keys:
$corr = array(
'Name' => $customerName, //or string
'Service' => $serviceName, //or string
'Date' => '2015-06-06'
);
$new_keys = array_map(function($i) { return '{{' . $i . '}}';}, array_keys($corr));
$trans = array_combine($new_keys, $corr);
$result = strtr($yourstring, $trans);

Try
<?php
$str = "Hello Mr/Mrs {{Name}}. You have subscribed for {{Service}} at {{Date}}.";
$arr = array(
'Name' => 'some Cust', //or string
'Service' => 'Some Service', //or string
'Date' => '2015-06-06'
);
$replaceKeys = array_map(
function ($el) {
return "{{{$el}}}";
},
array_keys($arr)
);
$str = str_replace($replaceKeys, array_values($arr), $str);
echo $str;

Related

Return substring in array with regex

I have the following string, eg: 'Hello [owner], we could not contact by phone [phone], it is correct?'.
Regex would like to return in the form of array, all that is within []. Inside the bracket will only alpha characters.
Return:
$array = [
0 => '[owner]',
1 => '[phone]'
];
How should I proceed to have this return in php?
Try:
$text = 'Hello [owner], we could not contact by phone [phone], it is correct?';
preg_match_all("/\[[^\]]*\]/", $text, $matches);
$result = $matches[0];
print_r($result);
Output:
Array
(
[0] => [owner]
[1] => [phone]
)
I'm assuming that the end goal of all of this is that you want to replace the [placeholder]s with some other text, so use preg_replace_callback instead:
<?php
$str = 'Hello [owner], we could not contact by phone [phone], it is correct?';
$fields = [
'owner' => 'pedrosalpr',
'phone' => '5556667777'
];
$str = preg_replace_callback('/\[([^\]]+)\]/', function($matches) use ($fields) {
if (isset($fields[$matches[1]])) {
return $fields[$matches[1]];
}
return $matches[0];
}, $str);
echo $str;
?>
Output:
Hello pedrosalpr, we could not contact by phone 5556667777, it is correct?

How to get value from nested array using string

I have an array like this:
$temp = array( '123' => array( '456' => array( '789' => '0' ) ),
'abc' => array( 'def' => array( 'ghi' => 'jkl' ) )
);
I have a string like this:
$address = '123_456_789';
Can I get value of $temp['123']['456']['789'] using above array $temp and string $address?
Is there any way to achieve this and is it good practice to use it?
This is a simple function that accepts an array and a string address where the keys are separated by any defined delimiter. With this approach, we can use a for-loop to iterate to the desired depth of the array, as shown below.
<?php
function delimitArray($array, $address, $delimiter="_") {
$address = explode($delimiter, $address);
$num_args = count($address);
$val = $array;
for ( $i = 0; $i < $num_args; $i++ ) {
// every iteration brings us closer to the truth
$val = $val[$address[$i]];
}
return $val;
}
$temp = array("123"=>array("456"=>array("789"=>"hello world")));
$address = "123_456_789";
echo delimitArray($temp,$address,"_");
?>
Hello if string $address = '123_456_789'; is your case then you can use explode function to split the string by using some delimeter and you can output your value
<?php
$temp = array('123' => array('456' => array('789' => '0')),
'abc' => array('def' => array('ghi' => 'jkl')),
);
$address = '123_456_789';
$addr = explode("_", $address);
echo $temp[$addr[0]][$addr[1]][$addr[2]];
Using this array library you can easily get element value by either converting your string to array of keys using explode:
Arr::get($temp, explode('_', $address))
or replacing _ with . to take advantage of dot notation access
Arr::get($temp, str_replace('_', '.', $address))
Another benefit of using this method is that you can set default fallback value to return if element with given keys does not exists in array.

How to add a function to every key in array

I am using code igniter and I have an array which stores data sent from the form.
Sometimes user can type a text with a space, ex " John", or "James ", so I would like to use a trim() function to delete those whitespaces.
$data = array(
'name' => ' James',
'surname' => 'Doe ',
'street_name' => ' The Steet',
'number' => '45 flat 5/6 ',
'postcode' => ' 12-345 ',
'city' => ' New York ',
);
Instead of adding a trim to every single line in array I would prefer to add a function which will go key by key and use a trim function to every key in array. How to do that?
The array_map function applies a function to every element in an array. In this case, the function you want is trim.
$trimmedData = array_map('trim', $data);
Simplest vanilla code to understand the process
foreach($data as $key=>$val) {
$data[$key] = trim($val);
}
var_dump($data);
or use a function e.g. array_map()
$newarray = array_map("trim", $old_array);
A function?
function trim_whitespace_from_array( $input ){
foreach($input as $key=>$val) {
$input[$key] = trim($val);
}
return $input;
}
$data = array(
'name' => ' James',
'surname' => 'Doe ',
'street_name' => ' The Steet',
'number' => '45 flat 5/6 ',
'postcode' => ' 12-345 ',
'city' => ' New York ',
);
$trimmed = trim_whitespace_from_array($data);
Pretty much the same as what #donald123 said.
The function you want already exists. It's called array_map(). It allows one to call a custom function (that would be trim() in your case) on all the values from an array and produces an array from the values returned by that function; it preserves the keys.
$data = array_map('trim', $data);

How can I replace multiple strings within a string without overlapping results?

I'm trying to create common masks from a string like so:
012abc.d+e_fg~hijk => 012{start}.d+{middle}_fg~{end}jk
replace:
$arrFromTo = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
Instead I keep overlapping replacements and get something like this instead (using a loop of str_replace's):
012{{pre}art}.d+{mi{post}le}_fg~{end}jk
Because the st is found in the already replaced {start} and dd is found in {middle}.
How would you replace the following?
$str = 'abc.d+e_fg~hijk';
echo replace_vars($str); // Desired output: 012{start}.d+{middle}_fg~{end}kJ
I might misunderstand, but you don't seem to need regex for the replacing. They're simple, literal replacements.
$from = '012abc.d+e_fg~hijk';
$arrFromTo = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
$to = strtr($from, $arrFromTo); // 012{start}.d+{middle}_fg~{end}jk
strtr() is awesome. It takes a very readable input and it doesn't re-replace like your problem in the loop.
You can use preg_replace like this:
$str = '012abc.d+e_fg~hijk';
$arrFromTo = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
$reArr=array();
foreach($arrFromTo as $k=>$v){
$reArr['/' . $k . '(?![^{}]*})/'] = $v;
}
echo preg_replace(array_keys($reArr), array_values($reArr), $str);
//=> 012{start}.d+{middle}_fg~{end}jk
Core of this regex is this negative lookaead: (?![^{}]*})
Which avoid matching keys of array if it is enclosed in {...} since all the replacements are enclosed in {...}.
This will search the string for each replacement in order. If it finds one, it will split the string, and search the remainder of the string for any other replacements.
$str = '012abc.d+e_fg~hijk';
$rep = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
$searched = '';
foreach ($rep as $key => $r) {
if (strpos($str, $key) !== false) {
$searched .= substr($str, 0, strpos($str, $key)) . $r;
$str = substr($str, strpos($str, $key) + strlen($key));
}
}
$searched .= $str;
echo $searched; //012{start}.d+{middle}_fg~{end}jk
It will search and find them in the order that you have specified.

Turn text inside brackets to an array PHP

If I have a string that looks like this:
$myString = "[sometext][moretext][993][112]This is a long text";
I want it to be turned into:
$string = "This is a long text";
$arrayDigits[0] = 993;
$arrayDigits[1] = 112;
$arrayText[0] = "sometext";
$arrayText[1] = "moretext";
How can I do this with PHP?
I understand Regular Expressions is the solution. Please notice that $myString was just an example. There can be several brackets, not just two of each, as in my example.
Thanks for your help!
This is what I came up with.
<?php
#For better display
header("Content-Type: text/plain");
#The String
$myString = "[sometext][moretext][993][112]This is a long text";
#Initialize the array
$matches = array();
#Fill it with matches. It would populate $matches[1].
preg_match_all("|\[(.+?)\]|", $myString, $matches);
#Remove anything inside of square brackets, and assign to $string.
$string = preg_replace("|\[.+\]|", "", $myString);
#Display the results.
print_r($matches[1]);
print_r($string);
After that, you can iterate over the $matches array and check each value to assign it to a new array.
Try this:
$s = '[sometext][moretext][993][112]This is a long text';
preg_match_all('/\[(\w+)\]/', $s, $m);
$m[1] will contain all texts in the brakets, after this you could check type of each value. Also, you could check this using two preg_match_all: at first time with pattern /\[(\d+)\]/ (will return array of digits), in the second - pattern /\[([a-zA-z]+)\]/ (that will return words):
$s = '[sometext][moretext][993][112]This is a long text';
preg_match_all('/\[(\d+)\]/', $s, $matches);
$arrayOfDigits = $matches[1];
preg_match_all('/\[([a-zA-Z]+)\]/', $s, $matches);
$arrayOfWords = $matches[1];
For cases like yours you can make use of named subpatterns so to "tokenize" your string. With some little code, this can be made easily configurable with an array of tokens:
$subject = "[sometext][moretext][993][112]This is a long text";
$groups = array(
'digit' => '\[\d+]',
'text' => '\[\w+]',
'free' => '.+'
);
Each group contains the subpattern and it's name. They match in their order, so if the group digit matches, it won't give text a chance (which is necessary here because \d+ is a subset of \w+). This array can then turned into a full pattern:
foreach($groups as $name => &$subpattern)
$subpattern = sprintf('(?<%s>%s)', $name, $subpattern);
unset($subpattern);
$pattern = sprintf('/(?:%s)/', implode('|', $groups));
The pattern looks like this:
/(?:(?<digit>\[\d+])|(?<text>\[\w+])|(?<free>.+))/
Everything left to do is to execute it against your string, capture the matches and filter them for some normalized output:
if (preg_match_all($pattern, $subject, $matches))
{
$matches = array_intersect_key($matches, $groups);
$matches = array_map('array_filter', $matches);
$matches = array_map('array_values', $matches);
print_r($matches);
}
The matches are now nicely accessible in an array:
Array
(
[digit] => Array
(
[0] => [993]
[1] => [112]
)
[text] => Array
(
[0] => [sometext]
[1] => [moretext]
)
[free] => Array
(
[0] => This is a long text
)
)
The full example at once:
$subject = "[sometext][moretext][993][112]This is a long text";
$groups = array(
'digit' => '\[\d+]',
'text' => '\[\w+]',
'free' => '.+'
);
foreach($groups as $name => &$subpattern)
$subpattern = sprintf('(?<%s>%s)', $name, $subpattern);
unset($subpattern);
$pattern = sprintf('/(?:%s)/', implode('|', $groups));
if (preg_match_all($pattern, $subject, $matches))
{
$matches = array_intersect_key($matches, $groups);
$matches = array_map('array_filter', $matches);
$matches = array_map('array_values', $matches);
print_r($matches);
}
You could try something along the lines of:
<?php
function parseString($string) {
// identify data in brackets
static $pattern = '#(?:\[)([^\[\]]+)(?:\])#';
// result container
$t = array(
'string' => null,
'digits' => array(),
'text' => array(),
);
$t['string'] = preg_replace_callback($pattern, function($m) use(&$t) {
// shove matched string into digits/text groups
$t[is_numeric($m[1]) ? 'digits' : 'text'][] = $m[1];
// remove the brackets from the text
return '';
}, $string);
return $t;
}
$string = "[sometext][moretext][993][112]This is a long text";
$result = parseString($string);
var_dump($result);
/*
$result === array(
"string" => "This is a long text",
"digits" => array(
993,
112,
),
"text" => array(
"sometext",
"moretext",
),
);
*/
(PHP5.3 - using closures)

Categories