diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27cc5d8..5ad4b33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,21 +7,21 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache Composer dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: /tmp/composer-cache key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} - - uses: php-actions/composer@v5 + - uses: php-actions/composer@v6 - name: Archive build run: mkdir /tmp/github-actions/ && tar -cvf /tmp/github-actions/build.tar ./ - name: Upload build archive for test runners - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: build-artifact path: /tmp/github-actions @@ -31,7 +31,7 @@ jobs: needs: [composer] steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: build-artifact path: /tmp/github-actions @@ -40,9 +40,9 @@ jobs: run: tar -xvf /tmp/github-actions/build.tar ./ - name: PHP Unit tests - uses: php-actions/phpunit@v2 + uses: php-actions/phpunit@v3 with: - php_version: 8.0 + php_version: 8.1 php_extensions: xdebug configuration: test/phpunit/phpunit.xml bootstrap: vendor/autoload.php @@ -52,7 +52,7 @@ jobs: needs: [composer] steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: build-artifact path: /tmp/github-actions @@ -61,6 +61,6 @@ jobs: run: tar -xvf /tmp/github-actions/build.tar ./ - name: PHP Static Analysis - uses: php-actions/phpstan@v2 + uses: php-actions/phpstan@v3 with: - path: src/ \ No newline at end of file + path: src/ diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 0e7d790..4d528e7 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -1,6 +1,6 @@ build: environment: - php: 8.0.0 + php: 8.1.0 nodes: analysis: @@ -29,4 +29,4 @@ checks: filter: excluded_paths: - test/* - - vendor/* \ No newline at end of file + - vendor/* diff --git a/broken.json b/broken.json new file mode 100644 index 0000000..b33f44e --- /dev/null +++ b/broken.json @@ -0,0 +1,4 @@ +{ + "name": "Some broken JSON", + "description" "This JSON is not correctly formed"; +} diff --git a/composer.json b/composer.json index 7f46fc1..68a6fa7 100644 --- a/composer.json +++ b/composer.json @@ -10,15 +10,15 @@ }, "require": { - "php": ">=7.2", + "php": ">=8.1", "ext-curl": "*", "ext-json": "*", - "phpgt/http": "*", - "phpgt/curl": "*", - - "php-http/httplug": "^2.0", - "react/promise": "2.*", - "react/event-loop": "^1.0" + "phpgt/async": "dev-master", + "phpgt/promise": "dev-master as v2.0.0", + "phpgt/http": "^v1.1", + "phpgt/curl": "^v3.0", + "phpgt/json": "^v1.1", + "php-http/httplug": "^2.3" }, "require-dev": { diff --git a/composer.lock b/composer.lock index cdc144f..ca82aa5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5fcbec4092bc74898fd585f50ffc5c30", + "content-hash": "68177928c08746bd1857e011a9574aee", "packages": [ { "name": "php-http/httplug", @@ -125,28 +125,76 @@ }, "time": "2020-07-07T09:29:14+00:00" }, + { + "name": "phpgt/async", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/Async.git", + "reference": "3d2bdeca8cafc8573b416da3ac591d5d88f6dea9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/Async/zipball/3d2bdeca8cafc8573b416da3ac591d5d88f6dea9", + "reference": "3d2bdeca8cafc8573b416da3ac591d5d88f6dea9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=7.4", + "phpgt/promise": "^2.0" + }, + "require-dev": { + "phpstan/phpstan": "^v1.8", + "phpunit/phpunit": "^v9.5" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\Async\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promise-based non-blocking operations.", + "support": { + "issues": "https://github.com/PhpGt/Async/issues", + "source": "https://github.com/PhpGt/Async/tree/master" + }, + "funding": [ + { + "url": "https://github.com/phpgt", + "type": "github" + } + ], + "time": "2023-01-19T11:11:58+00:00" + }, { "name": "phpgt/curl", - "version": "v3.0.4", + "version": "v3.0.5", "source": { "type": "git", "url": "https://github.com/PhpGt/Curl.git", - "reference": "50436aa457d9ea508499b2f32b032b1882262efd" + "reference": "c5fde51ee627dab553c95d7f564b8ec71ee9df6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Curl/zipball/50436aa457d9ea508499b2f32b032b1882262efd", - "reference": "50436aa457d9ea508499b2f32b032b1882262efd", + "url": "https://api.github.com/repos/PhpGt/Curl/zipball/c5fde51ee627dab553c95d7f564b8ec71ee9df6d", + "reference": "c5fde51ee627dab553c95d7f564b8ec71ee9df6d", "shasum": "" }, "require": { "ext-curl": "*", "ext-json": "*", - "php": ">=7.2.0" + "php": ">=7.4", + "phpgt/json": "^v1.0" }, "require-dev": { - "phpstan/phpstan": ">=0.12.64", - "phpunit/phpunit": "9.*" + "phpstan/phpstan": "^v1.8", + "phpunit/phpunit": "^v9.5" }, "type": "library", "autoload": { @@ -173,7 +221,59 @@ ], "support": { "issues": "https://github.com/PhpGt/Curl/issues", - "source": "https://github.com/PhpGt/Curl/tree/v3.0.4" + "source": "https://github.com/PhpGt/Curl/tree/v3.0.5" + }, + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2022-09-07T21:39:47+00:00" + }, + { + "name": "phpgt/dataobject", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/PhpGt/DataObject.git", + "reference": "dde4c5ba9e4cd0e048e4558107da32ce3f9ce7e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhpGt/DataObject/zipball/dde4c5ba9e4cd0e048e4558107da32ce3f9ce7e4", + "reference": "dde4c5ba9e4cd0e048e4558107da32ce3f9ce7e4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.0", + "phpgt/typesafegetter": "^1.0" + }, + "require-dev": { + "phpstan/phpstan": "^v1.8", + "phpunit/phpunit": "^v9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gt\\DataObject\\": "./src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" + } + ], + "description": " Structured, type-safe, immutable data transfer.", + "support": { + "issues": "https://github.com/PhpGt/DataObject/issues", + "source": "https://github.com/PhpGt/DataObject/tree/v1.0.4" }, "funding": [ { @@ -181,31 +281,31 @@ "type": "github" } ], - "time": "2020-12-24T19:03:28+00:00" + "time": "2022-09-15T09:21:05+00:00" }, { "name": "phpgt/http", - "version": "v1.1.4", + "version": "v1.1.7", "source": { "type": "git", "url": "https://github.com/PhpGt/Http.git", - "reference": "80ed7269050bc0f2de2402c5461711dd2418f55f" + "reference": "6d32a4cbf3efea655b3204591501fc84721b00ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Http/zipball/80ed7269050bc0f2de2402c5461711dd2418f55f", - "reference": "80ed7269050bc0f2de2402c5461711dd2418f55f", + "url": "https://api.github.com/repos/PhpGt/Http/zipball/6d32a4cbf3efea655b3204591501fc84721b00ca", + "reference": "6d32a4cbf3efea655b3204591501fc84721b00ca", "shasum": "" }, "require": { - "php": ">=8.0", + "php": ">=8.1", "phpgt/input": "^v1", "psr/http-message": "^v1.0.1", - "willdurand/negotiation": "v3.0.*" + "willdurand/negotiation": "v3.1.0" }, "require-dev": { - "phpstan/phpstan": ">=0.12.64", - "phpunit/phpunit": "9.*" + "phpstan/phpstan": "^v1.8", + "phpunit/phpunit": "^v9.5" }, "type": "library", "autoload": { @@ -220,7 +320,7 @@ "description": "PSR-7 HTTP message implementation.", "support": { "issues": "https://github.com/PhpGt/Http/issues", - "source": "https://github.com/PhpGt/Http/tree/v1.1.4" + "source": "https://github.com/PhpGt/Http/tree/v1.1.7" }, "funding": [ { @@ -228,28 +328,28 @@ "type": "github" } ], - "time": "2021-06-23T15:45:09+00:00" + "time": "2022-09-28T16:04:19+00:00" }, { "name": "phpgt/input", - "version": "v1.2.0", + "version": "v1.2.1", "source": { "type": "git", "url": "https://github.com/PhpGt/Input.git", - "reference": "95136bcbe1c50c3d8db90eb3b1944d049ec2b2a0" + "reference": "fbcd56090e6b69050ebf585e31aa3b8decbbfa0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Input/zipball/95136bcbe1c50c3d8db90eb3b1944d049ec2b2a0", - "reference": "95136bcbe1c50c3d8db90eb3b1944d049ec2b2a0", + "url": "https://api.github.com/repos/PhpGt/Input/zipball/fbcd56090e6b69050ebf585e31aa3b8decbbfa0a", + "reference": "fbcd56090e6b69050ebf585e31aa3b8decbbfa0a", "shasum": "" }, "require": { - "php": ">=8.0", + "php": ">=8.1", "phpgt/http": "^v1.1" }, "require-dev": { - "phpstan/phpstan": "^v1.4", + "phpstan/phpstan": "^v1.8", "phpunit/phpunit": "^v9.5" }, "type": "library", @@ -265,7 +365,7 @@ "description": "Encapsulated user input.", "support": { "issues": "https://github.com/PhpGt/Input/issues", - "source": "https://github.com/PhpGt/Input/tree/v1.2.0" + "source": "https://github.com/PhpGt/Input/tree/v1.2.1" }, "funding": [ { @@ -273,35 +373,35 @@ "type": "github" } ], - "time": "2022-03-18T19:03:02+00:00" + "time": "2022-09-27T10:37:03+00:00" }, { - "name": "psr/http-client", - "version": "1.0.1", + "name": "phpgt/json", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + "url": "https://github.com/PhpGt/Json.git", + "reference": "26b3972a9d95b8d7d2985841f3b69d8876fc7c38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "url": "https://api.github.com/repos/PhpGt/Json/zipball/26b3972a9d95b8d7d2985841f3b69d8876fc7c38", + "reference": "26b3972a9d95b8d7d2985841f3b69d8876fc7c38", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" + "ext-json": "*", + "php": ">=8.1", + "phpgt/dataobject": "^1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "phpstan/phpstan": "^v1.8", + "phpunit/phpunit": "^v9.5" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Client\\": "src/" + "Gt\\Json\\": "./src" } }, "notification-url": "https://packagist.org/downloads/", @@ -310,105 +410,105 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" } ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], + "description": " Structured, type-safe, immutable JSON objects.", "support": { - "source": "https://github.com/php-fig/http-client/tree/master" + "issues": "https://github.com/PhpGt/Json/issues", + "source": "https://github.com/PhpGt/Json/tree/v1.2.0" }, - "time": "2020-06-29T06:28:15+00:00" + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2022-09-15T11:31:25+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "phpgt/promise", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/PhpGt/Promise.git", + "reference": "402b90d8bd688a7f09107c0825087c3f0f5cf54b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/PhpGt/Promise/zipball/402b90d8bd688a7f09107c0825087c3f0f5cf54b", + "reference": "402b90d8bd688a7f09107c0825087c3f0f5cf54b", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.1", + "php-http/httplug": "^2.2.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "phpstan/phpstan": "1.8.0", + "phpunit/phpunit": "9.5.21" }, + "default-branch": true, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Gt\\Promise\\": "./src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "Pleasantly work with asynchronous code.", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "W3C", + "async", + "asynchronous", + "callback", + "concurrency", + "concurrent", + "deferred", + "promise", + "then" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "issues": "https://github.com/PhpGt/Promise/issues", + "source": "https://github.com/PhpGt/Promise/tree/master" }, - "time": "2016-08-06T14:39:51+00:00" + "funding": [ + { + "url": "https://github.com/sponsors/PhpGt", + "type": "github" + } + ], + "time": "2023-01-19T19:04:28+00:00" }, { - "name": "react/event-loop", - "version": "v1.3.0", + "name": "phpgt/typesafegetter", + "version": "v1.2.4", "source": { "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "187fb56f46d424afb6ec4ad089269c72eec2e137" + "url": "https://github.com/PhpGt/TypeSafeGetter.git", + "reference": "ebffd758e69b8a0eebcad30f3daf408915b9ddf3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/187fb56f46d424afb6ec4ad089269c72eec2e137", - "reference": "187fb56f46d424afb6ec4ad089269c72eec2e137", + "url": "https://api.github.com/repos/PhpGt/TypeSafeGetter/zipball/ebffd758e69b8a0eebcad30f3daf408915b9ddf3", + "reference": "ebffd758e69b8a0eebcad30f3daf408915b9ddf3", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "suggest": { - "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop", - "ext-uv": "* for ExtUvLoop" + "phpstan/phpstan": "v1.8.0", + "phpunit/phpunit": "v9.5.21" }, "type": "library", "autoload": { "psr-4": { - "React\\EventLoop\\": "src" + "Gt\\TypeSafeGetter\\": "./src" } }, "notification-url": "https://packagist.org/downloads/", @@ -417,74 +517,50 @@ ], "authors": [ { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "name": "Greg Bowler", + "email": "greg.bowler@g105b.com" } ], - "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", - "keywords": [ - "asynchronous", - "event-loop" - ], + "description": "An interface for objects that expose type-safe getter methods.", "support": { - "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.3.0" + "issues": "https://github.com/PhpGt/TypeSafeGetter/issues", + "source": "https://github.com/PhpGt/TypeSafeGetter/tree/v1.2.4" }, "funding": [ { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", + "url": "https://github.com/sponsors/PhpGt", "type": "github" } ], - "time": "2022-03-17T11:10:22+00:00" + "time": "2022-07-08T17:17:42+00:00" }, { - "name": "react/promise", - "version": "v2.9.0", + "name": "psr/http-client", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "234f8fd1023c9158e2314fa9d7d0e6a83db42910" + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/234f8fd1023c9158e2314fa9d7d0e6a83db42910", - "reference": "234f8fd1023c9158e2314fa9d7d0e6a83db42910", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "shasum": "" }, "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { - "React\\Promise\\": "src/" + "Psr\\Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -493,59 +569,88 @@ ], "authors": [ { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", "keywords": [ - "promise", - "promises" + "http", + "http-client", + "psr", + "psr-18" ], "support": { - "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v2.9.0" + "source": "https://github.com/php-fig/http-client/tree/master" }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.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": [ { - "url": "https://github.com/clue", - "type": "github" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "time": "2022-02-11T10:27:51+00:00" + "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/master" + }, + "time": "2016-08-06T14:39:51+00:00" }, { "name": "willdurand/negotiation", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/willdurand/Negotiation.git", - "reference": "04e14f38d4edfcc974114a07d2777d90c98f3d9c" + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/04e14f38d4edfcc974114a07d2777d90c98f3d9c", - "reference": "04e14f38d4edfcc974114a07d2777d90c98f3d9c", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", "shasum": "" }, "require": { @@ -586,38 +691,38 @@ ], "support": { "issues": "https://github.com/willdurand/Negotiation/issues", - "source": "https://github.com/willdurand/Negotiation/tree/3.0.0" + "source": "https://github.com/willdurand/Negotiation/tree/3.1.0" }, - "time": "2020-09-25T08:01:41+00:00" + "time": "2022-01-30T20:08:53+00:00" } ], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.4.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -644,7 +749,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -660,7 +765,7 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", @@ -723,16 +828,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.14.0", + "version": "v4.15.3", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", "shasum": "" }, "require": { @@ -773,9 +878,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" }, - "time": "2022-05-31T20:59:12+00:00" + "time": "2023-01-16T22:05:37+00:00" }, { "name": "phar-io/manifest", @@ -1000,25 +1105,30 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.1", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" + "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d", + "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { "ext-tokenizer": "*", - "psalm/phar": "^4.8" + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { @@ -1044,27 +1154,27 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2" }, - "time": "2022-03-15T21:29:03+00:00" + "time": "2022-10-14T12:47:21+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.15.0", + "version": "v1.16.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" + "reference": "be8cac52a0827776ff9ccda8c381ac5b71aeb359" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be8cac52a0827776ff9ccda8c381ac5b71aeb359", + "reference": "be8cac52a0827776ff9ccda8c381ac5b71aeb359", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" @@ -1111,9 +1221,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" + "source": "https://github.com/phpspec/prophecy/tree/v1.16.0" }, - "time": "2021-12-08T12:19:24+00:00" + "time": "2022-11-29T15:06:56+00:00" }, { "name": "phpstan/phpstan", @@ -1176,23 +1286,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.15", + "version": "9.2.23", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" + "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", + "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.13.0", + "nikic/php-parser": "^4.14", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -1241,7 +1351,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.23" }, "funding": [ { @@ -1249,7 +1359,7 @@ "type": "github" } ], - "time": "2022-03-07T09:28:20+00:00" + "time": "2022-12-28T12:41:10+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1763,16 +1873,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -1825,7 +1935,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { @@ -1833,7 +1943,7 @@ "type": "github" } ], - "time": "2020-10-26T15:49:45+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", @@ -2023,16 +2133,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "shasum": "" }, "require": { @@ -2088,7 +2198,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" }, "funding": [ { @@ -2096,7 +2206,7 @@ "type": "github" } ], - "time": "2021-11-11T14:18:36+00:00" + "time": "2022-09-14T06:03:37+00:00" }, { "name": "sebastian/global-state", @@ -2451,16 +2561,16 @@ }, { "name": "sebastian/type", - "version": "3.0.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", "shasum": "" }, "require": { @@ -2472,7 +2582,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2495,7 +2605,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.0.0" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" }, "funding": [ { @@ -2503,7 +2613,7 @@ "type": "github" } ], - "time": "2022-03-15T09:54:48+00:00" + "time": "2022-09-12T14:47:03+00:00" }, { "name": "sebastian/version", @@ -2667,13 +2777,23 @@ "time": "2022-06-03T18:03:27+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "phpgt/promise", + "version": "9999999-dev", + "alias": "v2.0.0", + "alias_normalized": "2.0.0.0" + } + ], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "phpgt/async": 20, + "phpgt/promise": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.2", + "php": ">=8.1", "ext-curl": "*", "ext-json": "*" }, diff --git a/example/01-basic-fetch.php b/example/01-basic-fetch.php index 408e373..5af6871 100644 --- a/example/01-basic-fetch.php +++ b/example/01-basic-fetch.php @@ -2,33 +2,35 @@ require(implode(DIRECTORY_SEPARATOR, ["..", "vendor", "autoload.php"])); use Gt\Fetch\Http; +use Gt\Fetch\Response\Blob; use Gt\Fetch\Response\BodyResponse; -use Gt\Fetch\Response\Json; - -/* - * This example fetches the list of repositories in the PhpGt organisation from - * Github's public API. - */ +use Gt\Json\JsonKvpObject; +use Gt\Json\JsonPrimitive\JsonArrayPrimitive; $http = new Http(); + $http->fetch("https://api.github.com/orgs/phpgt/repos") -->then(function(BodyResponse $response) { - if(!$response->ok) { - echo "Error fetching Github's API."; - exit(1); - } + ->then(function(BodyResponse $response) { + if(!$response->ok) { + throw new RuntimeException("Can't retrieve Github's API on $response->uri"); + } - return $response->json(); -}) -->then(function(Json $json) { -// $json is a pre-decoded object. Expected response is an array of Repositories, -// as per https://developer.github.com/v3/repos/#list-organization-repositories - echo "PHP.Gt repository list:" . PHP_EOL; + return $response->json(); + }) + ->then(function(JsonArrayPrimitive $json) { + echo "SUCCESS: Json promise resolved!", PHP_EOL; + echo "PHP.Gt has the following repositories: "; + $repoList = []; + /** @var JsonKvpObject $item */ + foreach($json->getPrimitiveValue() as $item) { + array_push($repoList, $item->getString("name")); + } - foreach($json as $repo) { - echo $repo->getString("name") . PHP_EOL; - } -}); + echo wordwrap(implode(", ", $repoList)) . "."; + echo PHP_EOL, PHP_EOL; + }) + ->catch(function(Throwable $reason) { + echo "ERROR: ", PHP_EOL, $reason->getMessage(), PHP_EOL, PHP_EOL; + }); -// To execute the above Promise(s), call wait() or all(). -$http->wait(); \ No newline at end of file +$http->wait(); diff --git a/example/02-post-request.php b/example/02-post-request.php index d4e5368..39a1538 100644 --- a/example/02-post-request.php +++ b/example/02-post-request.php @@ -3,7 +3,7 @@ use Gt\Fetch\Http; use Gt\Fetch\Response\BodyResponse; -use Gt\Fetch\Response\Json; +use Gt\Json\JsonObject; /* * This example uses postman-echo.com do test the request/response. @@ -26,32 +26,33 @@ "email" => "zuck@fb.com", ]), ]) -->then(function(BodyResponse $response) { - if(!$response->ok) { - echo "Error posting to Postman Echo." . PHP_EOL; - exit(1); - } + ->then(function(BodyResponse $response) { + if(!$response->ok) { + throw new RuntimeException("Error posting to Postman Echo."); + } // Postman Echo servers respond with a JSON representation of the request // that was received. - return $response->json(); -}) -->then(function(Json $json) { - echo "The Postman Echo server received the following form fields:"; - echo PHP_EOL; + return $response->json(); + }) + ->then(function(JsonObject $json) { + echo "The Postman Echo server received the following form fields:"; + echo PHP_EOL; - foreach($json->form as $key => $value) { - echo "$key = $value" . PHP_EOL; - } + $formObject = $json->getObject("form"); + foreach($formObject->asArray() as $key => $value) { + echo "$key = $value" . PHP_EOL; + } - $firstName = strtok($json->form->getString("name"), " "); - $dob = $json->form->getDateTime("dob"); - $age = date("Y") - $dob->format("Y"); - echo PHP_EOL; - echo "$firstName is $age years old!" . PHP_EOL; -}, function($error) { - var_dump($error); -}); + $firstName = strtok($formObject->getString("name"), " "); + $dob = $formObject->getDateTime("dob"); + $age = date("Y") - $dob->format("Y"); + echo PHP_EOL; + echo "$firstName is $age years old!" . PHP_EOL; + }) + ->catch(function(Throwable $error) { + echo "An error occurred: ", $error->getMessage(); + }); // To execute the above Promise(s), call wait() or all(). $http->wait(); -die("done waiting"); \ No newline at end of file +die("done waiting"); diff --git a/example/03-download-jpg.php b/example/03-download-jpg.php index 20f0bca..4b9a64b 100644 --- a/example/03-download-jpg.php +++ b/example/03-download-jpg.php @@ -26,8 +26,6 @@ return $response->blob(); }) ->then(function(Blob $blob) { - $extension = null; - switch($blob->type) { case "image/jpeg": $extension = "jpg"; @@ -49,4 +47,4 @@ }); // To execute the above Promise(s), call wait() or all(). -$http->wait(); \ No newline at end of file +$http->wait(); diff --git a/example/04-multiple-concurrent-fetch.php b/example/04-multiple-concurrent-fetch.php new file mode 100644 index 0000000..9dff4e8 --- /dev/null +++ b/example/04-multiple-concurrent-fetch.php @@ -0,0 +1,57 @@ +ok) { + throw new RuntimeException("Can't retrieve Github's API on $response->uri"); + } + + return $response->json(); +}; +$listReposFromJson = function(JsonArrayPrimitive $json) { + echo "SUCCESS: Json promise resolved!", PHP_EOL; + echo "PHP.Gt has the following repositories: "; + $repoList = []; + /** @var JsonKvpObject $item */ + foreach($json->getPrimitiveValue() as $item) { + array_push($repoList, $item->getString("name")); + } + + echo wordwrap(implode(", ", $repoList)) . "."; + echo PHP_EOL, PHP_EOL; +}; +$errorHandler = function(Throwable $reason) { + echo "ERROR: ", PHP_EOL, $reason->getMessage(), PHP_EOL, PHP_EOL; +}; + +$http = new Http(); + +$http->fetch("https://api.github.com/orgs/phpgt/repos") + ->then($getJsonFromResponse) + ->then($listReposFromJson) + ->catch($errorHandler); + +$http->fetch("https://github.com/phpgt/__this-does-not-exist") + ->then($getJsonFromResponse) + ->then($listReposFromJson) + ->catch($errorHandler); + +$http->fetch("https://cataas.com/cat") + ->then(function(BodyResponse $response) { + return $response->blob(); + }) + ->then(function(Blob $blob) { + $file = new SplFileObject("/tmp/cat", "w"); + $bytesWritten = $file->fwrite($blob); + echo "Written $bytesWritten bytes.", PHP_EOL; + echo "Type: $blob->type", PHP_EOL; + echo "Photo written to ", $file->getPathname(), PHP_EOL, PHP_EOL; + }); + +$http->wait(); diff --git a/src/AbortController.php b/src/AbortController.php index 361162c..aa5850e 100644 --- a/src/AbortController.php +++ b/src/AbortController.php @@ -2,8 +2,8 @@ namespace Gt\Fetch; class AbortController extends Controller { - public $signal; - public $aborted; + public self $signal; + public bool $aborted; public function __construct() { $this->signal = $this; @@ -13,4 +13,4 @@ public function __construct() { public function abort():void { $this->aborted = true; } -} \ No newline at end of file +} diff --git a/src/CurlOptBuilder.php b/src/CurlOptBuilder.php index 9371c71..5666d47 100644 --- a/src/CurlOptBuilder.php +++ b/src/CurlOptBuilder.php @@ -3,6 +3,7 @@ use Gt\Http\RequestMethod; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\UriInterface; /** * Converts a Fetch Init array to CURLOPT_* key-values. @@ -10,11 +11,16 @@ * @see https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax */ class CurlOptBuilder { - protected $curlOptArray; - protected $integrity; - protected $signal; - - public function __construct($input, array $init = []) { + /** @var array */ + protected array $curlOptArray; + protected ?string $integrity; + protected ?Controller $signal; + + /** @param array $init */ + public function __construct( + null|string|UriInterface|RequestInterface $input, + array $init = [], + ) { $this->curlOptArray = []; if($input instanceof RequestInterface) { @@ -32,11 +38,12 @@ public function __construct($input, array $init = []) { } } + /** @return array */ public function asCurlOptArray():array { return $this->curlOptArray; } - protected function fromRequestObject(RequestInterface $request) { + protected function fromRequestObject(RequestInterface $request):void { $this->curlOptArray[CURLOPT_URL] = (string)$request->getUri(); if($method = $request->getMethod()) { @@ -46,17 +53,15 @@ protected function fromRequestObject(RequestInterface $request) { $this->setHeaders($request->getHeaders()); } - /** - * @param string $value The request method, e.g., GET, POST. - */ - protected function setMethod(string $value) { + /** @param string $value The request method, e.g., GET, POST. */ + protected function setMethod(string $value):void { $this->curlOptArray[CURLOPT_CUSTOMREQUEST] = RequestMethod::filterMethodName($value); } /** - * @param array $headers Any headers you want to add to your request, - * contained within an associative array. + * @param array $headers Any headers you want + * to add to your request, contained within an associative array. */ protected function setHeaders(array $headers):void { $rawHeaders = []; @@ -75,10 +80,11 @@ protected function setHeaders(array $headers):void { } /** - * @param string $body Any body that you want to add to your request: - * this can be a string, associative array (form data) or binary object. + * @param string|array $body Any body that you want to + * add to your request: this can be a string, associative array + * (representing form data) or binary object. */ - protected function setBody($body):void { + protected function setBody(string|array $body):void { $this->curlOptArray[CURLOPT_POSTFIELDS] = $body; } @@ -164,4 +170,4 @@ protected function setSignal(Controller $value):void { public function getSignal():?Controller { return $this->signal ?? null; } -} \ No newline at end of file +} diff --git a/src/Http.php b/src/Http.php index d76452f..f102fe1 100644 --- a/src/Http.php +++ b/src/Http.php @@ -1,63 +1,62 @@ "GET", CURLOPT_FOLLOWLOCATION => true, CURLOPT_USERAGENT => self::USER_AGENT, ]; - /** @var LoopInterface */ - protected $loop; - /** @var TimerInterface */ - protected $timer; + /** @var array */ + public readonly array $curlOptions; + private readonly float $interval; + private RequestResolver $requestResolver; + + private Loop $loop; + private Timer $timer; + + /** + * @param array $curlOptions + */ public function __construct( array $curlOptions = [], float $interval = 0.01, string $curlClass = Curl::class, string $curlMultiClass = CurlMulti::class ) { - $this->curlOptions = $curlOptions + $this->curlOptions; + $this->curlOptions = $curlOptions + self::DEFAULT_CURL_OPTIONS; $this->interval = $interval; - $this->loop = LoopFactory::create(); + $this->loop = new Loop(); $this->requestResolver = new RequestResolver( $this->loop, $curlClass, $curlMultiClass ); - $this->timer = $this->loop->addPeriodicTimer( - $this->interval, - [$this->requestResolver, "tick"] - ); + $this->timer = new PeriodicTimer($this->interval, true); + $this->timer->addCallback($this->requestResolver->tick(...)); + $this->loop->addTimer($this->timer); } - /** - * @interface HttpClient - */ + /** @interface HttpClient */ public function sendRequest( RequestInterface $request ):ResponseInterface { @@ -74,12 +73,10 @@ public function sendRequest( return $returnValue; } - /** - * @interface HttpAsyncClient - */ + /** @interface HttpAsyncClient */ public function sendAsyncRequest( RequestInterface $request - ):HttpPromise { + ):HttpPromiseInterface { return $this->fetch($request); } @@ -87,14 +84,14 @@ public function sendAsyncRequest( * Creates a new Deferred object to perform the resolution of the request and * returns a PSR-7 compatible promise that represents the result of the response * - * Long-hand for the GlobalFetchHelper get, head, post, etc. - * - * @param string|UriInterface|RequestInterface $input - * @param array $init + * @param array $init */ - public function fetch($input, array $init = []):HttpPromise { + public function fetch( + string|UriInterface|RequestInterface $input, + array $init = [] + ):Promise { $deferred = new Deferred(); - $deferredPromise = $deferred->promise(); + $deferredPromise = $deferred->getPromise(); $uri = $this->ensureUriInterface($input); @@ -113,25 +110,12 @@ public function fetch($input, array $init = []):HttpPromise { $curlOptBuilder->getSignal() ); - $newPromise = new Promise($this->loop); - - $deferredPromise->then(function(ResponseInterface $response) - use($newPromise) { - $newPromise->resolve($response); - }); - - return $newPromise; + return $deferredPromise; } - public function getCurlOptions():array { - return $this->curlOptions; - } - - /** - * @param string|UriInterface $input - * @return string - */ - public function ensureUriInterface($input):UriInterface { + public function ensureUriInterface( + string|UriInterface|RequestInterface $input + ):UriInterface { if($input instanceof RequestInterface) { $uri = $input->getUri(); } @@ -154,13 +138,15 @@ public function wait():void { * Begins execution of all promises, returning its own Promise that will * resolve when the last HTTP request is fully resolved. */ - public function all():HttpPromise { + public function all():HttpPromiseInterface { $start = microtime(true); $this->wait(); $end = microtime(true); - $promise = new Promise($this->loop); - $promise->resolve($end - $start); + $deferred = new Deferred(); + $this->loop->addDeferredToTimer($deferred); + $promise = $deferred->getPromise(); + $deferred->resolve($end - $start); return $promise; } -} \ No newline at end of file +} diff --git a/src/Promise.php b/src/Promise.php deleted file mode 100644 index 254840b..0000000 --- a/src/Promise.php +++ /dev/null @@ -1,146 +0,0 @@ -loop = $loop; - $this->state = self::PENDING; - } - - public function then( - callable $onFulfilled = null, - callable $onRejected = null - ):self { - $newPromise = new self($this->loop); - - if(is_null($onFulfilled)) { - $onFulfilled = function($resolvedValue) { - return $resolvedValue; - }; - } - if(is_null($onRejected)) { - $onRejected = function(Exception $exception) { - return $exception; - }; - } - - $this->onFulfilled = function($resolvedValue) - use($onFulfilled, $newPromise) { - try { - $return = $onFulfilled($resolvedValue); - - if($return instanceof HttpPromise) { - $return->then(function($innerResolvedValue) use($newPromise) { - $newPromise->resolve($innerResolvedValue); - }); - } - else { - $newPromise->resolve($return ?? $resolvedValue); - } - } - catch(Exception $exception) { - $newPromise->reject($exception); - } - }; - - $this->onRejected = function(Exception $exception) - use($onRejected, $newPromise) { - $return = $onRejected($exception); - $newPromise->reject($return); - }; - - if($this->getState() === self::FULFILLED) { - $this->doResolve($this->resolvedValue); - } - - if($this->getState() === self::REJECTED) { - $this->doReject($this->exception); - } - - return $newPromise; - } - - /** - * Returns the state of the promise, one of PENDING, FULFILLED or REJECTED. - * - * @return string - */ - public function getState() { - return $this->state; - } - - public function wait($unwrap = true) { - $loop = $this->loop; - $state = $this->getState(); - - while($state === self::PENDING) { - $loop->futureTick(function() use($loop) { - $loop->stop(); - }); - - $loop->run(); - } - - if($unwrap) { - if($state === self::REJECTED) { - throw $this->exception; - } - - return $this->resolvedValue; - } - - return null; - } - - public function resolve($resolvedValue):void { - if($this->getState() !== self::PENDING) { - throw new RuntimeException("Promise is already resolved"); - } - - $this->state = self::FULFILLED; - $this->resolvedValue = $resolvedValue; - $this->doResolve($resolvedValue); - } - - public function reject(Exception $exception) { - if($this->getState() !== self::PENDING) { - throw new RuntimeException("Promise is already resolved"); - } - - $this->state = self::REJECTED; - $this->exception = $exception; - $this->doReject($exception); - } - - protected function doResolve($resolvedValue):void { - $onFulfilled = $this->onFulfilled; - - if(!is_null($this->onFulfilled)) { - $onFulfilled($resolvedValue); - } - } - - protected function doReject(Exception $exception):void { - $onRejected = $this->onRejected; - - if(!is_null($this->onRejected)) { - $onRejected($exception); - } - } -} \ No newline at end of file diff --git a/src/RequestResolver.php b/src/RequestResolver.php index 5417690..bafedd4 100644 --- a/src/RequestResolver.php +++ b/src/RequestResolver.php @@ -1,58 +1,63 @@ */ + private array $curlMultiList; + /** @var array */ + private array $curlList; + /** @var array */ + private array $deferredList; + /** @var array */ + private array $responseList; + /** @var array */ + private array $headerList; + /** @var array */ + private array $integrityList; + /** @var array */ + private array $signalList; public function __construct( - LoopInterface $loop, - string $curlClass, - string $curlMultiClass + private readonly Loop $loop, + private readonly string $curlClass, + private readonly string $curlMultiClass, ) { - $this->loop = $loop; - $this->curlList = []; $this->curlMultiList = []; + $this->curlList = []; $this->deferredList = []; + $this->responseList = []; $this->headerList = []; - - $this->curlClass = $curlClass; - $this->curlMultiClass = $curlMultiClass; + $this->integrityList = []; + $this->signalList = []; } + /** + * Adds a new job to resolve. An HTTP request will be made to the + * provided UriInterface via curl_multi, using the provided options + * array. The Deferred object will be used to pass back the resolved + * BodyResponse, or rejected Throwable. + * The optional $integrity value will match the response's body with the + * provided hash as Subresource Identity (SRI). + * The optional Controller $signal is used internally by curl to provide + * an abort signal. + * @param array $curlOptArray + */ public function add( UriInterface $uri, array $curlOptArray, Deferred $deferred, string $integrity = null, - Controller $signal = null + Controller $signal = null, ):void { /** @var CurlInterface $curl */ $curl = new $this->curlClass($uri); @@ -68,27 +73,66 @@ public function add( // curlopt3: Finally, hard-code these curlopt settings: $curl->setOpt(CURLOPT_RETURNTRANSFER, false); $curl->setOpt(CURLOPT_HEADER, false); - $curl->setOpt(CURLOPT_HEADERFUNCTION, [$this, "writeHeader"]); - $curl->setOpt(CURLOPT_WRITEFUNCTION, [$this, "writeBody"]); - $curl->setOpt(CURLOPT_PROGRESSFUNCTION, [$this, "progress"]); + $curl->setOpt(CURLOPT_HEADERFUNCTION, $this->writeHeader(...)); + $curl->setOpt(CURLOPT_WRITEFUNCTION, $this->writeBody(...)); + $curl->setOpt(CURLOPT_PROGRESSFUNCTION, $this->progress(...)); $curl->setOpt(CURLOPT_NOPROGRESS, false); /** @var CurlMultiInterface $curlMulti */ $curlMulti = new $this->curlMultiClass(); $curlMulti->add($curl); - $this->curlList []= $curl; - $this->curlMultiList []= $curlMulti; - $this->deferredList []= $deferred; - $this->integrityList []= $integrity; + $this->loop->addDeferredToTimer($deferred); + $bodyResponse = new BodyResponse(); $bodyResponse->startDeferredResponse($this->loop, $curl); - $this->responseList []= $bodyResponse; - $this->headerList []= ""; - $this->signalList []= $signal; + + array_push($this->curlList, $curl); + array_push($this->curlMultiList, $curlMulti); + array_push($this->deferredList, $deferred); + array_push($this->integrityList, $integrity); + array_push($this->responseList, $bodyResponse); + array_push($this->headerList, ""); + array_push($this->signalList, $signal); + } + + public function tick():void { + $totalActive = 0; + + foreach($this->curlMultiList as $i => $curlMulti) { + $active = 0; + + do { + $status = $curlMulti->exec($active); + } + while($status === CURLM_CALL_MULTI_PERFORM); + + if($status !== CURLM_OK) { + // TODO: Throw exception. + die("ERROR!"); + } + + $totalActive += $active; + + if($active === 0) { + $this->responseList[$i]?->endDeferredResponse( + $this->integrityList[$i] + ); + if($this->deferredList[$i]) { + $this->deferredList[$i]->resolve($this->responseList[$i]); + } + + $this->responseList[$i] = null; + $this->deferredList[$i] = null; + } + } + + if($totalActive === 0) { + $this->loop->halt(); + } } - public function writeHeader($ch, string $rawHeader) { + private function writeHeader(CurlHandle|CurlInterface $ch, string $rawHeader):int { $i = $this->getIndex($ch); $headerLine = trim($rawHeader); @@ -123,7 +167,7 @@ public function writeHeader($ch, string $rawHeader) { return strlen($rawHeader); } - public function writeBody($ch, $content):int { + private function writeBody(CurlHandle|CurlInterface $ch, string $content):int { $i = $this->getIndex($ch); $body = $this->responseList[$i]->getBody(); @@ -134,67 +178,27 @@ public function writeBody($ch, $content):int { $this->deferredList[$i] = null; } -// To indiciate that this function has successfully run, cURL expects it to +// To indicate that this function has successfully run, cURL expects it to // return the number of bytes read. If this does not match the same number // that cURL sees, cURL will drop the connection. return strlen($content); } - public function progress( - $ch, + /** + * @noinspection PhpUnusedParameterInspection + */ + private function progress( + CurlHandle|CurlInterface $ch, int $expectedDownloadedBytes, int $downloadedBytes, int $expectedUploadedBytes, int $uploadedBytes ):int { $index = $this->getIndex($ch); - if($this->signalList[$index]) { - return (int)$this->signalList[$index]->aborted; - } - - return 0; - } - - public function tick():void { - $totalActive = 0; - - foreach($this->curlMultiList as $i => $curlMulti) { - $active = 0; - - do { - $status = $curlMulti->exec($active); - } - while($status === CURLM_CALL_MULTI_PERFORM); - - if($status !== CURLM_OK) { - // TODO: Throw exception. - die("ERROR!"); - } - - $totalActive += $active; - - if($active === 0) { - if($this->responseList[$i]) { - $this->responseList[$i]->endDeferredResponse( - $this->integrityList[$i] - ); - } - if($this->deferredList[$i]) { - $this->deferredList[$i]->resolve($this->responseList[$i]); - } - - $this->responseList[$i] = null; - $this->deferredList[$i] = null; - } - } - - if($totalActive === 0) { - $this->loop->stop(); - return; - } + return (int)$this->signalList[$index]?->aborted; } - protected function getIndex($chIncoming):int { + private function getIndex(CurlHandle|CurlInterface $chIncoming):int { $i = -1; $match = false; foreach($this->curlList as $i => $curl) { @@ -211,4 +215,4 @@ protected function getIndex($chIncoming):int { return $i; } -} \ No newline at end of file +} diff --git a/src/Response/ArrayBuffer.php b/src/Response/ArrayBuffer.php index c9fa49a..150ffc8 100644 --- a/src/Response/ArrayBuffer.php +++ b/src/Response/ArrayBuffer.php @@ -1,12 +1,23 @@ */ class ArrayBuffer extends SplFixedArray { + public function __get(string $name):mixed { + switch($name) { + case "byteLength": + return count($this); + } + + throw new RuntimeException("Undefined property: $name"); + } + public function transfer( self $oldBuffer, int $newByteLength = null @@ -17,4 +28,4 @@ public function transfer( public function slice(int $begin, int $end):self { return $this; } -} \ No newline at end of file +} diff --git a/src/Response/Blob.php b/src/Response/Blob.php index 49f5779..face25b 100644 --- a/src/Response/Blob.php +++ b/src/Response/Blob.php @@ -1,9 +1,11 @@ |ArrayBuffer|Blob|string $blobParts + * @param array $options + */ + public function __construct( + array|ArrayBuffer|self|string $blobParts, + array $options = [], + ) { $this->type = $options["type"] ?? ""; - $this->endings = $options["endings"] ?? self::ENDINGS_TRANSPARENT; +// $this->endings = $options["endings"] ?? self::ENDINGS_TRANSPARENT; $partType = $this->getBlobPartsType($blobParts); @@ -47,25 +55,26 @@ public function __toString():string { return $this->getContent(); } - public function __get(string $key) { - switch($key) { + public function __get(string $name):mixed { + switch($name) { case "size": return $this->size; - break; case "type": return $this->type; - break; } + + throw new RuntimeException("Undefined property: $name"); } public function getContent():string { return $this->content; } - protected function getBlobPartsType($blobParts):string { - $type = null; - + /** @param array|ArrayBuffer|Blob|string $blobParts */ + protected function getBlobPartsType( + array|ArrayBuffer|self|string $blobParts + ):string { if(is_array($blobParts)) { return self::PART_TYPE_ARRAY; } @@ -81,12 +90,9 @@ protected function getBlobPartsType($blobParts):string { if(is_string($blobParts)) { return self::PART_TYPE_STRING; } - - if(is_null($type)) { - throw new InvlidBlobPartTypeException($blobParts); - } } + /** @param array $input */ protected function loadArray(array $input):string { return $this->loadIterable($input); } @@ -103,21 +109,20 @@ protected function loadString(string $input):string { return $input; } + /** @param iterable $input */ protected function loadIterable(iterable $input):string { $buffer = ""; foreach($input as $i) { - if(self::ENDINGS_NATIVE) { - $i = str_replace( - ["\n", "\r\n"], - PHP_EOL, - $i - ); - } + $i = str_replace( + ["\n", "\r\n"], + PHP_EOL, + $i + ); $buffer .= $i; } return $buffer; } -} \ No newline at end of file +} diff --git a/src/Response/BodyResponse.php b/src/Response/BodyResponse.php index 9ba006b..f612345 100644 --- a/src/Response/BodyResponse.php +++ b/src/Response/BodyResponse.php @@ -1,18 +1,19 @@ getHeaders(); - break; + return $this->getResponseHeaders(); case "ok": - return ($this->statusCode >= 200 - && $this->statusCode < 300); - break; + return ($this->getStatusCode() >= 200 + && $this->getStatusCode() < 300); case "redirected": $redirectCount = $this->curl->getInfo( CURLINFO_REDIRECT_COUNT ); return $redirectCount > 0; - break; case "status": return $this->getStatusCode(); - break; case "statusText": return StatusCode::REASON_PHRASE[$this->status] ?? null; - break; case "uri": case "url": return $this->curl->getInfo(CURLINFO_EFFECTIVE_URL); - break; + + case "type": + return $this->headers->get("content-type")?->getValue() ?? ""; } throw new RuntimeException("Undefined property: $name"); } public function arrayBuffer():Promise { - $newPromise = new Promise($this->loop); + $newDeferred = new Deferred(); + $newPromise = $newDeferred->getPromise(); - $deferredPromise = $this->deferred->promise(); + $deferredPromise = $this->deferred->getPromise(); $deferredPromise->then(function(string $resolvedValue) - use($newPromise) { + use($newDeferred) { $bytes = strlen($resolvedValue); $arrayBuffer = new SplFixedArray($bytes); for($i = 0; $i < $bytes; $i++) { $arrayBuffer->offsetSet($i, ord($resolvedValue[$i])); } - $newPromise->resolve($arrayBuffer); + $newDeferred->resolve($arrayBuffer); }); return $newPromise; } public function blob():Promise { - $newPromise = new Promise($this->loop); - - $type = $this->getHeaderLine("Content-Type"); + $newDeferred = new Deferred(); - $deferredPromise = $this->deferred->promise(); - $deferredPromise->then(function(string $resolvedValue) - use($newPromise, $type) { - $newPromise->resolve( - new Blob($resolvedValue, [ - "type" => $type, - ]) - ); + $this->text()->then(function(string $text)use($newDeferred) { + $newDeferred->resolve(new Blob($text, [ + "type" => $this->type + ])); }); - return $newPromise; + return $newDeferred->getPromise(); } public function formData():Promise { - $newPromise = new Promise($this->loop); + $newDeferred = new Deferred(); + $newPromise = $newDeferred->getPromise(); - $deferredPromise = $this->deferred->promise(); + $deferredPromise = $this->deferred->getPromise(); $deferredPromise->then(function(string $resolvedValue) - use($newPromise) { + use($newDeferred) { parse_str($resolvedValue, $bodyData); - $newPromise->resolve($bodyData); + $newDeferred->resolve($bodyData); }); return $newPromise; } public function json(int $depth = 512, int $options = 0):Promise { - $newPromise = new Promise($this->loop); + $newDeferred = new Deferred(); + $newPromise = $newDeferred->getPromise(); - $deferredPromise = $this->deferred->promise(); + $deferredPromise = $this->deferred->getPromise(); $deferredPromise->then(function(string $resolvedValue) - use($newPromise, $depth, $options) { - $json = json_decode( - $resolvedValue, - false, - $depth, - $options - ); - if(is_null($json)) { - $errorMessage = json_last_error_msg(); - $exception = new JsonDecodeException($errorMessage); - $newPromise->reject($exception); + use($newDeferred, $depth, $options) { + $builder = new JsonObjectBuilder($depth, $options); + try { + $json = $builder->fromJsonString($resolvedValue); + $newDeferred->resolve($json); + } + catch(JsonDecodeException $exception) { + $newDeferred->reject($exception); } - - $newPromise->resolve(new Json($json)); }); return $newPromise; } public function text():Promise { - $newPromise = new Promise($this->loop); + $newDeferred = new Deferred(); + $newPromise = $newDeferred->getPromise(); - $deferredPromise = $this->deferred->promise(); - $deferredPromise->then(function(string $resolvedValue) - use($newPromise) { - $newPromise->resolve($resolvedValue); - }); + $this->deferred->getPromise() + ->then(function(string $html)use($newDeferred) { + $newDeferred->resolve($html); + }); return $newPromise; } public function startDeferredResponse( - LoopInterface $loop, + Loop $loop, CurlInterface $curl ):Deferred { $this->loop = $loop; @@ -179,15 +166,15 @@ public function endDeferredResponse(string $integrity = null):void { } public function deferredResponseStatus():?string { - return $this->deferredStatus; + return $this->deferredStatus ?? null; } - protected function checkIntegrity(?string $integrity, $contents) { + protected function checkIntegrity(?string $integrity, string $contents):void { if(is_null($integrity)) { return; } - list($algo, $hash) = explode("-", $integrity); + [$algo, $hash] = explode("-", $integrity); $availableAlgos = hash_algos(); if(!in_array($algo, $availableAlgos)) { @@ -200,4 +187,4 @@ protected function checkIntegrity(?string $integrity, $contents) { throw new IntegrityMismatchException(); } } -} \ No newline at end of file +} diff --git a/src/Response/BodyResponseFactory.php b/src/Response/BodyResponseFactory.php deleted file mode 100644 index e18b9b5..0000000 --- a/src/Response/BodyResponseFactory.php +++ /dev/null @@ -1,16 +0,0 @@ -getStatusCode(), - $response->getResponseHeaders(), - $response->getBody() - ); - - return $bodyResponse; - } -} \ No newline at end of file diff --git a/src/Response/Json.php b/src/Response/Json.php deleted file mode 100644 index fc00b66..0000000 --- a/src/Response/Json.php +++ /dev/null @@ -1,177 +0,0 @@ -jsonObject = $jsonObject; - $this->iteratorKey = 0; - $this->setPropertiesRecursive(); - } - - public function __toString():string { - return json_encode($this->jsonObject); - } - - /** @return self|int|bool|string|float */ - public function __get(string $key) { - return $this->offsetGet($key); - } - - public function __set(string $key, $value) { - $this->offsetSet($key, $value); - } - - public function __unset(string $key) { - $this->offsetUnset($key); - } - - public function __isset(string $key):bool { - return $this->offsetExists($key); - } - - /** @link https://php.net/manual/en/arrayaccess.offsetexists.php */ - public function offsetExists($offset):bool { - return isset($this->iteratorProperties[$offset]); - } - - /** - * @return self|int|bool|string|float - * @link https://php.net/manual/en/arrayaccess.offsetget.php - */ - public function offsetGet($offset) { - if(is_array($this->jsonObject) && isset($this->jsonObject[0])) { - return $this->jsonObject[$offset]; - } - - return $this->iteratorProperties[$offset] ?? null; - } - - /** @link https://php.net/manual/en/arrayaccess.offsetset.php */ - public function offsetSet($offset, $value):void { - throw new ImmutableObjectModificationException(); - } - - /** @link https://php.net/manual/en/arrayaccess.offsetunset.php */ - public function offsetUnset($offset):void { - throw new ImmutableObjectModificationException(); - } - - /** - * @return int|float|bool|string|null|Json - * @link https://php.net/manual/en/iterator.current.php - */ - public function current() { - if(is_array($this->jsonObject)) { - $obj = $this->jsonObject[$this->iteratorKey]; - if($obj instanceof StdClass) { - return new self($obj); - } - - return $obj; - } - - $property = $this->iteratorPropertyNames[$this->iteratorKey]; - return $this->jsonObject->$property; - } - - /** @link https://php.net/manual/en/iterator.next.php */ - public function next():void { - $this->iteratorKey++; - } - - /** @link https://php.net/manual/en/iterator.key.php */ - public function key() { - if(is_array($this->jsonObject)) { - return $this->iteratorKey; - } - - return $this->iteratorPropertyNames[$this->iteratorKey]; - } - - /** @link https://php.net/manual/en/iterator.valid.php */ - public function valid():bool { - if(!isset($this->iteratorPropertyNames[$this->iteratorKey])) { - return false; - } - - $property = $this->iteratorPropertyNames[$this->iteratorKey]; - if(is_array($this->jsonObject)) { - return isset($this->jsonObject[$this->iteratorKey]); - } - - return isset($this->jsonObject->{$property}); - } - - /** @link https://php.net/manual/en/iterator.rewind.php */ - public function rewind():void { - $this->iteratorKey = 0; - } - - public function getBool(string $key):?bool { - $value = $this->offsetGet($key); - if(is_null($value)) { - return null; - } - - return (bool)$value; - } - - public function getString(string $key):?string { - $value = $this->offsetGet($key); - if(is_null($value)) { - return null; - } - - return (string)$value; - } - - public function getInt(string $key):?int { - $value = $this->offsetGet($key); - if(is_null($value)) { - return null; - } - - return (int)$value; - } - - public function getFloat(string $key):?float { - $value = $this->offsetGet($key); - if(is_null($value)) { - return null; - } - - return (float)$value; - } - - public function getDateTime(string $key):?DateTime { - $value = $this->offsetGet($key); - if(is_null($value)) { - return null; - } - - return new DateTime($value); - } - - private function setPropertiesRecursive():void { - foreach($this->jsonObject as $key => $value) { - if($value instanceof StdClass) { - $this->iteratorProperties[$key] = new self($value); - } - else { - $this->iteratorProperties[$key] = $value; - } - - $this->iteratorPropertyNames []= $key; - } - } -} \ No newline at end of file diff --git a/test/phpunit/Helper/ResponseSimulator.php b/test/phpunit/Helper/ResponseSimulator.php index 4f833d8..1b21aaa 100644 --- a/test/phpunit/Helper/ResponseSimulator.php +++ b/test/phpunit/Helper/ResponseSimulator.php @@ -48,7 +48,7 @@ static protected function generateHeaders():array { } static protected function generateBody():string { - if(strlen(self::$bodyBuffer) > 0) { + if(strlen(self::$bodyBuffer ?? "") > 0) { return self::$bodyBuffer; } @@ -99,4 +99,4 @@ static public function sendChunk($ch):int { static public function setExpectedBody(string $body) { self::$bodyBuffer = $body; } -} \ No newline at end of file +} diff --git a/test/phpunit/Helper/TestCurl.php b/test/phpunit/Helper/TestCurl.php index a4debc5..6d76663 100644 --- a/test/phpunit/Helper/TestCurl.php +++ b/test/phpunit/Helper/TestCurl.php @@ -26,4 +26,4 @@ public function setOpt(int $option, $value):bool { public function getHandle() { return $this; } -} \ No newline at end of file +} diff --git a/test/phpunit/HttpTest.php b/test/phpunit/HttpTest.php index f079b6f..08ff248 100644 --- a/test/phpunit/HttpTest.php +++ b/test/phpunit/HttpTest.php @@ -2,13 +2,13 @@ namespace Gt\Fetch\Test; use Gt\Fetch\Http; -use Gt\Fetch\Promise; use Gt\Fetch\Response\BodyResponse; use Gt\Fetch\Test\Helper\ResponseSimulator; use Gt\Fetch\Test\Helper\TestCurl; use Gt\Fetch\Test\Helper\TestCurlMulti; use Gt\Http\Request; use Gt\Http\Uri; +use Gt\Promise\Promise; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\RequestInterface; @@ -109,8 +109,6 @@ public function testAsyncRequest() { $request->method("getUri") ->willReturn($uri); - /** @var RequestInterface $request */ - $promise = $http->sendAsyncRequest($request); self::assertInstanceOf( Promise::class, @@ -140,7 +138,7 @@ public function testGetOptions() { uniqid() => uniqid(), ]; $http = new Http($options); - $actualOptions = $http->getCurlOptions(); + $actualOptions = $http->curlOptions; foreach($options as $key => $value) { self::assertEquals($value, $actualOptions[$key]); @@ -245,4 +243,4 @@ public function testEnsureUriInterface() { $uri = $sut->ensureUriInterface($request); self::assertInstanceOf(UriInterface::class, $uri); } -} \ No newline at end of file +} diff --git a/test/phpunit/Response/BodyResponseTest.php b/test/phpunit/Response/BodyResponseTest.php index 951cc5a..326ac82 100644 --- a/test/phpunit/Response/BodyResponseTest.php +++ b/test/phpunit/Response/BodyResponseTest.php @@ -1,14 +1,17 @@ method("tell") ->willReturn(0); @@ -44,15 +45,12 @@ public function testText() { public function testBlob() { $exampleContents = "Example stream contents"; - /** @var MockObject|LoopInterface $loop */ - $loop = self::createMock(LoopInterface::class); - /** @var MockObject|StreamInterface $stream */ + $loop = self::createMock(Loop::class); $stream = self::createMock(StreamInterface::class); $stream->method("tell") ->willReturn(0); $stream->method("getContents") ->willReturn($exampleContents); - /** @var MockObject|Curl $curl */ $curl = self::createMock(Curl::class); $sutOutput = null; @@ -60,7 +58,7 @@ public function testBlob() { $sut = $sut->withBody($stream); $sut->startDeferredResponse($loop, $curl); $promise = $sut->blob(); - $promise->then(function(string $fulfilledValue)use(&$sutOutput) { + $promise->then(function(Blob $fulfilledValue)use(&$sutOutput) { $sutOutput = $fulfilledValue; }); $sut->endDeferredResponse(); @@ -73,8 +71,8 @@ public function testJson() { $exampleObj->test = "Example"; $jsonString = json_encode($exampleObj); - /** @var MockObject|LoopInterface $loop */ - $loop = self::createMock(LoopInterface::class); + $loop = self::createMock(Loop::class); + $stream = self::createMock(StreamInterface::class); /** @var MockObject|StreamInterface $stream */ $stream = self::createMock(StreamInterface::class); $stream->method("tell") @@ -84,26 +82,27 @@ public function testJson() { /** @var MockObject|Curl $curl */ $curl = self::createMock(Curl::class); + /** @var null|JsonKvpObject $sutOutput */ $sutOutput = null; $sut = new BodyResponse(); $sut = $sut->withBody($stream); $sut->startDeferredResponse($loop, $curl); $promise = $sut->json(); - $promise->then(function(StdClass $fulfilledValue)use(&$sutOutput) { + $promise->then(function(JsonKvpObject $fulfilledValue)use(&$sutOutput) { $sutOutput = $fulfilledValue; }); $sut->endDeferredResponse(); foreach($exampleObj as $key => $value) { - self::assertEquals($value, $sutOutput->$key); + self::assertEquals($value, $sutOutput->getString($key)); } } public function testInvalidJson() { $jsonString = "{'this;': is not valid JSON'}"; - /** @var MockObject|LoopInterface $loop */ - $loop = self::createMock(LoopInterface::class); + $loop = self::createMock(Loop::class); + $stream = self::createMock(StreamInterface::class); /** @var MockObject|StreamInterface $stream */ $stream = self::createMock(StreamInterface::class); $stream->method("tell") @@ -119,11 +118,13 @@ public function testInvalidJson() { $sut = $sut->withBody($stream); $sut->startDeferredResponse($loop, $curl); $promise = $sut->json(); - $promise->then(function($fulfilledValue)use(&$sutOutput) { - $sutOutput = $fulfilledValue; - }, function($errorValue)use(&$sutError) { - $sutError = $errorValue; - }); + $promise + ->then(function($fulfilledValue)use(&$sutOutput) { + $sutOutput = $fulfilledValue; + }) + ->catch(function($errorValue)use(&$sutError) { + $sutError = $errorValue; + }); $sut->endDeferredResponse(); self::assertNull($sutOutput); @@ -139,8 +140,8 @@ public function testArrayBuffer() { 110, 32, 100, 111, 103, ]; - /** @var MockObject|LoopInterface $loop */ - $loop = self::createMock(LoopInterface::class); + $loop = self::createMock(Loop::class); + $stream = self::createMock(StreamInterface::class); /** @var MockObject|StreamInterface $stream */ $stream = self::createMock(StreamInterface::class); $stream->method("tell") @@ -183,8 +184,8 @@ public function testFormData() { "repository" => "fetch", ]; - /** @var MockObject|LoopInterface $loop */ - $loop = self::createMock(LoopInterface::class); + $loop = self::createMock(Loop::class); + $stream = self::createMock(StreamInterface::class); /** @var MockObject|StreamInterface $stream */ $stream = self::createMock(StreamInterface::class); $stream->method("tell") @@ -212,8 +213,8 @@ public function testFormData() { public function testDeferredResponseStatus() { $exampleContents = "Example stream contents"; - /** @var MockObject|LoopInterface $loop */ - $loop = self::createMock(LoopInterface::class); + $loop = self::createMock(Loop::class); + $stream = self::createMock(StreamInterface::class); /** @var MockObject|StreamInterface $stream */ $stream = self::createMock(StreamInterface::class); $stream->method("tell") @@ -232,13 +233,13 @@ public function testDeferredResponseStatus() { ); $sut->startDeferredResponse($loop, $curl); self::assertEquals( - Promise::PENDING, + HttpPromiseInterface::PENDING, $sut->deferredResponseStatus() ); $sut->endDeferredResponse(); self::assertEquals( - Promise::FULFILLED, + HttpPromiseInterface::FULFILLED, $sut->deferredResponseStatus() ); } @@ -249,9 +250,8 @@ public function testGetHeaders() { $sut = $sut->withAddedHeader("X-Test-Two", "Example2"); $headers = $sut->headers; - self::assertIsArray($headers); - self::assertEquals("Example1", $headers["X-Test-One"]); - self::assertEquals("Example2", $headers["X-Test-Two"]); + self::assertEquals("Example1", $headers->get("X-Test-One")); + self::assertEquals("Example2", $headers->get("X-Test-Two")); } public function testGetOk() { @@ -265,9 +265,7 @@ public function testGetOk() { } public function testGetRedirected() { - /** @var MockObject|LoopInterface $loop */ - $loop = self::createMock(LoopInterface::class); - /** @var MockObject|Curl $curl */ + $loop = self::createMock(Loop::class); $curl = self::createMock(Curl::class); $curl->method("getInfo") ->willReturn(0, 3); @@ -290,9 +288,7 @@ public function testGetStatusText() { } public function testGetUrl() { - /** @var MockObject|LoopInterface $loop */ - $loop = self::createMock(LoopInterface::class); - /** @var MockObject|Curl $loop */ + $loop = self::createMock(Loop::class); $curl = self::createMock(Curl::class); $curl->method("getInfo") ->willReturn("/", "/test", "/test/123"); @@ -310,4 +306,4 @@ public function testUndefinedProperty() { $sut->test123; } -} \ No newline at end of file +} diff --git a/test/phpunit/Response/JsonTest.php b/test/phpunit/Response/JsonTest.php deleted file mode 100644 index 6f2c56c..0000000 --- a/test/phpunit/Response/JsonTest.php +++ /dev/null @@ -1,152 +0,0 @@ -name = "Mark"; - $simple->company = "Facebook"; - - $sut = new Json($simple); - self::assertEquals($simple->name, $sut->name); - self::assertEquals($simple->company, $sut->company); - } - - public function testGetterNested() { - $nested = new StdClass(); - $nested->name = new StdClass(); - $nested->name->first = "Margaret"; - $nested->name->last = "Hamilton"; - $nested->job = new StdClass(); - $nested->job->company = "MIT"; - $nested->job->title = "Systems Engineer"; - - $sut = new Json($nested); - self::assertInstanceOf(Json::class, $sut->name); - self::assertEquals($nested->name->first, $sut->name->first); - self::assertEquals($nested->name->last, $sut->name->last); - self::assertEquals($nested->job->company, $sut->job->company); - self::assertEquals($nested->job->title, $sut->job->title); - } - - public function testArrayAccessSimple() { - $simple = new StdClass(); - $simple->name = "Marissa"; - $simple->company = "Yahoo"; - - $sut = new Json($simple); - self::assertEquals($simple->name, $sut["name"]); - self::assertEquals($simple->company, $sut["company"]); - } - - public function testArrayAccessArray() { - $array = ["Bread", "Beans", "Milk", "Coffee"]; - $sut = new Json($array); - - foreach($array as $i => $value) { - self::assertEquals($value, $sut[$i]); - } - } - - public function testToStringSimple() { - $simple = new StdClass(); - $simple->name = "Sundar"; - $simple->company = "Google"; - - $sut = new Json($simple); - self::assertEquals(json_encode($simple), (string)$sut); - } - - public function testToStringArray() { - $array = ["Bread", "Beans", "Milk", "Coffee"]; - $sut = new Json($array); - self::assertEquals(json_encode($array), (string)$sut); - } - - public function testIteratorSimple() { - $simple = new StdClass(); - $simple->name = "Satya"; - $simple->company = "Microsoft"; - - $sut = new Json($simple); - $i = 0; - foreach($sut as $key => $value) { - $i++; - self::assertEquals($simple->$key, $value); - } - - - self::assertEquals(count(get_object_vars($simple)), $i); - } - - public function testIteratorArray() { - $array = ["Bread", "Beans", "Milk", "Coffee"]; - $sut = new Json($array); - - foreach($sut as $i => $value) { - self::assertEquals($array[$i], $value); - } - self::assertEquals(count($array) - 1, $i); - } - - public function testAssocArray() { - $sut = new Json([ - "name" => "Katherine", - "employer" => "NASA", - ]); - self::assertEquals("Katherine", $sut->name); - self::assertEquals("NASA", $sut->employer); - } - - public function testIsset() { - $obj = new StdClass(); - $obj->name = "Simon"; - $sut = new Json($obj); - self::assertTrue(isset($obj->name)); - self::assertFalse(isset($obj->age)); - } - - public function testSet() { - $sut = new Json(["example"]); - self::expectException(ImmutableObjectModificationException::class); - $sut->name = "Test"; - } - - public function testUnset() { - $sut = new Json(["example"]); - self::expectException(ImmutableObjectModificationException::class); - unset($sut->name); - } - - public function testGetMissing() { - $sut = new Json(["name" => "nothing"]); - self::assertNull($sut->thisDoesNotExist); - } - - public function testGetTypeSafe() { - $obj = new StdClass(); - $obj->boolean = true; - $obj->integer = 123; - $obj->string = "Hello!"; - $obj->floatingPoint = 123.456; - $obj->timeString = "2019-11-05 11:22:33"; - $sut = new Json($obj); - - self::assertIsBool($sut->getBool("boolean")); - self::assertEquals($obj->boolean, $sut->getBool("boolean")); - self::assertIsInt($sut->getInt("integer")); - self::assertEquals($obj->integer, $sut->getInt("integer")); - self::assertIsString($sut->getString("string")); - self::assertEquals($obj->string, $sut->getString("string")); - self::assertIsFloat($sut->getFloat("floatingPoint")); - self::assertEquals($obj->floatingPoint, $sut->getFloat("floatingPoint")); - self::assertInstanceOf(DateTime::class, $sut->getDateTime("timeString")); - self::assertEquals($obj->timeString, $sut->getDateTime("timeString")->format("Y-m-d H:i:s")); - } -} \ No newline at end of file