├── LICENSE ├── README.md ├── composer.json └── lib_autolink.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2008 Cal Henderson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lib_autolink - PHP HTML link formatting library 2 | 3 | [![Build Status](https://github.com/iamcal/lib_autolink/actions/workflows/php.yml/badge.svg)](https://github.com/iamcal/lib_autolink/actions) 4 | [![Latest Stable Version](http://img.shields.io/packagist/v/iamcal/lib_autolink.svg?style=flat)](https://packagist.org/packages/iamcal/lib_autolink) 5 | 6 | Find URLs in HTML that are not already links, and make them into links. 7 | 8 | Looking to do this in JavaScript? https://github.com/iamcal/autolink-js 9 | 10 | 11 | ## Usage 12 | 13 | include('lib_autolink.php'); 14 | 15 | 16 | # simple mode 17 | $html = autolink($html); 18 | 19 | # truncate URLs longer than 20 characters 20 | $html = autolink($html, 20); 21 | 22 | # insert some magic into the tags 23 | $html = autolink($html, 30, ' class="mylink"'); 24 | 25 | # By default if the display url is truncated, a title attribute is added to the link, if you don't want this, add a 4th parameter of false 26 | $html = autolink($html, 30, ' class="mylink"', false); 27 | 28 | # link up email address 29 | $html = autolink_email($html); 30 | 31 | 32 | ## Testing 33 | 34 | If you have perl's Test::Harness installed (you almost certainly do), you can 35 | run the tests using: 36 | 37 | prove --exec 'php' t/*.t 38 | 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iamcal/lib_autolink", 3 | "description": "Adds anchors to urls in a text", 4 | "keywords": ["link", "autolink", "anchor"], 5 | "autoload": { 6 | "files": ["lib_autolink.php"] 7 | }, 8 | "license": "MIT" 9 | } 10 | 11 | -------------------------------------------------------------------------------- /lib_autolink.php: -------------------------------------------------------------------------------- 1 | 8 | # This code is licensed under the MIT license 9 | # 10 | 11 | #################################################################### 12 | 13 | # 14 | # These are global options. You can set them before calling the autolinking 15 | # functions to change the output. 16 | # 17 | 18 | $GLOBALS['autolink_options'] = array( 19 | 20 | # Should http:// be visibly stripped from the front 21 | # of URLs? 22 | 'strip_protocols' => true, 23 | 24 | ); 25 | 26 | #################################################################### 27 | 28 | function autolink($text, $limit=30, $tagfill='', $auto_title = true){ 29 | 30 | $text = autolink_do($text, '![a-z][a-z-]+://!i', $limit, $tagfill, $auto_title); 31 | $text = autolink_do($text, '!(mailto|skype):!i', $limit, $tagfill, $auto_title); 32 | $text = autolink_do($text, '!www\\.!i', $limit, $tagfill, $auto_title, 'http://'); 33 | return $text; 34 | } 35 | 36 | #################################################################### 37 | 38 | function autolink_do($text, $sub, $limit, $tagfill, $auto_title, $force_prefix=null){ 39 | 40 | $text_l = StrToLower($text); 41 | $cursor = 0; 42 | $loop = 1; 43 | $buffer = ''; 44 | 45 | while (($cursor < strlen($text)) && $loop){ 46 | 47 | $ok = 1; 48 | $matched = preg_match($sub, $text_l, $m, PREG_OFFSET_CAPTURE, $cursor); 49 | 50 | if (!$matched){ 51 | 52 | $loop = 0; 53 | $ok = 0; 54 | 55 | }else{ 56 | 57 | $pos = $m[0][1]; 58 | $sub_len = strlen($m[0][0]); 59 | 60 | $pre_hit = substr($text, $cursor, $pos-$cursor); 61 | $hit = substr($text, $pos, $sub_len); 62 | $pre = substr($text, 0, $pos); 63 | $post = substr($text, $pos + $sub_len); 64 | 65 | $fail_text = $pre_hit.$hit; 66 | $fail_len = strlen($fail_text); 67 | 68 | # 69 | # substring found - first check to see if we're inside a link tag already... 70 | # 71 | 72 | $bits = preg_split("!!i", $pre); 73 | $last_bit = array_pop($bits); 74 | if (preg_match("!\n"; 77 | 78 | $ok = 0; 79 | $cursor += $fail_len; 80 | $buffer .= $fail_text; 81 | } 82 | } 83 | 84 | # 85 | # looks like a nice spot to autolink from - check the pre 86 | # to see if there was whitespace before this match 87 | # 88 | 89 | if ($ok){ 90 | 91 | if ($pre){ 92 | if (!preg_match('![\s\(\[\{>\pZ\p{Cc}]$!s', $pre)){ 93 | 94 | #echo "fail 2 at $cursor ($pre)
\n"; 95 | 96 | $ok = 0; 97 | $cursor += $fail_len; 98 | $buffer .= $fail_text; 99 | } 100 | } 101 | } 102 | 103 | # 104 | # we want to autolink here - find the extent of the url 105 | # 106 | 107 | if ($ok){ 108 | if (preg_match('/^([a-z0-9\-\.\/\-_%~!?=,:;&+*#@\(\)\$]+)/i', $post, $matches)){ 109 | 110 | $url = $hit.$matches[1]; 111 | 112 | $cursor += strlen($url) + strlen($pre_hit); 113 | $buffer .= $pre_hit; 114 | 115 | $url = html_entity_decode($url); 116 | 117 | 118 | # 119 | # remove trailing punctuation from url 120 | # 121 | 122 | while (preg_match('|[.,!;:?]$|', $url)){ 123 | $url = substr($url, 0, strlen($url)-1); 124 | $cursor--; 125 | } 126 | foreach (array('()', '[]', '{}') as $pair){ 127 | $o = substr($pair, 0, 1); 128 | $c = substr($pair, 1, 1); 129 | if (preg_match("!^(\\$c|^)[^\\$o]+\\$c$!", $url)){ 130 | $url = substr($url, 0, strlen($url)-1); 131 | $cursor--; 132 | } 133 | } 134 | 135 | 136 | # 137 | # nice-i-fy url here 138 | # 139 | 140 | $link_url = $url; 141 | $display_url = $url; 142 | 143 | if ($force_prefix) $link_url = $force_prefix.$link_url; 144 | 145 | if ($GLOBALS['autolink_options']['strip_protocols']){ 146 | if (preg_match('!^(http|https)://!i', $display_url, $m)){ 147 | 148 | $display_url = substr($display_url, strlen($m[1])+3); 149 | } 150 | } 151 | 152 | $display_url = autolink_label($display_url, $limit); 153 | 154 | 155 | # 156 | # add the url 157 | # 158 | 159 | $link_url_enc = HtmlSpecialChars($link_url); 160 | $display_url_enc = HtmlSpecialChars($display_url); 161 | 162 | $currentTagfill = $tagfill; 163 | if ($display_url != $link_url && !preg_match('@title=@msi',$currentTagfill) && $auto_title) { 164 | 165 | $display_quoted = preg_quote($display_url, '!'); 166 | 167 | if (!preg_match("!^(http|https)://{$display_quoted}$!i", $link_url)){ 168 | 169 | $currentTagfill .= ' title="'.$link_url_enc.'"'; 170 | } 171 | } 172 | 173 | $buffer .= "{$display_url_enc}"; 174 | 175 | }else{ 176 | #echo "fail 3 at $cursor
\n"; 177 | 178 | $ok = 0; 179 | $cursor += $fail_len; 180 | $buffer .= $fail_text; 181 | } 182 | } 183 | 184 | } 185 | 186 | # 187 | # add everything from the cursor to the end onto the buffer. 188 | # 189 | 190 | $buffer .= substr($text, $cursor); 191 | 192 | return $buffer; 193 | } 194 | 195 | #################################################################### 196 | 197 | function autolink_label($text, $limit){ 198 | 199 | if (!$limit){ return $text; } 200 | 201 | if (strlen($text) > $limit){ 202 | return substr($text, 0, $limit-3).'...'; 203 | } 204 | 205 | return $text; 206 | } 207 | 208 | #################################################################### 209 | 210 | function autolink_email($text, $tagfill=''){ 211 | 212 | $atom = '[^()<>@,;:\\\\".\\[\\]\\x00-\\x20\\x7f]+'; # from RFC822 213 | 214 | #die($atom); 215 | 216 | $text_l = StrToLower($text); 217 | $cursor = 0; 218 | $loop = 1; 219 | $buffer = ''; 220 | 221 | while(($cursor < strlen($text)) && $loop){ 222 | 223 | # 224 | # find an '@' symbol 225 | # 226 | 227 | $ok = 1; 228 | $pos = strpos($text_l, '@', $cursor); 229 | 230 | if ($pos === false){ 231 | 232 | $loop = 0; 233 | $ok = 0; 234 | 235 | }else{ 236 | 237 | $pre = substr($text, $cursor, $pos-$cursor); 238 | $hit = substr($text, $pos, 1); 239 | $post = substr($text, $pos + 1); 240 | 241 | $fail_text = $pre.$hit; 242 | $fail_len = strlen($fail_text); 243 | 244 | #die("$pre::$hit::$post::$fail_text"); 245 | 246 | # 247 | # substring found - first check to see if we're inside a link tag already... 248 | # 249 | 250 | $bits = preg_split("!!i", $pre); 251 | $last_bit = array_pop($bits); 252 | if (preg_match("!\n"; 255 | 256 | $ok = 0; 257 | $cursor += $fail_len; 258 | $buffer .= $fail_text; 259 | } 260 | } 261 | 262 | # 263 | # check backwards 264 | # 265 | 266 | if ($ok){ 267 | if (preg_match("!($atom(\.$atom)*)\$!", $pre, $matches)){ 268 | 269 | # move matched part of address into $hit 270 | 271 | $len = strlen($matches[1]); 272 | $plen = strlen($pre); 273 | 274 | $hit = substr($pre, $plen-$len).$hit; 275 | $pre = substr($pre, 0, $plen-$len); 276 | 277 | }else{ 278 | 279 | #echo "fail 2 at $cursor ($pre)
\n"; 280 | 281 | $ok = 0; 282 | $cursor += $fail_len; 283 | $buffer .= $fail_text; 284 | } 285 | } 286 | 287 | # 288 | # check forwards 289 | # 290 | 291 | if ($ok){ 292 | if (preg_match("!^($atom(\.$atom)*)!", $post, $matches)){ 293 | 294 | # move matched part of address into $hit 295 | 296 | $len = strlen($matches[1]); 297 | 298 | $hit .= substr($post, 0, $len); 299 | $post = substr($post, $len); 300 | 301 | }else{ 302 | #echo "fail 3 at $cursor ($post)
\n"; 303 | 304 | $ok = 0; 305 | $cursor += $fail_len; 306 | $buffer .= $fail_text; 307 | } 308 | } 309 | 310 | # 311 | # commit 312 | # 313 | 314 | if ($ok) { 315 | 316 | $cursor += strlen($pre) + strlen($hit); 317 | $buffer .= $pre; 318 | $buffer .= "$hit"; 319 | 320 | } 321 | 322 | } 323 | 324 | # 325 | # add everything from the cursor to the end onto the buffer. 326 | # 327 | 328 | $buffer .= substr($text, $cursor); 329 | 330 | return $buffer; 331 | } 332 | 333 | #################################################################### 334 | 335 | ?> 336 | --------------------------------------------------------------------------------