Updated OAuth2 to use base site URL instead of API subdomain and switched to Guzzle.
This commit is contained in:
parent
2d6c135fad
commit
d330e07925
29 changed files with 860 additions and 1198 deletions
2
LICENCE
2
LICENCE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2024, flashwave <me@flash.moe>
|
||||
Copyright (c) 2024-2025, flashwave <me@flash.moe>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.3.0
|
||||
0.4.0
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
"homepage": "https://api.flashii.net",
|
||||
"license": "bsd-3-clause-clear",
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
"php": ">=8.1",
|
||||
"psr/http-client": "^1.0",
|
||||
"guzzlehttp/guzzle": "~7.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"phpstan/phpstan": "^1.12"
|
||||
"phpunit/phpunit": "~10.5",
|
||||
"phpstan/phpstan": "~2.1"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
|
|
661
composer.lock
generated
661
composer.lock
generated
|
@ -4,21 +4,618 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "5e3fedd4bbe38b200656788e9a11f058",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
"content-hash": "8fb12a81006adf6cb1a3b217ba677763",
|
||||
"packages": [
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.12.1",
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "7.9.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "d281ed313b989f213357e3be1a179f02196ac99b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
|
||||
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
|
||||
"reference": "d281ed313b989f213357e3be1a179f02196ac99b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.5.3 || ^2.0.3",
|
||||
"guzzlehttp/psr7": "^2.7.0",
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-client-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"ext-curl": "*",
|
||||
"guzzle/client-integration-tests": "3.0.2",
|
||||
"php-http/message-factory": "^1.1",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Required for CURL handler support",
|
||||
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
|
||||
"psr/log": "Required for using the Log middleware"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Jeremy Lindblom",
|
||||
"email": "jeremeamia@gmail.com",
|
||||
"homepage": "https://github.com/jeremeamia"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle is a PHP HTTP client library",
|
||||
"keywords": [
|
||||
"client",
|
||||
"curl",
|
||||
"framework",
|
||||
"http",
|
||||
"http client",
|
||||
"psr-18",
|
||||
"psr-7",
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||
"source": "https://github.com/guzzle/guzzle/tree/7.9.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-24T11:22:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "2.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
|
||||
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle promises library",
|
||||
"keywords": [
|
||||
"promise"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/promises/issues",
|
||||
"source": "https://github.com/guzzle/promises/tree/2.0.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-17T10:06:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
|
||||
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 || ^2.0",
|
||||
"ralouphie/getallheaders": "^3.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"http-interop/http-factory-tests": "0.9.0",
|
||||
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||
},
|
||||
"suggest": {
|
||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Psr7\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://sagikazarmark.hu"
|
||||
}
|
||||
],
|
||||
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||
"keywords": [
|
||||
"http",
|
||||
"message",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response",
|
||||
"stream",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-18T11:15:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
"version": "1.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-client.git",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP clients",
|
||||
"homepage": "https://github.com/php-fig/http-client",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-client",
|
||||
"psr",
|
||||
"psr-18"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-client"
|
||||
},
|
||||
"time": "2023-09-23T14:17:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-factory"
|
||||
},
|
||||
"time": "2024-04-15T12:06:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/2.0"
|
||||
},
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ralouphie/getallheaders",
|
||||
"version": "3.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ralouphie/getallheaders.git",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
|
||||
"reference": "120b605dfeb996808c31b6477290a714d356e822",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.1",
|
||||
"phpunit/phpunit": "^5 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/getallheaders.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ralph Khattar",
|
||||
"email": "ralph.khattar@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A polyfill for getallheaders.",
|
||||
"support": {
|
||||
"issues": "https://github.com/ralouphie/getallheaders/issues",
|
||||
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
|
||||
},
|
||||
"time": "2019-03-08T08:55:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
|
||||
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/contracts",
|
||||
"name": "symfony/contracts"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "3.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"function.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-25T14:20:29+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "024473a478be9df5fdaca2c793f2232fe788e414"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
|
||||
"reference": "024473a478be9df5fdaca2c793f2232fe788e414",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -57,7 +654,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -65,20 +662,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-08T17:47:46+00:00"
|
||||
"time": "2025-02-12T12:17:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.3.1",
|
||||
"version": "v5.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b"
|
||||
"reference": "447a020a1f875a434d62f2a401f53b82a396e494"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b",
|
||||
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494",
|
||||
"reference": "447a020a1f875a434d62f2a401f53b82a396e494",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -121,9 +718,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0"
|
||||
},
|
||||
"time": "2024-10-08T18:51:32+00:00"
|
||||
"time": "2024-12-30T11:07:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
|
@ -245,20 +842,20 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.12.11",
|
||||
"version": "2.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733"
|
||||
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0d1fc20a962a91be578bcfe7cf939e6e1a2ff733",
|
||||
"reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f",
|
||||
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2|^8.0"
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan-shim": "*"
|
||||
|
@ -299,7 +896,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-17T14:08:01+00:00"
|
||||
"time": "2025-03-09T09:30:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
@ -624,16 +1221,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "10.5.38",
|
||||
"version": "10.5.45",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132"
|
||||
"reference": "bd68a781d8e30348bc297449f5234b3458267ae8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132",
|
||||
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8",
|
||||
"reference": "bd68a781d8e30348bc297449f5234b3458267ae8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -643,7 +1240,7 @@
|
|||
"ext-mbstring": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"myclabs/deep-copy": "^1.12.0",
|
||||
"myclabs/deep-copy": "^1.12.1",
|
||||
"phar-io/manifest": "^2.0.4",
|
||||
"phar-io/version": "^3.2.1",
|
||||
"php": ">=8.1",
|
||||
|
@ -705,7 +1302,7 @@
|
|||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -721,7 +1318,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-28T13:06:21+00:00"
|
||||
"time": "2025-02-06T16:08:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
@ -1692,12 +2289,12 @@
|
|||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@ parameters:
|
|||
checkBenevolentUnionTypes: true
|
||||
paths:
|
||||
- src
|
||||
- tests
|
||||
#- tests
|
||||
|
|
|
@ -1,35 +1,34 @@
|
|||
<?php declare(strict_types=1);
|
||||
// AuthorizedHttpClient.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
// Updated: 2025-03-20
|
||||
|
||||
namespace Flashii;
|
||||
|
||||
use Flashii\Credentials\Credentials;
|
||||
use Flashii\Http\{HttpClient,HttpRequest,HttpResponse,RequestSpeedrun};
|
||||
use Psr\Http\Client\{ClientExceptionInterface,ClientInterface};
|
||||
use Psr\Http\Message\{RequestInterface,ResponseInterface};
|
||||
|
||||
/**
|
||||
* Provides a proxy HttpClient that ensures the HTTP Authorization header is set on requests.
|
||||
*/
|
||||
class AuthorizedHttpClient implements HttpClient {
|
||||
use RequestSpeedrun;
|
||||
|
||||
class AuthorizedHttpClient implements ClientInterface {
|
||||
/**
|
||||
* @param HttpClient $httpClient Underlying HttpClient implementation.
|
||||
* @param ClientInterface $client Underlying ClientInterface implementation.
|
||||
* @param Credentials $credentials Credentials to use for the Authorization header.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly HttpClient $httpClient,
|
||||
private readonly ClientInterface $client,
|
||||
private readonly Credentials $credentials
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the underlying HttpClient instance.
|
||||
* Retrieves the underlying ClientInterface instance.
|
||||
*
|
||||
* @return HttpClient
|
||||
* @return ClientInterface
|
||||
*/
|
||||
public function getHttpClient(): HttpClient {
|
||||
return $this->httpClient;
|
||||
public function getClient(): ClientInterface {
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,25 +40,22 @@ class AuthorizedHttpClient implements HttpClient {
|
|||
return $this->credentials;
|
||||
}
|
||||
|
||||
public function createRequest(string $method, string $url): HttpRequest {
|
||||
return $this->httpClient->createRequest($method, $url);
|
||||
}
|
||||
|
||||
public function sendRequest(HttpRequest $request): HttpResponse {
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface {
|
||||
$authz = $this->credentials->getHttpAuthorization();
|
||||
if($authz !== '')
|
||||
$request->setHeader('Authorization', $authz);
|
||||
$request = $request->withHeader('Authorization', $authz);
|
||||
|
||||
return $this->httpClient->sendRequest($request);
|
||||
return $this->client->sendRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a HTTP request and returns a HTTP response without applying the HTTP authorization header.
|
||||
* Sends a PSR-7 request and returns a PSR-7 response without applying the HTTP authorization header.
|
||||
*
|
||||
* @param HttpRequest $request Request to send.
|
||||
* @return HttpResponse
|
||||
* @param RequestInterface $request
|
||||
* @throws ClientExceptionInterface If an error happens while processing the request.
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function sendRequestAnonymous(HttpRequest $request): HttpResponse {
|
||||
return $this->httpClient->sendRequest($request);
|
||||
public function sendRequestAnonymous(RequestInterface $request): ResponseInterface {
|
||||
return $this->client->sendRequest($request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
<?php declare(strict_types=1);
|
||||
// FlashiiClient.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
// Updated: 2025-03-20
|
||||
|
||||
namespace Flashii;
|
||||
|
||||
use RuntimeException;
|
||||
use Flashii\Credentials\{AnonymousCredentials,BasicCredentials,BearerCredentials,Credentials,MisuzuCredentials};
|
||||
use Flashii\Http\HttpClient;
|
||||
use Flashii\Http\Curl\CurlHttpClient;
|
||||
use Flashii\Http\Stream\StreamHttpClient;
|
||||
use Flashii\OAuth2\OAuth2Client;
|
||||
use Flashii\V1\V1Client;
|
||||
use GuzzleHttp\Client as GuzzleHttpClient;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
/**
|
||||
* Flashii API client.
|
||||
|
@ -47,12 +46,12 @@ class FlashiiClient {
|
|||
private ?V1Client $v1 = null;
|
||||
|
||||
/**
|
||||
* @param string $userAgentOrHttpClient User agent string for your application or a HttpClient implementation, providing a string will autodetect whether to use cURL or PHP streams.
|
||||
* @param string $userAgentOrHttpClient User agent string for your application or a ClientInterface implementation, providing a string will use Guzzle.
|
||||
* @param ?Credentials $credentials Credentials to authenticate with, leave null for unauthorised.
|
||||
* @param ?FlashiiUrls $urls Object containing base URLs, leave null to use the production environment.
|
||||
*/
|
||||
public function __construct(
|
||||
string|HttpClient $userAgentOrHttpClient = '',
|
||||
string|ClientInterface $userAgentOrHttpClient = '',
|
||||
?Credentials $credentials = null,
|
||||
?FlashiiUrls $urls = null
|
||||
) {
|
||||
|
@ -60,10 +59,16 @@ class FlashiiClient {
|
|||
$this->credentials = $credentials ?? new AnonymousCredentials;
|
||||
|
||||
if(is_string($userAgentOrHttpClient))
|
||||
$httpClient = self::createHttpClient($userAgentOrHttpClient);
|
||||
$httpClient = new GuzzleHttpClient([
|
||||
'allow_redirects' => true,
|
||||
'timeout' => 10,
|
||||
'headers' => [
|
||||
'User-Agent' => trim(sprintf('%s %s', trim($userAgentOrHttpClient), self::userAgentString())),
|
||||
],
|
||||
]);
|
||||
else {
|
||||
if($userAgentOrHttpClient instanceof AuthorizedHttpClient)
|
||||
$httpClient = $userAgentOrHttpClient->getHttpClient();
|
||||
$httpClient = $userAgentOrHttpClient->getClient();
|
||||
else
|
||||
$httpClient = $userAgentOrHttpClient;
|
||||
}
|
||||
|
@ -81,7 +86,6 @@ class FlashiiClient {
|
|||
*/
|
||||
public function oauth2(): OAuth2Client {
|
||||
$this->oauth2 ??= new OAuth2Client(
|
||||
$this,
|
||||
$this->httpClient,
|
||||
$this->credentials,
|
||||
$this->urls
|
||||
|
@ -201,27 +205,6 @@ class FlashiiClient {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HTTP client instance.
|
||||
*
|
||||
* Will use the cURL implementation if the extension is loaded, otherwise it will fall back to PHP streams.
|
||||
*
|
||||
* @param string $userAgent User-Agent string to use in requests.
|
||||
* @return HttpClient
|
||||
*/
|
||||
public static function createHttpClient(string $userAgent): HttpClient {
|
||||
$userAgent = trim(sprintf('%s %s', trim($userAgent), self::userAgentString()));
|
||||
|
||||
try {
|
||||
if(extension_loaded('curl'))
|
||||
return new CurlHttpClient($userAgent);
|
||||
} catch(RuntimeException $ex) {
|
||||
// fall through to the stream implementation if cURL fails to initialise for some reason
|
||||
}
|
||||
|
||||
return new StreamHttpClient($userAgent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the User-Agent string for the client library.
|
||||
*
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
<?php declare(strict_types=1);
|
||||
// FlashiiUrls.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-22
|
||||
// Updated: 2025-03-20
|
||||
|
||||
namespace Flashii;
|
||||
|
||||
use Stringable;
|
||||
|
||||
/**
|
||||
* Defines the base URLs the client has to be aware of to use the API.
|
||||
*/
|
||||
final class FlashiiUrls {
|
||||
/**
|
||||
* Production base URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const PROD_URL = 'https://flashii.net';
|
||||
|
||||
/**
|
||||
* Production API base URL.
|
||||
*
|
||||
|
@ -20,14 +29,33 @@ final class FlashiiUrls {
|
|||
* @param string $apiUrl API base URL, without trailing /.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $url,
|
||||
private readonly string $apiUrl
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the API base URL.
|
||||
* Retrieves a prefixed URL.
|
||||
*
|
||||
* @param string $path Path to append to the URL.
|
||||
* @param array<string, mixed> $args Query arguments to append to the URL.
|
||||
* @param array<string, Stringable|scalar|null|list<Stringable|scalar|null>> $args Query arguments to append to the URL.
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl(string $path = '', array $args = []): string {
|
||||
$url = $this->url . $path;
|
||||
|
||||
if(!empty($args)) {
|
||||
$url .= str_contains($url, '?') ? '&' : '?';
|
||||
$url .= UriQuery::encode($args);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a prefixed API URL.
|
||||
*
|
||||
* @param string $path Path to append to the URL.
|
||||
* @param array<string, Stringable|scalar|null|list<Stringable|scalar|null>> $args Query arguments to append to the URL.
|
||||
* @return string
|
||||
*/
|
||||
public function getApiUrl(string $path = '', array $args = []): string {
|
||||
|
@ -35,7 +63,7 @@ final class FlashiiUrls {
|
|||
|
||||
if(!empty($args)) {
|
||||
$url .= str_contains($url, '?') ? '&' : '?';
|
||||
$url .= http_build_query($args, encoding_type: PHP_QUERY_RFC3986);
|
||||
$url .= UriQuery::encode($args);
|
||||
}
|
||||
|
||||
return $url;
|
||||
|
@ -47,6 +75,6 @@ final class FlashiiUrls {
|
|||
* @return FlashiiUrls
|
||||
*/
|
||||
public static function production(): self {
|
||||
return new static(self::PROD_API_URL);
|
||||
return new static(self::PROD_URL, self::PROD_API_URL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// CurlHttpClient.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http\Curl;
|
||||
|
||||
use CurlHandle;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Flashii\Http\{HttpClient,HttpRequest,HttpResponse,RequestSpeedrun};
|
||||
|
||||
/**
|
||||
* cURL extension backed HTTP client implementation.
|
||||
*/
|
||||
class CurlHttpClient implements HttpClient {
|
||||
use RequestSpeedrun;
|
||||
|
||||
private readonly string $userAgent;
|
||||
private readonly CurlHandle $handle;
|
||||
|
||||
/**
|
||||
* @param string $userAgent User-Agent string.
|
||||
* @throws RuntimeException If cURL initialisation failed.
|
||||
*/
|
||||
public function __construct(
|
||||
string $userAgent
|
||||
) {
|
||||
$handle = curl_init();
|
||||
if($handle === false)
|
||||
throw new RuntimeException('Failed to initialise cURL.');
|
||||
|
||||
$version = curl_version();
|
||||
if(!is_array($version) || !array_key_exists('version', $version) || !is_string($version['version']))
|
||||
throw new RuntimeException('Failed to retrieve cURL version info.');
|
||||
|
||||
$this->handle = $handle;
|
||||
$this->userAgent = trim($userAgent) . sprintf(' cURL/%s', $version['version']);
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
curl_close($this->handle);
|
||||
}
|
||||
|
||||
public function createRequest(string $method, string $url): HttpRequest {
|
||||
return new CurlHttpRequest($this->userAgent, $method, $url);
|
||||
}
|
||||
|
||||
public function sendRequest(HttpRequest $request): HttpResponse {
|
||||
if(!($request instanceof CurlHttpRequest))
|
||||
throw new InvalidArgumentException('Type of $request is not understood by this HttpClient.');
|
||||
|
||||
curl_reset($this->handle);
|
||||
curl_setopt_array($this->handle, $request->getOpts());
|
||||
|
||||
$response = curl_exec($this->handle);
|
||||
if($response === false)
|
||||
throw new RuntimeException(curl_error($this->handle), curl_errno($this->handle));
|
||||
if($response === true)
|
||||
throw new RuntimeException('Request executed successfully but cURL returned no data???');
|
||||
|
||||
$parts = explode("\r\n\r\n", $response, 2);
|
||||
|
||||
$headerLines = array_reverse(explode("\r\n", $parts[0]));
|
||||
$statusLine = array_pop($headerLines);
|
||||
if(!is_string($statusLine))
|
||||
throw new RuntimeException('Unable to read status header.');
|
||||
|
||||
$statusLineScan = sscanf($statusLine, 'HTTP/%s %d %[^\t\r\n]');
|
||||
if($statusLineScan === null)
|
||||
throw new RuntimeException('Failed to decode HTTP status line.');
|
||||
[$_, $statusCode, $statusLine] = $statusLineScan;
|
||||
|
||||
$headers = [];
|
||||
while(($headerLine = array_pop($headerLines)) !== null) {
|
||||
$headerLine = trim((string)$headerLine);
|
||||
if($headerLine === '')
|
||||
continue;
|
||||
|
||||
$headerParts = explode(':', $headerLine, 2);
|
||||
$headers[strtolower(trim($headerParts[0]))] = count($headerParts) > 1 ? trim($headerParts[1]) : '';
|
||||
}
|
||||
|
||||
return new CurlHttpResponse($statusCode ?? 0, $statusLine ?? '', $headers, count($parts) > 1 ? $parts[1] : '');
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// CurlHttpRequest.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http\Curl;
|
||||
|
||||
use Flashii\Http\HttpRequest;
|
||||
|
||||
/**
|
||||
* Provides a cURL extension backed HTTP request implementation.
|
||||
*/
|
||||
class CurlHttpRequest implements HttpRequest {
|
||||
/** @var array<string, string> */
|
||||
private array $headers = [];
|
||||
|
||||
private ?string $body = null;
|
||||
|
||||
/**
|
||||
* @param string $userAgent User-Agent string.
|
||||
* @param string $method Request method.
|
||||
* @param string $url Request URI.
|
||||
*/
|
||||
public function __construct(
|
||||
private string $userAgent,
|
||||
private string $method,
|
||||
private string $url
|
||||
) {}
|
||||
|
||||
public function setHeader(string $name, string $value): void {
|
||||
$this->headers[strtolower($name)] = sprintf('%s: %s', $name, $value);
|
||||
}
|
||||
|
||||
public function removeHeader(string $name): void {
|
||||
unset($this->headers[strtolower($name)]);
|
||||
}
|
||||
|
||||
public function setBody(string $body, string $contentType = 'application/octet-stream'): void {
|
||||
$this->setHeader('content-type', $contentType);
|
||||
$this->body = $body;
|
||||
}
|
||||
|
||||
public function setBodyParams(array $params): void {
|
||||
$this->setBody(
|
||||
http_build_query($params, encoding_type: PHP_QUERY_RFC3986),
|
||||
'application/x-www-form-urlencoded'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this request to an array of CURLOPT_* settings.
|
||||
*
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public function getOpts(): array {
|
||||
return [
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_CUSTOMREQUEST => $this->method,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_HTTPHEADER => $this->headers,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TCP_FASTOPEN => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 2,
|
||||
CURLOPT_MAXREDIRS => 5,
|
||||
CURLOPT_POSTFIELDS => $this->body,
|
||||
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_URL => $this->url,
|
||||
CURLOPT_USERAGENT => $this->userAgent,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// CurlHttpResponse.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http\Curl;
|
||||
|
||||
use Flashii\Http\{HttpResponse,JsonBody,SuccessStatusCode};
|
||||
|
||||
/**
|
||||
* cURL extension backed HTTP response.
|
||||
*/
|
||||
class CurlHttpResponse implements HttpResponse {
|
||||
use JsonBody, SuccessStatusCode;
|
||||
|
||||
/**
|
||||
* @param int $statusCode HTTP status code.
|
||||
* @param string $statusText HTTP status text.
|
||||
* @param array<string, string> $headers HTTP response headers.
|
||||
* @param string $body HTTP body.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly int $statusCode,
|
||||
private readonly string $statusText,
|
||||
private readonly array $headers,
|
||||
private readonly string $body
|
||||
) {}
|
||||
|
||||
public function getStatusCode(): int {
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function getStatusText(): string {
|
||||
return $this->statusText;
|
||||
}
|
||||
|
||||
public function getHeaders(): array {
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function hasHeader(string $name): bool {
|
||||
return array_key_exists(strtolower($name), $this->headers);
|
||||
}
|
||||
|
||||
public function getHeader(string $name): string {
|
||||
return $this->headers[strtolower($name)] ?? '';
|
||||
}
|
||||
|
||||
public function hasBody(): bool {
|
||||
return $this->body !== '';
|
||||
}
|
||||
|
||||
public function getBody(): string {
|
||||
return $this->body;
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// HttpClient.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Provides a common interface for making HTTP clients.
|
||||
*/
|
||||
interface HttpClient {
|
||||
/**
|
||||
* Creates a HTTP request object.
|
||||
*
|
||||
* @param string $method Desired request method.
|
||||
* @param string $url Target URL.
|
||||
* @throws RuntimeException If the request could not be initialised.
|
||||
* @return HttpRequest
|
||||
*/
|
||||
function createRequest(string $method, string $url): HttpRequest;
|
||||
|
||||
/**
|
||||
* Sends a HTTP request and returns a HTTP response.
|
||||
*
|
||||
* @param HttpRequest $request Request to send.
|
||||
* @throws InvalidArgumentException If $request is not understood by the HttpClient implementation.
|
||||
* @throws RuntimeException If the request failed in an unexpected way.
|
||||
* @return HttpResponse
|
||||
*/
|
||||
function sendRequest(HttpRequest $request): HttpResponse;
|
||||
|
||||
/**
|
||||
* Combines createRequest and sendRequest into one method call.
|
||||
*
|
||||
* Less noise for when we don't need to touch HttpRequest :)
|
||||
*
|
||||
* @param string $method Desired request method.
|
||||
* @param string $url Target URL.
|
||||
* @throws InvalidArgumentException If $request is not understood by the HttpClient implementation.
|
||||
* @throws RuntimeException If the request could not be initialised.
|
||||
* @throws RuntimeException If the request failed in an unexpected way.
|
||||
* @return HttpResponse
|
||||
*/
|
||||
function request(string $method, string $url): HttpResponse;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// HttpRequest.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http;
|
||||
|
||||
/**
|
||||
* Provides a common interface for HTTP requests.
|
||||
*/
|
||||
interface HttpRequest {
|
||||
/**
|
||||
* Sets a header value.
|
||||
*
|
||||
* @param string $name Header name.
|
||||
* @param string $value Header value.
|
||||
*/
|
||||
function setHeader(string $name, string $value): void;
|
||||
|
||||
/**
|
||||
* Removes a header value.
|
||||
*
|
||||
* @param string $name.
|
||||
*/
|
||||
function removeHeader(string $name): void;
|
||||
|
||||
/**
|
||||
* Sets a raw body with a provided media type.
|
||||
*
|
||||
* @param string $body Request body.
|
||||
* @param string $contentType Request body content type.
|
||||
*/
|
||||
function setBody(string $body, string $contentType = 'application/octet-stream'): void;
|
||||
|
||||
/**
|
||||
* Sets a urlencoded form request body.
|
||||
*
|
||||
* @param array<string, mixed> $params Form parameters.
|
||||
*/
|
||||
function setBodyParams(array $params): void;
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// HttpResponse.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Provides a common interface for HTTP responses.
|
||||
*/
|
||||
interface HttpResponse {
|
||||
/**
|
||||
* Returns the status code.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function getStatusCode(): int;
|
||||
|
||||
/**
|
||||
* Returns the status text.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getStatusText(): string;
|
||||
|
||||
/**
|
||||
* Returns true if the status code indicates success (200-299).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function isSuccessStatusCode(): bool;
|
||||
|
||||
/**
|
||||
* Throws an exception if isSuccessStatusCode was false.
|
||||
*
|
||||
* @throws RuntimeException if the status code did not indicate success.
|
||||
*/
|
||||
function ensureSuccessStatusCode(): void;
|
||||
|
||||
/**
|
||||
* Returns all response headers.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
function getHeaders(): array;
|
||||
|
||||
/**
|
||||
* Checks if a response header is present.
|
||||
*
|
||||
* @param string $name Name of the header.
|
||||
* @return bool true if it is present, false if not.
|
||||
*/
|
||||
function hasHeader(string $name): bool;
|
||||
|
||||
/**
|
||||
* Returns the value of a response header.
|
||||
*
|
||||
* @param string $name Name of the header.
|
||||
* @return string
|
||||
*/
|
||||
function getHeader(string $name): string;
|
||||
|
||||
/**
|
||||
* Checks if the response has a body.
|
||||
*
|
||||
* @return bool true if it has a body, false if not.
|
||||
*/
|
||||
function hasBody(): bool;
|
||||
|
||||
/**
|
||||
* Returns the body of this response.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getBody(): string;
|
||||
|
||||
/**
|
||||
* Attempts to json_decode the body string.
|
||||
*
|
||||
* @param bool $associative true to return JSON objects as PHP arrays instead of PHP objects.
|
||||
* @return mixed
|
||||
*/
|
||||
function getJsonBody(bool $associative = true): mixed;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// JsonBody.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http;
|
||||
|
||||
/**
|
||||
* Common implementation for the getJsonBody method of HttpResponse.
|
||||
*/
|
||||
trait JsonBody {
|
||||
/**
|
||||
* Attempts to json_decode the body string.
|
||||
*
|
||||
* @param bool $associative true to return JSON objects as PHP arrays instead of PHP objects.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getJsonBody(bool $associative = false): mixed {
|
||||
return json_decode($this->getBody(), $associative, 512, JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// RequestSpeedrun.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Common implementation for the request method of HttpClient.
|
||||
*/
|
||||
trait RequestSpeedrun {
|
||||
/**
|
||||
* Combines createRequest and sendRequest into one method call.
|
||||
*
|
||||
* Less noise for when we don't need to touch HttpRequest :)
|
||||
*
|
||||
* @param string $method Desired request method.
|
||||
* @param string $url Target URL.
|
||||
* @throws InvalidArgumentException If $request is not understood by the HttpClient implementation.
|
||||
* @throws RuntimeException If the request could not be initialised.
|
||||
* @throws RuntimeException If the request failed in an unexpected way.
|
||||
* @return HttpResponse
|
||||
*/
|
||||
public function request(string $method, string $url): HttpResponse {
|
||||
return $this->sendRequest($this->createRequest($method, $url));
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// StreamHttpClient.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http\Stream;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Flashii\Http\{HttpClient,HttpRequest,HttpResponse,RequestSpeedrun};
|
||||
|
||||
/**
|
||||
* PHP stream backed HTTP client implementation.
|
||||
*/
|
||||
class StreamHttpClient implements HttpClient {
|
||||
use RequestSpeedrun;
|
||||
|
||||
/**
|
||||
* @param string $userAgent User-Agent string.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $userAgent
|
||||
) {}
|
||||
|
||||
public function createRequest(string $method, string $url): HttpRequest {
|
||||
return new StreamHttpRequest($this->userAgent, $method, $url);
|
||||
}
|
||||
|
||||
public function sendRequest(HttpRequest $request): HttpResponse {
|
||||
if(!($request instanceof StreamHttpRequest))
|
||||
throw new InvalidArgumentException('Type of $request is not understood by this HttpClient.');
|
||||
|
||||
$ctx = stream_context_create($request->getStreamOptions());
|
||||
$handle = fopen($request->getUrl(), 'rb', context: $ctx);
|
||||
if($handle === false)
|
||||
throw new RuntimeException('Failed create HTTP request.');
|
||||
|
||||
$metaData = stream_get_meta_data($handle);
|
||||
if(!array_key_exists('wrapper_data', $metaData) || !is_array($metaData['wrapper_data']))
|
||||
throw new RuntimeException('wrapper_data missing from stream metadata.');
|
||||
|
||||
$wrapperData = array_reverse($metaData['wrapper_data']);
|
||||
|
||||
$statusLine = array_pop($wrapperData);
|
||||
if(!is_string($statusLine))
|
||||
throw new RuntimeException('Unable to read status header.');
|
||||
|
||||
$statusLineScan = sscanf($statusLine, 'HTTP/%s %d %[^\t\r\n]');
|
||||
if($statusLineScan === null)
|
||||
throw new RuntimeException('Failed to decode HTTP status line.');
|
||||
[$_, $statusCode, $statusLine] = $statusLineScan;
|
||||
|
||||
$headers = [];
|
||||
while(($headerLine = array_pop($wrapperData)) !== null) {
|
||||
if(!is_string($headerLine)) {
|
||||
if(is_scalar($headerLine))
|
||||
$headerLine = (string)$headerLine;
|
||||
else continue;
|
||||
}
|
||||
|
||||
$headerLine = trim($headerLine);
|
||||
if($headerLine === '')
|
||||
continue;
|
||||
|
||||
$headerParts = explode(':', $headerLine, 2);
|
||||
$headers[strtolower(trim($headerParts[0]))] = count($headerParts) > 1 ? trim($headerParts[1]) : '';
|
||||
}
|
||||
|
||||
$body = stream_get_contents($handle);
|
||||
if($body === false)
|
||||
throw new RuntimeException('Request executed successfully but failed to retrieve data.');
|
||||
|
||||
return new StreamHttpResponse($statusCode ?? 0, $statusLine ?? '', $headers, $body);
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// StreamHttpRequest.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http\Stream;
|
||||
|
||||
use Flashii\Http\HttpRequest;
|
||||
|
||||
/**
|
||||
* Provides a builder for PHP stream backed HTTP requests.
|
||||
*/
|
||||
class StreamHttpRequest implements HttpRequest {
|
||||
/** @var array<string, string> */
|
||||
private array $headers = [];
|
||||
|
||||
private ?string $body = null;
|
||||
|
||||
/**
|
||||
* @param string $userAgent User-Agent string.
|
||||
* @param string $method Request method.
|
||||
* @param string $url Request URI.
|
||||
*/
|
||||
public function __construct(
|
||||
private string $userAgent,
|
||||
private string $method,
|
||||
private string $url
|
||||
) {}
|
||||
|
||||
public function setHeader(string $name, string $value): void {
|
||||
$this->headers[strtolower($name)] = sprintf('%s: %s', $name, $value);
|
||||
}
|
||||
|
||||
public function removeHeader(string $name): void {
|
||||
unset($this->headers[strtolower($name)]);
|
||||
}
|
||||
|
||||
public function setBody(string $body, string $contentType = 'application/octet-stream'): void {
|
||||
$this->setHeader('content-type', $contentType);
|
||||
$this->body = $body;
|
||||
}
|
||||
|
||||
public function setBodyParams(array $params): void {
|
||||
$this->setBody(
|
||||
http_build_query($params, encoding_type: PHP_QUERY_RFC3986),
|
||||
'application/x-www-form-urlencoded'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request URI.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl(): string {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this request to an array that can be passed to stream_context_create.
|
||||
*
|
||||
* @return array{'http': array{
|
||||
* method: string,
|
||||
* header: array<string, string>,
|
||||
* user_agent: string,
|
||||
* content: ?string,
|
||||
* follow_location: int,
|
||||
* max_redirects: int,
|
||||
* protocol_version: string,
|
||||
* timeout: float,
|
||||
* ignore_errors: bool
|
||||
* }}
|
||||
*/
|
||||
public function getStreamOptions(): array {
|
||||
return [
|
||||
'http' => [
|
||||
'method' => $this->method,
|
||||
'header' => $this->headers,
|
||||
'user_agent' => $this->userAgent,
|
||||
'content' => $this->body,
|
||||
'follow_location' => 1,
|
||||
'max_redirects' => 5,
|
||||
'protocol_version' => '1.1',
|
||||
'timeout' => 7,
|
||||
'ignore_errors' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// StreamHttpResponse.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http\Stream;
|
||||
|
||||
use Flashii\Http\{HttpResponse,JsonBody,SuccessStatusCode};
|
||||
|
||||
/**
|
||||
* PHP stream backed HTTP response.
|
||||
*/
|
||||
class StreamHttpResponse implements HttpResponse {
|
||||
use JsonBody, SuccessStatusCode;
|
||||
|
||||
/**
|
||||
* @param int $statusCode HTTP status code.
|
||||
* @param string $statusText HTTP status text.
|
||||
* @param array<string, string> $headers HTTP response headers.
|
||||
* @param string $body HTTP body.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly int $statusCode,
|
||||
private readonly string $statusText,
|
||||
private readonly array $headers,
|
||||
private readonly string $body
|
||||
) {}
|
||||
|
||||
public function getStatusCode(): int {
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function getStatusText(): string {
|
||||
return $this->statusText;
|
||||
}
|
||||
|
||||
public function getHeaders(): array {
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function hasHeader(string $name): bool {
|
||||
return array_key_exists(strtolower($name), $this->headers);
|
||||
}
|
||||
|
||||
public function getHeader(string $name): string {
|
||||
return $this->headers[strtolower($name)] ?? '';
|
||||
}
|
||||
|
||||
public function hasBody(): bool {
|
||||
return $this->body !== '';
|
||||
}
|
||||
|
||||
public function getBody(): string {
|
||||
return $this->body;
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// SuccessStatusCode.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
namespace Flashii\Http;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Common implementation for the SuccessStatusCode methods of HttpResponse.
|
||||
*/
|
||||
trait SuccessStatusCode {
|
||||
/**
|
||||
* Returns true if the status code indicates success (200-299).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccessStatusCode(): bool {
|
||||
$statusCode = $this->getStatusCode();
|
||||
return $statusCode >= 200 && $statusCode <= 299;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if isSuccessStatusCode was false.
|
||||
*
|
||||
* @throws RuntimeException if the status code did not indicate success.
|
||||
*/
|
||||
public function ensureSuccessStatusCode(): void {
|
||||
if(!$this->isSuccessStatusCode())
|
||||
throw new RuntimeException(sprintf('HTTP request returned status code %03d.', $this->getStatusCode()));
|
||||
}
|
||||
}
|
30
src/Json.php
Normal file
30
src/Json.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php declare(strict_types=1);
|
||||
// Json.php
|
||||
// Created: 2025-03-20
|
||||
// Updated: 2025-03-20
|
||||
|
||||
namespace Flashii;
|
||||
|
||||
use Stringable;
|
||||
/**
|
||||
* JSON decoder with different default flags.
|
||||
*/
|
||||
final class Json {
|
||||
/**
|
||||
* Decodes a JSON string.
|
||||
*
|
||||
* @param Stringable|string $json
|
||||
* @param ?bool $associative
|
||||
* @param int<1, max> $depth
|
||||
* @param int $flags
|
||||
* @return mixed
|
||||
*/
|
||||
public static function decode(
|
||||
Stringable|string $json,
|
||||
?bool $associative = null,
|
||||
int $depth = 512,
|
||||
int $flags = JSON_INVALID_UTF8_SUBSTITUTE
|
||||
): mixed {
|
||||
return json_decode((string)$json, $associative, $depth, $flags | JSON_THROW_ON_ERROR);
|
||||
}
|
||||
}
|
|
@ -1,28 +1,28 @@
|
|||
<?php declare(strict_types=1);
|
||||
// OAuth2Client.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-22
|
||||
// Updated: 2025-03-20
|
||||
|
||||
namespace Flashii\OAuth2;
|
||||
|
||||
use RuntimeException;
|
||||
use InvalidArgumentException;
|
||||
use Flashii\{AuthorizedHttpClient,FlashiiClient,FlashiiUrls,UriBase64};
|
||||
use RuntimeException;
|
||||
use Stringable;
|
||||
use Flashii\{AuthorizedHttpClient,FlashiiClient,FlashiiUrls,Json,UriBase64,UriQuery};
|
||||
use Flashii\Credentials\{BasicCredentials,Credentials};
|
||||
use GuzzleHttp\Psr7\Request as GuzzleHttpRequest;
|
||||
|
||||
/**
|
||||
* Implements the OAuth2 portions of the API.
|
||||
*/
|
||||
class OAuth2Client {
|
||||
/**
|
||||
* @param FlashiiClient $flashiiClient Parent client instance to fork upon successful auth.
|
||||
* @param AuthorizedHttpClient $httpClient Authorized HTTP client instance.
|
||||
* @param AuthorizedHttpClient $client Authorized HTTP client instance.
|
||||
* @param Credentials $credentials API credentials.
|
||||
* @param FlashiiUrls $urls API URLs.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly FlashiiClient $flashiiClient,
|
||||
private readonly AuthorizedHttpClient $httpClient,
|
||||
private readonly AuthorizedHttpClient $client,
|
||||
private readonly Credentials $credentials,
|
||||
private readonly FlashiiUrls $urls
|
||||
) {}
|
||||
|
@ -35,11 +35,11 @@ class OAuth2Client {
|
|||
* @param ?string $scope Scope to request, null to omit.
|
||||
* @param ?string $redirectUri URI to redirect to, required if the app has more than one redirect URI registered.
|
||||
* @param ?string $clientId Client ID of the app attempting to authorise.
|
||||
* @param array<string, mixed> $args Additional arguments to add to the URL.
|
||||
* @param array<string, Stringable|scalar|null|list<Stringable|scalar|null>> $args Additional arguments to add to the URL.
|
||||
* @throws InvalidArgumentException If the Credentials instance passed to the constructor is not an instance of BasicCredentials and $clientId was left as null.
|
||||
* @return string Authorisation URL that the user can be redirected to.
|
||||
*/
|
||||
public function createAuthoriseUrl(
|
||||
public function createAuthorizeUrl(
|
||||
string $codeVerifier,
|
||||
?string $state = null,
|
||||
?string $scope = null,
|
||||
|
@ -66,7 +66,7 @@ class OAuth2Client {
|
|||
if($redirectUri !== null)
|
||||
$args['redirect_uri'] = $redirectUri;
|
||||
|
||||
return $this->urls->getApiUrl('/oauth2/authorise', $args);
|
||||
return $this->urls->getUrl('/oauth2/authorize', $args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,11 +77,11 @@ class OAuth2Client {
|
|||
* @param ?string $scope Scope to request, null to omit.
|
||||
* @param ?string $redirectUri URI to redirect to, required if the app has more than one redirect URI registered.
|
||||
* @param ?string $clientId Client ID of the app attempting to authorise.
|
||||
* @param array<string, mixed> $args Additional arguments to add to the URL.
|
||||
* @param array<string, Stringable|scalar|null|list<Stringable|scalar|null>> $args Additional arguments to add to the URL.
|
||||
* @throws InvalidArgumentException If the Credentials instance passed to the constructor is not an instance of BasicCredentials and $clientId was left as null.
|
||||
* @return string Authorisation URL that the user can be redirected to.
|
||||
*/
|
||||
public function createAuthorizeUrl(
|
||||
public function createAuthoriseUrl(
|
||||
string $codeVerifier,
|
||||
?string $state = null,
|
||||
?string $scope = null,
|
||||
|
@ -89,7 +89,7 @@ class OAuth2Client {
|
|||
?string $clientId = null,
|
||||
array $args = []
|
||||
): string {
|
||||
return $this->createAuthoriseUrl(
|
||||
return $this->createAuthorizeUrl(
|
||||
$codeVerifier,
|
||||
$state,
|
||||
$scope,
|
||||
|
@ -106,7 +106,7 @@ class OAuth2Client {
|
|||
* @param ?string $clientId Client ID, if Basic auth is impossible for some reason.
|
||||
* @return OAuth2Error|OAuth2AuthorizationRequest
|
||||
*/
|
||||
public function requestAuthorise(
|
||||
public function requestAuthorize(
|
||||
?string $scope = null,
|
||||
?string $clientId = null
|
||||
): OAuth2Error|OAuth2AuthorizationRequest {
|
||||
|
@ -121,14 +121,18 @@ class OAuth2Client {
|
|||
$args['client_id'] = $clientId;
|
||||
}
|
||||
|
||||
$request = $this->httpClient->createRequest('POST', $this->urls->getApiUrl('/oauth2/request-authorise'));
|
||||
$request->setBodyParams($args);
|
||||
$request = new GuzzleHttpRequest(
|
||||
'POST',
|
||||
$this->urls->getUrl('/oauth2/request-authorize'),
|
||||
['Content-Type' => 'application/x-www-form-urlencoded'],
|
||||
UriQuery::encode($args)
|
||||
);
|
||||
|
||||
$response = $useAnonymous
|
||||
? $this->httpClient->sendRequestAnonymous($request)
|
||||
: $this->httpClient->sendRequest($request);
|
||||
? $this->client->sendRequestAnonymous($request)
|
||||
: $this->client->sendRequest($request);
|
||||
|
||||
$body = $response->getJsonBody();
|
||||
$body = Json::decode($response->getBody());
|
||||
if(!is_object($body))
|
||||
throw new RuntimeException('API response was not a JSON object.');
|
||||
|
||||
|
@ -145,11 +149,11 @@ class OAuth2Client {
|
|||
* @param ?string $clientId See requestAuthorise documentation.
|
||||
* @return OAuth2Error|OAuth2AuthorizationRequest
|
||||
*/
|
||||
public function requestAuthorize(
|
||||
public function requestAuthorise(
|
||||
?string $scope = null,
|
||||
?string $clientId = null
|
||||
): OAuth2Error|OAuth2AuthorizationRequest {
|
||||
return $this->requestAuthorise($scope, $clientId);
|
||||
return $this->requestAuthorize($scope, $clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,14 +182,18 @@ class OAuth2Client {
|
|||
$args['client_secret'] = $clientSecret;
|
||||
}
|
||||
|
||||
$request = $this->httpClient->createRequest('POST', $this->urls->getApiUrl('/oauth2/token'));
|
||||
$request->setBodyParams($args);
|
||||
$request = new GuzzleHttpRequest(
|
||||
'POST',
|
||||
$this->urls->getUrl('/oauth2/token'),
|
||||
['Content-Type' => 'application/x-www-form-urlencoded'],
|
||||
UriQuery::encode($args)
|
||||
);
|
||||
|
||||
$response = $useAnonymous
|
||||
? $this->httpClient->sendRequestAnonymous($request)
|
||||
: $this->httpClient->sendRequest($request);
|
||||
? $this->client->sendRequestAnonymous($request)
|
||||
: $this->client->sendRequest($request);
|
||||
|
||||
$body = $response->getJsonBody();
|
||||
$body = Json::decode($response->getBody());
|
||||
if(!is_object($body))
|
||||
throw new RuntimeException('API response was not a JSON object.');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
// UriBase64.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
// Updated: 2025-03-20
|
||||
|
||||
namespace Flashii;
|
||||
|
||||
|
@ -23,10 +23,9 @@ final class UriBase64 {
|
|||
* Decodes data encoded with URI-safe MIME base64.
|
||||
*
|
||||
* @param string $string The encoded data.
|
||||
* @param bool $strict If the strict parameter is set to true then the base64_decode() function will return false if the input contains character from outside the base64 alphabet. Otherwise invalid characters will be silently discarded.
|
||||
* @return string|false Returns the decoded data or false on failure. The returned data may be binary.
|
||||
* @return string Returns the decoded data, may be binary.
|
||||
*/
|
||||
public static function decode(string $string, bool $strict = false): string|false {
|
||||
public static function decode(string $string): string {
|
||||
return base64_decode(str_pad(strtr($string, '-_', '+/'), strlen($string) % 4, '=', STR_PAD_RIGHT));
|
||||
}
|
||||
}
|
||||
|
|
66
src/UriQuery.php
Normal file
66
src/UriQuery.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php declare(strict_types=1);
|
||||
// UriQuery.php
|
||||
// Created: 2025-03-20
|
||||
// Updated: 2025-03-20
|
||||
|
||||
namespace Flashii;
|
||||
|
||||
use Stringable;
|
||||
/**
|
||||
* Provides URI query encoding.
|
||||
*/
|
||||
final class UriQuery {
|
||||
/**
|
||||
* Parses a urlencoded query/form string.
|
||||
*
|
||||
* @param string $query urlencoded string.
|
||||
* @return array<string, list<?string>>
|
||||
*/
|
||||
public static function decode(string $query): array {
|
||||
$params = [];
|
||||
|
||||
if($query !== '') {
|
||||
$paramParts = explode('&', $query);
|
||||
foreach($paramParts as $paramPart) {
|
||||
$parts = explode('=', $paramPart, 2);
|
||||
$name = urldecode($parts[0]);
|
||||
if(!array_key_exists($name, $params))
|
||||
$params[$name] = [];
|
||||
$params[$name][] = count($parts) > 1 ? urldecode($parts[1]) : null;
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a query/form string from params.
|
||||
*
|
||||
* @param array<string, Stringable|scalar|null|list<Stringable|scalar|null>> $params
|
||||
* @return string
|
||||
*/
|
||||
public static function encode(array $params): string {
|
||||
$parts = [];
|
||||
|
||||
foreach($params as $name => $values) {
|
||||
$base = rawurlencode($name);
|
||||
if($values === null) {
|
||||
$parts[] = $base;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(is_scalar($values) || $values instanceof Stringable)
|
||||
$values = [$values];
|
||||
|
||||
foreach($values as $value) {
|
||||
$part = $base;
|
||||
if(is_scalar($value) || $value instanceof Stringable)
|
||||
$part .= sprintf('=%s', rawurlencode((string)$value));
|
||||
|
||||
$parts[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
return implode('&', $parts);
|
||||
}
|
||||
}
|
|
@ -1,23 +1,24 @@
|
|||
<?php declare(strict_types=1);
|
||||
// V1EmotesClient.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
// Updated: 2025-03-20
|
||||
|
||||
namespace Flashii\V1\Emotes;
|
||||
|
||||
use Flashii\FlashiiUrls;
|
||||
use Flashii\Http\HttpClient;
|
||||
use Flashii\{FlashiiUrls,Json};
|
||||
use GuzzleHttp\Psr7\Request as GuzzleHttpRequest;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
/**
|
||||
* Implements the APIv1 emotes portion.
|
||||
*/
|
||||
class V1EmotesClient {
|
||||
/**
|
||||
* @param HttpClient $httpClient HTTP client.
|
||||
* @param ClientInterface $client HTTP client.
|
||||
* @param FlashiiUrls $urls API URLs.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly HttpClient $httpClient,
|
||||
private readonly ClientInterface $client,
|
||||
private readonly FlashiiUrls $urls
|
||||
) {}
|
||||
|
||||
|
@ -38,10 +39,11 @@ class V1EmotesClient {
|
|||
if($includeOrder)
|
||||
$args['include_order'] = '1';
|
||||
|
||||
$response = $this->httpClient->request('GET', $this->urls->getApiUrl('/v1/emotes', $args));
|
||||
$response->ensureSuccessStatusCode();
|
||||
$response = $this->client->sendRequest(
|
||||
new GuzzleHttpRequest('GET', $this->urls->getApiUrl('/v1/emotes', $args))
|
||||
);
|
||||
|
||||
$emotes = $response->getJsonBody();
|
||||
$emotes = Json::decode($response->getBody());
|
||||
if(!is_array($emotes))
|
||||
return [];
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
<?php declare(strict_types=1);
|
||||
// V1Client.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
// Updated: 2025-03-20
|
||||
|
||||
namespace Flashii\V1;
|
||||
|
||||
use RuntimeException;
|
||||
use Flashii\FlashiiUrls;
|
||||
use Flashii\Http\HttpClient;
|
||||
use Flashii\{FlashiiUrls,Json};
|
||||
use Flashii\V1\Emotes\V1EmotesClient;
|
||||
use Flashii\V1\Users\V1User;
|
||||
use GuzzleHttp\Psr7\Request as GuzzleHttpRequest;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
|
||||
/**
|
||||
* Implements the APIv1 client.
|
||||
|
@ -18,11 +19,11 @@ class V1Client {
|
|||
private ?V1EmotesClient $emotes = null;
|
||||
|
||||
/**
|
||||
* @param HttpClient $httpClient HTTP client.
|
||||
* @param ClientInterface $client HTTP client.
|
||||
* @param FlashiiUrls $urls API URLs.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly HttpClient $httpClient,
|
||||
private readonly ClientInterface $client,
|
||||
private readonly FlashiiUrls $urls
|
||||
) {}
|
||||
|
||||
|
@ -34,15 +35,15 @@ class V1Client {
|
|||
* @return ?V1User
|
||||
*/
|
||||
public function me(): ?V1User {
|
||||
$response = $this->httpClient->request('GET', $this->urls->getApiUrl('/v1/me'));
|
||||
$response = $this->client->sendRequest(
|
||||
new GuzzleHttpRequest('GET', $this->urls->getApiUrl('/v1/me'))
|
||||
);
|
||||
if($response->getStatusCode() === 401)
|
||||
return null;
|
||||
|
||||
$response->ensureSuccessStatusCode();
|
||||
|
||||
$me = $response->getJsonBody();
|
||||
$me = Json::decode($response->getBody());
|
||||
if(!is_object($me))
|
||||
throw new RuntimeException('API response was not a JSON object.');
|
||||
throw new RuntimeException('Failed to fetch current user info.');
|
||||
|
||||
return V1User::decode($me);
|
||||
}
|
||||
|
@ -53,7 +54,7 @@ class V1Client {
|
|||
* @return V1EmotesClient
|
||||
*/
|
||||
public function emotes(): V1EmotesClient {
|
||||
$this->emotes ??= new V1EmotesClient($this->httpClient, $this->urls);
|
||||
$this->emotes ??= new V1EmotesClient($this->client, $this->urls);
|
||||
return $this->emotes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// CurlHttpTest.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Flashii\{AuthorizedHttpClient,FlashiiClient};
|
||||
use Flashii\Credentials\{BasicCredentials,BearerCredentials};
|
||||
use Flashii\Http\Curl\{CurlHttpClient,CurlHttpRequest,CurlHttpResponse};
|
||||
|
||||
/**
|
||||
* @covers CurlHttpClient
|
||||
* @covers CurlHttpRequest
|
||||
* @covers CurlHttpResponse
|
||||
* @uses AuthorizedHttpClient
|
||||
* @uses FlashiiClient
|
||||
* @uses BasicCredentials
|
||||
* @uses BearerCredentials
|
||||
*/
|
||||
final class CurlHttpTest extends TestCase {
|
||||
public function testHttpGet(): void {
|
||||
$client = new CurlHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$request = $client->createRequest('GET', 'https://httpbin.org/get?alreadyhere=true');
|
||||
$request->setHeader('X-Test', 'meow');
|
||||
$request->setHeader('X-Test2', 'mewow');
|
||||
$request->removeHeader('x-test2');
|
||||
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertTrue($response->hasHeader('Content-Type'));
|
||||
$this->assertEquals('application/json', $response->getHeader('Content-type'));
|
||||
$this->assertTrue($response->hasBody());
|
||||
|
||||
$body = $response->getJsonBody();
|
||||
$this->assertIsObject($body);
|
||||
|
||||
$body = $response->getJsonBody(true);
|
||||
$this->assertIsArray($body);
|
||||
$this->assertArrayHasKey('headers', $body);
|
||||
$this->assertIsArray($body['headers']);
|
||||
$this->assertArrayHasKey('X-Test', $body['headers']);
|
||||
$this->assertEquals('meow', $body['headers']['X-Test']);
|
||||
$this->assertArrayNotHasKey('X-Test2', $body['headers']);
|
||||
|
||||
$this->assertArrayHasKey('args', $body);
|
||||
$this->assertIsArray($body['args']);
|
||||
$this->assertArrayHasKey('alreadyhere', $body['args']);
|
||||
$this->assertEquals('true', $body['args']['alreadyhere']);
|
||||
}
|
||||
|
||||
public function testHttpFailStatus(): void {
|
||||
$client = new CurlHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$request = $client->createRequest('GET', 'https://httpbin.org/status/404');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
|
||||
$request = $client->createRequest('POST', 'https://httpbin.org/status/503');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertEquals(503, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testEnsureSuccess(): void {
|
||||
$client = new CurlHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$request = $client->createRequest('PATCH', 'https://httpbin.org/status/204');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertTrue($response->isSuccessStatusCode());
|
||||
|
||||
$response->ensureSuccessStatusCode();
|
||||
|
||||
$request = $client->createRequest('DELETE', 'https://httpbin.org/status/418');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertFalse($response->isSuccessStatusCode());
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('HTTP request returned status code 418.');
|
||||
$response->ensureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public function testAuthorizedHttp(): void {
|
||||
$realClient = new CurlHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$client = new AuthorizedHttpClient($realClient, new BasicCredentials('freakyfurball', 'express1'));
|
||||
$this->assertFalse(
|
||||
$realClient->sendRequest(
|
||||
$realClient->createRequest('GET', 'https://httpbin.org/basic-auth/freakyfurball/express1')
|
||||
)->isSuccessStatusCode()
|
||||
);
|
||||
$this->assertTrue(
|
||||
$client->sendRequest(
|
||||
$client->createRequest('GET', 'https://httpbin.org/basic-auth/freakyfurball/express1')
|
||||
)->isSuccessStatusCode()
|
||||
);
|
||||
$this->assertFalse(
|
||||
$client->sendRequest(
|
||||
$client->createRequest('GET', 'https://httpbin.org/basic-auth/freakyfurball/express2')
|
||||
)->isSuccessStatusCode()
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$realClient->sendRequest(
|
||||
$realClient->createRequest('GET', 'https://httpbin.org/bearer')
|
||||
)->isSuccessStatusCode()
|
||||
);
|
||||
|
||||
$accessToken = 'DSw7ih2fMfXW3fAkUKJitltBtc7JT8TA';
|
||||
$client = new AuthorizedHttpClient($realClient, new BearerCredentials($accessToken));
|
||||
$response = $client->sendRequest(
|
||||
$client->createRequest('GET', 'https://httpbin.org/bearer')
|
||||
);
|
||||
$this->assertTrue($response->hasBody());
|
||||
|
||||
$body = $response->getJsonBody(true);
|
||||
$this->assertIsArray($body);
|
||||
|
||||
$this->assertArrayHasKey('authenticated', $body);
|
||||
$this->assertIsBool($body['authenticated']);
|
||||
$this->assertTrue($body['authenticated']);
|
||||
|
||||
$this->assertArrayHasKey('token', $body);
|
||||
$this->assertIsString($body['token']);
|
||||
$this->assertEquals($accessToken, $body['token']);
|
||||
}
|
||||
|
||||
public function testHttpBody(): void {
|
||||
$client = new CurlHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$random = base64_encode(random_bytes(100));
|
||||
$request = $client->createRequest('PUT', 'https://httpbin.org/anything');
|
||||
$request->setBody($random, 'application/x-test-data');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertTrue($response->hasBody());
|
||||
|
||||
$body = $response->getJsonBody(true);
|
||||
$this->assertIsArray($body);
|
||||
|
||||
$this->assertArrayHasKey('method', $body);
|
||||
$this->assertIsString($body['method']);
|
||||
$this->assertEquals('PUT', $body['method']);
|
||||
|
||||
$this->assertArrayHasKey('headers', $body);
|
||||
$this->assertIsArray($body['headers']);
|
||||
$this->assertArrayHasKey('Content-Type', $body['headers']);
|
||||
$this->assertIsString($body['headers']['Content-Type']);
|
||||
$this->assertEquals('application/x-test-data', $body['headers']['Content-Type']);
|
||||
|
||||
$this->assertArrayHasKey('data', $body);
|
||||
$this->assertIsString($body['data']);
|
||||
$this->assertEquals($random, $body['data']);
|
||||
|
||||
$request = $client->createRequest('POST', 'https://httpbin.org/post');
|
||||
$request->setBodyParams([
|
||||
'string' => 'meow',
|
||||
'more' => 'wowzers this one has spaces crazy',
|
||||
]);
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertTrue($response->hasBody());
|
||||
|
||||
$body = $response->getJsonBody(true);
|
||||
$this->assertIsArray($body);
|
||||
|
||||
$this->assertArrayHasKey('headers', $body);
|
||||
$this->assertIsArray($body['headers']);
|
||||
$this->assertArrayHasKey('Content-Type', $body['headers']);
|
||||
$this->assertIsString($body['headers']['Content-Type']);
|
||||
$this->assertEquals('application/x-www-form-urlencoded', $body['headers']['Content-Type']);
|
||||
|
||||
$this->assertArrayHasKey('form', $body);
|
||||
$this->assertIsArray($body['form']);
|
||||
$this->assertArrayHasKey('string', $body['form']);
|
||||
$this->assertIsString($body['form']['string']);
|
||||
$this->assertEquals('meow', $body['form']['string']);
|
||||
$this->assertArrayHasKey('more', $body['form']);
|
||||
$this->assertIsString($body['form']['more']);
|
||||
$this->assertEquals('wowzers this one has spaces crazy', $body['form']['more']);
|
||||
}
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
// StreamHttpTest.php
|
||||
// Created: 2024-11-16
|
||||
// Updated: 2024-11-16
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Flashii\{AuthorizedHttpClient,FlashiiClient};
|
||||
use Flashii\Credentials\{BasicCredentials,BearerCredentials};
|
||||
use Flashii\Http\Stream\{StreamHttpClient,StreamHttpRequest,StreamHttpResponse};
|
||||
|
||||
/**
|
||||
* @covers StreamHttpClient
|
||||
* @covers StreamHttpRequest
|
||||
* @covers StreamHttpResponse
|
||||
* @uses AuthorizedHttpClient
|
||||
* @uses FlashiiClient
|
||||
* @uses BasicCredentials
|
||||
* @uses BearerCredentials
|
||||
*/
|
||||
final class StreamHttpTest extends TestCase {
|
||||
public function testHttpGet(): void {
|
||||
$client = new StreamHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$request = $client->createRequest('GET', 'https://httpbin.org/get?alreadyhere=true');
|
||||
$request->setHeader('X-Test', 'meow');
|
||||
$request->setHeader('X-Test2', 'mewow');
|
||||
$request->removeHeader('x-test2');
|
||||
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertTrue($response->hasHeader('Content-Type'));
|
||||
$this->assertEquals('application/json', $response->getHeader('Content-type'));
|
||||
$this->assertTrue($response->hasBody());
|
||||
|
||||
$body = $response->getJsonBody();
|
||||
$this->assertIsObject($body);
|
||||
|
||||
$body = $response->getJsonBody(true);
|
||||
$this->assertIsArray($body);
|
||||
$this->assertArrayHasKey('headers', $body);
|
||||
$this->assertIsArray($body['headers']);
|
||||
$this->assertArrayHasKey('X-Test', $body['headers']);
|
||||
$this->assertEquals('meow', $body['headers']['X-Test']);
|
||||
$this->assertArrayNotHasKey('X-Test2', $body['headers']);
|
||||
|
||||
$this->assertArrayHasKey('args', $body);
|
||||
$this->assertIsArray($body['args']);
|
||||
$this->assertArrayHasKey('alreadyhere', $body['args']);
|
||||
$this->assertEquals('true', $body['args']['alreadyhere']);
|
||||
}
|
||||
|
||||
public function testHttpFailStatus(): void {
|
||||
$client = new StreamHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$request = $client->createRequest('GET', 'https://httpbin.org/status/404');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
|
||||
$request = $client->createRequest('POST', 'https://httpbin.org/status/503');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertEquals(503, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testEnsureSuccess(): void {
|
||||
$client = new StreamHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$request = $client->createRequest('PATCH', 'https://httpbin.org/status/204');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertTrue($response->isSuccessStatusCode());
|
||||
|
||||
$response->ensureSuccessStatusCode();
|
||||
|
||||
$request = $client->createRequest('DELETE', 'https://httpbin.org/status/418');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertFalse($response->isSuccessStatusCode());
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('HTTP request returned status code 418.');
|
||||
$response->ensureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public function testAuthorizedHttp(): void {
|
||||
$realClient = new StreamHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$client = new AuthorizedHttpClient($realClient, new BasicCredentials('freakyfurball', 'express1'));
|
||||
$this->assertFalse(
|
||||
$realClient->sendRequest(
|
||||
$realClient->createRequest('GET', 'https://httpbin.org/basic-auth/freakyfurball/express1')
|
||||
)->isSuccessStatusCode()
|
||||
);
|
||||
$this->assertTrue(
|
||||
$client->sendRequest(
|
||||
$client->createRequest('GET', 'https://httpbin.org/basic-auth/freakyfurball/express1')
|
||||
)->isSuccessStatusCode()
|
||||
);
|
||||
$this->assertFalse(
|
||||
$client->sendRequest(
|
||||
$client->createRequest('GET', 'https://httpbin.org/basic-auth/freakyfurball/express2')
|
||||
)->isSuccessStatusCode()
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$realClient->sendRequest(
|
||||
$realClient->createRequest('GET', 'https://httpbin.org/bearer')
|
||||
)->isSuccessStatusCode()
|
||||
);
|
||||
|
||||
$accessToken = 'DSw7ih2fMfXW3fAkUKJitltBtc7JT8TA';
|
||||
$client = new AuthorizedHttpClient($realClient, new BearerCredentials($accessToken));
|
||||
$response = $client->sendRequest(
|
||||
$client->createRequest('GET', 'https://httpbin.org/bearer')
|
||||
);
|
||||
$this->assertTrue($response->hasBody());
|
||||
|
||||
$body = $response->getJsonBody(true);
|
||||
$this->assertIsArray($body);
|
||||
|
||||
$this->assertArrayHasKey('authenticated', $body);
|
||||
$this->assertIsBool($body['authenticated']);
|
||||
$this->assertTrue($body['authenticated']);
|
||||
|
||||
$this->assertArrayHasKey('token', $body);
|
||||
$this->assertIsString($body['token']);
|
||||
$this->assertEquals($accessToken, $body['token']);
|
||||
}
|
||||
|
||||
public function testHttpBody(): void {
|
||||
$client = new StreamHttpClient(FlashiiClient::userAgentString());
|
||||
|
||||
$random = base64_encode(random_bytes(100));
|
||||
$request = $client->createRequest('PUT', 'https://httpbin.org/anything');
|
||||
$request->setBody($random, 'application/x-test-data');
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertTrue($response->hasBody());
|
||||
|
||||
$body = $response->getJsonBody(true);
|
||||
$this->assertIsArray($body);
|
||||
|
||||
$this->assertArrayHasKey('method', $body);
|
||||
$this->assertIsString($body['method']);
|
||||
$this->assertEquals('PUT', $body['method']);
|
||||
|
||||
$this->assertArrayHasKey('headers', $body);
|
||||
$this->assertIsArray($body['headers']);
|
||||
$this->assertArrayHasKey('Content-Type', $body['headers']);
|
||||
$this->assertIsString($body['headers']['Content-Type']);
|
||||
$this->assertEquals('application/x-test-data', $body['headers']['Content-Type']);
|
||||
|
||||
$this->assertArrayHasKey('data', $body);
|
||||
$this->assertIsString($body['data']);
|
||||
$this->assertEquals($random, $body['data']);
|
||||
|
||||
$request = $client->createRequest('POST', 'https://httpbin.org/post');
|
||||
$request->setBodyParams([
|
||||
'string' => 'meow',
|
||||
'more' => 'wowzers this one has spaces crazy',
|
||||
]);
|
||||
$response = $client->sendRequest($request);
|
||||
$this->assertTrue($response->hasBody());
|
||||
|
||||
$body = $response->getJsonBody(true);
|
||||
$this->assertIsArray($body);
|
||||
|
||||
$this->assertArrayHasKey('headers', $body);
|
||||
$this->assertIsArray($body['headers']);
|
||||
$this->assertArrayHasKey('Content-Type', $body['headers']);
|
||||
$this->assertIsString($body['headers']['Content-Type']);
|
||||
$this->assertEquals('application/x-www-form-urlencoded', $body['headers']['Content-Type']);
|
||||
|
||||
$this->assertArrayHasKey('form', $body);
|
||||
$this->assertIsArray($body['form']);
|
||||
$this->assertArrayHasKey('string', $body['form']);
|
||||
$this->assertIsString($body['form']['string']);
|
||||
$this->assertEquals('meow', $body['form']['string']);
|
||||
$this->assertArrayHasKey('more', $body['form']);
|
||||
$this->assertIsString($body['form']['more']);
|
||||
$this->assertEquals('wowzers this one has spaces crazy', $body['form']['more']);
|
||||
}
|
||||
}
|
|
@ -29,6 +29,9 @@ function git_changes(): array {
|
|||
}
|
||||
|
||||
function collect_files(string $directory): array {
|
||||
if(!is_dir($directory))
|
||||
return [];
|
||||
|
||||
$files = [];
|
||||
$dir = glob(realpath($directory) . DIRECTORY_SEPARATOR . '*');
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue