Merge remote-tracking branch 'upstream/develop' into more-q

This commit is contained in:
Michael 2021-10-03 17:40:49 +00:00
commit deb4f8d9ef
173 changed files with 11971 additions and 2238 deletions

View File

@ -21,6 +21,9 @@ Excerp from nitters about page.
Changelog
---------
* **Version 2.0**
* Changes the used hook by the addon, so that attached previews of postings get replaced as well.
This means the admins need to reload the addon
* **Version 1.1**
* Initial localization support with DE translation
* Configurable nitter instance address from the admin panel

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 07:44+0100\n"
"POT-Creation-Date: 2021-10-01 16:10+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: nitter.php:51
#: nitter.php:52
#, php-format
msgid ""
"Which nitter server shall be used for the replacements in the post bodies? "
@ -25,17 +25,17 @@ msgid ""
"public Nitter servers."
msgstr ""
#: nitter.php:52
#: nitter.php:53
msgid "Nitter server"
msgstr ""
#: nitter.php:53
#: nitter.php:54
msgid "Save Settings"
msgstr ""
#: nitter.php:65
#: nitter.php:99
#, php-format
msgid ""
"Links to Twitter in this posting were replaced by links to the Nitter "
"instance at %s"
"In an attempt to protect your privacy, links to Twitter in this posting were "
"replaced by links to the Nitter instance at %s"
msgstr ""

View File

@ -1,41 +1,50 @@
# ADDON nitter
# Copyright (C)
# Copyright (C)
# This file is distributed under the same license as the Friendica nitter addon package.
#
#
#
# Translators:
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2021
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 07:44+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"POT-Creation-Date: 2021-10-01 16:10+0200\n"
"PO-Revision-Date: 2021-05-21 12:58+0000\n"
"Last-Translator: Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2021\n"
"Language-Team: German (https://www.transifex.com/Friendica/teams/12172/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: nitter.php:51
#: nitter.php:52
#, php-format
msgid ""
"Which nitter server shall be used for the replacements in the post bodies? "
"Use the URL with servername and protocol. See %s for a list of available "
"public Nitter servers."
msgstr "Welcher Nitter server soll für die Ersetzungen verwendet werden? Gib die URL mit Servername und Protokoll an. Eine Liste von öffentlichen Nitter servern findest du unter %s."
msgstr ""
"Welcher Nitter server soll für die Ersetzungen verwendet werden? Gib die URL"
" mit Servername und Protokoll an. Eine Liste von öffentlichen Nitter servern"
" findest du unter %s."
#: nitter.php:52
#: nitter.php:53
msgid "Nitter server"
msgstr "Nitter Server"
#: nitter.php:53
#: nitter.php:54
msgid "Save Settings"
msgstr "Einstellungen Speichern"
#: nitter.php:65
#: nitter.php:99
#, php-format
msgid ""
"Links to Twitter in this posting were replaced by links to the Nitter "
"instance at %s"
msgstr "In diesem Beitrag wurden Links nach twitter.com durch die Nitter Instanz auf %s ersetzt."
"In an attempt to protect your privacy, links to Twitter in this posting were"
" replaced by links to the Nitter instance at %s"
msgstr ""
"Um deine Privatsphäre zu schützen, wurden in diesem Beitrag Links nach "
"twitter.com durch die Nitter Instanz auf %s ersetzt."

View File

@ -1,7 +1,10 @@
<?php
if(! function_exists("string_plural_select_de")) {
function string_plural_select_de($n){
$n = intval($n);
return intval($n != 1);
}}
;
$a->strings["Which nitter server shall be used for the replacements in the post bodies? Use the URL with servername and protocol. See %s for a list of available public Nitter servers."] = "Welcher Nitter server soll für die Ersetzungen verwendet werden? Gib die URL mit Servername und Protokoll an. Eine Liste von öffentlichen Nitter servern findest du unter %s.";
$a->strings["Nitter server"] = "Nitter Server";
$a->strings["Save Settings"] = "Einstellungen Speichern";
$a->strings["Links to Twitter in this posting were replaced by links to the Nitter instance at %s"] = "In diesem Beitrag wurden Links nach twitter.com durch die Nitter Instanz auf %s ersetzt.";

View File

