Using preg_replace_callback - php

How can I update this code :
$data = preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.strlen('$2').':\"$2\";'", $array);
with the preg_replace_callback function?
Thanks.

preg_replace_callback() is very similar to preg_replace(), except parameter 2 is a callable function that takes $matches as a parameter. Don't forget to remove the /e modifier, since we aren't executing anything.
$array = array(
's:1:"test";',
's:2:"one more";',
);
$data = preg_replace_callback('!s:(\d+):"(.*?)";!', function($matches) {
$string = $matches[2];
$length = strlen($string);
return 's:' . $length . ':"' . $string . '";';
}, $array);
print_r($data);
// Array ( [0] => s:4:"test"; [1] => s:8:"one more"; )

Related

Automatically add links to strings of keywords

In my article, I want to automatically add links to keywords.
My keywords array:
$keywords = [
0=>['id'=>1,'slug'=>'getName','url'=>'https://example.com/1'],
1=>['id'=>2,'slug'=>'testName','url'=>'https://example.com/2'],
2=>['id'=>3,'slug'=>'ign','url'=>'https://example.com/3'],
];
This is my code:
private function keywords_replace(string $string, array $key_array)
{
$array_first = $key_array;
$array_last = [];
foreach ($array_first as $key=>$value)
{
$array_last[$key] = [$key, $value['slug'], '<a target="_blank" href="' . $value['url'] . '" title="' . $value['slug'] . '">' . $value['slug'] . '</a>'];
}
$count = count($array_last);
for ($i=0; $i<$count;$i++)
{
for ($j=$count-1;$j>$i;$j--)
{
if (strlen($array_last[$j][1]) > strlen($array_last[$j-1][1]))
{
$tmp = $array_last[$j];
$array_last[$j] = $array_last[$j-1];
$array_last[$j-1] = $tmp;
}
}
}
$keys = $array_last;
foreach ($keys as $key)
{
$string = str_ireplace($key[1],$key[0],$string);
}
foreach ($keys as $key)
{
$string = str_ireplace($key[0],$key[2],$string);
}
return $string;
}
result:
$str = "<p>Just a test: getName testName";
echo $this->keywords_replace($str,$keywords);
like this:Just a test: getName testName
very import: If the string has no spaces, it will not match.Because I will use other languages, sentences will not have spaces like English. Like Wordpress key words auto link
I think my code is not perfect,Is there a better algorithm to implement this function? Thanks!
You can use array_reduce and preg_replace to replace all occurrences of the slug words in your string with the corresponding url values:
$keywords = [
0=>['id'=>1,'slug'=>'getName','url'=>'https://www.getname.com'],
1=>['id'=>2,'slug'=>'testName','url'=>'https://www.testname.com'],
2=>['id'=>3,'slug'=>'ign','url'=>'https://www.ign.com'],
];
$str = "<p>Just a test: getName testName";
echo array_reduce($keywords, function ($c, $v) { return preg_replace('/\\b(' . $v['slug'] . ')\\b/', $v['url'], $c); }, $str);
Output:
<p>Just a test: https://www.getname.com https://www.testname.com
Demo on 3v4l.org
Update
To change the text into links, you need to use this:
echo array_reduce($keywords,
function ($c, $v) {
return preg_replace('/\\b(' . $v['slug'] . ')\\b/',
'$1', $c);
},
$str);
Output:
<p>Just a test: getName testName
Updated demo
Update 2
Because some of the links that are being substituted include words that are also values of slug, it's necessary to do all the replacements at once using the array format of strtr. We build an array of patterns and replacements using array_column, array_combine and array_map, then pass that to strtr:
$reps = array_combine(array_column($keywords, 'slug'),
array_map(function ($k) { return '' . $k['slug'] . ''; }, $keywords
));
$newstr = strtr($str, $reps);
New demo
First you need to change structure of array to key/value using loop that result stored in $newKeywords. Then using preg_replace_callback() select every word in string and check that it exist in key of array. If exist, wrap it in anchor tag.
$newKeywords = [];
foreach ($keywords as $keyword)
$newKeywords[$keyword['slug']] = $keyword['url'];
$newStr = preg_replace_callback("/(\w+)/", function($m) use($newKeywords){
return isset($newKeywords[$m[0]]) ? "<a href='{$newKeywords[$m[0]]}'>{$m[0]}</a>" : $m[0];
}, $str);
Output:
<p>Just a test: <a href='https://www.getname.com'>getName</a> <a href='https://www.testname.com'>testName</a></p>
Check result in demo
My answer uses preg_replace as does Nick's above.
It relies on the patterns and replacements being equally sized arrays, with corresponding patterns and replacements.
Word boundaries need to be respected, which I doubt you can do with a simple string replacement.
<?php
$keywords = [
0=>['id'=>1,'slug'=>'foo','url'=>'https://www.example.com/foo'],
1=>['id'=>2,'slug'=>'bar','url'=>'https://www.example.com/bar'],
2=>['id'=>3,'slug'=>'baz','url'=>'https://www.example.com/baz'],
];
foreach ($keywords as $item)
{
$patterns[] = '#\b(' . $item['slug'] . ')\b#i';
$replacements[] = '$1';
}
$html = "<p>I once knew a barbed man named <i>Foo</i>, he often visited the bar.</p>";
print preg_replace($patterns, $replacements, $html);
Output:
<p>I once knew a barbed man named <i>Foo</i>, he often visited the bar.</p>
This is my answer: thanks for #Nick
$content = array_reduce($keywords , function ($c, $v) {
return preg_replace('/(>[^<>]*?)(' . $v['slug'] . ')([^<>]*?<)/', '$1$2$3', $c);
}, $str);

