Replace youtube link with embed and leave other text unharmed - php

I'm trying to turn youtube links into embed iframes, to play the videos. However, my current code is replacing the entire sentence with the embed code. What I want to do is to just convert the youtube link to an embed code, and leave the rest of the text unharmed.
Example: This is a youtube link: https://www.youtube.com/watch?v=T-8XurAKMkU and some text after.
Turned into: This is a youtube link: <embed> and some text after.
My current code:
$testing = "This is a youtube link: https://www.youtube.com/watch?v=T-8XurAKMkU and some text after.";
echo $core->convertyoutube($testing);
And the function:
public function convertyoutube($link) {
if (strpos($link, 'youtube.com/watch?v=') == true) {
$url = $link;
parse_str(parse_url($url, PHP_URL_QUERY), $youtube_array);
$videoid = $youtube_array['v'];
$embed = "<iframe width='420' height='315' src='https://www.youtube.com/embed/".$videoid."'></iframe>"; // what it should create with the extracted code
return $embed;
}
}

Well You are returning only embed code:
return $embed;
You need to replace only youtube part:
public function convertyoutube($link) {
$position = strpos($link, 'youtube.com/watch?v=');
if ($position !== false) {
$chunks = explode(' ', $link);
foreach ($chunks as &$chunk) {
$isYoutubeLink = strpos($chunk, 'youtube.com/watch?v=');
if ($isYoutubeLink !== false) {
$url = $chunk;
parse_str(parse_url($url, PHP_URL_QUERY), $youtube_array);
$videoid = $youtube_array['v'];
$chunk = "<iframe width='420' height='315' src='https://www.youtube.com/embed/".$videoid."'></iframe>"; // what it should create with the extracted code
}
}
return implode(' ', $chunks);
}
}
It works with multiple links in sentence. I guess there is "better" way with using regexp, however I am not very good at regexp and don't like to use it where it is not mandatory.

You could actually do this all with a single regex.
echo preg_replace('/https?:\/\/(?:www\.)?youtube\.com\/watch\?v=(.+?)(?:&|\s|$)/',
'<iframe width="420" height="315" src="https://www.youtube.com/embed/$1"></iframe>',
'This is a youtube link: https://www.youtube.com/watch?v=T-8XurAKMkU and some text after.');
Output:
This is a youtube link: https://www.youtube.com/watch?v=T-8XurAKMkU and some text after.
Regex101 Demo: https://regex101.com/r/cT2mW1/2

It will be better if you convert the links at the front-end view with javascript to avoid a excess loading of server. But it's your choise.
Single youtube video has different links like these:
1) https://www.youtube.com/watch?v=lfKON5AMvTM
2) https://youtu.be/lfKON5AMvTM
For this reason you should write your codes like this to catch every type of youtube links:
public function convertYouTube($content) {
$content = preg_replace("/http(s)?:\/\/youtu\.be\/([^\40\t\r\n\<]+)/i", '<iframe width="420" height="315" src="https://www.youtube.com/embed/$2"></iframe>', $content);
$content = preg_replace("/http(s)?:\/\/(w{3}\.)?youtube\.com\/watch\/?\?v=([^\40\t\r\n\<]+)/i", '<iframe width="420" height="315" src="https://www.youtube.com/embed/$3"></iframe>', $content);
return $content;
}

Related

YouTube embedded video issue

