Fancybox is deprecated

This commit is contained in:
Michael 2024-11-09 18:24:19 +00:00 committed by Tobias Diekershoff
commit 5f781acfdf
234 changed files with 29623 additions and 0 deletions

View file

@ -0,0 +1,14 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/'
schedule:
interval: daily
time: '11:00'
open-pull-requests-limit: 10
- package-ecosystem: composer
directory: '/'
schedule:
interval: daily
time: '11:00'
open-pull-requests-limit: 10

View file

@ -0,0 +1,12 @@
name: Lint
on: push
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- run: npm ci
- run: npm run lint

View file

@ -0,0 +1,18 @@
name: Test
on: push
jobs:
run:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-versions: ['7.2', '7.3', '7.4', '8.0']
name: PHP ${{ matrix.php-versions }}
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
- run: composer validate --no-interaction --strict
- run: composer install --no-interaction --prefer-dist
- run: npm test

View file

@ -0,0 +1,5 @@
.DS_Store
vendor
env
*.cache
node_modules

View file

@ -0,0 +1,3 @@
vendor
composer.lock
tests/fixtures

View file

@ -0,0 +1,6 @@
{
"singleQuote": true,
"phpVersion": "7.2",
"trailingCommaPHP": true,
"braceStyle": "psr-2"
}

View file

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at abraham@abrah.am. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -0,0 +1,21 @@
# Contributing to TwitterOAuth
## 👏 Thanks!
Thanks for your interest in contributing to TwitterOAuth. We appreciate contributions small and large.
## 🌱 Grow
If you have an idea for something new or would like to improve something. Please [open a quick issue](https://github.com/abraham/twitteroauth/issues/new) explaining the changes and the reasons for them. Everyone's time is important and we don't want you duplicating work someone else might already be working on.
GitHub has [outlined instructions](https://help.github.com/articles/fork-a-repo/) for forking a repo. To work on an update to this repo, you will:
- Fork the repo
- Make the changes
- Submit a pull request
Once the [pull request](https://help.github.com/articles/about-pull-requests/) is reviewed, if the changes are approved they will be merged in to the project.
## 🐛 Bugs
Please [open a new issue](https://github.com/abraham/twitteroauth/issues/new) and details what you are trying to do, what is happening, and what you expect to happen. Err on the side of providing more details.

View file

@ -0,0 +1,22 @@
Copyright (c) 2009 Abraham Williams - http://abrah.am - abraham@abrah.am
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,11 @@
<span itemprop="name">TwitterOAuth</span> [![Build Status](https://github.com/abraham/twitteroauth/workflows/Test/badge.svg)](https://github.com/abraham/twitteroauth/actions) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/abraham/twitteroauth/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/abraham/twitteroauth/?branch=master) [![Issues Count](https://img.shields.io/github/issues/abraham/twitteroauth.svg)](https://github.com/abraham/twitteroauth/issues) [![Latest Version](https://img.shields.io/packagist/v/abraham/twitteroauth.svg)](https://packagist.org/packages/abraham/twitteroauth) [![Downloads this Month](https://img.shields.io/packagist/dm/abraham/twitteroauth.svg)](https://packagist.org/packages/abraham/twitteroauth)
---
<p itemprop="description">The most popular PHP library for Twitter's OAuth REST API.</p>
See documentation at https://twitteroauth.com.
PHP versions [listed](https://secure.php.net/supported-versions.php) as "active support" or "security fixes only" are supported.
<img src="https://raw.githubusercontent.com/abraham/twitteroauth-com/master/images/twitter-logo-blue.png" itemprop="image" alt="Twitter bird" width="200px">

View file

@ -0,0 +1,35 @@
<?php
/**
* Use to autoload needed classes without Composer.
*
* @param string $class The fully-qualified class name.
* @return void
*/
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = 'Abraham\\TwitterOAuth\\';
// base directory for the namespace prefix
$base_dir = __DIR__ . '/src/';
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// if the file exists, require it
if (file_exists($file)) {
require $file;
}
});

View file

@ -0,0 +1,55 @@
{
"name": "abraham/twitteroauth",
"type": "library",
"description": "The most popular PHP library for use with the Twitter OAuth REST API.",
"keywords": [
"twitter",
"api",
"oauth",
"rest",
"social",
"twitter api",
"twitter oauth"
],
"license": "MIT",
"homepage": "https://twitteroauth.com",
"authors": [
{
"name": "Abraham Williams",
"email": "abraham@abrah.am",
"homepage": "https://abrah.am",
"role": "Developer"
}
],
"support": {
"source": "https://github.com/abraham/twitteroauth",
"issues": "https://github.com/abraham/twitteroauth/issues"
},
"repositories": [
{
"type": "git",
"url": "https://github.com/morozov/php-vcr"
},
{
"type": "git",
"url": "https://github.com/abraham/phpunit-testlistener-vcr"
}
],
"require": {
"php": "^7.2 || ^7.3 || ^7.4 || ^8.0",
"ext-curl": "*",
"composer/ca-bundle": "^1.2"
},
"require-dev": {
"phpunit/phpunit": "^8",
"squizlabs/php_codesniffer": "^3",
"phpmd/phpmd": "^2",
"php-vcr/php-vcr": "^1",
"php-vcr/phpunit-testlistener-vcr": "dev-php-8"
},
"autoload": {
"psr-4": {
"Abraham\\TwitterOAuth\\": "src"
}
}
}

2761
x-twitter/vendor/abraham/twitteroauth/composer.lock generated vendored Normal file

File diff suppressed because it is too large Load diff

511
x-twitter/vendor/abraham/twitteroauth/package-lock.json generated vendored Normal file
View file

@ -0,0 +1,511 @@
{
"name": "twitteroauth",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@prettier/plugin-php": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/@prettier/plugin-php/-/plugin-php-0.16.0.tgz",
"integrity": "sha512-HG/FamMUtq4/9hZmeuvwy0BWmOr4m9OWacvLSUmmgUrQd4+TRZW7Nqs2MAJkwSNiBIgAKTt2Vw9PK+33Gxxx8g==",
"dev": true,
"requires": {
"linguist-languages": "^7.5.1",
"mem": "^8.0.0",
"php-parser": "3.0.2"
}
},
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"dependencies": {
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"cliui": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"dev": true,
"requires": {
"string-width": "^3.1.0",
"strip-ansi": "^5.2.0",
"wrap-ansi": "^5.1.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"concurrently": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.3.0.tgz",
"integrity": "sha512-8MhqOB6PWlBfA2vJ8a0bSFKATOdWlHiQlk11IfmQBPaHVP8oP2gsh2MObE6UR3hqDHqvaIvLTyceNW6obVuFHQ==",
"dev": true,
"requires": {
"chalk": "^2.4.2",
"date-fns": "^2.0.1",
"lodash": "^4.17.15",
"read-pkg": "^4.0.1",
"rxjs": "^6.5.2",
"spawn-command": "^0.0.2-1",
"supports-color": "^6.1.0",
"tree-kill": "^1.2.2",
"yargs": "^13.3.0"
}
},
"date-fns": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.15.0.tgz",
"integrity": "sha512-ZCPzAMJZn3rNUvvQIMlXhDr4A+Ar07eLeGsGREoWU19a3Pqf5oYa+ccd+B3F6XVtQY6HANMFdOQ8A+ipFnvJdQ==",
"dev": true
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"dev": true
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
"linguist-languages": {
"version": "7.11.1",
"resolved": "https://registry.npmjs.org/linguist-languages/-/linguist-languages-7.11.1.tgz",
"integrity": "sha512-+cRUk+1WTbydcdzipXQER2iilX+wMrb1LPkbkGuDP/IcGPJRDmOZH6Olf1iH6sHlHwPnJYiNJH39YsFCVZxvUQ==",
"dev": true
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"lodash": {
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"dev": true
},
"map-age-cleaner": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
"integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
"dev": true,
"requires": {
"p-defer": "^1.0.0"
}
},
"mem": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/mem/-/mem-8.0.0.tgz",
"integrity": "sha512-qrcJOe6uD+EW8Wrci1Vdiua/15Xw3n/QnaNXE7varnB6InxSk7nu3/i5jfy3S6kWxr8WYJ6R1o0afMUtvorTsA==",
"dev": true,
"requires": {
"map-age-cleaner": "^0.1.3",
"mimic-fn": "^3.1.0"
}
},
"mimic-fn": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz",
"integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==",
"dev": true
},
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"dev": true,
"requires": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
}
},
"p-defer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
"integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
"dev": true
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"php-parser": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/php-parser/-/php-parser-3.0.2.tgz",
"integrity": "sha512-a7y1+odEGsceLDLpu7oNyspZ0pK8FMWJOoim4/yd82AtnEZNLdCLZ67arnOQZ9K0lHJiSp4/7lVUpGELVxE14w==",
"dev": true
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"prettier": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
"dev": true
},
"prettier-plugin-package": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-package/-/prettier-plugin-package-1.3.0.tgz",
"integrity": "sha512-KPNHR/Jm2zTevBp1SnjzMnooO1BOQW2bixVbOp8flOJoW+dxdDwEncObfsKZdkjwrv6AIH4oWqm5EO/etDmK9Q==",
"dev": true
},
"read-pkg": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz",
"integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=",
"dev": true,
"requires": {
"normalize-package-data": "^2.3.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0"
}
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
},
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
},
"rxjs": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
"integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true
},
"spawn-command": {
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
"dev": true
},
"spdx-correct": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
"integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
"dev": true,
"requires": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-exceptions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
"dev": true
},
"spdx-expression-parse": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
"dev": true,
"requires": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-license-ids": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
"integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true
},
"tslib": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
"dev": true
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
"requires": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
},
"wrap-ansi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
"string-width": "^3.0.0",
"strip-ansi": "^5.0.0"
}
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yargs": {
"version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
"dev": true,
"requires": {
"cliui": "^5.0.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.1.2"
}
},
"yargs-parser": {
"version": "13.1.2",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
}

View file

@ -0,0 +1,44 @@
{
"name": "twitteroauth",
"version": "0.0.0",
"description": "The most popular PHP library for use with the Twitter OAuth REST API.",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/abraham/twitteroauth.git"
},
"author": "Abraham Williams <abraham@abrah.am>",
"homepage": "https://github.com/abraham/twitteroauth#readme",
"bugs": {
"url": "https://github.com/abraham/twitteroauth/issues"
},
"scripts": {
"fix": "concurrently npm:fix:*",
"fix:phpcbf": "./vendor/bin/phpcbf src tests --standard=PSR12",
"fix:prettier": "prettier . --write",
"lint": "concurrently npm:lint:*",
"lint:phpcs": "./vendor/bin/phpcs src tests --standard=PSR12",
"lint:prettier": "prettier . --check",
"postinstall": "composer install --no-interaction",
"test": "./vendor/bin/phpunit"
},
"keywords": [
"twitter",
"api",
"oauth",
"rest",
"social",
"twitter-api",
"twitter-oauth"
],
"dependencies": {},
"devDependencies": {
"@prettier/plugin-php": "0.16.0",
"concurrently": "^5.3.0",
"prettier": "2.2.1",
"prettier-plugin-package": "1.3.0"
},
"directories": {
"test": "tests"
}
}

View file

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<ruleset name="My first PHPMD rule set"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>Keep TwitterOAuth source code clean.</description>
<!-- <rule ref="rulesets/cleancode.xml" /> -->
<rule ref="rulesets/codesize.xml" />
<rule ref="rulesets/controversial.xml" />
<rule ref="rulesets/design.xml" />
<rule ref="rulesets/naming.xml" />
<rule ref="rulesets/unusedcode.xml" />
</ruleset>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
bootstrap="tests/bootstrap.php"
testdox="true">
<testsuites>
<testsuite name="TwitterOAuth Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<listeners>
<listener
class="VCR\PHPUnit\TestListener\VCRTestListener"
file="vendor/php-vcr/phpunit-testlistener-vcr/src/VCRTestListener.php" />
</listeners>
</phpunit>