@ -2,7 +2,7 @@
/*
* Name: nitter
* Description: Replaces links to twitter.com to a nitter server in all displays of postings on a node.
* Version: 1.1
* Version: 2.0
* Author: Tobias Diekershoff <tobias@social.diekershoff.de>
*
* Copyright (c) 2020 Tobias Diekershoff
@ -30,7 +30,7 @@ use Friendica\DI;
function nitter_install()
{
Addon::registerHook ('prepare_body', 'addon/nitter/nitter.php', 'nitter_render');
Addon::registerHook ('prepare_body_final', 'addon/nitter/nitter.php', 'nitter_render');
}
/* Handle the send data from the admin settings
@ -72,6 +72,6 @@ function nitter_render(&$a, &$o)
$replaced = true;
}
if ($replaced) {
$o['html'] .= '<hr><p>' . DI::l10n()->t('Links to Twitter in this posting were replaced by links to the Nitter instance at %s', $nitter) . '</p>';
$o['html'] .= '<hr><p>' . DI::l10n()->t('In an attempt to protect your privacy, links to Twitter in this posting were replaced by links to the Nitter instance at %s', $nitter) . '</p>';
}
}

View File

@ -21,7 +21,7 @@
}
],
"require": {
"abraham/twitteroauth": "^0.7.4",
"abraham/twitteroauth": "2.0.0",
"jublonet/codebird-php": "dev-master"
},
"license": "3-clause BSD license",

101
twitter/composer.lock generated
View File

@ -4,30 +4,33 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "eaeac282537509fd7d0bc4f92f01bc04",
"content-hash": "fafd1db0b4f04c4268f5034ce8c7f6ea",
"packages": [
{
"name": "abraham/twitteroauth",
"version": "0.7.4",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/abraham/twitteroauth.git",
"reference": "c6f9e692552dd037b2324ed0dfa28a4e60875acf"
"reference": "96f49e67baec10f5e5cb703d87be16ba01a798a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abraham/twitteroauth/zipball/c6f9e692552dd037b2324ed0dfa28a4e60875acf",
"reference": "c6f9e692552dd037b2324ed0dfa28a4e60875acf",
"url": "https://api.github.com/repos/abraham/twitteroauth/zipball/96f49e67baec10f5e5cb703d87be16ba01a798a5",
"reference": "96f49e67baec10f5e5cb703d87be16ba01a798a5",
"shasum": ""
},
"require": {
"composer/ca-bundle": "^1.2",
"ext-curl": "*",
"php": "^5.6 || ^7.0"
"php": "^7.2 || ^7.3 || ^7.4 || ^8.0"
},
"require-dev": {
"phpmd/phpmd": "~2.6",
"phpunit/phpunit": "~5.7",
"squizlabs/php_codesniffer": "~3.0"
"php-vcr/php-vcr": "^1",
"php-vcr/phpunit-testlistener-vcr": "dev-php-8",
"phpmd/phpmd": "^2",
"phpunit/phpunit": "^8",
"squizlabs/php_codesniffer": "^3"
},
"type": "library",
"autoload": {
@ -58,7 +61,78 @@
"social",
"twitter"
],
"time": "2017-06-30T22:02:01+00:00"
"time": "2020-12-02T01:27:06+00:00"
},
{
"name": "composer/ca-bundle",
"version": "1.2.10",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "9fdb22c2e97a614657716178093cd1da90a64aa8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/9fdb22c2e97a614657716178093cd1da90a64aa8",
"reference": "9fdb22c2e97a614657716178093cd1da90a64aa8",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^0.12.55",
"psr/log": "^1.0",
"symfony/phpunit-bridge": "^4.2 || ^5",
"symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\CaBundle\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
"keywords": [
"cabundle",
"cacert",
"certificate",
"ssl",
"tls"
],
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2021-06-07T13:58:28+00:00"
},
{
"name": "composer/installers",
@ -185,12 +259,12 @@
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/jublonet/codebird-php.git",
"url": "https://github.com/jublo/codebird-php.git",
"reference": "df362d8ad629aad6c4c7dbf36a440e569ec41368"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jublonet/codebird-php/zipball/df362d8ad629aad6c4c7dbf36a440e569ec41368",
"url": "https://api.github.com/repos/jublo/codebird-php/zipball/df362d8ad629aad6c4c7dbf36a440e569ec41368",
"reference": "df362d8ad629aad6c4c7dbf36a440e569ec41368",
"shasum": ""
},
@ -249,5 +323,6 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
"platform-dev": [],
"plugin-api-version": "1.1.0"
}

View File

@ -104,7 +104,11 @@ function twitter_install()
Hook::register('notifier_normal' , __FILE__, 'twitter_post_hook');
Hook::register('jot_networks' , __FILE__, 'twitter_jot_nets');
Hook::register('cron' , __FILE__, 'twitter_cron');
Hook::register('support_follow' , __FILE__, 'twitter_support_follow');
Hook::register('follow' , __FILE__, 'twitter_follow');
Hook::register('unfollow' , __FILE__, 'twitter_unfollow');
Hook::register('block' , __FILE__, 'twitter_block');
Hook::register('unblock' , __FILE__, 'twitter_unblock');
Hook::register('expire' , __FILE__, 'twitter_expire');
Hook::register('prepare_body' , __FILE__, 'twitter_prepare_body');
Hook::register('check_item_notification', __FILE__, 'twitter_check_item_notification');
@ -134,6 +138,13 @@ function twitter_check_item_notification(App $a, array &$notification_data)
}
}
function twitter_support_follow(App $a, array &$data)
{
if ($data['protocol'] == Protocol::TWITTER) {
$data['result'] = true;
}
}
function twitter_follow(App $a, array &$contact)
{
Logger::info('Check if contact is twitter contact', ['url' => $contact["url"]]);
@ -148,19 +159,7 @@ function twitter_follow(App $a, array &$contact)
$uid = $a->getLoggedInUserId();
$ckey = DI::config()->get('twitter', 'consumerkey');
$csecret = DI::config()->get('twitter', 'consumersecret');
$otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
$osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
// If the addon is not configured (general or for this user) quit here
if (empty($ckey) || empty($csecret) || empty($otoken) || empty($osecret)) {
$contact = false;
return;
}
$connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
$connection->post('friendships/create', ['screen_name' => $nickname]);
twitter_api_contact('friendships/create', ['network' => Protocol::TWITTER, 'nick' => $nickname], $uid);
$user = twitter_fetchuser($nickname);
@ -173,6 +172,48 @@ function twitter_follow(App $a, array &$contact)
}
}
function twitter_unfollow(App $a, array &$hook_data)
{
$hook_data['result'] = twitter_api_contact('friendship/destroy', $hook_data['contact'], $hook_data['uid']);
}
function twitter_block(App $a, array &$hook_data)
{
$hook_data['result'] = twitter_api_contact('blocks/create', $hook_data['contact'], $hook_data['uid']);
}
function twitter_unblock(App $a, array &$hook_data)
{
$hook_data['result'] = twitter_api_contact('blocks/destroy', $hook_data['contact'], $hook_data['uid']);
}
function twitter_api_contact(string $apiPath, array $contact, int $uid): ?bool
{
if ($contact['network'] !== Protocol::TWITTER) {
return null;
}
$ckey = DI::config()->get('twitter', 'consumerkey');
$csecret = DI::config()->get('twitter', 'consumersecret');
$otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
$osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
// If the addon is not configured (general or for this user) quit here
if (empty($ckey) || empty($csecret) || empty($otoken) || empty($osecret)) {
return null;
}
try {
$connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
$result = $connection->post($apiPath, ['screen_name' => $contact['nick']]);
Logger::info('[twitter] API call successful', ['apiPath' => $apiPath, 'result' => $result]);
return true;
} catch (Exception $e) {
Logger::notice('[twitter] API call failed', ['apiPath' => $apiPath, 'uid' => $uid, 'url' => $contact['url'], 'exception' => $e]);
return false;
}
}
function twitter_jot_nets(App $a, array &$jotnets_fields)
{
if (!local_user()) {
@ -481,7 +522,7 @@ function twitter_probe_detect(App $a, array &$hookData)
}
}
function twitter_action(App $a, $uid, $pid, $action)
function twitter_api_post(string $apiPath, string $pid, int $uid)
{
if (empty($pid)) {
return;
@ -496,34 +537,21 @@ function twitter_action(App $a, $uid, $pid, $action)
$post = ['id' => $pid];
Logger::debug('before action', ['action' => $action, 'pid' => $pid, 'data' => $post]);
$result = [];
Logger::debug('before action', ['action' => $apiPath, 'pid' => $pid, 'data' => $post]);
try {
switch ($action) {
case 'delete':
// To-Do: $result = $connection->post('statuses/destroy', $post);
break;
case 'like':
$result = $connection->post('favorites/create', $post);
if ($connection->getLastHttpCode() != 200) {
Logger::warning('Unable to create favorite', ['result' => $result]);
}
break;
case 'unlike':
$result = $connection->post('favorites/destroy', $post);
if ($connection->getLastHttpCode() != 200) {
Logger::warning('Unable to destroy favorite', ['result' => $result]);
}
break;
default:
Logger::warning('Unhandled action', ['action' => $action]);
$result = $connection->post($apiPath, $post);
if ($connection->getLastHttpCode() != 200) {
Logger::warning('[twitter] API call unsuccessful', ['apiPath' => $apiPath, 'post' => $post, 'result' => $result]);
}
} catch (TwitterOAuthException $twitterOAuthException) {
Logger::warning('Unable to communicate with twitter', ['action' => $action, 'data' => $post, 'code' => $twitterOAuthException->getCode(), 'exception' => $twitterOAuthException]);
Logger::warning('Unable to communicate with twitter', ['apiPath' => $apiPath, 'data' => $post, 'code' => $twitterOAuthException->getCode(), 'exception' => $twitterOAuthException]);
$result = false;
}
Logger::info('after action', ['action' => $action, 'result' => $result]);
Logger::info('after action', ['action' => $apiPath, 'result' => $result]);
return $result;
}
function twitter_get_id(string $uri)
@ -594,17 +622,19 @@ function twitter_post_hook(App $a, array &$b)
}
}
if (($b['verb'] == Activity::POST) && $b['deleted']) {
twitter_action($a, $b['uid'], twitter_get_id($thr_parent['uri']), 'delete');
}
/**
* @TODO This can't work at the moment:
* - Posts created on Friendica and mirrored to Twitter don't have a Twitter ID
* - Posts created on Twitter and mirrored on Friendica do not trigger the notifier hook this is part of.
*/
//if (($b['verb'] == Activity::POST) && $b['deleted']) {
// twitter_api_post('statuses/destroy', twitter_get_id($thr_parent['uri']), $b['uid']);
//}
if ($b['verb'] == Activity::LIKE) {
Logger::info('Like', ['uid' => $b['uid'], 'id' => twitter_get_id($b["thr-parent"])]);
if ($b['deleted']) {
twitter_action($a, $b["uid"], twitter_get_id($b["thr-parent"]), "unlike");
} else {
twitter_action($a, $b["uid"], twitter_get_id($b["thr-parent"]), "like");
}
twitter_api_post($b['deleted'] ? 'favorite/destroy' : 'favorite/create', twitter_get_id($b["thr-parent"]), $b["uid"]);
return;
}
@ -612,7 +642,11 @@ function twitter_post_hook(App $a, array &$b)
if ($b['verb'] == Activity::ANNOUNCE) {
Logger::info('Retweet', ['uid' => $b['uid'], 'id' => twitter_get_id($b["thr-parent"])]);
if ($b['deleted']) {
twitter_action($a, $b['uid'], twitter_get_id($thr_parent['extid']), 'delete');
/**
* @TODO This can't work at the moment:
* - Twitter post reshare removal doesn't seem to trigger the notifier hook this is part of
*/
//twitter_api_post('statuses/destroy', twitter_get_id($thr_parent['extid']), $b['uid']);
} else {
twitter_retweet($b["uid"], twitter_get_id($b["thr-parent"]));
}
@ -740,8 +774,7 @@ function twitter_post_hook(App $a, array &$b)
$post['in_reply_to_status_id'] = twitter_get_id($thr_parent['uri']);
}
$url = 'statuses/update';
$result = $connection->post($url, $post);
$result = $connection->post('statuses/update', $post);
Logger::info('twitter_post send', ['id' => $b['id'], 'result' => $result]);
if (!empty($result->source)) {
@ -2116,13 +2149,7 @@ function twitter_retweet(int $uid, int $id, int $item_id = 0)
{
Logger::info('Retweeting', ['user' => $uid, 'id' => $id]);
$ckey = DI::config()->get('twitter', 'consumerkey');
$csecret = DI::config()->get('twitter', 'consumersecret');
$otoken = DI::pConfig()->get($uid, 'twitter', 'oauthtoken');
$osecret = DI::pConfig()->get($uid, 'twitter', 'oauthsecret');
$connection = new TwitterOAuth($ckey, $csecret, $otoken, $osecret);
$result = $connection->post('statuses/retweet/' . $id);
$result = twitter_api_post('statuses/retweet', $id, $uid);
Logger::info('Retweeted', ['user' => $uid, 'id' => $id, 'result' => $result]);

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

@ -1,4 +1,5 @@
.DS_Store
composer.lock
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

@ -1,13 +0,0 @@
language: php
dist: trusty
php:
- '5.6'
- '7.0'
- '7.1'
- hhvm
sudo: false
before_script:
- composer self-update
- composer install --prefer-source --no-interaction
script:
- vendor/bin/phpunit

View File

@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
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
- 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
- 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

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

@ -1,5 +1,5 @@
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
@ -8,10 +8,10 @@ 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

View File

@ -1,5 +1,6 @@
<span itemprop="name">TwitterOAuth</span> [![Build Status](https://img.shields.io/travis/abraham/twitteroauth.svg)](https://travis-ci.org/abraham/twitteroauth) [![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)
------------
<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>
@ -7,4 +8,4 @@ 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-demo/master/images/twitter-logo-blue.png" itemprop="image" alt="Twitter bird" width="200px">
<img src="https://raw.githubusercontent.com/abraham/twitteroauth-com/master/images/twitter-logo-blue.png" itemprop="image" alt="Twitter bird" width="200px">

View File

@ -7,7 +7,6 @@
* @return void
*/
spl_autoload_register(function ($class) {
// project-specific namespace prefix
$prefix = 'Abraham\\TwitterOAuth\\';

View File

@ -1,34 +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"
},
"require": {
"php": "^5.6 || ^7.0",
"ext-curl": "*"
},
"require-dev": {
"phpunit/phpunit": "~5.7",
"squizlabs/php_codesniffer": "~3.0",
"phpmd/phpmd": "~2.6"
},
"autoload": {
"psr-4": {
"Abraham\\TwitterOAuth\\": "src"
}
"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
twitter/vendor/abraham/twitteroauth/composer.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

511
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

@ -1,11 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
bootstrap="tests/bootstrap.php">
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

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**
@ -13,6 +15,11 @@ class Config
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
*
@ -38,32 +45,44 @@ class Config
* @param int $connectionTimeout
* @param int $timeout
*/
public function setTimeouts($connectionTimeout, $timeout)
public function setTimeouts(int $connectionTimeout, int $timeout): void
{
$this->connectionTimeout = (int)$connectionTimeout;
$this->timeout = (int)$timeout;
$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($value)
public function setDecodeJsonAsArray(bool $value): void
{
$this->decodeJsonAsArray = (bool)$value;
$this->decodeJsonAsArray = $value;
}
/**
* @param string $userAgent
*/
public function setUserAgent($userAgent)
public function setUserAgent(string $userAgent): void
{
$this->userAgent = (string)$userAgent;
$this->userAgent = $userAgent;
}
/**
* @param array $proxy
*/
public function setProxy(array $proxy)
public function setProxy(array $proxy): void
{
$this->proxy = $proxy;
}
@ -73,9 +92,9 @@ class Config
*
* @param boolean $gzipEncoding
*/
public function setGzipEncoding($gzipEncoding)
public function setGzipEncoding(bool $gzipEncoding): void
{
$this->gzipEncoding = (bool)$gzipEncoding;
$this->gzipEncoding = $gzipEncoding;
}
/**
@ -83,8 +102,8 @@ class Config
*
* @param int $value
*/
public function setChunkSize($value)
public function setChunkSize(int $value): void
{
$this->chunkSize = (int)$value;
$this->chunkSize = $value;
}
}

View File

@ -1,8 +1,12 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
class Consumer
@ -15,12 +19,15 @@ class Consumer
public $callbackUrl;
/**
* @param string $key
* @param string $secret
* @param string|null $key
* @param string|null $secret
* @param null $callbackUrl
*/
public function __construct($key, $secret, $callbackUrl = null)
{
public function __construct(
?string $key,
?string $secret,
?string $callbackUrl = null
) {
$this->key = $key;
$this->secret = $secret;
$this->callbackUrl = $callbackUrl;

View File

@ -1,8 +1,12 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**
@ -19,17 +23,20 @@ class HmacSha1 extends SignatureMethod
*/
public function getName()
{
return "HMAC-SHA1";
return 'HMAC-SHA1';
}
/**
* {@inheritDoc}
*/
public function buildSignature(Request $request, Consumer $consumer, Token $token = null)
{
public function buildSignature(
Request $request,
Consumer $consumer,
Token $token = null
): string {
$signatureBase = $request->getSignatureBaseString();
$parts = [$consumer->secret, null !== $token ? $token->secret : ""];
$parts = [$consumer->secret, null !== $token ? $token->secret : ''];
$parts = Util::urlencodeRfc3986($parts);
$key = implode('&', $parts);

View File

@ -1,8 +1,12 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
class Request
@ -10,6 +14,7 @@ class Request
protected $parameters;
protected $httpMethod;
protected $httpUrl;
protected $json;
public static $version = '1.0';
/**
@ -19,9 +24,15 @@ class Request
* @param string $httpUrl
* @param array|null $parameters
*/
public function __construct($httpMethod, $httpUrl, array $parameters = [])
{
$parameters = array_merge(Util::parseParameters(parse_url($httpUrl, PHP_URL_QUERY)), $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;
@ -41,21 +52,28 @@ class Request
public static function fromConsumerAndToken(
Consumer $consumer,
Token $token = null,
$httpMethod,
$httpUrl,
array $parameters = []
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
'oauth_version' => Request::$version,
'oauth_nonce' => Request::generateNonce(),
'oauth_timestamp' => time(),
'oauth_consumer_key' => $consumer->key,
];
if (null !== $token) {
$defaults['oauth_token'] = $token->key;
}
$parameters = array_merge($defaults, $parameters);
// 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);
}
@ -64,33 +82,35 @@ class Request
* @param string $name
* @param string $value
*/
public function setParameter($name, $value)
public function setParameter(string $name, string $value)
{
$this->parameters[$name] = $value;
}
/**
* @param $name
* @param string $name
*
* @return string|null
*/
public function getParameter($name)
public function getParameter(string $name): ?string
{
return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
return isset($this->parameters[$name])
? $this->parameters[$name]
: null;
}
/**
* @return array
*/
public function getParameters()
public function getParameters(): array
{
return $this->parameters;
}
/**
* @param $name
* @param string $name
*/
public function removeParameter($name)
public function removeParameter(string $name): void
{
unset($this->parameters[$name]);
}
@ -100,7 +120,7 @@ class Request
*
* @return string
*/
public function getSignableParameters()
public function getSignableParameters(): string
{
// Grab all parameters
$params = $this->parameters;
@ -123,12 +143,12 @@ class Request
*
* @return string
*/
public function getSignatureBaseString()
public function getSignatureBaseString(): string
{
$parts = [
$this->getNormalizedHttpMethod(),
$this->getNormalizedHttpUrl(),
$this->getSignableParameters()
$this->getSignableParameters(),
];
$parts = Util::urlencodeRfc3986($parts);
@ -141,7 +161,7 @@ class Request
*
* @return string
*/
public function getNormalizedHttpMethod()
public function getNormalizedHttpMethod(): string
{
return strtoupper($this->httpMethod);
}
@ -152,7 +172,7 @@ class Request
*
* @return string
*/
public function getNormalizedHttpUrl()
public function getNormalizedHttpUrl(): string
{
$parts = parse_url($this->httpUrl);
@ -168,7 +188,7 @@ class Request
*
* @return string
*/
public function toUrl()
public function toUrl(): string
{
$postData = $this->toPostdata();
$out = $this->getNormalizedHttpUrl();
@ -183,7 +203,7 @@ class Request
*
* @return string
*/
public function toPostdata()
public function toPostdata(): string
{
return Util::buildHttpQuery($this->parameters);
}
@ -194,19 +214,25 @@ class Request
* @return string
* @throws TwitterOAuthException
*/
public function toHeader()
public function toHeader(): string
{
$first = true;
$out = 'Authorization: OAuth';
foreach ($this->parameters as $k => $v) {
if (substr($k, 0, 5) != "oauth") {
if (substr($k, 0, 5) != 'oauth') {
continue;
}
if (is_array($v)) {
throw new TwitterOAuthException('Arrays not supported in headers');
throw new TwitterOAuthException(
'Arrays not supported in headers'
);
}
$out .= ($first) ? ' ' : ', ';
$out .= Util::urlencodeRfc3986($k) . '="' . Util::urlencodeRfc3986($v) . '"';
$out .= $first ? ' ' : ', ';
$out .=
Util::urlencodeRfc3986($k) .
'="' .
Util::urlencodeRfc3986($v) .
'"';
$first = false;
}
return $out;
@ -215,7 +241,7 @@ class Request
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return $this->toUrl();
}
@ -225,11 +251,17 @@ class Request
* @param Consumer $consumer
* @param Token $token
*/
public function signRequest(SignatureMethod $signatureMethod, Consumer $consumer, Token $token = null)
{
$this->setParameter("oauth_signature_method", $signatureMethod->getName());
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);
$this->setParameter('oauth_signature', $signature);
}
/**
@ -239,15 +271,18 @@ class Request
*
* @return string
*/
public function buildSignature(SignatureMethod $signatureMethod, Consumer $consumer, Token $token = null)
{
public function buildSignature(
SignatureMethod $signatureMethod,
Consumer $consumer,
Token $token = null
): string {
return $signatureMethod->buildSignature($this, $consumer, $token);
}
/**
* @return string
*/
public static function generateNonce()
public static function generateNonce(): string
{
return md5(microtime() . mt_rand());
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**
@ -23,7 +25,7 @@ class Response
/**
* @param string $apiPath
*/
public function setApiPath($apiPath)
public function setApiPath(string $apiPath): void
{
$this->apiPath = $apiPath;
}
@ -31,7 +33,7 @@ class Response
/**
* @return string|null
*/
public function getApiPath()
public function getApiPath(): ?string
{
return $this->apiPath;
}
@ -55,7 +57,7 @@ class Response
/**
* @param int $httpCode
*/
public function setHttpCode($httpCode)
public function setHttpCode(int $httpCode): void
{
$this->httpCode = $httpCode;
}
@ -63,7 +65,7 @@ class Response
/**
* @return int
*/
public function getHttpCode()
public function getHttpCode(): int
{
return $this->httpCode;
}
@ -71,7 +73,7 @@ class Response
/**
* @param array $headers
*/
public function setHeaders(array $headers)
public function setHeaders(array $headers): void
{
foreach ($headers as $key => $value) {
if (substr($key, 0, 1) == 'x') {
@ -84,7 +86,7 @@ class Response
/**
* @return array
*/
public function getsHeaders()
public function getsHeaders(): array
{
return $this->headers;
}
@ -92,7 +94,7 @@ class Response
/**
* @param array $xHeaders
*/
public function setXHeaders(array $xHeaders = [])
public function setXHeaders(array $xHeaders = []): void
{
$this->xHeaders = $xHeaders;
}
@ -100,7 +102,7 @@ class Response
/**
* @return array
*/
public function getXHeaders()
public function getXHeaders(): array
{
return $this->xHeaders;
}

View File

@ -1,8 +1,12 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**
@ -30,7 +34,11 @@ abstract class SignatureMethod
*
* @return string
*/
abstract public function buildSignature(Request $request, Consumer $consumer, Token $token = null);
abstract public function buildSignature(
Request $request,
Consumer $consumer,
Token $token = null
);
/**
* Verifies that a given signature is correct
@ -42,8 +50,12 @@ abstract class SignatureMethod
*
* @return bool
*/
public function checkSignature(Request $request, Consumer $consumer, Token $token, $signature)
{
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
@ -58,7 +70,7 @@ abstract class SignatureMethod
// 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});
$result |= ord($built[$i]) ^ ord($signature[$i]);
}
return $result == 0;

View File

@ -1,8 +1,12 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
class Token
@ -16,7 +20,7 @@ class Token
* @param string $key The OAuth Token
* @param string $secret The OAuth Token Secret
*/
public function __construct($key, $secret)
public function __construct(?string $key, ?string $secret)
{
$this->key = $key;
$this->secret = $secret;
@ -28,9 +32,10 @@ class Token
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return sprintf("oauth_token=%s&oauth_token_secret=%s",
return sprintf(
'oauth_token=%s&oauth_token_secret=%s',
Util::urlencodeRfc3986($this->key),
Util::urlencodeRfc3986($this->secret)
);

View File

@ -1,12 +1,17 @@
<?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.
@ -15,9 +20,9 @@ use Abraham\TwitterOAuth\Util\JsonDecoder;
*/
class TwitterOAuth extends Config
{
const API_VERSION = '1.1';
const API_HOST = 'https://api.twitter.com';
const UPLOAD_HOST = 'https://upload.twitter.com';
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;
@ -29,6 +34,8 @@ class TwitterOAuth extends Config
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
@ -38,16 +45,20 @@ class TwitterOAuth extends Config
* @param string|null $oauthToken The Client Token (optional)
* @param string|null $oauthTokenSecret The Client Token Secret (optional)
*/
public function __construct($consumerKey, $consumerSecret, $oauthToken = null, $oauthTokenSecret = null)
{
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->token = new Token($oauthToken, $oauthTokenSecret);
$this->setOauthToken($oauthToken, $oauthTokenSecret);
}
if (empty($oauthToken) && !empty($oauthTokenSecret)) {
$this->bearer = $oauthTokenSecret;
$this->setBearer($oauthTokenSecret);
}
}
@ -55,15 +66,27 @@ class TwitterOAuth extends Config
* @param string $oauthToken
* @param string $oauthTokenSecret
*/
public function setOauthToken($oauthToken, $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()
public function getLastApiPath(): ?string
{
return $this->response->getApiPath();
}
@ -71,7 +94,7 @@ class TwitterOAuth extends Config
/**
* @return int
*/
public function getLastHttpCode()
public function getLastHttpCode(): int
{
return $this->response->getHttpCode();
}
@ -79,7 +102,7 @@ class TwitterOAuth extends Config
/**
* @return array
*/
public function getLastXHeaders()
public function getLastXHeaders(): array
{
return $this->response->getXHeaders();
}
@ -95,11 +118,29 @@ class TwitterOAuth extends Config
/**
* Resets the last response cache.
*/
public function resetLastResponse()
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.
*
@ -108,7 +149,7 @@ class TwitterOAuth extends Config
*
* @return string
*/
public function url($path, array $parameters)
public function url(string $path, array $parameters): string
{
$this->resetLastResponse();
$this->response->setApiPath($path);
@ -125,7 +166,7 @@ class TwitterOAuth extends Config
* @return array
* @throws TwitterOAuthException
*/
public function oauth($path, array $parameters = [])
public function oauth(string $path, array $parameters = []): array
{
$response = [];
$this->resetLastResponse();
@ -151,15 +192,28 @@ class TwitterOAuth extends Config
*
* @return array|object
*/
public function oauth2($path, array $parameters = [])
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);
$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;
@ -173,9 +227,9 @@ class TwitterOAuth extends Config
*
* @return array|object
*/
public function get($path, array $parameters = [])
public function get(string $path, array $parameters = [])
{
return $this->http('GET', self::API_HOST, $path, $parameters);
return $this->http('GET', self::API_HOST, $path, $parameters, false);
}
/**
@ -183,12 +237,16 @@ class TwitterOAuth extends Config
*
* @param string $path
* @param array $parameters
* @param bool $json
*
* @return array|object
*/
public function post($path, array $parameters = [])
{
return $this->http('POST', self::API_HOST, $path, $parameters);
public function post(
string $path,
array $parameters = [],
bool $json = false
) {
return $this->http('POST', self::API_HOST, $path, $parameters, $json);
}
/**
@ -199,9 +257,9 @@ class TwitterOAuth extends Config
*
* @return array|object
*/
public function delete($path, array $parameters = [])
public function delete(string $path, array $parameters = [])
{
return $this->http('DELETE', self::API_HOST, $path, $parameters);
return $this->http('DELETE', self::API_HOST, $path, $parameters, false);
}
/**
@ -212,9 +270,9 @@ class TwitterOAuth extends Config
*
* @return array|object
*/
public function put($path, array $parameters = [])
public function put(string $path, array $parameters = [])
{
return $this->http('PUT', self::API_HOST, $path, $parameters);
return $this->http('PUT', self::API_HOST, $path, $parameters, false);
}
/**
@ -226,8 +284,11 @@ class TwitterOAuth extends Config
*
* @return array|object
*/
public function upload($path, array $parameters = [], $chunked = false)
{
public function upload(
string $path,
array $parameters = [],
bool $chunked = false
) {
if ($chunked) {
return $this->uploadMediaChunked($path, $parameters);
} else {
@ -235,6 +296,27 @@ class TwitterOAuth extends Config
}
}
/**
* 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.
*
@ -243,12 +325,24 @@ class TwitterOAuth extends Config
*
* @return array|object
*/
private function uploadMediaNotChunked($path, array $parameters)
private function uploadMediaNotChunked(string $path, array $parameters)
{
$file = file_get_contents($parameters['media']);
$base = base64_encode($file);
$parameters['media'] = $base;
return $this->http('POST', self::UPLOAD_HOST, $path, $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
);
}
/**
@ -259,27 +353,46 @@ class TwitterOAuth extends Config
*
* @return array|object
*/
private function uploadMediaChunked($path, array $parameters)
private function uploadMediaChunked(string $path, array $parameters)
{
$init = $this->http('POST', self::UPLOAD_HOST, $path, $this->mediaInitParameters($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))
]);
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
]);
$finalize = $this->http(
'POST',
self::UPLOAD_HOST,
'media/upload',
[
'command' => 'FINALIZE',
'media_id' => $init->media_id_string,
],
false
);
return $finalize;
}
@ -291,20 +404,41 @@ class TwitterOAuth extends Config
*
* @return array
*/
private function mediaInitParameters(array $parameters)
private function mediaInitParameters(array $parameters): array
{
$return = [
'command' => 'INIT',
'media_type' => $parameters['media_type'],
'total_bytes' => filesize($parameters['media'])
$allowed_keys = [
'media_type',
'additional_owners',
'media_category',
'shared',
];
if (isset($parameters['additional_owners'])) {
$return['additional_owners'] = $parameters['additional_owners'];
$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);
}
}
if (isset($parameters['media_category'])) {
$return['media_category'] = $parameters['media_category'];
}
return $return;
return $parameters;
}
/**
@ -312,39 +446,104 @@ class TwitterOAuth extends Config
* @param string $host
* @param string $path
* @param array $parameters
* @param bool $json
*
* @return array|object
*/
private function http($method, $host, $path, array $parameters)
{
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);
$result = $this->oAuthRequest($url, $method, $parameters);
$response = JsonDecoder::decode($result, $this->decodeJsonAsArray);
$this->response->setBody($response);
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($url, $method, array $parameters)
{
$request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters);
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);
$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
@ -354,7 +553,13 @@ class TwitterOAuth extends Config
} else {
$authorization = 'Authorization: Bearer ' . $this->bearer;
}
return $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters);
return $this->request(
$request->getNormalizedHttpUrl(),
$method,
$authorization,
$parameters,
$json
);
}
/**
@ -362,8 +567,9 @@ class TwitterOAuth extends Config
*
* @return array
*/
private function curlOptions()
private function curlOptions(): array
{
$bundlePath = CaBundle::getSystemCaRootBundlePath();
$options = [
// CURLOPT_VERBOSE => true,
CURLOPT_CONNECTTIMEOUT => $this->connectionTimeout,
@ -373,19 +579,17 @@ class TwitterOAuth extends Config
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_USERAGENT => $this->userAgent,
$this->curlCaOpt($bundlePath) => $bundlePath,
];
if ($this->useCAFile()) {
$options[CURLOPT_CAINFO] = __DIR__ . DIRECTORY_SEPARATOR . 'cacert.pem';
}
if($this->gzipEncoding) {
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_PROXYUSERPWD] =
$this->proxy['CURLOPT_PROXYUSERPWD'];
$options[CURLOPT_PROXYPORT] = $this->proxy['CURLOPT_PROXYPORT'];
$options[CURLOPT_PROXYAUTH] = CURLAUTH_BASIC;
$options[CURLOPT_PROXYTYPE] = CURLPROXY_HTTP;
@ -401,22 +605,40 @@ class TwitterOAuth extends Config
* @param string $method
* @param string $authorization
* @param array $postfields
* @param bool $json
*
* @return string
* @throws TwitterOAuthException
*/
private function request($url, $method, $authorization, array $postfields)
{
$options = $this->curlOptions($url, $authorization);
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:'];
$options[CURLOPT_HTTPHEADER] = [
'Accept: application/json',
$authorization,
'Expect:',
];
switch ($method) {
case 'GET':
break;
case 'POST':
$options[CURLOPT_POST] = true;
$options[CURLOPT_POSTFIELDS] = Util::buildHttpQuery($postfields);
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';
@ -426,21 +648,28 @@ class TwitterOAuth extends Config
break;
}
if (in_array($method, ['GET', 'PUT', 'DELETE']) && !empty($postfields)) {
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) {
throw new TwitterOAuthException(curl_error($curlHandle), curl_errno($curlHandle));
$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));
$this->response->setHttpCode(
curl_getinfo($curlHandle, CURLINFO_HTTP_CODE)
);
$parts = explode("\r\n\r\n", $response);
$responseBody = array_pop($parts);
$responseHeader = array_pop($parts);
@ -458,12 +687,12 @@ class TwitterOAuth extends Config
*
* @return array
*/
private function parseHeaders($header)
private function parseHeaders(string $header): array
{
$headers = [];
foreach (explode("\r\n", $header) as $line) {
if (strpos($line, ':') !== false) {
list ($key, $value) = explode(': ', $line);
[$key, $value] = explode(': ', $line);
$key = str_replace('-', '_', strtolower($key));
$headers[$key] = trim($value);
}
@ -478,7 +707,7 @@ class TwitterOAuth extends Config
*
* @return string
*/
private function encodeAppAuthorization(Consumer $consumer)
private function encodeAppAuthorization(Consumer $consumer): string
{
$key = rawurlencode($consumer->key);
$secret = rawurlencode($consumer->secret);
@ -486,23 +715,13 @@ class TwitterOAuth extends Config
}
/**
* Is the code running from a Phar module.
* Get Curl CA option based on whether the given path is a directory or file.
*
* @return boolean
* @param string $path
* @return int
*/
private function pharRunning()
private function curlCaOpt(string $path): int
{
return class_exists('Phar') && \Phar::running(false) !== '';
}
/**
* Use included CA file instead of OS provided list.
*
* @return boolean
*/
private function useCAFile()
{
/* Use CACert file when not in a PHAR file. */
return !$this->pharRunning();
return is_dir($path) ? CURLOPT_CAPATH : CURLOPT_CAINFO;
}
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
/**

View File

@ -1,24 +1,31 @@
<?php
/**
* The MIT License
* Copyright (c) 2007 Andy Smith
*/
declare(strict_types=1);
namespace Abraham\TwitterOAuth;
class Util
{
/**
* @param $input
* @param mixed $input
*
* @return array|mixed|string
* @return mixed
*/
public static function urlencodeRfc3986($input)
{
$output = '';
if (is_array($input)) {
$output = array_map([__NAMESPACE__ . '\Util', 'urlencodeRfc3986'], $input);
$output = array_map(
[__NAMESPACE__ . '\Util', 'urlencodeRfc3986'],
$input
);
} elseif (is_scalar($input)) {
$output = rawurlencode($input);
$output = rawurlencode((string) $input);
}
return $output;
}
@ -28,7 +35,7 @@ class Util
*
* @return string
*/
public static function urldecodeRfc3986($string)
public static function urldecodeRfc3986($string): string
{
return urldecode($string);
}
@ -42,7 +49,7 @@ class Util
*
* @return array
*/
public static function parseParameters($input)
public static function parseParameters($input): array
{
if (!is_string($input)) {
return [];
@ -79,7 +86,7 @@ class Util
*
* @return string
*/
public static function buildHttpQuery(array $params)
public static function buildHttpQuery(array $params): string
{
if (empty($params)) {
return '';

View File

@ -15,9 +15,12 @@ class JsonDecoder
*
* @return array|object
*/
public static function decode($string, $asArray)
public static function decode(string $string, bool $asArray)
{
if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
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);
}

View File

@ -1,10 +1,13 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth\Tests;
use PHPUnit\Framework\TestCase;
use Abraham\TwitterOAuth\SignatureMethod;
abstract class AbstractSignatureMethodTest extends \PHPUnit_Framework_TestCase
abstract class AbstractSignatureMethodTest extends TestCase
{
protected $name;
@ -25,7 +28,10 @@ abstract class AbstractSignatureMethodTest extends \PHPUnit_Framework_TestCase
*/
public function testBuildSignature($expected, $request, $consumer, $token)
{
$this->assertEquals($expected, $this->getClass()->buildSignature($request, $consumer, $token));
$this->assertEquals(
$expected,
$this->getClass()->buildSignature($request, $consumer, $token)
);
}
protected function getRequest()
@ -35,8 +41,11 @@ abstract class AbstractSignatureMethodTest extends \PHPUnit_Framework_TestCase
->getMock();
}
protected function getConsumer($key = null, $secret = null, $callbackUrl = null)
{
protected function getConsumer(
$key = null,
$secret = null,
$callbackUrl = null
) {
return $this->getMockBuilder('Abraham\TwitterOAuth\Consumer')
->setConstructorArgs([$key, $secret, $callbackUrl])
->getMock();

View File

@ -1,16 +1,23 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth\Tests;
use PHPUnit\Framework\TestCase;
use Abraham\TwitterOAuth\Consumer;
class ConsumerTest extends \PHPUnit_Framework_TestCase {
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());
$this->assertEquals(
"Consumer[key=$key,secret=$secret]",
$consumer->__toString()
);
}
}
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth\Tests;
use Abraham\TwitterOAuth\HmacSha1;
@ -16,20 +18,30 @@ class HmacSha1Test extends AbstractSignatureMethodTest
public function signatureDataProvider()
{
return [
['5CoEcoq7XoKFjwYCieQvuzadeUA=', $this->getRequest(), $this->getConsumer(), $this->getToken()],
[
'5CoEcoq7XoKFjwYCieQvuzadeUA=',
$this->getRequest(),
$this->getConsumer(),
$this->getToken(),
],
[
'EBw0gHngam3BTx8kfPfNNSyKem4=',
$this->getRequest(),
$this->getConsumer('key', 'secret'),
$this->getToken()
$this->getToken(),
],
[
'kDsHFZzws2a5M6cAQjfpdNBo+v8=',
$this->getRequest(),
$this->getConsumer('key', 'secret'),
$this->getToken('key', 'secret')
$this->getToken('key', 'secret'),
],
[
'EBw0gHngam3BTx8kfPfNNSyKem4=',
$this->getRequest(),
$this->getConsumer('key', 'secret'),
null,
],
['EBw0gHngam3BTx8kfPfNNSyKem4=', $this->getRequest(), $this->getConsumer('key', 'secret'), null],
];
}
}
}

View File

@ -1,10 +1,14 @@
<?php
declare(strict_types=1);
namespace Abraham\TwitterOAuth\Tests;
use PHPUnit\Framework\TestCase;
use Abraham\TwitterOAuth\Token;
class TokenTest extends \PHPUnit_Framework_TestCase {
class TokenTest extends TestCase
{
/**
* @dataProvider tokenProvider
*/
@ -19,8 +23,16 @@ class TokenTest extends \PHPUnit_Framework_TestCase {
{
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'],
[
'oauth_token=key%2Bkey&oauth_token_secret=secret',
'key+key',
'secret',
],
[
'oauth_token=key~key&oauth_token_secret=secret',
'key~key',
'secret',
],
];
}
}
}

View File

@ -1,19 +1,30 @@
<?php
/**
* WARNING: Running these tests will post and delete through the actual Twitter account.
* 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 \PHPUnit_Framework_TestCase
class TwitterOAuthTest extends TestCase
{
/** @var TwitterOAuth */
protected $twitter;
protected function setUp()
protected function setUp(): void
{
$this->twitter = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
$this->twitter = new TwitterOAuth(
CONSUMER_KEY,
CONSUMER_SECRET,
ACCESS_TOKEN,
ACCESS_TOKEN_SECRET
);
$this->userId = explode('-', ACCESS_TOKEN)[0];
}
public function testBuildClient()
@ -22,20 +33,30 @@ class TwitterOAuthTest extends \PHPUnit_Framework_TestCase
$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']);
$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']);
$result = $twitter->oauth2('oauth2/token', [
'grant_type' => 'client_credentials',
]);
$this->assertEquals(200, $twitter->getLastHttpCode());
$this->assertObjectHasAttribute('token_type', $result);
$this->assertObjectHasAttribute('access_token', $result);
@ -45,40 +66,47 @@ class TwitterOAuthTest extends \PHPUnit_Framework_TestCase
/**
* @depends testOauth2Token
* @vcr testOauth2BearerToken.json
*/
public function testBearerToken($accessToken)
public function testOauth2BearerToken($accessToken)
{
$twitter = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, null, $accessToken->access_token);
$result = $twitter->get('statuses/user_timeline', ['screen_name' => 'twitterapi']);
if ($twitter->getLastHttpCode() !== 200) {
$this->assertEquals('foo', substr($accessToken->access_token, 0, 75));
$this->assertEquals('foo', print_r($result, true));
}
$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;
}
// This causes issues for parallel run tests.
// /**
// * @depends testBearerToken
// */
// 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',
// array('access_token' => urldecode($accessToken->access_token))
// );
// $this->assertEquals(200, $twitter->getLastHttpCode());
// $this->assertObjectHasAttribute('access_token', $result);
// return $result;
// }
/**
* @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]);
$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);
@ -88,66 +116,95 @@ class TwitterOAuthTest extends \PHPUnit_Framework_TestCase
}
/**
* @expectedException \Abraham\TwitterOAuth\TwitterOAuthException
* @expectedExceptionMessage Could not authenticate you
* @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]);
return $result;
$result = $twitter->oauth('oauth/request_token', [
'oauth_callback' => OAUTH_CALLBACK,
]);
}
/**
* @expectedException \Abraham\TwitterOAuth\TwitterOAuthException
* @expectedExceptionMessage Invalid oauth_verifier parameter
* @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"]);
$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);
$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()
{
// Include entities boolean added to test parameter value cohearsion
$this->twitter->get('account/verify_credentials', ["include_entities" => false]);
$user = $this->twitter->get('account/verify_credentials', [
'include_entities' => false,
'include_email' => true,
]);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
$this->assertObjectHasAttribute('email', $user);
}
// BUG: testing is too unreliable for now
// public function testSetProxy()
// {
// $this->twitter->setProxy(array(
// '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 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']);
@ -157,27 +214,32 @@ class TwitterOAuthTest extends \PHPUnit_Framework_TestCase
/**
* @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->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']);
if ($this->twitter->getLastHttpCode() == 403) {
// Status already favorited
$this->assertEquals(139, $result->errors[0]->code);
} else {
$this->assertEquals(200, $this->twitter->getLastHttpCode());
}
$result = $this->twitter->post('favorites/create', [
'id' => '6242973112',
]);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
}
/**
* @depends testPostFavoritesCreate
* @vcr testPostFavoritesDestroy.json
*/
public function testPostFavoritesDestroy()
{
@ -185,49 +247,121 @@ class TwitterOAuthTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(200, $this->twitter->getLastHttpCode());
}
public function testPostStatusesUpdateWithMedia()
/**
* @vcr testPostDirectMessagesEventsNew.json
*/
public function testPostDirectMessagesEventsNew()
{
$this->twitter->setTimeouts(60, 30);
// Image source https://www.flickr.com/photos/titrans/8548825587/
$file_path = __DIR__ . '/kitten.jpg';
$result = $this->twitter->upload('media/upload', ['media' => $file_path]);
$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());
$this->assertObjectHasAttribute('media_id_string', $result);
$parameters = ['status' => 'Hello World ' . time(), 'media_ids' => $result->media_id_string];
$result = $this->twitter->post('statuses/update', $parameters);
$this->assertEquals(200, $this->twitter->getLastHttpCode());
if ($this->twitter->getLastHttpCode() == 200) {
$result = $this->twitter->post('statuses/destroy/' . $result->id_str);
}
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);
$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 ' . time(), 'media_ids' => $result->media_id_string];
$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());
if ($this->twitter->getLastHttpCode() == 200) {
$result = $this->twitter->post('statuses/destroy/' . $result->id_str);
}
$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こんにちは世界 ' . time()]);
$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)
{
@ -235,16 +369,23 @@ class TwitterOAuthTest extends \PHPUnit_Framework_TestCase
$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());
$this->assertObjectHasAttribute(
'statuses',
$this->twitter->getLastBody()
);
}
/**
* @depends testLastResult
* @vcr testResetLastResponse.json
*/
public function testResetLastResponse()
{

View File

@ -2,9 +2,10 @@
namespace Abraham\TwitterOAuth\Tests;
use PHPUnit\Framework\TestCase;
use Abraham\TwitterOAuth\Util\JsonDecoder;
class JsonDecoderTest extends \PHPUnit_Framework_TestCase
class JsonDecoderTest extends TestCase
{
/**
* @dataProvider jsonProvider
@ -19,7 +20,11 @@ class JsonDecoderTest extends \PHPUnit_Framework_TestCase
return [
['[]', true, []],
['[1,2,3]', true, [1, 2, 3]],
['[{"id": 556179961825226750}]', true, [['id' => 556179961825226750]]],
[
'[{"id": 556179961825226750}]',
true,
[['id' => 556179961825226750]],
],
['[]', false, []],
['[1,2,3]', false, [1, 2, 3]],
[
@ -29,10 +34,9 @@ class JsonDecoderTest extends \PHPUnit_Framework_TestCase
$this->getClass(function ($object) {
$object->id = 556179961825226750;
return $object;
})
]
}),
],
],
];
}

View File

@ -1,12 +1,10 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
declare(strict_types=1);
define('CONSUMER_KEY', getenv('TEST_CONSUMER_KEY'));
define('CONSUMER_SECRET', getenv('TEST_CONSUMER_SECRET'));
define('ACCESS_TOKEN', getenv('TEST_ACCESS_TOKEN'));
define('ACCESS_TOKEN_SECRET', getenv('TEST_ACCESS_TOKEN_SECRET'));
define('OAUTH_CALLBACK', getenv('TEST_OAUTH_CALLBACK'));
define('PROXY', getenv('TEST_CURLOPT_PROXY'));
define('PROXYUSERPWD', getenv('TEST_CURLOPT_PROXYUSERPWD'));
define('PROXYPORT', getenv('TEST_CURLOPT_PROXYPORT'));
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 @@
[]

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.

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 211 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

@ -1,13 +0,0 @@
# WARNING: Running the tests will perform live actions as the Twitter account.
# Set all values, move to `env`, run `source tests/env` and `phpunit` to start testing.
# To run the tests you must register Twitter application at https://app.twitter.com/.
export TEST_CONSUMER_KEY=
export TEST_CONSUMER_SECRET=
export TEST_ACCESS_TOKEN=
export TEST_ACCESS_TOKEN_SECRET=
export TEST_OAUTH_CALLBACK=
# You can find proxies for testing at http://proxylist.hidemyass.com/.
export TEST_CURLOPT_PROXY=
export TEST_CURLOPT_PROXYUSERPWD=
export TEST_CURLOPT_PROXYPORT=

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');

View File

@ -60,7 +60,7 @@ class ClassLoader
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
@ -279,7 +279,7 @@ class ClassLoader
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**

View File

@ -7,5 +7,6 @@ $baseDir = dirname($vendorDir);
return array(
'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src/Composer/Installers'),
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
'Abraham\\TwitterOAuth\\' => array($vendorDir . '/abraham/twitteroauth/src'),
);

View File

@ -13,6 +13,9 @@ class ComposerAutoloaderInitTwitterAddon
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {

View File

@ -10,6 +10,7 @@ class ComposerStaticInitTwitterAddon
'C' =>
array (
'Composer\\Installers\\' => 20,
'Composer\\CaBundle\\' => 18,
),
'A' =>
array (
@ -22,6 +23,10 @@ class ComposerStaticInitTwitterAddon
array (
0 => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers',
),
'Composer\\CaBundle\\' =>
array (
0 => __DIR__ . '/..' . '/composer/ca-bundle/src',
),
'Abraham\\TwitterOAuth\\' =>
array (
0 => __DIR__ . '/..' . '/abraham/twitteroauth/src',

View File

@ -0,0 +1,19 @@
Copyright (C) 2016 Composer
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,85 @@
composer/ca-bundle
==================
Small utility library that lets you find a path to the system CA bundle,
and includes a fallback to the Mozilla CA bundle.
Originally written as part of [composer/composer](https://github.com/composer/composer),
now extracted and made available as a stand-alone library.
Installation
------------
Install the latest version with:
```bash
$ composer require composer/ca-bundle
```
Requirements
------------
* PHP 5.3.2 is required but using the latest version of PHP is highly recommended.
Basic usage
-----------
### `Composer\CaBundle\CaBundle`
- `CaBundle::getSystemCaRootBundlePath()`: Returns the system CA bundle path, or a path to the bundled one as fallback
- `CaBundle::getBundledCaBundlePath()`: Returns the path to the bundled CA file
- `CaBundle::validateCaFile($filename)`: Validates a CA file using openssl_x509_parse only if it is safe to use
- `CaBundle::isOpensslParseSafe()`: Test if it is safe to use the PHP function openssl_x509_parse()
- `CaBundle::reset()`: Resets the static caches
#### To use with curl
```php
$curl = curl_init("https://example.org/");
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
if (is_dir($caPathOrFile)) {
curl_setopt($curl, CURLOPT_CAPATH, $caPathOrFile);
} else {
curl_setopt($curl, CURLOPT_CAINFO, $caPathOrFile);
}
$result = curl_exec($curl);
```
#### To use with php streams
```php
$opts = array(
'http' => array(
'method' => "GET"
)
);
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
if (is_dir($caPathOrFile)) {
$opts['ssl']['capath'] = $caPathOrFile;
} else {
$opts['ssl']['cafile'] = $caPathOrFile;
}
$context = stream_context_create($opts);
$result = file_get_contents('https://example.com', false, $context);
```
#### To use with Guzzle
```php
$client = new \GuzzleHttp\Client([
\GuzzleHttp\RequestOptions::VERIFY => \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath()
]);
```
License
-------
composer/ca-bundle is licensed under the MIT License, see the LICENSE file for details.

View File

@ -0,0 +1,54 @@
{
"name": "composer/ca-bundle",
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
"type": "library",
"license": "MIT",
"keywords": [
"cabundle",
"cacert",
"certificate",
"ssl",
"tls"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues"
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.2 || ^5",
"phpstan/phpstan": "^0.12.55",
"psr/log": "^1.0",
"symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
},
"autoload": {
"psr-4": {
"Composer\\CaBundle\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Composer\\CaBundle\\": "tests"
}
},
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"scripts": {
"test": "SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 vendor/bin/simple-phpunit",
"phpstan": "vendor/bin/phpstan analyse"
}
}

View File

@ -0,0 +1,353 @@
<?php
/*
* This file is part of composer/ca-bundle.
*
* (c) Composer <https://github.com/composer>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Composer\CaBundle;
use Psr\Log\LoggerInterface;
use Symfony\Component\Process\PhpProcess;
/**
* @author Chris Smith <chris@cs278.org>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class CaBundle
{
/** @var string|null */
private static $caPath;
/** @var array<string, bool> */
private static $caFileValidity = array();
/** @var bool|null */
private static $useOpensslParse;
/**
* Returns the system CA bundle path, or a path to the bundled one
*
* This method was adapted from Sslurp.
* https://github.com/EvanDotPro/Sslurp
*
* (c) Evan Coury <me@evancoury.com>
*
* For the full copyright and license information, please see below:
*
* Copyright (c) 2013, Evan Coury
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
* @return string path to a CA bundle file or directory
*/
public static function getSystemCaRootBundlePath(LoggerInterface $logger = null)
{
if (self::$caPath !== null) {
return self::$caPath;
}
$caBundlePaths = array();
// If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_FILE');
// If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_DIR');
$caBundlePaths[] = ini_get('openssl.cafile');
$caBundlePaths[] = ini_get('openssl.capath');
$otherLocations = array(
'/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package)
'/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
'/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package)
'/usr/local/share/certs/ca-root-nss.crt', // FreeBSD (ca_root_nss_package)
'/usr/ssl/certs/ca-bundle.crt', // Cygwin
'/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package
'/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option)
'/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat?
'/etc/ssl/cert.pem', // OpenBSD
'/usr/local/etc/ssl/cert.pem', // FreeBSD 10.x
'/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package
'/usr/local/etc/openssl@1.1/cert.pem', // OS X homebrew, openssl@1.1 package
);
foreach($otherLocations as $location) {
$otherLocations[] = dirname($location);
}
$caBundlePaths = array_merge($caBundlePaths, $otherLocations);
foreach ($caBundlePaths as $caBundle) {
if ($caBundle && self::caFileUsable($caBundle, $logger)) {
return self::$caPath = $caBundle;
}
if ($caBundle && self::caDirUsable($caBundle)) {
return self::$caPath = $caBundle;
}
}
return self::$caPath = static::getBundledCaBundlePath(); // Bundled CA file, last resort
}
/**
* Returns the path to the bundled CA file
*
* In case you don't want to trust the user or the system, you can use this directly
*
* @return string path to a CA bundle file
*/
public static function getBundledCaBundlePath()
{
$caBundleFile = __DIR__.'/../res/cacert.pem';
// cURL does not understand 'phar://' paths
// see https://github.com/composer/ca-bundle/issues/10
if (0 === strpos($caBundleFile, 'phar://')) {
$tempCaBundleFile = tempnam(sys_get_temp_dir(), 'openssl-ca-bundle-');
if (false === $tempCaBundleFile) {
throw new \RuntimeException('Could not create a temporary file to store the bundled CA file');
}
file_put_contents(
$tempCaBundleFile,
file_get_contents($caBundleFile)
);
register_shutdown_function(function() use ($tempCaBundleFile) {
@unlink($tempCaBundleFile);
});
$caBundleFile = $tempCaBundleFile;
}
return $caBundleFile;
}
/**
* Validates a CA file using opensl_x509_parse only if it is safe to use
*
* @param string $filename
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
*
* @return bool
*/
public static function validateCaFile($filename, LoggerInterface $logger = null)
{
static $warned = false;
if (isset(self::$caFileValidity[$filename])) {
return self::$caFileValidity[$filename];
}
$contents = file_get_contents($filename);
// assume the CA is valid if php is vulnerable to
// https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
if (!static::isOpensslParseSafe()) {
if (!$warned && $logger) {
$logger->warning(sprintf(
'Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.',
PHP_VERSION
));
$warned = true;
}
$isValid = !empty($contents);
} elseif (is_string($contents) && strlen($contents) > 0) {
$contents = preg_replace("/^(\\-+(?:BEGIN|END))\\s+TRUSTED\\s+(CERTIFICATE\\-+)\$/m", '$1 $2', $contents);
if (null === $contents) {
// regex extraction failed
$isValid = false;
} else {
$isValid = (bool) openssl_x509_parse($contents);
}
} else {
$isValid = false;
}
if ($logger) {
$logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid'));
}
return self::$caFileValidity[$filename] = $isValid;
}
/**
* Test if it is safe to use the PHP function openssl_x509_parse().
*
* This checks if OpenSSL extensions is vulnerable to remote code execution
* via the exploit documented as CVE-2013-6420.
*
* @return bool
*/
public static function isOpensslParseSafe()
{
if (null !== self::$useOpensslParse) {
return self::$useOpensslParse;
}
if (PHP_VERSION_ID >= 50600) {
return self::$useOpensslParse = true;
}
// Vulnerable:
// PHP 5.3.0 - PHP 5.3.27
// PHP 5.4.0 - PHP 5.4.22
// PHP 5.5.0 - PHP 5.5.6
if (
(PHP_VERSION_ID < 50400 && PHP_VERSION_ID >= 50328)
|| (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50423)
|| PHP_VERSION_ID >= 50507
) {
// This version of PHP has the fix for CVE-2013-6420 applied.
return self::$useOpensslParse = true;
}
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
// Windows is probably insecure in this case.
return self::$useOpensslParse = false;
}
$compareDistroVersionPrefix = function ($prefix, $fixedVersion) {
$regex = '{^'.preg_quote($prefix).'([0-9]+)$}';
if (preg_match($regex, PHP_VERSION, $m)) {
return ((int) $m[1]) >= $fixedVersion;
}
return false;
};
// Hard coded list of PHP distributions with the fix backported.
if (
$compareDistroVersionPrefix('5.3.3-7+squeeze', 18) // Debian 6 (Squeeze)
|| $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) // Debian 7 (Wheezy)
|| $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9) // Ubuntu 12.04 (Precise)
) {
return self::$useOpensslParse = true;
}
// Symfony Process component is missing so we assume it is unsafe at this point
if (!class_exists('Symfony\Component\Process\PhpProcess')) {
return self::$useOpensslParse = false;
}
// This is where things get crazy, because distros backport security
// fixes the chances are on NIX systems the fix has been applied but
// it's not possible to verify that from the PHP version.
//
// To verify exec a new PHP process and run the issue testcase with
// known safe input that replicates the bug.
// Based on testcase in https://github.com/php/php-src/commit/c1224573c773b6845e83505f717fbf820fc18415
// changes in https://github.com/php/php-src/commit/76a7fd893b7d6101300cc656058704a73254d593
$cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K';
$script = <<<'EOT'
error_reporting(-1);
$info = openssl_x509_parse(base64_decode('%s'));
var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']);
EOT;
$script = '<'."?php\n".sprintf($script, $cert);
try {
$process = new PhpProcess($script);
$process->mustRun();
} catch (\Exception $e) {
// In the case of any exceptions just accept it is not possible to
// determine the safety of openssl_x509_parse and bail out.
return self::$useOpensslParse = false;
}
$output = preg_split('{\r?\n}', trim($process->getOutput()));
$errorOutput = trim($process->getErrorOutput());
if (
is_array($output)
&& count($output) === 3
&& $output[0] === sprintf('string(%d) "%s"', strlen(PHP_VERSION), PHP_VERSION)
&& $output[1] === 'string(27) "stefan.esser@sektioneins.de"'
&& $output[2] === 'int(-1)'
&& preg_match('{openssl_x509_parse\(\): illegal (?:ASN1 data type for|length in) timestamp in - on line \d+}', $errorOutput)
) {
// This PHP has the fix backported probably by a distro security team.
return self::$useOpensslParse = true;
}
return self::$useOpensslParse = false;
}
/**
* Resets the static caches
* @return void
*/
public static function reset()
{
self::$caFileValidity = array();
self::$caPath = null;
self::$useOpensslParse = null;
}
/**
* @param string $name
* @return string|false
*/
private static function getEnvVariable($name)
{
if (isset($_SERVER[$name])) {
return (string) $_SERVER[$name];
}
if (PHP_SAPI === 'cli' && ($value = getenv($name)) !== false && $value !== null) {
return (string) $value;
}
return false;
}
/**
* @param string|false $certFile
* @return bool
*/
private static function caFileUsable($certFile, LoggerInterface $logger = null)
{
return $certFile && @is_file($certFile) && @is_readable($certFile) && static::validateCaFile($certFile, $logger);
}
/**
* @param string|false $certDir
* @return bool
*/
private static function caDirUsable($certDir)
{
return $certDir && @is_dir($certDir) && @is_readable($certDir) && glob($certDir . '/*');
}
}

View File

@ -1,29 +1,32 @@
[
{
"name": "abraham/twitteroauth",
"version": "0.7.4",
"version_normalized": "0.7.4.0",
"version": "2.0.0",
"version_normalized": "2.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/abraham/twitteroauth.git",
"reference": "c6f9e692552dd037b2324ed0dfa28a4e60875acf"
"reference": "96f49e67baec10f5e5cb703d87be16ba01a798a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abraham/twitteroauth/zipball/c6f9e692552dd037b2324ed0dfa28a4e60875acf",
"reference": "c6f9e692552dd037b2324ed0dfa28a4e60875acf",
"url": "https://api.github.com/repos/abraham/twitteroauth/zipball/96f49e67baec10f5e5cb703d87be16ba01a798a5",
"reference": "96f49e67baec10f5e5cb703d87be16ba01a798a5",
"shasum": ""
},
"require": {
"composer/ca-bundle": "^1.2",
"ext-curl": "*",
"php": "^5.6 || ^7.0"
"php": "^7.2 || ^7.3 || ^7.4 || ^8.0"
},
"require-dev": {
"phpmd/phpmd": "~2.6",
"phpunit/phpunit": "~5.7",
"squizlabs/php_codesniffer": "~3.0"
"php-vcr/php-vcr": "^1",
"php-vcr/phpunit-testlistener-vcr": "dev-php-8",
"phpmd/phpmd": "^2",
"phpunit/phpunit": "^8",
"squizlabs/php_codesniffer": "^3"
},
"time": "2017-06-30T22:02:01+00:00",
"time": "2020-12-02T01:27:06+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -55,6 +58,79 @@
"twitter"
]
},
{
"name": "composer/ca-bundle",
"version": "1.2.10",
"version_normalized": "1.2.10.0",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "9fdb22c2e97a614657716178093cd1da90a64aa8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/9fdb22c2e97a614657716178093cd1da90a64aa8",
"reference": "9fdb22c2e97a614657716178093cd1da90a64aa8",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^0.12.55",
"psr/log": "^1.0",
"symfony/phpunit-bridge": "^4.2 || ^5",
"symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0"
},
"time": "2021-06-07T13:58:28+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Composer\\CaBundle\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
"keywords": [
"cabundle",
"cacert",
"certificate",
"ssl",
"tls"
],
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
]
},
{
"name": "composer/installers",
"version": "v1.6.0",
@ -183,12 +259,12 @@
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/jublonet/codebird-php.git",
"url": "https://github.com/jublo/codebird-php.git",
"reference": "df362d8ad629aad6c4c7dbf36a440e569ec41368"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jublonet/codebird-php/zipball/df362d8ad629aad6c4c7dbf36a440e569ec41368",
"url": "https://api.github.com/repos/jublo/codebird-php/zipball/df362d8ad629aad6c4c7dbf36a440e569ec41368",
"reference": "df362d8ad629aad6c4c7dbf36a440e569ec41368",
"shasum": ""
},

View File

@ -0,0 +1,19 @@
Copyright (c) 2012 Kyle Robinson Young
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,105 @@
{
"name": "composer/installers",
"type": "composer-plugin",
"license": "MIT",
"description": "A multi-framework Composer library installer",
"keywords": [
"installer",
"Aimeos",
"AGL",
"AnnotateCms",
"Attogram",
"Bitrix",
"CakePHP",
"Chef",
"Cockpit",
"CodeIgniter",
"concrete5",
"Craft",
"Croogo",
"DokuWiki",
"Dolibarr",
"Drupal",
"Elgg",
"Eliasis",
"ExpressionEngine",
"eZ Platform",
"FuelPHP",
"Grav",
"Hurad",
"ImageCMS",
"iTop",
"Joomla",
"Kanboard",
"Kohana",
"Lan Management System",
"Laravel",
"Lavalite",
"Lithium",
"Magento",
"majima",
"Mako",
"Mautic",
"Maya",
"MODX",
"MODX Evo",
"MediaWiki",
"OXID",
"osclass",
"MODULEWork",
"Moodle",
"Piwik",
"pxcms",
"phpBB",
"Plentymarkets",
"PPI",
"Puppet",
"Porto",
"RadPHP",
"ReIndex",
"Roundcube",
"shopware",
"SilverStripe",
"SMF",
"SyDES",
"symfony",
"Thelia",
"TYPO3",
"WolfCMS",
"WordPress",
"YAWIK",
"Zend",
"Zikula"
],
"homepage": "https://composer.github.io/installers/",
"authors": [
{
"name": "Kyle Robinson Young",
"email": "kyle@dontkry.com",
"homepage": "https://github.com/shama"
}
],
"autoload": {
"psr-4": { "Composer\\Installers\\": "src/Composer/Installers" }
},
"extra": {
"class": "Composer\\Installers\\Plugin",
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"replace": {
"shama/baton": "*",
"roundcube/plugin-installer": "*"
},
"require": {
"composer-plugin-api": "^1.0"
},
"require-dev": {
"composer/composer": "1.0.*@dev",
"phpunit/phpunit": "^4.8.36"
},
"scripts": {
"test": "phpunit"
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Composer\Installers;
class AglInstaller extends BaseInstaller
{
protected $locations = array(
'module' => 'More/{$name}/',
);
/**
* Format package name to CamelCase
*/
public function inflectPackageVars($vars)
{
$vars['name'] = preg_replace_callback('/(?:^|_|-)(.?)/', function ($matches) {
return strtoupper($matches[1]);
}, $vars['name']);
return $vars;
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace Composer\Installers;
class AimeosInstaller extends BaseInstaller
{
protected $locations = array(
'extension' => 'ext/{$name}/',
);
}

View File

@ -0,0 +1,11 @@
<?php
namespace Composer\Installers;
class AnnotateCmsInstaller extends BaseInstaller
{
protected $locations = array(
'module' => 'addons/modules/{$name}/',
'component' => 'addons/components/{$name}/',
'service' => 'addons/services/{$name}/',
);
}

View File

@ -0,0 +1,49 @@
<?php
namespace Composer\Installers;
class AsgardInstaller extends BaseInstaller
{
protected $locations = array(
'module' => 'Modules/{$name}/',
'theme' => 'Themes/{$name}/'
);
/**
* Format package name.
*
* For package type asgard-module, cut off a trailing '-plugin' if present.
*
* For package type asgard-theme, cut off a trailing '-theme' if present.
*
*/
public function inflectPackageVars($vars)
{
if ($vars['type'] === 'asgard-module') {
return $this->inflectPluginVars($vars);
}
if ($vars['type'] === 'asgard-theme') {
return $this->inflectThemeVars($vars);
}
return $vars;
}
protected function inflectPluginVars($vars)
{
$vars['name'] = preg_replace('/-module$/', '', $vars['name']);
$vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
$vars['name'] = str_replace(' ', '', ucwords($vars['name']));
return $vars;
}
protected function inflectThemeVars($vars)
{
$vars['name'] = preg_replace('/-theme$/', '', $vars['name']);
$vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']);
$vars['name'] = str_replace(' ', '', ucwords($vars['name']));
return $vars;
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace Composer\Installers;
class AttogramInstaller extends BaseInstaller
{
protected $locations = array(
'module' => 'modules/{$name}/',
);
}

View File

@ -0,0 +1,136 @@
<?php
namespace Composer\Installers;
use Composer\IO\IOInterface;
use Composer\Composer;
use Composer\Package\PackageInterface;
abstract class BaseInstaller
{
protected $locations = array();
protected $composer;
protected $package;
protected $io;
/**
* Initializes base installer.
*
* @param PackageInterface $package
* @param Composer $composer
* @param IOInterface $io
*/
public function __construct(PackageInterface $package = null, Composer $composer = null, IOInterface $io = null)
{
$this->composer = $composer;
$this->package = $package;
$this->io = $io;
}
/**
* Return the install path based on package type.
*
* @param PackageInterface $package
* @param string $frameworkType
* @return string
*/
public function getInstallPath(PackageInterface $package, $frameworkType = '')
{
$type = $this->package->getType();
$prettyName = $this->package->getPrettyName();
if (strpos($prettyName, '/') !== false) {
list($vendor, $name) = explode('/', $prettyName);
} else {
$vendor = '';
$name = $prettyName;
}
$availableVars = $this->inflectPackageVars(compact('name', 'vendor', 'type'));
$extra = $package->getExtra();
if (!empty($extra['installer-name'])) {
$availableVars['name'] = $extra['installer-name'];
}
if ($this->composer->getPackage()) {
$extra = $this->composer->getPackage()->getExtra();
if (!empty($extra['installer-paths'])) {
$customPath = $this->mapCustomInstallPaths($extra['installer-paths'], $prettyName, $type, $vendor);
if ($customPath !== false) {
return $this->templatePath($customPath, $availableVars);
}
}
}
$packageType = substr($type, strlen($frameworkType) + 1);
$locations = $this->getLocations();
if (!isset($locations[$packageType])) {
throw new \InvalidArgumentException(sprintf('Package type "%s" is not supported', $type));
}
return $this->templatePath($locations[$packageType], $availableVars);
}
/**
* For an installer to override to modify the vars per installer.
*
* @param array $vars
* @return array
*/
public function inflectPackageVars($vars)
{
return $vars;
}
/**
* Gets the installer's locations
*
* @return array
*/
public function getLocations()
{
return $this->locations;
}
/**
* Replace vars in a path
*
* @param string $path
* @param array $vars
* @return string
*/
protected function templatePath($path, array $vars = array())
{
if (strpos($path, '{') !== false) {
extract($vars);
preg_match_all('@\{\$([A-Za-z0-9_]*)\}@i', $path, $matches);
if (!empty($matches[1])) {
foreach ($matches[1] as $var) {
$path = str_replace('{$' . $var . '}', $$var, $path);
}
}
}
return $path;
}
/**
* Search through a passed paths array for a custom install path.
*
* @param array $paths
* @param string $name
* @param string $type
* @param string $vendor = NULL
* @return string
*/
protected function mapCustomInstallPaths(array $paths, $name, $type, $vendor = NULL)
{
foreach ($paths as $path => $names) {
if (in_array($name, $names) || in_array('type:' . $type, $names) || in_array('vendor:' . $vendor, $names)) {
return $path;
}
}
return false;
}
}

View File

@ -0,0 +1,126 @@
<?php
namespace Composer\Installers;
use Composer\Util\Filesystem;
/**
* Installer for Bitrix Framework. Supported types of extensions:
* - `bitrix-d7-module` copy the module to directory `bitrix/modules/<vendor>.<name>`.
* - `bitrix-d7-component` copy the component to directory `bitrix/components/<vendor>/<name>`.
* - `bitrix-d7-template` copy the template to directory `bitrix/templates/<vendor>_<name>`.
*
* You can set custom path to directory with Bitrix kernel in `composer.json`:
*
* ```json
* {
* "extra": {
* "bitrix-dir": "s1/bitrix"
* }
* }
* ```
*
* @author Nik Samokhvalov <nik@samokhvalov.info>
* @author Denis Kulichkin <onexhovia@gmail.com>
*/
class BitrixInstaller extends BaseInstaller
{
protected $locations = array(
'module' => '{$bitrix_dir}/modules/{$name}/', // deprecated, remove on the major release (Backward compatibility will be broken)
'component' => '{$bitrix_dir}/components/{$name}/', // deprecated, remove on the major release (Backward compatibility will be broken)
'theme' => '{$bitrix_dir}/templates/{$name}/', // deprecated, remove on the major release (Backward compatibility will be broken)
'd7-module' => '{$bitrix_dir}/modules/{$vendor}.{$name}/',
'd7-component' => '{$bitrix_dir}/components/{$vendor}/{$name}/',
'd7-template' => '{$bitrix_dir}/templates/{$vendor}_{$name}/',
);
/**
* @var array Storage for informations about duplicates at all the time of installation packages.
*/
private static $checkedDuplicates = array();
/**
* {@inheritdoc}
*/
public function inflectPackageVars($vars)
{
if ($this->composer->getPackage()) {
$extra = $this->composer->getPackage()->getExtra();
if (isset($extra['bitrix-dir'])) {
$vars['bitrix_dir'] = $extra['bitrix-dir'];
}
}
if (!isset($vars['bitrix_dir'])) {
$vars['bitrix_dir'] = 'bitrix';
}
return parent::inflectPackageVars($vars);
}
/**
* {@inheritdoc}
*/
protected function templatePath($path, array $vars = array())
{
$templatePath = parent::templatePath($path, $vars);
$this->checkDuplicates($templatePath, $vars);
return $templatePath;
}
/**
* Duplicates search packages.
*
* @param string $path
* @param array $vars
*/
protected function checkDuplicates($path, array $vars = array())
{
$packageType = substr($vars['type'], strlen('bitrix') + 1);
$localDir = explode('/', $vars['bitrix_dir']);
array_pop($localDir);
$localDir[] = 'local';
$localDir = implode('/', $localDir);
$oldPath = str_replace(
array('{$bitrix_dir}', '{$name}'),
array($localDir, $vars['name']),
$this->locations[$packageType]
);
if (in_array($oldPath, static::$checkedDuplicates)) {
return;
}
if ($oldPath !== $path && file_exists($oldPath) && $this->io && $this->io->isInteractive()) {
$this->io->writeError(' <error>Duplication of packages:</error>');
$this->io->writeError(' <info>Package ' . $oldPath . ' will be called instead package ' . $path . '</info>');
while (true) {
switch ($this->io->ask(' <info>Delete ' . $oldPath . ' [y,n,?]?</info> ', '?')) {
case 'y':
$fs = new Filesystem();
$fs->removeDirectory($oldPath);
break 2;
case 'n':
break 2;
case '?':
default:
$this->io->writeError(array(
' y - delete package ' . $oldPath . ' and to continue with the installation',
' n - don\'t delete and to continue with the installation',
));
$this->io->writeError(' ? - print help');
break;
}
}
}
static::$checkedDuplicates[] = $oldPath;
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace Composer\Installers;
class BonefishInstaller extends BaseInstaller
{
protected $locations = array(
'package' => 'Packages/{$vendor}/{$name}/'
);
}

View File

@ -0,0 +1,82 @@
<?php
namespace Composer\Installers;
use Composer\DependencyResolver\Pool;
class CakePHPInstaller extends BaseInstaller
{
protected $locations = array(
'plugin' => 'Plugin/{$name}/',
);
/**
* Format package name to CamelCase
*/
public function inflectPackageVars($vars)
{
if ($this->matchesCakeVersion('>=', '3.0.0')) {
return $vars;
}
$nameParts = explode('/', $vars['name']);
foreach ($nameParts as &$value) {
$value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value));
$value = str_replace(array('-', '_'), ' ', $value);
$value = str_replace(' ', '', ucwords($value));
}
$vars['name'] = implode('/', $nameParts);
return $vars;
}
/**
* Change the default plugin location when cakephp >= 3.0
*/
public function getLocations()
{
if ($this->matchesCakeVersion('>=', '3.0.0')) {
$this->locations['plugin'] = $this->composer->getConfig()->get('vendor-dir') . '/{$vendor}/{$name}/';
}
return $this->locations;
}
/**
* Check if CakePHP version matches against a version
*
* @param string $matcher
* @param string $version
* @return bool
*/
protected function matchesCakeVersion($matcher, $version)
{
if (class_exists('Composer\Semver\Constraint\MultiConstraint')) {
$multiClass = 'Composer\Semver\Constraint\MultiConstraint';
$constraintClass = 'Composer\Semver\Constraint\Constraint';
} else {
$multiClass = 'Composer\Package\LinkConstraint\MultiConstraint';
$constraintClass = 'Composer\Package\LinkConstraint\VersionConstraint';
}
$repositoryManager = $this->composer->getRepositoryManager();
if ($repositoryManager) {
$repos = $repositoryManager->getLocalRepository();
if (!$repos) {
return false;
}
$cake3 = new $multiClass(array(
new $constraintClass($matcher, $version),
new $constraintClass('!=', '9999999-dev'),
));
$pool = new Pool('dev');
$pool->addRepository($repos);
$packages = $pool->whatProvides('cakephp/cakephp');
foreach ($packages as $package) {
$installed = new $constraintClass('=', $package->getVersion());
if ($cake3->matches($installed)) {
return true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace Composer\Installers;
class ChefInstaller extends BaseInstaller
{
protected $locations = array(
'cookbook' => 'Chef/{$vendor}/{$name}/',
'role' => 'Chef/roles/{$name}/',
);
}

View File

@ -0,0 +1,9 @@
<?php
namespace Composer\Installers;
class CiviCrmInstaller extends BaseInstaller
{
protected $locations = array(
'ext' => 'ext/{$name}/'
);
}

View File

@ -0,0 +1,10 @@
<?php
namespace Composer\Installers;
class ClanCatsFrameworkInstaller extends BaseInstaller
{
protected $locations = array(
'ship' => 'CCF/orbit/{$name}/',
'theme' => 'CCF/app/themes/{$name}/',
);
}

View File

@ -0,0 +1,34 @@
<?php
namespace Composer\Installers;
class CockpitInstaller extends BaseInstaller
{
protected $locations = array(
'module' => 'cockpit/modules/addons/{$name}/',
);
/**
* Format module name.
*
* Strip `module-` prefix from package name.
*
* @param array @vars
*
* @return array
*/
public function inflectPackageVars($vars)
{
if ($vars['type'] == 'cockpit-module') {
return $this->inflectModuleVars($vars);
}
return $vars;
}
public function inflectModuleVars($vars)
{
$vars['name'] = ucfirst(preg_replace('/cockpit-/i', '', $vars['name']));
return $vars;
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace Composer\Installers;
class CodeIgniterInstaller extends BaseInstaller
{
protected $locations = array(
'library' => 'application/libraries/{$name}/',
'third-party' => 'application/third_party/{$name}/',
'module' => 'application/modules/{$name}/',
);
}

View File

@ -0,0 +1,13 @@
<?php
namespace Composer\Installers;
class Concrete5Installer extends BaseInstaller
{
protected $locations = array(
'core' => 'concrete/',
'block' => 'application/blocks/{$name}/',
'package' => 'packages/{$name}/',
'theme' => 'application/themes/{$name}/',
'update' => 'updates/{$name}/',
);
}

View File

@ -0,0 +1,35 @@
<?php
namespace Composer\Installers;
/**
* Installer for Craft Plugins
*/
class CraftInstaller extends BaseInstaller
{
const NAME_PREFIX = 'craft';
const NAME_SUFFIX = 'plugin';
protected $locations = array(
'plugin' => 'craft/plugins/{$name}/',
);
/**
* Strip `craft-` prefix and/or `-plugin` suffix from package names
*
* @param array $vars
*
* @return array
*/
final public function inflectPackageVars($vars)
{
return $this->inflectPluginVars($vars);
}
private function inflectPluginVars($vars)
{
$vars['name'] = preg_replace('/-' . self::NAME_SUFFIX . '$/i', '', $vars['name']);
$vars['name'] = preg_replace('/^' . self::NAME_PREFIX . '-/i', '', $vars['name']);
return $vars;
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Composer\Installers;
class CroogoInstaller extends BaseInstaller
{
protected $locations = array(
'plugin' => 'Plugin/{$name}/',
'theme' => 'View/Themed/{$name}/',
);
/**
* Format package name to CamelCase
*/
public function inflectPackageVars($vars)
{
$vars['name'] = strtolower(str_replace(array('-', '_'), ' ', $vars['name']));
$vars['name'] = str_replace(' ', '', ucwords($vars['name']));
return $vars;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Composer\Installers;
class DecibelInstaller extends BaseInstaller
{
/** @var array */
protected $locations = array(
'app' => 'app/{$name}/',
);
}

Some files were not shown because too many files have changed in this diff Show More