├── .github ├── dependabot.yml └── workflows │ ├── main.yaml │ └── pages.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── _config.yml ├── package-lock.json ├── package.json └── src ├── _data ├── 20.json └── jack.json ├── _layouts └── base.html ├── callback.html ├── css └── style.css ├── images ├── sign-in-with-twitter-gray.png ├── sign-in-with-twitter-link.png └── twitter-logo-blue.png ├── index.html ├── profile.html ├── redirect.html └── uptime.md /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: '/' 5 | schedule: 6 | interval: weekly 7 | - package-ecosystem: bundler 8 | directory: '/' 9 | schedule: 10 | interval: weekly 11 | - package-ecosystem: npm 12 | directory: '/' 13 | schedule: 14 | interval: weekly 15 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: Main 2 | on: push 3 | jobs: 4 | lint: 5 | runs-on: ubuntu-latest 6 | name: Lint 7 | steps: 8 | - uses: actions/checkout@v4 9 | - run: npm ci 10 | - run: npm run lint 11 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 2 | name: Deploy Jekyll with GitHub Pages dependencies preinstalled 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ['main'] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: 'pages' 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Build job 26 | build: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | - name: Setup Pages 32 | uses: actions/configure-pages@v5 33 | - name: Build with Jekyll 34 | uses: actions/jekyll-build-pages@v1 35 | with: 36 | source: ./src 37 | destination: ./_site 38 | - name: Upload artifact 39 | uses: actions/upload-pages-artifact@v3 40 | 41 | # Deployment job 42 | deploy: 43 | environment: 44 | name: github-pages 45 | url: ${{ steps.deployment.outputs.page_url }} 46 | runs-on: ubuntu-latest 47 | needs: build 48 | steps: 49 | - name: Deploy to GitHub Pages 50 | id: deployment 51 | uses: actions/deploy-pages@v4 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | cache 3 | .env 4 | node_modules 5 | _site 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | package-lock.json 3 | vendor 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "jekyll" 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.8.7) 5 | public_suffix (>= 2.0.2, < 7.0) 6 | base64 (0.2.0) 7 | bigdecimal (3.1.9) 8 | colorator (1.1.0) 9 | concurrent-ruby (1.3.5) 10 | csv (3.3.2) 11 | em-websocket (0.5.3) 12 | eventmachine (>= 0.12.9) 13 | http_parser.rb (~> 0) 14 | eventmachine (1.2.7) 15 | ffi (1.17.1-arm64-darwin) 16 | ffi (1.17.1-x86_64-linux-gnu) 17 | forwardable-extended (2.6.0) 18 | google-protobuf (4.29.3-arm64-darwin) 19 | bigdecimal 20 | rake (>= 13) 21 | google-protobuf (4.29.3-x86_64-linux) 22 | bigdecimal 23 | rake (>= 13) 24 | http_parser.rb (0.8.0) 25 | i18n (1.14.7) 26 | concurrent-ruby (~> 1.0) 27 | jekyll (4.4.1) 28 | addressable (~> 2.4) 29 | base64 (~> 0.2) 30 | colorator (~> 1.0) 31 | csv (~> 3.0) 32 | em-websocket (~> 0.5) 33 | i18n (~> 1.0) 34 | jekyll-sass-converter (>= 2.0, < 4.0) 35 | jekyll-watch (~> 2.0) 36 | json (~> 2.6) 37 | kramdown (~> 2.3, >= 2.3.1) 38 | kramdown-parser-gfm (~> 1.0) 39 | liquid (~> 4.0) 40 | mercenary (~> 0.3, >= 0.3.6) 41 | pathutil (~> 0.9) 42 | rouge (>= 3.0, < 5.0) 43 | safe_yaml (~> 1.0) 44 | terminal-table (>= 1.8, < 4.0) 45 | webrick (~> 1.7) 46 | jekyll-sass-converter (3.1.0) 47 | sass-embedded (~> 1.75) 48 | jekyll-watch (2.2.1) 49 | listen (~> 3.0) 50 | json (2.9.1) 51 | kramdown (2.5.1) 52 | rexml (>= 3.3.9) 53 | kramdown-parser-gfm (1.1.0) 54 | kramdown (~> 2.0) 55 | liquid (4.0.4) 56 | listen (3.9.0) 57 | rb-fsevent (~> 0.10, >= 0.10.3) 58 | rb-inotify (~> 0.9, >= 0.9.10) 59 | mercenary (0.4.0) 60 | pathutil (0.16.2) 61 | forwardable-extended (~> 2.6) 62 | public_suffix (6.0.1) 63 | rake (13.2.1) 64 | rb-fsevent (0.11.2) 65 | rb-inotify (0.11.1) 66 | ffi (~> 1.0) 67 | rexml (3.4.0) 68 | rouge (4.5.1) 69 | safe_yaml (1.0.5) 70 | sass-embedded (1.83.4-arm64-darwin) 71 | google-protobuf (~> 4.29) 72 | sass-embedded (1.83.4-x86_64-linux-gnu) 73 | google-protobuf (~> 4.29) 74 | terminal-table (3.0.2) 75 | unicode-display_width (>= 1.1.1, < 3) 76 | unicode-display_width (2.6.0) 77 | webrick (1.9.1) 78 | 79 | PLATFORMS 80 | arm64-darwin-22 81 | x86_64-linux 82 | 83 | DEPENDENCIES 84 | jekyll 85 | 86 | BUNDLED WITH 87 | 2.4.18 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Abraham Williams - http://abrah.am - abraham@abrah.am 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TwitterOAuth 2 | 3 | Documentation site for the TwitterOAuth PHP library. 4 | 5 | ## Development 6 | 7 | 1. Install npm packages. 8 | ```console 9 | npm ci 10 | ``` 11 | 1. Install ruby gems 12 | ```console 13 | bundle install 14 | ``` 15 | 1. Visit [http://localhost:4000/](http://localhost:4000/). 16 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | source: src 2 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twitteroauth-com", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "twitteroauth-com", 9 | "version": "0.0.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "prettier": "3.5.3" 13 | } 14 | }, 15 | "node_modules/prettier": { 16 | "version": "3.5.3", 17 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", 18 | "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", 19 | "dev": true, 20 | "license": "MIT", 21 | "bin": { 22 | "prettier": "bin/prettier.cjs" 23 | }, 24 | "engines": { 25 | "node": ">=14" 26 | }, 27 | "funding": { 28 | "url": "https://github.com/prettier/prettier?sponsor=1" 29 | } 30 | } 31 | }, 32 | "dependencies": { 33 | "prettier": { 34 | "version": "3.5.3", 35 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", 36 | "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", 37 | "dev": true 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twitteroauth-com", 3 | "version": "0.0.0", 4 | "description": "The first PHP Library to support OAuth 1.0A for Twitter's REST API.", 5 | "scripts": { 6 | "fix": "prettier --write .", 7 | "lint": "prettier --check .", 8 | "start": "bundle exec jekyll serve --open-url --livereload" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/abraham/twitteroauth-com.git" 13 | }, 14 | "keywords": [ 15 | "twitter", 16 | "api", 17 | "oauth", 18 | "demo", 19 | "rest", 20 | "social" 21 | ], 22 | "author": "Abraham Williams ", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/abraham/twitteroauth-com/issues" 26 | }, 27 | "homepage": "https://twitteroauth.com", 28 | "private": true, 29 | "devDependencies": { 30 | "prettier": "3.5.3" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/_data/20.json: -------------------------------------------------------------------------------- 1 | { 2 | "created_at": "Tue Mar 21 20:50:14 +0000 2006", 3 | "id": 20, 4 | "id_str": "20", 5 | "full_text": "just setting up my twttr", 6 | "truncated": false, 7 | "display_text_range": [0, 24], 8 | "entities": { 9 | "hashtags": [], 10 | "symbols": [], 11 | "user_mentions": [], 12 | "urls": [] 13 | }, 14 | "source": "\u003ca href=\"http://twitter.com\" rel=\"nofollow\"\u003eTwitter Web Client\u003c/a\u003e", 15 | "in_reply_to_status_id": null, 16 | "in_reply_to_status_id_str": null, 17 | "in_reply_to_user_id": null, 18 | "in_reply_to_user_id_str": null, 19 | "in_reply_to_screen_name": null, 20 | "user": { 21 | "id": 12, 22 | "id_str": "12", 23 | "name": "jack", 24 | "screen_name": "jack", 25 | "location": "", 26 | "description": "#bitcoin", 27 | "url": null, 28 | "entities": { "description": { "urls": [] } }, 29 | "protected": false, 30 | "followers_count": 5033667, 31 | "friends_count": 4510, 32 | "listed_count": 28022, 33 | "created_at": "Tue Mar 21 20:50:14 +0000 2006", 34 | "favourites_count": 31461, 35 | "utc_offset": null, 36 | "time_zone": null, 37 | "geo_enabled": true, 38 | "verified": true, 39 | "statuses_count": 27342, 40 | "lang": null, 41 | "contributors_enabled": false, 42 | "is_translator": false, 43 | "is_translation_enabled": false, 44 | "profile_background_color": "EBEBEB", 45 | "profile_background_image_url": "http://abs.twimg.com/images/themes/theme7/bg.gif", 46 | "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme7/bg.gif", 47 | "profile_background_tile": false, 48 | "profile_image_url": "http://pbs.twimg.com/profile_images/1661201415899951105/azNjKOSH_normal.jpg", 49 | "profile_image_url_https": "https://pbs.twimg.com/profile_images/1661201415899951105/azNjKOSH_normal.jpg", 50 | "profile_banner_url": "https://pbs.twimg.com/profile_banners/12/1688241283", 51 | "profile_link_color": "990000", 52 | "profile_sidebar_border_color": "DFDFDF", 53 | "profile_sidebar_fill_color": "F3F3F3", 54 | "profile_text_color": "333333", 55 | "profile_use_background_image": true, 56 | "has_extended_profile": true, 57 | "default_profile": false, 58 | "default_profile_image": false, 59 | "following": false, 60 | "follow_request_sent": false, 61 | "notifications": false, 62 | "translator_type": "regular" 63 | }, 64 | "geo": null, 65 | "coordinates": null, 66 | "place": null, 67 | "contributors": null, 68 | "is_quote_status": false, 69 | "retweet_count": 115869, 70 | "favorite_count": 141616, 71 | "favorited": false, 72 | "retweeted": false, 73 | "lang": "en" 74 | } 75 | -------------------------------------------------------------------------------- /src/_data/jack.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 12, 3 | "id_str": "12", 4 | "name": "jack", 5 | "screen_name": "jack", 6 | "location": "", 7 | "profile_location": null, 8 | "description": "#bitcoin", 9 | "url": null, 10 | "entities": { "description": { "urls": [] } }, 11 | "protected": false, 12 | "followers_count": 5033667, 13 | "friends_count": 4510, 14 | "listed_count": 28022, 15 | "created_at": "Tue Mar 21 20:50:14 +0000 2006", 16 | "favourites_count": 31461, 17 | "utc_offset": null, 18 | "time_zone": null, 19 | "geo_enabled": true, 20 | "verified": true, 21 | "statuses_count": 27342, 22 | "lang": null, 23 | "status": { 24 | "created_at": "Sat Dec 26 00:21:17 +0000 2020", 25 | "id": 1342626501238276096, 26 | "id_str": "1342626501238276096", 27 | "full_text": "RT @PowerDNS_Bert: In this post, we'll reverse-engineer the actual mRNA code of the @BioNTech_Group/@pfizer SARS-CoV-2 vaccine, character f\u2026", 28 | "truncated": false, 29 | "display_text_range": [0, 140], 30 | "entities": { 31 | "hashtags": [], 32 | "symbols": [], 33 | "user_mentions": [ 34 | { 35 | "screen_name": "PowerDNS_Bert", 36 | "name": "Bert Hubert 🇩🇪🇺🇸", 37 | "id": 185980279, 38 | "id_str": "185980279", 39 | "indices": [3, 17] 40 | }, 41 | { 42 | "screen_name": "BioNTech_Group", 43 | "name": "BioNTech SE", 44 | "id": 1060984350995550208, 45 | "id_str": "1060984350995550208", 46 | "indices": [84, 99] 47 | }, 48 | { 49 | "screen_name": "pfizer", 50 | "name": "Pfizer Inc.", 51 | "id": 56488059, 52 | "id_str": "56488059", 53 | "indices": [100, 107] 54 | } 55 | ], 56 | "urls": [] 57 | }, 58 | "source": "\u003ca href=\"http://twitter.com/download/iphone\" rel=\"nofollow\"\u003eTwitter for iPhone\u003c/a\u003e", 59 | "in_reply_to_status_id": null, 60 | "in_reply_to_status_id_str": null, 61 | "in_reply_to_user_id": null, 62 | "in_reply_to_user_id_str": null, 63 | "in_reply_to_screen_name": null, 64 | "geo": null, 65 | "coordinates": null, 66 | "place": null, 67 | "contributors": null, 68 | "retweeted_status": { 69 | "created_at": "Fri Dec 25 20:30:45 +0000 2020", 70 | "id": 1342568484946010116, 71 | "id_str": "1342568484946010116", 72 | "full_text": "In this post, we'll reverse-engineer the actual mRNA code of the @BioNTech_Group/@pfizer SARS-CoV-2 vaccine, character for character. And along the way, this will also explain how the vaccine works. Surprisingly, there are some fun mysteries in there!\nhttps://t.co/HbjPFUXHUG", 73 | "truncated": false, 74 | "display_text_range": [0, 275], 75 | "entities": { 76 | "hashtags": [], 77 | "symbols": [], 78 | "user_mentions": [ 79 | { 80 | "screen_name": "BioNTech_Group", 81 | "name": "BioNTech SE", 82 | "id": 1060984350995550208, 83 | "id_str": "1060984350995550208", 84 | "indices": [65, 80] 85 | }, 86 | { 87 | "screen_name": "pfizer", 88 | "name": "Pfizer Inc.", 89 | "id": 56488059, 90 | "id_str": "56488059", 91 | "indices": [81, 88] 92 | } 93 | ], 94 | "urls": [ 95 | { 96 | "url": "https://t.co/HbjPFUXHUG", 97 | "expanded_url": "https://berthub.eu/articles/posts/reverse-engineering-source-code-of-the-biontech-pfizer-vaccine/", 98 | "display_url": "berthub.eu/articles/posts\u2026", 99 | "indices": [252, 275] 100 | } 101 | ] 102 | }, 103 | "source": "\u003ca href=\"https://mobile.twitter.com\" rel=\"nofollow\"\u003eTwitter Web App\u003c/a\u003e", 104 | "in_reply_to_status_id": null, 105 | "in_reply_to_status_id_str": null, 106 | "in_reply_to_user_id": null, 107 | "in_reply_to_user_id_str": null, 108 | "in_reply_to_screen_name": null, 109 | "geo": null, 110 | "coordinates": null, 111 | "place": null, 112 | "contributors": null, 113 | "is_quote_status": false, 114 | "retweet_count": 3680, 115 | "favorite_count": 7866, 116 | "favorited": false, 117 | "retweeted": false, 118 | "possibly_sensitive": false, 119 | "lang": "en" 120 | }, 121 | "is_quote_status": false, 122 | "retweet_count": 3680, 123 | "favorite_count": 0, 124 | "favorited": false, 125 | "retweeted": false, 126 | "lang": "en" 127 | }, 128 | "contributors_enabled": false, 129 | "is_translator": false, 130 | "is_translation_enabled": false, 131 | "profile_background_color": "EBEBEB", 132 | "profile_background_image_url": "http://abs.twimg.com/images/themes/theme7/bg.gif", 133 | "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme7/bg.gif", 134 | "profile_background_tile": false, 135 | "profile_image_url": "http://pbs.twimg.com/profile_images/1661201415899951105/azNjKOSH_normal.jpg", 136 | "profile_image_url_https": "https://pbs.twimg.com/profile_images/1661201415899951105/azNjKOSH_normal.jpg", 137 | "profile_banner_url": "https://pbs.twimg.com/profile_banners/12/1688241283", 138 | "profile_link_color": "990000", 139 | "profile_sidebar_border_color": "DFDFDF", 140 | "profile_sidebar_fill_color": "F3F3F3", 141 | "profile_text_color": "333333", 142 | "profile_use_background_image": true, 143 | "has_extended_profile": true, 144 | "default_profile": false, 145 | "default_profile_image": false, 146 | "following": false, 147 | "follow_request_sent": false, 148 | "notifications": false, 149 | "translator_type": "regular" 150 | } 151 | -------------------------------------------------------------------------------- /src/_layouts/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TwitterOAuth PHP Library for the Twitter REST API 8 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 26 | 30 | 31 | 35 | 36 | 37 | 41 | 45 | 49 | 50 | 51 | 52 | 56 | 60 | 64 | 65 | 66 | 67 | 71 | 72 | 73 | 74 | 75 | 130 | 131 |
{{ content }}
132 | 133 | 134 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /src/callback.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: TwitterOAuth Callback 3 | layout: base 4 | permalink: callback 5 | --- 6 | 7 |