I want to embed the YouTube video on my website. The problem is with timeline and origin link properties. For example, I have some text and 2 bbCode video tags, one with timeline and 2nd without timeline:
This is just a test example.
[youtube]https://www.youtube.com/watch?v=xHLE2LTAItw&t=53s[/youtube]
[youtube]https://www.youtube.com/watch?v=xHLE2LTAItw[/youtube]
Code:
if (preg_match_all("/\[youtube\]((\s|.)+?)\[\/youtube\]/i", $text, $matches)) {
$allMatches = count($matches[0]);
if (is_array($matches[0]) && $allMatches > 0) {
for ($i = 0; $i < $allMatches; $i++) {
$text = str_replace("watch?v=", "embed/", $text);
if (strpos($matches[0][$i], "&t=") !== false) {
$text = str_replace("&t=", "?start=", $text);
$text = preg_replace("#s\[\/youtube\]#i", "&enablejsapi=1&origin=". HAZELFENCES_WEBSITE ."[/youtube]", $text);
} else {
$text = preg_replace("#\[\/youtube\]#i", "?enablejsapi=1&origin=". HAZELFENCES_WEBSITE ."[/youtube]", $text);
}
}
$text = preg_replace("/\[youtube\]((\s|.)+?)\[\/youtube\]/i", "<iframe width=\"640\" height=\"510\" src=\"\\1\" loading=\"lazy\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>", $text);
}
}
return $text;
The iframe src from the first video returns as: src="https://www.youtube.com/embed/xHLE2LTAItw?start=53&enablejsapi=1&origin=https://test.com?enablejsapi=1&origin=https://test.com" which is wrong. It should be:
https://www.youtube.com/embed/xHLE2LTAItw?start=53&enablejsapi=1&origin=https://test.com
The video link without timeline must be:
https://www.youtube.com/embed/xHLE2LTAItw?enablejsapi=1&origin=https://test.com, which is correct using my code. The only issue is with timeline videos.
Please note, this issue is only occurs when 2 video links are available. One with timeline and the second one without timeline.
For example: https://3v4l.org/JTAUp
Any ideas how to fix it? Thank you.
Ok. It's a bit tricky but I have finally fixed it by using RegExp and preg_replace function. First of all, I have checked for the video link with timeline and then preg_replace the &t=00s[/youtube] with ?start=\\1&enablejsapi=1&origin=". HAZELFENCES_WEBSITE.
Secondly, I checked for the video without timeline and preg_replace the /embed/xHLE2LTAItw[/youtube] with /\\1\\2?enablejsapi=1&origin=". HAZELFENCES_WEBSITE ."[/youtube].
My code:
$text = str_replace("watch?v=", "embed/", $text);
$isVideoWithTimeline = preg_match("#\[youtube\](http|https):\/\/www\.youtube\.com\/([a-z]+\?v=|[a-z]+\/)[a-zA-Z0-9]+(&|&[a-zA-Z]+;)[a-zA-Z]=\d+s\[\/youtube\]#si", $text);
if ($isVideoWithTimeline > 0) {
$text = preg_replace("#&t=([0-9]+)s\[\/youtube\]#si", "?start=\\1&enablejsapi=1&origin=". HAZELFENCES_WEBSITE ."[/youtube]", $text);
}
$isVideoWithoutTimeline = preg_match("#\[youtube\](http|https):\/\/www\.youtube\.com\/([a-z]+\?v=|[a-z]+\/)[a-zA-Z0-9]+\[\/youtube\]#si", $text);
if ($isVideoWithoutTimeline > 0) {
$text = preg_replace("#/([a-z]+\?v=|[a-z]+\/)([a-zA-Z0-9]+)\[\/youtube\]#si", "/\\1\\2?enablejsapi=1&origin=". HAZELFENCES_WEBSITE ."[/youtube]", $text);
}
$text = preg_replace("/\[youtube\]((\s|.)+?)\[\/youtube\]/i", "<iframe width=\"640\" height=\"510\" src=\"\\1\" loading=\"lazy\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>", $text);
return $text;
Now, everything works well. The issue is resolved.

String to URL but detect if url is image?