Warnings in Regular Expression with Posix Collating Elements

I am trying to run following regular expression based function in php where in the end am returning the output.
function vg_excerpt_more( $output ) {
$string = $output;
$pattern_auto_excerpt = '#([...]</p>)$#';
$pattern_manual_excerpt = '#(</p>)$#';
$replacement = ' [Continue...]</p>';
if ( preg_match( $pattern_auto_excerpt, $string ) ) {
$pattern = $pattern_auto_excerpt;
} else if ( preg_match( $pattern_manual_excerpt, $string ) ) {
$pattern = $pattern_manual_excerpt;
}
$output = preg_replace( $pattern, $replacement, $string );
return $output;
}
add_filter( 'the_excerpt', 'vg_excerpt_more' );
add_filter( 'excerpt_more', 'vg_excerpt_more' );
Well, the string could either end in [...]</p> OR </p> so I have to check the two cases.
The problem is, it is throwing warnings as -
WARNING: PREG_MATCH(): COMPILATION FAILED: POSIX COLLATING ELEMENTS
ARE NOT SUPPORTED AT OFFSET 1 in - 'preg_match( $pattern_auto_excerpt,
$string )'
and
WARNING: PREG_REPLACE(): EMPTY REGULAR EXPRESSION in - '$output =
preg_replace( $pattern, $replacement, $string );'
EDIT:
After useful replies by #user1852180 I moved ahead and did this -
function vg_excerpt_more( $output ) {
$string = $output;
$pattern = '';
// $pattern_auto_excerpt = '#(\[...\]</p>)$#';
$pattern_auto_excerpt = '#(\[(?:\.|…)+\])#';
$pattern_manual_excerpt = '#(</p>)$#';
$replacement = ' [Continue...]</p>';
if ( preg_match( $pattern_auto_excerpt, $string ) ) {
$pattern = '#(\[(?:\.|…)+\]</p>)$#';
if ( preg_match( $pattern, $string ) ) {
return preg_replace( $pattern, $replacement, $string ) . "Dummy2";
}
} else if ( preg_match( $pattern_manual_excerpt, $string ) ) {
$pattern = $pattern_manual_excerpt;
return preg_replace( $pattern, $replacement, $string ) . "Dummy";
}
return $output;
}
add_filter( 'the_excerpt', 'vg_excerpt_more' );
add_filter( 'excerpt_more', 'vg_excerpt_more' );
But am still seeing [...] in the frontend along with the replacement.
PS. It also never prints 'Dummy2', always 'Dummy'.
You need to escape the brackets in the first pattern, and the dot:
$pattern_auto_excerpt = '#(\[(?:\.|…)+\]</p>)$#';
You don't need to use the if/else to check if it has [...], let regex check that with the question mark:
function vg_excerpt_more( $output ) {
$pattern = '#(?:\[(?:\.|…)+\])?</p>$#';
$replacement = ' [Continue...]</p>';
return preg_replace( $pattern, $replacement, $output );
}

