str_replace is replacing on one line, but not on another - php

I am running into a strange issue. I have the following code:
$foo = array(
"some" => array(
"foo" => "boohoo",
"bar" => "foobar"
),
"really" => array(
"foo" => "boohoo",
"bar" => "barfoo"
),
"strange" => array(
"foo" => "boohoo",
"bar" => "foobarfoo"
),
"occurences" => array(
"foo" => "boohoo",
"bar" => "barbaz"
)
);
$page = "";
foreach($foo as $bar)
{
$subj = $template->loadTemplate('foobar', true);
$str = "";
$str = str_replace("{foo}", $bar['foo'], $subj);
$str = str_replace("{bar}", $bar['bar'], $subj);
$page .= $str;
}
The issue here is that when the PHP Code is run, {bar} is replaced in my template but not {foo}. I switched the two str_replace lines around and I got a different result -- {foo} is replaced but {bar} isn't! I've also tried swapping it for preg_replace and nothing changed. For the record, the $template->loadTemplate() function performs no operations on the string loaded, it simply gets the template from a file.
My questions are: why does PHP behave in this fashion, and second, how can I overcome this limitation/bug?

You changing only one, because use same input string for both replaces:
$str = str_replace("{foo}", $bar['foo'], $subj);
$str = str_replace("{bar}", $bar['bar'], $subj);
Try this:
$str = str_replace("{foo}", $bar['foo'], $subj);
$str = str_replace("{bar}", $bar['bar'], $str);

As said CORRUPT you are replacing the strings voiding the previous command.
I'd add that str_replace supports Array() as parameter.
$str = str_replace(Array("{foo}","{bar}"), Array($bar['foo'], $bar['bar']) , $subj);

Related

PHP - preg_replace on an array not working as expected