8 | Finish authorization 9 | 17 |

18 |

19 | The user should now have returned from Twitter having authorized access to 20 | their account. A request_token is a short-lived one time use value so if it is 21 | invalid users will have to start the authorization flow over. 22 |

23 | 24 |

Bootstrapping

25 |

Set up as before but hold off on the TwitterOAuth instance.

26 | 27 |
 28 | require 'vendor/autoload.php';
 29 | use Abraham\TwitterOAuth\TwitterOAuth;
 30 | 
 31 | define('CONSUMER_KEY', getenv('CONSUMER_KEY'));
 32 | define('CONSUMER_SECRET', getenv('CONSUMER_SECRET'));
 33 | define('OAUTH_CALLBACK', getenv('OAUTH_CALLBACK'));
 34 | 
35 | 36 |

Sessions

37 |

38 | Pull the temporary oauth_token back out of sessions. If the oauth_token is 39 | different from the one you sent them to Twitter with, abort the flow and don't 40 | continue with authorization. 41 |

42 | 43 |
 44 | $request_token = [];
 45 | $request_token['oauth_token'] = $_SESSION['oauth_token'];
 46 | $request_token['oauth_token_secret'] = $_SESSION['oauth_token_secret'];
 47 | 
 48 | if (isset($_REQUEST['oauth_token']) && $request_token['oauth_token'] !== $_REQUEST['oauth_token']) {
 49 |     // Abort! Something is wrong.
 50 | }
 51 | 
