Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PHP function to turn all URLs in clickable links
<?php
/**
* Turn all URLs in clickable links.
*
* @param string $value
* @param array $protocols http/https, ftp, mail, twitter
* @param array $attributes
* @return string
*/
public function linkify($value, $protocols = array('http', 'mail'), array $attributes = array())
{
// 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('~(?:(https?)://([^\s<]+)|(www\.[^\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('~' . preg_quote($protocol, '~') . '://([^\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);
}
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
@jasny

This comment has been minimized.

Copy link
Owner Author

commented Jun 28, 2015

Turning links like www.example.com and http://twitter.com into clickable links. Sounds like an easy task, right? We’ll there are a few problems that might arise, especially if the text is already HTML formatted.

This function first takes out all potential dangers, by extracting links and tags and replacing them with a placeholder. It than extracts all URLs and replaces them with a placeholder, storing the full HTML link. At the end it replaces all placeholders with the links and tags.

@Hackmastr

This comment has been minimized.

Copy link

commented Aug 27, 2015

Amazing function! Thanks a ton! I was having a headache trying to make clickable text.

@cferdinandi

This comment has been minimized.

Copy link

commented Sep 10, 2015

@jasny Will this detect (and skip) urls that are image sources? Example: <img src="http://someurl.com">

@davidgorges

This comment has been minimized.

Copy link

commented Sep 10, 2015

Very helpful, thank you!
Quick note: the phpDoc comment has an additional parameter $mode that does not exist in the function.

@acoustika

This comment has been minimized.

Copy link

commented Sep 28, 2015

Hmmm, trying to make this work. But isn't Attr being overwritten in each iterattion so I can only have ONE attribute? og how is it supposed to be input if I wanna have both target="_blank" and rel="nofollow" in my links?

@fabiandev

This comment has been minimized.

Copy link

commented Mar 1, 2016

@acoustika I think it may just have been a typo.
Use .= instead of = in the for loop to support multiple attributes:

// Link attributes
$attr = '';
foreach ($attributes as $key => $val) {
    $attr .= ' ' . $key . '="' . htmlentities($val) . '"';
}
@doctorconceptual

This comment has been minimized.

Copy link

commented Oct 12, 2016

this is doing incorrect linking of email addresses with dots.

@acarlson101

This comment has been minimized.

Copy link

commented Dec 24, 2016

Hello jasny,

This code is very helpful. Is it MIT licensed for open source use?

@will83

This comment has been minimized.

Copy link

commented Jun 14, 2017

I have problem with accented characters on links. The output of the API is ok, but after linkify(), all my accented characters looks like �

@mhm9002

This comment has been minimized.

Copy link

commented Nov 20, 2017

You're wonderful!

@willemijns

This comment has been minimized.

Copy link

commented Jan 30, 2018

Hey folks,

What is the input variable ?
How to place the output in anorther variable ?

@umarleadz

This comment has been minimized.

Copy link

commented Feb 20, 2018

You did it exactly what I need thank you so much.

have a blessing day

@phasornc

This comment has been minimized.

Copy link

commented Jun 20, 2018

If the URL is in parentheses (www.example.com) this code will include the trailing parenthesis in the URL giving you this (www.example.com)

@papanipapi

This comment has been minimized.

Copy link

commented Nov 8, 2018

Thank you. It really works.

@mynukeviet

This comment has been minimized.

Copy link

commented Feb 13, 2019

@jasny Will this detect (and skip) urls that are image sources? Example: <img src="http://someurl.com">

Me too :)

@bradycharron

This comment has been minimized.

Copy link

commented Mar 20, 2019

Hello jasny,

This code is very helpful. Is it MIT licensed for open source use?

I am also curious about the licensing for this code.

@AryanKumar

This comment has been minimized.

Copy link

commented May 11, 2019

Well, it`s a mind-boggling function and very useful. But I face issues here please anyone can suggest http://prntscr.com/nn9bv9.

In case: mailto I am getting any number along with URL like this <49>aryan4983@gmail.com. ScreenCast:- http://prntscr.com/nn9bv9

@AryanKumar

This comment has been minimized.

Copy link

commented May 11, 2019

write like this:-
aryan4983@gmail.com

Do not prepend any text in mail. If do so it works fine. Well, I think it appends index number of an array

@piep14

This comment has been minimized.

Copy link

commented May 31, 2019

@breakermind

Allow display images and youtube video:
CSS class for links .htmllink and images .htmlimg

public function linkify($showimg = 1, $value, $protocols = array('http', 'mail', 'https'), array $attributes = array('target' => '_blank'))
{       
    // 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('~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(?<![\.,:])~i', 
                function ($match) use ($protocol, &$links, $attr, $showimg) { 
                    if ($match[1]){
                        $protocol = $match[1]; $link = $match[2] ?: $match[3]; 
                        // Youtube
                        if($showimg == 1){                                  
                            if(strpos($link, 'youtube.com')>=0 || strpos($link, 'youtu.be')>=0){                                  
                                $link = '<iframe width="100%" height="315" src="https://www.youtube.com/embed/'.end(explode('=', $link)).'?rel=0&showinfo=0&color=orange&iv_load_policy=3" frameborder="0" allowfullscreen></iframe>';
                                return '<' . array_push($links, $link) . '></a>';
                            }
                            if(strpos($link, '.png')>0 || strpos($link, '.jpg')>0 || strpos($link, '.jpeg')>0 || strpos($link, '.gif')>0 || strpos($link, '.bmp')>0){
                                return '<' . array_push($links, "<a $attr href=\"$protocol://$link\" class=\"htmllink\"><img src=\"$protocol://$link\" class=\"htmlimg\">") . '></a>';
                            }
                        }                           
                        return '<' . array_push($links, "<a $attr href=\"$protocol://$link\" class=\"htmllink\">$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]}\" class=\"htmllink\">{$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]  . "\" class=\"htmllink\">{$match[0]}</a>") . '>'; }, $value); break;
            default:        $value = preg_replace_callback('~' . preg_quote($protocol, '~') . '://([^\s<]+?)(?<![\.,:])~i', function ($match) use ($protocol, &$links, $attr) { return '<' . array_push($links, "<a $attr href=\"$protocol://{$match[1]}\" class=\"htmllink\">{$match[1]}</a>") . '>'; }, $value); break;
        }
    }
    
    // Insert all link
    return preg_replace_callback('/<(\d+)>/', function ($match) use (&$links) { return $links[$match[1] - 1]; }, $value);
}

I have an error with this code ^^

Only variables should be passed by reference

On this line :

$link = '<iframe width="100%" height="315" src="https://www.youtube.com/embed/'.end(explode('=', $link)).'?rel=0&showinfo=0&color=orange&iv_load_policy=3" frameborder="0" allowfullscreen></iframe>';

My call :

echo TextHelper::linkify(1, 'un lien youtube : https://www.youtube.com/embed/sJeB24OLQis et un lien : https://www.brico.fr/');

Maybe is important, the function "linkify" is static.

public static function linkify($showimg = 1, $value, array $protocols = ['http', 'mail', 'https'], array $attributes = array('target' => '_blank'))

Thank you.

@piep14

This comment has been minimized.

Copy link

commented Jun 3, 2019

The function does not convert multiple types of links?

Example:

a youtube link: https://www.youtube.com/embed/sJeB24OLQis and a link: https://www.brico.fr/

It sends me 2 iframes Youtube which does not make sense to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.