├── 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 | [](https://github.com/iamcal/lib_autolink/actions)
4 | [](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 |
--------------------------------------------------------------------------------