52 | 53 |

Finish bootstrapping

54 |

Now we make a TwitterOAuth instance with the temporary request token.

55 | 56 |
 57 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $request_token['oauth_token'], $request_token['oauth_token_secret']);
 58 | 
59 | 60 |

Get access_token

61 |

62 | At this point we will use the temporary request token to get the long lived 63 | access_token that authorized to act as the user. 64 |

65 | 66 | Request 67 |
 68 | $access_token = $connection->oauth("oauth/access_token", ["oauth_verifier" => $_REQUEST['oauth_verifier']]);
 69 | 
70 | 71 | Response Cached 72 |
 73 | [
 74 |   "oauth_token" => "62532xx-eWudHldSbIaelX7swmsiHImEL4KinwaGloxxxxxx",
 75 |   "oauth_token_secret" => "2EEfA6BG5ly3sR3XjE0IBSnlQu4ZrUzPiYxxxxxx",
 76 |   "user_id" => "6253282",
 77 |   "screen_name" => "twitterapi"
 78 | ]
 79 | 
80 | 81 |

Credentials storage

82 |

83 | This is the important part where you save the credentials to your database of 84 | choice. 85 |

86 | 87 |
 88 | $_SESSION['access_token'] = $access_token;
 89 | 
90 | 91 |

92 | You now know the users identity and can start interacting with their Twitter 93 | account. 94 |

