PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/Twitter/Autolink.php

http://github.com/mikenz/twitter-text-php
PHP | 449 lines | 145 code | 37 blank | 267 comment | 13 complexity | ff4190db996bee7e3613b51b29fc8631 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. /**
  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
  7. * @package Twitter
  8. */
  9. require_once 'Regex.php';
  10. /**
  11. * Twitter Autolink Class
  12. *
  13. * Parses tweets and generates HTML anchor tags around URLs, usernames,
  14. * username/list pairs and hashtags.
  15. *
  16. * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
  17. * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
  18. * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
  19. *
  20. * @author Mike Cochrane <mikec@mikenz.geek.nz>
  21. * @author Nick Pope <nick@nickpope.me.uk>
  22. * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
  23. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
  24. * @package Twitter
  25. */
  26. class Twitter_Autolink extends Twitter_Regex {
  27. /**
  28. * CSS class for auto-linked URLs.
  29. *
  30. * @var string
  31. */
  32. protected $class_url = 'url';
  33. /**
  34. * CSS class for auto-linked username URLs.
  35. *
  36. * @var string
  37. */
  38. protected $class_user = 'username';
  39. /**
  40. * CSS class for auto-linked list URLs.
  41. *
  42. * @var string
  43. */
  44. protected $class_list = 'list';
  45. /**
  46. * CSS class for auto-linked hashtag URLs.
  47. *
  48. * @var string
  49. */
  50. protected $class_hash = 'hashtag';
  51. /**
  52. * URL base for username links (the username without the @ will be appended).
  53. *
  54. * @var string
  55. */
  56. protected $url_base_user = 'http://twitter.com/';
  57. /**
  58. * URL base for list links (the username/list without the @ will be appended).
  59. *
  60. * @var string
  61. */
  62. protected $url_base_list = 'http://twitter.com/';
  63. /**
  64. * URL base for hashtag links (the hashtag without the # will be appended).
  65. *
  66. * @var string
  67. */
  68. protected $url_base_hash = 'http://twitter.com/search?q=%23';
  69. /**
  70. * Whether to include the value 'nofollow' in the 'rel' attribute.
  71. *
  72. * @var bool
  73. */
  74. protected $nofollow = true;
  75. /**
  76. * Whether to include the value 'external' in the 'rel' attribute.
  77. *
  78. * Often this is used to be matched on in JavaScript for dynamically adding
  79. * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
  80. * been undeprecated and thus the 'target' attribute can be used. If this is
  81. * set to false then the 'target' attribute will be output.
  82. *
  83. * @var bool
  84. */
  85. protected $external = true;
  86. /**
  87. * The scope to open the link in.
  88. *
  89. * Support for the 'target' attribute was deprecated in HTML 4.01 but has
  90. * since been reinstated in HTML 5. To output the 'target' attribute you
  91. * must disable the adding of the string 'external' to the 'rel' attribute.
  92. *
  93. * @var string
  94. */
  95. protected $target = '_blank';
  96. /**
  97. * Provides fluent method chaining.
  98. *
  99. * @param string $tweet The tweet to be converted.
  100. * @param bool $full_encode Whether to encode all special characters.
  101. *
  102. * @see __construct()
  103. *
  104. * @return Twitter_Autolink
  105. */
  106. public static function create($tweet, $full_encode = false) {
  107. return new self($tweet, $full_encode);
  108. }
  109. /**
  110. * Reads in a tweet to be parsed and converted to contain links.
  111. *
  112. * As the intent is to produce links and output the modified tweet to the
  113. * user, we take this opportunity to ensure that we escape user input.
  114. *
  115. * @see htmlspecialchars()
  116. *
  117. * @param string $tweet The tweet to be converted.
  118. * @param bool $escape Whether to escape the tweet (default: true).
  119. * @param bool $full_encode Whether to encode all special characters.
  120. */
  121. public function __construct($tweet, $escape = true, $full_encode = false) {
  122. if ($escape) {
  123. if ($full_encode) {
  124. parent::__construct(htmlentities($tweet, ENT_QUOTES, 'UTF-8', false));
  125. } else {
  126. parent::__construct(htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false));
  127. }
  128. } else {
  129. parent::__construct($tweet);
  130. }
  131. }
  132. /**
  133. * CSS class for auto-linked URLs.
  134. *
  135. * @return string CSS class for URL links.
  136. */
  137. public function getURLClass() {
  138. return $this->class_url;
  139. }
  140. /**
  141. * CSS class for auto-linked URLs.
  142. *
  143. * @param string $v CSS class for URL links.
  144. *
  145. * @return Twitter_Autolink Fluid method chaining.
  146. */
  147. public function setURLClass($v) {
  148. $this->class_url = trim($v);
  149. return $this;
  150. }
  151. /**
  152. * CSS class for auto-linked username URLs.
  153. *
  154. * @return string CSS class for username links.
  155. */
  156. public function getUsernameClass() {
  157. return $this->class_user;
  158. }
  159. /**
  160. * CSS class for auto-linked username URLs.
  161. *
  162. * @param string $v CSS class for username links.
  163. *
  164. * @return Twitter_Autolink Fluid method chaining.
  165. */
  166. public function setUsernameClass($v) {
  167. $this->class_user = trim($v);
  168. return $this;
  169. }
  170. /**
  171. * CSS class for auto-linked username/list URLs.
  172. *
  173. * @return string CSS class for username/list links.
  174. */
  175. public function getListClass() {
  176. return $this->class_list;
  177. }
  178. /**
  179. * CSS class for auto-linked username/list URLs.
  180. *
  181. * @param string $v CSS class for username/list links.
  182. *
  183. * @return Twitter_Autolink Fluid method chaining.
  184. */
  185. public function setListClass($v) {
  186. $this->class_list = trim($v);
  187. return $this;
  188. }
  189. /**
  190. * CSS class for auto-linked hashtag URLs.
  191. *
  192. * @return string CSS class for hashtag links.
  193. */
  194. public function getHashtagClass() {
  195. return $this->class_hash;
  196. }
  197. /**
  198. * CSS class for auto-linked hashtag URLs.
  199. *
  200. * @param string $v CSS class for hashtag links.
  201. *
  202. * @return Twitter_Autolink Fluid method chaining.
  203. */
  204. public function setHashtagClass($v) {
  205. $this->class_hash = trim($v);
  206. return $this;
  207. }
  208. /**
  209. * Whether to include the value 'nofollow' in the 'rel' attribute.
  210. *
  211. * @return bool Whether to add 'nofollow' to the 'rel' attribute.
  212. */
  213. public function getNoFollow() {
  214. return $this->nofollow;
  215. }
  216. /**
  217. * Whether to include the value 'nofollow' in the 'rel' attribute.
  218. *
  219. * @param bool $v The value to add to the 'target' attribute.
  220. *
  221. * @return Twitter_Autolink Fluid method chaining.
  222. */
  223. public function setNoFollow($v) {
  224. $this->nofollow = $v;
  225. return $this;
  226. }
  227. /**
  228. * Whether to include the value 'external' in the 'rel' attribute.
  229. *
  230. * Often this is used to be matched on in JavaScript for dynamically adding
  231. * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
  232. * been undeprecated and thus the 'target' attribute can be used. If this is
  233. * set to false then the 'target' attribute will be output.
  234. *
  235. * @return bool Whether to add 'external' to the 'rel' attribute.
  236. */
  237. public function getExternal() {
  238. return $this->external;
  239. }
  240. /**
  241. * Whether to include the value 'external' in the 'rel' attribute.
  242. *
  243. * Often this is used to be matched on in JavaScript for dynamically adding
  244. * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
  245. * been undeprecated and thus the 'target' attribute can be used. If this is
  246. * set to false then the 'target' attribute will be output.
  247. *
  248. * @param bool $v The value to add to the 'target' attribute.
  249. *
  250. * @return Twitter_Autolink Fluid method chaining.
  251. */
  252. public function setExternal($v) {
  253. $this->external = $v;
  254. return $this;
  255. }
  256. /**
  257. * The scope to open the link in.
  258. *
  259. * Support for the 'target' attribute was deprecated in HTML 4.01 but has
  260. * since been reinstated in HTML 5. To output the 'target' attribute you
  261. * must disable the adding of the string 'external' to the 'rel' attribute.
  262. *
  263. * @return string The value to add to the 'target' attribute.
  264. */
  265. public function getTarget() {
  266. return $this->target;
  267. }
  268. /**
  269. * The scope to open the link in.
  270. *
  271. * Support for the 'target' attribute was deprecated in HTML 4.01 but has
  272. * since been reinstated in HTML 5. To output the 'target' attribute you
  273. * must disable the adding of the string 'external' to the 'rel' attribute.
  274. *
  275. * @param string $v The value to add to the 'target' attribute.
  276. *
  277. * @return Twitter_Autolink Fluid method chaining.
  278. */
  279. public function setTarget($v) {
  280. $this->target = trim($v);
  281. return $this;
  282. }
  283. /**
  284. * Adds links to all elements in the tweet.
  285. *
  286. * @return string The modified tweet.
  287. */
  288. public function addLinks() {
  289. $original = $this->tweet;
  290. $this->tweet = $this->addLinksToURLs();
  291. $this->tweet = $this->addLinksToHashtags();
  292. $this->tweet = $this->addLinksToUsernamesAndLists();
  293. $modified = $this->tweet;
  294. $this->tweet = $original;
  295. return $modified;
  296. }
  297. /**
  298. * Adds links to hashtag elements in the tweet.
  299. *
  300. * @return string The modified tweet.
  301. */
  302. public function addLinksToHashtags() {
  303. return preg_replace_callback(
  304. self::REGEX_HASHTAG,
  305. array($this, '_addLinksToHashtags'),
  306. $this->tweet);
  307. }
  308. /**
  309. * Adds links to URL elements in the tweet.
  310. *
  311. * @return string The modified tweet.
  312. */
  313. public function addLinksToURLs() {
  314. return preg_replace_callback(
  315. self::$REGEX_VALID_URL,
  316. array($this, '_addLinksToURLs'),
  317. $this->tweet);
  318. }
  319. /**
  320. * Adds links to username/list elements in the tweet.
  321. *
  322. * @return string The modified tweet.
  323. */
  324. public function addLinksToUsernamesAndLists() {
  325. return preg_replace_callback(
  326. self::REGEX_USERNAME_LIST,
  327. array($this, '_addLinksToUsernamesAndLists'),
  328. $this->tweet);
  329. }
  330. /**
  331. * Wraps a tweet element in an HTML anchor tag using the provided URL.
  332. *
  333. * This is a helper function to perform the generation of the link.
  334. *
  335. * @param string $url The URL to use as the href.
  336. * @param string $class The CSS class(es) to apply (space separated).
  337. * @param string $element The tweet element to wrap.
  338. *
  339. * @return string The tweet element with a link applied.
  340. */
  341. protected function wrap($url, $class, $element) {
  342. $link = '<a';
  343. if ($class) $link .= ' class="'.$class.'"';
  344. $link .= ' href="'.$url.'"';
  345. $rel = array();
  346. if ($this->external) $rel[] = 'external';
  347. if ($this->nofollow) $rel[] = 'nofollow';
  348. if (!empty($rel)) $link .= ' rel="'.implode(' ', $rel).'"';
  349. if ($this->target) $link .= ' target="'.$this->target.'"';
  350. $link .= '>'.$element.'</a>';
  351. return $link;
  352. }
  353. /**
  354. * Callback used by the method that adds links to hashtags.
  355. *
  356. * @see addLinksToHashtags()
  357. *
  358. * @param array $matches The regular expression matches.
  359. *
  360. * @return string The link-wrapped hashtag.
  361. */
  362. protected function _addLinksToHashtags($matches) {
  363. $replacement = $matches[1];
  364. $element = $matches[2] . $matches[3];
  365. $url = $this->url_base_hash . $matches[3];
  366. $replacement .= $this->wrap($url, $this->class_hash, $element);
  367. return $replacement;
  368. }
  369. /**
  370. * Callback used by the method that adds links to URLs.
  371. *
  372. * @see addLinksToURLs()
  373. *
  374. * @param array $matches The regular expression matches.
  375. *
  376. * @return string The link-wrapped URL.
  377. */
  378. protected function _addLinksToURLs($matches) {
  379. list($all, $before, $url, $protocol, $domain, $path, $query) = array_pad($matches, 7, '');
  380. $url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8', false);
  381. if (!$protocol) return $all;
  382. return $before . $this->wrap($url, $this->class_url, $url);
  383. }
  384. /**
  385. * Callback used by the method that adds links to username/list pairs.
  386. *
  387. * @see addLinksToUsernamesAndLists()
  388. *
  389. * @param array $matches The regular expression matches.
  390. *
  391. * @return string The link-wrapped username/list pair.
  392. */
  393. protected function _addLinksToUsernamesAndLists($matches) {
  394. list($all, $before, $at, $username, $slash_listname, $after) = array_pad($matches, 6, '');
  395. # If $after is not empty, there is an invalid character.
  396. if (!empty($after)) return $all;
  397. if (!empty($slash_listname)) {
  398. # Replace the list and username
  399. $element = $username . substr($slash_listname, 0, 26);
  400. $class = $this->class_list;
  401. $url = $this->url_base_list . $element;
  402. $postfix = substr($slash_listname, 26);
  403. } else {
  404. # Replace the username
  405. $element = $username;
  406. $class = $this->class_user;
  407. $url = $this->url_base_user . $element;
  408. $postfix = '';
  409. }
  410. return $before . $at . $this->wrap($url, $class, $element) . $postfix . $after;
  411. }
  412. }