How do I linkify urls in a string with php? - php

I have the following string:
"Look on http://www.google.com".
I need to convert it to:
"Look on http://www.google.com"
The original string can have more than 1 URL string.
How do I do this in php?
Thanks

You can use the following:
$string = "Look on http://www.google.com";
$string = preg_replace(
"~[[:alpha:]]+://[^<>[:space:]]+[[:alnum:]/]~",
"\\0",
$string);
PHP versions < 5.3 (ereg_replace) otherwise (preg_replace)

lib_autolink does a pretty good job, avoiding pitfalls like extra punctuation after the link and links inside HTML tags:
https://github.com/iamcal/lib_autolink

Have a look at regular expressions. You would then do something like:
$text = preg_replace('#(https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?)#', '$1', $text);

Small update from nowadays. Just a regex won't be enough. Urls could contain unicode characters, brackets, punctuation etc.
There is a Url highlight library that cover lots of edge cases.
Example:
<?php
use VStelmakh\UrlHighlight\UrlHighlight;
$urlHighlight = new UrlHighlight();
$urlHighlight->highlightUrls('Look on http://www.google.com or even google.com.');
// return: 'Look on http://www.google.com or even google.com.'

You will need to use regular expressions...
Something like this will help.
$result = preg_replace('/\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[A-Z0-9+&##\/%=~_|]/i', '\0', $text);

I found an example which allows for links that include ftp, https and others which seems to work fine for multiple URLs
how-to-detect-urls-in-text-and-convert-to-html-links-php-using-regular-expressions
<?php
// The Regular Expression filter
$pattern = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/";
//example text
$text="Example user text with a URL http://www.zero7web.com , second link http://www.didsburydesign.co.uk";
// convert URLs into Links
$text= preg_replace($pattern, "\\0", $text);
echo $text;
?>
Proabably a good idea to add nofollow to the link too is it's a user submitted value.

Try this...
<?
function link_it($text)
{
$text= preg_replace("/(^|[\n ])([\w]*?)([\w]*?:\/\/[\w]+[^ \,\"\n\r\t<]*)/is", "$1$2<a href=\"$3\" >$3</a>", $text);
$text= preg_replace("/(^|[\n ])([\w]*?)((www)\.[^ \,\"\t\n\r<]*)/is", "$1$2<a href=\"http://$3\" >$3</a>", $text);
$text= preg_replace("/(^|[\n ])([\w]*?)((ftp)\.[^ \,\"\t\n\r<]*)/is", "$1$2<a href=\"ftp://$3\" >$3</a>", $text);
$text= preg_replace("/(^|[\n ])([a-z0-9&\-_\.]+?)#([\w\-]+\.([\w\-\.]+)+)/i", "$1$2#$3", $text);
return($text);
}
$text = "ini link gue: http://sapua.com <br>
https://sapua.com <br>
anything1://www.sss.com <br>
dua www.google.com <br>
tiga http://www.google.com <br>
ftp.sapua.com <br>
someone#sapua.com
";
print link_it($text);
?>

I found this code at http://code.seebz.net/p/autolink-php/ and tweaked it a bit to recognize www.* as links. I am not very conversant with regular expressions but I think the two str_replace lines can be modified to one regexp
<?php
$text = 'First link is: www.example.com. Second link is http://example.com. Third link is https://example.com. Fourth link is http://www.example.com. Fifth link is www.example.com';
function autolink($str, $attributes=array()) {
$str = str_replace("http://www","www",$str);
$str = str_replace("https://www","www",$str);
$attrs = '';
foreach ($attributes as $attribute => $value) {
$attrs .= " {$attribute}=\"{$value}\"";
}
$str = ' ' . $str;
$str = preg_replace(
'`([^"=\'>])((http|https|ftp)://[^\s<]+[^\s<\.)])`i',
'$1<a href="$2"'.$attrs.'>$2</a>',
$str
);
$str = preg_replace(
'`([^"=\'>])((www).[^\s<]+[^\s<\.)])`i',
'$1<a href="http://$2"'.$attrs.'>$2</a>',
$str
);
$str = substr($str, 1);
return $str;
}
echo autolink($text);
?>

