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);
Related
What is the most efficient pattern to replace dots in dot-separated string to an array-like string e.g x.y.z -> x[y][z]
Here is my current code, but I guess there should be a shorter method using regexp.
function convert($input)
{
if (strpos($input, '.') === false) {
return $input;
}
$input = str_replace_first('.', '[', $input);
$input = str_replace('.', '][', $input);
return $input . ']';
}
In your particular case "an array-like string" can be easily obtained using preg_replace function:
$input = "x.d.dsaf.d2.d";
print_r(preg_replace("/\.([^.]+)/", "[$1]", $input)); // "x[d][dsaf][d2][d]"
From what I can understand from your question; "x.y.z" is a String and so should "x[y][z]" be, right?
If that is the case, you may want to give the following code snippet a try:
<?php
$dotSeparatedString = "x.y.z";
$arrayLikeString = "";
//HERE IS THE REGEX YOU ASKED FOR...
$arrayLikeString = str_replace(".", "", preg_replace("#(\.[a-z0-9]*[^.])#", "[$1]", $dotSeparatedString));
var_dump($arrayLikeString); //DUMPS: 'x[y][z]'
Hope it helps you, though....
Using a fairly simple preg_replace_callback() that simply returns a different replacement for the first occurrence of . compared to the other occurrences.
$in = "x.y.z";
function cb($matches) {
static $first = true;
if (!$first)
return '][';
$first = false;
return '[';
}
$out = preg_replace_callback('/(\.)/', 'cb', $in) . ((strpos('.', $in) !== false) ? ']' : ']');
var_dump($out);
The ternary append is to handle the case of no . to replace
already answered but you could simply explode on the period delimiter then reconstruct a string.
$in = 'x.y.z';
$array = explode('.', $in);
$out = '';
foreach ($array as $key => $part){
$out .= ($key) ? '[' . $part . ']' : $part;
}
echo $out;
Using the following code:
$text = "أطلقت غوغل النسخة المخصصة للأجهزة الذكية العاملة بنظام أندرويد من الإصدار “25″ لمتصفحها الشهير كروم.ولم تحدث غوغل تطبيق كروم للأجهزة العاملة بأندرويد منذ شهر تشرين الثاني العام الماضي، وهو المتصفح الذي يستخدمه نسبة 2.02% من أصحاب الأجهزة الذكية حسب دراسة سابقة. ";
$tags = "غوغل, غوغل النسخة, كروم";
$tags = explode(",", $tags);
foreach($tags as $k=>$v) {
$text = preg_replace("/\b{$v}\b/u","$0",$text, 1);
}
echo $text;
Will give the following result:
I love PHP">love PHP</a>, but I am facing a problem
Note that my text is in Arabic.
The way is to do all in one pass. The idea is to build a pattern with an alternation of tags. To make this way work, you must before sort the tags because the regex engine will stop at the first alternative that succeeds (otherwise 'love' will always match even if it is followed by 'php' and 'love php' will never be matched).
To limit the replacement to the first occurence of each word you can remove tag from the array once it has been found and you test if it is always present in the array inside the replacement callback function:
$text = 'I love PHP, I love love but I am facing a problem';
$tagsCSV = 'love, love php, facing';
$tags = explode(', ', $tagsCSV);
rsort($tags);
$tags = array_map('preg_quote', $tags);
$pattern = '/\b(?:' . implode('|', $tags) . ')\b/iu';
$text = preg_replace_callback($pattern, function ($m) use (&$tags) {
$mLC = mb_strtolower($m[0], 'UTF-8');
if (false === $key = array_search($mLC, $tags))
return $m[0];
unset($tags[$key]);
return '<a href="index.php?s=news&tag=' . rawurlencode($mLC)
. '">' . $m[0] . '</a>';
}, $text);
Note: when you build an url you must encode special characters, this is the reason why I use preg_replace_callback instead of preg_replace to be able to use rawurlencode.
If you have to deal with an utf8 encoded string, you need to add the u modifier to the pattern and you need to replace strtolower with mb_strtolower)
the preg_split way
$tags = explode(', ', $tagsCSV);
rsort($tags);
$tags = array_map('preg_quote', $tags);
$pattern = '/\b(' . implode('|', $tags) . ')\b/iu';
$items = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
$itemsLength = count($items);
$i = 1;
while ($i<$itemsLength && count($tags)) {
if (false !== $key = array_search(mb_strtolower($items[$i], 'UTF-8'), $tags)) {
$items[$i] = '<a href="index.php?s=news&tag=' . rawurlencode($tags[$key])
. '">' . $items[$i] . '</a>';
unset($tags[$key]);
}
$i+=2;
}
$result = implode('', $items);
Instead of calling preg_replace multiple times, call it a single time with a regexp that matches any of the tags:
$tags = explode(",", tags);
$tags_re = '/\b(' . implode('|', $tags) . ')\b/u';
$text = preg_replace($tags_re, '$0', $text, 1);
This turns the list of tags into the regexp /\b(love|love php|facing)\b/u. x|y in a regexp means to match either x or y.
I need some help with twitter hashtag, I need to extract a certain hashtag as string variable in PHP.
Until now I have this
$hash = preg_replace ("/#(\\w+)/", "#$1", $tweet_text);
but this just transforms hashtag_string into link
Use preg_match() to identify the hash and capture it to a variable, like so:
$string = 'Tweet #hashtag';
preg_match("/#(\\w+)/", $string, $matches);
$hash = $matches[1];
var_dump( $hash); // Outputs 'hashtag'
Demo
I think this function will help you:
echo get_hashtags($string);
function get_hashtags($string, $str = 1) {
preg_match_all('/#(\w+)/',$string,$matches);
$i = 0;
if ($str) {
foreach ($matches[1] as $match) {
$count = count($matches[1]);
$keywords .= "$match";
$i++;
if ($count > $i) $keywords .= ", ";
}
} else {
foreach ($matches[1] as $match) {
$keyword[] = $match;
}
$keywords = $keyword;
}
return $keywords;
}
As i understand you are saying that
in text/pargraph/post you want to show tag with hash sign(#) like this:- #tag
and in url you want to remove # sign because the string after # is not sended to server in request so i have edited your code and try out this:-
$string="www.funnenjoy.com is best #SocialNetworking #website";
$text=preg_replace('/#(\\w+)/','<a href=/hash/$1>$0</a>',$string);
echo $text; // output will be www.funnenjoy.com is best <a href=search/SocialNetworking>#SocialNetworking</a> <a href=/search/website>#website</a>
Extract multiple hashtag to array
$body = 'My #name is #Eminem, I am rap #god, #Yoyoya check it #out';
$hashtag_set = [];
$array = explode('#', $body);
foreach ($array as $key => $row) {
$hashtag = [];
if (!empty($row)) {
$hashtag = explode(' ', $row);
$hashtag_set[] = '#' . $hashtag[0];
}
}
print_r($hashtag_set);
You can use preg_match_all() PHP function
preg_match_all('/(?<!\w)#\w+/', $description, $allMatches);
will give you only hastag array
preg_match_all('/#(\w+)/', $description, $allMatches);
will give you hastag and without hastag array
print_r($allMatches)
You can extract a value in a string with preg_match function
preg_match("/#(\w+)/", $tweet_text, $matches);
$hash = $matches[1];
preg_match will store matching results in an array. You should take a look at the doc to see how to play with it.
Here's a non Regex way to do it:
<?php
$tweet = "Foo bar #hashTag hello world";
$hashPos = strpos($tweet,'#');
$hashTag = '';
while ($tweet[$hashPos] !== ' ') {
$hashTag .= $tweet[$hashPos++];
}
echo $hashTag;
Demo
Note: This will only pickup the first hashtag in the tweet.
I want to normalize (so canonicalize) a string into the normal form for names:
First letter of the name is uppercase
The difficulty by this is now to follow this rule with second and third name.
My method:
public function namilize($string)
{
$strings = explode(' ', $string);
foreach ($strings as $string) {
$string = ucfirst(strtolower($string));
}
$string = implode(' ', $strings);
return $string;
}
Somehow the
$string = ucfirst(strtolower($string));
fails.
What do I have to correct?
Is there a better way?
Regards
EDIT:
Hi,
thank you all for all the comments and answers.
I found another "modern" method:
public function namilize($string)
{
$string = mb_convert_case($string, MB_CASE_TITLE, mb_detect_encoding($string));
}
When I now would additionally add some regex for Mc and O's than it would be complete :)
public function namilize($name) {
$name = strtolower($name);
$normalized = array();
foreach (preg_split('/([^a-z])/', $name, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $word) {
if (preg_match('/^(mc)(.*)$/', $word, $matches)) {
$word = $matches[1] . ucfirst($matches[2]);
}
$normalized[] = ucfirst($word);
}
return implode('', $normalized);
}
Note that this will work for names like John O'Brian, James McManus, etc. For other names with prefixes like McManus, simply add the prefix to the preg_match(). Obviously, this runs the possibility of false positives, but no method is going to be 100% foolproof.
You have to pass the $string by reference, note the &:
public function namilize($string)
{
$strings = explode(' ', $string);
foreach ($strings as &$string) {
$string = ucfirst(strtolower($string));
}
$string = implode(' ', $strings);
return $string;
}
Or use the function suggested by #thetaiko ucwords($string)
The $string inside the foreach will only store the last iteration (or the last name). This doesn't really matter though because the variable in the foreach is never used for output. The implode just undoes what you did with the explode so you will end up with the exact same output as the input. I changed the variable names to be more descriptive in this example:
function namilize($name_in)
{
$a_names = explode(' ', $name_in); //explode string into array
foreach ($a_names as $name) {
$a_fullname[] = ucfirst(strtolower($name)); //build array of proper case names
}
$string = implode(' ', $a_fullname); //convert array into string
return $string;
}
I'm having trouble finding a correct regex to achieve what I want.
I have a sentence like that :
Hi, my name is Stan, you are welcome, hello.
and I would like to transform it like that :
[hi|hello|welcome], my name is [stan|jack] you are [hi|hello|welcome] [hi|hello|welcome].
Right now my regex is half working, because somes words are not replaced, and those replaced are deleting some characters
Here is my test code
<?php
$test = 'Hi, my name is Stan, you are welcome, hello.';
$words = array(
array('hi', 'hello', 'welcome'),
array('stan', 'jack'),
);
$result = $test;
foreach ($words as $group) {
if (count($group) > 0) {
$replacement = '[' . implode('|', $group) . ']';
foreach ($group as $word) {
$result = preg_replace('#([^\[])' . $word . '([^\]])#i', $replacement, $result);
}
}
}
echo $test . '<br />' . $result;
Any help will be appreciated
The regex you are using is overcomplicated. You simply need to use a regex substitution using regular brackets ():
<?php
$test = 'Hi, my name is Stan, you are welcome, hello.';
$words = array(
array('hi', 'hello', 'welcome'),
array('stan', 'jack'),
);
$result = $test;
foreach ($words as $group) {
if (count($group) > 0) {
$imploded = implode('|', $group);
$replacement = "[$imploded]";
$search = "($imploded)";
$result = preg_replace("/$search/i", $replacement, $result);
}
}
echo $test . '<br />' . $result;
Your regular expression:
'#([^\[])' . $word . '([^\]])#i'
matches one character before and after $word as well. And as they do, they replace it. So your replacement string needs to reference these parts, too:
'$1' . $replacement . '$2'
Demo
preg_replace supports array as parameter. No need to iterate with a loop.
$s = array("/(hi|hello|welcome)/i", "/(stan|jack)/i");
$r = array("[hi|hello|welcome]", "[stan|jack]");
preg_replace($s, $r, $str);
or dynamically
$test = 'Hi, my name is Stan, you are welcome, hello.';
$s = array("hi|hello|welcome", "stan|jack");
$r = array_map(create_function('$a','return "[$a]";'), $s);
$s = array_map(create_function('$a','return "/($a)/i";'), $s);
echo preg_replace($s, $r, $str);
//[hi|hello|welcome], my name is [stan|jack], you are [hi|hello|welcome], [hi|hello|welcome].