95 |

96 | Next step: get your profile data 99 |

100 | -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | position: relative; 3 | min-height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 50px; 8 | margin-bottom: 100px; 9 | } 10 | 11 | .page-header { 12 | padding: 40px 15px; 13 | text-align: center; 14 | } 15 | 16 | .footer { 17 | position: absolute; 18 | bottom: 0; 19 | width: 100%; 20 | height: 60px; 21 | background-color: #f5f5f5; 22 | } 23 | 24 | .container .text-muted { 25 | margin: 20px 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/images/sign-in-with-twitter-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/twitteroauth-com/64174f81dd8a846089b0d1d1faf7a335ff35be85/src/images/sign-in-with-twitter-gray.png -------------------------------------------------------------------------------- /src/images/sign-in-with-twitter-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/twitteroauth-com/64174f81dd8a846089b0d1d1faf7a335ff35be85/src/images/sign-in-with-twitter-link.png -------------------------------------------------------------------------------- /src/images/twitter-logo-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/twitteroauth-com/64174f81dd8a846089b0d1d1faf7a335ff35be85/src/images/twitter-logo-blue.png -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: TwitterOAuth PHP Library for the Twitter REST API 3 | layout: base 4 | --- 5 | 6 | 12 | 13 |

14 | Installation 15 | 18 | 24 |

25 | 26 |

With Composer

27 | 28 |

29 | The recommended and easy as pie method is 30 | Composer. Setup require in your 31 | projects composer.json file. Latest release: 32 | 35 |

36 | 37 |
 38 | composer require abraham/twitteroauth
 39 | 
40 | 41 |

Import the TwitterOAuth class.

42 |
 43 | require "vendor/autoload.php";
 44 | 
 45 | use Abraham\TwitterOAuth\TwitterOAuth;
 46 | 
47 | 48 |

Start making API requests.

49 |
 50 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token, $access_token_secret);
 51 | $content = $connection->get("account/verify_credentials");
 52 | 