View file

@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**
* Handle setting and storing config for TwitterOAuth.
*
* @author Abraham Williams <abraham@abrah.am>
*/
class Config
{
/** @var int How long to wait for a response from the API */
protected $timeout = 5;
/** @var int how long to wait while connecting to the API */
protected $connectionTimeout = 5;
/** @var int How many times we retry request when API is down */
protected $maxRetries = 0;
/** @var int Delay in seconds before we retry the request */
protected $retriesDelay = 1;
/**
* Decode JSON Response as associative Array
*
* @see http://php.net/manual/en/function.json-decode.php
*
* @var bool
*/
protected $decodeJsonAsArray = false;
/** @var string User-Agent header */
protected $userAgent = 'TwitterOAuth (+https://twitteroauth.com)';
/** @var array Store proxy connection details */
protected $proxy = [];
/** @var bool Whether to encode the curl requests with gzip or not */
protected $gzipEncoding = true;
/** @var integer Size for Chunked Uploads */
protected $chunkSize = 250000; // 0.25 MegaByte
/**
* Set the connection and response timeouts.
*
* @param int $connectionTimeout
* @param int $timeout
*/
public function setTimeouts(int $connectionTimeout, int $timeout): void
{
$this->connectionTimeout = $connectionTimeout;
$this->timeout = $timeout;
}
/**
* Set the number of times to retry on error and how long between each.
*
* @param int $maxRetries
* @param int $retriesDelay
*/
public function setRetries(int $maxRetries, int $retriesDelay): void
{
$this->maxRetries = $maxRetries;
$this->retriesDelay = $retriesDelay;
}
/**
* @param bool $value
*/
public function setDecodeJsonAsArray(bool $value): void
{
$this->decodeJsonAsArray = $value;
}
/**
* @param string $userAgent
*/
public function setUserAgent(string $userAgent): void
{
$this->userAgent = $userAgent;
}
/**
* @param array $proxy
*/
public function setProxy(array $proxy): void
{
$this->proxy = $proxy;
}
/**
* Whether to encode the curl requests with gzip or not.
*
* @param boolean $gzipEncoding
*/
public function setGzipEncoding(bool $gzipEncoding): void
{
$this->gzipEncoding = $gzipEncoding;
}
/**
* Set the size of each part of file for chunked media upload.
*
* @param int $value
*/
public function setChunkSize(int $value): void
{
$this->chunkSize = $value;
}
}

View file