I have a PHP array that looks like this..
Array
(
[0] => post: 746
[1] => post: 2
[2] => post: 84
)
I am trying to remove the post: from each item in the array and return one that looks like this...
Array
(
[0] => 746
[1] => 2
[2] => 84
)
I have attempted to use preg_replace like this...
$array = preg_replace('/^post: *([0-9]+)/', $array );
print_r($array);
But this is not working for me, how should I be doing this?
You've missed the second argument of preg_replace function, which is with what should replace the match, also your regex has small problem, here is the fixed version:
preg_replace('/^post:\s*([0-9]+)$/', '$1', $array );
Demo: https://3v4l.org/64fO6
You don't have a pattern for the replacement, or a empty string place holder.
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject)
Is what you are trying to do (there are other args, but they are optional).
$array = preg_replace('/post: /', '', $array );
Should do it.
<?php
$array=array("post: 746",
"post: 2",
"post: 84");
$array = preg_replace('/^post: /', '', $array );
print_r($array);
?>
Array
(
[0] => 746
[1] => 2
[2] => 84
)
You could do this without using a regex using array_map and substr to check the prefix and return the string without the prefix:
$items = [
"post: 674",
"post: 2",
"post: 84",
];
$result = array_map(function($x){
$prefix = "post: ";
if (substr($x, 0, strlen($prefix)) == $prefix) {
return substr($x, strlen($prefix));
}
return $x;
}, $items);
print_r($result);
Result:
Array
(
[0] => 674
[1] => 2
[2] => 84
)
There are many ways to do this that don't involve regular expressions, which are really not needed for breaking up a simple string like this.
For example:
<?php
$input = Array( 'post: 746', 'post: 2', 'post: 84');
$output = array_map(function ($n) {
$o = explode(': ', $n);
return (int)$o[1];
}, $input);
var_dump($output);
And here's another one that is probably even faster:
<?php
$input = Array( 'post: 746', 'post: 2', 'post: 84');
$output = array_map(function ($n) {
return (int)substr($n, strpos($n, ':')+1);
}, $input);
var_dump($output);
If you don't need integers in the output just remove the cast to int.
Or just use str_replace, which in many cases like this is a drop in replacement for preg_replace.
<?php
$input = Array( 'post: 746', 'post: 2', 'post: 84');
$output = str_replace('post: ', '', $input);
var_dump($output);
You can use array_map() to iterate the array then strip out any non-digital characters via filter_var() with FILTER_SANITIZE_NUMBER_INT or trim() with a "character mask" containing the six undesired characters.
You can also let preg_replace() do the iterating for you. Using preg_replace() offers the most brief syntax, but regular expressions are often slower than non-preg_ techniques and it may be overkill for your seemingly simple task.
Codes: (Demo)
$array = ["post: 746", "post: 2", "post: 84"];
// remove all non-integer characters
var_export(array_map(function($v){return filter_var($v, FILTER_SANITIZE_NUMBER_INT);}, $array));
// only necessary if you have elements with non-"post: " AND non-integer substrings
var_export(preg_replace('~^post: ~', '', $array));
// I shuffled the character mask to prove order doesn't matter
var_export(array_map(function($v){return trim($v, ': opst');}, $array));
Output: (from each technique is the same)
array (
0 => '746',
1 => '2',
2 => '84',
)
p.s. If anyone is going to entertain the idea of using explode() to create an array of each element then store the second element of the array as the new desired string (and I wouldn't go to such trouble) be sure to:
split on or : (colon, space) or even post: (post, colon, space) because splitting on : (colon only) forces you to tidy up the second element's leading space and
use explode()'s 3rd parameter (limit) and set it to 2 because logically, you don't need more than two elements

Array to string conversion error to replace two elements

I have this code:
<?php
$url_constructor = "http://myecommerce.dev/edit/article_name/article_id";
$cart_line_link = str_replace($url_constructor, array(
'article_id',
'article_name'
) , array(
$line['article_name'],
$line['article_id']
));
I need replace the /article_name and /article_id by the cart lines variables.
I want get a result like this example with article:
"http://myecommerce.dev/edit/blue-mug/1728"
The correct syntax of str_replace() is:
str_replace ($search, $replace, $subject);
So, try this:
$cart_line_link = str_replace(array(
'article_id',
'article_name'
), array(
$line['article_name'],
$line['article_id']
), $url_constructor);

Group values of array according to regex in PHP

I have an array that looks like this:
[0] => name {
[1] => abc
[2] => def
[3] => }
[4] =>
[5] => othername {
[6] => 123
[7] => 456
[8] => 789
[9] => }
[10] =>
As you can see each group (under each name) can have different amount of lines and different items in them but each group starts with the same regex syntax and ends with a closing } and a blank line after that.
I need to get an array for each group (for each name) recursively. I made a preg_match regex that will find each name line but I don't know how to make an array with that having also all the lines that are before the next name group.
So I want to obtain:
array(
array('name {', 'abc', 'def', '}'),
array('othername {', '123', '456', '789', '}')
)
How can I approach this? Thanks in advance.
Clicquot beat me but I'm not sure his works/is tested. Here's my solution
<?php
$array = array('0' => 'name {', '1' => 'abc', '2' => 'def', '3' => "}\n", '4' => "\n",
'5' => 'othername {',
'6' => '123',
'7' => '456',
'8' => '789',
'9' => '}',
'10' => "\n");
$string = array();
$count = 0;
foreach($array as $value){
$value = trim($value);
$pos = strpos($value, '{');
if ($pos !== false) {
$count++;
}
if(!empty($value)) {
$string[$count][] = $value;
}
}
print_r($string);
Maybe this code is what you asked for. But I'd never use it myself. If you try to build a code parser you shuold look for parsing-trees with a "grammar" or syntax analysis. It is part of parsing and compilers. The slide bellow shows how a compiler or interpreter can work. If you are only aiming to represent your data in order to reach some specific goal, maybe you can look at formats like json. You should always mention in one sentence what your overall goal is prior to asking a specific question.
(sorry for the German)
The issues of the code below are, that syntax variations can break the code, and you are highly voulnerable because the code changes your php variable contents concerning to the contents of your array.
If you are processing a file with code I do recommend you also to process it streight away, instead of first saving it to an array (which costs more memory).
<?php
$syntaxArray = array(
0 => 'name {',
1 => 'abc',
2 => 'def',
3 => '}',
4 => 'othername {',
5 => '123',
6 => '456',
7 => '789',
8 => '}'
);
$regex_open_array = '-(\w+)\s*{-';
$array_open = false;
foreach($syntaxArray as $line){
$matches = array();
if( preg_match($regex_open_array, $line, $matches) ){
if($array_open){
/*recursive array creations do not work - if that's what you need put it in the comments*/
throw new RuntimeException('An array can not be open while the previous array is not closed');
}
$array_open = true;
/*create an array variable
* ....
* isolate the variable name
*/
$currentArray = $matches[1];
/*you can either create a new variable in your php code with the array name or use a big array and use the name as key
* $big_array[$name] = array();
* using this solution every array name must be only used once.
* of course you can use a name twice if you first check prior to creating a new array if the key is already in use.
*/
/*creates a variable with the given name. Must not overwrite an other arrays name like $syntaxArray*/
$$currentArray = array();
}else if($line == '}'){
/*the closing string could be wrapped with trim() or matched with a regex but I followed your example*/
$array_open = false;
}else{
if(!$array_open){
throw new RuntimeException('There is not array currently open');
}
$arr = &$$currentArray;
$arr[] = $line;
}
}
var_dump($name);
var_dump($othername);
Outputs:
array(2) { [0]=> string(3) "abc" [1]=> string(3) "def" } array(3) { [0]=> string(3) "123" [1]=> string(3) "456" [2]=> string(3) "789" }
This isn't really a regex thing. What you want to do is iterate through the array, and check each item. You'd search through the value there for a '{' or a '}'. When you see an opening bracket, you'd set the key in your "condensed" array that you want to index into, and then check subsequent items for a closing bracket. When you see the closing bracket, you'd change the key that you're indexing into in your "condensed" array or whatever and begin anew. When the value isn't a bracket, append it. It'll look something like this (sorry for the bad PHP):
$big_array = array();
$temp_key = NULL;
foreach (array as $key => $value) {
if (strpo($value, '{')) {
$big_array[$key] = array();
$temp_key = $key;
} else if (strpos($value, '}')) {
$temp_key = NULL;
} else {
$big_array[$temp_key].append($value); // or whatever
}
}
This assumes that you having a matching open / close bracket structure , you'd want to add in a ton of error checking of course.