53 | 54 |

Requirements

55 | 56 |

PHP

57 |

58 | Versions listed as 59 | "active support" or "security fixes" are supported. 60 |

61 | 62 |

Dependencies

63 | 71 | 72 |

Authorization flow

73 |

74 | This site is a working example. 75 | Sign in with Twitter 81 | to see the flow. 82 |

83 | 84 |

Usage

85 |

86 | Unlike many Twitter API libraries, TwitterOAuth doesn't provide a custom 87 | function for every API method. Instead there are a couple of generic functions 88 | so that as Twitter adds features to the API you don't need to update the 89 | library. Here is an example of 90 | GET statuses/home_timeline. 94 |

95 | 96 | HTTP 97 |
 98 | GET https://api.twitter.com/1.1/statuses/home_timeline.json?count=25&exclude_replies=true
 99 | 
100 | 101 | TwitterOAuth 102 |
103 | $statuses = $connection->get("statuses/home_timeline", ["count" => 25, "exclude_replies" => true]);
104 | 
105 | 106 |

v2 API

107 |

108 | v2 API methods are supported by setting the API Version. E.g. 109 | GET /2/users. 113 |

114 | 115 | HTTP 116 |
117 | GET https://api.twitter.com/2/users?id=12
118 | 
119 | 120 | TwitterOAuth 121 |
122 | $connection = new TwitterOAuth(...);
123 | $connection->setApiVersion('2');
124 | $response = $connection->get('users', ['ids' => 12]);
125 | 
126 | 127 |