i'm trying to do something in PHP
I'm trying to get the link of an image -> store it to my DB, but I'd like the user to be able to store text before it, and after it, I've gotten my hands on a similar function for links, but the image part is missing.
As you can see the turnUrlIntoHyperlink does a regex check over the entire arg passed, turning the text that contains it to the url, so users can post something like
Hey check this cool site "https://stackoverflow.com" its dope!
And the entire argument posting to my database.
However i can't seem to get the same function working for the Convert Image, as it simply won't post and removed text before/after it before when i made the attempt.
How would i do this in a correct way, and can i combine these 2 functions in to 1 function?
function convertImg($string) {
return preg_replace('/((https?):\/\/(\S*)\.(jpg|gif|png)(\?(\S*))?(?=\s|$|\pP))/i', '<img src="$1" />', $string);
}
function turnUrlIntoHyperlink($string){
//The Regular Expression filter
$reg_exUrl = "/(?i)\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))/";
// Check if there is a url in the text
if(preg_match_all($reg_exUrl, $string, $url)) {
// Loop through all matches
foreach($url[0] as $newLinks){
if(strstr( $newLinks, ":" ) === false){
$link = 'http://'.$newLinks;
}else{
$link = $newLinks;
}
// Create Search and Replace strings
$search = $newLinks;
$replace = ''.$link.'';
$string = str_replace($search, $replace, $string);
}
}
//Return result
return $string;
}
more explained in detail :
When i post a link like https://google.com/ I'd like it to be a href,
But if i post an image like https://image.shutterstock.com/image-photo/duck-on-white-background-260nw-1037486431.jpg , i'd like it to be a img src,
Currently, i'm storing it in my db and echoing it to a little debug panel,
Do you mean that you want to make an <img> inside <a> element?
Your turnUrlIntoHyperlink function have captured the url successfully, so we can just use explode to get string before and after the link.
$exploded = explode($link, $string);
$string_before = $exploded[0];
$string_after = $exploded[1];
Code example:
<?php
function turnUrlIntoHyperlink($string){
//The Regular Expression filter
$reg_exUrl = "/(?i)\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))/";
// Check if there is a url in the text
if(preg_match_all($reg_exUrl, $string, $url)) {
// add http protocol if the url does not already contain it
$newLinks = $url[0][0];
if(strstr( $newLinks, ":" ) === false){
$link = 'http://'.$newLinks;
}else{
$link = $newLinks;
}
$exploded = explode($link, $string);
$string_before = $exploded[0];
$string_after = $exploded[1];
return $string_before.'<img src="'.$link.'">'.$string_after;
}
return $string;
}
echo turnUrlIntoHyperlink('Hey check this cool site https://stackoverflow.com/img/myimage.png its dope!');
Output:
Hey check this cool site <img src="https://stackoverflow.com/img/myimage.png"> its dope!
Edit: the question has been edited
Since an image URL is just another kind of link/URL, your logic should go like this pseudocode:
if link is image and link is url
print <img src=link> tag
else if link is url and link is not image
print <a href=link> tag
else
print link
So you can just write a new function to "merge" those two function:
function convertToImgOrHyperlink($string) {
$result = convertImg($string);
if($result != $string) return $result;
$result = turnUrlIntoHyperlink($string);
if($result != $string) return $result;
return $string;
}
echo convertToImgOrHyperlink('Hey check this cool site https://stackoverflow.com/img/myimage.png its dope!');
echo "\r\n\r\n";
echo convertToImgOrHyperlink('Hey check this cool site https://stackoverflow.com/ its dope!');
echo "\r\n\r\n";
Output:
Hey check this cool site <img src="https://stackoverflow.com/img/myimage.png" /> its dope!
Hey check this cool site https://stackoverflow.com/ its dope!
The basic idea is that since image url is also a link, such check must be done first. Then if it's effective (input and return is different), then do <img> convertion. Otherwise do <a> convertion.

How to append embed code to youtube anchor link