BBCode only use the first replacement but not the others

The code below will only use the first line in the array (which makes the text bold).
function bbcode($string) {
$codes = Array(
'/\[b\](.+?)\[\/b\]/' => '<b>\1</b>',
'/\[i\](.+?)\[\/i\]/' => '<i>\1</i>',
'/\[u\](.+?)\[\/u\]/' => '<u>\1</u>',
'/\[s\](.+?)\[\/s\]/' => '<s>\1</s>',
'/\[url=(.+?)\](.+?)\[\/url\]/' => '\2',
'/\[image=(.+?)\]/' => '<div class="centered"><img src="\1" alt=""></div>'
);
while($replace = current($codes)) {
$bbcode = preg_replace(key($codes), $replace, $string);
next($codes);
return stripslashes(nl2br($bbcode));
}
}
echo bbcode('[b]This text[/b] will be bold and also [b]this text[/b]. But when I use some other BBCodes, it will [u]not[/u] work as planned. Here's a [url=http://google.com/]link[/url] too which will not be replaced as planned.');
Output: This text will be bold and also this text. But when I use some other bbcode, it will [u]not[/u] work as planned. Here's a [url=http://google.com/]link[/url] too which will not be replaced as planned.
How can I do so that my code will search for the right BBCode in the array and replace it with the key?
preg_replace accepts arrays as well for patterns and replacements.
Use the notation $n in the replacement part instead of \n that is reserved for the pattern.
Here is what I'd do:
function bbcode($string) {
$codes = Array(
'/\[b\](.+?)\[\/b\]/' => '<b>$1</b>',
'/\[i\](.+?)\[\/i\]/' => '<i>$1</i>',
'/\[u\](.+?)\[\/u\]/' => '<u>$1</u>',
'/\[s\](.+?)\[\/s\]/' => '<s>$1</s>',
'/\[url=(.+?)\](.+?)\[\/url\]/' => '$2',
'/\[image=(.+?)\]/' => '<div class="centered"><img src="$1" alt=""></div>'
);
$bbcode = preg_replace(array_keys($codes), array_values($codes), $string);
return stripslashes(nl2br($bbcode));
}

PHP equivalent of Python's `str.format` method?

Is there an equivalent of Python str.format in PHP?
In Python:
"my {} {} cat".format("red", "fat")
All I see I can do in PHP natively is by naming the entries and using str_replace:
str_replace(array('{attr1}', '{attr2}'), array('red', 'fat'), 'my {attr1} {attr2} cat')
Is there any other PHP's native alternatives?
sprintf is the closest thing. It's the old-style Python string formatting:
sprintf("my %s %s cat", "red", "fat")
As PHP doesn't really have a proper alternative to str.format in Python, I decided to implement my very simple own which as most of the basic functionnalitites of the Python's one.
function format($msg, $vars)
{
$vars = (array)$vars;
$msg = preg_replace_callback('#\{\}#', function($r){
static $i = 0;
return '{'.($i++).'}';
}, $msg);
return str_replace(
array_map(function($k) {
return '{'.$k.'}';
}, array_keys($vars)),
array_values($vars),
$msg
);
}
# Samples:
# Hello foo and bar
echo format('Hello {} and {}.', array('foo', 'bar'));
# Hello Mom
echo format('Hello {}', 'Mom');
# Hello foo, bar and foo
echo format('Hello {}, {1} and {0}', array('foo', 'bar'));
# I'm not a fool nor a bar
echo format('I\'m not a {foo} nor a {}', array('foo' => 'fool', 'bar'));
The order doesn't matter,
You can omit the name/number if you want it to simply increment (the first {} matched will be transformed into {0}, etc),
You can name your parameters,
You can mix the three other points.
I know it's an old question, but I believe strtr with replace pairs deserves to be mentioned:
(PHP 4, PHP 5, PHP 7)
strtr — Translate characters or replace substrings
Description:
strtr ( string $str , string $from , string $to ) : string
strtr ( string $str , array $replace_pairs ) : string
<?php
var_dump(
strtr(
"test {test1} {test1} test1 {test2}",
[
"{test1}" => "two",
"{test2}" => "four",
"test1" => "three",
"test" => "one"
]
));
?>
this code would output:
string(22) "one two two three four"
Same output is generated even if you change the array items order:
<?php
var_dump(
strtr(
"test {test1} {test1} test1 {test2}",
[
"test" => "one",
"test1" => "three",
"{test1}" => "two",
"{test2}" => "four"
]
));
?>
string(22) "one two two three four"

Categories