Methods

128 |

129 | TwitterOAuth provides a couple of minimalist wrappers around Twitter's API 130 | methods. 131 |

132 | 133 |

OAuth

134 |

135 | Only used when authorizing access to a users account. Includes API methods 136 | like 137 | POST oauth/request_token 141 | and 142 | POST oauth/access_token. 146 |

147 | 148 | Example 149 |
150 | $access_token = $connection->oauth("oauth/access_token", ["oauth_verifier" => "nMznkpFRTMCuNMsmALzel9FgPlmWQDWg"]);
151 | 
152 | 153 |

URL

154 |

155 | This is a special wrapper that doesn't hit the API. It builds the URL where 156 | users will authorize access to their account at. Only used for 157 | GET oauth/authorize 161 | and 162 | GET oauth/authenticate. 166 |

167 | 168 | Example 169 |
170 | $url = $connection->url("oauth/authorize", ["oauth_token" => "EaQLH34YD8pgKkUiSp8RbjjOgNxIYVh7"]);
171 | 
172 | 173 |

GET

174 |

175 | API methods that are HTTP GET requests. E.g. 176 | GET search/tweets. 180 |

181 | 182 | HTTP 183 |
184 | GET https://api.twitter.com/1.1/search/tweets.json?q=twitterapi
185 | 
186 | 187 | TwitterOAuth 188 |
189 | $statuses = $connection->get("search/tweets", ["q" => "twitterapi"]);
190 | 
191 | 192 |

POST

193 |

194 | API methods that are HTTP POST requests. E.g. 195 | POST statuses/update. 199 |

200 | 201 | HTTP 202 |
203 | POST https://api.twitter.com/1.1/statuses/update.json?status=hello%20world
204 | 
205 | 206 | TwitterOAuth 207 |
208 | $statues = $connection->post("statuses/update", ["status" => "hello world"]);
209 | 
210 | 211 |

