{"id":20,"date":"2013-08-27T02:57:14","date_gmt":"2013-08-27T02:57:14","guid":{"rendered":"http:\/\/www.webtipblog.com\/?p=20"},"modified":"2013-11-27T20:40:19","modified_gmt":"2013-11-27T20:40:19","slug":"add-links-to-twitter-mentions-hashtags-and-urls-with-php-and-the-twitter-1-1-oauth-api","status":"publish","type":"post","link":"https:\/\/www.webtipblog.com\/add-links-to-twitter-mentions-hashtags-and-urls-with-php-and-the-twitter-1-1-oauth-api\/","title":{"rendered":"Add Links to Twitter Mentions, Hashtags, and URLs with PHP and the Twitter 1.1 oAuth API"},"content":{"rendered":"
If you’re using the Twitter v1.1 API to fetch a user’s statuses from their timeline, then you’ve likely come across the fact that user mentions, hashtags, and urls do not have links in the “text” node of the JSON response the API returns. There is no <a> element to follow the link, and there are no links to follow to check out a mention or hashtag on Twitter site. This can easily be overcome using PHP and parsing some of the data in the JSON response and wrapping the entities in the desired <a> elements. <\/p>\n
For example, a tweet that looks like this:<\/p>\n
@joesexton00 contributes to #webtipblog, check it out at www.webtipblog.com!<\/pre>\nwill look like this after we run it through our function:<\/p>\n
<a href='http:\/\/twitter.com\/joesexton00' target='_blank'>@joesexton00<\/a> contributes to <a href='http:\/\/twitter.com\/search?q=%23webtipblog&src=hash' target='_blank'>#webtipblog<\/a>, check it out at <a href='http:\/\/www.webtipblog.com' target='_blank'>www.webtipblog.com<\/a>!<\/pre>\n
\nFor reference, the API will return JSON that looks like this when you make a valid request for statuses\/user_timeline per the documentation<\/a>:<\/p>\n{\r\n \"coordinates\": null,\r\n \"favorited\": false,\r\n \"truncated\": false,\r\n \"created_at\": \"Wed Aug 29 17:12:58 +0000 2012\",\r\n \"id_str\": \"240859602684612608\",\r\n \"entities\": {\r\n \"urls\": [\r\n {\r\n \"expanded_url\": \"https:\/\/dev.twitter.com\/blog\/twitter-certified-products\",\r\n \"url\": \"https:\/\/t.co\/MjJ8xAnT\",\r\n \"indices\": [\r\n 52,\r\n 73\r\n ],\r\n \"display_url\": \"dev.twitter.com\/blog\/twitter-c\\u2026\"\r\n }\r\n ],\r\n \"hashtags\": [\r\n\r\n ],\r\n \"user_mentions\": [\r\n\r\n ]\r\n },\r\n \"in_reply_to_user_id_str\": null,\r\n \"contributors\": null,\r\n \"text\": \"Introducing the Twitter Certified Products Program: https:\/\/t.co\/MjJ8xAnT\",\r\n \"retweet_count\": 121,\r\n \"in_reply_to_status_id_str\": null,\r\n \"id\": 240859602684612608,\r\n \"geo\": null,\r\n \"retweeted\": false,\r\n \"possibly_sensitive\": false,\r\n \"in_reply_to_user_id\": null,\r\n \"place\": null,\r\n \"user\": {\r\n \"profile_sidebar_fill_color\": \"DDEEF6\",\r\n \"profile_sidebar_border_color\": \"C0DEED\",\r\n \"profile_background_tile\": false,\r\n \"name\": \"Twitter API\",\r\n \"profile_image_url\": \"http:\/\/a0.twimg.com\/profile_images\/2284174872\/7df3h38zabcvjylnyfe3_normal.png\",\r\n \"created_at\": \"Wed May 23 06:01:13 +0000 2007\",\r\n \"location\": \"San Francisco, CA\",\r\n \"follow_request_sent\": false,\r\n \"profile_link_color\": \"0084B4\",\r\n \"is_translator\": false,\r\n \"id_str\": \"6253282\",\r\n \"entities\": {\r\n \"url\": {\r\n \"urls\": [\r\n {\r\n \"expanded_url\": null,\r\n \"url\": \"http:\/\/dev.twitter.com\",\r\n \"indices\": [\r\n 0,\r\n 22\r\n ]\r\n }\r\n ]\r\n },\r\n \"description\": {\r\n \"urls\": [\r\n\r\n ]\r\n }\r\n },\r\n \"default_profile\": true,\r\n \"contributors_enabled\": true,\r\n \"favourites_count\": 24,\r\n \"url\": \"http:\/\/dev.twitter.com\",\r\n \"profile_image_url_https\": \"https:\/\/si0.twimg.com\/profile_images\/2284174872\/7df3h38zabcvjylnyfe3_normal.png\",\r\n \"utc_offset\": -28800,\r\n \"id\": 6253282,\r\n \"profile_use_background_image\": true,\r\n \"listed_count\": 10775,\r\n \"profile_text_color\": \"333333\",\r\n \"lang\": \"en\",\r\n \"followers_count\": 1212864,\r\n \"protected\": false,\r\n \"notifications\": null,\r\n \"profile_background_image_url_https\": \"https:\/\/si0.twimg.com\/images\/themes\/theme1\/bg.png\",\r\n \"profile_background_color\": \"C0DEED\",\r\n \"verified\": true,\r\n \"geo_enabled\": true,\r\n \"time_zone\": \"Pacific Time (US & Canada)\",\r\n \"description\": \"The Real Twitter API. I tweet about API changes, service issues and happily answer questions about Twitter and our API. Don't get an answer? It's on my website.\",\r\n \"default_profile_image\": false,\r\n \"profile_background_image_url\": \"http:\/\/a0.twimg.com\/images\/themes\/theme1\/bg.png\",\r\n \"statuses_count\": 3333,\r\n \"friends_count\": 31,\r\n \"following\": null,\r\n \"show_all_inline_media\": false,\r\n \"screen_name\": \"twitterapi\"\r\n },\r\n \"in_reply_to_screen_name\": null,\r\n \"source\": \"<a href=\\\"http:\/\/sites.google.com\/site\/yorufukurou\/\\\" rel=\\\"nofollow\\\">YoruFukurou<\/a>\",\r\n \"in_reply_to_status_id\": null\r\n },<\/pre>\n
\nAs you can see, we are given the raw tweet text in the ‘text” node, with no links, so we’re left to our own devices to add the <a> elements. Note that we are given an “entities” node that has details about any link, mention, or hashtag entities within the tweet text. The “entities” node has the index of the entity location within the text node, so we can use that to find the original raw entity text within the tweet. We can wrap that entity with a link to back to Twitter and just replace the original text with the new attribute. <\/p>\n
\nThis PHP function that will take a single node and return the formatted text: <\/p>\n\/**\r\n * addTweetEntityLinks\r\n *\r\n * adds a link around any entities in a twitter feed\r\n * twitter entities include urls, user mentions, and hashtags\r\n *\r\n * @author Joe Sexton <joe@webtipblog.com>\r\n * @param object $tweet a JSON tweet object v1.1 API\r\n * @return string tweet\r\n *\/\r\nfunction addTweetEntityLinks( $tweet )\r\n{\r\n\t\/\/ actual tweet as a string\r\n\t$tweetText = $tweet->text;\r\n\r\n\t\/\/ create an array to hold urls\r\n\t$tweetEntites = array();\r\n\r\n\t\/\/ add each url to the array\r\n\tforeach( $tweet->entities->urls as $url ) {\r\n\t\t$tweetEntites[] = array (\r\n\t\t\t\t'type' => 'url',\r\n\t\t\t\t'curText' => substr( $tweetText, $url->indices[0], ( $url->indices[1] - $url->indices[0] ) ),\r\n\t\t\t\t'newText' => \"<a href='\".$url->expanded_url.\"' target='_blank'>\".$url->display_url.\"<\/a>\"\r\n\t\t\t);\r\n\t} \/\/ end foreach\r\n\r\n\t\/\/ add each user mention to the array\r\n\tforeach ( $tweet->entities->user_mentions as $mention ) {\r\n\t\t$string = substr( $tweetText, $mention->indices[0], ( $mention->indices[1] - $mention->indices[0] ) );\r\n\t\t$tweetEntites[] = array (\r\n\t\t\t\t'type' => 'mention',\r\n\t\t\t\t'curText' => substr( $tweetText, $mention->indices[0], ( $mention->indices[1] - $mention->indices[0] ) ),\r\n\t\t\t\t'newText' => \"<a href='http:\/\/twitter.com\/\".$mention->screen_name.\"' target='_blank'>\".$string.\"<\/a>\"\r\n\t\t\t);\r\n\t} \/\/ end foreach\r\n\r\n\t\/\/ add each hashtag to the array\r\n\tforeach ( $tweet->entities->hashtags as $tag ) {\r\n\t\t$string = substr( $tweetText, $tag->indices[0], ( $tag->indices[1] - $tag->indices[0] ) );\r\n\t\t$tweetEntites[] = array (\r\n\t\t\t\t'type' => 'hashtag',\r\n\t\t\t\t'curText' => substr( $tweetText, $tag->indices[0], ( $tag->indices[1] - $tag->indices[0] ) ),\r\n\t\t\t\t'newText' => \"<a href='http:\/\/twitter.com\/search?q=%23\".$tag->text.\"&src=hash' target='_blank'>\".$string.\"<\/a>\"\r\n\t\t\t);\r\n\t} \/\/ end foreach\r\n\r\n\t\/\/ replace the old text with the new text for each entity\r\n\tforeach ( $tweetEntites as $entity ) {\r\n\t\t$tweetText = str_replace( $entity['curText'], $entity['newText'], $tweetText );\r\n\t} \/\/ end foreach\r\n\r\n\treturn $tweetText;\r\n\r\n} \/\/ end addTweetEntityLinks()<\/pre>\nNote that the JSON response does not have urls for any mentions or hashtags so the function builds those urls.<\/small><\/p>\n
\nAll that’s left is to loop through the JSON response and process each tweet:<\/p>\n\/\/ $data = Twitter JSON response...\r\n\r\nif ( !empty( $data ) ) {\r\n foreach ( $data as $tweet ) {\r\n $tweetText = addTweetEntityLinks( $tweet ); \r\n \/\/ use $tweetText however you want to\r\n }\r\n}<\/pre>\n\nSee my article on formatting the date in the Twitter REST API<\/a>.\n<\/p>\n
\nAlso see my PHP Twitter class on Github<\/a> that extends Abraham\u2019s Twitter OAuth library<\/a> and adds date formatting and entity links automatically.<\/p>\n
Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"
If you’re using the Twitter v1.1 API to fetch a user’s statuses from their timeline, then you’ve likely come across the fact that user mentions, hashtags, and urls do not have links in the “text” node of the JSON response the API returns. There is no <a> element to follow the link, and there are […]<\/p>\n","protected":false},"author":1,"featured_media":316,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[11],"_links":{"self":[{"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/posts\/20"}],"collection":[{"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/comments?post=20"}],"version-history":[{"count":16,"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/posts\/20\/revisions"}],"predecessor-version":[{"id":323,"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/posts\/20\/revisions\/323"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/media\/316"}],"wp:attachment":[{"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/media?parent=20"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/categories?post=20"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webtipblog.com\/wp-json\/wp\/v2\/tags?post=20"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}