Correctly linkifying a URL is non-trivial. (See: http://www.codinghorror.com/blog/2008/10/the-problem-with-urls.html for more on why this is so.) I spent quite a bit of time on this and have come up with a pretty good solution to the problem (for both PHP and/or Javascript). See:
http://jmrware.com/articles/2010/linkifyurl/linkify.html

Checkout my linkify function, which uses preg_replace_callback (PHP 5.3 only). It supports http, email and twitter:
http://www.jasny.net/articles/linkify-turning-urls-into-clickable-links-in-php/
/**
* Turn all URLs in clickable links.
*
* #param string $value
* #param array $protocols http/https, ftp, mail, twitter
* #param array $attributes
* #param string $mode normal or all
* #return string
*/
function linkify($value, $protocols = array('http', 'mail'), array $attributes = array(), $mode = 'normal')
{
// Link attributes
$attr = '';
foreach ($attributes as $key => $val) {
$attr = ' ' . $key . '="' . htmlentities($val) . '"';
}
$links = array();
// Extract existing links and tags
$value = preg_replace_callback('~(<a .*?>.*?</a>|<.*?>)~i', function ($match) use (&$links) { return '<' . array_push($links, $match[1]) . '>'; }, $value);
// Extract text links for each protocol
foreach ((array)$protocols as $protocol) {
switch ($protocol) {
case 'http':
case 'https': $value = preg_replace_callback($mode != 'all' ? '~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(?<![\.,:])~i' : '~([^\s<]+\.[^\s<]+)(?<![\.,:])~i', function ($match) use ($protocol, &$links, $attr) { if ($match[1]) $protocol = $match[1]; $link = $match[2] ?: $match[3]; return '<' . array_push($links, '<a' . $attr . ' href="' . $protocol . '://' . $link . '">' . $link . '</a>') . '>'; }, $value); break;
case 'mail': $value = preg_replace_callback('~([^\s<]+?#[^\s<]+?\.[^\s<]+)(?<![\.,:])~', function ($match) use (&$links, $attr) { return '<' . array_push($links, '<a' . $attr . ' href="mailto:' . $match[1] . '">' . $match[1] . '</a>') . '>'; }, $value); break;
case 'twitter': $value = preg_replace_callback('~(?<!\w)[##](\w++)~', function ($match) use (&$links, $attr) { return '<' . array_push($links, '<a' . $attr . ' href="https://twitter.com/' . ($match[0][0] == '#' ? '' : 'search/%23') . $match[1] . '">' . $match[0] . '</a>') . '>'; }, $value); break;
default: $value = preg_replace_callback($mode != 'all' ? '~' . preg_quote($protocol, '~') . '://([^\s<]+?)(?<![\.,:])~i' : '~([^\s<]+)(?<![\.,:])~i', function ($match) use ($protocol, &$links, $attr) { return '<' . array_push($links, '<a' . $attr . ' href="' . $protocol . '://' . $match[1] . '">' . $match[1] . '</a>') . '>'; }, $value); break;
}
}
// Insert all link
return preg_replace_callback('/<(\d+)>/', function ($match) use (&$links) { return $links[$match[1] - 1]; }, $value);
}

Simple like that:
$text = "Atenção, isto é um link de teste";
$text = str_replace(
[' ', ',', '&', '&', 'á', 'ã', 'à', 'é', 'ê', 'í', 'ç', 'õ', 'ô', 'ó', 'ú', "'", ")", "(", "]", "["],
['-', '', '', '', 'a', 'a', 'a', 'e', 'e', 'i', 'c', 'o', 'o', 'o', 'u', '', "", "", "", ""],
$text
);
echo strtolower($text);
If you need more chars, add them in the str_replace.

Related

Replace word in text between Tags

i know that there are many similar question but none have what im looking for, i need to replace a word from a text between tags but the problem is that is not having in account spaces or other wors so it doesnt match,
I tried adding before comma the /b but is not working.
Code Example:
$content= preg_replace('#(?<=<p>)'.$value->data['target'].'(?=.*</p>)#', $value->data['string'], $content);
You can try to find the text between tags and replace it. You can try the following code:
function replace_content_between_tags(string $content, string $tagname, string $searchStr, string $replaceWith)
{
$pattern = "#<\s*?$tagname\b[^>]*>(.*?)</$tagname\b[^>]*>#s";
preg_match_all($pattern, $content, $matches);
foreach($matches[1] as $key => $match) {
if(strpos($match, $searchStr) !== false) {
$replaceContent = '<' . $tagname . '>' . $match . '</' . $tagname . '>';
$content = str_replace($replaceContent, $replaceWith, $content);
break ;
}
}
return $content;
}
$new_content = replace_content_between_tags($content, 'p', $value->data['target'], $value->data['string']);

How to refactor a function to use preg_replace_callback() with multiple regex strings and replacement strings in multiple arrays?

I am refactoring some legacy PHP code (version 5.5) and I need to refactor a function to use preg_replace_callback() from preg_replace() (this is because of a \e evaluation deprecation) however what is being passed into the only 2 preg_replace() calls are arrays of search patterns and arrays of replacements.
I am aware I need to pass in a callback function to preg_replace_callback() but what would be the way to go about this when each pattern is different?
function format($in, $options = 0)
{
if (!$options) {
$options = FORMAT_BREAKS | FORMAT_HTMLCHARS | FORMAT_CENSOR;
}
if ($options & FORMAT_CENSOR) {
if (!$this->replaces_loaded) {
$this->get_replaces();
}
if ($this->censor) {
$in = preg_replace($this->censor, '####', $in);
}
}
if ($options & FORMAT_MBCODE) {
$search = array(
'~(^|\s)([a-z0-9-_.]+#[a-z0-9-.]+\.[a-z0-9-_.]+)~i',
'~(^|\s)(http|https|ftp)://(\w+[^\s\[\]]+)~ise'
);
$replace = array(
'\\1[email]\\2[/email]',
'\'\\1[url]\' .wordwrap(\'\\2://\\3\', 1, \' \', 1) . \'[/url]\''
);
$brackets = (strpos($in, '[') !== false) && (strpos($in, ']') !== false);
if ($brackets) {
$b_search = array(
'~\[code](.*?)\[/code]~ise',
'~\[php](.*?)\[/php]~ise',
'~\[php=([0-9]+?)](.*?)\[/php]~ise',
'~\[img](http|https|ftp)://(.*?)\[/img]~ise',
'~\[url](.*?)\[/url]~ise',
'~\[url=(http|https|ftp)://(.+?)](.+?)\[/url]~ise'
);
$b_replace = array(
'\'[code]\' . function (array $matches){ return base64_encode(\'\\1\') . \'[/code]\'',
'\'[php]\' . base64_encode(\'\\1\') . \'[/php]\'',
'\'[php=\\1]\' . base64_encode(\'\\2\') . \'[/php]\'',
'\'[img]\' . wordwrap(\'\\1://\\2\', 1, \' \', 1) . \'[/img]\'',
'\'[url]\' . wordwrap(\'\\1\\2\', 1, \' \', 1) . \'[/url]\'',
'\'[url=\' . wordwrap(\'\\1://\\2\', 1, \' \', 1) . \']\\3[/url]\''
);
$search = array_merge($search, $b_search);
$replace = array_merge($replace, $b_replace);
error_log(print_r($replace));
}
$in = preg_replace($search, $replace, $in);
$brackets = (strpos($in, '[') !== false) && (strpos($in, ']') !== false); //We may have auto-parsed a URL, adding a bracket
}
$strtr = array();
if ($options & FORMAT_HTMLCHARS) {
$strtr['&'] = '&';
$strtr['"'] = '"';
$strtr['\''] = ''';
$strtr['<'] = '<';
$strtr['>'] = '>';
}
if ($options & FORMAT_BREAKS) {
$strtr["\n"] = "<br />\n";
}
if ($this->user['user_view_emoticons'] && ($options & FORMAT_EMOTICONS)) {
if (!$this->replaces_loaded) {
$this->get_replaces();
}
$strtr = array_merge($strtr, $this->emotes['replacement']);
}
$in = strtr($in, $strtr);
if (($options & FORMAT_MBCODE) && $brackets) {
$search = array(
'~\[(/)?([bi])]~i',
'~\[u]~i',
'~\[s]~i',
'~\[/[us]]~i',
'~\[url](h t t p|h t t p s|f t p) : / /(.+?)\[/url]~ise',
'~\[url=(h t t p|h t t p s|f t p) : / /(.+?)](.+?)\[/url]~ise',
'~\[email]([a-z0-9-_.]+#[a-z0-9-.]+\.[a-z0-9-_.]+)?\[/email]~i',
'~\[email=([^<]+?)](.*?)\[/email]~i',
'~\[img](h t t p|h t t p s|f t p) : / /(.*?)\[/img]~ise',
'~\[(right|center)](.*?)\[/\1]~is',
'~\[code](.*?)\[/code]~ise',
'~\[php](.*?)\[/php]~ise',
'~\[php=([0-9]+?)](.*?)\[/php]~ise',
'~\[color=(\S+?)](.*?)\[/color]~is',
'~\[font=(.+?)](.*?)\[/font]~is',
'~\[size=([0-9]+?)](.*?)\[/size]~is'
);
$replace = array(
'<\\1\\2>',
'<span style=\'text-decoration:underline\'>',
'<span style=\'text-decoration:line-through\'>',
'</span>',
'\'\' . str_replace(\' \', \'\', \'\\1://\\2\') . \'\'',
'\'\\3\'',
'\\1',
'\\2',
'\'<img src="\' . str_replace(\' \', \'\', \'\\1://\\2\') . \'" alt="\' . str_replace(\' \', \'\', \'\\1://\\2\') . \'" />\'',
'<div align="\\1">\\2</div>',
'$this->format_code(\'\\1\', 0)',
'$this->format_code(\'\\1\', 1)',
'$this->format_code(\'\\2\', 1, \'\\1\')',
'<span style=\'color:\\1\'>\\2</span>',
'<span style=\'font-family:\\1\'>\\2</span>',
'<span style=\'font-size:\\1ex\'>\\2</span>'
);
if ((substr_count($in, '[quote]') + substr_count($in, '[quote=')) == substr_count($in, '[/quote]')) {
$search[] = '~\[quote=(.+?)]~i';
$search[] = '~\[quote]~i';
$search[] = '~\[/quote]~i';
$replace[] = '<table style="width:90%; margin-left:5%; margin-right:5%;" border="0" cellpadding="3" cellspacing="0"><tr><td><b>\\1 ' . $this->lang->main_said . ':</b></td></tr><tr><td class="quote">';
$replace[] = '<table style="width:90%; margin-left:5%; margin-right:5%;" border="0" cellpadding="3" cellspacing="0"><tr><td><b>' . $this->lang->main_quote . ':</b></td></tr><tr><td class="quote">';
$replace[] = '</td></tr></table>';
}
$in = preg_replace_callback($search, $replace, $in);
$in = str_replace(array(' ', "\t", '&#'), array(' ', ' ', '&#'), $in);
}
return $in;
}
I tested trying to place an anonymous functions directly into the replacement arrays but was served the error: Object of class Closure could not be converted to string. But perhaps I may have gone about it incorrectly?
$replace = array(
'\\1[email]\\2[/email]',
"'\'\\1[url]\'" . function (array $matches) {return wordwrap($matches[1], $matches[2], $matches[3], $matches[4]); } . " \'[/url]\''"
);
Advice would be much appreciated.
Create an associative array where the keys contain regex patterns and the values contain the callbacks (no prepending/appending strings to the callbacks).
I am not going to rewrite that behemoth from my phone, so I'll demonstrate a single replacement.
Feed your array of patterns and callbacks to preg_replace_callback_array().
Code: (Demo)
$patternCallbacks = [
'~\[code](.*?)\[/code]~is' =>
function($m) {
return '[code]' . base64_encode($m[1]) . '[/code]';
},
// add more elements as needed...
];
echo preg_replace_callback_array(
$patternCallbacks,
'This is my [code]script[/code] to display'
);
Output:
This is my [code]c2NyaXB0[/code] to display
Edit, since you cannot use preg_replace_callback_array(), you will need to make iterated calls of preg_replace_callback().
Code: (Demo)
$patternCallbacks = [
'~\[code](.*?)\[/code]~is' =>
function($m) {
return '[code]' . base64_encode($m[1]) . '[/code]';
},
'~\[php(?:=\d+)?]\K(.*?)\[/php]~is' =>
function($m) {
return base64_encode($m[1]) . '[/php]';
},
];
$text = <<<TEXT
This is my [code]script[/code] to display.
It has [php]unnumbered tag
code[/php] and [php=8]numbered tag code[/php].'
TEXT;
foreach ($patternCallbacks as $pattern => $callback) {
$text = preg_replace_callback($pattern, $callback, $text);
}
echo $text;
Output:
This is my [code]c2NyaXB0[/code] to display.
It has [php]dW5udW1iZXJlZCB0YWcKIGNvZGU=[/php] and [php=8]bnVtYmVyZWQgdGFnIGNvZGU=[/php].

How set font color in php after a character

I have a string selected from database, and I want to change the font color of string after a '<', then back to initial color after '>'. Example: rowselected= abcd<efgh>lmno
How can I change the color of efgh?
I tried with
<?php between ('<', '>', rowselected) echo '<span style="color:red;">' . rowselected . '</span>' ?>
obviusly not work, but i'm searching a solution like this
A solution using a regex to get the matches on the string and then replace them:
$str = 'abcd<efgh>lmno';
preg_match_all('/<[\S]*?>/m', $str, $matches, PREG_PATTERN_ORDER);
$replacements = $needles = [];
foreach ($matches[0] as $match) {
$needles[] = $match;
$replacements[] = '<span style="color:red;">' . $match . '</span>';
}
echo str_replace($needles, $replacements, $str);
Result: abcd<span style="color:red;"><efgh></span>lmno
You can simply apply str_replace for <> sign. Like this way
$rowselected = 'abcd<efgh>lmno';
$rowselected = str_replace('<', '<<span style="color:red">', $rowselected);
$rowselected = str_replace('>', '</span>>', $row);
// result
// "abcd<<span style="color:red"</span>>efgh</span>>lmno"

Only replace words outside of HTML-tags

I would like to replace words outside of HTML-tags.
So if I got
Hello
and I want to replace "Hello" with "Bye" I would like to get this result:
Bye.
Well, I learned that I have to use a DOM-parser to achieve that.
So I used https://github.com/sunra/php-simple-html-dom-parser and included it.
Now I did
$test = $dom->find('text');
To get the text of the dom.
Now I can loop through the results:
foreach($test as $t) {
if (strpos($t->innertext,$word)!==false) {
$t->innertext = preg_replace(
'/\b' . preg_quote( $word, "/" ) . '\b/i',
"<a href='$url' target='$target' data-uk-tooltip title='$item->title'>\$0</a>",
$t->innertext,1
);
}
}
But unfortunately, if $item->title contains $word, the HTML-structure is smashed.
It looks like there was much confusion. According to the docs, $dom->find($tag) returns an array of all tags, but you are looking for a tag called text ?
Maybe you should try $test = $dom->find('a'); instead ?
Also in your code, it is not clear where the variables $url, $target and $item come from :
foreach($test as $t) {
if (strpos($t->innertext,$word)!==false) {
$t->innertext = preg_replace(
'/\b' . preg_quote( $word, "/" ) . '\b/i',
"<a href='$url' target='$target' data-uk-tooltip title='$item->title'>\$0</a>",
$t->innertext,1
);
}
}
This should work better:
foreach($test as $t) {
if (strpos($t->innertext,$word)!==false) {
$t->innertext = preg_replace(
'/\b' . preg_quote( $word, "/" ) . '\b/i',
"Replacement",
$t->innertext,1
);
}
}

How can I avoid adding href to an overlapping keyword in string?

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.

Categories