Media

212 |

213 | Upload images using 214 | POST media/upload. This uses the v1.1 API as there is no v2 equivalent. 218 |

219 |

220 | Note: When creating the application in the Twitter Developer Portal, you must 221 | enable Read and Write privileges under Settings > User authentication 222 | settings, as otherwise Tweet creation will fail. 223 |

224 | 225 |
226 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token, $access_token_secret);
227 | $connection->setApiVersion(1.1);
228 | $media1 = $connection->upload('media/upload', ['media' => '/path/to/file/kitten1.jpg']);
229 | $media2 = $connection->upload('media/upload', ['media' => '/path/to/file/kitten2.jpg'], ['chunkedUpload' => true]);
230 | $connection->setApiVersion(2);
231 | $parameters = [
232 |     'text' => 'Meow Meow Meow',
233 |     'media' => ['media_ids' => [$media1->media_id_string, $media2->media_id_string]]
234 | ];
235 | $result = $connection->post('tweets', $parameters, ['jsonPayload' => true]);
236 | 
237 | 238 |

JSON data

239 |

240 | Send JSON data to 241 | POST direct_messages/events/new. 245 |

246 | 247 |
248 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token, $access_token_secret);
249 | $data = [
250 |     'event' => [
251 |         'type' => 'message_create',
252 |         'message_create' => [
253 |             'target' => [
254 |                 'recipient_id' => $userId
255 |             ],
256 |             'message_data' => [
257 |                 'text' => 'Hello World!'
258 |             ]
259 |         ]
260 |     ]
261 | ];
262 | $result = $connection->post('direct_messages/events/new', $data, ['jsonPayload' => true]);
263 | 
264 | 265 |

Streaming

266 |

Streaming is not currently supported.

267 | 268 |

Proxy

269 |

HTTP proxy support can be enabled like this.

270 | 271 |
272 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token, $access_token_secret);
273 | $connection->setProxy([
274 |     'CURLOPT_PROXY' => '127.0.0.0',
275 |     'CURLOPT_PROXYUSERPWD' => '',
276 |     'CURLOPT_PROXYPORT' => 8080,
277 | ]);
278 | 
279 | 280 |

Error handling

281 |

After every request you should validate it was a success.

282 | 283 |
284 | $statues = $connection->post("statuses/update", ["status" => "hello world"]);
285 | if (in_array ($connection->getLastHttpCode(), [200, 201])) {
286 |     // Tweet posted successfully
287 | } else {
288 |     // Handle error case
289 | }
290 | 
291 | 292 |

Changing timeout settings

293 |

294 | If you experience any timeout errors you can change the default timeout 295 | settings for cURL. 296 |

297 | 298 |
299 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token, $access_token_secret);
300 | $connection->setTimeouts(10, 15);
301 | 
302 | 303 |

304 | The first parameter corresponds to the timeout for the connect phase and the 305 | second to the maximum time the request is allowed to take. 306 |

307 | 308 |

Related projects

309 | 310 |

<twitter-user> Web Component

311 |

312 | If you are looking for an easy way to render user profiles on websites, check 313 | out twitter-user. 314 |

315 | 316 | 317 | 318 |

<twitter-status> Web Component

319 |

320 | If you are looking for an easy way to render tweets on websites, check out 321 | twitter-status. 322 |

323 | 324 | 325 | 326 |

TypeScript types

327 |

328 | Easy TypeScript types for Twitter API objects with 329 | twitter-d.ts. 330 |

331 | 332 |

Test data in Ruby

333 |

334 | Easily mock test Twitter data with 335 | 336 | Faker::Twitter 337 | 338 | . 339 |

340 | -------------------------------------------------------------------------------- /src/profile.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: TwitterOAuth Profile 3 | layout: base 4 | permalink: profile 5 | --- 6 | 7 |

8 | User details 9 | 17 |

18 |

19 | The user has now authorized access to their Twitter account. The access_token 20 | is specific to the user so anytime you want to act as the user create a 21 | TwitterOAuth instance and start making requests. 22 |

23 | 24 |

Credential storage

25 |

26 | Pull the long-lived credentials out of storage. This example uses basic PHP 27 | sessions but your implementation should use a database like 28 | PostgreSQL/MongoDB/etc. 29 |