Only reverse the direction of consecutive Hebrew words in a string including non-Hebrew characters

I am trying to reverse a string containing Hebrew from RTL to LTR, but my coding attempt is reversing brackets as well. strrev() didn't work because it does not actually work for UTF8 strings. So I wrote a custom function, below is my code:
$str = 'תירס גדול A-10 (פרי גליל)';
function utf8_strrev($str)
{
$arr = '';
$words = explode(" ", $str);
$start_tag = '(';
$end_tag = ')';
foreach ($words as $word)
{
if (preg_match("/\p{Hebrew}/u", $word))
{
preg_match_all('/./us', $word, $ar);
echo print_r($ar[0]);
echo '<br>';
$arr = join('', array_reverse($ar[0])) . " " . $arr;
} else
{
preg_match_all('/./us', $word, $ar);
$arr = join('', $ar[0]) . " " . $arr;
}
}
return $arr;
}
OUTPUT :
)לילג ירפ( A-10 לודג סרית
what it should be:
(לילג ירפ) A-10 לודג סרית
Found this function in the comments on the docs that KoenHoeijmakers posted. I tested it, but I don't read Hebrew so it is hard for me to tell if it's working correctly.
function utf8_strrev($str){
preg_match_all('/./us', $str, $ar);
return join('',array_reverse($ar[0]));
}
Edit
Based on reading your question again, I think this works as you need it to?
function utf8_strrev($str)
{
$arr = '';
$words = explode(" ", $str);
$start_tag = '(';
$end_tag = ')';
foreach ($words as $word)
{
if (preg_match("/\p{Hebrew}/u", $word))
{
preg_match_all('/./us', $word, $ar);
$arr = join('', array_reverse($ar[0])) . " " . $arr;
} else
{
preg_match_all('/./us', $word, $ar);
$arr = join('', $ar[0]) . " " . $arr;
}
}
return preg_replace(array('/\)(.)/','/(.)\(/','/\}(.)/','/(.)\{/'),array('($1','$1)','{$1','$1}'),$arr);
}
$str='תירס גדול A-10 {פרי גליל}';
echo utf8_strrev($str);
Outut
{לילג ירפ} A-10 לודג סרית
Again, I don't read Hebrew, but hopefully it answers your question.
Note The reason I used preg_replace instead of str_replace is because the string replace method was giving me issues text like ( somthing something ( or ) something something )
I don't speak/read Hebrew, but this seems to work as desired.
I generally dislike nesting preg_ calls inside preg_ calls, but in this case, splitting the unicode characters into an array then reversing the elements prevents the need to bother with mb_encodings.
Code: (Demo)
$str = 'תירס גדול A-10 (פרי גליל)';
echo preg_replace_callback(
"/\p{Hebrew}+(?: \p{Hebrew}+)*/u",
fn($m) => implode(
array_reverse(
preg_split('~~u', $m[0], 0, PREG_SPLIT_NO_EMPTY)
)
),
$str
);
Output:
סרית A-10 (לילג ירפ)
The preg_replace_callback() pattern isolates consecutive space-delimited Hebrew words, then the anonymous function splits the multibyte letters into individual array elements, before reversing their order and joining the elements back into a single mutated string.

save a value in a preg function

I have this code:
<?php
$array = array();
$test = 'this is a #test';
$regex = "#(\#.+)#";
$test = preg_replace($regex, '<strong>$1</strong>', $test);
echo $test;
?>
and I would like to do: $array[] = $1
Does anyone have a suggestion, please?
If you use PHP ≥ 5.3.0 you can use an anonymous function and preg_replace_callback. First the callback:
$array = array();
$callback = function ($match) use (&$array) {
$array[] = $match[1];
return '<strong>'.$match[1].'</strong>';
};
$input = 'this is a #test';
$regex = '/(#.*)/';
$output = preg_replace_callback($regex, $callback, $input);
echo "Result string:\n", $output, "\n";
echo "Result array:\n";
print_r($array);
Results in:
Result string:
this is a <strong>#test</strong>
Result array:
Array
(
[0] => #test
)
Before PHP 5.3.0 you can only use create_function or any function defined somewhere else in the code. Both of them cannot access the local variable $array defined in the parent scope of $callback. In this case you would either have to use a global variable for $array (ugh!) or define the function inside a class and make $array a member of that class.
In PHP 4 >= 4.0.5, PHP 5, use preg_replace_callback with global variable.
PHP code:
$array = array();
$input = 'this is a #test';
$regex = '/(#\w*)/';
$output = preg_replace_callback(
$regex,
create_function(
'$match', 'global $array;
$array[] = $match[1]; return "<strong>" . $match[1] . "</strong>";'),
$input);
echo "Result string:\n", $output, "\n\n";
echo "Result array:\n";
print_r($array);
Output:
Result string:
this is a <strong>#test</strong>
Result array:
Array
(
[0] => #test
)
Demo:
Click here.
You can use:
preg_match($regex, $test, $matches);
$my_array = $matches[1];

Could you tell how to replace by regular expression

Could you tell how to replace string by preg-replace (need regular expression):
/user/{parent_id}/{action}/step/1
At the equivalent values of an array:
array('parent_id'=>32, 'action'=>'some');
To make:
/user/32/some/step/1
Addition
This is a typical problem, so I probably will not know what the names of variables come
You can use str_replace
For example:
str_replace(array("{parent_id}", "{action}"), array(32, 'some'), "/user/{parent_id}/{action}/step/1");
$arr = array('parent_id'=>32, 'action'=>'some');
$out = str_replace(array_keys($arr),array_values($arr),$in);
no need for regexps!
Say you have:
$arr = array('parent_id'=>32, 'action'=>'some');
$in = '/usr/{parent_id}/{action}/step/1';
This will replace the braces:
function bracelize($str) {
return '{' . $str . '}';
}
$search = array_map('bracelize', array_keys($arr));
$out = str_replace($search, $arr, $in);
Or if you are using PHP >= 5.3 you can use lambdas:
$search = array_map(function ($v) { return '{'.$v.'}';}, array_keys($arr));
$out = str_replace($search, $arr, $in);
$s = '/user/{parent_id}/{action}/step/1';
$replacement = array('parent_id'=>32, 'action'=>'some');
$res = preg_replace(array('/\\{parent_id\\}/', '/\\{action\\}/'), $replacement, $s);
Of course, you could just as well use str_replace (in fact, you ought to).
<?php
$string = '/usr/{parent_id}/{action}/step/1';
$pattern = array('#{parent_id}#', '#{action}#');
$values = array('32', 'some');
echo preg_replace($pattern, $values, $string);
?>
If your problem is not more complicated than this, i would recommend changing preg_replace to str_replace though.
EDIT: I see you don't know the variable names in advance. In which case you could do something like this.
<?php
function wrap_in_brackets(&$item)
{
$item = '{' . $item . '}';
return true;
}
$string = '/usr/{parent_id}/{action}/step/1';
$variables = array('parent_id' => 32, 'action' => 'some');
$keys = array_keys($variables);
array_walk($keys, 'wrap_in_brackets');
echo str_replace($keys, array_values($variables), $string);
?>
Expanding on mvds' answer:
$in = 'user/{parent_id}/{action}/step/1';
$arr = array('{parent_id}' => 32, '{action}' => 'some');
$out = str_replace(array_keys($arr), $arr, $in);
Or:
$in = 'user/{parent_id}/{action}/step/1';
$arr = array('parent_id' => 32, 'action' => 'some');
$arr[] = '';
$find = array_merge(array_keys($arr), array('{', '}'));
$out = str_replace($find, $arr, $in);

Categories