@ -0,0 +1,43 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
class Consumer
{
/** @var string */
public $key;
/** @var string */
public $secret;
/** @var string|null */
public $callbackUrl;
/**
* @param string|null $key
* @param string|null $secret
* @param null $callbackUrl
*/
public function __construct(
?string $key,
?string $secret,
?string $callbackUrl = null
) {
$this->key = $key;
$this->secret = $secret;
$this->callbackUrl = $callbackUrl;
}
/**
* @return string
*/
public function __toString()
{
return "Consumer[key=$this->key,secret=$this->secret]";
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**
* The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
* where the Signature Base String is the text and the key is the concatenated values (each first
* encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
* character (ASCII code 38) even if empty.
* - Chapter 9.2 ("HMAC-SHA1")
*/
class HmacSha1 extends SignatureMethod
{
/**
* {@inheritDoc}
*/
public function getName()
{
return 'HMAC-SHA1';
}
/**
* {@inheritDoc}
*/
public function buildSignature(
Request $request,
Consumer $consumer,
Token $token = null
): string {
$signatureBase = $request->getSignatureBaseString();
$parts = [$consumer->secret, null !== $token ? $token->secret : ''];
$parts = Util::urlencodeRfc3986($parts);
$key = implode('&', $parts);
return base64_encode(hash_hmac('sha1', $signatureBase, $key, true));
}
}

View file

@ -0,0 +1,289 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
class Request
{
protected $parameters;
protected $httpMethod;
protected $httpUrl;
protected $json;
public static $version = '1.0';
/**
* Constructor
*
* @param string $httpMethod
* @param string $httpUrl
* @param array|null $parameters
*/
public function __construct(
string $httpMethod,
string $httpUrl,
?array $parameters = []
) {
$parameters = array_merge(
Util::parseParameters(parse_url($httpUrl, PHP_URL_QUERY)),
$parameters
);
$this->parameters = $parameters;
$this->httpMethod = $httpMethod;
$this->httpUrl = $httpUrl;
}
/**
* pretty much a helper function to set up the request
*
* @param Consumer $consumer
* @param Token $token
* @param string $httpMethod
* @param string $httpUrl
* @param array $parameters
*
* @return Request
*/
public static function fromConsumerAndToken(
Consumer $consumer,
Token $token = null,
string $httpMethod,
string $httpUrl,
array $parameters = [],
$json = false
) {
$defaults = [
'oauth_version' => Request::$version,
'oauth_nonce' => Request::generateNonce(),
'oauth_timestamp' => time(),
'oauth_consumer_key' => $consumer->key,
];
if (null !== $token) {
$defaults['oauth_token'] = $token->key;
}
// The json payload is not included in the signature on json requests,
// therefore it shouldn't be included in the parameters array.
if ($json) {
$parameters = $defaults;
} else {
$parameters = array_merge($defaults, $parameters);
}
return new Request($httpMethod, $httpUrl, $parameters);
}
/**
* @param string $name
* @param string $value
*/
public function setParameter(string $name, string $value)
{
$this->parameters[$name] = $value;
}
/**
* @param string $name
*
* @return string|null
*/
public function getParameter(string $name): ?string
{
return isset($this->parameters[$name])
? $this->parameters[$name]
: null;
}
/**
* @return array
*/
public function getParameters(): array
{
return $this->parameters;
}
/**
* @param string $name
*/
public function removeParameter(string $name): void
{
unset($this->parameters[$name]);
}
/**
* The request parameters, sorted and concatenated into a normalized string.
*
* @return string
*/
public function getSignableParameters(): string
{
// Grab all parameters
$params = $this->parameters;
// Remove oauth_signature if present
// Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
if (isset($params['oauth_signature'])) {
unset($params['oauth_signature']);
}
return Util::buildHttpQuery($params);
}
/**
* Returns the base string of this request
*
* The base string defined as the method, the url
* and the parameters (normalized), each urlencoded
* and the concated with &.
*
* @return string
*/
public function getSignatureBaseString(): string
{
$parts = [
$this->getNormalizedHttpMethod(),
$this->getNormalizedHttpUrl(),
$this->getSignableParameters(),
];
$parts = Util::urlencodeRfc3986($parts);
return implode('&', $parts);
}
/**
* Returns the HTTP Method in uppercase
*
* @return string
*/
public function getNormalizedHttpMethod(): string
{
return strtoupper($this->httpMethod);
}
/**
* parses the url and rebuilds it to be
* scheme://host/path
*
* @return string
*/
public function getNormalizedHttpUrl(): string
{
$parts = parse_url($this->httpUrl);
$scheme = $parts['scheme'];
$host = strtolower($parts['host']);
$path = $parts['path'];
return "$scheme://$host$path";
}
/**
* Builds a url usable for a GET request
*
* @return string
*/
public function toUrl(): string
{
$postData = $this->toPostdata();
$out = $this->getNormalizedHttpUrl();
if ($postData) {
$out .= '?' . $postData;
}
return $out;
}
/**
* Builds the data one would send in a POST request
*
* @return string
*/
public function toPostdata(): string
{
return Util::buildHttpQuery($this->parameters);
}
/**
* Builds the Authorization: header
*
* @return string
* @throws TwitterOAuthException
*/
public function toHeader(): string
{
$first = true;
$out = 'Authorization: OAuth';
foreach ($this->parameters as $k => $v) {
if (substr($k, 0, 5) != 'oauth') {
continue;
}
if (is_array($v)) {
throw new TwitterOAuthException(
'Arrays not supported in headers'
);
}
$out .= $first ? ' ' : ', ';
$out .=
Util::urlencodeRfc3986($k) .
'="' .
Util::urlencodeRfc3986($v) .
'"';
$first = false;
}
return $out;
}
/**
* @return string
*/
public function __toString(): string
{
return $this->toUrl();
}
/**
* @param SignatureMethod $signatureMethod
* @param Consumer $consumer
* @param Token $token
*/
public function signRequest(
SignatureMethod $signatureMethod,
Consumer $consumer,
Token $token = null
) {
$this->setParameter(
'oauth_signature_method',
$signatureMethod->getName()
);
$signature = $this->buildSignature($signatureMethod, $consumer, $token);
$this->setParameter('oauth_signature', $signature);
}
/**
* @param SignatureMethod $signatureMethod
* @param Consumer $consumer
* @param Token $token
*
* @return string
*/
public function buildSignature(
SignatureMethod $signatureMethod,
Consumer $consumer,
Token $token = null
): string {
return $signatureMethod->buildSignature($this, $consumer, $token);
}
/**
* @return string
*/
public static function generateNonce(): string
{
return md5(microtime() . mt_rand());
}
}

View file

@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**
* The result of the most recent API request.
*
* @author Abraham Williams <abraham@abrah.am>
*/
class Response
{
/** @var string|null API path from the most recent request */
private $apiPath;
/** @var int HTTP status code from the most recent request */
private $httpCode = 0;
/** @var array HTTP headers from the most recent request */
private $headers = [];
/** @var array|object Response body from the most recent request */
private $body = [];
/** @var array HTTP headers from the most recent request that start with X */
private $xHeaders = [];
/**
* @param string $apiPath
*/
public function setApiPath(string $apiPath): void
{
$this->apiPath = $apiPath;
}
/**
* @return string|null
*/
public function getApiPath(): ?string
{
return $this->apiPath;
}
/**
* @param array|object $body
*/
public function setBody($body)
{
$this->body = $body;
}
/**
* @return array|object|string
*/
public function getBody()
{
return $this->body;
}
/**
* @param int $httpCode
*/
public function setHttpCode(int $httpCode): void
{
$this->httpCode = $httpCode;
}
/**
* @return int
*/
public function getHttpCode(): int
{
return $this->httpCode;
}
/**
* @param array $headers
*/
public function setHeaders(array $headers): void
{
foreach ($headers as $key => $value) {
if (substr($key, 0, 1) == 'x') {
$this->xHeaders[$key] = $value;
}
}
$this->headers = $headers;
}
/**
* @return array
*/
public function getsHeaders(): array
{
return $this->headers;
}
/**
* @param array $xHeaders
*/
public function setXHeaders(array $xHeaders = []): void
{
$this->xHeaders = $xHeaders;
}
/**
* @return array
*/
public function getXHeaders(): array
{
return $this->xHeaders;
}
}

View file

@ -0,0 +1,78 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**
* A class for implementing a Signature Method
* See section 9 ("Signing Requests") in the spec
*/
abstract class SignatureMethod
{
/**
* Needs to return the name of the Signature Method (ie HMAC-SHA1)
*
* @return string
*/
abstract public function getName();
/**
* Build up the signature
* NOTE: The output of this function MUST NOT be urlencoded.
* the encoding is handled in OAuthRequest when the final
* request is serialized
*
* @param Request $request
* @param Consumer $consumer
* @param Token $token
*
* @return string
*/
abstract public function buildSignature(
Request $request,
Consumer $consumer,
Token $token = null
);
/**
* Verifies that a given signature is correct
*
* @param Request $request
* @param Consumer $consumer
* @param Token $token
* @param string $signature
*
* @return bool
*/
public function checkSignature(
Request $request,
Consumer $consumer,
Token $token,
string $signature
): bool {
$built = $this->buildSignature($request, $consumer, $token);
// Check for zero length, although unlikely here
if (strlen($built) == 0 || strlen($signature) == 0) {
return false;
}
if (strlen($built) != strlen($signature)) {
return false;
}
// Avoid a timing leak with a (hopefully) time insensitive compare
$result = 0;
for ($i = 0; $i < strlen($signature); $i++) {
$result |= ord($built[$i]) ^ ord($signature[$i]);
}
return $result == 0;
}
}

View file

@ -0,0 +1,43 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
class Token
{
/** @var string */
public $key;
/** @var string */
public $secret;
/**
* @param string $key The OAuth Token
* @param string $secret The OAuth Token Secret
*/
public function __construct(?string $key, ?string $secret)
{
$this->key = $key;
$this->secret = $secret;
}
/**
* Generates the basic string serialization of a token that a server
* would respond to request_token and access_token calls with
*
* @return string
*/
public function __toString(): string
{
return sprintf(
'oauth_token=%s&oauth_token_secret=%s',
Util::urlencodeRfc3986($this->key),
Util::urlencodeRfc3986($this->secret)
);
}
}

View file

@ -0,0 +1,727 @@
<?php
/**
* The most popular PHP library for use with the Twitter OAuth REST API.
*
* @license MIT
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
use Abraham\TwitterOAuth\Util\JsonDecoder;
use Composer\CaBundle\CaBundle;
/**
* TwitterOAuth class for interacting with the Twitter API.
*
* @author Abraham Williams <abraham@abrah.am>
*/
class TwitterOAuth extends Config
{
private const API_VERSION = '1.1';
private const API_HOST = 'https://api.twitter.com';
private const UPLOAD_HOST = 'https://upload.twitter.com';
/** @var Response details about the result of the last request */
private $response;
/** @var string|null Application bearer token */
private $bearer;
/** @var Consumer Twitter application details */
private $consumer;
/** @var Token|null User access token details */
private $token;
/** @var HmacSha1 OAuth 1 signature type used by Twitter */
private $signatureMethod;
/** @var int Number of attempts we made for the request */
private $attempts = 0;
/**
* Constructor
*
* @param string $consumerKey The Application Consumer Key
* @param string $consumerSecret The Application Consumer Secret
* @param string|null $oauthToken The Client Token (optional)
* @param string|null $oauthTokenSecret The Client Token Secret (optional)
*/
public function __construct(
string $consumerKey,
string $consumerSecret,
?string $oauthToken = null,
?string $oauthTokenSecret = null
) {
$this->resetLastResponse();
$this->signatureMethod = new HmacSha1();
$this->consumer = new Consumer($consumerKey, $consumerSecret);
if (!empty($oauthToken) && !empty($oauthTokenSecret)) {
$this->setOauthToken($oauthToken, $oauthTokenSecret);
}
if (empty($oauthToken) && !empty($oauthTokenSecret)) {
$this->setBearer($oauthTokenSecret);
}
}
/**
* @param string $oauthToken
* @param string $oauthTokenSecret
*/
public function setOauthToken(
string $oauthToken,
string $oauthTokenSecret
): void {
$this->token = new Token($oauthToken, $oauthTokenSecret);
$this->bearer = null;
}
/**
* @param string $oauthTokenSecret
*/
public function setBearer(string $oauthTokenSecret): void
{
$this->bearer = $oauthTokenSecret;
$this->token = null;
}
/**
* @return string|null
*/
public function getLastApiPath(): ?string
{
return $this->response->getApiPath();
}
/**
* @return int
*/
public function getLastHttpCode(): int
{
return $this->response->getHttpCode();
}
/**
* @return array
*/
public function getLastXHeaders(): array
{
return $this->response->getXHeaders();
}
/**
* @return array|object|null
*/
public function getLastBody()
{
return $this->response->getBody();
}
/**
* Resets the last response cache.
*/
public function resetLastResponse(): void
{
$this->response = new Response();
}
/**
* Resets the attempts number.
*/
private function resetAttemptsNumber(): void
{
$this->attempts = 0;
}
/**
* Delays the retries when they're activated.
*/
private function sleepIfNeeded(): void
{
if ($this->maxRetries && $this->attempts) {
sleep($this->retriesDelay);
}
}
/**
* Make URLs for user browser navigation.
*
* @param string $path
* @param array $parameters
*
* @return string
*/
public function url(string $path, array $parameters): string
{
$this->resetLastResponse();
$this->response->setApiPath($path);
$query = http_build_query($parameters);
return sprintf('%s/%s?%s', self::API_HOST, $path, $query);
}
/**
* Make /oauth/* requests to the API.
*
* @param string $path
* @param array $parameters
*
* @return array
* @throws TwitterOAuthException
*/
public function oauth(string $path, array $parameters = []): array
{
$response = [];
$this->resetLastResponse();
$this->response->setApiPath($path);
$url = sprintf('%s/%s', self::API_HOST, $path);
$result = $this->oAuthRequest($url, 'POST', $parameters);
if ($this->getLastHttpCode() != 200) {
throw new TwitterOAuthException($result);
}
parse_str($result, $response);
$this->response->setBody($response);
return $response;
}
/**
* Make /oauth2/* requests to the API.
*
* @param string $path
* @param array $parameters
*
* @return array|object
*/
public function oauth2(string $path, array $parameters = [])
{
$method = 'POST';
$this->resetLastResponse();
$this->response->setApiPath($path);
$url = sprintf('%s/%s', self::API_HOST, $path);
$request = Request::fromConsumerAndToken(
$this->consumer,
$this->token,
$method,
$url,
$parameters
);
$authorization =
'Authorization: Basic ' .
$this->encodeAppAuthorization($this->consumer);
$result = $this->request(
$request->getNormalizedHttpUrl(),
$method,
$authorization,
$parameters
);
$response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
$this->response->setBody($response);
return $response;
}
/**
* Make GET requests to the API.
*
* @param string $path
* @param array $parameters
*
* @return array|object
*/
public function get(string $path, array $parameters = [])
{
return $this->http('GET', self::API_HOST, $path, $parameters, false);
}
/**
* Make POST requests to the API.
*
* @param string $path
* @param array $parameters
* @param bool $json
*
* @return array|object
*/
public function post(
string $path,
array $parameters = [],
bool $json = false
) {
return $this->http('POST', self::API_HOST, $path, $parameters, $json);
}
/**
* Make DELETE requests to the API.
*
* @param string $path
* @param array $parameters
*
* @return array|object
*/
public function delete(string $path, array $parameters = [])
{
return $this->http('DELETE', self::API_HOST, $path, $parameters, false);
}
/**
* Make PUT requests to the API.
*
* @param string $path
* @param array $parameters
*
* @return array|object
*/
public function put(string $path, array $parameters = [])
{
return $this->http('PUT', self::API_HOST, $path, $parameters, false);
}
/**
* Upload media to upload.twitter.com.
*
* @param string $path
* @param array $parameters
* @param boolean $chunked
*
* @return array|object
*/
public function upload(
string $path,
array $parameters = [],
bool $chunked = false
) {
if ($chunked) {
return $this->uploadMediaChunked($path, $parameters);
} else {
return $this->uploadMediaNotChunked($path, $parameters);
}
}
/**
* Progression of media upload
*
* @param string $media_id
*
* @return array|object
*/
public function mediaStatus(string $media_id)
{
return $this->http(
'GET',
self::UPLOAD_HOST,
'media/upload',
[
'command' => 'STATUS',
'media_id' => $media_id,
],
false
);
}
/**
* Private method to upload media (not chunked) to upload.twitter.com.
*
* @param string $path
* @param array $parameters
*
* @return array|object
*/
private function uploadMediaNotChunked(string $path, array $parameters)
{
if (
!is_readable($parameters['media']) ||
($file = file_get_contents($parameters['media'])) === false
) {
throw new \InvalidArgumentException(
'You must supply a readable file'
);
}
$parameters['media'] = base64_encode($file);
return $this->http(
'POST',
self::UPLOAD_HOST,
$path,
$parameters,
false
);
}
/**
* Private method to upload media (chunked) to upload.twitter.com.
*
* @param string $path
* @param array $parameters
*
* @return array|object
*/
private function uploadMediaChunked(string $path, array $parameters)
{
$init = $this->http(
'POST',
self::UPLOAD_HOST,
$path,
$this->mediaInitParameters($parameters),
false
);
// Append
$segmentIndex = 0;
$media = fopen($parameters['media'], 'rb');
while (!feof($media)) {
$this->http(
'POST',
self::UPLOAD_HOST,
'media/upload',
[
'command' => 'APPEND',
'media_id' => $init->media_id_string,
'segment_index' => $segmentIndex++,
'media_data' => base64_encode(
fread($media, $this->chunkSize)
),
],
false
);
}
fclose($media);
// Finalize
$finalize = $this->http(
'POST',
self::UPLOAD_HOST,
'media/upload',
[
'command' => 'FINALIZE',
'media_id' => $init->media_id_string,
],
false
);
return $finalize;
}
/**
* Private method to get params for upload media chunked init.
* Twitter docs: https://dev.twitter.com/rest/reference/post/media/upload-init.html
*
* @param array $parameters
*
* @return array
*/
private function mediaInitParameters(array $parameters): array
{
$allowed_keys = [
'media_type',
'additional_owners',
'media_category',
'shared',
];
$base = [
'command' => 'INIT',
'total_bytes' => filesize($parameters['media']),
];
$allowed_parameters = array_intersect_key(
$parameters,
array_flip($allowed_keys)
);
return array_merge($base, $allowed_parameters);
}
/**
* Cleanup any parameters that are known not to work.
*
* @param array $parameters
*
* @return array
*/
private function cleanUpParameters(array $parameters)
{
foreach ($parameters as $key => $value) {
// PHP coerces `true` to `"1"` which some Twitter APIs don't like.
if (is_bool($value)) {
$parameters[$key] = var_export($value, true);
}
}
return $parameters;
}
/**
* @param string $method
* @param string $host
* @param string $path
* @param array $parameters
* @param bool $json
*
* @return array|object
*/
private function http(
string $method,
string $host,
string $path,
array $parameters,
bool $json
) {
$this->resetLastResponse();
$this->resetAttemptsNumber();
$url = sprintf('%s/%s/%s.json', $host, self::API_VERSION, $path);
$this->response->setApiPath($path);
if (!$json) {
$parameters = $this->cleanUpParameters($parameters);
}
return $this->makeRequests($url, $method, $parameters, $json);
}
/**
*
* Make requests and retry them (if enabled) in case of Twitter's problems.
*
* @param string $method
* @param string $url
* @param string $method
* @param array $parameters
* @param bool $json
*
* @return array|object
*/
private function makeRequests(
string $url,
string $method,
array $parameters,
bool $json
) {
do {
$this->sleepIfNeeded();
$result = $this->oAuthRequest($url, $method, $parameters, $json);
$response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
$this->response->setBody($response);
$this->attempts++;
// Retry up to our $maxRetries number if we get errors greater than 500 (over capacity etc)
} while ($this->requestsAvailable());
return $response;
}
/**
* Checks if we have to retry request if API is down.
*
* @return bool
*/
private function requestsAvailable(): bool
{
return $this->maxRetries &&
$this->attempts <= $this->maxRetries &&
$this->getLastHttpCode() >= 500;
}
/**
* Format and sign an OAuth / API request
*
* @param string $url
* @param string $method
* @param array $parameters
* @param bool $json
*
* @return string
* @throws TwitterOAuthException
*/
private function oAuthRequest(
string $url,
string $method,
array $parameters,
bool $json = false
) {
$request = Request::fromConsumerAndToken(
$this->consumer,
$this->token,
$method,
$url,
$parameters,
$json
);
if (array_key_exists('oauth_callback', $parameters)) {
// Twitter doesn't like oauth_callback as a parameter.
unset($parameters['oauth_callback']);
}
if ($this->bearer === null) {
$request->signRequest(
$this->signatureMethod,
$this->consumer,
$this->token
);
$authorization = $request->toHeader();
if (array_key_exists('oauth_verifier', $parameters)) {
// Twitter doesn't always work with oauth in the body and in the header
// and it's already included in the $authorization header
unset($parameters['oauth_verifier']);
}
} else {
$authorization = 'Authorization: Bearer ' . $this->bearer;
}
return $this->request(
$request->getNormalizedHttpUrl(),
$method,
$authorization,
$parameters,
$json
);
}
/**
* Set Curl options.
*
* @return array
*/
private function curlOptions(): array
{
$bundlePath = CaBundle::getSystemCaRootBundlePath();
$options = [
// CURLOPT_VERBOSE => true,
CURLOPT_CONNECTTIMEOUT => $this->connectionTimeout,
CURLOPT_HEADER => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_USERAGENT => $this->userAgent,
$this->curlCaOpt($bundlePath) => $bundlePath,
];
if ($this->gzipEncoding) {
$options[CURLOPT_ENCODING] = 'gzip';
}
if (!empty($this->proxy)) {
$options[CURLOPT_PROXY] = $this->proxy['CURLOPT_PROXY'];
$options[CURLOPT_PROXYUSERPWD] =
$this->proxy['CURLOPT_PROXYUSERPWD'];
$options[CURLOPT_PROXYPORT] = $this->proxy['CURLOPT_PROXYPORT'];
$options[CURLOPT_PROXYAUTH] = CURLAUTH_BASIC;
$options[CURLOPT_PROXYTYPE] = CURLPROXY_HTTP;
}
return $options;
}
/**
* Make an HTTP request
*
* @param string $url
* @param string $method
* @param string $authorization
* @param array $postfields
* @param bool $json
*
* @return string
* @throws TwitterOAuthException
*/
private function request(
string $url,
string $method,
string $authorization,
array $postfields,
bool $json = false
): string {
$options = $this->curlOptions();
$options[CURLOPT_URL] = $url;
$options[CURLOPT_HTTPHEADER] = [
'Accept: application/json',
$authorization,
'Expect:',
];
switch ($method) {
case 'GET':
break;
case 'POST':
$options[CURLOPT_POST] = true;
if ($json) {
$options[CURLOPT_HTTPHEADER][] =
'Content-type: application/json';
$options[CURLOPT_POSTFIELDS] = json_encode($postfields);
} else {
$options[CURLOPT_POSTFIELDS] = Util::buildHttpQuery(
$postfields
);
}
break;
case 'DELETE':
$options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
break;
case 'PUT':
$options[CURLOPT_CUSTOMREQUEST] = 'PUT';
break;
}
if (
in_array($method, ['GET', 'PUT', 'DELETE']) &&
!empty($postfields)
) {
$options[CURLOPT_URL] .= '?' . Util::buildHttpQuery($postfields);
}
$curlHandle = curl_init();
curl_setopt_array($curlHandle, $options);
$response = curl_exec($curlHandle);
// Throw exceptions on cURL errors.
if (curl_errno($curlHandle) > 0) {
$error = curl_error($curlHandle);
$errorNo = curl_errno($curlHandle);
curl_close($curlHandle);
throw new TwitterOAuthException($error, $errorNo);
}
$this->response->setHttpCode(
curl_getinfo($curlHandle, CURLINFO_HTTP_CODE)
);
$parts = explode("\r\n\r\n", $response);
$responseBody = array_pop($parts);
$responseHeader = array_pop($parts);
$this->response->setHeaders($this->parseHeaders($responseHeader));
curl_close($curlHandle);
return $responseBody;
}
/**
* Get the header info to store.
*
* @param string $header
*
* @return array
*/
private function parseHeaders(string $header): array
{
$headers = [];
foreach (explode("\r\n", $header) as $line) {
if (strpos($line, ':') !== false) {
[$key, $value] = explode(': ', $line);
$key = str_replace('-', '_', strtolower($key));
$headers[$key] = trim($value);
}
}
return $headers;
}
/**
* Encode application authorization header with base64.
*
* @param Consumer $consumer
*
* @return string
*/
private function encodeAppAuthorization(Consumer $consumer): string
{
$key = rawurlencode($consumer->key);
$secret = rawurlencode($consumer->secret);
return base64_encode($key . ':' . $secret);
}
/**
* Get Curl CA option based on whether the given path is a directory or file.
*
* @param string $path
* @return int
*/
private function curlCaOpt(string $path): int
{
return is_dir($path) ? CURLOPT_CAPATH : CURLOPT_CAINFO;
}
}

View file

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**
* @author Abraham Williams <abraham@abrah.am>
*/
class TwitterOAuthException extends \Exception
{
}

View file

@ -0,0 +1,122 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
class Util
{
/**
* @param mixed $input
*
* @return mixed
*/
public static function urlencodeRfc3986($input)
{
$output = '';
if (is_array($input)) {
$output = array_map(
[__NAMESPACE__ . '\Util', 'urlencodeRfc3986'],
$input
);
} elseif (is_scalar($input)) {
$output = rawurlencode((string) $input);
}
return $output;
}
/**
* @param string $string
*
* @return string
*/
public static function urldecodeRfc3986($string): string
{
return urldecode($string);
}
/**
* This function takes a input like a=b&a=c&d=e and returns the parsed
* parameters like this
* array('a' => array('b','c'), 'd' => 'e')
*
* @param string $input
*
* @return array
*/
public static function parseParameters($input): array
{
if (!is_string($input)) {
return [];
}
$pairs = explode('&', $input);
$parameters = [];
foreach ($pairs as $pair) {
$split = explode('=', $pair, 2);
$parameter = Util::urldecodeRfc3986($split[0]);
$value = isset($split[1]) ? Util::urldecodeRfc3986($split[1]) : '';
if (isset($parameters[$parameter])) {
// We have already recieved parameter(s) with this name, so add to the list
// of parameters with this name
if (is_scalar($parameters[$parameter])) {
// This is the first duplicate, so transform scalar (string) into an array
// so we can add the duplicates
$parameters[$parameter] = [$parameters[$parameter]];
}
$parameters[$parameter][] = $value;
} else {
$parameters[$parameter] = $value;
}
}
return $parameters;
}
/**
* @param array $params
*
* @return string
*/
public static function buildHttpQuery(array $params): string
{
if (empty($params)) {
return '';
}
// Urlencode both keys and values
$keys = Util::urlencodeRfc3986(array_keys($params));
$values = Util::urlencodeRfc3986(array_values($params));
$params = array_combine($keys, $values);
// Parameters are sorted by name, using lexicographical byte value ordering.
// Ref: Spec: 9.1.1 (1)
uksort($params, 'strcmp');
$pairs = [];
foreach ($params as $parameter => $value) {
if (is_array($value)) {
// If two or more parameters share the same name, they are sorted by their value
// Ref: Spec: 9.1.1 (1)
// June 12th, 2010 - changed to sort because of issue 164 by hidetaka
sort($value, SORT_STRING);
foreach ($value as $duplicateValue) {
$pairs[] = $parameter . '=' . $duplicateValue;
}
} else {
$pairs[] = $parameter . '=' . $value;
}
}
// For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
// Each name-value pair is separated by an '&' character (ASCII code 38)
return implode('&', $pairs);
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Abraham\TwitterOAuth\Util;
/**
* @author louis <louis@systemli.org>
*/
class JsonDecoder
{
/**
* Decodes a JSON string to stdObject or associative array
*
* @param string $string
* @param bool $asArray
*
* @return array|object
*/
public static function decode(string $string, bool $asArray)
{
if (
version_compare(PHP_VERSION, '5.4.0', '>=') &&
!(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)
) {
return json_decode($string, $asArray, 512, JSON_BIGINT_AS_STRING);
}
return json_decode($string, $asArray);
}
}

View file

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth\Tests;
use PHPUnit\Framework\TestCase;
use Abraham\TwitterOAuth\SignatureMethod;
abstract class AbstractSignatureMethodTest extends TestCase
{
protected $name;
/**
* @return SignatureMethod
*/
abstract public function getClass();
abstract protected function signatureDataProvider();
public function testGetName()
{
$this->assertEquals($this->name, $this->getClass()->getName());
}
/**
* @dataProvider signatureDataProvider
*/
public function testBuildSignature($expected, $request, $consumer, $token)
{
$this->assertEquals(
$expected,
$this->getClass()->buildSignature($request, $consumer, $token)
);
}
protected function getRequest()
{
return $this->getMockBuilder('Abraham\TwitterOAuth\Request')
->disableOriginalConstructor()
->getMock();
}
protected function getConsumer(
$key = null,
$secret = null,
$callbackUrl = null
) {
return $this->getMockBuilder('Abraham\TwitterOAuth\Consumer')
->setConstructorArgs([$key, $secret, $callbackUrl])
->getMock();
}
protected function getToken($key = null, $secret = null)
{
return $this->getMockBuilder('Abraham\TwitterOAuth\Token')
->setConstructorArgs([$key, $secret])
->getMock();
}
}

View file

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth\Tests;
use PHPUnit\Framework\TestCase;
use Abraham\TwitterOAuth\Consumer;
class ConsumerTest extends TestCase
{
public function testToString()
{
$key = uniqid();
$secret = uniqid();
$consumer = new Consumer($key, $secret);
$this->assertEquals(
"Consumer[key=$key,secret=$secret]",
$consumer->__toString()
);
}
}

View file

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth\Tests;
use Abraham\TwitterOAuth\HmacSha1;
class HmacSha1Test extends AbstractSignatureMethodTest
{
protected $name = 'HMAC-SHA1';
public function getClass()
{
return new HmacSha1();
}
public function signatureDataProvider()
{
return [
[
'5CoEcoq7XoKFjwYCieQvuzadeUA=',
$this->getRequest(),
$this->getConsumer(),
$this->getToken(),
],
[
'EBw0gHngam3BTx8kfPfNNSyKem4=',
$this->getRequest(),
$this->getConsumer('key', 'secret'),
$this->getToken(),
],
[
'kDsHFZzws2a5M6cAQjfpdNBo+v8=',
$this->getRequest(),
$this->getConsumer('key', 'secret'),
$this->getToken('key', 'secret'),
],
[
'EBw0gHngam3BTx8kfPfNNSyKem4=',
$this->getRequest(),
$this->getConsumer('key', 'secret'),
null,
],
];
}
}

View file

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth\Tests;
use PHPUnit\Framework\TestCase;
use Abraham\TwitterOAuth\Token;
class TokenTest extends TestCase
{
/**
* @dataProvider tokenProvider
*/
public function testToString($expected, $key, $secret)
{
$token = new Token($key, $secret);
$this->assertEquals($expected, $token->__toString());
}
public function tokenProvider()
{
return [
['oauth_token=key&oauth_token_secret=secret', 'key', 'secret'],
[
'oauth_token=key%2Bkey&oauth_token_secret=secret',
'key+key',
'secret',
],
[
'oauth_token=key~key&oauth_token_secret=secret',
'key~key',
'secret',
],
];
}
}

View file

@ -0,0 +1,397 @@
<?php
/**
* WARNING: Running tests will post and delete through the actual Twitter account when updating or saving VCR cassettes.
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth\Test;
use PHPUnit\Framework\TestCase;
use Abraham\TwitterOAuth\TwitterOAuth;
class TwitterOAuthTest extends TestCase
{
/** @var TwitterOAuth */
protected $twitter;
protected function setUp(): void
{
$this->twitter = new TwitterOAuth(
CONSUMER_KEY,
CONSUMER_SECRET,
ACCESS_TOKEN,
ACCESS_TOKEN_SECRET
);
$this->userId = explode('-', ACCESS_TOKEN)[0];
}
public function testBuildClient()
{
$this->assertObjectHasAttribute('consumer', $this->twitter);
$this->assertObjectHasAttribute('token', $this->twitter);
}
/**
* @vcr testSetOauthToken.json
*/
public function testSetOauthToken()
{
$twitter = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET);
$twitter->setOauthToken(ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
$this->assertObjectHasAttribute('consumer', $twitter);
$this->assertObjectHasAttribute('token', $twitter);
$twitter->get('friendships/show', [
'target_screen_name' => 'twitterapi',
]);
$this->assertEquals(200, $twitter->getLastHttpCode());
}
/**
* @vcr testOauth2Token.json
*/
public function testOauth2Token()
{
$twitter = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET);
$result = $twitter->oauth2('oauth2/token', [
'grant_type' => 'client_credentials',
]);
$this->assertEquals(200, $twitter->getLastHttpCode());
$this->assertObjectHasAttribute('token_type', $result);
$this->assertObjectHasAttribute('access_token', $result);
$this->assertEquals('bearer', $result->token_type);
return $result;
}
/**
* @depends testOauth2Token
* @vcr testOauth2BearerToken.json
*/
public function testOauth2BearerToken($accessToken)
{
$twitter = new TwitterOAuth(
CONSUMER_KEY,
CONSUMER_SECRET,
null,
$accessToken->access_token
);
$result = $twitter->get('statuses/user_timeline', [
'screen_name' => 'twitterapi',
]);
$this->assertEquals(200, $twitter->getLastHttpCode());
return $accessToken;
}
/**
* @depends testOauth2BearerToken
* @vcr testOauth2TokenInvalidate.json
*/
public function testOauth2TokenInvalidate($accessToken)
{
$twitter = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET);
// HACK: access_token is already urlencoded but gets urlencoded again breaking the invalidate request.
$result = $twitter->oauth2('oauth2/invalidate_token', [
'access_token' => urldecode($accessToken->access_token),
]);
$this->assertEquals(200, $twitter->getLastHttpCode());
$this->assertObjectHasAttribute('access_token', $result);
}
/**
* @vcr testOauthRequestToken.json
*/
public function testOauthRequestToken()
{
$twitter = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET);
$result = $twitter->oauth('oauth/request_token', [
'oauth_callback' => OAUTH_CALLBACK,
]);
$this->assertEquals(200, $twitter->getLastHttpCode());
$this->assertArrayHasKey('oauth_token', $result);
$this->assertArrayHasKey('oauth_token_secret', $result);
$this->assertArrayHasKey('oauth_callback_confirmed', $result);
$this->assertEquals('true', $result['oauth_callback_confirmed']);
return $result;
}
/**
* @vcr testOauthRequestTokenException.json
*/
public function testOauthRequestTokenException()
{
$this->expectException(
\Abraham\TwitterOAuth\TwitterOAuthException::class
);
$this->expectErrorMessage('Could not authenticate you');
$twitter = new TwitterOAuth('CONSUMER_KEY', 'CONSUMER_SECRET');
$result = $twitter->oauth('oauth/request_token', [
'oauth_callback' => OAUTH_CALLBACK,
]);
}
/**
* @depends testOauthRequestToken
* @vcr testOauthAccessTokenTokenException.json
*/
public function testOauthAccessTokenTokenException(array $requestToken)
{
// Can't test this without a browser logging into Twitter so check for the correct error instead.
$this->expectException(
\Abraham\TwitterOAuth\TwitterOAuthException::class
);
$this->expectErrorMessage('Invalid oauth_verifier parameter');
$twitter = new TwitterOAuth(
CONSUMER_KEY,
CONSUMER_SECRET,
$requestToken['oauth_token'],
$requestToken['oauth_token_secret']
);
$twitter->oauth('oauth/access_token', [
'oauth_verifier' => 'fake_oauth_verifier',
]);
}
public function testUrl()
{
$url = $this->twitter->url('oauth/authorize', [
'foo' => 'bar',
'baz' => 'qux',
]);
$this->assertEquals(
'https://api.twitter.com/oauth/authorize?foo=bar&baz=qux',
$url
);
}
/**
* @vcr testGetAccountVerifyCredentials.json
*/
public function testGetAccountVerifyCredentials()
{
$user = $this->twitter->get('account/verify_credentials', [
'include_entities' => false,
'include_email' => true,
]);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
$this->assertObjectHasAttribute('email', $user);
}
/**
* @vcr testSetProxy.json
*/
public function testSetProxy()
{
$this->twitter->setProxy([
'CURLOPT_PROXY' => PROXY,
'CURLOPT_PROXYUSERPWD' => PROXYUSERPWD,
'CURLOPT_PROXYPORT' => PROXYPORT,
]);
$this->twitter->setTimeouts(60, 60);
$result = $this->twitter->get('account/verify_credentials');
$this->assertEquals(200, $this->twitter->getLastHttpCode());
$this->assertObjectHasAttribute('id', $result);
}
/**
* @vcr testGetStatusesMentionsTimeline.json
*/
public function testGetStatusesMentionsTimeline()
{
$this->twitter->get('statuses/mentions_timeline');
$this->assertEquals(200, $this->twitter->getLastHttpCode());
}
/**
* @vcr testGetSearchTweets.json
*/
public function testGetSearchTweets()
{
$result = $this->twitter->get('search/tweets', ['q' => 'twitter']);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
return $result->statuses;
}
/**
* @depends testGetSearchTweets
* @vcr testGetSearchTweetsWithMaxId.json
*/
public function testGetSearchTweetsWithMaxId($statuses)
{
$maxId = array_pop($statuses)->id_str;
$this->twitter->get('search/tweets', [
'q' => 'twitter',
'max_id' => $maxId,
]);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
}
/**
* @vcr testPostFavoritesCreate.json
*/
public function testPostFavoritesCreate()
{
$result = $this->twitter->post('favorites/create', [
'id' => '6242973112',
]);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
}
/**
* @depends testPostFavoritesCreate
* @vcr testPostFavoritesDestroy.json
*/
public function testPostFavoritesDestroy()
{
$this->twitter->post('favorites/destroy', ['id' => '6242973112']);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
}
/**
* @vcr testPostDirectMessagesEventsNew.json
*/
public function testPostDirectMessagesEventsNew()
{
$data = [
'event' => [
'type' => 'message_create',
'message_create' => [
'target' => [
'recipient_id' => $this->userId,
],
'message_data' => [
'text' => 'Hello World!',
],
],
],
];
$result = $this->twitter->post(
'direct_messages/events/new',
$data,
true
);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
return $result;
}
/**
* @depends testPostDirectMessagesEventsNew
* @vcr testDeleteDirectMessagesEventsDestroy.json
*/
public function testDeleteDirectMessagesEventsDestroy($message)
{
$this->twitter->delete('direct_messages/events/destroy', [
'id' => $message->event->id,
]);
$this->assertEquals(204, $this->twitter->getLastHttpCode());
}
/**
* @vcr testPostStatusesUpdateWithMedia.json
*/
public function testPostStatusesUpdateWithMedia()
{
$this->twitter->setTimeouts(60, 60);
// Image source https://www.flickr.com/photos/titrans/8548825587/
$file_path = __DIR__ . '/kitten.jpg';
$result = $this->twitter->upload('media/upload', [
'media' => $file_path,
]);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
$this->assertObjectHasAttribute('media_id_string', $result);
$parameters = [
'status' => 'Hello World ' . MOCK_TIME,
'media_ids' => $result->media_id_string,
];
$result = $this->twitter->post('statuses/update', $parameters);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
$result = $this->twitter->post('statuses/destroy/' . $result->id_str);
return $result;
}
/**
* @vcr testPostStatusUpdateWithInvalidMediaThrowsException.json
*/
public function testPostStatusUpdateWithInvalidMediaThrowsException()
{
$this->expectException(\InvalidArgumentException::class);
$file_path = __DIR__ . '/12345678900987654321.jpg';
$this->assertFalse(\is_readable($file_path));
$result = $this->twitter->upload('media/upload', [
'media' => $file_path,
]);
}
/**
* @vcr testPostStatusesUpdateWithMediaChunked.json
*/
public function testPostStatusesUpdateWithMediaChunked()
{
$this->twitter->setTimeouts(60, 30);
// Video source http://www.sample-videos.com/
$file_path = __DIR__ . '/video.mp4';
$result = $this->twitter->upload(
'media/upload',
['media' => $file_path, 'media_type' => 'video/mp4'],
true
);
$this->assertEquals(201, $this->twitter->getLastHttpCode());
$this->assertObjectHasAttribute('media_id_string', $result);
$parameters = [
'status' => 'Hello World ' . MOCK_TIME,
'media_ids' => $result->media_id_string,
];
$result = $this->twitter->post('statuses/update', $parameters);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
$result = $this->twitter->post('statuses/destroy/' . $result->id_str);
return $result;
}
/**
* @vcr testPostStatusesUpdateUtf8.json
*/
public function testPostStatusesUpdateUtf8()
{
$result = $this->twitter->post('statuses/update', [
'status' => 'xこんにちは世界 ' . MOCK_TIME,
]);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
return $result;
}
/**
* @depends testPostStatusesUpdateUtf8
* @vcr testPostStatusesDestroy.json
*/
public function testPostStatusesDestroy($status)
{
$this->twitter->post('statuses/destroy/' . $status->id_str);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
}
/**
* @vcr testLastResult.json
*/
public function testLastResult()
{
$this->twitter->get('search/tweets', ['q' => 'twitter']);
$this->assertEquals('search/tweets', $this->twitter->getLastApiPath());
$this->assertEquals(200, $this->twitter->getLastHttpCode());
$this->assertObjectHasAttribute(
'statuses',
$this->twitter->getLastBody()
);
}
/**
* @depends testLastResult
* @vcr testResetLastResponse.json
*/
public function testResetLastResponse()
{
$this->twitter->resetLastResponse();
$this->assertEquals('', $this->twitter->getLastApiPath());
$this->assertEquals(0, $this->twitter->getLastHttpCode());
$this->assertEquals([], $this->twitter->getLastBody());
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace Abraham\TwitterOAuth\Tests;
use PHPUnit\Framework\TestCase;
use Abraham\TwitterOAuth\Util\JsonDecoder;
class JsonDecoderTest extends TestCase
{
/**
* @dataProvider jsonProvider
*/
public function testDecode($input, $asArray, $expected)
{
$this->assertEquals($expected, JsonDecoder::decode($input, $asArray));
}
public function jsonProvider()
{
return [
['[]', true, []],
['[1,2,3]', true, [1, 2, 3]],
[
'[{"id": 556179961825226750}]',
true,
[['id' => 556179961825226750]],
],
['[]', false, []],
['[1,2,3]', false, [1, 2, 3]],
[
'[{"id": 556179961825226750}]',
false,
[
$this->getClass(function ($object) {
$object->id = 556179961825226750;
return $object;
}),
],
],
];
}
/**
* @param callable $callable
*
* @return stdClass
*/
private function getClass(\Closure $callable)
{
$object = new \stdClass();
return $callable($object);
}
}

View file

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
require 'vars.php';
require 'mocks.php';
\VCR\VCR::configure()->setStorage('json');
\VCR\VCR::turnOn();

View file

@ -0,0 +1,40 @@
[{
"request": {
"method": "DELETE",
"url": "https:\/\/api.twitter.com\/1.1\/direct_messages\/events\/destroy.json?id=1254206523385032714",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"dY4KEaTg5Y6Bv4JlofNCjoArx%2F4%3D\"",
"Expect": null
}
},
"response": {
"status": {
"http_version": "2",
"code": "204",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-security-policy": "default-src 'self'; connect-src 'self'; font-src 'self' https:\/\/*.twimg.com https:\/\/twitter.com https:\/\/ton.twitter.com data:; frame-src 'self' https:\/\/*.twimg.com https:\/\/twitter.com https:\/\/ton.twitter.com; img-src 'self' https:\/\/*.twimg.com https:\/\/twitter.com https:\/\/ton.twitter.com data:; media-src 'self' https:\/\/*.twimg.com https:\/\/twitter.com https:\/\/ton.twitter.com; object-src 'none'; script-src 'self' https:\/\/*.twimg.com https:\/\/twitter.com https:\/\/ton.twitter.com; style-src 'self' https:\/\/*.twimg.com https:\/\/twitter.com https:\/\/ton.twitter.com; report-uri https:\/\/twitter.com\/i\/csp_report?a=NVQWGYLXFVSG2%3D%3D%3D&ro=false;",
"content-type": "text\/html;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:52 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:52 GMT",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_asAbLVWtv6V2+ARemo0VNA==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:52 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, lang=en; Path=\/, guest_id=v1%3A158786111220677678; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:52 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "204 No Content",
"strict-transport-security": "max-age=631138519",
"x-access-level": "read-write-directmessages",
"x-connection-hash": "5cbff6bc4105c0040c4738daac7adcf4",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-response-time": "37",
"x-transaction": "00f02f6c00e12e76",
"x-twitter-response-tags": "BouncerCompliant",
"x-xss-protection": "0"
}
}
}]

View file

@ -0,0 +1,46 @@
[{
"request": {
"method": "GET",
"url": "https:\/\/api.twitter.com\/1.1\/account\/verify_credentials.json?include_email=true&include_entities=false",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"k8h8edFh9R2W3DCNJy5Nb07tWo0%3D\"",
"Expect": null
}
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "773",
"content-type": "application\/json;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:11 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:11 GMT",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_PPOKPD3f\/ek9QM3+ySQxjw==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:11 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, lang=en; Path=\/, guest_id=v1%3A158786107181888587; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:11 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-access-level": "read-write-directmessages",
"x-connection-hash": "7509696bc24b6d9d09d283b844a3c232",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-rate-limit-limit": "75",
"x-rate-limit-remaining": "73",
"x-rate-limit-reset": "1587861613",
"x-response-time": "46",
"x-transaction": "00cd4f6300163d67",
"x-twitter-response-tags": "BouncerExempt, BouncerCompliant",
"x-xss-protection": "0"
},
"body": "{\"id\":93915746,\"id_str\":\"93915746\",\"name\":\"OAuth Library Test\",\"screen_name\":\"oauthlibtest\",\"location\":\"\",\"description\":\"\",\"url\":null,\"entities\":{\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":58,\"friends_count\":2,\"listed_count\":6,\"created_at\":\"Tue Dec 01 18:37:44 +0000 2009\",\"favourites_count\":0,\"utc_offset\":null,\"time_zone\":null,\"geo_enabled\":true,\"verified\":false,\"statuses_count\":5,\"lang\":null,\"status\":{\"created_at\":\"Tue Dec 01 18:38:07 +0000 2009\",\"id\":6242973112,\"id_str\":\"6242973112\",\"text\":\"Test!\",\"truncated\":false,\"source\":\"\\u003ca href=\\\"http:\\\/\\\/twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web Client\\u003c\\\/a\\u003e\",\"in_reply_to_status_id\":null,\"in_reply_to_status_id_str\":null,\"in_reply_to_user_id\":null,\"in_reply_to_user_id_str\":null,\"in_reply_to_screen_name\":null,\"geo\":null,\"coordinates\":null,\"place\":null,\"contributors\":null,\"is_quote_status\":false,\"retweet_count\":2258,\"favorite_count\":75,\"favorited\":false,\"retweeted\":false,\"lang\":\"en\"},\"contributors_enabled\":false,\"is_translator\":false,\"is_translation_enabled\":false,\"profile_background_color\":\"C0DEED\",\"profile_background_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_tile\":false,\"profile_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_link_color\":\"1DA1F2\",\"profile_sidebar_border_color\":\"C0DEED\",\"profile_sidebar_fill_color\":\"DDEEF6\",\"profile_text_color\":\"333333\",\"profile_use_background_image\":true,\"has_extended_profile\":false,\"default_profile\":true,\"default_profile_image\":true,\"following\":false,\"follow_request_sent\":false,\"notifications\":false,\"translator_type\":\"none\",\"suspended\":false,\"needs_phone_verification\":false,\"email\":\"4braham+oauthlibtest@gmail.com\"}"
}
}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,46 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/oauth2\/token",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "Basic YXdKZk5ENHpGR2FwR09GS2Zkamc6TGZrbU5TUlBJWHdrUWtaVUI5RE5XU3p4NUxJYWl2U2tuVjRyeG5nb2pKYw==",
"Expect": null
},
"body": "grant_type=client_credentials"
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "152",
"content-type": "application\/json;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:09 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:09 GMT",
"ml": "A",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_NF00blSG9GZe8w8KpZvUDA==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:09 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, guest_id=v1%3A158786106988547101; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:09 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-connection-hash": "34e2373c53e7f9e0e80fe6af071dd6b8",
"x-content-type-options": "nosniff",
"x-frame-options": "DENY",
"x-response-time": "20",
"x-transaction": "007d4d19009f7a59",
"x-tsa-request-body-time": "0",
"x-twitter-response-tags": "BouncerCompliant",
"x-ua-compatible": "IE=edge,chrome=1",
"x-xss-protection": "0"
},
"body": "{\"token_type\":\"bearer\",\"access_token\":\"AAAAAAAAAAAAAAAAAAAAAFobAAAAAAAAjPes3FlPiFKh9HaIg%2Fw80waE0s8%3DQqxjhHDgZyjihGIK7olugzbpS0R1Gg8KNhzmer58a6oVbsSGc0\"}"
}
}]

View file

@ -0,0 +1,46 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/oauth2\/invalidate_token",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "Basic YXdKZk5ENHpGR2FwR09GS2Zkamc6TGZrbU5TUlBJWHdrUWtaVUI5RE5XU3p4NUxJYWl2U2tuVjRyeG5nb2pKYw==",
"Expect": null
},
"body": "access_token=AAAAAAAAAAAAAAAAAAAAAFobAAAAAAAAjPes3FlPiFKh9HaIg%2Fw80waE0s8%3DQqxjhHDgZyjihGIK7olugzbpS0R1Gg8KNhzmer58a6oVbsSGc0"
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "135",
"content-type": "application\/json;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:10 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:10 GMT",
"ml": "A",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_8Iv+DqoXk8DVAVDoUVltSA==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:10 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, guest_id=v1%3A158786107054950627; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:10 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-connection-hash": "18b7327592f746230c1c016c344dd14d",
"x-content-type-options": "nosniff",
"x-frame-options": "DENY",
"x-response-time": "19",
"x-transaction": "00c5257f00b7d371",
"x-tsa-request-body-time": "0",
"x-twitter-response-tags": "BouncerCompliant",
"x-ua-compatible": "IE=edge,chrome=1",
"x-xss-protection": "0"
},
"body": "{\"access_token\":\"AAAAAAAAAAAAAAAAAAAAAFobAAAAAAAAjPes3FlPiFKh9HaIg%2Fw80waE0s8%3DQqxjhHDgZyjihGIK7olugzbpS0R1Gg8KNhzmer58a6oVbsSGc0\"}"
}
}]

View file

@ -0,0 +1,45 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/oauth\/access_token",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"CE545gAAAAAAABtaAAABcbPlJBQ\", oauth_verifier=\"fake_oauth_verifier\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"0bcdtKs3nffzbE5abwaVjCI1HPw%3D\"",
"Expect": null
}
},
"response": {
"status": {
"http_version": "2",
"code": "401",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-encoding": "gzip",
"content-length": "93",
"content-security-policy": "default-src 'none'; connect-src 'self'; font-src https:\/\/abs.twimg.com https:\/\/abs-0.twimg.com data:; frame-src 'self' twitter:; img-src https:\/\/abs.twimg.com https:\/\/*.twimg.com https:\/\/pbs.twimg.com data:; media-src 'none'; object-src 'none'; script-src https:\/\/abs.twimg.com https:\/\/abs-0.twimg.com https:\/\/twitter.com https:\/\/mobile.twitter.com; style-src https:\/\/abs.twimg.com https:\/\/abs-0.twimg.com; report-uri https:\/\/twitter.com\/i\/csp_report?a=NVQWGYLXFVWG6Z3JNY%3D%3D%3D%3D%3D%3D&ro=false;",
"content-type": "text\/html;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:11 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:11 GMT",
"ml": "A",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_n0ZAgT2oLIc0HI23qMIGCA==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:11 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, guest_id=v1%3A158786107147893563; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:11 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "401 Unauthorized",
"strict-transport-security": "max-age=631138519",
"www-authenticate": "OAuth realm=\"https:\/\/api.twitter.com\"",
"x-connection-hash": "90157d4bdfce3a9b90fd408819c767bc",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-response-time": "41",
"x-transaction": "0080cead006a758d",
"x-twitter-response-tags": "BouncerCompliant",
"x-ua-compatible": "IE=edge,chrome=1",
"x-xss-protection": "0"
},
"body": "Error processing your OAuth request: Invalid oauth_verifier parameter"
}
}]

View file

@ -0,0 +1,44 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/oauth\/request_token",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_callback=\"https%3A%2F%2Ftwitteroauth.com%2Fcallback.php\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"LR7ZVqY%2Fcdisw1w3zssKI6Yjbls%3D\"",
"Expect": null
}
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-encoding": "gzip",
"content-length": "127",
"content-security-policy": "default-src 'none'; connect-src 'self'; font-src https:\/\/abs.twimg.com https:\/\/abs-0.twimg.com data:; frame-src 'self' twitter:; img-src https:\/\/abs.twimg.com https:\/\/*.twimg.com https:\/\/pbs.twimg.com data:; media-src 'none'; object-src 'none'; script-src https:\/\/abs.twimg.com https:\/\/abs-0.twimg.com https:\/\/twitter.com https:\/\/mobile.twitter.com; style-src https:\/\/abs.twimg.com https:\/\/abs-0.twimg.com; report-uri https:\/\/twitter.com\/i\/csp_report?a=NVQWGYLXFVWG6Z3JNY%3D%3D%3D%3D%3D%3D&ro=false;",
"content-type": "text\/html;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:10 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:10 GMT",
"ml": "A",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_mrnWVDThJvkLcAe4hmX0ng==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:10 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, guest_id=v1%3A158786107085601318; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:10 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-connection-hash": "bf00d267c647790cd34d8cd4a28f9895",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-response-time": "24",
"x-transaction": "0095391f006dd965",
"x-twitter-response-tags": "BouncerCompliant",
"x-ua-compatible": "IE=edge,chrome=1",
"x-xss-protection": "0"
},
"body": "oauth_token=CE545gAAAAAAABtaAAABcbPlJBQ&oauth_token_secret=tTVYBva8AlQu0JxVudzbf9oHXAbIARg5&oauth_callback_confirmed=true"
}
}]

View file

@ -0,0 +1,43 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/oauth\/request_token",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"CONSUMER_KEY\", oauth_callback=\"https%3A%2F%2Ftwitteroauth.com%2Fcallback.php\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"wOUt6ZyVGpWnQhsHNWqcr%2BpOWAw%3D\"",
"Expect": null
}
},
"response": {
"status": {
"http_version": "2",
"code": "401",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "89",
"content-type": "application\/json; charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:11 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:11 GMT",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_Vz8Os736+fzUwkQGIeIKuw==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:11 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, guest_id=v1%3A158786107116335546; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:11 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "401 Unauthorized",
"strict-transport-security": "max-age=631138519",
"www-authenticate": "OAuth realm=\"https:\/\/api.twitter.com\", api_error_code=32",
"x-connection-hash": "d620dbb5b35e124662532c3ef8e89c88",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-response-time": "6",
"x-transaction": "00bf1248004cdafa",
"x-twitter-response-tags": "BouncerCompliant",
"x-xss-protection": "0"
},
"body": "{\"errors\":[{\"code\":32,\"message\":\"Could not authenticate you.\"}]}"
}
}]

View file

@ -0,0 +1,46 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/1.1\/direct_messages\/events\/new.json",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"3457NqeumGmcalZLF091L9lt7F8%3D\"",
"Expect": null,
"Content-type": "application\/json"
},
"body": "{\"event\":{\"type\":\"message_create\",\"message_create\":{\"target\":{\"recipient_id\":\"93915746\"},\"message_data\":{\"text\":\"Hello World!\"}}}}"
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "206",
"content-type": "application\/json;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:51 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:51 GMT",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_Tfqxs0gur2QR4FFIZ3Wq6w==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:51 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, lang=en; Path=\/, guest_id=v1%3A158786111185015666; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:51 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-access-level": "read-write-directmessages",
"x-connection-hash": "bb4f30d1c6406b2cd5d25f20fccfdc1a",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-response-time": "70",
"x-transaction": "0057fa4c00fb95a1",
"x-tsa-request-body-time": "0",
"x-twitter-response-tags": "BouncerCompliant",
"x-xss-protection": "0"
},
"body": "{\"event\":{\"type\":\"message_create\",\"id\":\"1254206523385032714\",\"created_timestamp\":\"1587861111862\",\"message_create\":{\"target\":{\"recipient_id\":\"93915746\"},\"sender_id\":\"93915746\",\"message_data\":{\"text\":\"Hello World!\",\"entities\":{\"hashtags\":[],\"symbols\":[],\"user_mentions\":[],\"urls\":[]}}}}}"
}
}]

View file

@ -0,0 +1,45 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/1.1\/favorites\/create.json",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"EA30eIQPgat0Aw%2F59GyltEiE4Xg%3D\"",
"Expect": null
},
"body": "id=6242973112"
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "755",
"content-type": "application\/json;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:51 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:51 GMT",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_Jz87HIDSEIpDevFMBlDD7g==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:51 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, lang=en; Path=\/, guest_id=v1%3A158786111115490266; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:51 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-access-level": "read-write-directmessages",
"x-connection-hash": "7368af4d238e5c36df5379afb1bed3af",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-response-time": "72",
"x-transaction": "0012beac0086638b",
"x-tsa-request-body-time": "0",
"x-twitter-response-tags": "BouncerCompliant",
"x-xss-protection": "0"
},
"body": "{\"created_at\":\"Tue Dec 01 18:38:07 +0000 2009\",\"id\":6242973112,\"id_str\":\"6242973112\",\"text\":\"Test!\",\"truncated\":false,\"entities\":{\"hashtags\":[],\"symbols\":[],\"user_mentions\":[],\"urls\":[]},\"source\":\"\\u003ca href=\\\"http:\\\/\\\/twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web Client\\u003c\\\/a\\u003e\",\"in_reply_to_status_id\":null,\"in_reply_to_status_id_str\":null,\"in_reply_to_user_id\":null,\"in_reply_to_user_id_str\":null,\"in_reply_to_screen_name\":null,\"user\":{\"id\":93915746,\"id_str\":\"93915746\",\"name\":\"OAuth Library Test\",\"screen_name\":\"oauthlibtest\",\"location\":\"\",\"description\":\"\",\"url\":null,\"entities\":{\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":58,\"friends_count\":2,\"listed_count\":6,\"created_at\":\"Tue Dec 01 18:37:44 +0000 2009\",\"favourites_count\":1,\"utc_offset\":null,\"time_zone\":null,\"geo_enabled\":true,\"verified\":false,\"statuses_count\":5,\"lang\":null,\"contributors_enabled\":false,\"is_translator\":false,\"is_translation_enabled\":false,\"profile_background_color\":\"C0DEED\",\"profile_background_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_tile\":false,\"profile_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_link_color\":\"1DA1F2\",\"profile_sidebar_border_color\":\"C0DEED\",\"profile_sidebar_fill_color\":\"DDEEF6\",\"profile_text_color\":\"333333\",\"profile_use_background_image\":true,\"has_extended_profile\":false,\"default_profile\":true,\"default_profile_image\":true,\"following\":false,\"follow_request_sent\":false,\"notifications\":false,\"translator_type\":\"none\"},\"geo\":null,\"coordinates\":null,\"place\":null,\"contributors\":null,\"is_quote_status\":false,\"retweet_count\":2258,\"favorite_count\":76,\"favorited\":true,\"retweeted\":false,\"lang\":\"en\"}"
}
}]

View file

@ -0,0 +1,45 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/1.1\/favorites\/destroy.json",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"w3Nti04O5BMi8bySXjmO8%2BW5Pus%3D\"",
"Expect": null
},
"body": "id=6242973112"
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "753",
"content-type": "application\/json;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:51 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:51 GMT",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_s1J1pMUNrQO4\/v371oE9AQ==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:51 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, lang=en; Path=\/, guest_id=v1%3A158786111151392082; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:51 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-access-level": "read-write-directmessages",
"x-connection-hash": "a0dc865f09447e41b0d77e9eed981519",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-response-time": "44",
"x-transaction": "005d9083009bd4c9",
"x-tsa-request-body-time": "0",
"x-twitter-response-tags": "BouncerCompliant",
"x-xss-protection": "0"
},
"body": "{\"created_at\":\"Tue Dec 01 18:38:07 +0000 2009\",\"id\":6242973112,\"id_str\":\"6242973112\",\"text\":\"Test!\",\"truncated\":false,\"entities\":{\"hashtags\":[],\"symbols\":[],\"user_mentions\":[],\"urls\":[]},\"source\":\"\\u003ca href=\\\"http:\\\/\\\/twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web Client\\u003c\\\/a\\u003e\",\"in_reply_to_status_id\":null,\"in_reply_to_status_id_str\":null,\"in_reply_to_user_id\":null,\"in_reply_to_user_id_str\":null,\"in_reply_to_screen_name\":null,\"user\":{\"id\":93915746,\"id_str\":\"93915746\",\"name\":\"OAuth Library Test\",\"screen_name\":\"oauthlibtest\",\"location\":\"\",\"description\":\"\",\"url\":null,\"entities\":{\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":58,\"friends_count\":2,\"listed_count\":6,\"created_at\":\"Tue Dec 01 18:37:44 +0000 2009\",\"favourites_count\":0,\"utc_offset\":null,\"time_zone\":null,\"geo_enabled\":true,\"verified\":false,\"statuses_count\":5,\"lang\":null,\"contributors_enabled\":false,\"is_translator\":false,\"is_translation_enabled\":false,\"profile_background_color\":\"C0DEED\",\"profile_background_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_tile\":false,\"profile_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_link_color\":\"1DA1F2\",\"profile_sidebar_border_color\":\"C0DEED\",\"profile_sidebar_fill_color\":\"DDEEF6\",\"profile_text_color\":\"333333\",\"profile_use_background_image\":true,\"has_extended_profile\":false,\"default_profile\":true,\"default_profile_image\":true,\"following\":false,\"follow_request_sent\":false,\"notifications\":false,\"translator_type\":\"none\"},\"geo\":null,\"coordinates\":null,\"place\":null,\"contributors\":null,\"is_quote_status\":false,\"retweet_count\":2258,\"favorite_count\":75,\"favorited\":false,\"retweeted\":false,\"lang\":\"en\"}"
}
}]

View file

@ -0,0 +1,43 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/1.1\/statuses\/destroy\/1254206657548226561.json",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"kyOKi3x9Ar3foSG5%2BN9XzBbnIOw%3D\"",
"Expect": null
}
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "804",
"content-type": "application\/json;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:32:24 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:32:24 GMT",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_juPKvfSeQeQoZAVeLglnhA==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:32:24 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, lang=en; Path=\/, guest_id=v1%3A158786114418847477; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:32:24 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-access-level": "read-write-directmessages",
"x-connection-hash": "f4375157b19d6cd139b9917a6d76d0b0",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-response-time": "198",
"x-transaction": "00f3e731001ccb87",
"x-twitter-response-tags": "BouncerCompliant",
"x-xss-protection": "0"
},
"body": "{\"created_at\":\"Sun Apr 26 00:32:23 +0000 2020\",\"id\":1254206657548226561,\"id_str\":\"1254206657548226561\",\"text\":\"x\\u3053\\u3093\\u306b\\u3061\\u306f\\u4e16\\u754c 1587861062\",\"truncated\":false,\"entities\":{\"hashtags\":[],\"symbols\":[],\"user_mentions\":[],\"urls\":[]},\"source\":\"\\u003ca href=\\\"https:\\\/\\\/twitteroauth.com\\\" rel=\\\"nofollow\\\"\\u003eTwitterOAuth dev\\u003c\\\/a\\u003e\",\"in_reply_to_status_id\":null,\"in_reply_to_status_id_str\":null,\"in_reply_to_user_id\":null,\"in_reply_to_user_id_str\":null,\"in_reply_to_screen_name\":null,\"user\":{\"id\":93915746,\"id_str\":\"93915746\",\"name\":\"OAuth Library Test\",\"screen_name\":\"oauthlibtest\",\"location\":\"\",\"description\":\"\",\"url\":null,\"entities\":{\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":58,\"friends_count\":2,\"listed_count\":6,\"created_at\":\"Tue Dec 01 18:37:44 +0000 2009\",\"favourites_count\":0,\"utc_offset\":null,\"time_zone\":null,\"geo_enabled\":true,\"verified\":false,\"statuses_count\":6,\"lang\":null,\"contributors_enabled\":false,\"is_translator\":false,\"is_translation_enabled\":false,\"profile_background_color\":\"C0DEED\",\"profile_background_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_tile\":false,\"profile_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_link_color\":\"1DA1F2\",\"profile_sidebar_border_color\":\"C0DEED\",\"profile_sidebar_fill_color\":\"DDEEF6\",\"profile_text_color\":\"333333\",\"profile_use_background_image\":true,\"has_extended_profile\":false,\"default_profile\":true,\"default_profile_image\":true,\"following\":false,\"follow_request_sent\":false,\"notifications\":false,\"translator_type\":\"none\"},\"geo\":null,\"coordinates\":null,\"place\":null,\"contributors\":null,\"is_quote_status\":false,\"retweet_count\":0,\"favorite_count\":0,\"favorited\":false,\"retweeted\":false,\"lang\":\"ja\"}"
}
}]

View file

@ -0,0 +1,45 @@
[{
"request": {
"method": "POST",
"url": "https:\/\/api.twitter.com\/1.1\/statuses\/update.json",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"zIzkM9jxroYElpL1fPTyYnYE%2Bys%3D\"",
"Expect": null
},
"body": "status=x%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%E4%B8%96%E7%95%8C%201587861062"
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "804",
"content-type": "application\/json;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:32:23 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:32:23 GMT",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_8nFfK\/V8KyJDl1aminWCQw==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:32:23 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, lang=en; Path=\/, guest_id=v1%3A158786114384224672; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:32:23 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-access-level": "read-write-directmessages",
"x-connection-hash": "54c0be65e0c80b57d5b7c895e58061c8",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-response-time": "55",
"x-transaction": "00eb7dbc0057ef33",
"x-tsa-request-body-time": "0",
"x-twitter-response-tags": "BouncerCompliant",
"x-xss-protection": "0"
},
"body": "{\"created_at\":\"Sun Apr 26 00:32:23 +0000 2020\",\"id\":1254206657548226561,\"id_str\":\"1254206657548226561\",\"text\":\"x\\u3053\\u3093\\u306b\\u3061\\u306f\\u4e16\\u754c 1587861062\",\"truncated\":false,\"entities\":{\"hashtags\":[],\"symbols\":[],\"user_mentions\":[],\"urls\":[]},\"source\":\"\\u003ca href=\\\"https:\\\/\\\/twitteroauth.com\\\" rel=\\\"nofollow\\\"\\u003eTwitterOAuth dev\\u003c\\\/a\\u003e\",\"in_reply_to_status_id\":null,\"in_reply_to_status_id_str\":null,\"in_reply_to_user_id\":null,\"in_reply_to_user_id_str\":null,\"in_reply_to_screen_name\":null,\"user\":{\"id\":93915746,\"id_str\":\"93915746\",\"name\":\"OAuth Library Test\",\"screen_name\":\"oauthlibtest\",\"location\":\"\",\"description\":\"\",\"url\":null,\"entities\":{\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":58,\"friends_count\":2,\"listed_count\":6,\"created_at\":\"Tue Dec 01 18:37:44 +0000 2009\",\"favourites_count\":0,\"utc_offset\":null,\"time_zone\":null,\"geo_enabled\":true,\"verified\":false,\"statuses_count\":6,\"lang\":null,\"contributors_enabled\":false,\"is_translator\":false,\"is_translation_enabled\":false,\"profile_background_color\":\"C0DEED\",\"profile_background_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_tile\":false,\"profile_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_link_color\":\"1DA1F2\",\"profile_sidebar_border_color\":\"C0DEED\",\"profile_sidebar_fill_color\":\"DDEEF6\",\"profile_text_color\":\"333333\",\"profile_use_background_image\":true,\"has_extended_profile\":false,\"default_profile\":true,\"default_profile_image\":true,\"following\":false,\"follow_request_sent\":false,\"notifications\":false,\"translator_type\":\"none\"},\"geo\":null,\"coordinates\":null,\"place\":null,\"contributors\":null,\"is_quote_status\":false,\"retweet_count\":0,\"favorite_count\":0,\"favorited\":false,\"retweeted\":false,\"lang\":\"ja\"}"
}
}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,46 @@
[{
"request": {
"method": "GET",
"url": "https:\/\/api.twitter.com\/1.1\/friendships\/show.json?target_screen_name=twitterapi",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"TzPCDLbvxIAlxBqg5Fpf4JZpFJo%3D\"",
"Expect": null
}
},
"response": {
"status": {
"http_version": "2",
"code": "200",
"message": ""
},
"headers": {
"cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
"content-disposition": "attachment; filename=json.json",
"content-encoding": "gzip",
"content-length": "246",
"content-type": "application\/json;charset=utf-8",
"date": "Sun, 26 Apr 2020 00:31:09 GMT",
"expires": "Tue, 31 Mar 1981 05:00:00 GMT",
"last-modified": "Sun, 26 Apr 2020 00:31:09 GMT",
"pragma": "no-cache",
"server": "tsa_b",
"set-cookie": "personalization_id=\"v1_1Yr9ogG1fxy1wdDOY63jAw==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:09 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None, lang=en; Path=\/, guest_id=v1%3A158786106956820884; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:09 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None",
"status": "200 OK",
"strict-transport-security": "max-age=631138519",
"x-access-level": "read-write-directmessages",
"x-connection-hash": "e8b1e309982b5c1d1adedc814fa2e6c3",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-rate-limit-limit": "180",
"x-rate-limit-remaining": "178",
"x-rate-limit-reset": "1587861610",
"x-response-time": "20",
"x-transaction": "0075ffd2008ff583",
"x-twitter-response-tags": "BouncerCompliant",
"x-xss-protection": "0"
},
"body": "{\"relationship\":{\"source\":{\"id\":93915746,\"id_str\":\"93915746\",\"screen_name\":\"oauthlibtest\",\"following\":false,\"followed_by\":false,\"live_following\":false,\"following_received\":false,\"following_requested\":false,\"notifications_enabled\":false,\"can_dm\":false,\"blocking\":false,\"blocked_by\":false,\"muting\":false,\"want_retweets\":false,\"all_replies\":false,\"marked_spam\":false},\"target\":{\"id\":6253282,\"id_str\":\"6253282\",\"screen_name\":\"TwitterAPI\",\"following\":false,\"followed_by\":false,\"following_received\":false,\"following_requested\":false}}}"
}
}]

View file

@ -0,0 +1,20 @@
[{
"request": {
"method": "GET",
"url": "https:\/\/api.twitter.com\/1.1\/account\/verify_credentials.json",
"headers": {
"Host": "api.twitter.com",
"Accept": "application\/json",
"Authorization": "OAuth oauth_version=\"1.0\", oauth_nonce=\"2b67ebbeace76543f356ba8bbd59abde\", oauth_timestamp=\"1587861062\", oauth_consumer_key=\"awJfND4zFGapGOFKfdjg\", oauth_token=\"93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"ZN1bM0df5EPRy1d7oYcoZfj3Mpw%3D\"",
"Expect": null
}
},
"response": {
"status": {
"http_version": "1.1",
"code": "200",
"message": "OK"
},
"body": "HTTP\/2 200 \r\ncache-control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0\r\ncontent-disposition: attachment; filename=json.json\r\ncontent-encoding: gzip\r\ncontent-length: 778\r\ncontent-type: application\/json;charset=utf-8\r\ndate: Sun, 26 Apr 2020 00:31:49 GMT\r\nexpires: Tue, 31 Mar 1981 05:00:00 GMT\r\nlast-modified: Sun, 26 Apr 2020 00:31:49 GMT\r\npragma: no-cache\r\nserver: tsa_a\r\nset-cookie: personalization_id=\"v1_hI7rl+lJjoy5n7HgyNo9LQ==\"; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:49 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None\r\nset-cookie: lang=en; Path=\/\r\nset-cookie: guest_id=v1%3A158786110929931313; Max-Age=63072000; Expires=Tue, 26 Apr 2022 00:31:49 GMT; Path=\/; Domain=.twitter.com; Secure; SameSite=None\r\nstatus: 200 OK\r\nstrict-transport-security: max-age=631138519\r\nx-access-level: read-write-directmessages\r\nx-connection-hash: 18e8b1b5df2ee964aebf8f85055ada9c\r\nx-content-type-options: nosniff\r\nx-frame-options: SAMEORIGIN\r\nx-rate-limit-limit: 75\r\nx-rate-limit-remaining: 72\r\nx-rate-limit-reset: 1587861178\r\nx-response-time: 34\r\nx-transaction: 00716cfd00f6fd8d\r\nx-twitter-response-tags: BouncerExempt\r\nx-twitter-response-tags: BouncerCompliant\r\nx-xss-protection: 0\r\n\r\n{\"id\":93915746,\"id_str\":\"93915746\",\"name\":\"OAuth Library Test\",\"screen_name\":\"oauthlibtest\",\"location\":\"\",\"description\":\"\",\"url\":null,\"entities\":{\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":58,\"friends_count\":2,\"listed_count\":6,\"created_at\":\"Tue Dec 01 18:37:44 +0000 2009\",\"favourites_count\":0,\"utc_offset\":null,\"time_zone\":null,\"geo_enabled\":true,\"verified\":false,\"statuses_count\":1,\"lang\":null,\"status\":{\"created_at\":\"Tue Dec 01 18:38:07 +0000 2009\",\"id\":6242973112,\"id_str\":\"6242973112\",\"text\":\"Test!\",\"truncated\":false,\"entities\":{\"hashtags\":[],\"symbols\":[],\"user_mentions\":[],\"urls\":[]},\"source\":\"\\u003ca href=\\\"http:\\\/\\\/twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web Client\\u003c\\\/a\\u003e\",\"in_reply_to_status_id\":null,\"in_reply_to_status_id_str\":null,\"in_reply_to_user_id\":null,\"in_reply_to_user_id_str\":null,\"in_reply_to_screen_name\":null,\"geo\":null,\"coordinates\":null,\"place\":null,\"contributors\":null,\"is_quote_status\":false,\"retweet_count\":2258,\"favorite_count\":74,\"favorited\":false,\"retweeted\":false,\"lang\":\"en\"},\"contributors_enabled\":false,\"is_translator\":false,\"is_translation_enabled\":false,\"profile_background_color\":\"C0DEED\",\"profile_background_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/images\\\/themes\\\/theme1\\\/bg.png\",\"profile_background_tile\":false,\"profile_image_url\":\"http:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_image_url_https\":\"https:\\\/\\\/abs.twimg.com\\\/sticky\\\/default_profile_images\\\/default_profile_normal.png\",\"profile_link_color\":\"1DA1F2\",\"profile_sidebar_border_color\":\"C0DEED\",\"profile_sidebar_fill_color\":\"DDEEF6\",\"profile_text_color\":\"333333\",\"profile_use_background_image\":true,\"has_extended_profile\":false,\"default_profile\":true,\"default_profile_image\":true,\"following\":false,\"follow_request_sent\":false,\"notifications\":false,\"translator_type\":\"none\",\"suspended\":false,\"needs_phone_verification\":false}"
}
}]

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

View file

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
// Mock time and random values for consistent tests with VCR
function time()
{
return MOCK_TIME;
}
function microtime()
{
return 'FAKE_MICROTIME';
}
function mt_rand()
{
return 123456789;
}

View file

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
// These keys have been revoked and are only valid for teh VCR cassettes.
// To request new VCR cassettes please open an issue: https://github.com/abraham/twitteroauth/issues
// To update VCR cassettes
// 1. Delete all `tests/fixtures/*` files
// `rm tests/fixtures/*`
// 2. Set application and suer authorization credentials below
// 3. Set MOCK_TIME to current unix time
// 4. Run PHPUnit tests
// 5. Reset application credentials on Twitter dashboard
// 6. Commit new cassettes and revoked credentials
// TwitterOAuth dev
define('CONSUMER_KEY', 'awJfND4zFGapGOFKfdjg');
define('CONSUMER_SECRET', 'LfkmNSRPIXwkQkZUB9DNWSzx5LIaivSknV4rxngojJc');
define('OAUTH_CALLBACK', 'https://twitteroauth.com/callback.php');
// oauthlibtest
define('ACCESS_TOKEN', '93915746-KjE3c27dCt8awONxuUAaJ00yishXXwcH5CdLBnO1x');
define('ACCESS_TOKEN_SECRET', 'vurSbgJw6nHvv7xBfqKnBLWEQekOi59KFkXDLiY3Vqn3u');
// Timestamp the VCR cassettes were last updated
define('MOCK_TIME', 1587861062);
// https://free-proxy-list.net/
define('PROXY', '12.218.209.130');
define('PROXYUSERPWD', '');
define('PROXYPORT', '53281');

Binary file not shown.