30 | 31 |
32 | $access_token = $_SESSION['access_token'];
33 | 
34 | 35 |

Authenticated requests

36 |

Now we make a TwitterOAuth instance with the users access_token.

37 | 38 |
39 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token['oauth_token'], $access_token['oauth_token_secret']);
40 | 
41 | 42 |

Get account details

43 |

44 | At this point we will use the access token that is authorized to act as the 45 | user, to get their account details. 46 |

47 | 48 | Request 49 |
50 | $user = $connection->get('account/verify_credentials', ['tweet_mode' => 'extended', 'include_entities' => 'true']);
51 | 
52 | 53 | Response Cached 54 |
55 | 65 | 66 |
67 | {{ site.data.jack }}
68 | 
69 | 70 |

You now have the authenticated user's Twitter account details.

71 | 72 | 73 | 74 |
75 | 76 | -------------------------------------------------------------------------------- /src/redirect.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: TwitterOAuth Redirect 3 | layout: base 4 | permalink: redirect 5 | --- 6 | 7 |

8 | Starting authorization 9 | 17 |

18 |

19 | Generating a request_token should only happen when a user shows intent to sign 20 | into your site. It requires making an API request to Twitter. In your 21 | implementation this page should generally have no HTML rendered and instead do 22 | a redirect to the generated URL. 23 |

24 | 25 |

Bootstrapping

26 |

27 | First we set need to autoload the TwitterOAuth class and the need Twitter 28 | application details. We will also construct a TwitterOAuth instance with the 29 | application consumer_key and consumer_secret. 30 |

31 | 32 |
 33 | require 'vendor/autoload.php';
 34 | use Abraham\TwitterOAuth\TwitterOAuth;
 35 | 
 36 | define('CONSUMER_KEY', getenv('CONSUMER_KEY'));
 37 | define('CONSUMER_SECRET', getenv('CONSUMER_SECRET'));
 38 | define('OAUTH_CALLBACK', getenv('OAUTH_CALLBACK'));
 39 | 
 40 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET);
 41 | 
42 | 43 |

Generating a request_token

44 |

45 | Authorizing access to a users account through OAuth starts with getting a 46 | temporary request_token. This request_token is only good for a few minutes and 47 | will soon be forgotten about. 48 |

49 | 50 | Request 51 |
 52 | $request_token = $connection->oauth('oauth/request_token', array('oauth_callback' => OAUTH_CALLBACK));
 53 | 
54 | 55 | Response Cached 56 |
 57 | [
 58 |   "oauth_token" => "zlgW3QAAAAAA2_NZAAABfxxxxxxk",
 59 |   "oauth_token_secret" => "pBYEQzdbyMqIcyDzyn0X7LDxxxxxxxxx",
 60 |   "oauth_callback_confirmed" => "true"
 61 | ]
 62 | 
63 | 64 |

Sessions

65 |

66 | This demo site uses basic PHP sessions but you can use whatever 67 | session/storage implementation you want. 68 |

69 | 70 |
 71 | $_SESSION['oauth_token'] = $request_token['oauth_token'];
 72 | $_SESSION['oauth_token_secret'] = $request_token['oauth_token_secret'];
 73 | 
74 | 75 |

Build authorize URL

76 |

77 | Here we are building a URL the authorizing users must navigate to in their 78 | browser. It is to Twitter's authorize page where the list of permissions being 79 | granted is displayed along with allow/deny buttons. 80 |

81 | 82 | Request 83 |
 84 | $url = $connection->url('oauth/authorize', array('oauth_token' => $request_token['oauth_token']));
 85 | 
86 | 87 | Response Cached 88 |
 89 | https://api.twitter.com/oauth/authorize?oauth_token=Z6eEdO8MOmk394WozF5oKyuAv855l4Mlqo7hxxxxxx
 90 | 
91 | 92 |

Authorize on twitter.com

93 |

94 | Redirect the user to Twitter where they will authorize your application to 95 | access their account and be redirected back to the `OAUTH_CALLBACK` URL. 96 |

97 | 98 |

99 | Next step: callback 100 |

101 | -------------------------------------------------------------------------------- /src/uptime.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Uptime 3 | permalink: uptime 4 | --- 5 | 6 | ok 7 | --------------------------------------------------------------------------------