I use this code to make urls clickable as anchor links in forum posts.
function makelink($str) {
$str = preg_replace_callback('/((((http)(s)?:\/\/)|www\.)[-0-9æøåa-zA-Z?-??-?\(\)%_+\.~#?&;:#\/\/=]+)(?<!\.)/i', function($matches) {
if (strtolower(substr($matches[0], 0 , 4)) == 'www.') {
$matches[0] = 'http://' . $matches[0];
}
return ''.$matches[0].'';
}, $str);
return trim($str);
}
It works fine. Now I need to also make youtube links into embed codes underneath the link (appended to the link I guess).
It's ok for this that there is an extra replacement routine going on.
How could I make some code that replaces the resulting anchor (if it's a youtube link):
https://www.youtube.com/watch?v=LOLAy72Tv24
With this:
https://www.youtube.com/watch?v=LOLAy72Tv24
<br />
<iframe class="youtube" width="350" height="250" src="https://www.youtube.com/embed/LOLAy72Tv24/" allowfullscreen></iframe>
So it just needs to take the video id and put out embed code underneath the original link, while outputting both together.
Here's my proposed solution:
function makelink($str) {
$pattern = '/((((http)(s)?:\/\/)|www\.)[-0-9æøåa-zA-Z?-??-?\(\)%_+\.~#?&;:#\/\/=]+)(?<!\.)/i';
$str = preg_replace_callback($pattern, function($matches) {
if (strtolower(substr($matches[0], 0 , 4)) == 'www.') {
$matches[0] = 'http://' . $matches[0];
}
// store anchor tag html in a variable instead of returning immediately
$html = ''.$matches[0].'';
if (isYouTubeVideoUrl($matches[0])) {
$html .= '<br />'.makeiFrame($matches[0]);
}
return $html;
}, $str);
return trim($str);
}
function isYouTubeVideoUrl(string $url): bool
{
return (parse_url($url, PHP_URL_HOST) === 'www.youtube.com' || isYouTubeShortUrl($url))
&& strpos(parse_url($url, PHP_URL_QUERY), 'v=') !== false;
}
function isYouTubeShortUrl(string $url): bool
{
return parse_url($url, PHP_URL_HOST) === 'youtu.be';
}
function makeiFrame(string $url): string {
$embedUrl = 'https://www.youtube.com/embed/'.getYouTubeVideoId($url).'/';
return '<iframe class="youtube" width="350" height="250" src="'.$embedUrl.'" allowfullscreen></iframe>';
}
function getYouTubeVideoId(string $url): string
{
if (isYouTubeShortUrl($url)) {
preg_match('/[^\/]+$/', $url, $matches);
return $matches[0];
}
preg_match('/(?<=v=)(.*?)(?=(&|$))/', $url, $matches);
return $matches[0];
}
It is designed to work both with regular YouTube URLs (https://www.youtube.com/watch?v=LOLAy72Tv24) and short YouTube URLs (https://youtu.be/LOLAy72Tv24). It also supports the v parameter being anywhere in the query string for regular URLs.
Most of the code is pretty straightforward, the key lies in extracting the video id.
Short URLs have the format where the id is behind a slash, so [^\/]+$ looks for any characters that are not a slash at the end of the string:
[^\/] matches any character not a slash
+ is a quantifier for one or more, greedy
$ asserts the position at the end of the string
Regular URLs have the format where the id is in a parameter named v, so (?<=v=)(.+?)(?=(&|$)) looks for everything between v= and either & or the end of the string:
(?<=v=) is a positive lookbehind, assuring that we look for a string right after v=
(.+?) matches one or more characters (any, except for line terminators), lazy
(?=(&|$)) is a positive lookahead, assuring that we look for a string right before an ampersand (&) or the end of a string ($)

Detect and convert url in link and detect image and convert to img PHP

Currently, I use this code to detect and convert URL in link in text.
But now, I need to keep this system, but detect and convert image too.
public function convert_to_link($text)
{
$reg_user = '!#(.+)(?:\s|$)!U';
if (preg_match_all($reg_user, $text, $matches))
{
return preg_replace($reg_user, '$0', $text);
}
$reg_exUrl = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/";
if(preg_match($reg_exUrl, $text, $url)) {
// make the urls hyper links
if (preg_match("/\[img=([^\s'\"<>]+?)\]/i", $text))
{
//This is not working
return preg_replace("/\[img=([^\s'\"<>]+?)\]/i", "<img border=0 src=\"\\1\">", $text);
}
else
{
return preg_replace($reg_exUrl, '$0 ', $text);
}
}
else
{
return $text;
}
}
I use too [img][/img] code to convert image to but with the code located above, the result is bad :
Looks like your regex is looking for this format
[img=http://example.com/name.png]
but your example is in a different format
[img]http://example.com/name.png[/img]
a regex to identify the second form would be
"/^\[img\]([^\[]+)\[\/img\]$/i"

php youtube and links regex are combining and in turn rendering useless

i have a posting feature on my site that can embed links and youtube videos. the problem is, that the two clash together, and the youtube iframe ends up being a 404 page on my site. my codes for the youtube videos and links are below, but im not sure how to stop them from combining and ruining it.
by combining, i mean this http://www.youtube.com/watch?v=VhqiT2nWCVU turns to
<iframe src="http://www.youtube.com/watch?v=VhqiT2nWCVU">
which then turns into
<iframe src="">
sorry if i am unclear in any way. my codes are below.
function youtube($string)
{
return preg_replace(
'#(http://(www.)?youtube.com)?/(v/|watch\?v\=)([-|~_0-9A-Za-z]+)&?.*?#i',
'<iframe title="YouTube video player" width="480" height="390" src="http://www.youtube.com/embed/$4?rel=0" frameborder="0" allowfullscreen></iframe>',
$string
);
}
$posted = youtube($posted);
$rexProtocol = '(https?://)?';
$rexDomain = '((?:[-a-zA-Z0-9]{1,63}\.)+[-a-zA-Z0-9]{2,63}|(?:[0-9]{1,3}\.){3}[0-9]{1,3})';
$rexPort = '(:[0-9]{1,5})?';
$rexPath = '(/[!$-/0-9:;=#_\':;!a-zA-Z\x7f-\xff]*?)?';
$rexQuery = '(\?[!$-/0-9:;=#_\':;!a-zA-Z\x7f-\xff]+?)?';
$rexFragment = '(#[!$-/0-9:;=#_\':;!a-zA-Z\x7f-\xff]+?)?';
// Solution 1:
function callback($match)
{
// Prepend http:// if no protocol specified
$completeUrl = $match[1] ? $match[0] : "http://{$match[0]}";
return '<a href="' . $completeUrl . '">'
. $match[2] . $match[3] . $match[4] . '</a>';
}
$posted = preg_replace_callback("&\\b$rexProtocol$rexDomain$rexPort$rexPath$rexQuery$rexFragment(?=[?.!,;:\"]?(\s|$))&",
'callback', $posted);
A popular solution to this problem is to use placeholders. On your first pass you turn all YouTube links into a placeholder, for example:
{{youtube:VhqiT2nWCVU}}
After that you run your normal link converter. And at the end your run yet another regex to turn all your palceholders into youtube embeds.

Categories