3 * @author Mike Cochrane <mikec@mikenz.geek.nz>
4 * @author Nick Pope <nick@nickpope.me.uk>
5 * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
6 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
10 require_once 'Regex.php';
11 require_once 'Extractor.php';
14 * Twitter Autolink Class
16 * Parses tweets and generates HTML anchor tags around URLs, usernames,
17 * username/list pairs and hashtags.
19 * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
20 * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
21 * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
23 * @author Mike Cochrane <mikec@mikenz.geek.nz>
24 * @author Nick Pope <nick@nickpope.me.uk>
25 * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
26 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
29 class Twitter_Autolink extends Twitter_Regex {
32 * CSS class for auto-linked URLs.
36 protected $class_url = 'url';
39 * CSS class for auto-linked username URLs.
43 protected $class_user = 'username';
46 * CSS class for auto-linked list URLs.
50 protected $class_list = 'list';
53 * CSS class for auto-linked hashtag URLs.
57 protected $class_hash = 'hashtag';
60 * CSS class for auto-linked cashtag URLs.
64 protected $class_cash = 'cashtag';
67 * URL base for username links (the username without the @ will be appended).
71 protected $url_base_user = BASE_URL;
74 * URL base for list links (the username/list without the @ will be appended).
78 protected $url_base_list = BASE_URL.'lists/';
81 * URL base for hashtag links (the hashtag without the # will be appended).
85 protected $url_base_hash = BASE_URL.'hash/';
88 * URL base for cashtag links (the hashtag without the $ will be appended).
92 protected $url_base_cash = BASE_URL.'search?query=%24';
95 * Whether to include the value 'nofollow' in the 'rel' attribute.
99 protected $nofollow = true;
102 * Whether to include the value 'external' in the 'rel' attribute.
104 * Often this is used to be matched on in JavaScript for dynamically adding
105 * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
106 * been undeprecated and thus the 'target' attribute can be used. If this is
107 * set to false then the 'target' attribute will be output.
111 protected $external = true;
114 * The scope to open the link in.
116 * Support for the 'target' attribute was deprecated in HTML 4.01 but has
117 * since been reinstated in HTML 5. To output the 'target' attribute you
118 * must disable the adding of the string 'external' to the 'rel' attribute.
122 protected $target = '_blank';
125 * attribute for invisible span tag
129 protected $invisibleTagAttrs = "style='position:absolute;left:-9999px;'";
133 * @var Twitter_Extractor
135 protected $extractor = null;
138 * Provides fluent method chaining.
140 * @param string $tweet The tweet to be converted.
141 * @param bool $full_encode Whether to encode all special characters.
145 * @return Twitter_Autolink
147 public static function create($tweet = null, $full_encode = false) {
148 return new self($tweet, $full_encode);
152 * Reads in a tweet to be parsed and converted to contain links.
154 * As the intent is to produce links and output the modified tweet to the
155 * user, we take this opportunity to ensure that we escape user input.
157 * @see htmlspecialchars()
159 * @param string $tweet The tweet to be converted.
160 * @param bool $escape Whether to escape the tweet (default: true).
161 * @param bool $full_encode Whether to encode all special characters.
163 public function __construct($tweet = null, $escape = true, $full_encode = false) {
164 if ($escape && !empty($tweet)) {
166 parent::__construct(htmlentities($tweet, ENT_QUOTES, 'UTF-8', false));
168 parent::__construct(htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false));
171 parent::__construct($tweet);
173 $this->extractor = Twitter_Extractor::create();
177 * CSS class for auto-linked URLs.
179 * @return string CSS class for URL links.
181 public function getURLClass() {
182 return $this->class_url;
186 * CSS class for auto-linked URLs.
188 * @param string $v CSS class for URL links.
190 * @return Twitter_Autolink Fluid method chaining.
192 public function setURLClass($v) {
193 $this->class_url = trim($v);
198 * CSS class for auto-linked username URLs.
200 * @return string CSS class for username links.
202 public function getUsernameClass() {
203 return $this->class_user;
207 * CSS class for auto-linked username URLs.
209 * @param string $v CSS class for username links.
211 * @return Twitter_Autolink Fluid method chaining.
213 public function setUsernameClass($v) {
214 $this->class_user = trim($v);
219 * CSS class for auto-linked username/list URLs.
221 * @return string CSS class for username/list links.
223 public function getListClass() {
224 return $this->class_list;
228 * CSS class for auto-linked username/list URLs.
230 * @param string $v CSS class for username/list links.
232 * @return Twitter_Autolink Fluid method chaining.
234 public function setListClass($v) {
235 $this->class_list = trim($v);
240 * CSS class for auto-linked hashtag URLs.
242 * @return string CSS class for hashtag links.
244 public function getHashtagClass() {
245 return $this->class_hash;
249 * CSS class for auto-linked hashtag URLs.
251 * @param string $v CSS class for hashtag links.
253 * @return Twitter_Autolink Fluid method chaining.
255 public function setHashtagClass($v) {
256 $this->class_hash = trim($v);
261 * CSS class for auto-linked cashtag URLs.
263 * @return string CSS class for cashtag links.
265 public function getCashtagClass() {
266 return $this->class_cash;
270 * CSS class for auto-linked cashtag URLs.
272 * @param string $v CSS class for cashtag links.
274 * @return Twitter_Autolink Fluid method chaining.
276 public function setCashtagClass($v) {
277 $this->class_cash = trim($v);
282 * Whether to include the value 'nofollow' in the 'rel' attribute.
284 * @return bool Whether to add 'nofollow' to the 'rel' attribute.
286 public function getNoFollow() {
287 return $this->nofollow;
291 * Whether to include the value 'nofollow' in the 'rel' attribute.
293 * @param bool $v The value to add to the 'target' attribute.
295 * @return Twitter_Autolink Fluid method chaining.
297 public function setNoFollow($v) {
298 $this->nofollow = $v;
303 * Whether to include the value 'external' in the 'rel' attribute.
305 * Often this is used to be matched on in JavaScript for dynamically adding
306 * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
307 * been undeprecated and thus the 'target' attribute can be used. If this is
308 * set to false then the 'target' attribute will be output.
310 * @return bool Whether to add 'external' to the 'rel' attribute.
312 public function getExternal() {
313 return $this->external;
317 * Whether to include the value 'external' in the 'rel' attribute.
319 * Often this is used to be matched on in JavaScript for dynamically adding
320 * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
321 * been undeprecated and thus the 'target' attribute can be used. If this is
322 * set to false then the 'target' attribute will be output.
324 * @param bool $v The value to add to the 'target' attribute.
326 * @return Twitter_Autolink Fluid method chaining.
328 public function setExternal($v) {
329 $this->external = $v;
334 * The scope to open the link in.
336 * Support for the 'target' attribute was deprecated in HTML 4.01 but has
337 * since been reinstated in HTML 5. To output the 'target' attribute you
338 * must disable the adding of the string 'external' to the 'rel' attribute.
340 * @return string The value to add to the 'target' attribute.
342 public function getTarget() {
343 return $this->target;
347 * The scope to open the link in.
349 * Support for the 'target' attribute was deprecated in HTML 4.01 but has
350 * since been reinstated in HTML 5. To output the 'target' attribute you
351 * must disable the adding of the string 'external' to the 'rel' attribute.
353 * @param string $v The value to add to the 'target' attribute.
355 * @return Twitter_Autolink Fluid method chaining.
357 public function setTarget($v) {
358 $this->target = trim($v);
363 * Autolink with entities
365 * @param string $tweet
366 * @param array $entities
370 public function autoLinkEntities($tweet = null, $entities) {
371 if (is_null($tweet)) {
372 $tweet = $this->tweet;
377 foreach ($entities as $entity) {
378 if (isset($entity['screen_name'])) {
379 $text .= mb_substr($tweet, $beginIndex, $entity['indices'][0] - $beginIndex + 1);
381 $text .= mb_substr($tweet, $beginIndex, $entity['indices'][0] - $beginIndex);
384 if (isset($entity['url'])) {
385 $text .= $this->linkToUrl($entity);
386 } elseif (isset($entity['hashtag'])) {
387 $text .= $this->linkToHashtag($entity, $tweet);
388 } elseif (isset($entity['screen_name'])) {
389 $text .= $this->linkToMentionAndList($entity);
390 } elseif (isset($entity['cashtag'])) {
391 $text .= $this->linkToCashtag($entity, $tweet);
393 $beginIndex = $entity['indices'][1];
395 $text .= mb_substr($tweet, $beginIndex, mb_strlen($tweet));
400 * Auto-link hashtags, URLs, usernames and lists, with JSON entities.
402 * @param string The tweet to be converted
403 * @param mixed The entities info
404 * @return string that auto-link HTML added
407 public function autoLinkWithJson($tweet = null, $json) {
408 // concatenate entities
410 if (is_object($json)) {
411 $json = $this->object2array($json);
413 if (is_array($json)) {
414 foreach ($json as $key => $vals) {
415 $entities = array_merge($entities, $json[$key]);
419 // map JSON entity to twitter-text entity
420 foreach ($entities as $idx => $entity) {
421 if (!empty($entity['text'])) {
422 $entities[$idx]['hashtag'] = $entity['text'];
426 $entities = $this->extractor->removeOverlappingEntities($entities);
427 return $this->autoLinkEntities($tweet, $entities);
431 * convert Object to Array
436 protected function object2array($obj) {
437 $array = (array)$obj;
438 foreach ($array as $key => $var) {
439 if (is_object($var) || is_array($var)) {
440 $array[$key] = $this->object2array($var);
447 * Auto-link hashtags, URLs, usernames and lists.
449 * @param string The tweet to be converted
450 * @return string that auto-link HTML added
453 public function autoLink($tweet = null) {
454 if (is_null($tweet)) {
455 $tweet = $this->tweet;
457 $entities = $this->extractor->extractURLWithoutProtocol(false)->extractEntitiesWithIndices($tweet);
458 return $this->autoLinkEntities($tweet, $entities);
462 * Auto-link the @username and @username/list references in the provided text. Links to @username references will
463 * have the usernameClass CSS classes added. Links to @username/list references will have the listClass CSS class
466 * @return string that auto-link HTML added
469 public function autoLinkUsernamesAndLists($tweet = null) {
470 if (is_null($tweet)) {
471 $tweet = $this->tweet;
473 $entities = $this->extractor->extractMentionsOrListsWithIndices($tweet);
474 return $this->autoLinkEntities($tweet, $entities);
478 * Auto-link #hashtag references in the provided Tweet text. The #hashtag links will have the hashtagClass CSS class
481 * @return string that auto-link HTML added
484 public function autoLinkHashtags($tweet = null) {
485 if (is_null($tweet)) {
486 $tweet = $this->tweet;
488 $entities = $this->extractor->extractHashtagsWithIndices($tweet);
489 return $this->autoLinkEntities($tweet, $entities);
493 * Auto-link URLs in the Tweet text provided.
495 * This only auto-links URLs with protocol.
497 * @return string that auto-link HTML added
500 public function autoLinkURLs($tweet = null) {
501 if (is_null($tweet)) {
502 $tweet = $this->tweet;
504 $entities = $this->extractor->extractURLWithoutProtocol(false)->extractURLsWithIndices($tweet);
505 return $this->autoLinkEntities($tweet, $entities);
509 * Auto-link $cashtag references in the provided Tweet text. The $cashtag links will have the cashtagClass CSS class
512 * @return string that auto-link HTML added
515 public function autoLinkCashtags($tweet = null) {
516 if (is_null($tweet)) {
517 $tweet = $this->tweet;
519 $entities = $this->extractor->extractCashtagsWithIndices($tweet);
520 return $this->autoLinkEntities($tweet, $entities);
523 public function linkToUrl($entity) {
524 if (!empty($this->class_url)) $attributes['class'] = $this->class_url;
525 $attributes['href'] = $entity['url'];
526 $linkText = $this->escapeHTML($entity['url']);
528 if (!empty($entity['display_url']) && !empty($entity['expanded_url'])) {
529 // Goal: If a user copies and pastes a tweet containing t.co'ed link, the resulting paste
530 // should contain the full original URL (expanded_url), not the display URL.
532 // Method: Whenever possible, we actually emit HTML that contains expanded_url, and use
533 // font-size:0 to hide those parts that should not be displayed (because they are not part of display_url).
534 // Elements with font-size:0 get copied even though they are not visible.
535 // Note that display:none doesn't work here. Elements with display:none don't get copied.
537 // Additionally, we want to *display* ellipses, but we don't want them copied. To make this happen we
538 // wrap the ellipses in a tco-ellipsis class and provide an onCopy handler that sets display:none on
539 // everything with the tco-ellipsis class.
541 // As an example: The user tweets "hi http://longdomainname.com/foo"
542 // This gets shortened to "hi http://t.co/xyzabc", with display_url = "…nname.com/foo"
543 // This will get rendered as:
544 // <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
546 // <!-- There's a chance the onCopy event handler might not fire. In case that happens,
547 // we include an here so that the … doesn't bump up against the URL and ruin it.
548 // The is inside the tco-ellipsis span so that when the onCopy handler *does*
549 // fire, it doesn't get copied. Otherwise the copied text would have two spaces in a row,
550 // e.g. "hi http://longdomainname.com/foo".
551 // <span style='font-size:0'> </span>
553 // <span style='font-size:0'> <!-- This stuff should get copied but not displayed -->
556 // <span class='js-display-url'> <!-- This stuff should get displayed *and* copied -->
559 // <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
560 // <span style='font-size:0'> </span>
564 // Exception: pic.twitter.com images, for which expandedUrl = "https://twitter.com/#!/username/status/1234/photo/1
565 // For those URLs, display_url is not a substring of expanded_url, so we don't do anything special to render the elided parts.
566 // For a pic.twitter.com URL, the only elided part will be the "https://", so this is fine.
567 $displayURL = $entity['display_url'];
568 $expandedURL = $entity['expanded_url'];
569 $displayURLSansEllipses = preg_replace('/…/u', '', $displayURL);
570 $diplayURLIndexInExpandedURL = mb_strpos($expandedURL, $displayURLSansEllipses);
572 if ($diplayURLIndexInExpandedURL !== false) {
573 $beforeDisplayURL = mb_substr($expandedURL, 0, $diplayURLIndexInExpandedURL);
574 $afterDisplayURL = mb_substr($expandedURL, $diplayURLIndexInExpandedURL + mb_strlen($displayURLSansEllipses));
575 $precedingEllipsis = (preg_match('/\A…/u', $displayURL)) ? '…' : '';
576 $followingEllipsis = (preg_match('/…\z/u', $displayURL)) ? '…' : '';
578 $invisibleSpan = "<span {$this->invisibleTagAttrs}>";
580 $linkText = "<span class='tco-ellipsis'>{$precedingEllipsis}{$invisibleSpan} </span></span>";
581 $linkText .= "{$invisibleSpan}{$this->escapeHTML($beforeDisplayURL)}</span>";
582 $linkText .= "<span class='js-display-url'>{$this->escapeHTML($displayURLSansEllipses)}</span>";
583 $linkText .= "{$invisibleSpan}{$this->escapeHTML($afterDisplayURL)}</span>";
584 $linkText .= "<span class='tco-ellipsis'>{$invisibleSpan} </span>{$followingEllipsis}</span>";
586 $linkText = $entity['display_url'];
588 $attributes['title'] = $entity['expanded_url'];
589 } else if (!empty($entity['display_url'])) {
590 $linkText = $entity['display_url'];
593 return $this->linkToText($entity, $linkText, $attributes);
598 * @param array $entity
599 * @param string $tweet
603 public function linkToHashtag($entity, $tweet = null) {
604 if (is_null($tweet)) {
605 $tweet = $this->tweet;
608 $attributes = array();
610 $hash = mb_substr($tweet, $entity['indices'][0], 1);
611 $linkText = $hash . $entity['hashtag'];
613 $attributes['href'] = $this->url_base_hash . $entity['hashtag'];
614 $attributes['title'] = '#' . $entity['hashtag'];
615 if (!empty($this->class_hash)) {
616 $class[] = $this->class_hash;
618 if (preg_match(self::$patterns['rtl_chars'], $linkText)) {
621 if (!empty($class)) {
622 $attributes['class'] = join(' ', $class);
625 return $this->linkToText($entity, $linkText, $attributes);
630 * @param array $entity
634 public function linkToMentionAndList($entity) {
635 $attributes = array();
637 if (!empty($entity['list_slug'])) {
638 # Replace the list and username
639 $linkText = $entity['screen_name'] . $entity['list_slug'];
640 $class = $this->class_list;
641 $url = $this->url_base_list . $linkText;
643 # Replace the username
644 $linkText = $entity['screen_name'];
645 $class = $this->class_user;
646 $url = $this->url_base_user . $linkText;
648 if (!empty($class)) {
649 $attributes['class'] = $class;
651 $attributes['href'] = $url;
653 return $this->linkToText($entity, $linkText, $attributes);
658 * @param array $entity
659 * @param string $tweet
663 public function linkToCashtag($entity, $tweet = null) {
664 if (is_null($tweet)) {
665 $tweet = $this->tweet;
667 $attributes = array();
668 $doller = mb_substr($tweet, $entity['indices'][0], 1);
669 $linkText = $doller . $entity['cashtag'];
670 $attributes['href'] = $this->url_base_cash . $entity['cashtag'];
671 $attributes['title'] = $linkText;
672 if (!empty($this->class_cash)) {
673 $attributes['class'] = $this->class_cash;
676 return $this->linkToText($entity, $linkText, $attributes);
680 * Adds links to all elements in the tweet.
682 * @param boolean $loose if false, using autoLinkEntities
683 * @return string The modified tweet.
684 * @deprecated since version 1.1.0
686 public function addLinks($loose = false) {
688 return $this->autoLink();
692 $original = $this->tweet;
693 $this->tweet = $this->addLinksToURLs($loose);
694 $this->tweet = $this->addLinksToHashtags($loose);
695 $this->tweet = $this->addLinksToCashtags($loose);
696 $this->tweet = $this->addLinksToUsernamesAndLists($loose);
697 $modified = $this->tweet;
698 $this->tweet = $original;
703 * Adds links to hashtag elements in the tweet.
705 * @param boolean $loose if false, using autoLinkEntities
706 * @return string The modified tweet.
707 * @deprecated since version 1.1.0
709 public function addLinksToHashtags($loose = false) {
711 return $this->autoLinkHashtags();
713 return preg_replace_callback(
714 self::$patterns['valid_hashtag'],
715 array($this, '_addLinksToHashtags'),
720 * Adds links to cashtag elements in the tweet.
722 * @param boolean $loose if false, using autoLinkEntities
723 * @return string The modified tweet.
724 * @deprecated since version 1.1.0
726 public function addLinksToCashtags($loose = false) {
728 return $this->autoLinkCashtags();
730 return preg_replace_callback(
731 self::$patterns['valid_cashtag'],
732 array($this, '_addLinksToCashtags'),
737 * Adds links to URL elements in the tweet.
739 * @param boolean $loose if false, using autoLinkEntities
740 * @return string The modified tweet
741 * @deprecated since version 1.1.0.
743 public function addLinksToURLs($loose = false) {
745 return $this->autoLinkURLs();
747 return preg_replace_callback(
748 self::$patterns['valid_url'],
749 array($this, '_addLinksToURLs'),
754 * Adds links to username/list elements in the tweet.
756 * @param boolean $loose if false, using autoLinkEntities
757 * @return string The modified tweet.
758 * @deprecated since version 1.1.0
760 public function addLinksToUsernamesAndLists($loose = false) {
762 return $this->autoLinkUsernamesAndLists();
764 return preg_replace_callback(
765 self::$patterns['valid_mentions_or_lists'],
766 array($this, '_addLinksToUsernamesAndLists'),
772 * @param array $entity
773 * @param string $text
774 * @param array $attributes
778 public function linkToText(array $entity, $text, $attributes = array()) {
780 if ($this->external) $rel[] = 'external';
781 if ($this->nofollow) $rel[] = 'nofollow';
783 $attributes['rel'] = join(' ', $rel);
785 if ($this->target) $attributes['target'] = $this->target;
788 foreach ($attributes as $key => $val) {
789 $link .= ' ' . $key . '="' . $this->escapeHTML($val) . '"';
791 $link .= '>' . $text . '</a>';
798 * @param string $text
801 protected function escapeHTML($text) {
802 return htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false);
806 * Wraps a tweet element in an HTML anchor tag using the provided URL.
808 * This is a helper function to perform the generation of the link.
810 * @param string $url The URL to use as the href.
811 * @param string $class The CSS class(es) to apply (space separated).
812 * @param string $element The tweet element to wrap.
814 * @return string The tweet element with a link applied.
815 * @deprecated since version 1.1.0
817 protected function wrap($url, $class, $element) {
819 if ($class) $link .= ' class="'.$class.'"';
820 $link .= ' href="'.$url.'"';
822 if ($this->external) $rel[] = 'external';
823 if ($this->nofollow) $rel[] = 'nofollow';
824 if (!empty($rel)) $link .= ' rel="'.implode(' ', $rel).'"';
825 if ($this->target) $link .= ' target="'.$this->target.'"';
826 $link .= '>'.$element.'</a>';
831 * Wraps a tweet element in an HTML anchor tag using the provided URL.
833 * This is a helper function to perform the generation of the hashtag link.
835 * @param string $url The URL to use as the href.
836 * @param string $class The CSS class(es) to apply (space separated).
837 * @param string $element The tweet element to wrap.
839 * @return string The tweet element with a link applied.
840 * @deprecated since version 1.1.0
842 protected function wrapHash($url, $class, $element) {
843 $title = preg_replace('/#/u', '#', $element);
845 $link .= ' href="'.$url.'"';
846 $link .= ' title="'.$title.'"';
847 if ($class) $link .= ' class="'.$class.'"';
849 if ($this->external) $rel[] = 'external';
850 if ($this->nofollow) $rel[] = 'nofollow';
851 if (!empty($rel)) $link .= ' rel="'.implode(' ', $rel).'"';
852 if ($this->target) $link .= ' target="'.$this->target.'"';
853 $link .= '>'.$element.'</a>';
858 * Callback used by the method that adds links to hashtags.
860 * @see addLinksToHashtags()
861 * @param array $matches The regular expression matches.
862 * @return string The link-wrapped hashtag.
863 * @deprecated since version 1.1.0
865 protected function _addLinksToHashtags($matches) {
866 list($all, $before, $hash, $tag, $after) = array_pad($matches, 5, '');
867 if (preg_match(self::$patterns['end_hashtag_match'], $after)
868 || (!preg_match('!\A["\']!', $before) && preg_match('!\A["\']!', $after))
869 || preg_match('!\A</!', $after)) {
872 $replacement = $before;
873 $element = $hash . $tag;
874 $url = $this->url_base_hash . $tag;
875 $class_hash = $this->class_hash;
876 if (preg_match(self::$patterns['rtl_chars'], $element)) {
877 $class_hash .= ' rtl';
879 $replacement .= $this->wrapHash($url, $class_hash, $element);
884 * Callback used by the method that adds links to cashtags.
886 * @see addLinksToCashtags()
887 * @param array $matches The regular expression matches.
888 * @return string The link-wrapped cashtag.
889 * @deprecated since version 1.1.0
891 protected function _addLinksToCashtags($matches) {
892 list($all, $before, $cash, $tag, $after) = array_pad($matches, 5, '');
893 if (preg_match(self::$patterns['end_cashtag_match'], $after)
894 || (!preg_match('!\A["\']!', $before) && preg_match('!\A["\']!', $after))
895 || preg_match('!\A</!', $after)) {
898 $replacement = $before;
899 $element = $cash . $tag;
900 $url = $this->url_base_cash . $tag;
901 $replacement .= $this->wrapHash($url, $this->class_cash, $element);
906 * Callback used by the method that adds links to URLs.
908 * @see addLinksToURLs()
909 * @param array $matches The regular expression matches.
910 * @return string The link-wrapped URL.
911 * @deprecated since version 1.1.0
913 protected function _addLinksToURLs($matches) {
914 list($all, $before, $url, $protocol, $domain, $path, $query) = array_pad($matches, 7, '');
915 $url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8', false);
916 if (!$protocol) return $all;
917 return $before . $this->wrap($url, $this->class_url, $url);
921 * Callback used by the method that adds links to username/list pairs.
923 * @see addLinksToUsernamesAndLists()
924 * @param array $matches The regular expression matches.
925 * @return string The link-wrapped username/list pair.
926 * @deprecated since version 1.1.0
928 protected function _addLinksToUsernamesAndLists($matches) {
929 list($all, $before, $at, $username, $slash_listname, $after) = array_pad($matches, 6, '');
930 # If $after is not empty, there is an invalid character.
931 if (!empty($slash_listname)) {
932 # Replace the list and username
933 $element = $username . $slash_listname;
934 $class = $this->class_list;
935 $url = $this->url_base_list . $element;
937 if (preg_match(self::$patterns['end_mention_match'], $after)) return $all;
938 # Replace the username
939 $element = $username;
940 $class = $this->class_user;
941 $url = $this->url_base_user . $element;
943 # XXX: Due to use of preg_replace_callback() for multiple replacements in a
944 # single tweet and also as only the match is replaced and we have to
945 # use a look-ahead for $after because there is no equivalent for the
946 # $' (dollar apostrophe) global from Ruby, we MUST NOT append $after.
947 return $before . $at . $this->wrap($url, $class, $element);