3949 changed files with 366 additions and 619285 deletions
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace app\admin\controller; |
|||
|
|||
use app\admin\service\NoticeService; |
|||
|
|||
class Notice extends AdminBaseController |
|||
{ |
|||
// 弹窗通知消息 |
|||
public function popUp() |
|||
{ |
|||
$returnData = (new NoticeService())->popUp($this->request->param()); |
|||
return json($returnData); |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
<?php |
|||
|
|||
namespace app\admin\service; |
|||
|
|||
use GatewayClient\Gateway; |
|||
require_once __DIR__ . '/../../../vendor/autoload.php'; |
|||
|
|||
class NoticeService extends AdminBaseService |
|||
{ |
|||
public function popUp($param) |
|||
{ |
|||
try { |
|||
if (empty($param['user_id']) || empty($param['content'])) { |
|||
return $this->toData('400', '参数错误'); |
|||
} |
|||
if (!is_string($param['content'])) { |
|||
return $this->toData('400', 'content参数必须为string类型'); |
|||
} |
|||
// 多个用户ID之间用英文逗号隔开的 |
|||
$expUserID = explode(',', $param['user_id']); |
|||
if (count($expUserID) <= 0) { |
|||
return $this->toData('400', '用户ID不能为空'); |
|||
} |
|||
// 循环向每个用户发送弹窗 |
|||
Gateway::$registerAddress = env('GATEWAY_SERVER.REGISTER'); |
|||
foreach ($expUserID as $uid) { |
|||
Gateway::sendToUid($uid, $param['content']); |
|||
} |
|||
return $this->toData('0', 'ok'); |
|||
} catch (\Exception $e) { |
|||
return $this->toData('500', 'The system error', [$e->getMessage(), $e->getTrace()]); |
|||
} |
|||
} |
|||
} |
Binary file not shown.
@ -1,5 +0,0 @@ |
|||
@ECHO OFF |
|||
setlocal DISABLEDELAYEDEXPANSION |
|||
SET BIN_TARGET=%~dp0/var-dump-server |
|||
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0 |
|||
php "%BIN_TARGET%" %* |
@ -1,9 +0,0 @@ |
|||
root = true |
|||
|
|||
[*] |
|||
charset = utf-8 |
|||
end_of_line = lf |
|||
indent_size = 4 |
|||
indent_style = space |
|||
insert_final_newline = true |
|||
trim_trailing_whitespace = true |
@ -1,13 +0,0 @@ |
|||
.editorconfig export-ignore |
|||
.gitattributes export-ignore |
|||
/.github/ export-ignore |
|||
.gitignore export-ignore |
|||
/.php-cs-fixer.dist.php export-ignore |
|||
/phpstan-baseline.neon export-ignore |
|||
/phpstan.neon.dist export-ignore |
|||
/phpunit.xml.dist export-ignore |
|||
/psalm-baseline.xml export-ignore |
|||
/psalm.xml export-ignore |
|||
/tests/ export-ignore |
|||
/vendor-bin/ export-ignore |
|||
/Makefile export-ignore |
@ -1,2 +0,0 @@ |
|||
[*.yml] |
|||
indent_size = 2 |
@ -1,2 +0,0 @@ |
|||
github: [Nyholm, GrahamCampbell] |
|||
tidelift: "packagist/guzzlehttp/promises" |
@ -1,14 +0,0 @@ |
|||
daysUntilStale: 120 |
|||
daysUntilClose: 14 |
|||
exemptLabels: |
|||
- lifecycle/keep-open |
|||
- lifecycle/ready-for-merge |
|||
# Label to use when marking an issue as stale |
|||
staleLabel: lifecycle/stale |
|||
# Comment to post when marking an issue as stale. Set to `false` to disable |
|||
markComment: > |
|||
This issue has been automatically marked as stale because it has not had |
|||
recent activity. It will be closed after 2 weeks if no further activity occurs. Thank you |
|||
for your contributions. |
|||
# Comment to post when closing a stale issue. Set to `false` to disable |
|||
closeComment: false |
@ -1,21 +0,0 @@ |
|||
name: Checks |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
pull_request: |
|||
|
|||
permissions: |
|||
contents: read |
|||
|
|||
jobs: |
|||
composer-normalize: |
|||
name: Composer Normalize |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Composer normalize |
|||
uses: docker://ergebnis/composer-normalize-action |
@ -1,58 +0,0 @@ |
|||
name: CI |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
pull_request: |
|||
|
|||
permissions: |
|||
contents: read |
|||
|
|||
jobs: |
|||
build-lowest-version: |
|||
name: Build lowest version |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Set up PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: '7.2' |
|||
ini-values: error_reporting=E_ALL |
|||
coverage: 'none' |
|||
extensions: mbstring |
|||
|
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Install dependencies |
|||
run: composer update --no-interaction --prefer-stable --prefer-lowest --no-progress |
|||
|
|||
- name: Run tests |
|||
run: vendor/bin/phpunit |
|||
|
|||
build: |
|||
name: Build |
|||
runs-on: ubuntu-22.04 |
|||
strategy: |
|||
max-parallel: 10 |
|||
matrix: |
|||
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] |
|||
|
|||
steps: |
|||
- name: Set up PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: ${{ matrix.php }} |
|||
ini-values: error_reporting=E_ALL |
|||
coverage: 'none' |
|||
extensions: mbstring |
|||
|
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Install dependencies |
|||
run: composer update --no-interaction --no-progress |
|||
|
|||
- name: Run tests |
|||
run: vendor/bin/phpunit |
@ -1,82 +0,0 @@ |
|||
name: Static analysis |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
pull_request: |
|||
|
|||
permissions: |
|||
contents: read |
|||
|
|||
jobs: |
|||
phpstan: |
|||
name: PHPStan |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Setup PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: '7.4' |
|||
coverage: none |
|||
extensions: mbstring |
|||
|
|||
- name: Download dependencies |
|||
run: composer update --no-interaction --no-progress |
|||
|
|||
- name: Download PHPStan |
|||
run: composer bin phpstan update --no-interaction --no-progress |
|||
|
|||
- name: Execute PHPStan |
|||
run: vendor/bin/phpstan analyze --no-progress |
|||
|
|||
php-cs-fixer: |
|||
name: PHP-CS-Fixer |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Setup PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: '7.4' |
|||
coverage: none |
|||
extensions: mbstring |
|||
|
|||
- name: Download dependencies |
|||
run: composer update --no-interaction --no-progress |
|||
|
|||
- name: Download PHP CS Fixer |
|||
run: composer bin php-cs-fixer update --no-interaction --no-progress |
|||
|
|||
- name: Execute PHP CS Fixer |
|||
run: vendor/bin/php-cs-fixer fix --diff --dry-run |
|||
|
|||
psalm: |
|||
name: Psalm |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Setup PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: '7.4' |
|||
coverage: none |
|||
extensions: mbstring |
|||
|
|||
- name: Download dependencies |
|||
run: composer update --no-interaction --no-progress |
|||
|
|||
- name: Download Psalm |
|||
run: composer bin psalm update --no-interaction --no-progress |
|||
|
|||
- name: Execute Psalm |
|||
run: vendor/bin/psalm.phar --no-progress --output-format=github |
@ -1,7 +0,0 @@ |
|||
artifacts/ |
|||
vendor/ |
|||
composer.lock |
|||
phpunit.xml |
|||
.php-cs-fixer.php |
|||
.php-cs-fixer.cache |
|||
.phpunit.result.cache |
@ -1,28 +0,0 @@ |
|||
<?php |
|||
|
|||
$config = (new PhpCsFixer\Config()) |
|||
->setRiskyAllowed(true) |
|||
->setRules([ |
|||
'@PHP71Migration:risky' => true, |
|||
'@PHPUnit75Migration:risky' => true, |
|||
'@PSR12:risky' => true, |
|||
'@Symfony' => true, |
|||
'global_namespace_import' => false, |
|||
'no_superfluous_phpdoc_tags' => [ |
|||
'allow_mixed' => true, |
|||
], |
|||
'phpdoc_annotation_without_dot' => false, |
|||
'phpdoc_summary' => false, |
|||
'phpdoc_to_comment' => false, |
|||
'single_line_throw' => false, |
|||
'yoda_style' => false, |
|||
]) |
|||
->setFinder( |
|||
PhpCsFixer\Finder::create() |
|||
->in(__DIR__.'/src') |
|||
->in(__DIR__.'/tests') |
|||
->name('*.php') |
|||
) |
|||
; |
|||
|
|||
return $config; |
@ -1,13 +0,0 @@ |
|||
all: clean test |
|||
|
|||
test: |
|||
vendor/bin/phpunit |
|||
|
|||
coverage: |
|||
vendor/bin/phpunit --coverage-html=artifacts/coverage |
|||
|
|||
view-coverage: |
|||
open artifacts/coverage/index.html |
|||
|
|||
clean: |
|||
rm -rf artifacts/* |
@ -1,8 +0,0 @@ |
|||
parameters: |
|||
ignoreErrors: |
|||
- |
|||
message: '#^Dead catch \- GuzzleHttp\\Promise\\RejectionException is never thrown in the try block\.$#' |
|||
identifier: catch.neverThrown |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
@ -1,7 +0,0 @@ |
|||
includes: |
|||
- phpstan-baseline.neon |
|||
|
|||
parameters: |
|||
level: 5 |
|||
paths: |
|||
- src |
@ -1,21 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<phpunit |
|||
colors="true" |
|||
beStrictAboutOutputDuringTests="true" |
|||
beStrictAboutTestsThatDoNotTestAnything="true" |
|||
bootstrap="vendor/autoload.php" |
|||
> |
|||
<testsuites> |
|||
<testsuite name="GuzzleHttp Promise Test Suite"> |
|||
<directory>tests/</directory> |
|||
</testsuite> |
|||
</testsuites> |
|||
<filter> |
|||
<whitelist> |
|||
<directory>src/</directory> |
|||
<exclude> |
|||
<directory suffix="Interface.php">src/</directory> |
|||
</exclude> |
|||
</whitelist> |
|||
</filter> |
|||
</phpunit> |
@ -1,2 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<files psalm-version="5.26.1@d747f6500b38ac4f7dfc5edbcae6e4b637d7add0"/> |
@ -1,18 +0,0 @@ |
|||
<?xml version="1.0"?> |
|||
<psalm |
|||
errorLevel="4" |
|||
resolveFromConfigFile="true" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xmlns="https://getpsalm.org/schema/config" |
|||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" |
|||
errorBaseline="psalm-baseline.xml" |
|||
findUnusedBaselineEntry="true" |
|||
findUnusedCode="false" |
|||
> |
|||
<projectFiles> |
|||
<directory name="src" /> |
|||
<ignoreFiles> |
|||
<directory name="vendor" /> |
|||
</ignoreFiles> |
|||
</projectFiles> |
|||
</psalm> |
@ -1,18 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise\AggregateException; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class AggregateExceptionTest extends TestCase |
|||
{ |
|||
public function testHasReason(): void |
|||
{ |
|||
$e = new AggregateException('foo', ['baz', 'bar']); |
|||
$this->assertStringContainsString('foo', $e->getMessage()); |
|||
$this->assertSame(['baz', 'bar'], $e->getReason()); |
|||
} |
|||
} |
@ -1,115 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise\Coroutine; |
|||
use GuzzleHttp\Promise\Promise; |
|||
use GuzzleHttp\Promise\PromiseInterface; |
|||
use PHPUnit\Framework\TestCase; |
|||
use ReflectionClass; |
|||
|
|||
class CoroutineTest extends TestCase |
|||
{ |
|||
public function testReturnsCoroutine(): void |
|||
{ |
|||
$fn = function () { yield 'foo'; }; |
|||
$this->assertInstanceOf(Coroutine::class, Coroutine::of($fn)); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider promiseInterfaceMethodProvider |
|||
* |
|||
* @param string $method |
|||
* @param array $args |
|||
*/ |
|||
public function testShouldProxyPromiseMethodsToResultPromise($method, $args = []): void |
|||
{ |
|||
$coroutine = new Coroutine(function () { yield 0; }); |
|||
$mockPromise = $this->getMockForAbstractClass(PromiseInterface::class); |
|||
$mockPromise->expects($this->once())->method($method)->with(...$args); |
|||
|
|||
$resultPromiseProp = (new ReflectionClass(Coroutine::class))->getProperty('result'); |
|||
$resultPromiseProp->setAccessible(true); |
|||
$resultPromiseProp->setValue($coroutine, $mockPromise); |
|||
|
|||
$coroutine->{$method}(...$args); |
|||
} |
|||
|
|||
public function promiseInterfaceMethodProvider() |
|||
{ |
|||
return [ |
|||
['then', [null, null]], |
|||
['otherwise', [function (): void {}]], |
|||
['wait', [true]], |
|||
['getState', []], |
|||
['resolve', [null]], |
|||
['reject', [null]], |
|||
]; |
|||
} |
|||
|
|||
public function testShouldCancelResultPromiseAndOutsideCurrentPromise(): void |
|||
{ |
|||
$coroutine = new Coroutine(function () { yield 0; }); |
|||
|
|||
$mockPromises = [ |
|||
'result' => $this->getMockForAbstractClass(PromiseInterface::class), |
|||
'currentPromise' => $this->getMockForAbstractClass(PromiseInterface::class), |
|||
]; |
|||
foreach ($mockPromises as $propName => $mockPromise) { |
|||
/** |
|||
* @var $mockPromise \PHPUnit_Framework_MockObject_MockObject |
|||
*/ |
|||
$mockPromise->expects($this->once()) |
|||
->method('cancel') |
|||
->with(); |
|||
|
|||
$promiseProp = (new ReflectionClass(Coroutine::class))->getProperty($propName); |
|||
$promiseProp->setAccessible(true); |
|||
$promiseProp->setValue($coroutine, $mockPromise); |
|||
} |
|||
|
|||
$coroutine->cancel(); |
|||
} |
|||
|
|||
public function testWaitShouldResolveChainedCoroutines(): void |
|||
{ |
|||
$promisor = function () { |
|||
return Coroutine::of(function () { |
|||
yield $promise = new Promise(function () use (&$promise): void { |
|||
$promise->resolve(1); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
$promise = $promisor()->then($promisor)->then($promisor); |
|||
|
|||
$this->assertSame(1, $promise->wait()); |
|||
} |
|||
|
|||
public function testWaitShouldHandleIntermediateErrors(): void |
|||
{ |
|||
$promise = Coroutine::of(function () { |
|||
yield $promise = new Promise(function () use (&$promise): void { |
|||
$promise->resolve(1); |
|||
}); |
|||
}) |
|||
->then(function () { |
|||
return Coroutine::of(function () { |
|||
yield $promise = new Promise(function () use (&$promise): void { |
|||
$promise->reject(new \Exception()); |
|||
}); |
|||
}); |
|||
}) |
|||
->otherwise(function (?\Exception $error = null) { |
|||
if (!$error) { |
|||
self::fail('Error did not propagate.'); |
|||
} |
|||
|
|||
return 3; |
|||
}); |
|||
|
|||
$this->assertSame(3, $promise->wait()); |
|||
} |
|||
} |
@ -1,58 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise as P; |
|||
use GuzzleHttp\Promise\FulfilledPromise; |
|||
use GuzzleHttp\Promise\Promise; |
|||
use GuzzleHttp\Promise\PromiseInterface; |
|||
use GuzzleHttp\Promise\RejectedPromise; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class CreateTest extends TestCase |
|||
{ |
|||
public function testCreatesPromiseForValue(): void |
|||
{ |
|||
$p = P\Create::promiseFor('foo'); |
|||
$this->assertInstanceOf(FulfilledPromise::class, $p); |
|||
} |
|||
|
|||
public function testReturnsPromiseForPromise(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$this->assertSame($p, P\Create::promiseFor($p)); |
|||
} |
|||
|
|||
public function testReturnsPromiseForThennable(): void |
|||
{ |
|||
$p = new Thennable(); |
|||
$wrapped = P\Create::promiseFor($p); |
|||
$this->assertNotSame($p, $wrapped); |
|||
$this->assertInstanceOf(PromiseInterface::class, $wrapped); |
|||
$p->resolve('foo'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo', $wrapped->wait()); |
|||
} |
|||
|
|||
public function testReturnsRejection(): void |
|||
{ |
|||
$p = P\Create::rejectionFor('fail'); |
|||
$this->assertInstanceOf(RejectedPromise::class, $p); |
|||
$this->assertSame('fail', PropertyHelper::get($p, 'reason')); |
|||
} |
|||
|
|||
public function testReturnsPromisesAsIsInRejectionFor(): void |
|||
{ |
|||
$a = new Promise(); |
|||
$b = P\Create::rejectionFor($a); |
|||
$this->assertSame($a, $b); |
|||
} |
|||
|
|||
public function testIterForReturnsIterator(): void |
|||
{ |
|||
$iter = new \ArrayIterator(); |
|||
$this->assertSame($iter, P\Create::iterFor($iter)); |
|||
} |
|||
} |
@ -1,433 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise as P; |
|||
use GuzzleHttp\Promise\EachPromise; |
|||
use GuzzleHttp\Promise\FulfilledPromise; |
|||
use GuzzleHttp\Promise\Promise; |
|||
use GuzzleHttp\Promise\RejectedPromise; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Promise\EachPromise |
|||
*/ |
|||
class EachPromiseTest extends TestCase |
|||
{ |
|||
public function testReturnsSameInstance(): void |
|||
{ |
|||
$each = new EachPromise([], ['concurrency' => 100]); |
|||
$this->assertSame($each->promise(), $each->promise()); |
|||
} |
|||
|
|||
public function testResolvesInCaseOfAnEmptyList(): void |
|||
{ |
|||
$promises = []; |
|||
$each = new EachPromise($promises); |
|||
$p = $each->promise(); |
|||
$this->assertNull($p->wait()); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
} |
|||
|
|||
public function testResolvesInCaseOfAnEmptyListAndInvokesFulfilled(): void |
|||
{ |
|||
$promises = []; |
|||
$each = new EachPromise($promises); |
|||
$p = $each->promise(); |
|||
$onFulfilledCalled = false; |
|||
$onRejectedCalled = false; |
|||
$p->then( |
|||
function () use (&$onFulfilledCalled): void { |
|||
$onFulfilledCalled = true; |
|||
}, |
|||
function () use (&$onRejectedCalled): void { |
|||
$onRejectedCalled = true; |
|||
} |
|||
); |
|||
$this->assertNull($p->wait()); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
$this->assertTrue($onFulfilledCalled); |
|||
$this->assertFalse($onRejectedCalled); |
|||
} |
|||
|
|||
public function testInvokesAllPromises(): void |
|||
{ |
|||
$promises = [new Promise(), new Promise(), new Promise()]; |
|||
$called = []; |
|||
$each = new EachPromise($promises, [ |
|||
'fulfilled' => function ($value) use (&$called): void { |
|||
$called[] = $value; |
|||
}, |
|||
]); |
|||
$p = $each->promise(); |
|||
$promises[0]->resolve('a'); |
|||
$promises[1]->resolve('c'); |
|||
$promises[2]->resolve('b'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(['a', 'c', 'b'], $called); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
} |
|||
|
|||
public function testIsWaitable(): void |
|||
{ |
|||
$a = $this->createSelfResolvingPromise('a'); |
|||
$b = $this->createSelfResolvingPromise('b'); |
|||
$called = []; |
|||
$each = new EachPromise([$a, $b], [ |
|||
'fulfilled' => function ($value) use (&$called): void { $called[] = $value; }, |
|||
]); |
|||
$p = $each->promise(); |
|||
$this->assertNull($p->wait()); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
$this->assertSame(['a', 'b'], $called); |
|||
} |
|||
|
|||
public function testCanResolveBeforeConsumingAll(): void |
|||
{ |
|||
$called = 0; |
|||
$a = $this->createSelfResolvingPromise('a'); |
|||
$b = new Promise(function (): void { $this->fail(); }); |
|||
$each = new EachPromise([$a, $b], [ |
|||
'fulfilled' => function ($value, $idx, Promise $aggregate) use (&$called): void { |
|||
$this->assertSame($idx, 0); |
|||
$this->assertSame('a', $value); |
|||
$aggregate->resolve(null); |
|||
++$called; |
|||
}, |
|||
'rejected' => function (\Exception $reason): void { |
|||
$this->fail($reason->getMessage()); |
|||
}, |
|||
]); |
|||
$p = $each->promise(); |
|||
$p->wait(); |
|||
$this->assertNull($p->wait()); |
|||
$this->assertSame(1, $called); |
|||
$this->assertTrue(P\Is::fulfilled($a)); |
|||
$this->assertTrue(P\Is::pending($b)); |
|||
// Resolving $b has no effect on the aggregate promise. |
|||
$b->resolve('foo'); |
|||
$this->assertSame(1, $called); |
|||
} |
|||
|
|||
public function testLimitsPendingPromises(): void |
|||
{ |
|||
$pending = [new Promise(), new Promise(), new Promise(), new Promise()]; |
|||
$promises = new \ArrayIterator($pending); |
|||
$each = new EachPromise($promises, ['concurrency' => 2]); |
|||
$p = $each->promise(); |
|||
$this->assertCount(2, PropertyHelper::get($each, 'pending')); |
|||
$pending[0]->resolve('a'); |
|||
$this->assertCount(2, PropertyHelper::get($each, 'pending')); |
|||
$this->assertTrue($promises->valid()); |
|||
$pending[1]->resolve('b'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertCount(2, PropertyHelper::get($each, 'pending')); |
|||
$this->assertTrue($promises->valid()); |
|||
$promises[2]->resolve('c'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertCount(1, PropertyHelper::get($each, 'pending')); |
|||
$this->assertTrue(P\Is::pending($p)); |
|||
$promises[3]->resolve('d'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertNull(PropertyHelper::get($each, 'pending')); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
$this->assertFalse($promises->valid()); |
|||
} |
|||
|
|||
public function testDynamicallyLimitsPendingPromises(): void |
|||
{ |
|||
$calls = []; |
|||
$pendingFn = function ($count) use (&$calls) { |
|||
$calls[] = $count; |
|||
|
|||
return 2; |
|||
}; |
|||
$pending = [new Promise(), new Promise(), new Promise(), new Promise()]; |
|||
$promises = new \ArrayIterator($pending); |
|||
$each = new EachPromise($promises, ['concurrency' => $pendingFn]); |
|||
$p = $each->promise(); |
|||
$this->assertCount(2, PropertyHelper::get($each, 'pending')); |
|||
$pending[0]->resolve('a'); |
|||
$this->assertCount(2, PropertyHelper::get($each, 'pending')); |
|||
$this->assertTrue($promises->valid()); |
|||
$pending[1]->resolve('b'); |
|||
$this->assertCount(2, PropertyHelper::get($each, 'pending')); |
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue($promises->valid()); |
|||
$promises[2]->resolve('c'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertCount(1, PropertyHelper::get($each, 'pending')); |
|||
$this->assertTrue(P\Is::pending($p)); |
|||
$promises[3]->resolve('d'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertNull(PropertyHelper::get($each, 'pending')); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
$this->assertSame([0, 1, 1, 1], $calls); |
|||
$this->assertFalse($promises->valid()); |
|||
} |
|||
|
|||
public function testClearsReferencesWhenResolved(): void |
|||
{ |
|||
$called = false; |
|||
$a = new Promise(function () use (&$a, &$called): void { |
|||
$a->resolve('a'); |
|||
$called = true; |
|||
}); |
|||
$each = new EachPromise([$a], [ |
|||
'concurrency' => function () { return 1; }, |
|||
'fulfilled' => function (): void {}, |
|||
'rejected' => function (): void {}, |
|||
]); |
|||
$each->promise()->wait(); |
|||
$this->assertNull(PropertyHelper::get($each, 'onFulfilled')); |
|||
$this->assertNull(PropertyHelper::get($each, 'onRejected')); |
|||
$this->assertNull(PropertyHelper::get($each, 'iterable')); |
|||
$this->assertNull(PropertyHelper::get($each, 'pending')); |
|||
$this->assertNull(PropertyHelper::get($each, 'concurrency')); |
|||
$this->assertTrue($called); |
|||
} |
|||
|
|||
public function testCanBeCancelled(): void |
|||
{ |
|||
$called = false; |
|||
$a = new FulfilledPromise('a'); |
|||
$b = new Promise(function () use (&$called): void { $called = true; }); |
|||
$each = new EachPromise([$a, $b], [ |
|||
'fulfilled' => function ($value, $idx, Promise $aggregate): void { |
|||
$aggregate->cancel(); |
|||
}, |
|||
'rejected' => function ($reason) use (&$called): void { |
|||
$called = true; |
|||
}, |
|||
]); |
|||
$p = $each->promise(); |
|||
$p->wait(false); |
|||
$this->assertTrue(P\Is::fulfilled($a)); |
|||
$this->assertTrue(P\Is::pending($b)); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
$this->assertFalse($called); |
|||
} |
|||
|
|||
public function testDoesNotBlowStackWithFulfilledPromises(): void |
|||
{ |
|||
$pending = []; |
|||
for ($i = 0; $i < 100; ++$i) { |
|||
$pending[] = new FulfilledPromise($i); |
|||
} |
|||
$values = []; |
|||
$each = new EachPromise($pending, [ |
|||
'fulfilled' => function ($value) use (&$values): void { |
|||
$values[] = $value; |
|||
}, |
|||
]); |
|||
$called = false; |
|||
$each->promise()->then(function () use (&$called): void { |
|||
$called = true; |
|||
}); |
|||
$this->assertFalse($called); |
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue($called); |
|||
$this->assertSame(range(0, 99), $values); |
|||
} |
|||
|
|||
public function testDoesNotBlowStackWithRejectedPromises(): void |
|||
{ |
|||
$pending = []; |
|||
for ($i = 0; $i < 100; ++$i) { |
|||
$pending[] = new RejectedPromise($i); |
|||
} |
|||
$values = []; |
|||
$each = new EachPromise($pending, [ |
|||
'rejected' => function ($value) use (&$values): void { |
|||
$values[] = $value; |
|||
}, |
|||
]); |
|||
$called = false; |
|||
$each->promise()->then( |
|||
function () use (&$called): void { $called = true; }, |
|||
function (): void { $this->fail('Should not have rejected.'); } |
|||
); |
|||
$this->assertFalse($called); |
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue($called); |
|||
$this->assertSame(range(0, 99), $values); |
|||
} |
|||
|
|||
public function testReturnsPromiseForWhatever(): void |
|||
{ |
|||
$called = []; |
|||
$arr = ['a', 'b']; |
|||
$each = new EachPromise($arr, [ |
|||
'fulfilled' => function ($v) use (&$called): void { $called[] = $v; }, |
|||
]); |
|||
$p = $each->promise(); |
|||
$this->assertNull($p->wait()); |
|||
$this->assertSame(['a', 'b'], $called); |
|||
} |
|||
|
|||
public function testRejectsAggregateWhenNextThrows(): void |
|||
{ |
|||
$iter = function () { |
|||
yield 'a'; |
|||
throw new \Exception('Failure'); |
|||
}; |
|||
$each = new EachPromise($iter()); |
|||
$p = $each->promise(); |
|||
$e = null; |
|||
$received = null; |
|||
$p->then(null, function ($reason) use (&$e): void { $e = $reason; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertInstanceOf(\Exception::class, $e); |
|||
$this->assertSame('Failure', $e->getMessage()); |
|||
} |
|||
|
|||
public function testDoesNotCallNextOnIteratorUntilNeededWhenWaiting(): void |
|||
{ |
|||
$results = []; |
|||
$values = [10]; |
|||
$remaining = 9; |
|||
$iter = function () use (&$values) { |
|||
while ($value = array_pop($values)) { |
|||
yield $value; |
|||
} |
|||
}; |
|||
$each = new EachPromise($iter(), [ |
|||
'concurrency' => 1, |
|||
'fulfilled' => function ($r) use (&$results, &$values, &$remaining): void { |
|||
$results[] = $r; |
|||
if ($remaining > 0) { |
|||
$values[] = $remaining--; |
|||
} |
|||
}, |
|||
]); |
|||
$each->promise()->wait(); |
|||
$this->assertSame(range(10, 1), $results); |
|||
} |
|||
|
|||
public function testDoesNotCallNextOnIteratorUntilNeededWhenAsync(): void |
|||
{ |
|||
$firstPromise = new Promise(); |
|||
$pending = [$firstPromise]; |
|||
$values = [$firstPromise]; |
|||
$results = []; |
|||
$remaining = 9; |
|||
$iter = function () use (&$values) { |
|||
while ($value = array_pop($values)) { |
|||
yield $value; |
|||
} |
|||
}; |
|||
$each = new EachPromise($iter(), [ |
|||
'concurrency' => 1, |
|||
'fulfilled' => function ($r) use (&$results, &$values, &$remaining, &$pending): void { |
|||
$results[] = $r; |
|||
if ($remaining-- > 0) { |
|||
$pending[] = $values[] = new Promise(); |
|||
} |
|||
}, |
|||
]); |
|||
$i = 0; |
|||
$each->promise(); |
|||
while ($promise = array_pop($pending)) { |
|||
$promise->resolve($i++); |
|||
P\Utils::queue()->run(); |
|||
} |
|||
$this->assertSame(range(0, 9), $results); |
|||
} |
|||
|
|||
private function createSelfResolvingPromise($value) |
|||
{ |
|||
$p = new Promise(function () use (&$p, $value): void { |
|||
$p->resolve($value); |
|||
}); |
|||
$trickCsFixer = true; |
|||
|
|||
return $p; |
|||
} |
|||
|
|||
public function testMutexPreventsGeneratorRecursion(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$results = $promises = []; |
|||
for ($i = 0; $i < 20; ++$i) { |
|||
$p = $this->createSelfResolvingPromise($i); |
|||
$pending[] = $p; |
|||
$promises[] = $p; |
|||
} |
|||
|
|||
$iter = function () use (&$promises, &$pending) { |
|||
foreach ($promises as $promise) { |
|||
// Resolve a promises, which will trigger the then() function, |
|||
// which would cause the EachPromise to try to add more |
|||
// promises to the queue. Without a lock, this would trigger |
|||
// a "Cannot resume an already running generator" fatal error. |
|||
if ($p = array_pop($pending)) { |
|||
$p->wait(); |
|||
} |
|||
yield $promise; |
|||
} |
|||
}; |
|||
|
|||
$each = new EachPromise($iter(), [ |
|||
'concurrency' => 5, |
|||
'fulfilled' => function ($r) use (&$results, &$pending): void { |
|||
$results[] = $r; |
|||
}, |
|||
]); |
|||
|
|||
$each->promise()->wait(); |
|||
$this->assertCount(20, $results); |
|||
} |
|||
|
|||
public function testIteratorWithSameKey(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$iter = function () { |
|||
yield 'foo' => $this->createSelfResolvingPromise(1); |
|||
yield 'foo' => $this->createSelfResolvingPromise(2); |
|||
yield 1 => $this->createSelfResolvingPromise(3); |
|||
yield 1 => $this->createSelfResolvingPromise(4); |
|||
}; |
|||
$called = 0; |
|||
$each = new EachPromise($iter(), [ |
|||
'fulfilled' => function ($value, $idx, Promise $aggregate) use (&$called): void { |
|||
++$called; |
|||
if ($value < 3) { |
|||
$this->assertSame('foo', $idx); |
|||
} else { |
|||
$this->assertSame(1, $idx); |
|||
} |
|||
}, |
|||
]); |
|||
$each->promise()->wait(); |
|||
$this->assertSame(4, $called); |
|||
} |
|||
|
|||
public function testIsWaitableWhenLimited(): void |
|||
{ |
|||
$promises = [ |
|||
$this->createSelfResolvingPromise('a'), |
|||
$this->createSelfResolvingPromise('c'), |
|||
$this->createSelfResolvingPromise('b'), |
|||
$this->createSelfResolvingPromise('d'), |
|||
]; |
|||
$called = []; |
|||
$each = new EachPromise($promises, [ |
|||
'concurrency' => 2, |
|||
'fulfilled' => function ($value) use (&$called): void { |
|||
$called[] = $value; |
|||
}, |
|||
]); |
|||
$p = $each->promise(); |
|||
$this->assertNull($p->wait()); |
|||
$this->assertSame(['a', 'c', 'b', 'd'], $called); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
} |
|||
} |
@ -1,36 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise as P; |
|||
use GuzzleHttp\Promise\FulfilledPromise; |
|||
use GuzzleHttp\Promise\Promise; |
|||
use GuzzleHttp\Promise\RejectedPromise; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class EachTest extends TestCase |
|||
{ |
|||
public function testCallsEachLimit(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$aggregate = P\Each::ofLimit($p, 2); |
|||
|
|||
$p->resolve('a'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue(P\Is::fulfilled($aggregate)); |
|||
} |
|||
|
|||
public function testEachLimitAllRejectsOnFailure(): void |
|||
{ |
|||
$p = [new FulfilledPromise('a'), new RejectedPromise('b')]; |
|||
$aggregate = P\Each::ofLimitAll($p, 2); |
|||
|
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue(P\Is::rejected($aggregate)); |
|||
|
|||
$result = P\Utils::inspect($aggregate); |
|||
$this->assertSame('b', $result['reason']); |
|||
} |
|||
} |
@ -1,115 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise as P; |
|||
use GuzzleHttp\Promise\FulfilledPromise; |
|||
use GuzzleHttp\Promise\Promise; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Promise\FulfilledPromise |
|||
*/ |
|||
class FulfilledPromiseTest extends TestCase |
|||
{ |
|||
public function testReturnsValueWhenWaitedUpon(): void |
|||
{ |
|||
$p = new FulfilledPromise('foo'); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
$this->assertSame('foo', $p->wait(true)); |
|||
} |
|||
|
|||
public function testCannotCancel(): void |
|||
{ |
|||
$p = new FulfilledPromise('foo'); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
$p->cancel(); |
|||
$this->assertSame('foo', $p->wait()); |
|||
} |
|||
|
|||
/** |
|||
* @expectedExceptionMessage Cannot resolve a fulfilled promise |
|||
*/ |
|||
public function testCannotResolve(): void |
|||
{ |
|||
$this->expectException(\LogicException::class); |
|||
|
|||
$p = new FulfilledPromise('foo'); |
|||
$p->resolve('bar'); |
|||
} |
|||
|
|||
/** |
|||
* @expectedExceptionMessage Cannot reject a fulfilled promise |
|||
*/ |
|||
public function testCannotReject(): void |
|||
{ |
|||
$this->expectException(\LogicException::class); |
|||
|
|||
$p = new FulfilledPromise('foo'); |
|||
$p->reject('bar'); |
|||
} |
|||
|
|||
public function testCanResolveWithSameValue(): void |
|||
{ |
|||
$p = new FulfilledPromise('foo'); |
|||
$p->resolve('foo'); |
|||
$this->assertSame('foo', $p->wait()); |
|||
} |
|||
|
|||
public function testCannotResolveWithPromise(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
|
|||
new FulfilledPromise(new Promise()); |
|||
} |
|||
|
|||
public function testReturnsSelfWhenNoOnFulfilled(): void |
|||
{ |
|||
$p = new FulfilledPromise('a'); |
|||
$this->assertSame($p, $p->then()); |
|||
} |
|||
|
|||
public function testAsynchronouslyInvokesOnFulfilled(): void |
|||
{ |
|||
$p = new FulfilledPromise('a'); |
|||
$r = null; |
|||
$f = function ($d) use (&$r): void { $r = $d; }; |
|||
$p2 = $p->then($f); |
|||
$this->assertNotSame($p, $p2); |
|||
$this->assertNull($r); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('a', $r); |
|||
} |
|||
|
|||
public function testReturnsNewRejectedWhenOnFulfilledFails(): void |
|||
{ |
|||
$p = new FulfilledPromise('a'); |
|||
$f = function (): void { throw new \Exception('b'); }; |
|||
$p2 = $p->then($f); |
|||
$this->assertNotSame($p, $p2); |
|||
try { |
|||
$p2->wait(); |
|||
$this->fail(); |
|||
} catch (\Exception $e) { |
|||
$this->assertSame('b', $e->getMessage()); |
|||
} |
|||
} |
|||
|
|||
public function testOtherwiseIsSugarForRejections(): void |
|||
{ |
|||
$c = null; |
|||
$p = new FulfilledPromise('foo'); |
|||
$p->otherwise(function ($v) use (&$c): void { $c = $v; }); |
|||
$this->assertNull($c); |
|||
} |
|||
|
|||
public function testDoesNotTryToFulfillTwiceDuringTrampoline(): void |
|||
{ |
|||
$fp = new FulfilledPromise('a'); |
|||
$t1 = $fp->then(function ($v) { return $v.' b'; }); |
|||
$t1->resolve('why!'); |
|||
$this->assertSame('why!', $t1->wait()); |
|||
} |
|||
} |
@ -1,42 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise as P; |
|||
use GuzzleHttp\Promise\FulfilledPromise; |
|||
use GuzzleHttp\Promise\Promise; |
|||
use GuzzleHttp\Promise\RejectedPromise; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class IsTest extends TestCase |
|||
{ |
|||
public function testKnowsIfFulfilled(): void |
|||
{ |
|||
$p = new FulfilledPromise(null); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
$this->assertFalse(P\Is::rejected($p)); |
|||
} |
|||
|
|||
public function testKnowsIfRejected(): void |
|||
{ |
|||
$p = new RejectedPromise(null); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
$this->assertFalse(P\Is::fulfilled($p)); |
|||
} |
|||
|
|||
public function testKnowsIfSettled(): void |
|||
{ |
|||
$p = new RejectedPromise(null); |
|||
$this->assertTrue(P\Is::settled($p)); |
|||
$this->assertFalse(P\Is::pending($p)); |
|||
} |
|||
|
|||
public function testKnowsIfPending(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$this->assertFalse(P\Is::settled($p)); |
|||
$this->assertTrue(P\Is::pending($p)); |
|||
} |
|||
} |
@ -1,51 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise\Promise; |
|||
use GuzzleHttp\Promise\PromiseInterface; |
|||
|
|||
class NotPromiseInstance extends Thennable implements PromiseInterface |
|||
{ |
|||
private $nextPromise; |
|||
|
|||
public function __construct() |
|||
{ |
|||
$this->nextPromise = new Promise(); |
|||
} |
|||
|
|||
public function then(?callable $res = null, ?callable $rej = null): PromiseInterface |
|||
{ |
|||
return $this->nextPromise->then($res, $rej); |
|||
} |
|||
|
|||
public function otherwise(callable $onRejected): PromiseInterface |
|||
{ |
|||
return $this->then($onRejected); |
|||
} |
|||
|
|||
public function resolve($value): void |
|||
{ |
|||
$this->nextPromise->resolve($value); |
|||
} |
|||
|
|||
public function reject($reason): void |
|||
{ |
|||
$this->nextPromise->reject($reason); |
|||
} |
|||
|
|||
public function wait(bool $unwrap = true, ?bool $defaultResolution = null): void |
|||
{ |
|||
} |
|||
|
|||
public function cancel(): void |
|||
{ |
|||
} |
|||
|
|||
public function getState(): string |
|||
{ |
|||
return $this->nextPromise->getState(); |
|||
} |
|||
} |
@ -1,773 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise as P; |
|||
use GuzzleHttp\Promise\CancellationException; |
|||
use GuzzleHttp\Promise\FulfilledPromise; |
|||
use GuzzleHttp\Promise\Promise; |
|||
use GuzzleHttp\Promise\RejectedPromise; |
|||
use GuzzleHttp\Promise\RejectionException; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Promise\Promise |
|||
*/ |
|||
class PromiseTest extends TestCase |
|||
{ |
|||
public function testCannotResolveNonPendingPromise(): void |
|||
{ |
|||
$this->expectException(\LogicException::class); |
|||
$this->expectExceptionMessage('The promise is already fulfilled'); |
|||
|
|||
$p = new Promise(); |
|||
$p->resolve('foo'); |
|||
$p->resolve('bar'); |
|||
$this->assertSame('foo', $p->wait()); |
|||
} |
|||
|
|||
public function testCanResolveWithSameValue(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$p->resolve('foo'); |
|||
$p->resolve('foo'); |
|||
$this->assertSame('foo', $p->wait()); |
|||
} |
|||
|
|||
public function testCannotRejectNonPendingPromise(): void |
|||
{ |
|||
$this->expectException(\LogicException::class); |
|||
$this->expectExceptionMessage('Cannot change a fulfilled promise to rejected'); |
|||
|
|||
$p = new Promise(); |
|||
$p->resolve('foo'); |
|||
$p->reject('bar'); |
|||
$this->assertSame('foo', $p->wait()); |
|||
} |
|||
|
|||
public function testCanRejectWithSameValue(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$p->reject('foo'); |
|||
$p->reject('foo'); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
} |
|||
|
|||
public function testCannotRejectResolveWithSameValue(): void |
|||
{ |
|||
$this->expectException(\LogicException::class); |
|||
$this->expectExceptionMessage('Cannot change a fulfilled promise to rejected'); |
|||
|
|||
$p = new Promise(); |
|||
$p->resolve('foo'); |
|||
$p->reject('foo'); |
|||
} |
|||
|
|||
public function testInvokesWaitFunction(): void |
|||
{ |
|||
$p = new Promise(function () use (&$p): void { |
|||
$p->resolve('10'); |
|||
}); |
|||
$this->assertSame('10', $p->wait()); |
|||
} |
|||
|
|||
public function testRejectsAndThrowsWhenWaitFailsToResolve(): void |
|||
{ |
|||
$this->expectException(RejectionException::class); |
|||
$this->expectExceptionMessage('The promise was rejected with reason: Invoking the wait callback did not resolve the promise'); |
|||
|
|||
$p = new Promise(function (): void {}); |
|||
$p->wait(); |
|||
} |
|||
|
|||
public function testThrowsWhenUnwrapIsRejectedWithNonException(): void |
|||
{ |
|||
$this->expectException(RejectionException::class); |
|||
$this->expectExceptionMessage('The promise was rejected with reason: foo'); |
|||
|
|||
$p = new Promise(function () use (&$p): void { |
|||
$p->reject('foo'); |
|||
}); |
|||
$p->wait(); |
|||
} |
|||
|
|||
public function testThrowsWhenUnwrapIsRejectedWithException(): void |
|||
{ |
|||
$this->expectException(\UnexpectedValueException::class); |
|||
$this->expectExceptionMessage('foo'); |
|||
|
|||
$e = new \UnexpectedValueException('foo'); |
|||
$p = new Promise(function () use (&$p, $e): void { |
|||
$p->reject($e); |
|||
}); |
|||
$p->wait(); |
|||
} |
|||
|
|||
public function testDoesNotUnwrapExceptionsWhenDisabled(): void |
|||
{ |
|||
$p = new Promise(function () use (&$p): void { |
|||
$p->reject('foo'); |
|||
}); |
|||
$this->assertTrue(P\Is::pending($p)); |
|||
$p->wait(false); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
} |
|||
|
|||
public function testRejectsSelfWhenWaitThrows(): void |
|||
{ |
|||
$e = new \UnexpectedValueException('foo'); |
|||
$p = new Promise(function () use ($e): void { |
|||
throw $e; |
|||
}); |
|||
try { |
|||
$p->wait(); |
|||
$this->fail(); |
|||
} catch (\UnexpectedValueException $e) { |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
} |
|||
} |
|||
|
|||
public function testWaitsOnNestedPromises(): void |
|||
{ |
|||
$p = new Promise(function () use (&$p): void { |
|||
$p->resolve('_'); |
|||
}); |
|||
$p2 = new Promise(function () use (&$p2): void { |
|||
$p2->resolve('foo'); |
|||
}); |
|||
$p3 = $p->then(function () use ($p2) { |
|||
return $p2; |
|||
}); |
|||
$this->assertSame('foo', $p3->wait()); |
|||
} |
|||
|
|||
public function testThrowsWhenWaitingOnPromiseWithNoWaitFunction(): void |
|||
{ |
|||
$this->expectException(RejectionException::class); |
|||
|
|||
$p = new Promise(); |
|||
$p->wait(); |
|||
} |
|||
|
|||
public function testThrowsWaitExceptionAfterPromiseIsResolved(): void |
|||
{ |
|||
$p = new Promise(function () use (&$p): void { |
|||
$p->reject('Foo!'); |
|||
throw new \Exception('Bar?'); |
|||
}); |
|||
|
|||
try { |
|||
$p->wait(); |
|||
$this->fail(); |
|||
} catch (\Exception $e) { |
|||
$this->assertSame('Bar?', $e->getMessage()); |
|||
} |
|||
} |
|||
|
|||
public function testGetsActualWaitValueFromThen(): void |
|||
{ |
|||
$p = new Promise(function () use (&$p): void { |
|||
$p->reject('Foo!'); |
|||
}); |
|||
$p2 = $p->then(null, function ($reason) { |
|||
return new RejectedPromise([$reason]); |
|||
}); |
|||
|
|||
try { |
|||
$p2->wait(); |
|||
$this->fail('Should have thrown'); |
|||
} catch (RejectionException $e) { |
|||
$this->assertSame(['Foo!'], $e->getReason()); |
|||
} |
|||
} |
|||
|
|||
public function testWaitBehaviorIsBasedOnLastPromiseInChain(): void |
|||
{ |
|||
$p3 = new Promise(function () use (&$p3): void { |
|||
$p3->resolve('Whoop'); |
|||
}); |
|||
$p2 = new Promise(function () use (&$p2, $p3): void { |
|||
$p2->reject($p3); |
|||
}); |
|||
$p = new Promise(function () use (&$p, $p2): void { |
|||
$p->reject($p2); |
|||
}); |
|||
$this->assertSame('Whoop', $p->wait()); |
|||
} |
|||
|
|||
public function testWaitsOnAPromiseChainEvenWhenNotUnwrapped(): void |
|||
{ |
|||
$p2 = new Promise(function () use (&$p2): void { |
|||
$p2->reject('Fail'); |
|||
}); |
|||
$p = new Promise(function () use ($p2, &$p): void { |
|||
$p->resolve($p2); |
|||
}); |
|||
$p->wait(false); |
|||
$this->assertTrue(P\Is::rejected($p2)); |
|||
} |
|||
|
|||
public function testCannotCancelNonPending(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$p->resolve('foo'); |
|||
$p->cancel(); |
|||
$this->assertTrue(P\Is::fulfilled($p)); |
|||
} |
|||
|
|||
public function testCancelsPromiseWhenNoCancelFunction(): void |
|||
{ |
|||
$this->expectException(CancellationException::class); |
|||
|
|||
$p = new Promise(); |
|||
$p->cancel(); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
$p->wait(); |
|||
} |
|||
|
|||
public function testCancelsPromiseWithCancelFunction(): void |
|||
{ |
|||
$called = false; |
|||
$p = new Promise(null, function () use (&$called): void { |
|||
$called = true; |
|||
}); |
|||
$p->cancel(); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
$this->assertTrue($called); |
|||
} |
|||
|
|||
public function testCancelsUppermostPendingPromise(): void |
|||
{ |
|||
$called = false; |
|||
$p1 = new Promise(null, function () use (&$called): void { |
|||
$called = true; |
|||
}); |
|||
$p2 = $p1->then(function (): void {}); |
|||
$p3 = $p2->then(function (): void {}); |
|||
$p4 = $p3->then(function (): void {}); |
|||
$p3->cancel(); |
|||
$this->assertTrue(P\Is::rejected($p1)); |
|||
$this->assertTrue(P\Is::rejected($p2)); |
|||
$this->assertTrue(P\Is::rejected($p3)); |
|||
$this->assertTrue(P\Is::pending($p4)); |
|||
$this->assertTrue($called); |
|||
|
|||
try { |
|||
$p3->wait(); |
|||
$this->fail(); |
|||
} catch (CancellationException $e) { |
|||
$this->assertStringContainsString('cancelled', $e->getMessage()); |
|||
} |
|||
|
|||
try { |
|||
$p4->wait(); |
|||
$this->fail(); |
|||
} catch (CancellationException $e) { |
|||
$this->assertStringContainsString('cancelled', $e->getMessage()); |
|||
} |
|||
|
|||
$this->assertTrue(P\Is::rejected($p4)); |
|||
} |
|||
|
|||
public function testCancelsChildPromises(): void |
|||
{ |
|||
$called1 = $called2 = $called3 = false; |
|||
$p1 = new Promise(null, function () use (&$called1): void { |
|||
$called1 = true; |
|||
}); |
|||
$p2 = new Promise(null, function () use (&$called2): void { |
|||
$called2 = true; |
|||
}); |
|||
$p3 = new Promise(null, function () use (&$called3): void { |
|||
$called3 = true; |
|||
}); |
|||
$p4 = $p2->then(function () use ($p3) { |
|||
return $p3; |
|||
}); |
|||
$p5 = $p4->then(function (): void { |
|||
$this->fail(); |
|||
}); |
|||
$p4->cancel(); |
|||
$this->assertTrue(P\Is::pending($p1)); |
|||
$this->assertTrue(P\Is::rejected($p2)); |
|||
$this->assertTrue(P\Is::pending($p3)); |
|||
$this->assertTrue(P\Is::rejected($p4)); |
|||
$this->assertTrue(P\Is::pending($p5)); |
|||
$this->assertFalse($called1); |
|||
$this->assertTrue($called2); |
|||
$this->assertFalse($called3); |
|||
} |
|||
|
|||
public function testRejectsPromiseWhenCancelFails(): void |
|||
{ |
|||
$called = false; |
|||
$p = new Promise(null, function () use (&$called): void { |
|||
$called = true; |
|||
throw new \Exception('e'); |
|||
}); |
|||
$p->cancel(); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
$this->assertTrue($called); |
|||
try { |
|||
$p->wait(); |
|||
$this->fail(); |
|||
} catch (\Exception $e) { |
|||
$this->assertSame('e', $e->getMessage()); |
|||
} |
|||
} |
|||
|
|||
public function testCreatesPromiseWhenFulfilledAfterThen(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$carry = null; |
|||
$p2 = $p->then(function ($v) use (&$carry): void { |
|||
$carry = $v; |
|||
}); |
|||
$this->assertNotSame($p, $p2); |
|||
$p->resolve('foo'); |
|||
P\Utils::queue()->run(); |
|||
|
|||
$this->assertSame('foo', $carry); |
|||
} |
|||
|
|||
public function testCreatesPromiseWhenFulfilledBeforeThen(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$p->resolve('foo'); |
|||
$carry = null; |
|||
$p2 = $p->then(function ($v) use (&$carry): void { |
|||
$carry = $v; |
|||
}); |
|||
$this->assertNotSame($p, $p2); |
|||
$this->assertNull($carry); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo', $carry); |
|||
} |
|||
|
|||
public function testCreatesPromiseWhenFulfilledWithNoCallback(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$p->resolve('foo'); |
|||
$p2 = $p->then(); |
|||
$this->assertNotSame($p, $p2); |
|||
$this->assertInstanceOf(FulfilledPromise::class, $p2); |
|||
} |
|||
|
|||
public function testCreatesPromiseWhenRejectedAfterThen(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$carry = null; |
|||
$p2 = $p->then(null, function ($v) use (&$carry): void { |
|||
$carry = $v; |
|||
}); |
|||
$this->assertNotSame($p, $p2); |
|||
$p->reject('foo'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo', $carry); |
|||
} |
|||
|
|||
public function testCreatesPromiseWhenRejectedBeforeThen(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$p->reject('foo'); |
|||
$carry = null; |
|||
$p2 = $p->then(null, function ($v) use (&$carry): void { |
|||
$carry = $v; |
|||
}); |
|||
$this->assertNotSame($p, $p2); |
|||
$this->assertNull($carry); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo', $carry); |
|||
} |
|||
|
|||
public function testCreatesPromiseWhenRejectedWithNoCallback(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$p->reject('foo'); |
|||
$p2 = $p->then(); |
|||
$this->assertNotSame($p, $p2); |
|||
$this->assertInstanceOf(RejectedPromise::class, $p2); |
|||
} |
|||
|
|||
public function testInvokesWaitFnsForThens(): void |
|||
{ |
|||
$p = new Promise(function () use (&$p): void { |
|||
$p->resolve('a'); |
|||
}); |
|||
$p2 = $p |
|||
->then(function ($v) { |
|||
return $v.'-1-'; |
|||
}) |
|||
->then(function ($v) { |
|||
return $v.'2'; |
|||
}); |
|||
$this->assertSame('a-1-2', $p2->wait()); |
|||
} |
|||
|
|||
public function testStacksThenWaitFunctions(): void |
|||
{ |
|||
$p1 = new Promise(function () use (&$p1): void { |
|||
$p1->resolve('a'); |
|||
}); |
|||
$p2 = new Promise(function () use (&$p2): void { |
|||
$p2->resolve('b'); |
|||
}); |
|||
$p3 = new Promise(function () use (&$p3): void { |
|||
$p3->resolve('c'); |
|||
}); |
|||
$p4 = $p1 |
|||
->then(function () use ($p2) { |
|||
return $p2; |
|||
}) |
|||
->then(function () use ($p3) { |
|||
return $p3; |
|||
}); |
|||
$this->assertSame('c', $p4->wait()); |
|||
} |
|||
|
|||
public function testForwardsFulfilledDownChainBetweenGaps(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$r = $r2 = null; |
|||
$p->then(null, null) |
|||
->then(function ($v) use (&$r) { |
|||
$r = $v; |
|||
|
|||
return $v.'2'; |
|||
}) |
|||
->then(function ($v) use (&$r2): void { |
|||
$r2 = $v; |
|||
}); |
|||
$p->resolve('foo'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo', $r); |
|||
$this->assertSame('foo2', $r2); |
|||
} |
|||
|
|||
public function testForwardsRejectedPromisesDownChainBetweenGaps(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$r = $r2 = null; |
|||
$p->then(null, null) |
|||
->then(null, function ($v) use (&$r) { |
|||
$r = $v; |
|||
|
|||
return $v.'2'; |
|||
}) |
|||
->then(function ($v) use (&$r2): void { |
|||
$r2 = $v; |
|||
}); |
|||
$p->reject('foo'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo', $r); |
|||
$this->assertSame('foo2', $r2); |
|||
} |
|||
|
|||
public function testForwardsThrownPromisesDownChainBetweenGaps(): void |
|||
{ |
|||
$e = new \Exception(); |
|||
$p = new Promise(); |
|||
$r = $r2 = null; |
|||
$p->then(null, null) |
|||
->then(null, function ($v) use (&$r, $e): void { |
|||
$r = $v; |
|||
throw $e; |
|||
}) |
|||
->then( |
|||
null, |
|||
function ($v) use (&$r2): void { |
|||
$r2 = $v; |
|||
} |
|||
); |
|||
$p->reject('foo'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo', $r); |
|||
$this->assertSame($e, $r2); |
|||
} |
|||
|
|||
public function testForwardsReturnedRejectedPromisesDownChainBetweenGaps(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$rejected = new RejectedPromise('bar'); |
|||
$r = $r2 = null; |
|||
$p->then(null, null) |
|||
->then(null, function ($v) use (&$r, $rejected) { |
|||
$r = $v; |
|||
|
|||
return $rejected; |
|||
}) |
|||
->then( |
|||
null, |
|||
function ($v) use (&$r2): void { |
|||
$r2 = $v; |
|||
} |
|||
); |
|||
$p->reject('foo'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo', $r); |
|||
$this->assertSame('bar', $r2); |
|||
try { |
|||
$p->wait(); |
|||
} catch (RejectionException $e) { |
|||
$this->assertSame('foo', $e->getReason()); |
|||
} |
|||
} |
|||
|
|||
public function testForwardsHandlersToNextPromise(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$p2 = new Promise(); |
|||
$resolved = null; |
|||
$p |
|||
->then(function ($v) use ($p2) { |
|||
return $p2; |
|||
}) |
|||
->then(function ($value) use (&$resolved): void { |
|||
$resolved = $value; |
|||
}); |
|||
$p->resolve('a'); |
|||
$p2->resolve('b'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('b', $resolved); |
|||
} |
|||
|
|||
public function testRemovesReferenceFromChildWhenParentWaitedUpon(): void |
|||
{ |
|||
$r = null; |
|||
$p = new Promise(function () use (&$p): void { |
|||
$p->resolve('a'); |
|||
}); |
|||
$p2 = new Promise(function () use (&$p2): void { |
|||
$p2->resolve('b'); |
|||
}); |
|||
$pb = $p->then( |
|||
function ($v) use ($p2, &$r) { |
|||
$r = $v; |
|||
|
|||
return $p2; |
|||
} |
|||
) |
|||
->then(function ($v) { |
|||
return $v.'.'; |
|||
}); |
|||
$this->assertSame('a', $p->wait()); |
|||
$this->assertSame('b', $p2->wait()); |
|||
$this->assertSame('b.', $pb->wait()); |
|||
$this->assertSame('a', $r); |
|||
} |
|||
|
|||
public function testForwardsHandlersWhenFulfilledPromiseIsReturned(): void |
|||
{ |
|||
$res = []; |
|||
$p = new Promise(); |
|||
$p2 = new Promise(); |
|||
$p2->resolve('foo'); |
|||
$p2->then(function ($v) use (&$res): void { |
|||
$res[] = 'A:'.$v; |
|||
}); |
|||
// $res is A:foo |
|||
$p |
|||
->then(function () use ($p2, &$res) { |
|||
$res[] = 'B'; |
|||
|
|||
return $p2; |
|||
}) |
|||
->then(function ($v) use (&$res): void { |
|||
$res[] = 'C:'.$v; |
|||
}); |
|||
$p->resolve('a'); |
|||
$p->then(function ($v) use (&$res): void { |
|||
$res[] = 'D:'.$v; |
|||
}); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(['A:foo', 'B', 'D:a', 'C:foo'], $res); |
|||
} |
|||
|
|||
public function testForwardsHandlersWhenRejectedPromiseIsReturned(): void |
|||
{ |
|||
$res = []; |
|||
$p = new Promise(); |
|||
$p2 = new Promise(); |
|||
$p2->reject('foo'); |
|||
$p2->then(null, function ($v) use (&$res): void { |
|||
$res[] = 'A:'.$v; |
|||
}); |
|||
$p->then(null, function () use ($p2, &$res) { |
|||
$res[] = 'B'; |
|||
|
|||
return $p2; |
|||
}) |
|||
->then(null, function ($v) use (&$res): void { |
|||
$res[] = 'C:'.$v; |
|||
}); |
|||
$p->reject('a'); |
|||
$p->then(null, function ($v) use (&$res): void { |
|||
$res[] = 'D:'.$v; |
|||
}); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(['A:foo', 'B', 'D:a', 'C:foo'], $res); |
|||
} |
|||
|
|||
public function testDoesNotForwardRejectedPromise(): void |
|||
{ |
|||
$res = []; |
|||
$p = new Promise(); |
|||
$p2 = new Promise(); |
|||
$p2->cancel(); |
|||
$p2->then(function ($v) use (&$res) { |
|||
$res[] = "B:$v"; |
|||
|
|||
return $v; |
|||
}); |
|||
$p->then(function ($v) use ($p2, &$res) { |
|||
$res[] = "B:$v"; |
|||
|
|||
return $p2; |
|||
}) |
|||
->then(function ($v) use (&$res): void { |
|||
$res[] = 'C:'.$v; |
|||
}); |
|||
$p->resolve('a'); |
|||
$p->then(function ($v) use (&$res): void { |
|||
$res[] = 'D:'.$v; |
|||
}); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(['B:a', 'D:a'], $res); |
|||
} |
|||
|
|||
public function testRecursivelyForwardsWhenOnlyThennable(): void |
|||
{ |
|||
$res = []; |
|||
$p = new Promise(); |
|||
$p2 = new Thennable(); |
|||
$p2->resolve('foo'); |
|||
$p2->then(function ($v) use (&$res): void { |
|||
$res[] = 'A:'.$v; |
|||
}); |
|||
$p->then(function () use ($p2, &$res) { |
|||
$res[] = 'B'; |
|||
|
|||
return $p2; |
|||
}) |
|||
->then(function ($v) use (&$res): void { |
|||
$res[] = 'C:'.$v; |
|||
}); |
|||
$p->resolve('a'); |
|||
$p->then(function ($v) use (&$res): void { |
|||
$res[] = 'D:'.$v; |
|||
}); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(['A:foo', 'B', 'D:a', 'C:foo'], $res); |
|||
} |
|||
|
|||
public function testRecursivelyForwardsWhenNotInstanceOfPromise(): void |
|||
{ |
|||
$res = []; |
|||
$p = new Promise(); |
|||
$p2 = new NotPromiseInstance(); |
|||
$p2->then(function ($v) use (&$res): void { |
|||
$res[] = 'A:'.$v; |
|||
}); |
|||
$p->then(function () use ($p2, &$res) { |
|||
$res[] = 'B'; |
|||
|
|||
return $p2; |
|||
}) |
|||
->then(function ($v) use (&$res): void { |
|||
$res[] = 'C:'.$v; |
|||
}); |
|||
$p->resolve('a'); |
|||
$p->then(function ($v) use (&$res): void { |
|||
$res[] = 'D:'.$v; |
|||
}); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(['B', 'D:a'], $res); |
|||
$p2->resolve('foo'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(['B', 'D:a', 'A:foo', 'C:foo'], $res); |
|||
} |
|||
|
|||
public function testCannotResolveWithSelf(): void |
|||
{ |
|||
$this->expectException(\LogicException::class); |
|||
$this->expectExceptionMessage('Cannot fulfill or reject a promise with itself'); |
|||
|
|||
$p = new Promise(); |
|||
$p->resolve($p); |
|||
} |
|||
|
|||
public function testCannotRejectWithSelf(): void |
|||
{ |
|||
$this->expectException(\LogicException::class); |
|||
$this->expectExceptionMessage('Cannot fulfill or reject a promise with itself'); |
|||
|
|||
$p = new Promise(); |
|||
$p->reject($p); |
|||
} |
|||
|
|||
public function testDoesNotBlowStackWhenWaitingOnNestedThens(): void |
|||
{ |
|||
$inner = new Promise(function () use (&$inner): void { |
|||
$inner->resolve(0); |
|||
}); |
|||
$prev = $inner; |
|||
for ($i = 1; $i < 100; ++$i) { |
|||
$prev = $prev->then(function ($i) { |
|||
return $i + 1; |
|||
}); |
|||
} |
|||
|
|||
$parent = new Promise(function () use (&$parent, $prev): void { |
|||
$parent->resolve($prev); |
|||
}); |
|||
|
|||
$this->assertSame(99, $parent->wait()); |
|||
} |
|||
|
|||
public function testOtherwiseIsSugarForRejections(): void |
|||
{ |
|||
$p = new Promise(); |
|||
$p->reject('foo'); |
|||
$p->otherwise(function ($v) use (&$c): void { |
|||
$c = $v; |
|||
}); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame($c, 'foo'); |
|||
} |
|||
|
|||
public function testRepeatedWaitFulfilled(): void |
|||
{ |
|||
$promise = new Promise(function () use (&$promise): void { |
|||
$promise->resolve('foo'); |
|||
}); |
|||
|
|||
$this->assertSame('foo', $promise->wait()); |
|||
$this->assertSame('foo', $promise->wait()); |
|||
} |
|||
|
|||
public function testRepeatedWaitRejected(): void |
|||
{ |
|||
$promise = new Promise(function () use (&$promise): void { |
|||
$promise->reject(new \RuntimeException('foo')); |
|||
}); |
|||
|
|||
$exceptionCount = 0; |
|||
try { |
|||
$promise->wait(); |
|||
} catch (\Exception $e) { |
|||
$this->assertSame('foo', $e->getMessage()); |
|||
++$exceptionCount; |
|||
} |
|||
|
|||
try { |
|||
$promise->wait(); |
|||
} catch (\Exception $e) { |
|||
$this->assertSame('foo', $e->getMessage()); |
|||
++$exceptionCount; |
|||
} |
|||
|
|||
$this->assertSame(2, $exceptionCount); |
|||
} |
|||
} |
@ -1,29 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
/** |
|||
* A class to help get properties of an object. |
|||
* |
|||
* @internal |
|||
* |
|||
* @author Tobias Nyholm <tobias.nyholm@gmail.com> |
|||
*/ |
|||
class PropertyHelper |
|||
{ |
|||
/** |
|||
* @param object $object |
|||
* @param string $property |
|||
* |
|||
* @throws \ReflectionException |
|||
*/ |
|||
public static function get($object, $property) |
|||
{ |
|||
$property = (new \ReflectionObject($object))->getProperty($property); |
|||
$property->setAccessible(true); |
|||
|
|||
return $property->getValue($object); |
|||
} |
|||
} |
@ -1,151 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise as P; |
|||
use GuzzleHttp\Promise\Promise; |
|||
use GuzzleHttp\Promise\RejectedPromise; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Promise\RejectedPromise |
|||
*/ |
|||
class RejectedPromiseTest extends TestCase |
|||
{ |
|||
public function testThrowsReasonWhenWaitedUpon(): void |
|||
{ |
|||
$p = new RejectedPromise('foo'); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
try { |
|||
$p->wait(true); |
|||
$this->fail(); |
|||
} catch (\Exception $e) { |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
$this->assertStringContainsString('foo', $e->getMessage()); |
|||
} |
|||
} |
|||
|
|||
public function testCannotCancel(): void |
|||
{ |
|||
$p = new RejectedPromise('foo'); |
|||
$p->cancel(); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
} |
|||
|
|||
/** |
|||
* @exepctedExceptionMessage Cannot resolve a rejected promise |
|||
*/ |
|||
public function testCannotResolve(): void |
|||
{ |
|||
$this->expectException(\LogicException::class); |
|||
|
|||
$p = new RejectedPromise('foo'); |
|||
$p->resolve('bar'); |
|||
} |
|||
|
|||
/** |
|||
* @expectedExceptionMessage Cannot reject a rejected promise |
|||
*/ |
|||
public function testCannotReject(): void |
|||
{ |
|||
$this->expectException(\LogicException::class); |
|||
|
|||
$p = new RejectedPromise('foo'); |
|||
$p->reject('bar'); |
|||
} |
|||
|
|||
public function testCanRejectWithSameValue(): void |
|||
{ |
|||
$p = new RejectedPromise('foo'); |
|||
$p->reject('foo'); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
} |
|||
|
|||
public function testThrowsSpecificException(): void |
|||
{ |
|||
$e = new \Exception(); |
|||
$p = new RejectedPromise($e); |
|||
try { |
|||
$p->wait(true); |
|||
$this->fail(); |
|||
} catch (\Exception $e2) { |
|||
$this->assertSame($e, $e2); |
|||
} |
|||
} |
|||
|
|||
public function testCannotResolveWithPromise(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
|
|||
new RejectedPromise(new Promise()); |
|||
} |
|||
|
|||
public function testReturnsSelfWhenNoOnReject(): void |
|||
{ |
|||
$p = new RejectedPromise('a'); |
|||
$this->assertSame($p, $p->then()); |
|||
} |
|||
|
|||
public function testInvokesOnRejectedAsynchronously(): void |
|||
{ |
|||
$p = new RejectedPromise('a'); |
|||
$r = null; |
|||
$f = function ($reason) use (&$r): void { $r = $reason; }; |
|||
$p->then(null, $f); |
|||
$this->assertNull($r); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('a', $r); |
|||
} |
|||
|
|||
public function testReturnsNewRejectedWhenOnRejectedFails(): void |
|||
{ |
|||
$p = new RejectedPromise('a'); |
|||
$f = function (): void { throw new \Exception('b'); }; |
|||
$p2 = $p->then(null, $f); |
|||
$this->assertNotSame($p, $p2); |
|||
try { |
|||
$p2->wait(); |
|||
$this->fail(); |
|||
} catch (\Exception $e) { |
|||
$this->assertSame('b', $e->getMessage()); |
|||
} |
|||
} |
|||
|
|||
public function testWaitingIsNoOp(): void |
|||
{ |
|||
$p = new RejectedPromise('a'); |
|||
$p->wait(false); |
|||
$this->assertTrue(P\Is::rejected($p)); |
|||
} |
|||
|
|||
public function testOtherwiseIsSugarForRejections(): void |
|||
{ |
|||
$p = new RejectedPromise('foo'); |
|||
$p->otherwise(function ($v) use (&$c): void { $c = $v; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo', $c); |
|||
} |
|||
|
|||
public function testCanResolveThenWithSuccess(): void |
|||
{ |
|||
$actual = null; |
|||
$p = new RejectedPromise('foo'); |
|||
$p->otherwise(function ($v) { |
|||
return $v.' bar'; |
|||
})->then(function ($v) use (&$actual): void { |
|||
$actual = $v; |
|||
}); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('foo bar', $actual); |
|||
} |
|||
|
|||
public function testDoesNotTryToRejectTwiceDuringTrampoline(): void |
|||
{ |
|||
$fp = new RejectedPromise('a'); |
|||
$t1 = $fp->then(null, function ($v) { return $v.' b'; }); |
|||
$t1->resolve('why!'); |
|||
$this->assertSame('why!', $t1->wait()); |
|||
} |
|||
} |
@ -1,30 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise\RejectionException; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Promise\RejectionException |
|||
*/ |
|||
class RejectionExceptionTest extends TestCase |
|||
{ |
|||
public function testCanGetReasonFromException(): void |
|||
{ |
|||
$thing = new Thing1('foo'); |
|||
$e = new RejectionException($thing); |
|||
|
|||
$this->assertSame($thing, $e->getReason()); |
|||
$this->assertSame('The promise was rejected with reason: foo', $e->getMessage()); |
|||
} |
|||
|
|||
public function testCanGetReasonMessageFromJson(): void |
|||
{ |
|||
$reason = new Thing2(); |
|||
$e = new RejectionException($reason); |
|||
$this->assertStringContainsString('{}', $e->getMessage()); |
|||
} |
|||
} |
@ -1,35 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise\TaskQueue; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class TaskQueueTest extends TestCase |
|||
{ |
|||
public function testKnowsIfEmpty(): void |
|||
{ |
|||
$tq = new TaskQueue(false); |
|||
$this->assertTrue($tq->isEmpty()); |
|||
} |
|||
|
|||
public function testKnowsIfFull(): void |
|||
{ |
|||
$tq = new TaskQueue(false); |
|||
$tq->add(function (): void {}); |
|||
$this->assertFalse($tq->isEmpty()); |
|||
} |
|||
|
|||
public function testExecutesTasksInOrder(): void |
|||
{ |
|||
$tq = new TaskQueue(false); |
|||
$called = []; |
|||
$tq->add(function () use (&$called): void { $called[] = 'a'; }); |
|||
$tq->add(function () use (&$called): void { $called[] = 'b'; }); |
|||
$tq->add(function () use (&$called): void { $called[] = 'c'; }); |
|||
$tq->run(); |
|||
$this->assertSame(['a', 'b', 'c'], $called); |
|||
} |
|||
} |
@ -1,27 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise\Promise; |
|||
|
|||
class Thennable |
|||
{ |
|||
private $nextPromise; |
|||
|
|||
public function __construct() |
|||
{ |
|||
$this->nextPromise = new Promise(); |
|||
} |
|||
|
|||
public function then(?callable $res = null, ?callable $rej = null) |
|||
{ |
|||
return $this->nextPromise->then($res, $rej); |
|||
} |
|||
|
|||
public function resolve($value): void |
|||
{ |
|||
$this->nextPromise->resolve($value); |
|||
} |
|||
} |
@ -1,20 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
class Thing1 |
|||
{ |
|||
private $message; |
|||
|
|||
public function __construct($message) |
|||
{ |
|||
$this->message = $message; |
|||
} |
|||
|
|||
public function __toString() |
|||
{ |
|||
return $this->message; |
|||
} |
|||
} |
@ -1,14 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
class Thing2 implements \JsonSerializable |
|||
{ |
|||
#[\ReturnTypeWillChange] |
|||
public function jsonSerialize() |
|||
{ |
|||
return '{}'; |
|||
} |
|||
} |
@ -1,734 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Promise\Tests; |
|||
|
|||
use GuzzleHttp\Promise\AggregateException; |
|||
use GuzzleHttp\Promise as P; |
|||
use GuzzleHttp\Promise\FulfilledPromise; |
|||
use GuzzleHttp\Promise\Promise; |
|||
use GuzzleHttp\Promise\PromiseInterface; |
|||
use GuzzleHttp\Promise\RejectedPromise; |
|||
use GuzzleHttp\Promise\RejectionException; |
|||
use GuzzleHttp\Promise\TaskQueue; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class UtilsTest extends TestCase |
|||
{ |
|||
public function testWaitsOnAllPromisesIntoArray(): void |
|||
{ |
|||
$e = new \Exception(); |
|||
$a = new Promise(function () use (&$a): void { $a->resolve('a'); }); |
|||
$b = new Promise(function () use (&$b): void { $b->reject('b'); }); |
|||
$c = new Promise(function () use (&$c, $e): void { $c->reject($e); }); |
|||
$results = P\Utils::inspectAll([$a, $b, $c]); |
|||
$this->assertSame([ |
|||
['state' => 'fulfilled', 'value' => 'a'], |
|||
['state' => 'rejected', 'reason' => 'b'], |
|||
['state' => 'rejected', 'reason' => $e], |
|||
], $results); |
|||
} |
|||
|
|||
public function testUnwrapsPromisesWithNoDefaultAndFailure(): void |
|||
{ |
|||
$this->expectException(RejectionException::class); |
|||
|
|||
$promises = [new FulfilledPromise('a'), new Promise()]; |
|||
P\Utils::unwrap($promises); |
|||
} |
|||
|
|||
public function testUnwrapsPromisesWithNoDefault(): void |
|||
{ |
|||
$promises = [new FulfilledPromise('a')]; |
|||
$this->assertSame(['a'], P\Utils::unwrap($promises)); |
|||
} |
|||
|
|||
public function testUnwrapsPromisesWithKeys(): void |
|||
{ |
|||
$promises = [ |
|||
'foo' => new FulfilledPromise('a'), |
|||
'bar' => new FulfilledPromise('b'), |
|||
]; |
|||
$this->assertSame([ |
|||
'foo' => 'a', |
|||
'bar' => 'b', |
|||
], P\Utils::unwrap($promises)); |
|||
} |
|||
|
|||
public function testAllAggregatesSortedArray(): void |
|||
{ |
|||
$a = new Promise(); |
|||
$b = new Promise(); |
|||
$c = new Promise(); |
|||
$d = P\Utils::all([$a, $b, $c]); |
|||
$b->resolve('b'); |
|||
$a->resolve('a'); |
|||
$c->resolve('c'); |
|||
$d->then( |
|||
function ($value) use (&$result): void { $result = $value; }, |
|||
function ($reason) use (&$result): void { $result = $reason; } |
|||
); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(['a', 'b', 'c'], $result); |
|||
} |
|||
|
|||
public function testPromisesDynamicallyAddedToStack(): void |
|||
{ |
|||
$promises = new \ArrayIterator(); |
|||
$counter = 0; |
|||
$promises['a'] = new FulfilledPromise('a'); |
|||
$promises['b'] = $promise = new Promise(function () use (&$promise, &$promises, &$counter): void { |
|||
++$counter; // Make sure the wait function is called only once |
|||
$promise->resolve('b'); |
|||
$promises['c'] = $subPromise = new Promise(function () use (&$subPromise): void { |
|||
$subPromise->resolve('c'); |
|||
}); |
|||
}); |
|||
$result = P\Utils::all($promises, true)->wait(); |
|||
$this->assertCount(3, $promises); |
|||
$this->assertCount(3, $result); |
|||
$this->assertSame($result['c'], 'c'); |
|||
$this->assertSame(1, $counter); |
|||
} |
|||
|
|||
public function testAllThrowsWhenAnyRejected(): void |
|||
{ |
|||
$a = new Promise(); |
|||
$b = new Promise(); |
|||
$c = new Promise(); |
|||
$d = P\Utils::all([$a, $b, $c]); |
|||
$b->resolve('b'); |
|||
$a->reject('fail'); |
|||
$c->resolve('c'); |
|||
$d->then( |
|||
function ($value) use (&$result): void { $result = $value; }, |
|||
function ($reason) use (&$result): void { $result = $reason; } |
|||
); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('fail', $result); |
|||
} |
|||
|
|||
public function testSomeAggregatesSortedArrayWithMax(): void |
|||
{ |
|||
$a = new Promise(); |
|||
$b = new Promise(); |
|||
$c = new Promise(); |
|||
$d = P\Utils::some(2, [$a, $b, $c]); |
|||
$b->resolve('b'); |
|||
$c->resolve('c'); |
|||
$a->resolve('a'); |
|||
$d->then(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(['b', 'c'], $result); |
|||
} |
|||
|
|||
public function testSomeRejectsWhenTooManyRejections(): void |
|||
{ |
|||
$a = new Promise(); |
|||
$b = new Promise(); |
|||
$d = P\Utils::some(2, [$a, $b]); |
|||
$a->reject('bad'); |
|||
$b->resolve('good'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue(P\Is::rejected($d)); |
|||
$d->then(null, function ($reason) use (&$called): void { |
|||
$called = $reason; |
|||
}); |
|||
P\Utils::queue()->run(); |
|||
$this->assertInstanceOf(AggregateException::class, $called); |
|||
$this->assertContains('bad', $called->getReason()); |
|||
} |
|||
|
|||
public function testCanWaitUntilSomeCountIsSatisfied(): void |
|||
{ |
|||
$a = new Promise(function () use (&$a): void { $a->resolve('a'); }); |
|||
$b = new Promise(function () use (&$b): void { $b->resolve('b'); }); |
|||
$c = new Promise(function () use (&$c): void { $c->resolve('c'); }); |
|||
$d = P\Utils::some(2, [$a, $b, $c]); |
|||
$this->assertSame(['a', 'b'], $d->wait()); |
|||
} |
|||
|
|||
public function testThrowsIfImpossibleToWaitForSomeCount(): void |
|||
{ |
|||
$this->expectException(AggregateException::class); |
|||
$this->expectExceptionMessage('Not enough promises to fulfill count'); |
|||
|
|||
$a = new Promise(function () use (&$a): void { $a->resolve('a'); }); |
|||
$d = P\Utils::some(2, [$a]); |
|||
$d->wait(); |
|||
} |
|||
|
|||
public function testThrowsIfResolvedWithoutCountTotalResults(): void |
|||
{ |
|||
$this->expectException(AggregateException::class); |
|||
$this->expectExceptionMessage('Not enough promises to fulfill count'); |
|||
|
|||
$a = new Promise(); |
|||
$b = new Promise(); |
|||
$d = P\Utils::some(3, [$a, $b]); |
|||
$a->resolve('a'); |
|||
$b->resolve('b'); |
|||
$d->wait(); |
|||
} |
|||
|
|||
public function testAnyReturnsFirstMatch(): void |
|||
{ |
|||
$a = new Promise(); |
|||
$b = new Promise(); |
|||
$c = P\Utils::any([$a, $b]); |
|||
$b->resolve('b'); |
|||
$a->resolve('a'); |
|||
$c->then(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('b', $result); |
|||
} |
|||
|
|||
public function testSettleFulfillsWithFulfilledAndRejected(): void |
|||
{ |
|||
$a = new Promise(); |
|||
$b = new Promise(); |
|||
$c = new Promise(); |
|||
$d = P\Utils::settle([$a, $b, $c]); |
|||
$b->resolve('b'); |
|||
$c->resolve('c'); |
|||
$a->reject('a'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue(P\Is::fulfilled($d)); |
|||
$d->then(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame([ |
|||
['state' => 'rejected', 'reason' => 'a'], |
|||
['state' => 'fulfilled', 'value' => 'b'], |
|||
['state' => 'fulfilled', 'value' => 'c'], |
|||
], $result); |
|||
} |
|||
|
|||
public function testCanInspectFulfilledPromise(): void |
|||
{ |
|||
$p = new FulfilledPromise('foo'); |
|||
$this->assertSame([ |
|||
'state' => 'fulfilled', |
|||
'value' => 'foo', |
|||
], P\Utils::inspect($p)); |
|||
} |
|||
|
|||
public function testCanInspectRejectedPromise(): void |
|||
{ |
|||
$p = new RejectedPromise('foo'); |
|||
$this->assertSame([ |
|||
'state' => 'rejected', |
|||
'reason' => 'foo', |
|||
], P\Utils::inspect($p)); |
|||
} |
|||
|
|||
public function testCanInspectRejectedPromiseWithNormalException(): void |
|||
{ |
|||
$e = new \Exception('foo'); |
|||
$p = new RejectedPromise($e); |
|||
$this->assertSame([ |
|||
'state' => 'rejected', |
|||
'reason' => $e, |
|||
], P\Utils::inspect($p)); |
|||
} |
|||
|
|||
public function testReturnsTrampoline(): void |
|||
{ |
|||
$this->assertInstanceOf(TaskQueue::class, P\Utils::queue()); |
|||
$this->assertSame(P\Utils::queue(), P\Utils::queue()); |
|||
} |
|||
|
|||
public function testCanScheduleThunk(): void |
|||
{ |
|||
$tramp = P\Utils::queue(); |
|||
$promise = P\Utils::task(function () { return 'Hi!'; }); |
|||
$c = null; |
|||
$promise->then(function ($v) use (&$c): void { $c = $v; }); |
|||
$this->assertNull($c); |
|||
$tramp->run(); |
|||
$this->assertSame('Hi!', $c); |
|||
} |
|||
|
|||
public function testCanScheduleThunkWithRejection(): void |
|||
{ |
|||
$tramp = P\Utils::queue(); |
|||
$promise = P\Utils::task(function (): void { throw new \Exception('Hi!'); }); |
|||
$c = null; |
|||
$promise->otherwise(function ($v) use (&$c): void { $c = $v; }); |
|||
$this->assertNull($c); |
|||
$tramp->run(); |
|||
$this->assertSame('Hi!', $c->getMessage()); |
|||
} |
|||
|
|||
public function testCanScheduleThunkWithWait(): void |
|||
{ |
|||
$tramp = P\Utils::queue(); |
|||
$promise = P\Utils::task(function () { return 'a'; }); |
|||
$this->assertSame('a', $promise->wait()); |
|||
$tramp->run(); |
|||
} |
|||
|
|||
public function testYieldsFromCoroutine(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promise = P\Coroutine::of(function () { |
|||
$value = (yield new FulfilledPromise('a')); |
|||
yield $value.'b'; |
|||
}); |
|||
$promise->then(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('ab', $result); |
|||
} |
|||
|
|||
public function testCanCatchExceptionsInCoroutine(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promise = P\Coroutine::of(function () { |
|||
try { |
|||
yield new RejectedPromise('a'); |
|||
$this->fail('Should have thrown into the coroutine!'); |
|||
} catch (RejectionException $e) { |
|||
$value = (yield new FulfilledPromise($e->getReason())); |
|||
yield $value.'b'; |
|||
} |
|||
}); |
|||
$promise->then(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue(P\Is::fulfilled($promise)); |
|||
$this->assertSame('ab', $result); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider rejectsParentExceptionProvider |
|||
*/ |
|||
public function testRejectsParentExceptionWhenException(PromiseInterface $promise): void |
|||
{ |
|||
$promise->then( |
|||
function (): void { $this->fail(); }, |
|||
function ($reason) use (&$result): void { $result = $reason; } |
|||
); |
|||
P\Utils::queue()->run(); |
|||
$this->assertInstanceOf(\Exception::class, $result); |
|||
$this->assertSame('a', $result->getMessage()); |
|||
} |
|||
|
|||
public function rejectsParentExceptionProvider() |
|||
{ |
|||
return [ |
|||
[P\Coroutine::of(function () { |
|||
yield new FulfilledPromise(0); |
|||
throw new \Exception('a'); |
|||
})], |
|||
[P\Coroutine::of(function () { |
|||
throw new \Exception('a'); |
|||
yield new FulfilledPromise(0); |
|||
})], |
|||
]; |
|||
} |
|||
|
|||
public function testCanRejectFromRejectionCallback(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promise = P\Coroutine::of(function () { |
|||
yield new FulfilledPromise(0); |
|||
yield new RejectedPromise('no!'); |
|||
}); |
|||
$promise->then( |
|||
function (): void { $this->fail(); }, |
|||
function ($reason) use (&$result): void { $result = $reason; } |
|||
); |
|||
P\Utils::queue()->run(); |
|||
$this->assertInstanceOf(RejectionException::class, $result); |
|||
$this->assertSame('no!', $result->getReason()); |
|||
} |
|||
|
|||
public function testCanAsyncReject(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$rej = new Promise(); |
|||
$promise = P\Coroutine::of(function () use ($rej) { |
|||
yield new FulfilledPromise(0); |
|||
yield $rej; |
|||
}); |
|||
$promise->then( |
|||
function (): void { $this->fail(); }, |
|||
function ($reason) use (&$result): void { $result = $reason; } |
|||
); |
|||
$rej->reject('no!'); |
|||
P\Utils::queue()->run(); |
|||
$this->assertInstanceOf(RejectionException::class, $result); |
|||
$this->assertSame('no!', $result->getReason()); |
|||
} |
|||
|
|||
public function testCanCatchAndThrowOtherException(): void |
|||
{ |
|||
$promise = P\Coroutine::of(function () { |
|||
try { |
|||
yield new RejectedPromise('a'); |
|||
$this->fail('Should have thrown into the coroutine!'); |
|||
} catch (RejectionException $e) { |
|||
throw new \Exception('foo'); |
|||
} |
|||
}); |
|||
$promise->otherwise(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue(P\Is::rejected($promise)); |
|||
$this->assertStringContainsString('foo', $result->getMessage()); |
|||
} |
|||
|
|||
public function testCanCatchAndYieldOtherException(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promise = P\Coroutine::of(function () { |
|||
try { |
|||
yield new RejectedPromise('a'); |
|||
$this->fail('Should have thrown into the coroutine!'); |
|||
} catch (RejectionException $e) { |
|||
yield new RejectedPromise('foo'); |
|||
} |
|||
}); |
|||
$promise->otherwise(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertTrue(P\Is::rejected($promise)); |
|||
$this->assertStringContainsString('foo', $result->getMessage()); |
|||
} |
|||
|
|||
public function createLotsOfSynchronousPromise() |
|||
{ |
|||
return P\Coroutine::of(function () { |
|||
$value = 0; |
|||
for ($i = 0; $i < 1000; ++$i) { |
|||
$value = (yield new FulfilledPromise($i)); |
|||
} |
|||
yield $value; |
|||
}); |
|||
} |
|||
|
|||
public function testLotsOfSynchronousDoesNotBlowStack(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promise = $this->createLotsOfSynchronousPromise(); |
|||
$promise->then(function ($v) use (&$r): void { $r = $v; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(999, $r); |
|||
} |
|||
|
|||
public function testLotsOfSynchronousWaitDoesNotBlowStack(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promise = $this->createLotsOfSynchronousPromise(); |
|||
$promise->then(function ($v) use (&$r): void { $r = $v; }); |
|||
$this->assertSame(999, $promise->wait()); |
|||
$this->assertSame(999, $r); |
|||
} |
|||
|
|||
private function createLotsOfFlappingPromise() |
|||
{ |
|||
return P\Coroutine::of(function () { |
|||
$value = 0; |
|||
for ($i = 0; $i < 1000; ++$i) { |
|||
try { |
|||
if ($i % 2) { |
|||
$value = (yield new FulfilledPromise($i)); |
|||
} else { |
|||
$value = (yield new RejectedPromise($i)); |
|||
} |
|||
} catch (\Exception $e) { |
|||
$value = (yield new FulfilledPromise($i)); |
|||
} |
|||
} |
|||
yield $value; |
|||
}); |
|||
} |
|||
|
|||
public function testLotsOfTryCatchingDoesNotBlowStack(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promise = $this->createLotsOfFlappingPromise(); |
|||
$promise->then(function ($v) use (&$r): void { $r = $v; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(999, $r); |
|||
} |
|||
|
|||
public function testLotsOfTryCatchingWaitingDoesNotBlowStack(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promise = $this->createLotsOfFlappingPromise(); |
|||
$promise->then(function ($v) use (&$r): void { $r = $v; }); |
|||
$this->assertSame(999, $promise->wait()); |
|||
$this->assertSame(999, $r); |
|||
} |
|||
|
|||
public function testAsyncPromisesWithCorrectlyYieldedValues(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promises = [ |
|||
new Promise(), |
|||
new Promise(), |
|||
new Promise(), |
|||
]; |
|||
|
|||
eval(' |
|||
$promise = \GuzzleHttp\Promise\Coroutine::of(function () use ($promises) { |
|||
$value = null; |
|||
$this->assertSame(\'skip\', (yield new \GuzzleHttp\Promise\FulfilledPromise(\'skip\'))); |
|||
foreach ($promises as $idx => $p) { |
|||
$value = (yield $p); |
|||
$this->assertSame($idx, $value); |
|||
$this->assertSame(\'skip\', (yield new \GuzzleHttp\Promise\FulfilledPromise(\'skip\'))); |
|||
} |
|||
$this->assertSame(\'skip\', (yield new \GuzzleHttp\Promise\FulfilledPromise(\'skip\'))); |
|||
yield $value; |
|||
}); |
|||
'); |
|||
|
|||
$promises[0]->resolve(0); |
|||
$promises[1]->resolve(1); |
|||
$promises[2]->resolve(2); |
|||
|
|||
$promise->then(function ($v) use (&$r): void { $r = $v; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(2, $r); |
|||
} |
|||
|
|||
public function testYieldFinalWaitablePromise(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$p1 = new Promise(function () use (&$p1): void { |
|||
$p1->resolve('skip me'); |
|||
}); |
|||
$p2 = new Promise(function () use (&$p2): void { |
|||
$p2->resolve('hello!'); |
|||
}); |
|||
$co = P\Coroutine::of(function () use ($p1, $p2) { |
|||
yield $p1; |
|||
yield $p2; |
|||
}); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('hello!', $co->wait()); |
|||
} |
|||
|
|||
public function testCanYieldFinalPendingPromise(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$p1 = new Promise(); |
|||
$p2 = new Promise(); |
|||
$co = P\Coroutine::of(function () use ($p1, $p2) { |
|||
yield $p1; |
|||
yield $p2; |
|||
}); |
|||
$p1->resolve('a'); |
|||
$p2->resolve('b'); |
|||
$co->then(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('b', $result); |
|||
} |
|||
|
|||
public function testCanNestYieldsAndFailures(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$p1 = new Promise(); |
|||
$p2 = new Promise(); |
|||
$p3 = new Promise(); |
|||
$p4 = new Promise(); |
|||
$p5 = new Promise(); |
|||
$co = P\Coroutine::of(function () use ($p1, $p2, $p3, $p4, $p5) { |
|||
try { |
|||
yield $p1; |
|||
} catch (\Exception $e) { |
|||
yield $p2; |
|||
try { |
|||
yield $p3; |
|||
yield $p4; |
|||
} catch (\Exception $e) { |
|||
yield $p5; |
|||
} |
|||
} |
|||
}); |
|||
$p1->reject('a'); |
|||
$p2->resolve('b'); |
|||
$p3->resolve('c'); |
|||
$p4->reject('d'); |
|||
$p5->resolve('e'); |
|||
$co->then(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame('e', $result); |
|||
} |
|||
|
|||
public function testCanYieldErrorsAndSuccessesWithoutRecursion(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$promises = []; |
|||
for ($i = 0; $i < 20; ++$i) { |
|||
$promises[] = new Promise(); |
|||
} |
|||
|
|||
$co = P\Coroutine::of(function () use ($promises) { |
|||
for ($i = 0; $i < 20; $i += 4) { |
|||
try { |
|||
yield $promises[$i]; |
|||
yield $promises[$i + 1]; |
|||
} catch (\Exception $e) { |
|||
yield $promises[$i + 2]; |
|||
yield $promises[$i + 3]; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
for ($i = 0; $i < 20; $i += 4) { |
|||
$promises[$i]->resolve($i); |
|||
$promises[$i + 1]->reject($i + 1); |
|||
$promises[$i + 2]->resolve($i + 2); |
|||
$promises[$i + 3]->resolve($i + 3); |
|||
} |
|||
|
|||
$co->then(function ($value) use (&$result): void { $result = $value; }); |
|||
P\Utils::queue()->run(); |
|||
$this->assertSame(19, $result); |
|||
} |
|||
|
|||
public function testCanWaitOnPromiseAfterFulfilled(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$f = function () { |
|||
static $i = 0; |
|||
++$i; |
|||
|
|||
return $p = new Promise(function () use (&$p, $i): void { |
|||
$p->resolve($i.'-bar'); |
|||
}); |
|||
}; |
|||
|
|||
$promises = []; |
|||
for ($i = 0; $i < 20; ++$i) { |
|||
$promises[] = $f(); |
|||
} |
|||
|
|||
$p = P\Coroutine::of(function () use ($promises) { |
|||
yield new FulfilledPromise('foo!'); |
|||
foreach ($promises as $promise) { |
|||
yield $promise; |
|||
} |
|||
}); |
|||
|
|||
$this->assertSame('20-bar', $p->wait()); |
|||
} |
|||
|
|||
public function testCanWaitOnErroredPromises(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$p1 = new Promise(function () use (&$p1): void { $p1->reject('a'); }); |
|||
$p2 = new Promise(function () use (&$p2): void { $p2->resolve('b'); }); |
|||
$p3 = new Promise(function () use (&$p3): void { $p3->resolve('c'); }); |
|||
$p4 = new Promise(function () use (&$p4): void { $p4->reject('d'); }); |
|||
$p5 = new Promise(function () use (&$p5): void { $p5->resolve('e'); }); |
|||
$p6 = new Promise(function () use (&$p6): void { $p6->reject('f'); }); |
|||
|
|||
$co = P\Coroutine::of(function () use ($p1, $p2, $p3, $p4, $p5, $p6) { |
|||
try { |
|||
yield $p1; |
|||
} catch (\Exception $e) { |
|||
yield $p2; |
|||
try { |
|||
yield $p3; |
|||
yield $p4; |
|||
} catch (\Exception $e) { |
|||
yield $p5; |
|||
yield $p6; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
$res = P\Utils::inspect($co); |
|||
$this->assertSame('f', $res['reason']); |
|||
} |
|||
|
|||
public function testCoroutineOtherwiseIntegrationTest(): void |
|||
{ |
|||
if (defined('HHVM_VERSION')) { |
|||
$this->markTestIncomplete('Broken on HHVM.'); |
|||
} |
|||
|
|||
$a = new Promise(); |
|||
$b = new Promise(); |
|||
$promise = P\Coroutine::of(function () use ($a, $b) { |
|||
// Execute the pool of commands concurrently, and process errors. |
|||
yield $a; |
|||
yield $b; |
|||
})->otherwise(function (\Exception $e): void { |
|||
// Throw errors from the operations as a specific Multipart error. |
|||
throw new \OutOfBoundsException('a', 0, $e); |
|||
}); |
|||
$a->resolve('a'); |
|||
$b->reject('b'); |
|||
$reason = P\Utils::inspect($promise)['reason']; |
|||
$this->assertInstanceOf(\OutOfBoundsException::class, $reason); |
|||
$this->assertInstanceOf(RejectionException::class, $reason->getPrevious()); |
|||
} |
|||
|
|||
public function testCanManuallySettleTaskQueueGeneratedPromises(): void |
|||
{ |
|||
$p1 = P\Utils::task(function () { return 'a'; }); |
|||
$p2 = P\Utils::task(function () { return 'b'; }); |
|||
$p3 = P\Utils::task(function () { return 'c'; }); |
|||
|
|||
$p1->cancel(); |
|||
$p2->resolve('b2'); |
|||
|
|||
$results = P\Utils::inspectAll([$p1, $p2, $p3]); |
|||
|
|||
$this->assertSame([ |
|||
['state' => 'rejected', 'reason' => 'Promise has been cancelled'], |
|||
['state' => 'fulfilled', 'value' => 'b2'], |
|||
['state' => 'fulfilled', 'value' => 'c'], |
|||
], $results); |
|||
} |
|||
} |
@ -1,9 +0,0 @@ |
|||
{ |
|||
"require": { |
|||
"php": "^7.4 || ^8.0", |
|||
"friendsofphp/php-cs-fixer": "3.68.5" |
|||
}, |
|||
"config": { |
|||
"preferred-install": "dist" |
|||
} |
|||
} |
@ -1,10 +0,0 @@ |
|||
{ |
|||
"require": { |
|||
"php": "^7.4 || ^8.0", |
|||
"phpstan/phpstan": "2.1.2", |
|||
"phpstan/phpstan-deprecation-rules": "2.0.1" |
|||
}, |
|||
"config": { |
|||
"preferred-install": "dist" |
|||
} |
|||
} |
@ -1,9 +0,0 @@ |
|||
{ |
|||
"require": { |
|||
"php": "^7.4 || ^8.0", |
|||
"psalm/phar": "5.26.1" |
|||
}, |
|||
"config": { |
|||
"preferred-install": "dist" |
|||
} |
|||
} |
@ -1,12 +0,0 @@ |
|||
root = true |
|||
|
|||
[*] |
|||
charset = utf-8 |
|||
end_of_line = lf |
|||
indent_size = 4 |
|||
indent_style = space |
|||
insert_final_newline = true |
|||
trim_trailing_whitespace = true |
|||
|
|||
[{Makefile, *.mk}] |
|||
indent_style = tab |
@ -1,13 +0,0 @@ |
|||
.editorconfig export-ignore |
|||
.gitattributes export-ignore |
|||
/.github/ export-ignore |
|||
.gitignore export-ignore |
|||
.php-cs-fixer.dist.php export-ignore |
|||
/Makefile export-ignore |
|||
/phpstan-baseline.neon export-ignore |
|||
/phpstan.neon.dist export-ignore |
|||
/phpunit.xml.dist export-ignore |
|||
/psalm-baseline.xml export-ignore |
|||
/psalm.xml export-ignore |
|||
/tests/ export-ignore |
|||
/vendor-bin/ export-ignore |
@ -1,2 +0,0 @@ |
|||
[*.yml] |
|||
indent_size = 2 |
@ -1,2 +0,0 @@ |
|||
github: [Nyholm, GrahamCampbell] |
|||
tidelift: "packagist/guzzlehttp/psr7" |
@ -1,14 +0,0 @@ |
|||
daysUntilStale: 120 |
|||
daysUntilClose: 14 |
|||
exemptLabels: |
|||
- lifecycle/keep-open |
|||
- lifecycle/ready-for-merge |
|||
# Label to use when marking an issue as stale |
|||
staleLabel: lifecycle/stale |
|||
# Comment to post when marking an issue as stale. Set to `false` to disable |
|||
markComment: > |
|||
This issue has been automatically marked as stale because it has not had |
|||
recent activity. It will be closed after 2 weeks if no further activity occurs. Thank you |
|||
for your contributions. |
|||
# Comment to post when closing a stale issue. Set to `false` to disable |
|||
closeComment: false |
@ -1,21 +0,0 @@ |
|||
name: Checks |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
pull_request: |
|||
|
|||
permissions: |
|||
contents: read |
|||
|
|||
jobs: |
|||
composer-normalize: |
|||
name: Composer Normalize |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Composer normalize |
|||
uses: docker://ergebnis/composer-normalize-action |
@ -1,58 +0,0 @@ |
|||
name: CI |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
pull_request: |
|||
|
|||
permissions: |
|||
contents: read |
|||
|
|||
jobs: |
|||
build-lowest-version: |
|||
name: Build lowest version |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Set up PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: '7.2' |
|||
ini-values: error_reporting=E_ALL |
|||
coverage: 'none' |
|||
extensions: mbstring |
|||
|
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Install dependencies |
|||
run: composer update --no-interaction --prefer-stable --prefer-lowest --no-progress |
|||
|
|||
- name: Run tests |
|||
run: vendor/bin/phpunit --testsuite "Guzzle PSR-7 Unit Test Suite" |
|||
|
|||
build: |
|||
name: Build |
|||
runs-on: ubuntu-22.04 |
|||
strategy: |
|||
max-parallel: 10 |
|||
matrix: |
|||
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] |
|||
|
|||
steps: |
|||
- name: Set up PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: ${{ matrix.php }} |
|||
ini-values: error_reporting=E_ALL |
|||
coverage: 'none' |
|||
extensions: mbstring |
|||
|
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Install dependencies |
|||
run: composer update --no-interaction --no-progress |
|||
|
|||
- name: Run tests |
|||
run: vendor/bin/phpunit --testsuite "Guzzle PSR-7 Unit Test Suite" |
@ -1,45 +0,0 @@ |
|||
name: Integration |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
pull_request: |
|||
|
|||
permissions: |
|||
contents: read |
|||
|
|||
jobs: |
|||
build: |
|||
name: Test |
|||
runs-on: ubuntu-22.04 |
|||
strategy: |
|||
max-parallel: 10 |
|||
matrix: |
|||
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] |
|||
|
|||
steps: |
|||
- name: Set up PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: ${{ matrix.php }} |
|||
ini-values: error_reporting=E_ALL |
|||
coverage: none |
|||
|
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Install dependencies |
|||
run: composer update --no-interaction --no-progress |
|||
|
|||
- name: Start server |
|||
run: php -S 127.0.0.1:10002 tests/Integration/server.php & |
|||
|
|||
- name: Run Guzzle tests |
|||
env: |
|||
TEST_SERVER: 127.0.0.1:10002 |
|||
run: vendor/bin/phpunit --testsuite "Guzzle PSR-7 Integration Test Suite" |
|||
|
|||
- name: Run Interop tests |
|||
env: |
|||
TEST_SERVER: 127.0.0.1:10002 |
|||
run: vendor/bin/phpunit --testsuite "PSR-17 Integration Test Suite" |
@ -1,82 +0,0 @@ |
|||
name: Static analysis |
|||
|
|||
on: |
|||
push: |
|||
branches: |
|||
pull_request: |
|||
|
|||
permissions: |
|||
contents: read |
|||
|
|||
jobs: |
|||
phpstan: |
|||
name: PHPStan |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Setup PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: '7.4' |
|||
coverage: none |
|||
extensions: mbstring |
|||
|
|||
- name: Download dependencies |
|||
run: composer update --no-interaction --no-progress |
|||
|
|||
- name: Download PHPStan |
|||
run: composer bin phpstan update --no-interaction --no-progress |
|||
|
|||
- name: Execute PHPStan |
|||
run: vendor/bin/phpstan analyze --no-progress |
|||
|
|||
php-cs-fixer: |
|||
name: PHP-CS-Fixer |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Setup PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: '7.4' |
|||
coverage: none |
|||
extensions: mbstring |
|||
|
|||
- name: Download dependencies |
|||
run: composer update --no-interaction --no-progress |
|||
|
|||
- name: Download PHP CS Fixer |
|||
run: composer bin php-cs-fixer update --no-interaction --no-progress |
|||
|
|||
- name: Execute PHP CS Fixer |
|||
run: vendor/bin/php-cs-fixer fix --diff --dry-run |
|||
|
|||
psalm: |
|||
name: Psalm |
|||
runs-on: ubuntu-22.04 |
|||
|
|||
steps: |
|||
- name: Checkout code |
|||
uses: actions/checkout@v4 |
|||
|
|||
- name: Setup PHP |
|||
uses: shivammathur/setup-php@v2 |
|||
with: |
|||
php-version: '7.4' |
|||
coverage: none |
|||
extensions: mbstring |
|||
|
|||
- name: Download dependencies |
|||
run: composer update --no-interaction --no-progress |
|||
|
|||
- name: Download Psalm |
|||
run: composer bin psalm update --no-interaction --no-progress |
|||
|
|||
- name: Execute Psalm |
|||
run: vendor/bin/psalm.phar --no-progress --output-format=github |
@ -1,7 +0,0 @@ |
|||
.php-cs-fixer.php |
|||
.php-cs-fixer.cache |
|||
.phpunit.result.cache |
|||
composer.lock |
|||
vendor/ |
|||
/phpstan.neon |
|||
/phpunit.xml |
@ -1,28 +0,0 @@ |
|||
<?php |
|||
|
|||
$config = (new PhpCsFixer\Config()) |
|||
->setRiskyAllowed(true) |
|||
->setRules([ |
|||
'@PHP71Migration:risky' => true, |
|||
'@PHPUnit75Migration:risky' => true, |
|||
'@PSR12:risky' => true, |
|||
'@Symfony' => true, |
|||
'global_namespace_import' => false, |
|||
'no_superfluous_phpdoc_tags' => [ |
|||
'allow_mixed' => true, |
|||
], |
|||
'phpdoc_annotation_without_dot' => false, |
|||
'phpdoc_summary' => false, |
|||
'phpdoc_to_comment' => false, |
|||
'single_line_throw' => false, |
|||
'yoda_style' => false, |
|||
]) |
|||
->setFinder( |
|||
PhpCsFixer\Finder::create() |
|||
->in(__DIR__.'/src') |
|||
->in(__DIR__.'/tests') |
|||
->name('*.php') |
|||
) |
|||
; |
|||
|
|||
return $config; |
@ -1,18 +0,0 @@ |
|||
test: |
|||
vendor/bin/phpunit $(TEST) |
|||
|
|||
check-tag: |
|||
$(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1")) |
|||
|
|||
tag: check-tag |
|||
@echo Tagging $(TAG) |
|||
chag update $(TAG) |
|||
git commit -a -m '$(TAG) release' |
|||
chag tag |
|||
@echo "Release has been created. Push using 'make release'" |
|||
@echo "Changes made in the release commit" |
|||
git diff HEAD~1 HEAD |
|||
|
|||
release: check-tag |
|||
git push origin master |
|||
git push origin $(TAG) |
@ -1,751 +0,0 @@ |
|||
parameters: |
|||
ignoreErrors: |
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/AppendStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\BufferStream\:\:getSize\(\) never returns null so it can be removed from the return type\.$#' |
|||
identifier: return.unusedType |
|||
count: 1 |
|||
path: src/BufferStream.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/CachingStream.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/DroppingStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn___toString\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_close\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_detach\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_eof\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_getContents\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_getMetadata\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_getSize\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_isReadable\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_isSeekable\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_isWritable\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_read\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_rewind\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_seek\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_tell\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Access to an undefined property GuzzleHttp\\Psr7\\FnStream\:\:\$_fn_write\.$#' |
|||
identifier: property.notFound |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:detach\(\) should return resource\|null but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:eof\(\) should return bool but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:getContents\(\) should return string but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:getSize\(\) should return int\|null but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:isReadable\(\) should return bool but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:isSeekable\(\) should return bool but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:isWritable\(\) should return bool but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:read\(\) should return string but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:tell\(\) should return int but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\FnStream\:\:write\(\) should return int but returns mixed\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Trying to invoke mixed but it''s not a callable\.$#' |
|||
identifier: callable.nonCallable |
|||
count: 16 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/FnStream.php |
|||
|
|||
- |
|||
message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' |
|||
identifier: function.alreadyNarrowedType |
|||
count: 1 |
|||
path: src/Header.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$values of static method GuzzleHttp\\Psr7\\Header\:\:splitList\(\) expects array\<string\>\|string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 2 |
|||
path: src/Header.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$character_mask of function trim expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 3 |
|||
path: src/Header.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$method of class GuzzleHttp\\Psr7\\ServerRequest constructor expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/HttpFactory.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/InflateStream.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/LazyOpenStream.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/LimitStream.php |
|||
|
|||
- |
|||
message: '#^Binary operation "\." between ''Invalid response…'' and mixed results in an error\.$#' |
|||
identifier: binaryOp.invalid |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Binary operation "\." between ''http\://''\|''https\://'' and mixed results in an error\.$#' |
|||
identifier: binaryOp.invalid |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Cannot access offset 0 on mixed\.$#' |
|||
identifier: offsetAccess.nonOffsetAccessible |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^PHPDoc tag @var with type array\<array\> is not subtype of native type list\<array\<string\>\>\.$#' |
|||
identifier: varTag.nativeType |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$haystack of function substr_count expects string, string\|null given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$string of function substr expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$headers of class GuzzleHttp\\Psr7\\Response constructor expects array\<array\<string\>\|string\>, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$headers of static method GuzzleHttp\\Psr7\\Message\:\:parseRequestUri\(\) expects array, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$str of function explode expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 2 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 2 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$subject of function preg_match expects string, string\|null given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$subject of function preg_match_all expects string, string\|null given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#3 \$body of class GuzzleHttp\\Psr7\\Response constructor expects Psr\\Http\\Message\\StreamInterface\|resource\|string\|null, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#3 \$headers of class GuzzleHttp\\Psr7\\Request constructor expects array\<array\<string\>\|string\>, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#4 \$body of class GuzzleHttp\\Psr7\\Request constructor expects Psr\\Http\\Message\\StreamInterface\|resource\|string\|null, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Variable \$headerLines in PHPDoc tag @var does not match assigned variable \$count\.$#' |
|||
identifier: varTag.differentVariable |
|||
count: 1 |
|||
path: src/Message.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$name of method GuzzleHttp\\Psr7\\MultipartStream\:\:createElement\(\) expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/MultipartStream.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$resource of static method GuzzleHttp\\Psr7\\Utils\:\:streamFor\(\) expects bool\|\(callable\(\)\: mixed\)\|float\|int\|Iterator\|Psr\\Http\\Message\\StreamInterface\|resource\|string\|null, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/MultipartStream.php |
|||
|
|||
- |
|||
message: '#^Parameter \#3 \$filename of method GuzzleHttp\\Psr7\\MultipartStream\:\:createElement\(\) expects string\|null, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/MultipartStream.php |
|||
|
|||
- |
|||
message: '#^Parameter \#4 \$headers of method GuzzleHttp\\Psr7\\MultipartStream\:\:createElement\(\) expects array\<string\>, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/MultipartStream.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/MultipartStream.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/NoSeekStream.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/PumpStream.php |
|||
|
|||
- |
|||
message: '#^Cannot cast mixed to int\.$#' |
|||
identifier: cast.int |
|||
count: 1 |
|||
path: src/Query.php |
|||
|
|||
- |
|||
message: '#^Cannot cast mixed to string\.$#' |
|||
identifier: cast.string |
|||
count: 3 |
|||
path: src/Query.php |
|||
|
|||
- |
|||
message: '#^Cannot access offset int\|string on mixed\.$#' |
|||
identifier: offsetAccess.nonOffsetAccessible |
|||
count: 5 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Cannot cast mixed to int\.$#' |
|||
identifier: cast.int |
|||
count: 2 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\ServerRequest\:\:normalizeNestedFileSpec\(\) should return array\<Psr\\Http\\Message\\UploadedFileInterface\> but returns array\<int\|string, array\<Psr\\Http\\Message\\UploadedFileInterface\>\|Psr\\Http\\Message\\UploadedFileInterface\>\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$authority of static method GuzzleHttp\\Psr7\\ServerRequest\:\:extractHostAndPortFromAuthority\(\) expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$host of method GuzzleHttp\\Psr7\\Uri\:\:withHost\(\) expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 3 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$input of function array_keys expects array, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$method of class GuzzleHttp\\Psr7\\ServerRequest constructor expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$port of method GuzzleHttp\\Psr7\\Uri\:\:withPort\(\) expects int\|null, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 2 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$query of method GuzzleHttp\\Psr7\\Uri\:\:withQuery\(\) expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$streamOrFile of class GuzzleHttp\\Psr7\\UploadedFile constructor expects Psr\\Http\\Message\\StreamInterface\|resource\|string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$str of function explode expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#3 \$headers of class GuzzleHttp\\Psr7\\ServerRequest constructor expects array\<array\<string\>\|string\>, array given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#3 \$subject of function str_replace expects array\<string\>\|string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#4 \$clientFilename of class GuzzleHttp\\Psr7\\UploadedFile constructor expects string\|null, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Parameter \#5 \$clientMediaType of class GuzzleHttp\\Psr7\\UploadedFile constructor expects string\|null, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/ServerRequest.php |
|||
|
|||
- |
|||
message: '#^Offset ''size'' on array\{0\: int, 1\: int, 2\: int, 3\: int, 4\: int, 5\: int, 6\: int, 7\: int, \.\.\.\} in isset\(\) always exists and is not nullable\.$#' |
|||
identifier: isset.offset |
|||
count: 1 |
|||
path: src/Stream.php |
|||
|
|||
- |
|||
message: '#^Property GuzzleHttp\\Psr7\\Stream\:\:\$stream \(resource\) in isset\(\) is not nullable\.$#' |
|||
identifier: isset.property |
|||
count: 10 |
|||
path: src/Stream.php |
|||
|
|||
- |
|||
message: '#^Property GuzzleHttp\\Psr7\\Stream\:\:\$uri \(string\|null\) does not accept mixed\.$#' |
|||
identifier: assign.propertyType |
|||
count: 1 |
|||
path: src/Stream.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/Stream.php |
|||
|
|||
- |
|||
message: '#^Cannot access offset ''stream'' on mixed\.$#' |
|||
identifier: offsetAccess.nonOffsetAccessible |
|||
count: 1 |
|||
path: src/StreamWrapper.php |
|||
|
|||
- |
|||
message: '#^Cannot access offset string on mixed\.$#' |
|||
identifier: offsetAccess.nonOffsetAccessible |
|||
count: 1 |
|||
path: src/StreamWrapper.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\StreamWrapper\:\:getResource\(\) should return resource but returns resource\|false\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/StreamWrapper.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\StreamWrapper\:\:stream_stat\(\) should return array\{dev\: int, ino\: int, mode\: int, nlink\: int, uid\: int, gid\: int, rdev\: int, size\: int, \.\.\.\}\|false but returns array\{dev\: 0, ino\: 0, mode\: mixed, nlink\: 0, uid\: 0, gid\: 0, rdev\: 0, size\: int, \.\.\.\}\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/StreamWrapper.php |
|||
|
|||
- |
|||
message: '#^Property GuzzleHttp\\Psr7\\StreamWrapper\:\:\$stream \(Psr\\Http\\Message\\StreamInterface\) does not accept mixed\.$#' |
|||
identifier: assign.propertyType |
|||
count: 1 |
|||
path: src/StreamWrapper.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\UploadedFile\:\:isStringNotEmpty\(\) has parameter \$param with no type specified\.$#' |
|||
identifier: missingType.parameter |
|||
count: 1 |
|||
path: src/UploadedFile.php |
|||
|
|||
- |
|||
message: '#^Cannot cast mixed to int\.$#' |
|||
identifier: cast.int |
|||
count: 1 |
|||
path: src/Uri.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\Uri\:\:filterPath\(\) should return string but returns string\|null\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/Uri.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\Uri\:\:filterQueryAndFragment\(\) should return string but returns string\|null\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/Uri.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\Uri\:\:filterUserInfoComponent\(\) should return string but returns string\|null\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/Uri.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(int\<0, 65535\>\|string\)\: mixed\)\|null, ''urldecode'' given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Uri.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$str of function rawurlencode expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Uri.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$path of method Psr\\Http\\Message\\UriInterface\:\:withPath\(\) expects string, string\|null given\.$#' |
|||
identifier: argument.type |
|||
count: 3 |
|||
path: src/UriNormalizer.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$query of method Psr\\Http\\Message\\UriInterface\:\:withQuery\(\) expects string, string\|null given\.$#' |
|||
identifier: argument.type |
|||
count: 2 |
|||
path: src/UriNormalizer.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$str of function rawurldecode expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/UriNormalizer.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$str of function strtoupper expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/UriNormalizer.php |
|||
|
|||
- |
|||
message: '#^Strict comparison using \=\=\= between '''' and non\-falsy\-string will always evaluate to false\.$#' |
|||
identifier: identical.alwaysFalse |
|||
count: 1 |
|||
path: src/UriResolver.php |
|||
|
|||
- |
|||
message: '#^Binary operation "\+" between mixed and array results in an error\.$#' |
|||
identifier: binaryOp.invalid |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Binary operation "\." between ''\:'' and mixed results in an error\.$#' |
|||
identifier: binaryOp.invalid |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Binary operation "\.\=" between mixed and non\-falsy\-string results in an error\.$#' |
|||
identifier: assignOp.invalid |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Cannot access offset ''Host'' on mixed\.$#' |
|||
identifier: offsetAccess.nonOffsetAccessible |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Cannot call method getHost\(\) on mixed\.$#' |
|||
identifier: method.nonObject |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Cannot call method getPort\(\) on mixed\.$#' |
|||
identifier: method.nonObject |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Cannot call method getScheme\(\) on mixed\.$#' |
|||
identifier: method.nonObject |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Cannot call method withQuery\(\) on mixed\.$#' |
|||
identifier: method.nonObject |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Method GuzzleHttp\\Psr7\\Utils\:\:tryGetContents\(\) should return string but returns string\|false\.$#' |
|||
identifier: return.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^PHPDoc tag @var with type object is not subtype of native type object\.$#' |
|||
identifier: varTag.nativeType |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$input of function array_keys expects array, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$keys of static method GuzzleHttp\\Psr7\\Utils\:\:caselessRemove\(\) expects array\<int\|string\>, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$method of class GuzzleHttp\\Psr7\\Request constructor expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$method of class GuzzleHttp\\Psr7\\ServerRequest constructor expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#1 \$source of class GuzzleHttp\\Psr7\\PumpStream constructor expects callable\(int\)\: \(string\|false\|null\), Closure\(\)\: mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$uri of class GuzzleHttp\\Psr7\\Request constructor expects Psr\\Http\\Message\\UriInterface\|string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#2 \$uri of class GuzzleHttp\\Psr7\\ServerRequest constructor expects Psr\\Http\\Message\\UriInterface\|string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#3 \$headers of class GuzzleHttp\\Psr7\\Request constructor expects array\<array\<string\>\|string\>, array given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#3 \$headers of class GuzzleHttp\\Psr7\\ServerRequest constructor expects array\<array\<string\>\|string\>, array given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#4 \$body of class GuzzleHttp\\Psr7\\Request constructor expects Psr\\Http\\Message\\StreamInterface\|resource\|string\|null, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#4 \$body of class GuzzleHttp\\Psr7\\ServerRequest constructor expects Psr\\Http\\Message\\StreamInterface\|resource\|string\|null, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#5 \$version of class GuzzleHttp\\Psr7\\Request constructor expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Parameter \#5 \$version of class GuzzleHttp\\Psr7\\ServerRequest constructor expects string, mixed given\.$#' |
|||
identifier: argument.type |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Unreachable statement \- code above always terminates\.$#' |
|||
identifier: deadCode.unreachable |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Variable \$contents might not be defined\.$#' |
|||
identifier: variable.undefined |
|||
count: 1 |
|||
path: src/Utils.php |
|||
|
|||
- |
|||
message: '#^Variable \$handle might not be defined\.$#' |
|||
identifier: variable.undefined |
|||
count: 1 |
|||
path: src/Utils.php |
@ -1,11 +0,0 @@ |
|||
includes: |
|||
- phpstan-baseline.neon |
|||
- vendor-bin/phpstan/vendor/phpstan/phpstan-deprecation-rules/rules.neon |
|||
|
|||
parameters: |
|||
ignoreErrors: |
|||
- |
|||
identifier: missingType.iterableValue |
|||
level: max |
|||
paths: |
|||
- src |
@ -1,35 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<phpunit |
|||
backupGlobals="true" |
|||
colors="true" |
|||
beStrictAboutOutputDuringTests="true" |
|||
beStrictAboutTestsThatDoNotTestAnything="true" |
|||
bootstrap="vendor/autoload.php" |
|||
convertDeprecationsToExceptions="true" |
|||
> |
|||
<testsuites> |
|||
<testsuite name="Guzzle PSR-7 Unit Test Suite"> |
|||
<directory>tests</directory> |
|||
<exclude>tests/Integration</exclude> |
|||
</testsuite> |
|||
<testsuite name="Guzzle PSR-7 Integration Test Suite"> |
|||
<directory>tests/Integration</directory> |
|||
</testsuite> |
|||
<testsuite name="PSR-17 Integration Test Suite"> |
|||
<directory>./vendor/http-interop/http-factory-tests/test</directory> |
|||
</testsuite> |
|||
</testsuites> |
|||
<filter> |
|||
<whitelist> |
|||
<directory>src</directory> |
|||
</whitelist> |
|||
</filter> |
|||
<php> |
|||
<const name="REQUEST_FACTORY" value="GuzzleHttp\Psr7\HttpFactory"/> |
|||
<const name="RESPONSE_FACTORY" value="GuzzleHttp\Psr7\HttpFactory"/> |
|||
<const name="SERVER_REQUEST_FACTORY" value="GuzzleHttp\Psr7\HttpFactory"/> |
|||
<const name="STREAM_FACTORY" value="GuzzleHttp\Psr7\HttpFactory"/> |
|||
<const name="UPLOADED_FILE_FACTORY" value="GuzzleHttp\Psr7\HttpFactory"/> |
|||
<const name="URI_FACTORY" value="GuzzleHttp\Psr7\HttpFactory"/> |
|||
</php> |
|||
</phpunit> |
@ -1,137 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<files psalm-version="5.26.1@d747f6500b38ac4f7dfc5edbcae6e4b637d7add0"> |
|||
<file src="src/FnStream.php"> |
|||
<InvalidFunctionCall> |
|||
<code><![CDATA[($this->_fn___toString)()]]></code> |
|||
<code><![CDATA[($this->_fn_close)()]]></code> |
|||
<code><![CDATA[($this->_fn_detach)()]]></code> |
|||
<code><![CDATA[($this->_fn_eof)()]]></code> |
|||
<code><![CDATA[($this->_fn_getContents)()]]></code> |
|||
<code><![CDATA[($this->_fn_getMetadata)($key)]]></code> |
|||
<code><![CDATA[($this->_fn_getSize)()]]></code> |
|||
<code><![CDATA[($this->_fn_isReadable)()]]></code> |
|||
<code><![CDATA[($this->_fn_isSeekable)()]]></code> |
|||
<code><![CDATA[($this->_fn_isWritable)()]]></code> |
|||
<code><![CDATA[($this->_fn_read)($length)]]></code> |
|||
<code><![CDATA[($this->_fn_rewind)()]]></code> |
|||
<code><![CDATA[($this->_fn_seek)($offset, $whence)]]></code> |
|||
<code><![CDATA[($this->_fn_tell)()]]></code> |
|||
<code><![CDATA[($this->_fn_write)($string)]]></code> |
|||
</InvalidFunctionCall> |
|||
</file> |
|||
<file src="src/Header.php"> |
|||
<PossiblyUndefinedArrayOffset> |
|||
<code><![CDATA[$m[0]]]></code> |
|||
</PossiblyUndefinedArrayOffset> |
|||
</file> |
|||
<file src="src/HttpFactory.php"> |
|||
<ParamNameMismatch> |
|||
<code><![CDATA[$file]]></code> |
|||
</ParamNameMismatch> |
|||
</file> |
|||
<file src="src/Message.php"> |
|||
<PossiblyNullArgument> |
|||
<code><![CDATA[$matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1]]]></code> |
|||
<code><![CDATA[$parts[1]]]></code> |
|||
<code><![CDATA[$parts[1]]]></code> |
|||
</PossiblyNullArgument> |
|||
<PossiblyUndefinedArrayOffset> |
|||
<code><![CDATA[$parts[1]]]></code> |
|||
<code><![CDATA[$parts[1]]]></code> |
|||
<code><![CDATA[$parts[1]]]></code> |
|||
<code><![CDATA[$parts[1]]]></code> |
|||
</PossiblyUndefinedArrayOffset> |
|||
</file> |
|||
<file src="src/MessageTrait.php"> |
|||
<LessSpecificImplementedReturnType> |
|||
<code><![CDATA[MessageInterface]]></code> |
|||
<code><![CDATA[MessageInterface]]></code> |
|||
<code><![CDATA[MessageInterface]]></code> |
|||
<code><![CDATA[MessageInterface]]></code> |
|||
<code><![CDATA[MessageInterface]]></code> |
|||
<code><![CDATA[array]]></code> |
|||
<code><![CDATA[array]]></code> |
|||
</LessSpecificImplementedReturnType> |
|||
<ParamNameMismatch> |
|||
<code><![CDATA[$header]]></code> |
|||
<code><![CDATA[$header]]></code> |
|||
<code><![CDATA[$header]]></code> |
|||
<code><![CDATA[$header]]></code> |
|||
<code><![CDATA[$header]]></code> |
|||
<code><![CDATA[$header]]></code> |
|||
</ParamNameMismatch> |
|||
</file> |
|||
<file src="src/Request.php"> |
|||
<NoValue> |
|||
<code><![CDATA[$header]]></code> |
|||
</NoValue> |
|||
</file> |
|||
<file src="src/Response.php"> |
|||
<RedundantCast> |
|||
<code><![CDATA[(int) $code]]></code> |
|||
<code><![CDATA[(string) $reasonPhrase]]></code> |
|||
</RedundantCast> |
|||
</file> |
|||
<file src="src/ServerRequest.php"> |
|||
<InvalidArgument> |
|||
<code><![CDATA[$_SERVER['SERVER_PORT']]]></code> |
|||
</InvalidArgument> |
|||
<InvalidArrayOffset> |
|||
<code><![CDATA[$normalizedFiles[$key]]]></code> |
|||
</InvalidArrayOffset> |
|||
<InvalidReturnStatement> |
|||
<code><![CDATA[$normalizedFiles]]></code> |
|||
</InvalidReturnStatement> |
|||
<InvalidReturnType> |
|||
<code><![CDATA[UploadedFileInterface[]]]></code> |
|||
</InvalidReturnType> |
|||
<ParamNameMismatch> |
|||
<code><![CDATA[$attribute]]></code> |
|||
<code><![CDATA[$attribute]]></code> |
|||
<code><![CDATA[$attribute]]></code> |
|||
</ParamNameMismatch> |
|||
</file> |
|||
<file src="src/Stream.php"> |
|||
<InvalidPropertyAssignmentValue> |
|||
<code><![CDATA[$this->stream]]></code> |
|||
</InvalidPropertyAssignmentValue> |
|||
<RedundantCast> |
|||
<code><![CDATA[(int) $whence]]></code> |
|||
</RedundantCast> |
|||
<RedundantPropertyInitializationCheck> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
<code><![CDATA[isset($this->stream)]]></code> |
|||
</RedundantPropertyInitializationCheck> |
|||
</file> |
|||
<file src="src/Uri.php"> |
|||
<PossiblyInvalidArgument> |
|||
<code><![CDATA[$result]]></code> |
|||
</PossiblyInvalidArgument> |
|||
</file> |
|||
<file src="src/UriResolver.php"> |
|||
<TypeDoesNotContainType> |
|||
<code><![CDATA['' === $relativePath]]></code> |
|||
</TypeDoesNotContainType> |
|||
</file> |
|||
<file src="src/Utils.php"> |
|||
<FalsableReturnStatement> |
|||
<code><![CDATA[$contents]]></code> |
|||
</FalsableReturnStatement> |
|||
<MissingDocblockType> |
|||
<code><![CDATA[throw $ex;]]></code> |
|||
<code><![CDATA[throw $ex;]]></code> |
|||
</MissingDocblockType> |
|||
<PossiblyUndefinedVariable> |
|||
<code><![CDATA[$contents]]></code> |
|||
<code><![CDATA[$handle]]></code> |
|||
</PossiblyUndefinedVariable> |
|||
</file> |
|||
</files> |
@ -1,18 +0,0 @@ |
|||
<?xml version="1.0"?> |
|||
<psalm |
|||
errorLevel="3" |
|||
resolveFromConfigFile="true" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xmlns="https://getpsalm.org/schema/config" |
|||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" |
|||
errorBaseline="psalm-baseline.xml" |
|||
findUnusedBaselineEntry="true" |
|||
findUnusedCode="false" |
|||
> |
|||
<projectFiles> |
|||
<directory name="src" /> |
|||
<ignoreFiles> |
|||
<directory name="vendor" /> |
|||
</ignoreFiles> |
|||
</projectFiles> |
|||
</psalm> |
@ -1,221 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\AppendStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\StreamInterface; |
|||
|
|||
class AppendStreamTest extends TestCase |
|||
{ |
|||
public function testValidatesStreamsAreReadable(): void |
|||
{ |
|||
$a = new AppendStream(); |
|||
$s = $this->createMock(StreamInterface::class); |
|||
$s->expects(self::once()) |
|||
->method('isReadable') |
|||
->willReturn(false); |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Each stream must be readable'); |
|||
$a->addStream($s); |
|||
} |
|||
|
|||
public function testValidatesSeekType(): void |
|||
{ |
|||
$a = new AppendStream(); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('The AppendStream can only seek with SEEK_SET'); |
|||
$a->seek(100, SEEK_CUR); |
|||
} |
|||
|
|||
public function testTriesToRewindOnSeek(): void |
|||
{ |
|||
$a = new AppendStream(); |
|||
$s = $this->createMock(StreamInterface::class); |
|||
$s->expects(self::once()) |
|||
->method('isReadable') |
|||
->willReturn(true); |
|||
$s->expects(self::once()) |
|||
->method('isSeekable') |
|||
->willReturn(true); |
|||
$s->expects(self::once()) |
|||
->method('rewind') |
|||
->willThrowException(new \RuntimeException()); |
|||
$a->addStream($s); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Unable to seek stream 0 of the AppendStream'); |
|||
$a->seek(10); |
|||
} |
|||
|
|||
public function testSeeksToPositionByReading(): void |
|||
{ |
|||
$a = new AppendStream([ |
|||
Psr7\Utils::streamFor('foo'), |
|||
Psr7\Utils::streamFor('bar'), |
|||
Psr7\Utils::streamFor('baz'), |
|||
]); |
|||
|
|||
$a->seek(3); |
|||
self::assertSame(3, $a->tell()); |
|||
self::assertSame('bar', $a->read(3)); |
|||
|
|||
$a->seek(6); |
|||
self::assertSame(6, $a->tell()); |
|||
self::assertSame('baz', $a->read(3)); |
|||
} |
|||
|
|||
public function testDetachWithoutStreams(): void |
|||
{ |
|||
$s = new AppendStream(); |
|||
$s->detach(); |
|||
|
|||
self::assertSame(0, $s->getSize()); |
|||
self::assertTrue($s->eof()); |
|||
self::assertTrue($s->isReadable()); |
|||
self::assertSame('', (string) $s); |
|||
self::assertTrue($s->isSeekable()); |
|||
self::assertFalse($s->isWritable()); |
|||
} |
|||
|
|||
public function testDetachesEachStream(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'r'); |
|||
|
|||
$s1 = Psr7\Utils::streamFor($handle); |
|||
$s2 = Psr7\Utils::streamFor('bar'); |
|||
$a = new AppendStream([$s1, $s2]); |
|||
|
|||
$a->detach(); |
|||
|
|||
self::assertSame(0, $a->getSize()); |
|||
self::assertTrue($a->eof()); |
|||
self::assertTrue($a->isReadable()); |
|||
self::assertSame('', (string) $a); |
|||
self::assertTrue($a->isSeekable()); |
|||
self::assertFalse($a->isWritable()); |
|||
|
|||
self::assertNull($s1->detach()); |
|||
self::assertIsResource($handle, 'resource is not closed when detaching'); |
|||
fclose($handle); |
|||
} |
|||
|
|||
public function testClosesEachStream(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'r'); |
|||
|
|||
$s1 = Psr7\Utils::streamFor($handle); |
|||
$s2 = Psr7\Utils::streamFor('bar'); |
|||
$a = new AppendStream([$s1, $s2]); |
|||
|
|||
$a->close(); |
|||
|
|||
self::assertSame(0, $a->getSize()); |
|||
self::assertTrue($a->eof()); |
|||
self::assertTrue($a->isReadable()); |
|||
self::assertSame('', (string) $a); |
|||
self::assertTrue($a->isSeekable()); |
|||
self::assertFalse($a->isWritable()); |
|||
|
|||
self::assertFalse(is_resource($handle)); |
|||
} |
|||
|
|||
public function testIsNotWritable(): void |
|||
{ |
|||
$a = new AppendStream([Psr7\Utils::streamFor('foo')]); |
|||
self::assertFalse($a->isWritable()); |
|||
self::assertTrue($a->isSeekable()); |
|||
self::assertTrue($a->isReadable()); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Cannot write to an AppendStream'); |
|||
$a->write('foo'); |
|||
} |
|||
|
|||
public function testDoesNotNeedStreams(): void |
|||
{ |
|||
$a = new AppendStream(); |
|||
self::assertSame('', (string) $a); |
|||
} |
|||
|
|||
public function testCanReadFromMultipleStreams(): void |
|||
{ |
|||
$a = new AppendStream([ |
|||
Psr7\Utils::streamFor('foo'), |
|||
Psr7\Utils::streamFor('bar'), |
|||
Psr7\Utils::streamFor('baz'), |
|||
]); |
|||
self::assertFalse($a->eof()); |
|||
self::assertSame(0, $a->tell()); |
|||
self::assertSame('foo', $a->read(3)); |
|||
self::assertSame('bar', $a->read(3)); |
|||
self::assertSame('baz', $a->read(3)); |
|||
self::assertSame('', $a->read(1)); |
|||
self::assertTrue($a->eof()); |
|||
self::assertSame(9, $a->tell()); |
|||
self::assertSame('foobarbaz', (string) $a); |
|||
} |
|||
|
|||
public function testCanDetermineSizeFromMultipleStreams(): void |
|||
{ |
|||
$a = new AppendStream([ |
|||
Psr7\Utils::streamFor('foo'), |
|||
Psr7\Utils::streamFor('bar'), |
|||
]); |
|||
self::assertSame(6, $a->getSize()); |
|||
|
|||
$s = $this->createMock(StreamInterface::class); |
|||
$s->expects(self::once()) |
|||
->method('isSeekable') |
|||
->willReturn(false); |
|||
$s->expects(self::once()) |
|||
->method('isReadable') |
|||
->willReturn(true); |
|||
$a->addStream($s); |
|||
self::assertNull($a->getSize()); |
|||
} |
|||
|
|||
/** |
|||
* @requires PHP < 7.4 |
|||
*/ |
|||
public function testCatchesExceptionsWhenCastingToString(): void |
|||
{ |
|||
$s = $this->createMock(StreamInterface::class); |
|||
$s->expects(self::once()) |
|||
->method('isSeekable') |
|||
->willReturn(true); |
|||
$s->expects(self::once()) |
|||
->method('read') |
|||
->willThrowException(new \RuntimeException('foo')); |
|||
$s->expects(self::once()) |
|||
->method('isReadable') |
|||
->willReturn(true); |
|||
$s->expects(self::any()) |
|||
->method('eof') |
|||
->willReturn(false); |
|||
$a = new AppendStream([$s]); |
|||
self::assertFalse($a->eof()); |
|||
|
|||
$errors = []; |
|||
set_error_handler(static function (int $errorNumber, string $errorMessage) use (&$errors): bool { |
|||
$errors[] = ['number' => $errorNumber, 'message' => $errorMessage]; |
|||
|
|||
return true; |
|||
}); |
|||
(string) $a; |
|||
|
|||
restore_error_handler(); |
|||
|
|||
self::assertCount(1, $errors); |
|||
self::assertSame(E_USER_ERROR, $errors[0]['number']); |
|||
self::assertStringStartsWith('GuzzleHttp\Psr7\AppendStream::__toString exception:', $errors[0]['message']); |
|||
} |
|||
|
|||
public function testReturnsEmptyMetadata(): void |
|||
{ |
|||
$s = new AppendStream(); |
|||
self::assertSame([], $s->getMetadata()); |
|||
self::assertNull($s->getMetadata('foo')); |
|||
} |
|||
} |
@ -1,65 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\BufferStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class BufferStreamTest extends TestCase |
|||
{ |
|||
public function testHasMetadata(): void |
|||
{ |
|||
$b = new BufferStream(10); |
|||
self::assertTrue($b->isReadable()); |
|||
self::assertTrue($b->isWritable()); |
|||
self::assertFalse($b->isSeekable()); |
|||
self::assertNull($b->getMetadata('foo')); |
|||
self::assertSame(10, $b->getMetadata('hwm')); |
|||
self::assertSame([], $b->getMetadata()); |
|||
} |
|||
|
|||
public function testRemovesReadDataFromBuffer(): void |
|||
{ |
|||
$b = new BufferStream(); |
|||
self::assertSame(3, $b->write('foo')); |
|||
self::assertSame(3, $b->getSize()); |
|||
self::assertFalse($b->eof()); |
|||
self::assertSame('foo', $b->read(10)); |
|||
self::assertTrue($b->eof()); |
|||
self::assertSame('', $b->read(10)); |
|||
} |
|||
|
|||
public function testCanCastToStringOrGetContents(): void |
|||
{ |
|||
$b = new BufferStream(); |
|||
$b->write('foo'); |
|||
$b->write('baz'); |
|||
self::assertSame('foo', $b->read(3)); |
|||
$b->write('bar'); |
|||
self::assertSame('bazbar', (string) $b); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Cannot determine the position of a BufferStream'); |
|||
$b->tell(); |
|||
} |
|||
|
|||
public function testDetachClearsBuffer(): void |
|||
{ |
|||
$b = new BufferStream(); |
|||
$b->write('foo'); |
|||
$b->detach(); |
|||
self::assertTrue($b->eof()); |
|||
self::assertSame(3, $b->write('abc')); |
|||
self::assertSame('abc', $b->read(10)); |
|||
} |
|||
|
|||
public function testExceedingHighwaterMarkReturnsFalseButStillBuffers(): void |
|||
{ |
|||
$b = new BufferStream(5); |
|||
self::assertSame(3, $b->write('hi ')); |
|||
self::assertSame(0, $b->write('hello')); |
|||
self::assertSame('hi hello', (string) $b); |
|||
self::assertSame(4, $b->write('test')); |
|||
} |
|||
} |
@ -1,200 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\CachingStream; |
|||
use GuzzleHttp\Psr7\Stream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\CachingStream |
|||
*/ |
|||
class CachingStreamTest extends TestCase |
|||
{ |
|||
/** @var CachingStream */ |
|||
private $body; |
|||
/** @var Stream */ |
|||
private $decorated; |
|||
|
|||
protected function setUp(): void |
|||
{ |
|||
$this->decorated = Psr7\Utils::streamFor('testing'); |
|||
$this->body = new CachingStream($this->decorated); |
|||
} |
|||
|
|||
protected function tearDown(): void |
|||
{ |
|||
$this->decorated->close(); |
|||
$this->body->close(); |
|||
} |
|||
|
|||
public function testUsesRemoteSizeIfAvailable(): void |
|||
{ |
|||
$body = Psr7\Utils::streamFor('test'); |
|||
$caching = new CachingStream($body); |
|||
self::assertSame(4, $caching->getSize()); |
|||
} |
|||
|
|||
public function testUsesRemoteSizeIfNotAvailable(): void |
|||
{ |
|||
$body = new Psr7\PumpStream(function () { |
|||
return 'a'; |
|||
}); |
|||
$caching = new CachingStream($body); |
|||
self::assertNull($caching->getSize()); |
|||
} |
|||
|
|||
public function testReadsUntilCachedToByte(): void |
|||
{ |
|||
$this->body->seek(5); |
|||
self::assertSame('n', $this->body->read(1)); |
|||
$this->body->seek(0); |
|||
self::assertSame('t', $this->body->read(1)); |
|||
} |
|||
|
|||
public function testCanSeekNearEndWithSeekEnd(): void |
|||
{ |
|||
$baseStream = Psr7\Utils::streamFor(implode('', range('a', 'z'))); |
|||
$cached = new CachingStream($baseStream); |
|||
$cached->seek(-1, SEEK_END); |
|||
self::assertSame(25, $baseStream->tell()); |
|||
self::assertSame('z', $cached->read(1)); |
|||
self::assertSame(26, $cached->getSize()); |
|||
} |
|||
|
|||
public function testCanSeekToEndWithSeekEnd(): void |
|||
{ |
|||
$baseStream = Psr7\Utils::streamFor(implode('', range('a', 'z'))); |
|||
$cached = new CachingStream($baseStream); |
|||
$cached->seek(0, SEEK_END); |
|||
self::assertSame(26, $baseStream->tell()); |
|||
self::assertSame('', $cached->read(1)); |
|||
self::assertSame(26, $cached->getSize()); |
|||
} |
|||
|
|||
public function testCanUseSeekEndWithUnknownSize(): void |
|||
{ |
|||
$baseStream = Psr7\Utils::streamFor('testing'); |
|||
$decorated = Psr7\FnStream::decorate($baseStream, [ |
|||
'getSize' => function () { |
|||
return null; |
|||
}, |
|||
]); |
|||
$cached = new CachingStream($decorated); |
|||
$cached->seek(-1, SEEK_END); |
|||
self::assertSame('g', $cached->read(1)); |
|||
} |
|||
|
|||
public function testRewind(): void |
|||
{ |
|||
$a = Psr7\Utils::streamFor('foo'); |
|||
$d = new CachingStream($a); |
|||
self::assertSame('foo', $d->read(3)); |
|||
$d->rewind(); |
|||
self::assertSame('foo', $d->read(3)); |
|||
} |
|||
|
|||
public function testCanSeekToReadBytes(): void |
|||
{ |
|||
self::assertSame('te', $this->body->read(2)); |
|||
$this->body->seek(0); |
|||
self::assertSame('test', $this->body->read(4)); |
|||
self::assertSame(4, $this->body->tell()); |
|||
$this->body->seek(2); |
|||
self::assertSame(2, $this->body->tell()); |
|||
$this->body->seek(2, SEEK_CUR); |
|||
self::assertSame(4, $this->body->tell()); |
|||
self::assertSame('ing', $this->body->read(3)); |
|||
} |
|||
|
|||
public function testCanSeekToReadBytesWithPartialBodyReturned(): void |
|||
{ |
|||
$stream = fopen('php://temp', 'r+'); |
|||
fwrite($stream, 'testing'); |
|||
fseek($stream, 0); |
|||
|
|||
$this->decorated = $this->getMockBuilder(Stream::class) |
|||
->setConstructorArgs([$stream]) |
|||
->onlyMethods(['read']) |
|||
->getMock(); |
|||
|
|||
$this->decorated->expects(self::exactly(2)) |
|||
->method('read') |
|||
->willReturnCallback(function (int $length) use ($stream) { |
|||
return fread($stream, 2); |
|||
}); |
|||
|
|||
$this->body = new CachingStream($this->decorated); |
|||
|
|||
self::assertSame(0, $this->body->tell()); |
|||
$this->body->seek(4, SEEK_SET); |
|||
self::assertSame(4, $this->body->tell()); |
|||
|
|||
$this->body->seek(0); |
|||
self::assertSame('test', $this->body->read(4)); |
|||
} |
|||
|
|||
public function testWritesToBufferStream(): void |
|||
{ |
|||
$this->body->read(2); |
|||
$this->body->write('hi'); |
|||
$this->body->seek(0); |
|||
self::assertSame('tehiing', (string) $this->body); |
|||
} |
|||
|
|||
public function testSkipsOverwrittenBytes(): void |
|||
{ |
|||
$decorated = Psr7\Utils::streamFor( |
|||
implode("\n", array_map(function ($n) { |
|||
return str_pad((string) $n, 4, '0', STR_PAD_LEFT); |
|||
}, range(0, 25))) |
|||
); |
|||
|
|||
$body = new CachingStream($decorated); |
|||
|
|||
self::assertSame("0000\n", Psr7\Utils::readLine($body)); |
|||
self::assertSame("0001\n", Psr7\Utils::readLine($body)); |
|||
// Write over part of the body yet to be read, so skip some bytes |
|||
self::assertSame(5, $body->write("TEST\n")); |
|||
// Read, which skips bytes, then reads |
|||
self::assertSame("0003\n", Psr7\Utils::readLine($body)); |
|||
self::assertSame("0004\n", Psr7\Utils::readLine($body)); |
|||
self::assertSame("0005\n", Psr7\Utils::readLine($body)); |
|||
|
|||
// Overwrite part of the cached body (so don't skip any bytes) |
|||
$body->seek(5); |
|||
self::assertSame(5, $body->write("ABCD\n")); |
|||
self::assertSame("TEST\n", Psr7\Utils::readLine($body)); |
|||
self::assertSame("0003\n", Psr7\Utils::readLine($body)); |
|||
self::assertSame("0004\n", Psr7\Utils::readLine($body)); |
|||
self::assertSame("0005\n", Psr7\Utils::readLine($body)); |
|||
self::assertSame("0006\n", Psr7\Utils::readLine($body)); |
|||
self::assertSame(5, $body->write("1234\n")); |
|||
|
|||
// Seek to 0 and ensure the overwritten bit is replaced |
|||
$body->seek(0); |
|||
self::assertSame("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50)); |
|||
|
|||
// Ensure that casting it to a string does not include the bit that was overwritten |
|||
self::assertStringContainsString("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body); |
|||
} |
|||
|
|||
public function testClosesBothStreams(): void |
|||
{ |
|||
$s = fopen('php://temp', 'r'); |
|||
$a = Psr7\Utils::streamFor($s); |
|||
$d = new CachingStream($a); |
|||
$d->close(); |
|||
self::assertFalse(is_resource($s)); |
|||
} |
|||
|
|||
public function testEnsuresValidWhence(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Invalid whence'); |
|||
$this->body->seek(10, -123456); |
|||
} |
|||
} |
@ -1,30 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\BufferStream; |
|||
use GuzzleHttp\Psr7\DroppingStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class DroppingStreamTest extends TestCase |
|||
{ |
|||
public function testBeginsDroppingWhenSizeExceeded(): void |
|||
{ |
|||
$stream = new BufferStream(); |
|||
$drop = new DroppingStream($stream, 5); |
|||
self::assertSame(3, $drop->write('hel')); |
|||
self::assertSame(2, $drop->write('lo')); |
|||
self::assertSame(5, $drop->getSize()); |
|||
self::assertSame('hello', $drop->read(5)); |
|||
self::assertSame(0, $drop->getSize()); |
|||
$drop->write('12345678910'); |
|||
self::assertSame(5, $stream->getSize()); |
|||
self::assertSame(5, $drop->getSize()); |
|||
self::assertSame('12345', (string) $drop); |
|||
self::assertSame(0, $drop->getSize()); |
|||
$drop->write('hello'); |
|||
self::assertSame(0, $drop->write('test')); |
|||
} |
|||
} |
@ -1,128 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\FnStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\FnStream |
|||
*/ |
|||
class FnStreamTest extends TestCase |
|||
{ |
|||
public function testThrowsWhenNotImplemented(): void |
|||
{ |
|||
$this->expectException(\BadMethodCallException::class); |
|||
$this->expectExceptionMessage('seek() is not implemented in the FnStream'); |
|||
(new FnStream([]))->seek(1); |
|||
} |
|||
|
|||
public function testProxiesToFunction(): void |
|||
{ |
|||
$s = new FnStream([ |
|||
'read' => function ($len) { |
|||
$this->assertSame(3, $len); |
|||
|
|||
return 'foo'; |
|||
}, |
|||
]); |
|||
|
|||
self::assertSame('foo', $s->read(3)); |
|||
} |
|||
|
|||
public function testCanCloseOnDestruct(): void |
|||
{ |
|||
$called = false; |
|||
$s = new FnStream([ |
|||
'close' => function () use (&$called): void { |
|||
$called = true; |
|||
}, |
|||
]); |
|||
unset($s); |
|||
self::assertTrue($called); |
|||
} |
|||
|
|||
public function testDoesNotRequireClose(): void |
|||
{ |
|||
$s = new FnStream([]); |
|||
unset($s); |
|||
self::assertTrue(true); // strict mode requires an assertion |
|||
} |
|||
|
|||
public function testDecoratesStream(): void |
|||
{ |
|||
$a = Psr7\Utils::streamFor('foo'); |
|||
$b = FnStream::decorate($a, []); |
|||
self::assertSame(3, $b->getSize()); |
|||
self::assertSame($b->isWritable(), true); |
|||
self::assertSame($b->isReadable(), true); |
|||
self::assertSame($b->isSeekable(), true); |
|||
self::assertSame($b->read(3), 'foo'); |
|||
self::assertSame($b->tell(), 3); |
|||
self::assertSame($a->tell(), 3); |
|||
self::assertSame('', $a->read(1)); |
|||
self::assertSame($b->eof(), true); |
|||
self::assertSame($a->eof(), true); |
|||
$b->seek(0); |
|||
self::assertSame('foo', (string) $b); |
|||
$b->seek(0); |
|||
self::assertSame('foo', $b->getContents()); |
|||
self::assertSame($a->getMetadata(), $b->getMetadata()); |
|||
$b->seek(0, SEEK_END); |
|||
$b->write('bar'); |
|||
self::assertSame('foobar', (string) $b); |
|||
self::assertIsResource($b->detach()); |
|||
$b->close(); |
|||
} |
|||
|
|||
public function testDecoratesWithCustomizations(): void |
|||
{ |
|||
$called = false; |
|||
$a = Psr7\Utils::streamFor('foo'); |
|||
$b = FnStream::decorate($a, [ |
|||
'read' => function ($len) use (&$called, $a) { |
|||
$called = true; |
|||
|
|||
return $a->read($len); |
|||
}, |
|||
]); |
|||
self::assertSame('foo', $b->read(3)); |
|||
self::assertTrue($called); |
|||
} |
|||
|
|||
public function testDoNotAllowUnserialization(): void |
|||
{ |
|||
$a = new FnStream([]); |
|||
$b = serialize($a); |
|||
$this->expectException(\LogicException::class); |
|||
$this->expectExceptionMessage('FnStream should never be unserialized'); |
|||
unserialize($b); |
|||
} |
|||
|
|||
/** |
|||
* @requires PHP < 7.4 |
|||
*/ |
|||
public function testThatConvertingStreamToStringWillTriggerErrorAndWillReturnEmptyString(): void |
|||
{ |
|||
$a = new FnStream([ |
|||
'__toString' => function (): void { |
|||
throw new \Exception(); |
|||
}, |
|||
]); |
|||
|
|||
$errors = []; |
|||
set_error_handler(function (int $errorNumber, string $errorMessage) use (&$errors): void { |
|||
$errors[] = ['number' => $errorNumber, 'message' => $errorMessage]; |
|||
}); |
|||
(string) $a; |
|||
|
|||
restore_error_handler(); |
|||
|
|||
self::assertCount(1, $errors); |
|||
self::assertSame(E_USER_ERROR, $errors[0]['number']); |
|||
self::assertStringStartsWith('GuzzleHttp\Psr7\FnStream::__toString exception:', $errors[0]['message']); |
|||
} |
|||
} |
@ -1,13 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
class HasToString |
|||
{ |
|||
public function __toString(): string |
|||
{ |
|||
return 'foo'; |
|||
} |
|||
} |
@ -1,182 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class HeaderTest extends TestCase |
|||
{ |
|||
public function parseParamsProvider(): array |
|||
{ |
|||
$res1 = [ |
|||
[ |
|||
'<http:/.../front.jpeg>', |
|||
'rel' => 'front', |
|||
'type' => 'image/jpeg', |
|||
], |
|||
[ |
|||
'<http://.../back.jpeg>', |
|||
'rel' => 'back', |
|||
'type' => 'image/jpeg', |
|||
], |
|||
]; |
|||
|
|||
return [ |
|||
[ |
|||
'<http:/.../front.jpeg>; rel="front"; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"', |
|||
$res1, |
|||
], |
|||
[ |
|||
'<http:/.../front.jpeg>; rel="front"; type="image/jpeg",<http://.../back.jpeg>; rel=back; type="image/jpeg"', |
|||
$res1, |
|||
], |
|||
[ |
|||
'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', |
|||
[ |
|||
['foo' => 'baz', 'bar' => '123'], |
|||
['boo'], |
|||
['test' => '123'], |
|||
['foobar' => 'foo;bar'], |
|||
], |
|||
], |
|||
[ |
|||
'<http://.../side.jpeg?test=1>; rel="side"; type="image/jpeg",<http://.../side.jpeg?test=2>; rel=side; type="image/jpeg"', |
|||
[ |
|||
['<http://.../side.jpeg?test=1>', 'rel' => 'side', 'type' => 'image/jpeg'], |
|||
['<http://.../side.jpeg?test=2>', 'rel' => 'side', 'type' => 'image/jpeg'], |
|||
], |
|||
], |
|||
[ |
|||
'', |
|||
[], |
|||
], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider parseParamsProvider |
|||
*/ |
|||
public function testParseParams($header, $result): void |
|||
{ |
|||
self::assertSame($result, Psr7\Header::parse($header)); |
|||
} |
|||
|
|||
public function normalizeProvider(): array |
|||
{ |
|||
return [ |
|||
[ |
|||
'', |
|||
[], |
|||
], |
|||
[ |
|||
['a, b', 'c', 'd, e'], |
|||
['a', 'b', 'c', 'd', 'e'], |
|||
], |
|||
// Example 'accept-encoding' |
|||
[ |
|||
'gzip, br', |
|||
['gzip', 'br'], |
|||
], |
|||
// https://httpwg.org/specs/rfc7231.html#rfc.section.5.3.2 |
|||
[ |
|||
'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c', |
|||
['text/plain; q=0.5', 'text/html', 'text/x-dvi; q=0.8', 'text/x-c'], |
|||
], |
|||
// Example 'If-None-Match' with comma within an ETag |
|||
[ |
|||
'"foo", "foo,bar", "bar"', |
|||
['"foo"', '"foo,bar"', '"bar"'], |
|||
], |
|||
// https://httpwg.org/specs/rfc7234.html#cache.control.extensions |
|||
[ |
|||
'private, community="UCI"', |
|||
['private', 'community="UCI"'], |
|||
], |
|||
// The Cache-Control example with a comma within a community |
|||
[ |
|||
'private, community="Guzzle,Psr7"', |
|||
['private', 'community="Guzzle,Psr7"'], |
|||
], |
|||
// The Cache-Control example with an escaped space (quoted-pair) within a community |
|||
[ |
|||
'private, community="Guzzle\\ Psr7"', |
|||
['private', 'community="Guzzle\\ Psr7"'], |
|||
], |
|||
// The Cache-Control example with an escaped quote (quoted-pair) within a community |
|||
[ |
|||
'private, community="Guzzle\\"Psr7"', |
|||
['private', 'community="Guzzle\\"Psr7"'], |
|||
], |
|||
// The Cache-Control example with an escaped quote (quoted-pair) and a comma within a community |
|||
[ |
|||
'private, community="Guzzle\\",Psr7"', |
|||
['private', 'community="Guzzle\\",Psr7"'], |
|||
], |
|||
// The Cache-Control example with an escaped backslash (quoted-pair) within a community |
|||
[ |
|||
'private, community="Guzzle\\\\Psr7"', |
|||
['private', 'community="Guzzle\\\\Psr7"'], |
|||
], |
|||
// The Cache-Control example with an escaped backslash (quoted-pair) within a community |
|||
[ |
|||
'private, community="Guzzle\\\\", Psr7', |
|||
['private', 'community="Guzzle\\\\"', 'Psr7'], |
|||
], |
|||
// https://httpwg.org/specs/rfc7230.html#rfc.section.7 |
|||
[ |
|||
'foo ,bar,', |
|||
['foo', 'bar'], |
|||
], |
|||
// https://httpwg.org/specs/rfc7230.html#rfc.section.7 |
|||
[ |
|||
'foo , ,bar,charlie ', |
|||
['foo', 'bar', 'charlie'], |
|||
], |
|||
[ |
|||
"<https://example.gitlab.com>; rel=\"first\",\n<https://example.gitlab.com>; rel=\"next\",\n<https://example.gitlab.com>; rel=\"prev\",\n<https://example.gitlab.com>; rel=\"last\",", |
|||
['<https://example.gitlab.com>; rel="first"', '<https://example.gitlab.com>; rel="next"', '<https://example.gitlab.com>; rel="prev"', '<https://example.gitlab.com>; rel="last"'], |
|||
], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider normalizeProvider |
|||
*/ |
|||
public function testNormalize($header, $result): void |
|||
{ |
|||
self::assertSame($result, Psr7\Header::normalize([$header])); |
|||
self::assertSame($result, Psr7\Header::normalize($header)); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider normalizeProvider |
|||
*/ |
|||
public function testSplitList($header, $result): void |
|||
{ |
|||
self::assertSame($result, Psr7\Header::splitList($header)); |
|||
} |
|||
|
|||
public function testSplitListRejectsNestedArrays(): void |
|||
{ |
|||
$this->expectException(\TypeError::class); |
|||
|
|||
Psr7\Header::splitList([['foo']]); |
|||
} |
|||
|
|||
public function testSplitListArrayContainingNonStrings(): void |
|||
{ |
|||
$this->expectException(\TypeError::class); |
|||
|
|||
Psr7\Header::splitList(['foo', 'bar', 1, false]); |
|||
} |
|||
|
|||
public function testSplitListRejectsNonStrings(): void |
|||
{ |
|||
$this->expectException(\TypeError::class); |
|||
|
|||
Psr7\Header::splitList(false); |
|||
} |
|||
} |
@ -1,97 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\InflateStream; |
|||
use GuzzleHttp\Psr7\NoSeekStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @requires extension zlib |
|||
*/ |
|||
class InflateStreamTest extends TestCase |
|||
{ |
|||
public function testInflatesRfc1952Streams(): void |
|||
{ |
|||
$content = gzencode('test'); |
|||
$a = Psr7\Utils::streamFor($content); |
|||
$b = new InflateStream($a); |
|||
self::assertSame('test', (string) $b); |
|||
} |
|||
|
|||
public function testInflatesStreamsRfc1952WithFilename(): void |
|||
{ |
|||
$content = $this->getGzipStringWithFilename('test'); |
|||
$a = Psr7\Utils::streamFor($content); |
|||
$b = new InflateStream($a); |
|||
self::assertSame('test', (string) $b); |
|||
} |
|||
|
|||
public function testInflatesRfc1950Streams(): void |
|||
{ |
|||
$content = gzcompress('test'); |
|||
$a = Psr7\Utils::streamFor($content); |
|||
$b = new InflateStream($a); |
|||
self::assertSame('test', (string) $b); |
|||
} |
|||
|
|||
public function testInflatesRfc1952StreamsWithExtraFlags(): void |
|||
{ |
|||
$content = gzdeflate('test'); // RFC 1951. Raw deflate. No header. |
|||
|
|||
// +---+---+---+---+---+---+---+---+---+---+ |
|||
// |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->) |
|||
// +---+---+---+---+---+---+---+---+---+---+ |
|||
$header = "\x1f\x8B\x08"; |
|||
// set flags FHCRC, FEXTRA, FNAME and FCOMMENT |
|||
$header .= chr(0b00011110); |
|||
$header .= "\x00\x00\x00\x00"; // MTIME |
|||
$header .= "\x02\x03"; // XFL, OS |
|||
// 4 byte extra data |
|||
$header .= "\x04\x00\x41\x70\x00\x00"; /* XLEN + EXTRA */ |
|||
// file name (2 bytes + terminator) |
|||
$header .= "\x41\x70\x00"; |
|||
// file comment (3 bytes + terminator) |
|||
$header .= "\x41\x42\x43\x00"; |
|||
|
|||
// crc16 |
|||
$header .= pack('v', crc32($header)); |
|||
|
|||
$a = Psr7\Utils::streamFor($header.$content); |
|||
$b = new InflateStream($a); |
|||
self::assertSame('test', (string) $b); |
|||
} |
|||
|
|||
public function testInflatesStreamsPreserveSeekable(): void |
|||
{ |
|||
$content = gzencode('test'); |
|||
$seekable = Psr7\Utils::streamFor($content); |
|||
|
|||
$seekableInflate = new InflateStream($seekable); |
|||
self::assertTrue($seekableInflate->isSeekable()); |
|||
self::assertSame('test', (string) $seekableInflate); |
|||
|
|||
$nonSeekable = new NoSeekStream(Psr7\Utils::streamFor($content)); |
|||
$nonSeekableInflate = new InflateStream($nonSeekable); |
|||
self::assertFalse($nonSeekableInflate->isSeekable()); |
|||
self::assertSame('test', (string) $nonSeekableInflate); |
|||
} |
|||
|
|||
private function getGzipStringWithFilename($original_string) |
|||
{ |
|||
$gzipped = bin2hex(gzencode($original_string)); |
|||
|
|||
$header = substr($gzipped, 0, 20); |
|||
// set FNAME flag |
|||
$header[6] = 0; |
|||
$header[7] = 8; |
|||
// make a dummy filename |
|||
$filename = '64756d6d7900'; |
|||
$rest = substr($gzipped, 20); |
|||
|
|||
return hex2bin($header.$filename.$rest); |
|||
} |
|||
} |
@ -1,44 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7\Integration; |
|||
|
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class ServerRequestFromGlobalsTest extends TestCase |
|||
{ |
|||
protected function setUp(): void |
|||
{ |
|||
if (false === $this->getServerUri()) { |
|||
self::markTestSkipped(); |
|||
} |
|||
parent::setUp(); |
|||
} |
|||
|
|||
public function testBodyExists(): void |
|||
{ |
|||
$curl = curl_init(); |
|||
|
|||
curl_setopt($curl, CURLOPT_URL, $this->getServerUri()); |
|||
curl_setopt($curl, CURLOPT_POST, 1); |
|||
curl_setopt($curl, CURLOPT_POSTFIELDS, 'foobar'); |
|||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); |
|||
$response = curl_exec($curl); |
|||
curl_close($curl); |
|||
|
|||
self::assertNotFalse($response); |
|||
$data = json_decode($response, true); |
|||
self::assertIsArray($data); |
|||
self::assertArrayHasKey('method', $data); |
|||
self::assertArrayHasKey('uri', $data); |
|||
self::assertArrayHasKey('body', $data); |
|||
|
|||
self::assertEquals('foobar', $data['body']); |
|||
} |
|||
|
|||
private function getServerUri() |
|||
{ |
|||
return $_SERVER['TEST_SERVER'] ?? false; |
|||
} |
|||
} |
@ -1,15 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
require dirname(__DIR__, 2).'/vendor/autoload.php'; |
|||
|
|||
$request = GuzzleHttp\Psr7\ServerRequest::fromGlobals(); |
|||
|
|||
$output = [ |
|||
'method' => $request->getMethod(), |
|||
'uri' => $request->getUri()->__toString(), |
|||
'body' => $request->getBody()->__toString(), |
|||
]; |
|||
|
|||
echo json_encode($output); |
@ -1,68 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\LazyOpenStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class LazyOpenStreamTest extends TestCase |
|||
{ |
|||
private $fname; |
|||
|
|||
protected function setUp(): void |
|||
{ |
|||
$this->fname = tempnam(sys_get_temp_dir(), 'tfile'); |
|||
|
|||
if (file_exists($this->fname)) { |
|||
unlink($this->fname); |
|||
} |
|||
} |
|||
|
|||
protected function tearDown(): void |
|||
{ |
|||
if (file_exists($this->fname)) { |
|||
unlink($this->fname); |
|||
} |
|||
} |
|||
|
|||
public function testOpensLazily(): void |
|||
{ |
|||
$l = new LazyOpenStream($this->fname, 'w+'); |
|||
$l->write('foo'); |
|||
self::assertIsArray($l->getMetadata()); |
|||
self::assertFileExists($this->fname); |
|||
self::assertSame('foo', file_get_contents($this->fname)); |
|||
self::assertSame('foo', (string) $l); |
|||
} |
|||
|
|||
public function testProxiesToFile(): void |
|||
{ |
|||
file_put_contents($this->fname, 'foo'); |
|||
$l = new LazyOpenStream($this->fname, 'r'); |
|||
self::assertSame('foo', $l->read(4)); |
|||
self::assertTrue($l->eof()); |
|||
self::assertSame(3, $l->tell()); |
|||
self::assertTrue($l->isReadable()); |
|||
self::assertTrue($l->isSeekable()); |
|||
self::assertFalse($l->isWritable()); |
|||
$l->seek(1); |
|||
self::assertSame('oo', $l->getContents()); |
|||
self::assertSame('foo', (string) $l); |
|||
self::assertSame(3, $l->getSize()); |
|||
self::assertIsArray($l->getMetadata()); |
|||
$l->close(); |
|||
} |
|||
|
|||
public function testDetachesUnderlyingStream(): void |
|||
{ |
|||
file_put_contents($this->fname, 'foo'); |
|||
$l = new LazyOpenStream($this->fname, 'r'); |
|||
$r = $l->detach(); |
|||
self::assertIsResource($r); |
|||
fseek($r, 0); |
|||
self::assertSame('foo', stream_get_contents($r)); |
|||
fclose($r); |
|||
} |
|||
} |
@ -1,165 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\FnStream; |
|||
use GuzzleHttp\Psr7\LimitStream; |
|||
use GuzzleHttp\Psr7\NoSeekStream; |
|||
use GuzzleHttp\Psr7\Stream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\LimitStream |
|||
*/ |
|||
class LimitStreamTest extends TestCase |
|||
{ |
|||
/** @var LimitStream */ |
|||
private $body; |
|||
|
|||
/** @var Stream */ |
|||
private $decorated; |
|||
|
|||
protected function setUp(): void |
|||
{ |
|||
$this->decorated = Psr7\Utils::streamFor(fopen(__FILE__, 'r')); |
|||
$this->body = new LimitStream($this->decorated, 10, 3); |
|||
} |
|||
|
|||
public function testReturnsSubset(): void |
|||
{ |
|||
$body = new LimitStream(Psr7\Utils::streamFor('foo'), -1, 1); |
|||
self::assertSame('oo', (string) $body); |
|||
self::assertTrue($body->eof()); |
|||
$body->seek(0); |
|||
self::assertFalse($body->eof()); |
|||
self::assertSame('oo', $body->read(100)); |
|||
self::assertSame('', $body->read(1)); |
|||
self::assertTrue($body->eof()); |
|||
} |
|||
|
|||
public function testReturnsSubsetWhenCastToString(): void |
|||
{ |
|||
$body = Psr7\Utils::streamFor('foo_baz_bar'); |
|||
$limited = new LimitStream($body, 3, 4); |
|||
self::assertSame('baz', (string) $limited); |
|||
} |
|||
|
|||
public function testReturnsSubsetOfEmptyBodyWhenCastToString(): void |
|||
{ |
|||
$body = Psr7\Utils::streamFor('01234567891234'); |
|||
$limited = new LimitStream($body, 0, 10); |
|||
self::assertSame('', (string) $limited); |
|||
} |
|||
|
|||
public function testReturnsSpecificSubsetOBodyWhenCastToString(): void |
|||
{ |
|||
$body = Psr7\Utils::streamFor('0123456789abcdef'); |
|||
$limited = new LimitStream($body, 3, 10); |
|||
self::assertSame('abc', (string) $limited); |
|||
} |
|||
|
|||
public function testSeeksWhenConstructed(): void |
|||
{ |
|||
self::assertSame(0, $this->body->tell()); |
|||
self::assertSame(3, $this->decorated->tell()); |
|||
} |
|||
|
|||
public function testAllowsBoundedSeek(): void |
|||
{ |
|||
$this->body->seek(100); |
|||
self::assertSame(10, $this->body->tell()); |
|||
self::assertSame(13, $this->decorated->tell()); |
|||
$this->body->seek(0); |
|||
self::assertSame(0, $this->body->tell()); |
|||
self::assertSame(3, $this->decorated->tell()); |
|||
try { |
|||
$this->body->seek(-10); |
|||
self::fail(); |
|||
} catch (\RuntimeException $e) { |
|||
} |
|||
self::assertSame(0, $this->body->tell()); |
|||
self::assertSame(3, $this->decorated->tell()); |
|||
$this->body->seek(5); |
|||
self::assertSame(5, $this->body->tell()); |
|||
self::assertSame(8, $this->decorated->tell()); |
|||
// Fail |
|||
try { |
|||
$this->body->seek(1000, SEEK_END); |
|||
self::fail(); |
|||
} catch (\RuntimeException $e) { |
|||
} |
|||
} |
|||
|
|||
public function testReadsOnlySubsetOfData(): void |
|||
{ |
|||
$data = $this->body->read(100); |
|||
self::assertSame(10, strlen($data)); |
|||
self::assertSame('', $this->body->read(1000)); |
|||
|
|||
$this->body->setOffset(10); |
|||
$newData = $this->body->read(100); |
|||
self::assertSame(10, strlen($newData)); |
|||
self::assertNotSame($data, $newData); |
|||
} |
|||
|
|||
public function testThrowsWhenCurrentGreaterThanOffsetSeek(): void |
|||
{ |
|||
$a = Psr7\Utils::streamFor('foo_bar'); |
|||
$b = new NoSeekStream($a); |
|||
$c = new LimitStream($b); |
|||
$a->getContents(); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Could not seek to stream offset 2'); |
|||
$c->setOffset(2); |
|||
} |
|||
|
|||
public function testCanGetContentsWithoutSeeking(): void |
|||
{ |
|||
$a = Psr7\Utils::streamFor('foo_bar'); |
|||
$b = new NoSeekStream($a); |
|||
$c = new LimitStream($b); |
|||
self::assertSame('foo_bar', $c->getContents()); |
|||
} |
|||
|
|||
public function testClaimsConsumedWhenReadLimitIsReached(): void |
|||
{ |
|||
self::assertFalse($this->body->eof()); |
|||
$this->body->read(1000); |
|||
self::assertTrue($this->body->eof()); |
|||
} |
|||
|
|||
public function testContentLengthIsBounded(): void |
|||
{ |
|||
self::assertSame(10, $this->body->getSize()); |
|||
} |
|||
|
|||
public function testGetContentsIsBasedOnSubset(): void |
|||
{ |
|||
$body = new LimitStream(Psr7\Utils::streamFor('foobazbar'), 3, 3); |
|||
self::assertSame('baz', $body->getContents()); |
|||
} |
|||
|
|||
public function testReturnsNullIfSizeCannotBeDetermined(): void |
|||
{ |
|||
$a = new FnStream([ |
|||
'getSize' => function () { |
|||
return null; |
|||
}, |
|||
'tell' => function () { |
|||
return 0; |
|||
}, |
|||
]); |
|||
$b = new LimitStream($a); |
|||
self::assertNull($b->getSize()); |
|||
} |
|||
|
|||
public function testLengthLessOffsetWhenNoLimitSize(): void |
|||
{ |
|||
$a = Psr7\Utils::streamFor('foo_bar'); |
|||
$b = new LimitStream($a, -1, 4); |
|||
self::assertSame(3, $b->getSize()); |
|||
} |
|||
} |
@ -1,299 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\FnStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class MessageTest extends TestCase |
|||
{ |
|||
public function testConvertsRequestsToStrings(): void |
|||
{ |
|||
$request = new Psr7\Request('PUT', 'http://foo.com/hi?123', [ |
|||
'Baz' => 'bar', |
|||
'Qux' => 'ipsum', |
|||
], 'hello', '1.0'); |
|||
self::assertSame( |
|||
"PUT /hi?123 HTTP/1.0\r\nHost: foo.com\r\nBaz: bar\r\nQux: ipsum\r\n\r\nhello", |
|||
Psr7\Message::toString($request) |
|||
); |
|||
} |
|||
|
|||
public function testConvertsResponsesToStrings(): void |
|||
{ |
|||
$response = new Psr7\Response(200, [ |
|||
'Baz' => 'bar', |
|||
'Qux' => 'ipsum', |
|||
], 'hello', '1.0', 'FOO'); |
|||
self::assertSame( |
|||
"HTTP/1.0 200 FOO\r\nBaz: bar\r\nQux: ipsum\r\n\r\nhello", |
|||
Psr7\Message::toString($response) |
|||
); |
|||
} |
|||
|
|||
public function testCorrectlyRendersSetCookieHeadersToString(): void |
|||
{ |
|||
$response = new Psr7\Response(200, [ |
|||
'Set-Cookie' => ['bar', 'baz', 'qux'], |
|||
], 'hello', '1.0', 'FOO'); |
|||
self::assertSame( |
|||
"HTTP/1.0 200 FOO\r\nSet-Cookie: bar\r\nSet-Cookie: baz\r\nSet-Cookie: qux\r\n\r\nhello", |
|||
Psr7\Message::toString($response) |
|||
); |
|||
} |
|||
|
|||
public function testRewindsBody(): void |
|||
{ |
|||
$body = Psr7\Utils::streamFor('abc'); |
|||
$res = new Psr7\Response(200, [], $body); |
|||
Psr7\Message::rewindBody($res); |
|||
self::assertSame(0, $body->tell()); |
|||
$body->rewind(); |
|||
Psr7\Message::rewindBody($res); |
|||
self::assertSame(0, $body->tell()); |
|||
} |
|||
|
|||
public function testThrowsWhenBodyCannotBeRewound(): void |
|||
{ |
|||
$body = Psr7\Utils::streamFor('abc'); |
|||
$body->read(1); |
|||
$body = FnStream::decorate($body, [ |
|||
'rewind' => function (): void { |
|||
throw new \RuntimeException('a'); |
|||
}, |
|||
]); |
|||
$res = new Psr7\Response(200, [], $body); |
|||
|
|||
$this->expectException(\RuntimeException::class); |
|||
|
|||
Psr7\Message::rewindBody($res); |
|||
} |
|||
|
|||
public function testParsesRequestMessages(): void |
|||
{ |
|||
$req = "GET /abc HTTP/1.0\r\nHost: foo.com\r\nFoo: Bar\r\nBaz: Bam\r\nBaz: Qux\r\n\r\nTest"; |
|||
$request = Psr7\Message::parseRequest($req); |
|||
self::assertSame('GET', $request->getMethod()); |
|||
self::assertSame('/abc', $request->getRequestTarget()); |
|||
self::assertSame('1.0', $request->getProtocolVersion()); |
|||
self::assertSame('foo.com', $request->getHeaderLine('Host')); |
|||
self::assertSame('Bar', $request->getHeaderLine('Foo')); |
|||
self::assertSame('Bam, Qux', $request->getHeaderLine('Baz')); |
|||
self::assertSame('Test', (string) $request->getBody()); |
|||
self::assertSame('http://foo.com/abc', (string) $request->getUri()); |
|||
} |
|||
|
|||
public function testParsesRequestMessagesWithHttpsScheme(): void |
|||
{ |
|||
$req = "PUT /abc?baz=bar HTTP/1.1\r\nHost: foo.com:443\r\n\r\n"; |
|||
$request = Psr7\Message::parseRequest($req); |
|||
self::assertSame('PUT', $request->getMethod()); |
|||
self::assertSame('/abc?baz=bar', $request->getRequestTarget()); |
|||
self::assertSame('1.1', $request->getProtocolVersion()); |
|||
self::assertSame('foo.com:443', $request->getHeaderLine('Host')); |
|||
self::assertSame('', (string) $request->getBody()); |
|||
self::assertSame('https://foo.com/abc?baz=bar', (string) $request->getUri()); |
|||
} |
|||
|
|||
public function testParsesRequestMessagesWithUriWhenHostIsNotFirst(): void |
|||
{ |
|||
$req = "PUT / HTTP/1.1\r\nFoo: Bar\r\nHost: foo.com\r\n\r\n"; |
|||
$request = Psr7\Message::parseRequest($req); |
|||
self::assertSame('PUT', $request->getMethod()); |
|||
self::assertSame('/', $request->getRequestTarget()); |
|||
self::assertSame('http://foo.com/', (string) $request->getUri()); |
|||
} |
|||
|
|||
public function testParsesRequestMessagesWithFullUri(): void |
|||
{ |
|||
$req = "GET https://www.google.com:443/search?q=foobar HTTP/1.1\r\nHost: www.google.com\r\n\r\n"; |
|||
$request = Psr7\Message::parseRequest($req); |
|||
self::assertSame('GET', $request->getMethod()); |
|||
self::assertSame('https://www.google.com:443/search?q=foobar', $request->getRequestTarget()); |
|||
self::assertSame('1.1', $request->getProtocolVersion()); |
|||
self::assertSame('www.google.com', $request->getHeaderLine('Host')); |
|||
self::assertSame('', (string) $request->getBody()); |
|||
self::assertSame('https://www.google.com/search?q=foobar', (string) $request->getUri()); |
|||
} |
|||
|
|||
public function testParsesRequestMessagesWithCustomMethod(): void |
|||
{ |
|||
$req = "GET_DATA / HTTP/1.1\r\nFoo: Bar\r\nHost: foo.com\r\n\r\n"; |
|||
$request = Psr7\Message::parseRequest($req); |
|||
self::assertSame('GET_DATA', $request->getMethod()); |
|||
} |
|||
|
|||
public function testParsesRequestMessagesWithNumericHeader(): void |
|||
{ |
|||
$req = "GET /abc HTTP/1.0\r\nHost: foo.com\r\nFoo: Bar\r\nBaz: Bam\r\nBaz: Qux\r\n123: 456\r\n\r\nTest"; |
|||
$request = Psr7\Message::parseRequest($req); |
|||
self::assertSame('GET', $request->getMethod()); |
|||
self::assertSame('/abc', $request->getRequestTarget()); |
|||
self::assertSame('1.0', $request->getProtocolVersion()); |
|||
self::assertSame('foo.com', $request->getHeaderLine('Host')); |
|||
self::assertSame('Bar', $request->getHeaderLine('Foo')); |
|||
self::assertSame('Bam, Qux', $request->getHeaderLine('Baz')); |
|||
self::assertSame('456', $request->getHeaderLine('123')); |
|||
self::assertSame('Test', (string) $request->getBody()); |
|||
self::assertSame('http://foo.com/abc', (string) $request->getUri()); |
|||
} |
|||
|
|||
public function testParsesRequestMessagesWithFoldedHeadersOnHttp10(): void |
|||
{ |
|||
$req = "PUT / HTTP/1.0\r\nFoo: Bar\r\n Bam\r\n\r\n"; |
|||
$request = Psr7\Message::parseRequest($req); |
|||
self::assertSame('PUT', $request->getMethod()); |
|||
self::assertSame('/', $request->getRequestTarget()); |
|||
self::assertSame('Bar Bam', $request->getHeaderLine('Foo')); |
|||
} |
|||
|
|||
public function testRequestParsingFailsWithFoldedHeadersOnHttp11(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Invalid header syntax: Obsolete line folding'); |
|||
|
|||
Psr7\Message::parseResponse("GET_DATA / HTTP/1.1\r\nFoo: Bar\r\n Biz: Bam\r\n\r\n"); |
|||
} |
|||
|
|||
public function testParsesRequestMessagesWhenHeaderDelimiterIsOnlyALineFeed(): void |
|||
{ |
|||
$req = "PUT / HTTP/1.0\nFoo: Bar\nBaz: Bam\n\n"; |
|||
$request = Psr7\Message::parseRequest($req); |
|||
self::assertSame('PUT', $request->getMethod()); |
|||
self::assertSame('/', $request->getRequestTarget()); |
|||
self::assertSame('Bar', $request->getHeaderLine('Foo')); |
|||
self::assertSame('Bam', $request->getHeaderLine('Baz')); |
|||
} |
|||
|
|||
public function testValidatesRequestMessages(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
|
|||
Psr7\Message::parseRequest("HTTP/1.1 200 OK\r\n\r\n"); |
|||
} |
|||
|
|||
public function testParsesResponseMessages(): void |
|||
{ |
|||
$res = "HTTP/1.0 200 OK\r\nFoo: Bar\r\nBaz: Bam\r\nBaz: Qux\r\n\r\nTest"; |
|||
$response = Psr7\Message::parseResponse($res); |
|||
self::assertSame(200, $response->getStatusCode()); |
|||
self::assertSame('OK', $response->getReasonPhrase()); |
|||
self::assertSame('1.0', $response->getProtocolVersion()); |
|||
self::assertSame('Bar', $response->getHeaderLine('Foo')); |
|||
self::assertSame('Bam, Qux', $response->getHeaderLine('Baz')); |
|||
self::assertSame('Test', (string) $response->getBody()); |
|||
} |
|||
|
|||
public function testParsesResponseWithoutReason(): void |
|||
{ |
|||
$res = "HTTP/1.0 200\r\nFoo: Bar\r\nBaz: Bam\r\nBaz: Qux\r\n\r\nTest"; |
|||
$response = Psr7\Message::parseResponse($res); |
|||
self::assertSame(200, $response->getStatusCode()); |
|||
self::assertSame('OK', $response->getReasonPhrase()); |
|||
self::assertSame('1.0', $response->getProtocolVersion()); |
|||
self::assertSame('Bar', $response->getHeaderLine('Foo')); |
|||
self::assertSame('Bam, Qux', $response->getHeaderLine('Baz')); |
|||
self::assertSame('Test', (string) $response->getBody()); |
|||
} |
|||
|
|||
public function testParsesResponseWithLeadingDelimiter(): void |
|||
{ |
|||
$res = "\r\nHTTP/1.0 200\r\nFoo: Bar\r\n\r\nTest"; |
|||
$response = Psr7\Message::parseResponse($res); |
|||
self::assertSame(200, $response->getStatusCode()); |
|||
self::assertSame('OK', $response->getReasonPhrase()); |
|||
self::assertSame('1.0', $response->getProtocolVersion()); |
|||
self::assertSame('Bar', $response->getHeaderLine('Foo')); |
|||
self::assertSame('Test', (string) $response->getBody()); |
|||
} |
|||
|
|||
public function testParsesResponseWithFoldedHeadersOnHttp10(): void |
|||
{ |
|||
$res = "HTTP/1.0 200\r\nFoo: Bar\r\n Bam\r\n\r\nTest"; |
|||
$response = Psr7\Message::parseResponse($res); |
|||
self::assertSame(200, $response->getStatusCode()); |
|||
self::assertSame('OK', $response->getReasonPhrase()); |
|||
self::assertSame('1.0', $response->getProtocolVersion()); |
|||
self::assertSame('Bar Bam', $response->getHeaderLine('Foo')); |
|||
self::assertSame('Test', (string) $response->getBody()); |
|||
} |
|||
|
|||
public function testResponseParsingFailsWithFoldedHeadersOnHttp11(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Invalid header syntax: Obsolete line folding'); |
|||
Psr7\Message::parseResponse("HTTP/1.1 200\r\nFoo: Bar\r\n Biz: Bam\r\nBaz: Qux\r\n\r\nTest"); |
|||
} |
|||
|
|||
public function testParsesResponseWhenHeaderDelimiterIsOnlyALineFeed(): void |
|||
{ |
|||
$res = "HTTP/1.0 200\nFoo: Bar\nBaz: Bam\n\nTest\n\nOtherTest"; |
|||
$response = Psr7\Message::parseResponse($res); |
|||
self::assertSame(200, $response->getStatusCode()); |
|||
self::assertSame('OK', $response->getReasonPhrase()); |
|||
self::assertSame('1.0', $response->getProtocolVersion()); |
|||
self::assertSame('Bar', $response->getHeaderLine('Foo')); |
|||
self::assertSame('Bam', $response->getHeaderLine('Baz')); |
|||
self::assertSame("Test\n\nOtherTest", (string) $response->getBody()); |
|||
} |
|||
|
|||
public function testResponseParsingFailsWithoutHeaderDelimiter(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Invalid message: Missing header delimiter'); |
|||
Psr7\Message::parseResponse("HTTP/1.0 200\r\nFoo: Bar\r\n Baz: Bam\r\nBaz: Qux\r\n"); |
|||
} |
|||
|
|||
public function testValidatesResponseMessages(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
Psr7\Message::parseResponse("GET / HTTP/1.1\r\n\r\n"); |
|||
} |
|||
|
|||
public function testMessageBodySummaryWithSmallBody(): void |
|||
{ |
|||
$message = new Psr7\Response(200, [], 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'); |
|||
self::assertSame('Lorem ipsum dolor sit amet, consectetur adipiscing elit.', Psr7\Message::bodySummary($message)); |
|||
} |
|||
|
|||
public function testMessageBodySummaryWithLargeBody(): void |
|||
{ |
|||
$message = new Psr7\Response(200, [], 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'); |
|||
self::assertSame('Lorem ipsu (truncated...)', Psr7\Message::bodySummary($message, 10)); |
|||
} |
|||
|
|||
public function testMessageBodySummaryWithSpecialUTF8Characters(): void |
|||
{ |
|||
$message = new Psr7\Response(200, [], '’é€௵ဪ‱'); |
|||
self::assertSame('’é€௵ဪ‱', Psr7\Message::bodySummary($message)); |
|||
} |
|||
|
|||
public function testMessageBodySummaryWithSpecialUTF8CharactersAndLargeBody(): void |
|||
{ |
|||
$message = new Psr7\Response(200, [], '🤦🏾♀️'); |
|||
// The first Unicode codepoint of the body has four bytes. |
|||
self::assertNull(Psr7\Message::bodySummary($message, 3)); |
|||
} |
|||
|
|||
public function testMessageBodySummaryWithEmptyBody(): void |
|||
{ |
|||
$message = new Psr7\Response(200, [], ''); |
|||
self::assertNull(Psr7\Message::bodySummary($message)); |
|||
} |
|||
|
|||
public function testMessageBodySummaryNotInitiallyRewound(): void |
|||
{ |
|||
$message = new Psr7\Response(200, [], 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'); |
|||
$message->getBody()->read(10); |
|||
self::assertSame('Lorem ipsu (truncated...)', Psr7\Message::bodySummary($message, 10)); |
|||
} |
|||
|
|||
public function testGetResponseBodySummaryOfNonReadableStream(): void |
|||
{ |
|||
$message = new Psr7\Response(500, [], new ReadSeekOnlyStream()); |
|||
self::assertNull(Psr7\Message::bodySummary($message)); |
|||
} |
|||
} |
@ -1,25 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class MimeTypeTest extends TestCase |
|||
{ |
|||
public function testDetermineFromExtension(): void |
|||
{ |
|||
self::assertNull(Psr7\MimeType::fromExtension('not-a-real-extension')); |
|||
self::assertSame('application/json', Psr7\MimeType::fromExtension('json')); |
|||
} |
|||
|
|||
public function testDetermineFromFilename(): void |
|||
{ |
|||
self::assertSame( |
|||
'image/jpeg', |
|||
Psr7\MimeType::fromFilename('/tmp/images/IMG034821.JPEG') |
|||
); |
|||
} |
|||
} |
@ -1,330 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\MultipartStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class MultipartStreamTest extends TestCase |
|||
{ |
|||
public function testCreatesDefaultBoundary(): void |
|||
{ |
|||
$b = new MultipartStream(); |
|||
self::assertNotEmpty($b->getBoundary()); |
|||
} |
|||
|
|||
public function testCanProvideBoundary(): void |
|||
{ |
|||
$b = new MultipartStream([], 'foo'); |
|||
self::assertSame('foo', $b->getBoundary()); |
|||
} |
|||
|
|||
public function testIsNotWritable(): void |
|||
{ |
|||
$b = new MultipartStream(); |
|||
self::assertFalse($b->isWritable()); |
|||
} |
|||
|
|||
public function testCanCreateEmptyStream(): void |
|||
{ |
|||
$b = new MultipartStream(); |
|||
$boundary = $b->getBoundary(); |
|||
self::assertSame("--{$boundary}--\r\n", $b->getContents()); |
|||
self::assertSame(strlen($boundary) + 6, $b->getSize()); |
|||
} |
|||
|
|||
public function testValidatesFilesArrayElement(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
new MultipartStream([['foo' => 'bar']]); |
|||
} |
|||
|
|||
public function testEnsuresFileHasName(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
new MultipartStream([['contents' => 'bar']]); |
|||
} |
|||
|
|||
public function testSerializesFields(): void |
|||
{ |
|||
$b = new MultipartStream([ |
|||
[ |
|||
'name' => 'foo', |
|||
'contents' => 'bar', |
|||
], |
|||
[ |
|||
'name' => 'baz', |
|||
'contents' => 'bam', |
|||
], |
|||
], 'boundary'); |
|||
|
|||
$expected = \implode('', [ |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"foo\"\r\n", |
|||
"Content-Length: 3\r\n", |
|||
"\r\n", |
|||
"bar\r\n", |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"baz\"\r\n", |
|||
"Content-Length: 3\r\n", |
|||
"\r\n", |
|||
"bam\r\n", |
|||
"--boundary--\r\n", |
|||
]); |
|||
|
|||
self::assertSame($expected, (string) $b); |
|||
} |
|||
|
|||
public function testSerializesNonStringFields(): void |
|||
{ |
|||
$b = new MultipartStream([ |
|||
[ |
|||
'name' => 'int', |
|||
'contents' => (int) 1, |
|||
], |
|||
[ |
|||
'name' => 'bool', |
|||
'contents' => (bool) false, |
|||
], |
|||
[ |
|||
'name' => 'bool2', |
|||
'contents' => (bool) true, |
|||
], |
|||
[ |
|||
'name' => 'float', |
|||
'contents' => (float) 1.1, |
|||
], |
|||
], 'boundary'); |
|||
|
|||
$expected = \implode('', [ |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"int\"\r\n", |
|||
"Content-Length: 1\r\n", |
|||
"\r\n", |
|||
"1\r\n", |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"bool\"\r\n", |
|||
"\r\n", |
|||
"\r\n", |
|||
'--boundary', |
|||
"\r\n", |
|||
"Content-Disposition: form-data; name=\"bool2\"\r\n", |
|||
"Content-Length: 1\r\n", |
|||
"\r\n", |
|||
"1\r\n", |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"float\"\r\n", |
|||
"Content-Length: 3\r\n", |
|||
"\r\n", |
|||
"1.1\r\n", |
|||
"--boundary--\r\n", |
|||
'', |
|||
]); |
|||
|
|||
self::assertSame($expected, (string) $b); |
|||
} |
|||
|
|||
public function testSerializesFiles(): void |
|||
{ |
|||
$f1 = Psr7\FnStream::decorate(Psr7\Utils::streamFor('foo'), [ |
|||
'getMetadata' => static function (): string { |
|||
return '/foo/bar.txt'; |
|||
}, |
|||
]); |
|||
|
|||
$f2 = Psr7\FnStream::decorate(Psr7\Utils::streamFor('baz'), [ |
|||
'getMetadata' => static function (): string { |
|||
return '/foo/baz.jpg'; |
|||
}, |
|||
]); |
|||
|
|||
$f3 = Psr7\FnStream::decorate(Psr7\Utils::streamFor('bar'), [ |
|||
'getMetadata' => static function (): string { |
|||
return '/foo/bar.unknown'; |
|||
}, |
|||
]); |
|||
|
|||
$b = new MultipartStream([ |
|||
[ |
|||
'name' => 'foo', |
|||
'contents' => $f1, |
|||
], |
|||
[ |
|||
'name' => 'qux', |
|||
'contents' => $f2, |
|||
], |
|||
[ |
|||
'name' => 'qux', |
|||
'contents' => $f3, |
|||
], |
|||
], 'boundary'); |
|||
|
|||
$expected = \implode('', [ |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"foo\"; filename=\"bar.txt\"\r\n", |
|||
"Content-Length: 3\r\n", |
|||
"Content-Type: text/plain\r\n", |
|||
"\r\n", |
|||
"foo\r\n", |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"qux\"; filename=\"baz.jpg\"\r\n", |
|||
"Content-Length: 3\r\n", |
|||
"Content-Type: image/jpeg\r\n", |
|||
"\r\n", |
|||
"baz\r\n", |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"qux\"; filename=\"bar.unknown\"\r\n", |
|||
"Content-Length: 3\r\n", |
|||
"Content-Type: application/octet-stream\r\n", |
|||
"\r\n", |
|||
"bar\r\n", |
|||
"--boundary--\r\n", |
|||
]); |
|||
|
|||
self::assertSame($expected, (string) $b); |
|||
} |
|||
|
|||
public function testSerializesFilesWithMixedNewlines(): void |
|||
{ |
|||
$content = "LF\nCRLF\r\nCR\r"; |
|||
$contentLength = \strlen($content); |
|||
|
|||
$f1 = Psr7\FnStream::decorate(Psr7\Utils::streamFor($content), [ |
|||
'getMetadata' => static function (): string { |
|||
return '/foo/newlines.txt'; |
|||
}, |
|||
]); |
|||
|
|||
$b = new MultipartStream([ |
|||
[ |
|||
'name' => 'newlines', |
|||
'contents' => $f1, |
|||
], |
|||
], 'boundary'); |
|||
|
|||
$expected = \implode('', [ |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"newlines\"; filename=\"newlines.txt\"\r\n", |
|||
"Content-Length: {$contentLength}\r\n", |
|||
"Content-Type: text/plain\r\n", |
|||
"\r\n", |
|||
"{$content}\r\n", |
|||
"--boundary--\r\n", |
|||
]); |
|||
|
|||
// Do not perform newline normalization in the assertion! The `$content` must |
|||
// be embedded as-is in the payload. |
|||
self::assertSame($expected, (string) $b); |
|||
} |
|||
|
|||
public function testSerializesFilesWithCustomHeaders(): void |
|||
{ |
|||
$f1 = Psr7\FnStream::decorate(Psr7\Utils::streamFor('foo'), [ |
|||
'getMetadata' => static function (): string { |
|||
return '/foo/bar.txt'; |
|||
}, |
|||
]); |
|||
|
|||
$b = new MultipartStream([ |
|||
[ |
|||
'name' => 'foo', |
|||
'contents' => $f1, |
|||
'headers' => [ |
|||
'x-foo' => 'bar', |
|||
'content-disposition' => 'custom', |
|||
], |
|||
], |
|||
], 'boundary'); |
|||
|
|||
$expected = \implode('', [ |
|||
"--boundary\r\n", |
|||
"x-foo: bar\r\n", |
|||
"content-disposition: custom\r\n", |
|||
"Content-Length: 3\r\n", |
|||
"Content-Type: text/plain\r\n", |
|||
"\r\n", |
|||
"foo\r\n", |
|||
"--boundary--\r\n", |
|||
]); |
|||
|
|||
self::assertSame($expected, (string) $b); |
|||
} |
|||
|
|||
public function testSerializesFilesWithCustomHeadersAndMultipleValues(): void |
|||
{ |
|||
$f1 = Psr7\FnStream::decorate(Psr7\Utils::streamFor('foo'), [ |
|||
'getMetadata' => static function (): string { |
|||
return '/foo/bar.txt'; |
|||
}, |
|||
]); |
|||
|
|||
$f2 = Psr7\FnStream::decorate(Psr7\Utils::streamFor('baz'), [ |
|||
'getMetadata' => static function (): string { |
|||
return '/foo/baz.jpg'; |
|||
}, |
|||
]); |
|||
|
|||
$b = new MultipartStream([ |
|||
[ |
|||
'name' => 'foo', |
|||
'contents' => $f1, |
|||
'headers' => [ |
|||
'x-foo' => 'bar', |
|||
'content-disposition' => 'custom', |
|||
], |
|||
], |
|||
[ |
|||
'name' => 'foo', |
|||
'contents' => $f2, |
|||
'headers' => ['cOntenT-Type' => 'custom'], |
|||
], |
|||
], 'boundary'); |
|||
|
|||
$expected = \implode('', [ |
|||
"--boundary\r\n", |
|||
"x-foo: bar\r\n", |
|||
"content-disposition: custom\r\n", |
|||
"Content-Length: 3\r\n", |
|||
"Content-Type: text/plain\r\n", |
|||
"\r\n", |
|||
"foo\r\n", |
|||
"--boundary\r\n", |
|||
"cOntenT-Type: custom\r\n", |
|||
"Content-Disposition: form-data; name=\"foo\"; filename=\"baz.jpg\"\r\n", |
|||
"Content-Length: 3\r\n", |
|||
"\r\n", |
|||
"baz\r\n", |
|||
"--boundary--\r\n", |
|||
]); |
|||
|
|||
self::assertSame($expected, (string) $b); |
|||
} |
|||
|
|||
public function testCanCreateWithNoneMetadataStreamField(): void |
|||
{ |
|||
$str = 'dummy text'; |
|||
$a = Psr7\Utils::streamFor(static function () use ($str): string { |
|||
return $str; |
|||
}); |
|||
$b = new Psr7\LimitStream($a, \strlen($str)); |
|||
$c = new MultipartStream([ |
|||
[ |
|||
'name' => 'foo', |
|||
'contents' => $b, |
|||
], |
|||
], 'boundary'); |
|||
|
|||
$expected = \implode('', [ |
|||
"--boundary\r\n", |
|||
"Content-Disposition: form-data; name=\"foo\"\r\n", |
|||
"\r\n", |
|||
$str."\r\n", |
|||
"--boundary--\r\n", |
|||
]); |
|||
|
|||
self::assertSame($expected, (string) $c); |
|||
} |
|||
} |
@ -1,38 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\NoSeekStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\StreamInterface; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\NoSeekStream |
|||
* @covers \GuzzleHttp\Psr7\StreamDecoratorTrait |
|||
*/ |
|||
class NoSeekStreamTest extends TestCase |
|||
{ |
|||
public function testCannotSeek(): void |
|||
{ |
|||
$s = $this->createMock(StreamInterface::class); |
|||
$s->expects(self::never())->method('seek'); |
|||
$s->expects(self::never())->method('isSeekable'); |
|||
$wrapped = new NoSeekStream($s); |
|||
self::assertFalse($wrapped->isSeekable()); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Cannot seek a NoSeekStream'); |
|||
$wrapped->seek(2); |
|||
} |
|||
|
|||
public function testToStringDoesNotSeek(): void |
|||
{ |
|||
$s = \GuzzleHttp\Psr7\Utils::streamFor('foo'); |
|||
$s->seek(1); |
|||
$wrapped = new NoSeekStream($s); |
|||
self::assertSame('oo', (string) $wrapped); |
|||
|
|||
$wrapped->close(); |
|||
} |
|||
} |
@ -1,105 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\LimitStream; |
|||
use GuzzleHttp\Psr7\PumpStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class PumpStreamTest extends TestCase |
|||
{ |
|||
public function testHasMetadataAndSize(): void |
|||
{ |
|||
$p = new PumpStream(function (): void { |
|||
}, [ |
|||
'metadata' => ['foo' => 'bar'], |
|||
'size' => 100, |
|||
]); |
|||
|
|||
self::assertSame('bar', $p->getMetadata('foo')); |
|||
self::assertSame(['foo' => 'bar'], $p->getMetadata()); |
|||
self::assertSame(100, $p->getSize()); |
|||
} |
|||
|
|||
public function testCanReadFromCallable(): void |
|||
{ |
|||
$p = Psr7\Utils::streamFor(function ($size) { |
|||
return 'a'; |
|||
}); |
|||
self::assertSame('a', $p->read(1)); |
|||
self::assertSame(1, $p->tell()); |
|||
self::assertSame('aaaaa', $p->read(5)); |
|||
self::assertSame(6, $p->tell()); |
|||
} |
|||
|
|||
public function testStoresExcessDataInBuffer(): void |
|||
{ |
|||
$called = []; |
|||
$p = Psr7\Utils::streamFor(function ($size) use (&$called) { |
|||
$called[] = $size; |
|||
|
|||
return 'abcdef'; |
|||
}); |
|||
self::assertSame('a', $p->read(1)); |
|||
self::assertSame('b', $p->read(1)); |
|||
self::assertSame('cdef', $p->read(4)); |
|||
self::assertSame('abcdefabc', $p->read(9)); |
|||
self::assertSame([1, 9, 3], $called); |
|||
} |
|||
|
|||
public function testInifiniteStreamWrappedInLimitStream(): void |
|||
{ |
|||
$p = Psr7\Utils::streamFor(function () { |
|||
return 'a'; |
|||
}); |
|||
$s = new LimitStream($p, 5); |
|||
self::assertSame('aaaaa', (string) $s); |
|||
} |
|||
|
|||
public function testDescribesCapabilities(): void |
|||
{ |
|||
$p = Psr7\Utils::streamFor(function (): void { |
|||
}); |
|||
self::assertTrue($p->isReadable()); |
|||
self::assertFalse($p->isSeekable()); |
|||
self::assertFalse($p->isWritable()); |
|||
self::assertNull($p->getSize()); |
|||
self::assertSame('', $p->getContents()); |
|||
self::assertSame('', (string) $p); |
|||
$p->close(); |
|||
self::assertSame('', $p->read(10)); |
|||
self::assertTrue($p->eof()); |
|||
|
|||
try { |
|||
self::assertFalse($p->write('aa')); |
|||
self::fail(); |
|||
} catch (\RuntimeException $e) { |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @requires PHP < 7.4 |
|||
*/ |
|||
public function testThatConvertingStreamToStringWillTriggerErrorAndWillReturnEmptyString(): void |
|||
{ |
|||
$p = Psr7\Utils::streamFor(function ($size): void { |
|||
throw new \Exception(); |
|||
}); |
|||
self::assertInstanceOf(PumpStream::class, $p); |
|||
|
|||
$errors = []; |
|||
set_error_handler(function (int $errorNumber, string $errorMessage) use (&$errors): void { |
|||
$errors[] = ['number' => $errorNumber, 'message' => $errorMessage]; |
|||
}); |
|||
(string) $p; |
|||
|
|||
restore_error_handler(); |
|||
|
|||
self::assertCount(1, $errors); |
|||
self::assertSame(E_USER_ERROR, $errors[0]['number']); |
|||
self::assertStringStartsWith('GuzzleHttp\Psr7\PumpStream::__toString exception:', $errors[0]['message']); |
|||
} |
|||
} |
@ -1,124 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
class QueryTest extends TestCase |
|||
{ |
|||
public function parseQueryProvider() |
|||
{ |
|||
return [ |
|||
// Does not need to parse when the string is empty |
|||
['', []], |
|||
// Can parse mult-values items |
|||
['q=a&q=b', ['q' => ['a', 'b']]], |
|||
// Can parse multi-valued items that use numeric indices |
|||
['q[0]=a&q[1]=b', ['q[0]' => 'a', 'q[1]' => 'b']], |
|||
// Can parse duplicates and does not include numeric indices |
|||
['q[]=a&q[]=b', ['q[]' => ['a', 'b']]], |
|||
// Ensures that the value of "q" is an array even though one value |
|||
['q[]=a', ['q[]' => 'a']], |
|||
// Does not modify "." to "_" like PHP's parse_str() |
|||
['q.a=a&q.b=b', ['q.a' => 'a', 'q.b' => 'b']], |
|||
// Can decode %20 to " " |
|||
['q%20a=a%20b', ['q a' => 'a b']], |
|||
// Can parse funky strings with no values by assigning each to null |
|||
['q&a', ['q' => null, 'a' => null]], |
|||
// Does not strip trailing equal signs |
|||
['data=abc=', ['data' => 'abc=']], |
|||
// Can store duplicates without affecting other values |
|||
['foo=a&foo=b&?µ=c', ['foo' => ['a', 'b'], '?µ' => 'c']], |
|||
// Sets value to null when no "=" is present |
|||
['foo', ['foo' => null]], |
|||
// Preserves "0" keys. |
|||
['0', ['0' => null]], |
|||
// Sets the value to an empty string when "=" is present |
|||
['0=', ['0' => '']], |
|||
// Preserves falsey keys |
|||
['var=0', ['var' => '0']], |
|||
['a[b][c]=1&a[b][c]=2', ['a[b][c]' => ['1', '2']]], |
|||
['a[b]=c&a[d]=e', ['a[b]' => 'c', 'a[d]' => 'e']], |
|||
// Ensure it doesn't leave things behind with repeated values |
|||
// Can parse mult-values items |
|||
['q=a&q=b&q=c', ['q' => ['a', 'b', 'c']]], |
|||
// Keeps first null when parsing mult-values |
|||
['q&q=&q=a', ['q' => [null, '', 'a']]], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider parseQueryProvider |
|||
*/ |
|||
public function testParsesQueries($input, $output): void |
|||
{ |
|||
$result = Psr7\Query::parse($input); |
|||
self::assertSame($output, $result); |
|||
} |
|||
|
|||
public function testDoesNotDecode(): void |
|||
{ |
|||
$str = 'foo%20=bar'; |
|||
$data = Psr7\Query::parse($str, false); |
|||
self::assertSame(['foo%20' => 'bar'], $data); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider parseQueryProvider |
|||
*/ |
|||
public function testParsesAndBuildsQueries($input): void |
|||
{ |
|||
$result = Psr7\Query::parse($input, false); |
|||
self::assertSame($input, Psr7\Query::build($result, false)); |
|||
} |
|||
|
|||
public function testEncodesWithRfc1738(): void |
|||
{ |
|||
$str = Psr7\Query::build(['foo bar' => 'baz+'], PHP_QUERY_RFC1738); |
|||
self::assertSame('foo+bar=baz%2B', $str); |
|||
} |
|||
|
|||
public function testEncodesWithRfc3986(): void |
|||
{ |
|||
$str = Psr7\Query::build(['foo bar' => 'baz+'], PHP_QUERY_RFC3986); |
|||
self::assertSame('foo%20bar=baz%2B', $str); |
|||
} |
|||
|
|||
public function testDoesNotEncode(): void |
|||
{ |
|||
$str = Psr7\Query::build(['foo bar' => 'baz+'], false); |
|||
self::assertSame('foo bar=baz+', $str); |
|||
} |
|||
|
|||
public function testCanControlDecodingType(): void |
|||
{ |
|||
$result = Psr7\Query::parse('var=foo+bar', PHP_QUERY_RFC3986); |
|||
self::assertSame('foo+bar', $result['var']); |
|||
$result = Psr7\Query::parse('var=foo+bar', PHP_QUERY_RFC1738); |
|||
self::assertSame('foo bar', $result['var']); |
|||
} |
|||
|
|||
public function testBuildBooleans(): void |
|||
{ |
|||
$data = [ |
|||
'true' => true, |
|||
'false' => false, |
|||
]; |
|||
self::assertEquals(http_build_query($data), Psr7\Query::build($data)); |
|||
|
|||
$data = [ |
|||
'foo' => [true, 'true'], |
|||
'bar' => [false, 'false'], |
|||
]; |
|||
self::assertEquals('foo=1&foo=true&bar=0&bar=false', Psr7\Query::build($data, PHP_QUERY_RFC1738)); |
|||
|
|||
$data = [ |
|||
'foo' => true, |
|||
'bar' => false, |
|||
]; |
|||
self::assertEquals('foo=true&bar=false', Psr7\Query::build($data, PHP_QUERY_RFC3986, false)); |
|||
} |
|||
} |
@ -1,26 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\Stream; |
|||
use GuzzleHttp\Psr7\Utils; |
|||
|
|||
final class ReadSeekOnlyStream extends Stream |
|||
{ |
|||
public function __construct() |
|||
{ |
|||
parent::__construct(Utils::tryFopen('php://memory', 'wb')); |
|||
} |
|||
|
|||
public function isSeekable(): bool |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
public function isReadable(): bool |
|||
{ |
|||
return false; |
|||
} |
|||
} |
@ -1,362 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\Request; |
|||
use GuzzleHttp\Psr7\Uri; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\StreamInterface; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\MessageTrait |
|||
* @covers \GuzzleHttp\Psr7\Request |
|||
*/ |
|||
class RequestTest extends TestCase |
|||
{ |
|||
public function testRequestUriMayBeString(): void |
|||
{ |
|||
$r = new Request('GET', '/'); |
|||
self::assertSame('/', (string) $r->getUri()); |
|||
} |
|||
|
|||
public function testRequestUriMayBeUri(): void |
|||
{ |
|||
$uri = new Uri('/'); |
|||
$r = new Request('GET', $uri); |
|||
self::assertSame($uri, $r->getUri()); |
|||
} |
|||
|
|||
public function testValidateRequestUri(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
new Request('GET', '///'); |
|||
} |
|||
|
|||
public function testCanConstructWithBody(): void |
|||
{ |
|||
$r = new Request('GET', '/', [], 'baz'); |
|||
self::assertInstanceOf(StreamInterface::class, $r->getBody()); |
|||
self::assertSame('baz', (string) $r->getBody()); |
|||
} |
|||
|
|||
public function testNullBody(): void |
|||
{ |
|||
$r = new Request('GET', '/', [], null); |
|||
self::assertInstanceOf(StreamInterface::class, $r->getBody()); |
|||
self::assertSame('', (string) $r->getBody()); |
|||
} |
|||
|
|||
public function testFalseyBody(): void |
|||
{ |
|||
$r = new Request('GET', '/', [], '0'); |
|||
self::assertInstanceOf(StreamInterface::class, $r->getBody()); |
|||
self::assertSame('0', (string) $r->getBody()); |
|||
} |
|||
|
|||
public function testConstructorDoesNotReadStreamBody(): void |
|||
{ |
|||
$streamIsRead = false; |
|||
$body = Psr7\FnStream::decorate(Psr7\Utils::streamFor(''), [ |
|||
'__toString' => function () use (&$streamIsRead) { |
|||
$streamIsRead = true; |
|||
|
|||
return ''; |
|||
}, |
|||
]); |
|||
|
|||
$r = new Request('GET', '/', [], $body); |
|||
self::assertFalse($streamIsRead); |
|||
self::assertSame($body, $r->getBody()); |
|||
} |
|||
|
|||
public function testCapitalizesMethod(): void |
|||
{ |
|||
$r = new Request('get', '/'); |
|||
self::assertSame('GET', $r->getMethod()); |
|||
} |
|||
|
|||
public function testCapitalizesWithMethod(): void |
|||
{ |
|||
$r = new Request('GET', '/'); |
|||
self::assertSame('PUT', $r->withMethod('put')->getMethod()); |
|||
} |
|||
|
|||
public function testWithUri(): void |
|||
{ |
|||
$r1 = new Request('GET', '/'); |
|||
$u1 = $r1->getUri(); |
|||
$u2 = new Uri('http://www.example.com'); |
|||
$r2 = $r1->withUri($u2); |
|||
self::assertNotSame($r1, $r2); |
|||
self::assertSame($u2, $r2->getUri()); |
|||
self::assertSame($u1, $r1->getUri()); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider invalidMethodsProvider |
|||
*/ |
|||
public function testConstructWithInvalidMethods($method): void |
|||
{ |
|||
$this->expectException(\TypeError::class); |
|||
new Request($method, '/'); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider invalidMethodsProvider |
|||
*/ |
|||
public function testWithInvalidMethods($method): void |
|||
{ |
|||
$r = new Request('get', '/'); |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$r->withMethod($method); |
|||
} |
|||
|
|||
public function invalidMethodsProvider(): iterable |
|||
{ |
|||
return [ |
|||
[null], |
|||
[false], |
|||
[['foo']], |
|||
[new \stdClass()], |
|||
]; |
|||
} |
|||
|
|||
public function testSameInstanceWhenSameUri(): void |
|||
{ |
|||
$r1 = new Request('GET', 'http://foo.com'); |
|||
$r2 = $r1->withUri($r1->getUri()); |
|||
self::assertSame($r1, $r2); |
|||
} |
|||
|
|||
public function testWithRequestTarget(): void |
|||
{ |
|||
$r1 = new Request('GET', '/'); |
|||
$r2 = $r1->withRequestTarget('*'); |
|||
self::assertSame('*', $r2->getRequestTarget()); |
|||
self::assertSame('/', $r1->getRequestTarget()); |
|||
} |
|||
|
|||
public function testRequestTargetDoesNotAllowSpaces(): void |
|||
{ |
|||
$r1 = new Request('GET', '/'); |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$r1->withRequestTarget('/foo bar'); |
|||
} |
|||
|
|||
public function testRequestTargetDefaultsToSlash(): void |
|||
{ |
|||
$r1 = new Request('GET', ''); |
|||
self::assertSame('/', $r1->getRequestTarget()); |
|||
$r2 = new Request('GET', '*'); |
|||
self::assertSame('*', $r2->getRequestTarget()); |
|||
$r3 = new Request('GET', 'http://foo.com/bar baz/'); |
|||
self::assertSame('/bar%20baz/', $r3->getRequestTarget()); |
|||
} |
|||
|
|||
public function testBuildsRequestTarget(): void |
|||
{ |
|||
$r1 = new Request('GET', 'http://foo.com/baz?bar=bam'); |
|||
self::assertSame('/baz?bar=bam', $r1->getRequestTarget()); |
|||
} |
|||
|
|||
public function testBuildsRequestTargetWithFalseyQuery(): void |
|||
{ |
|||
$r1 = new Request('GET', 'http://foo.com/baz?0'); |
|||
self::assertSame('/baz?0', $r1->getRequestTarget()); |
|||
} |
|||
|
|||
public function testHostIsAddedFirst(): void |
|||
{ |
|||
$r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Foo' => 'Bar']); |
|||
self::assertSame([ |
|||
'Host' => ['foo.com'], |
|||
'Foo' => ['Bar'], |
|||
], $r->getHeaders()); |
|||
} |
|||
|
|||
public function testHeaderValueWithWhitespace(): void |
|||
{ |
|||
$r = new Request('GET', 'https://example.com/', [ |
|||
'User-Agent' => 'Linux f0f489981e90 5.10.104-linuxkit 1 SMP Wed Mar 9 19:05:23 UTC 2022 x86_64', |
|||
]); |
|||
self::assertSame([ |
|||
'Host' => ['example.com'], |
|||
'User-Agent' => ['Linux f0f489981e90 5.10.104-linuxkit 1 SMP Wed Mar 9 19:05:23 UTC 2022 x86_64'], |
|||
], $r->getHeaders()); |
|||
} |
|||
|
|||
public function testCanGetHeaderAsCsv(): void |
|||
{ |
|||
$r = new Request('GET', 'http://foo.com/baz?bar=bam', [ |
|||
'Foo' => ['a', 'b', 'c'], |
|||
]); |
|||
self::assertSame('a, b, c', $r->getHeaderLine('Foo')); |
|||
self::assertSame('', $r->getHeaderLine('Bar')); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideHeadersContainingNotAllowedChars |
|||
*/ |
|||
public function testContainsNotAllowedCharsOnHeaderField($header): void |
|||
{ |
|||
$this->expectExceptionMessage( |
|||
sprintf( |
|||
'"%s" is not valid header name', |
|||
$header |
|||
) |
|||
); |
|||
$r = new Request( |
|||
'GET', |
|||
'http://foo.com/baz?bar=bam', |
|||
[ |
|||
$header => 'value', |
|||
] |
|||
); |
|||
} |
|||
|
|||
public function provideHeadersContainingNotAllowedChars(): iterable |
|||
{ |
|||
return [[' key '], ['key '], [' key'], ['key/'], ['key('], ['key\\'], [' ']]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideHeadersContainsAllowedChar |
|||
*/ |
|||
public function testContainsAllowedCharsOnHeaderField($header): void |
|||
{ |
|||
$r = new Request( |
|||
'GET', |
|||
'http://foo.com/baz?bar=bam', |
|||
[ |
|||
$header => 'value', |
|||
] |
|||
); |
|||
self::assertArrayHasKey($header, $r->getHeaders()); |
|||
} |
|||
|
|||
public function provideHeadersContainsAllowedChar(): iterable |
|||
{ |
|||
return [ |
|||
['key'], |
|||
['key#'], |
|||
['key$'], |
|||
['key%'], |
|||
['key&'], |
|||
['key*'], |
|||
['key+'], |
|||
['key.'], |
|||
['key^'], |
|||
['key_'], |
|||
['key|'], |
|||
['key~'], |
|||
['key!'], |
|||
['key-'], |
|||
["key'"], |
|||
['key`'], |
|||
]; |
|||
} |
|||
|
|||
public function testHostIsNotOverwrittenWhenPreservingHost(): void |
|||
{ |
|||
$r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Host' => 'a.com']); |
|||
self::assertSame(['Host' => ['a.com']], $r->getHeaders()); |
|||
$r2 = $r->withUri(new Uri('http://www.foo.com/bar'), true); |
|||
self::assertSame('a.com', $r2->getHeaderLine('Host')); |
|||
} |
|||
|
|||
public function testWithUriSetsHostIfNotSet(): void |
|||
{ |
|||
$r = (new Request('GET', 'http://foo.com/baz?bar=bam'))->withoutHeader('Host'); |
|||
self::assertSame([], $r->getHeaders()); |
|||
$r2 = $r->withUri(new Uri('http://www.baz.com/bar'), true); |
|||
self::assertSame('www.baz.com', $r2->getHeaderLine('Host')); |
|||
} |
|||
|
|||
public function testOverridesHostWithUri(): void |
|||
{ |
|||
$r = new Request('GET', 'http://foo.com/baz?bar=bam'); |
|||
self::assertSame(['Host' => ['foo.com']], $r->getHeaders()); |
|||
$r2 = $r->withUri(new Uri('http://www.baz.com/bar')); |
|||
self::assertSame('www.baz.com', $r2->getHeaderLine('Host')); |
|||
} |
|||
|
|||
public function testAggregatesHeaders(): void |
|||
{ |
|||
$r = new Request('GET', '', [ |
|||
'ZOO' => 'zoobar', |
|||
'zoo' => ['foobar', 'zoobar'], |
|||
]); |
|||
self::assertSame(['ZOO' => ['zoobar', 'foobar', 'zoobar']], $r->getHeaders()); |
|||
self::assertSame('zoobar, foobar, zoobar', $r->getHeaderLine('zoo')); |
|||
} |
|||
|
|||
public function testAddsPortToHeader(): void |
|||
{ |
|||
$r = new Request('GET', 'http://foo.com:8124/bar'); |
|||
self::assertSame('foo.com:8124', $r->getHeaderLine('host')); |
|||
} |
|||
|
|||
public function testAddsPortToHeaderAndReplacePreviousPort(): void |
|||
{ |
|||
$r = new Request('GET', 'http://foo.com:8124/bar'); |
|||
$r = $r->withUri(new Uri('http://foo.com:8125/bar')); |
|||
self::assertSame('foo.com:8125', $r->getHeaderLine('host')); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideHeaderValuesContainingNotAllowedChars |
|||
*/ |
|||
public function testContainsNotAllowedCharsOnHeaderValue(string $value): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage(sprintf('"%s" is not valid header value', $value)); |
|||
|
|||
$r = new Request( |
|||
'GET', |
|||
'http://foo.com/baz?bar=bam', |
|||
[ |
|||
'testing' => $value, |
|||
] |
|||
); |
|||
} |
|||
|
|||
public function provideHeaderValuesContainingNotAllowedChars(): iterable |
|||
{ |
|||
// Explicit tests for newlines as the most common exploit vector. |
|||
$tests = [ |
|||
["new\nline"], |
|||
["new\r\nline"], |
|||
["new\rline"], |
|||
// Line folding is technically allowed, but deprecated. |
|||
// We don't support it. |
|||
["new\r\n line"], |
|||
["newline\n"], |
|||
["\nnewline"], |
|||
["newline\r\n"], |
|||
["\r\nnewline"], |
|||
]; |
|||
|
|||
for ($i = 0; $i <= 0xFF; ++$i) { |
|||
if (\chr($i) == "\t") { |
|||
continue; |
|||
} |
|||
if (\chr($i) == ' ') { |
|||
continue; |
|||
} |
|||
if ($i >= 0x21 && $i <= 0x7E) { |
|||
continue; |
|||
} |
|||
if ($i >= 0x80) { |
|||
continue; |
|||
} |
|||
|
|||
$tests[] = ['foo'.\chr($i).'bar']; |
|||
$tests[] = ['foo'.\chr($i)]; |
|||
} |
|||
|
|||
return $tests; |
|||
} |
|||
} |
@ -1,391 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\Response; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\StreamInterface; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\MessageTrait |
|||
* @covers \GuzzleHttp\Psr7\Response |
|||
*/ |
|||
class ResponseTest extends TestCase |
|||
{ |
|||
public function testDefaultConstructor(): void |
|||
{ |
|||
$r = new Response(); |
|||
self::assertSame(200, $r->getStatusCode()); |
|||
self::assertSame('1.1', $r->getProtocolVersion()); |
|||
self::assertSame('OK', $r->getReasonPhrase()); |
|||
self::assertSame([], $r->getHeaders()); |
|||
self::assertInstanceOf(StreamInterface::class, $r->getBody()); |
|||
self::assertSame('', (string) $r->getBody()); |
|||
} |
|||
|
|||
public function testCanConstructWithStatusCode(): void |
|||
{ |
|||
$r = new Response(404); |
|||
self::assertSame(404, $r->getStatusCode()); |
|||
self::assertSame('Not Found', $r->getReasonPhrase()); |
|||
} |
|||
|
|||
public function testConstructorDoesNotReadStreamBody(): void |
|||
{ |
|||
$streamIsRead = false; |
|||
$body = Psr7\FnStream::decorate(Psr7\Utils::streamFor(''), [ |
|||
'__toString' => function () use (&$streamIsRead) { |
|||
$streamIsRead = true; |
|||
|
|||
return ''; |
|||
}, |
|||
]); |
|||
|
|||
$r = new Response(200, [], $body); |
|||
self::assertFalse($streamIsRead); |
|||
self::assertSame($body, $r->getBody()); |
|||
} |
|||
|
|||
public function testStatusCanBeNumericString(): void |
|||
{ |
|||
$r = (new Response())->withStatus('201'); |
|||
|
|||
self::assertSame(201, $r->getStatusCode()); |
|||
self::assertSame('Created', $r->getReasonPhrase()); |
|||
} |
|||
|
|||
public function testCanConstructWithHeaders(): void |
|||
{ |
|||
$r = new Response(200, ['Foo' => 'Bar']); |
|||
self::assertSame(['Foo' => ['Bar']], $r->getHeaders()); |
|||
self::assertSame('Bar', $r->getHeaderLine('Foo')); |
|||
self::assertSame(['Bar'], $r->getHeader('Foo')); |
|||
} |
|||
|
|||
public function testCanConstructWithHeadersAsArray(): void |
|||
{ |
|||
$r = new Response(200, [ |
|||
'Foo' => ['baz', 'bar'], |
|||
]); |
|||
self::assertSame(['Foo' => ['baz', 'bar']], $r->getHeaders()); |
|||
self::assertSame('baz, bar', $r->getHeaderLine('Foo')); |
|||
self::assertSame(['baz', 'bar'], $r->getHeader('Foo')); |
|||
} |
|||
|
|||
public function testCanConstructWithBody(): void |
|||
{ |
|||
$r = new Response(200, [], 'baz'); |
|||
self::assertInstanceOf(StreamInterface::class, $r->getBody()); |
|||
self::assertSame('baz', (string) $r->getBody()); |
|||
} |
|||
|
|||
public function testNullBody(): void |
|||
{ |
|||
$r = new Response(200, [], null); |
|||
self::assertInstanceOf(StreamInterface::class, $r->getBody()); |
|||
self::assertSame('', (string) $r->getBody()); |
|||
} |
|||
|
|||
public function testFalseyBody(): void |
|||
{ |
|||
$r = new Response(200, [], '0'); |
|||
self::assertInstanceOf(StreamInterface::class, $r->getBody()); |
|||
self::assertSame('0', (string) $r->getBody()); |
|||
} |
|||
|
|||
public function testCanConstructWithReason(): void |
|||
{ |
|||
$r = new Response(200, [], null, '1.1', 'bar'); |
|||
self::assertSame('bar', $r->getReasonPhrase()); |
|||
|
|||
$r = new Response(200, [], null, '1.1', '0'); |
|||
self::assertSame('0', $r->getReasonPhrase(), 'Falsey reason works'); |
|||
} |
|||
|
|||
public function testCanConstructWithProtocolVersion(): void |
|||
{ |
|||
$r = new Response(200, [], null, '1000'); |
|||
self::assertSame('1000', $r->getProtocolVersion()); |
|||
} |
|||
|
|||
public function testWithStatusCodeAndNoReason(): void |
|||
{ |
|||
$r = (new Response())->withStatus(201); |
|||
self::assertSame(201, $r->getStatusCode()); |
|||
self::assertSame('Created', $r->getReasonPhrase()); |
|||
} |
|||
|
|||
public function testWithStatusCodeAndReason(): void |
|||
{ |
|||
$r = (new Response())->withStatus(201, 'Foo'); |
|||
self::assertSame(201, $r->getStatusCode()); |
|||
self::assertSame('Foo', $r->getReasonPhrase()); |
|||
|
|||
$r = (new Response())->withStatus(201, '0'); |
|||
self::assertSame(201, $r->getStatusCode()); |
|||
self::assertSame('0', $r->getReasonPhrase(), 'Falsey reason works'); |
|||
} |
|||
|
|||
public function testWithProtocolVersion(): void |
|||
{ |
|||
$r = (new Response())->withProtocolVersion('1000'); |
|||
self::assertSame('1000', $r->getProtocolVersion()); |
|||
} |
|||
|
|||
public function testSameInstanceWhenSameProtocol(): void |
|||
{ |
|||
$r = new Response(); |
|||
self::assertSame($r, $r->withProtocolVersion('1.1')); |
|||
} |
|||
|
|||
public function testWithBody(): void |
|||
{ |
|||
$b = Psr7\Utils::streamFor('0'); |
|||
$r = (new Response())->withBody($b); |
|||
self::assertInstanceOf(StreamInterface::class, $r->getBody()); |
|||
self::assertSame('0', (string) $r->getBody()); |
|||
} |
|||
|
|||
public function testSameInstanceWhenSameBody(): void |
|||
{ |
|||
$r = new Response(); |
|||
$b = $r->getBody(); |
|||
self::assertSame($r, $r->withBody($b)); |
|||
} |
|||
|
|||
public function testWithHeader(): void |
|||
{ |
|||
$r = new Response(200, ['Foo' => 'Bar']); |
|||
$r2 = $r->withHeader('baZ', 'Bam'); |
|||
self::assertSame(['Foo' => ['Bar']], $r->getHeaders()); |
|||
self::assertSame(['Foo' => ['Bar'], 'baZ' => ['Bam']], $r2->getHeaders()); |
|||
self::assertSame('Bam', $r2->getHeaderLine('baz')); |
|||
self::assertSame(['Bam'], $r2->getHeader('baz')); |
|||
} |
|||
|
|||
public function testNumericHeaderValue(): void |
|||
{ |
|||
$r = (new Response())->withHeader('Api-Version', 1); |
|||
self::assertSame(['Api-Version' => ['1']], $r->getHeaders()); |
|||
} |
|||
|
|||
public function testWithHeaderAsArray(): void |
|||
{ |
|||
$r = new Response(200, ['Foo' => 'Bar']); |
|||
$r2 = $r->withHeader('baZ', ['Bam', 'Bar']); |
|||
self::assertSame(['Foo' => ['Bar']], $r->getHeaders()); |
|||
self::assertSame(['Foo' => ['Bar'], 'baZ' => ['Bam', 'Bar']], $r2->getHeaders()); |
|||
self::assertSame('Bam, Bar', $r2->getHeaderLine('baz')); |
|||
self::assertSame(['Bam', 'Bar'], $r2->getHeader('baz')); |
|||
} |
|||
|
|||
public function testWithHeaderReplacesDifferentCase(): void |
|||
{ |
|||
$r = new Response(200, ['Foo' => 'Bar']); |
|||
$r2 = $r->withHeader('foO', 'Bam'); |
|||
self::assertSame(['Foo' => ['Bar']], $r->getHeaders()); |
|||
self::assertSame(['foO' => ['Bam']], $r2->getHeaders()); |
|||
self::assertSame('Bam', $r2->getHeaderLine('foo')); |
|||
self::assertSame(['Bam'], $r2->getHeader('foo')); |
|||
} |
|||
|
|||
public function testWithAddedHeader(): void |
|||
{ |
|||
$r = new Response(200, ['Foo' => 'Bar']); |
|||
$r2 = $r->withAddedHeader('foO', 'Baz'); |
|||
self::assertSame(['Foo' => ['Bar']], $r->getHeaders()); |
|||
self::assertSame(['Foo' => ['Bar', 'Baz']], $r2->getHeaders()); |
|||
self::assertSame('Bar, Baz', $r2->getHeaderLine('foo')); |
|||
self::assertSame(['Bar', 'Baz'], $r2->getHeader('foo')); |
|||
} |
|||
|
|||
public function testWithAddedHeaderAsArray(): void |
|||
{ |
|||
$r = new Response(200, ['Foo' => 'Bar']); |
|||
$r2 = $r->withAddedHeader('foO', ['Baz', 'Bam']); |
|||
self::assertSame(['Foo' => ['Bar']], $r->getHeaders()); |
|||
self::assertSame(['Foo' => ['Bar', 'Baz', 'Bam']], $r2->getHeaders()); |
|||
self::assertSame('Bar, Baz, Bam', $r2->getHeaderLine('foo')); |
|||
self::assertSame(['Bar', 'Baz', 'Bam'], $r2->getHeader('foo')); |
|||
} |
|||
|
|||
public function testWithAddedHeaderThatDoesNotExist(): void |
|||
{ |
|||
$r = new Response(200, ['Foo' => 'Bar']); |
|||
$r2 = $r->withAddedHeader('nEw', 'Baz'); |
|||
self::assertSame(['Foo' => ['Bar']], $r->getHeaders()); |
|||
self::assertSame(['Foo' => ['Bar'], 'nEw' => ['Baz']], $r2->getHeaders()); |
|||
self::assertSame('Baz', $r2->getHeaderLine('new')); |
|||
self::assertSame(['Baz'], $r2->getHeader('new')); |
|||
} |
|||
|
|||
public function testWithoutHeaderThatExists(): void |
|||
{ |
|||
$r = new Response(200, ['Foo' => 'Bar', 'Baz' => 'Bam']); |
|||
$r2 = $r->withoutHeader('foO'); |
|||
self::assertTrue($r->hasHeader('foo')); |
|||
self::assertSame(['Foo' => ['Bar'], 'Baz' => ['Bam']], $r->getHeaders()); |
|||
self::assertFalse($r2->hasHeader('foo')); |
|||
self::assertSame(['Baz' => ['Bam']], $r2->getHeaders()); |
|||
} |
|||
|
|||
public function testWithoutHeaderThatDoesNotExist(): void |
|||
{ |
|||
$r = new Response(200, ['Baz' => 'Bam']); |
|||
$r2 = $r->withoutHeader('foO'); |
|||
self::assertSame($r, $r2); |
|||
self::assertFalse($r2->hasHeader('foo')); |
|||
self::assertSame(['Baz' => ['Bam']], $r2->getHeaders()); |
|||
} |
|||
|
|||
public function testSameInstanceWhenRemovingMissingHeader(): void |
|||
{ |
|||
$r = new Response(); |
|||
self::assertSame($r, $r->withoutHeader('foo')); |
|||
} |
|||
|
|||
public function testPassNumericHeaderNameInConstructor(): void |
|||
{ |
|||
$r = new Response(200, ['Location' => 'foo', '123' => 'bar']); |
|||
self::assertSame('bar', $r->getHeaderLine('123')); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider invalidHeaderProvider |
|||
*/ |
|||
public function testConstructResponseInvalidHeader($header, $headerValue, $expectedMessage): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage($expectedMessage); |
|||
new Response(200, [$header => $headerValue]); |
|||
} |
|||
|
|||
public function invalidHeaderProvider(): iterable |
|||
{ |
|||
return [ |
|||
['foo', [], 'Header value can not be an empty array.'], |
|||
['', '', '"" is not valid header name'], |
|||
['foo', new \stdClass(), 'Header value must be scalar or null but stdClass provided.'], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider invalidWithHeaderProvider |
|||
*/ |
|||
public function testWithInvalidHeader($header, $headerValue, $expectedMessage): void |
|||
{ |
|||
$r = new Response(); |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage($expectedMessage); |
|||
$r->withHeader($header, $headerValue); |
|||
} |
|||
|
|||
public function invalidWithHeaderProvider(): iterable |
|||
{ |
|||
yield from $this->invalidHeaderProvider(); |
|||
yield [[], 'foo', 'Header name must be a string but array provided.']; |
|||
yield [false, 'foo', 'Header name must be a string but boolean provided.']; |
|||
yield [new \stdClass(), 'foo', 'Header name must be a string but stdClass provided.']; |
|||
yield ['', 'foo', '"" is not valid header name.']; |
|||
yield ["Content-Type\r\n\r\n", 'foo', "\"Content-Type\r\n\r\n\" is not valid header name."]; |
|||
yield ["Content-Type\r\n", 'foo', "\"Content-Type\r\n\" is not valid header name."]; |
|||
yield ["Content-Type\n", 'foo', "\"Content-Type\n\" is not valid header name."]; |
|||
yield ["\r\nContent-Type", 'foo', "\"\r\nContent-Type\" is not valid header name."]; |
|||
yield ["\nContent-Type", 'foo', "\"\nContent-Type\" is not valid header name."]; |
|||
yield ["\n", 'foo', "\"\n\" is not valid header name."]; |
|||
yield ["\r\n", 'foo', "\"\r\n\" is not valid header name."]; |
|||
yield ["\t", 'foo', "\"\t\" is not valid header name."]; |
|||
} |
|||
|
|||
public function testHeaderValuesAreTrimmed(): void |
|||
{ |
|||
$r1 = new Response(200, ['OWS' => " \t \tFoo\t \t "]); |
|||
$r2 = (new Response())->withHeader('OWS', " \t \tFoo\t \t "); |
|||
$r3 = (new Response())->withAddedHeader('OWS', " \t \tFoo\t \t "); |
|||
|
|||
foreach ([$r1, $r2, $r3] as $r) { |
|||
self::assertSame(['OWS' => ['Foo']], $r->getHeaders()); |
|||
self::assertSame('Foo', $r->getHeaderLine('OWS')); |
|||
self::assertSame(['Foo'], $r->getHeader('OWS')); |
|||
} |
|||
} |
|||
|
|||
public function testWithAddedHeaderArrayValueAndKeys(): void |
|||
{ |
|||
$message = (new Response())->withAddedHeader('list', ['foo' => 'one']); |
|||
$message = $message->withAddedHeader('list', ['foo' => 'two', 'bar' => 'three']); |
|||
|
|||
$headerLine = $message->getHeaderLine('list'); |
|||
self::assertSame('one, two, three', $headerLine); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider nonIntegerStatusCodeProvider |
|||
* |
|||
* @param mixed $invalidValues |
|||
*/ |
|||
public function testConstructResponseWithNonIntegerStatusCode($invalidValues): void |
|||
{ |
|||
$this->expectException(\TypeError::class); |
|||
new Response($invalidValues); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider nonIntegerStatusCodeProvider |
|||
* |
|||
* @param mixed $invalidValues |
|||
*/ |
|||
public function testResponseChangeStatusCodeWithNonInteger($invalidValues): void |
|||
{ |
|||
$response = new Response(); |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Status code must be an integer value.'); |
|||
$response->withStatus($invalidValues); |
|||
} |
|||
|
|||
public function nonIntegerStatusCodeProvider(): iterable |
|||
{ |
|||
return [ |
|||
['whatever'], |
|||
['1.01'], |
|||
[1.01], |
|||
[new \stdClass()], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider invalidStatusCodeRangeProvider |
|||
* |
|||
* @param mixed $invalidValues |
|||
*/ |
|||
public function testConstructResponseWithInvalidRangeStatusCode($invalidValues): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Status code must be an integer value between 1xx and 5xx.'); |
|||
new Response($invalidValues); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider invalidStatusCodeRangeProvider |
|||
* |
|||
* @param mixed $invalidValues |
|||
*/ |
|||
public function testResponseChangeStatusCodeWithWithInvalidRange($invalidValues): void |
|||
{ |
|||
$response = new Response(); |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Status code must be an integer value between 1xx and 5xx.'); |
|||
$response->withStatus($invalidValues); |
|||
} |
|||
|
|||
public function invalidStatusCodeRangeProvider(): iterable |
|||
{ |
|||
return [ |
|||
[600], |
|||
[99], |
|||
]; |
|||
} |
|||
} |
@ -1,566 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\ServerRequest; |
|||
use GuzzleHttp\Psr7\UploadedFile; |
|||
use GuzzleHttp\Psr7\Uri; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\ServerRequest |
|||
*/ |
|||
class ServerRequestTest extends TestCase |
|||
{ |
|||
public function dataNormalizeFiles(): iterable |
|||
{ |
|||
return [ |
|||
'Single file' => [ |
|||
[ |
|||
'file' => [ |
|||
'name' => 'MyFile.txt', |
|||
'type' => 'text/plain', |
|||
'tmp_name' => '/tmp/php/php1h4j1o', |
|||
'error' => '0', |
|||
'size' => '123', |
|||
], |
|||
], |
|||
[ |
|||
'file' => new UploadedFile( |
|||
'/tmp/php/php1h4j1o', |
|||
123, |
|||
UPLOAD_ERR_OK, |
|||
'MyFile.txt', |
|||
'text/plain' |
|||
), |
|||
], |
|||
], |
|||
'Empty file' => [ |
|||
[ |
|||
'image_file' => [ |
|||
'name' => '', |
|||
'type' => '', |
|||
'tmp_name' => '', |
|||
'error' => '4', |
|||
'size' => '0', |
|||
], |
|||
], |
|||
[ |
|||
'image_file' => new UploadedFile( |
|||
'', |
|||
0, |
|||
UPLOAD_ERR_NO_FILE, |
|||
'', |
|||
'' |
|||
), |
|||
], |
|||
], |
|||
'Already Converted' => [ |
|||
[ |
|||
'file' => new UploadedFile( |
|||
'/tmp/php/php1h4j1o', |
|||
123, |
|||
UPLOAD_ERR_OK, |
|||
'MyFile.txt', |
|||
'text/plain' |
|||
), |
|||
], |
|||
[ |
|||
'file' => new UploadedFile( |
|||
'/tmp/php/php1h4j1o', |
|||
123, |
|||
UPLOAD_ERR_OK, |
|||
'MyFile.txt', |
|||
'text/plain' |
|||
), |
|||
], |
|||
], |
|||
'Already Converted array' => [ |
|||
[ |
|||
'file' => [ |
|||
new UploadedFile( |
|||
'/tmp/php/php1h4j1o', |
|||
123, |
|||
UPLOAD_ERR_OK, |
|||
'MyFile.txt', |
|||
'text/plain' |
|||
), |
|||
new UploadedFile( |
|||
'', |
|||
0, |
|||
UPLOAD_ERR_NO_FILE, |
|||
'', |
|||
'' |
|||
), |
|||
], |
|||
], |
|||
[ |
|||
'file' => [ |
|||
new UploadedFile( |
|||
'/tmp/php/php1h4j1o', |
|||
123, |
|||
UPLOAD_ERR_OK, |
|||
'MyFile.txt', |
|||
'text/plain' |
|||
), |
|||
new UploadedFile( |
|||
'', |
|||
0, |
|||
UPLOAD_ERR_NO_FILE, |
|||
'', |
|||
'' |
|||
), |
|||
], |
|||
], |
|||
], |
|||
'Multiple files' => [ |
|||
[ |
|||
'text_file' => [ |
|||
'name' => 'MyFile.txt', |
|||
'type' => 'text/plain', |
|||
'tmp_name' => '/tmp/php/php1h4j1o', |
|||
'error' => '0', |
|||
'size' => '123', |
|||
], |
|||
'image_file' => [ |
|||
'name' => '', |
|||
'type' => '', |
|||
'tmp_name' => '', |
|||
'error' => '4', |
|||
'size' => '0', |
|||
], |
|||
], |
|||
[ |
|||
'text_file' => new UploadedFile( |
|||
'/tmp/php/php1h4j1o', |
|||
123, |
|||
UPLOAD_ERR_OK, |
|||
'MyFile.txt', |
|||
'text/plain' |
|||
), |
|||
'image_file' => new UploadedFile( |
|||
'', |
|||
0, |
|||
UPLOAD_ERR_NO_FILE, |
|||
'', |
|||
'' |
|||
), |
|||
], |
|||
], |
|||
'Nested files' => [ |
|||
[ |
|||
'file' => [ |
|||
'name' => [ |
|||
0 => 'MyFile.txt', |
|||
1 => 'Image.png', |
|||
], |
|||
'type' => [ |
|||
0 => 'text/plain', |
|||
1 => 'image/png', |
|||
], |
|||
'tmp_name' => [ |
|||
0 => '/tmp/php/hp9hskjhf', |
|||
1 => '/tmp/php/php1h4j1o', |
|||
2 => '/tmp/php/w0ensl4ar', |
|||
], |
|||
'error' => [ |
|||
0 => '0', |
|||
1 => '0', |
|||
], |
|||
'size' => [ |
|||
0 => '123', |
|||
1 => '7349', |
|||
], |
|||
], |
|||
'minimum_data' => [ |
|||
'tmp_name' => [ |
|||
0 => '/tmp/php/hp9hskjhf', |
|||
], |
|||
], |
|||
'nested' => [ |
|||
'name' => [ |
|||
'other' => 'Flag.txt', |
|||
'test' => [ |
|||
0 => 'Stuff.txt', |
|||
1 => '', |
|||
], |
|||
], |
|||
'type' => [ |
|||
'other' => 'text/plain', |
|||
'test' => [ |
|||
0 => 'text/plain', |
|||
1 => '', |
|||
], |
|||
], |
|||
'tmp_name' => [ |
|||
'other' => '/tmp/php/hp9hskjhf', |
|||
'test' => [ |
|||
0 => '/tmp/php/asifu2gp3', |
|||
1 => '', |
|||
], |
|||
], |
|||
'error' => [ |
|||
'other' => '0', |
|||
'test' => [ |
|||
0 => '0', |
|||
1 => '4', |
|||
], |
|||
], |
|||
'size' => [ |
|||
'other' => '421', |
|||
'test' => [ |
|||
0 => '32', |
|||
1 => '0', |
|||
], |
|||
], |
|||
], |
|||
], |
|||
[ |
|||
'file' => [ |
|||
0 => new UploadedFile( |
|||
'/tmp/php/hp9hskjhf', |
|||
123, |
|||
UPLOAD_ERR_OK, |
|||
'MyFile.txt', |
|||
'text/plain' |
|||
), |
|||
1 => new UploadedFile( |
|||
'/tmp/php/php1h4j1o', |
|||
7349, |
|||
UPLOAD_ERR_OK, |
|||
'Image.png', |
|||
'image/png' |
|||
), |
|||
2 => new UploadedFile( |
|||
'/tmp/php/w0ensl4ar', |
|||
null, |
|||
UPLOAD_ERR_OK |
|||
), |
|||
], |
|||
'minimum_data' => [ |
|||
0 => new UploadedFile( |
|||
'/tmp/php/hp9hskjhf', |
|||
0, |
|||
UPLOAD_ERR_OK |
|||
), |
|||
], |
|||
'nested' => [ |
|||
'other' => new UploadedFile( |
|||
'/tmp/php/hp9hskjhf', |
|||
421, |
|||
UPLOAD_ERR_OK, |
|||
'Flag.txt', |
|||
'text/plain' |
|||
), |
|||
'test' => [ |
|||
0 => new UploadedFile( |
|||
'/tmp/php/asifu2gp3', |
|||
32, |
|||
UPLOAD_ERR_OK, |
|||
'Stuff.txt', |
|||
'text/plain' |
|||
), |
|||
1 => new UploadedFile( |
|||
'', |
|||
0, |
|||
UPLOAD_ERR_NO_FILE, |
|||
'', |
|||
'' |
|||
), |
|||
], |
|||
], |
|||
], |
|||
], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider dataNormalizeFiles |
|||
*/ |
|||
public function testNormalizeFiles($files, $expected): void |
|||
{ |
|||
$result = ServerRequest::normalizeFiles($files); |
|||
|
|||
self::assertEquals($expected, $result); |
|||
} |
|||
|
|||
public function testNormalizeFilesRaisesException(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Invalid value in files specification'); |
|||
ServerRequest::normalizeFiles(['test' => 'something']); |
|||
} |
|||
|
|||
public function dataGetUriFromGlobals(): iterable |
|||
{ |
|||
$server = [ |
|||
'REQUEST_URI' => '/blog/article.php?id=10&user=foo', |
|||
'SERVER_PORT' => '443', |
|||
'SERVER_ADDR' => '217.112.82.20', |
|||
'SERVER_NAME' => 'www.example.org', |
|||
'SERVER_PROTOCOL' => 'HTTP/1.1', |
|||
'REQUEST_METHOD' => 'POST', |
|||
'QUERY_STRING' => 'id=10&user=foo', |
|||
'DOCUMENT_ROOT' => '/path/to/your/server/root/', |
|||
'HTTP_HOST' => 'www.example.org', |
|||
'HTTPS' => 'on', |
|||
'REMOTE_ADDR' => '193.60.168.69', |
|||
'REMOTE_PORT' => '5390', |
|||
'SCRIPT_NAME' => '/blog/article.php', |
|||
'SCRIPT_FILENAME' => '/path/to/your/server/root/blog/article.php', |
|||
'PHP_SELF' => '/blog/article.php', |
|||
]; |
|||
|
|||
return [ |
|||
'HTTPS request' => [ |
|||
'https://www.example.org/blog/article.php?id=10&user=foo', |
|||
$server, |
|||
], |
|||
'HTTPS request with different on value' => [ |
|||
'https://www.example.org/blog/article.php?id=10&user=foo', |
|||
array_merge($server, ['HTTPS' => '1']), |
|||
], |
|||
'HTTP request' => [ |
|||
'http://www.example.org/blog/article.php?id=10&user=foo', |
|||
array_merge($server, ['HTTPS' => 'off', 'SERVER_PORT' => '80']), |
|||
], |
|||
'HTTP_HOST missing -> fallback to SERVER_NAME' => [ |
|||
'https://www.example.org/blog/article.php?id=10&user=foo', |
|||
array_merge($server, ['HTTP_HOST' => null]), |
|||
], |
|||
'HTTP_HOST and SERVER_NAME missing -> fallback to SERVER_ADDR' => [ |
|||
'https://217.112.82.20/blog/article.php?id=10&user=foo', |
|||
array_merge($server, ['HTTP_HOST' => null, 'SERVER_NAME' => null]), |
|||
], |
|||
'Query string with ?' => [ |
|||
'https://www.example.org/path?continue=https://example.com/path?param=1', |
|||
array_merge($server, ['REQUEST_URI' => '/path?continue=https://example.com/path?param=1', 'QUERY_STRING' => '']), |
|||
], |
|||
'No query String' => [ |
|||
'https://www.example.org/blog/article.php', |
|||
array_merge($server, ['REQUEST_URI' => '/blog/article.php', 'QUERY_STRING' => '']), |
|||
], |
|||
'Host header with port' => [ |
|||
'https://www.example.org:8324/blog/article.php?id=10&user=foo', |
|||
array_merge($server, ['HTTP_HOST' => 'www.example.org:8324']), |
|||
], |
|||
'IPv6 local loopback address' => [ |
|||
'https://[::1]:8000/blog/article.php?id=10&user=foo', |
|||
array_merge($server, ['HTTP_HOST' => '[::1]:8000']), |
|||
], |
|||
'Invalid host' => [ |
|||
'https://localhost/blog/article.php?id=10&user=foo', |
|||
array_merge($server, ['HTTP_HOST' => 'a:b']), |
|||
], |
|||
'Different port with SERVER_PORT' => [ |
|||
'https://www.example.org:8324/blog/article.php?id=10&user=foo', |
|||
array_merge($server, ['SERVER_PORT' => '8324']), |
|||
], |
|||
'REQUEST_URI missing query string' => [ |
|||
'https://www.example.org/blog/article.php?id=10&user=foo', |
|||
array_merge($server, ['REQUEST_URI' => '/blog/article.php']), |
|||
], |
|||
'Empty server variable' => [ |
|||
'http://localhost', |
|||
[], |
|||
], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider dataGetUriFromGlobals |
|||
*/ |
|||
public function testGetUriFromGlobals($expected, $serverParams): void |
|||
{ |
|||
$_SERVER = $serverParams; |
|||
|
|||
self::assertEquals(new Uri($expected), ServerRequest::getUriFromGlobals()); |
|||
} |
|||
|
|||
public function testFromGlobals(): void |
|||
{ |
|||
$_SERVER = [ |
|||
'REQUEST_URI' => '/blog/article.php?id=10&user=foo', |
|||
'SERVER_PORT' => '443', |
|||
'SERVER_ADDR' => '217.112.82.20', |
|||
'SERVER_NAME' => 'www.example.org', |
|||
'SERVER_PROTOCOL' => 'HTTP/1.1', |
|||
'REQUEST_METHOD' => 'POST', |
|||
'QUERY_STRING' => 'id=10&user=foo', |
|||
'DOCUMENT_ROOT' => '/path/to/your/server/root/', |
|||
'CONTENT_TYPE' => 'text/plain', |
|||
'HTTP_HOST' => 'www.example.org', |
|||
'HTTP_ACCEPT' => 'text/html', |
|||
'HTTP_REFERRER' => 'https://example.com', |
|||
'HTTP_USER_AGENT' => 'My User Agent', |
|||
'HTTPS' => 'on', |
|||
'REMOTE_ADDR' => '193.60.168.69', |
|||
'REMOTE_PORT' => '5390', |
|||
'SCRIPT_NAME' => '/blog/article.php', |
|||
'SCRIPT_FILENAME' => '/path/to/your/server/root/blog/article.php', |
|||
'PHP_SELF' => '/blog/article.php', |
|||
]; |
|||
|
|||
$_COOKIE = [ |
|||
'logged-in' => 'yes!', |
|||
]; |
|||
|
|||
$_POST = [ |
|||
'name' => 'Pesho', |
|||
'email' => 'pesho@example.com', |
|||
]; |
|||
|
|||
$_GET = [ |
|||
'id' => 10, |
|||
'user' => 'foo', |
|||
]; |
|||
|
|||
$_FILES = [ |
|||
'file' => [ |
|||
'name' => 'MyFile.txt', |
|||
'type' => 'text/plain', |
|||
'tmp_name' => '/tmp/php/php1h4j1o', |
|||
'error' => UPLOAD_ERR_OK, |
|||
'size' => 123, |
|||
], |
|||
]; |
|||
|
|||
$server = ServerRequest::fromGlobals(); |
|||
|
|||
self::assertSame('POST', $server->getMethod()); |
|||
self::assertEquals([ |
|||
'Host' => ['www.example.org'], |
|||
'Content-Type' => ['text/plain'], |
|||
'Accept' => ['text/html'], |
|||
'Referrer' => ['https://example.com'], |
|||
'User-Agent' => ['My User Agent'], |
|||
], $server->getHeaders()); |
|||
self::assertSame('', (string) $server->getBody()); |
|||
self::assertSame('1.1', $server->getProtocolVersion()); |
|||
self::assertSame($_COOKIE, $server->getCookieParams()); |
|||
self::assertSame($_POST, $server->getParsedBody()); |
|||
self::assertSame($_GET, $server->getQueryParams()); |
|||
|
|||
self::assertEquals( |
|||
new Uri('https://www.example.org/blog/article.php?id=10&user=foo'), |
|||
$server->getUri() |
|||
); |
|||
|
|||
$expectedFiles = [ |
|||
'file' => new UploadedFile( |
|||
'/tmp/php/php1h4j1o', |
|||
123, |
|||
UPLOAD_ERR_OK, |
|||
'MyFile.txt', |
|||
'text/plain' |
|||
), |
|||
]; |
|||
|
|||
self::assertEquals($expectedFiles, $server->getUploadedFiles()); |
|||
} |
|||
|
|||
public function testUploadedFiles(): void |
|||
{ |
|||
$request1 = new ServerRequest('GET', '/'); |
|||
|
|||
$files = [ |
|||
'file' => new UploadedFile('test', 123, UPLOAD_ERR_OK), |
|||
]; |
|||
|
|||
$request2 = $request1->withUploadedFiles($files); |
|||
|
|||
self::assertNotSame($request2, $request1); |
|||
self::assertSame([], $request1->getUploadedFiles()); |
|||
self::assertSame($files, $request2->getUploadedFiles()); |
|||
} |
|||
|
|||
public function testServerParams(): void |
|||
{ |
|||
$params = ['name' => 'value']; |
|||
|
|||
$request = new ServerRequest('GET', '/', [], null, '1.1', $params); |
|||
self::assertSame($params, $request->getServerParams()); |
|||
} |
|||
|
|||
public function testCookieParams(): void |
|||
{ |
|||
$request1 = new ServerRequest('GET', '/'); |
|||
|
|||
$params = ['name' => 'value']; |
|||
|
|||
$request2 = $request1->withCookieParams($params); |
|||
|
|||
self::assertNotSame($request2, $request1); |
|||
self::assertEmpty($request1->getCookieParams()); |
|||
self::assertSame($params, $request2->getCookieParams()); |
|||
} |
|||
|
|||
public function testQueryParams(): void |
|||
{ |
|||
$request1 = new ServerRequest('GET', '/'); |
|||
|
|||
$params = ['name' => 'value']; |
|||
|
|||
$request2 = $request1->withQueryParams($params); |
|||
|
|||
self::assertNotSame($request2, $request1); |
|||
self::assertEmpty($request1->getQueryParams()); |
|||
self::assertSame($params, $request2->getQueryParams()); |
|||
} |
|||
|
|||
public function testParsedBody(): void |
|||
{ |
|||
$request1 = new ServerRequest('GET', '/'); |
|||
|
|||
$params = ['name' => 'value']; |
|||
|
|||
$request2 = $request1->withParsedBody($params); |
|||
|
|||
self::assertNotSame($request2, $request1); |
|||
self::assertEmpty($request1->getParsedBody()); |
|||
self::assertSame($params, $request2->getParsedBody()); |
|||
} |
|||
|
|||
public function testAttributes(): void |
|||
{ |
|||
$request1 = new ServerRequest('GET', '/'); |
|||
|
|||
$request2 = $request1->withAttribute('name', 'value'); |
|||
$request3 = $request2->withAttribute('other', 'otherValue'); |
|||
$request4 = $request3->withoutAttribute('other'); |
|||
$request5 = $request3->withoutAttribute('unknown'); |
|||
|
|||
self::assertNotSame($request2, $request1); |
|||
self::assertNotSame($request3, $request2); |
|||
self::assertNotSame($request4, $request3); |
|||
self::assertSame($request5, $request3); |
|||
|
|||
self::assertSame([], $request1->getAttributes()); |
|||
self::assertNull($request1->getAttribute('name')); |
|||
self::assertSame( |
|||
'something', |
|||
$request1->getAttribute('name', 'something'), |
|||
'Should return the default value' |
|||
); |
|||
|
|||
self::assertSame('value', $request2->getAttribute('name')); |
|||
self::assertSame(['name' => 'value'], $request2->getAttributes()); |
|||
self::assertSame(['name' => 'value', 'other' => 'otherValue'], $request3->getAttributes()); |
|||
self::assertSame(['name' => 'value'], $request4->getAttributes()); |
|||
} |
|||
|
|||
public function testNullAttribute(): void |
|||
{ |
|||
$request = (new ServerRequest('GET', '/'))->withAttribute('name', null); |
|||
|
|||
self::assertSame(['name' => null], $request->getAttributes()); |
|||
self::assertNull($request->getAttribute('name', 'different-default')); |
|||
|
|||
$requestWithoutAttribute = $request->withoutAttribute('name'); |
|||
|
|||
self::assertSame([], $requestWithoutAttribute->getAttributes()); |
|||
self::assertSame('different-default', $requestWithoutAttribute->getAttribute('name', 'different-default')); |
|||
} |
|||
} |
@ -1,148 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\StreamDecoratorTrait; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\StreamInterface; |
|||
|
|||
class Str implements StreamInterface |
|||
{ |
|||
use StreamDecoratorTrait; |
|||
|
|||
/** @var StreamInterface */ |
|||
private $stream; |
|||
} |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\StreamDecoratorTrait |
|||
*/ |
|||
class StreamDecoratorTraitTest extends TestCase |
|||
{ |
|||
/** @var StreamInterface */ |
|||
private $a; |
|||
/** @var StreamInterface */ |
|||
private $b; |
|||
/** @var resource */ |
|||
private $c; |
|||
|
|||
protected function setUp(): void |
|||
{ |
|||
$this->c = fopen('php://temp', 'r+'); |
|||
fwrite($this->c, 'foo'); |
|||
fseek($this->c, 0); |
|||
$this->a = Psr7\Utils::streamFor($this->c); |
|||
$this->b = new Str($this->a); |
|||
} |
|||
|
|||
/** |
|||
* @requires PHP < 7.4 |
|||
*/ |
|||
public function testCatchesExceptionsWhenCastingToString(): void |
|||
{ |
|||
$s = $this->createMock(Str::class); |
|||
$s->expects(self::once()) |
|||
->method('read') |
|||
->willThrowException(new \RuntimeException('foo')); |
|||
$msg = ''; |
|||
set_error_handler(function (int $errNo, string $str) use (&$msg): void { |
|||
$msg = $str; |
|||
}); |
|||
echo new Str($s); |
|||
restore_error_handler(); |
|||
self::assertStringContainsString('foo', $msg); |
|||
} |
|||
|
|||
public function testToString(): void |
|||
{ |
|||
self::assertSame('foo', (string) $this->b); |
|||
} |
|||
|
|||
public function testHasSize(): void |
|||
{ |
|||
self::assertSame(3, $this->b->getSize()); |
|||
} |
|||
|
|||
public function testReads(): void |
|||
{ |
|||
self::assertSame('foo', $this->b->read(10)); |
|||
} |
|||
|
|||
public function testCheckMethods(): void |
|||
{ |
|||
self::assertSame($this->a->isReadable(), $this->b->isReadable()); |
|||
self::assertSame($this->a->isWritable(), $this->b->isWritable()); |
|||
self::assertSame($this->a->isSeekable(), $this->b->isSeekable()); |
|||
} |
|||
|
|||
public function testSeeksAndTells(): void |
|||
{ |
|||
$this->b->seek(1); |
|||
self::assertSame(1, $this->a->tell()); |
|||
self::assertSame(1, $this->b->tell()); |
|||
$this->b->seek(0); |
|||
self::assertSame(0, $this->a->tell()); |
|||
self::assertSame(0, $this->b->tell()); |
|||
$this->b->seek(0, SEEK_END); |
|||
self::assertSame(3, $this->a->tell()); |
|||
self::assertSame(3, $this->b->tell()); |
|||
} |
|||
|
|||
public function testGetsContents(): void |
|||
{ |
|||
self::assertSame('foo', $this->b->getContents()); |
|||
self::assertSame('', $this->b->getContents()); |
|||
$this->b->seek(1); |
|||
self::assertSame('oo', $this->b->getContents()); |
|||
} |
|||
|
|||
public function testCloses(): void |
|||
{ |
|||
$this->b->close(); |
|||
self::assertFalse(is_resource($this->c)); |
|||
} |
|||
|
|||
public function testDetaches(): void |
|||
{ |
|||
$this->b->detach(); |
|||
self::assertFalse($this->b->isReadable()); |
|||
} |
|||
|
|||
public function testWrapsMetadata(): void |
|||
{ |
|||
self::assertSame($this->b->getMetadata(), $this->a->getMetadata()); |
|||
self::assertSame($this->b->getMetadata('uri'), $this->a->getMetadata('uri')); |
|||
} |
|||
|
|||
public function testWrapsWrites(): void |
|||
{ |
|||
$this->b->seek(0, SEEK_END); |
|||
$this->b->write('foo'); |
|||
self::assertSame('foofoo', (string) $this->a); |
|||
} |
|||
|
|||
public function testThrowsWithInvalidGetter(): void |
|||
{ |
|||
$this->expectException(\UnexpectedValueException::class); |
|||
$this->b->foo; |
|||
} |
|||
|
|||
public function testThrowsWhenGetterNotImplemented(): void |
|||
{ |
|||
$this->expectException(\BadMethodCallException::class); |
|||
$s = new BadStream(); |
|||
$s->stream; |
|||
} |
|||
} |
|||
|
|||
class BadStream |
|||
{ |
|||
use StreamDecoratorTrait; |
|||
|
|||
public function __construct() |
|||
{ |
|||
} |
|||
} |
@ -1,447 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\FnStream; |
|||
use GuzzleHttp\Psr7\Stream; |
|||
use GuzzleHttp\Psr7\StreamWrapper; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\Stream |
|||
*/ |
|||
class StreamTest extends TestCase |
|||
{ |
|||
public static $isFReadError = false; |
|||
|
|||
public function testConstructorThrowsExceptionOnInvalidArgument(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
new Stream(true); |
|||
} |
|||
|
|||
public function testConstructorInitializesProperties(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'r+'); |
|||
fwrite($handle, 'data'); |
|||
$stream = new Stream($handle); |
|||
self::assertTrue($stream->isReadable()); |
|||
self::assertTrue($stream->isWritable()); |
|||
self::assertTrue($stream->isSeekable()); |
|||
self::assertSame('php://temp', $stream->getMetadata('uri')); |
|||
self::assertIsArray($stream->getMetadata()); |
|||
self::assertSame(4, $stream->getSize()); |
|||
self::assertFalse($stream->eof()); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testConstructorInitializesPropertiesWithRbPlus(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'rb+'); |
|||
fwrite($handle, 'data'); |
|||
$stream = new Stream($handle); |
|||
self::assertTrue($stream->isReadable()); |
|||
self::assertTrue($stream->isWritable()); |
|||
self::assertTrue($stream->isSeekable()); |
|||
self::assertSame('php://temp', $stream->getMetadata('uri')); |
|||
self::assertIsArray($stream->getMetadata()); |
|||
self::assertSame(4, $stream->getSize()); |
|||
self::assertFalse($stream->eof()); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testStreamClosesHandleOnDestruct(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'r'); |
|||
$stream = new Stream($handle); |
|||
unset($stream); |
|||
self::assertFalse(is_resource($handle)); |
|||
} |
|||
|
|||
public function testConvertsToString(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'w+'); |
|||
fwrite($handle, 'data'); |
|||
$stream = new Stream($handle); |
|||
self::assertSame('data', (string) $stream); |
|||
self::assertSame('data', (string) $stream); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testConvertsToStringNonSeekableStream(): void |
|||
{ |
|||
$handle = popen('echo foo', 'r'); |
|||
$stream = new Stream($handle); |
|||
self::assertFalse($stream->isSeekable()); |
|||
self::assertSame('foo', trim((string) $stream)); |
|||
} |
|||
|
|||
public function testConvertsToStringNonSeekablePartiallyReadStream(): void |
|||
{ |
|||
$handle = popen('echo bar', 'r'); |
|||
$stream = new Stream($handle); |
|||
$firstLetter = $stream->read(1); |
|||
self::assertFalse($stream->isSeekable()); |
|||
self::assertSame('b', $firstLetter); |
|||
self::assertSame('ar', trim((string) $stream)); |
|||
} |
|||
|
|||
public function testGetsContents(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'w+'); |
|||
fwrite($handle, 'data'); |
|||
$stream = new Stream($handle); |
|||
self::assertSame('', $stream->getContents()); |
|||
$stream->seek(0); |
|||
self::assertSame('data', $stream->getContents()); |
|||
self::assertSame('', $stream->getContents()); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testChecksEof(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'w+'); |
|||
fwrite($handle, 'data'); |
|||
$stream = new Stream($handle); |
|||
self::assertSame(4, $stream->tell(), 'Stream cursor already at the end'); |
|||
self::assertFalse($stream->eof(), 'Stream still not eof'); |
|||
self::assertSame('', $stream->read(1), 'Need to read one more byte to reach eof'); |
|||
self::assertTrue($stream->eof()); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testGetSize(): void |
|||
{ |
|||
$size = filesize(__FILE__); |
|||
$handle = fopen(__FILE__, 'r'); |
|||
$stream = new Stream($handle); |
|||
self::assertSame($size, $stream->getSize()); |
|||
// Load from cache |
|||
self::assertSame($size, $stream->getSize()); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testEnsuresSizeIsConsistent(): void |
|||
{ |
|||
$h = fopen('php://temp', 'w+'); |
|||
self::assertSame(3, fwrite($h, 'foo')); |
|||
$stream = new Stream($h); |
|||
self::assertSame(3, $stream->getSize()); |
|||
self::assertSame(4, $stream->write('test')); |
|||
self::assertSame(7, $stream->getSize()); |
|||
self::assertSame(7, $stream->getSize()); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testProvidesStreamPosition(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'w+'); |
|||
$stream = new Stream($handle); |
|||
self::assertSame(0, $stream->tell()); |
|||
$stream->write('foo'); |
|||
self::assertSame(3, $stream->tell()); |
|||
$stream->seek(1); |
|||
self::assertSame(1, $stream->tell()); |
|||
self::assertSame(ftell($handle), $stream->tell()); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testDetachStreamAndClearProperties(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'r'); |
|||
$stream = new Stream($handle); |
|||
self::assertSame($handle, $stream->detach()); |
|||
self::assertIsResource($handle, 'Stream is not closed'); |
|||
self::assertNull($stream->detach()); |
|||
|
|||
$this->assertStreamStateAfterClosedOrDetached($stream); |
|||
|
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testCloseResourceAndClearProperties(): void |
|||
{ |
|||
$handle = fopen('php://temp', 'r'); |
|||
$stream = new Stream($handle); |
|||
$stream->close(); |
|||
|
|||
self::assertFalse(is_resource($handle)); |
|||
|
|||
$this->assertStreamStateAfterClosedOrDetached($stream); |
|||
} |
|||
|
|||
private function assertStreamStateAfterClosedOrDetached(Stream $stream): void |
|||
{ |
|||
self::assertFalse($stream->isReadable()); |
|||
self::assertFalse($stream->isWritable()); |
|||
self::assertFalse($stream->isSeekable()); |
|||
self::assertNull($stream->getSize()); |
|||
self::assertSame([], $stream->getMetadata()); |
|||
self::assertNull($stream->getMetadata('foo')); |
|||
|
|||
$throws = function (callable $fn): void { |
|||
try { |
|||
$fn(); |
|||
} catch (\Exception $e) { |
|||
$this->assertStringContainsString('Stream is detached', $e->getMessage()); |
|||
|
|||
return; |
|||
} |
|||
|
|||
$this->fail('Exception should be thrown after the stream is detached.'); |
|||
}; |
|||
|
|||
$throws(function () use ($stream): void { |
|||
$stream->read(10); |
|||
}); |
|||
$throws(function () use ($stream): void { |
|||
$stream->write('bar'); |
|||
}); |
|||
$throws(function () use ($stream): void { |
|||
$stream->seek(10); |
|||
}); |
|||
$throws(function () use ($stream): void { |
|||
$stream->tell(); |
|||
}); |
|||
$throws(function () use ($stream): void { |
|||
$stream->eof(); |
|||
}); |
|||
$throws(function () use ($stream): void { |
|||
$stream->getContents(); |
|||
}); |
|||
|
|||
if (\PHP_VERSION_ID >= 70400) { |
|||
$throws(function () use ($stream): void { |
|||
(string) $stream; |
|||
}); |
|||
} else { |
|||
$errors = []; |
|||
set_error_handler(function (int $errorNumber, string $errorMessage) use (&$errors): void { |
|||
$errors[] = ['message' => $errorMessage, 'number' => $errorNumber]; |
|||
}); |
|||
self::assertSame('', (string) $stream); |
|||
restore_error_handler(); |
|||
|
|||
self::assertCount(1, $errors); |
|||
self::assertStringStartsWith('GuzzleHttp\Psr7\Stream::__toString exception', $errors[0]['message']); |
|||
self::assertSame(E_USER_ERROR, $errors[0]['number']); |
|||
} |
|||
} |
|||
|
|||
public function testStreamReadingWithZeroLength(): void |
|||
{ |
|||
$r = fopen('php://temp', 'r'); |
|||
$stream = new Stream($r); |
|||
|
|||
self::assertSame('', $stream->read(0)); |
|||
|
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testStreamReadingWithNegativeLength(): void |
|||
{ |
|||
$r = fopen('php://temp', 'r'); |
|||
$stream = new Stream($r); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Length parameter cannot be negative'); |
|||
|
|||
try { |
|||
$stream->read(-1); |
|||
} catch (\Exception $e) { |
|||
$stream->close(); |
|||
throw $e; |
|||
} |
|||
|
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testStreamReadingFreadFalse(): void |
|||
{ |
|||
self::$isFReadError = true; |
|||
$r = fopen('php://temp', 'r'); |
|||
$stream = new Stream($r); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Unable to read from stream'); |
|||
|
|||
try { |
|||
$stream->read(1); |
|||
} catch (\Exception $e) { |
|||
self::$isFReadError = false; |
|||
$stream->close(); |
|||
throw $e; |
|||
} |
|||
|
|||
self::$isFReadError = false; |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testStreamReadingFreadException(): void |
|||
{ |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Unable to read from stream'); |
|||
|
|||
$r = StreamWrapper::getResource(new FnStream([ |
|||
'read' => function ($len): string { |
|||
throw new \ErrorException('Some error'); |
|||
}, |
|||
'isReadable' => function (): bool { |
|||
return true; |
|||
}, |
|||
'isWritable' => function (): bool { |
|||
return false; |
|||
}, |
|||
'eof' => function (): bool { |
|||
return false; |
|||
}, |
|||
])); |
|||
|
|||
$stream = new Stream($r); |
|||
$stream->read(1); |
|||
} |
|||
|
|||
/** |
|||
* @requires extension zlib |
|||
* |
|||
* @dataProvider gzipModeProvider |
|||
*/ |
|||
public function testGzipStreamModes(string $mode, bool $readable, bool $writable): void |
|||
{ |
|||
$r = gzopen('php://temp', $mode); |
|||
$stream = new Stream($r); |
|||
|
|||
self::assertSame($readable, $stream->isReadable()); |
|||
self::assertSame($writable, $stream->isWritable()); |
|||
|
|||
$stream->close(); |
|||
} |
|||
|
|||
public function gzipModeProvider(): iterable |
|||
{ |
|||
return [ |
|||
['mode' => 'rb9', 'readable' => true, 'writable' => false], |
|||
['mode' => 'wb2', 'readable' => false, 'writable' => true], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider readableModeProvider |
|||
*/ |
|||
public function testReadableStream(string $mode): void |
|||
{ |
|||
$r = fopen('php://temp', $mode); |
|||
$stream = new Stream($r); |
|||
|
|||
self::assertTrue($stream->isReadable()); |
|||
|
|||
$stream->close(); |
|||
} |
|||
|
|||
public function readableModeProvider(): iterable |
|||
{ |
|||
return [ |
|||
['r'], |
|||
['w+'], |
|||
['r+'], |
|||
['x+'], |
|||
['c+'], |
|||
['rb'], |
|||
['w+b'], |
|||
['r+b'], |
|||
['x+b'], |
|||
['c+b'], |
|||
['rt'], |
|||
['w+t'], |
|||
['r+t'], |
|||
['x+t'], |
|||
['c+t'], |
|||
['a+'], |
|||
['rb+'], |
|||
]; |
|||
} |
|||
|
|||
public function testWriteOnlyStreamIsNotReadable(): void |
|||
{ |
|||
$r = fopen('php://output', 'w'); |
|||
$stream = new Stream($r); |
|||
|
|||
self::assertFalse($stream->isReadable()); |
|||
|
|||
$stream->close(); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider writableModeProvider |
|||
*/ |
|||
public function testWritableStream(string $mode): void |
|||
{ |
|||
$r = fopen('php://temp', $mode); |
|||
$stream = new Stream($r); |
|||
|
|||
self::assertTrue($stream->isWritable()); |
|||
|
|||
$stream->close(); |
|||
} |
|||
|
|||
public function writableModeProvider(): iterable |
|||
{ |
|||
return [ |
|||
['w'], |
|||
['w+'], |
|||
['rw'], |
|||
['r+'], |
|||
['x+'], |
|||
['c+'], |
|||
['wb'], |
|||
['w+b'], |
|||
['r+b'], |
|||
['rb+'], |
|||
['x+b'], |
|||
['c+b'], |
|||
['w+t'], |
|||
['r+t'], |
|||
['x+t'], |
|||
['c+t'], |
|||
['a'], |
|||
['a+'], |
|||
]; |
|||
} |
|||
|
|||
public function testReadOnlyStreamIsNotWritable(): void |
|||
{ |
|||
$r = fopen('php://input', 'r'); |
|||
$stream = new Stream($r); |
|||
|
|||
self::assertFalse($stream->isWritable()); |
|||
|
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testCannotReadUnreadableStream(): void |
|||
{ |
|||
$r = fopen(tempnam(sys_get_temp_dir(), 'guzzle-psr7-'), 'w'); |
|||
$stream = new Stream($r); |
|||
|
|||
$stream->write('Hello world!!'); |
|||
|
|||
$stream->seek(0); |
|||
|
|||
$this->expectException(\RuntimeException::class); |
|||
|
|||
try { |
|||
$stream->getContents(); |
|||
} finally { |
|||
$stream->close(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
namespace GuzzleHttp\Psr7; |
|||
|
|||
use GuzzleHttp\Tests\Psr7\StreamTest; |
|||
|
|||
function fread($handle, $length) |
|||
{ |
|||
return StreamTest::$isFReadError ? false : \fread($handle, $length); |
|||
} |
@ -1,202 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\StreamWrapper; |
|||
use GuzzleHttp\Psr7\Utils; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\StreamInterface; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\StreamWrapper |
|||
*/ |
|||
class StreamWrapperTest extends TestCase |
|||
{ |
|||
public function testResource(): void |
|||
{ |
|||
$stream = Utils::streamFor('foo'); |
|||
$handle = StreamWrapper::getResource($stream); |
|||
self::assertSame('foo', fread($handle, 3)); |
|||
self::assertSame(3, ftell($handle)); |
|||
self::assertSame(3, fwrite($handle, 'bar')); |
|||
self::assertSame(0, fseek($handle, 0)); |
|||
self::assertSame('foobar', fread($handle, 6)); |
|||
self::assertSame('', fread($handle, 1)); |
|||
self::assertTrue(feof($handle)); |
|||
|
|||
$stBlksize = defined('PHP_WINDOWS_VERSION_BUILD') ? -1 : 0; |
|||
|
|||
self::assertEquals([ |
|||
'dev' => 0, |
|||
'ino' => 0, |
|||
'mode' => 33206, |
|||
'nlink' => 0, |
|||
'uid' => 0, |
|||
'gid' => 0, |
|||
'rdev' => 0, |
|||
'size' => 6, |
|||
'atime' => 0, |
|||
'mtime' => 0, |
|||
'ctime' => 0, |
|||
'blksize' => $stBlksize, |
|||
'blocks' => $stBlksize, |
|||
0 => 0, |
|||
1 => 0, |
|||
2 => 33206, |
|||
3 => 0, |
|||
4 => 0, |
|||
5 => 0, |
|||
6 => 0, |
|||
7 => 6, |
|||
8 => 0, |
|||
9 => 0, |
|||
10 => 0, |
|||
11 => $stBlksize, |
|||
12 => $stBlksize, |
|||
], fstat($handle)); |
|||
|
|||
self::assertTrue(fclose($handle)); |
|||
self::assertSame('foobar', (string) $stream); |
|||
} |
|||
|
|||
public function testStreamContext(): void |
|||
{ |
|||
$stream = Utils::streamFor('foo'); |
|||
|
|||
self::assertSame('foo', file_get_contents('guzzle://stream', false, StreamWrapper::createStreamContext($stream))); |
|||
} |
|||
|
|||
public function testStreamCast(): void |
|||
{ |
|||
$streams = [ |
|||
StreamWrapper::getResource(Utils::streamFor('foo')), |
|||
StreamWrapper::getResource(Utils::streamFor('bar')), |
|||
]; |
|||
$write = null; |
|||
$except = null; |
|||
self::assertIsInt(stream_select($streams, $write, $except, 0)); |
|||
} |
|||
|
|||
public function testValidatesStream(): void |
|||
{ |
|||
$stream = $this->createMock(StreamInterface::class); |
|||
$stream->expects(self::once()) |
|||
->method('isReadable') |
|||
->willReturn(false); |
|||
$stream->expects(self::once()) |
|||
->method('isWritable') |
|||
->willReturn(false); |
|||
|
|||
$this->expectException(\InvalidArgumentException::class); |
|||
StreamWrapper::getResource($stream); |
|||
} |
|||
|
|||
public function testReturnsFalseWhenStreamDoesNotExist(): void |
|||
{ |
|||
$this->expectWarning(); |
|||
fopen('guzzle://foo', 'r'); |
|||
} |
|||
|
|||
public function testCanOpenReadonlyStream(): void |
|||
{ |
|||
$stream = $this->createMock(StreamInterface::class); |
|||
$stream->expects(self::once()) |
|||
->method('isReadable') |
|||
->willReturn(false); |
|||
$stream->expects(self::once()) |
|||
->method('isWritable') |
|||
->willReturn(true); |
|||
$r = StreamWrapper::getResource($stream); |
|||
self::assertIsResource($r); |
|||
fclose($r); |
|||
} |
|||
|
|||
public function testUrlStat(): void |
|||
{ |
|||
StreamWrapper::register(); |
|||
|
|||
$stBlksize = defined('PHP_WINDOWS_VERSION_BUILD') ? -1 : 0; |
|||
|
|||
self::assertEquals( |
|||
[ |
|||
'dev' => 0, |
|||
'ino' => 0, |
|||
'mode' => 0, |
|||
'nlink' => 0, |
|||
'uid' => 0, |
|||
'gid' => 0, |
|||
'rdev' => 0, |
|||
'size' => 0, |
|||
'atime' => 0, |
|||
'mtime' => 0, |
|||
'ctime' => 0, |
|||
'blksize' => $stBlksize, |
|||
'blocks' => $stBlksize, |
|||
0 => 0, |
|||
1 => 0, |
|||
2 => 0, |
|||
3 => 0, |
|||
4 => 0, |
|||
5 => 0, |
|||
6 => 0, |
|||
7 => 0, |
|||
8 => 0, |
|||
9 => 0, |
|||
10 => 0, |
|||
11 => $stBlksize, |
|||
12 => $stBlksize, |
|||
], |
|||
stat('guzzle://stream') |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* @requires extension xmlreader |
|||
*/ |
|||
public function testXmlReaderWithStream(): void |
|||
{ |
|||
$stream = Utils::streamFor('<?xml version="1.0" encoding="utf-8"?><foo />'); |
|||
|
|||
StreamWrapper::register(); |
|||
libxml_set_streams_context(StreamWrapper::createStreamContext($stream)); |
|||
$reader = new \XMLReader(); |
|||
|
|||
self::assertTrue($reader->open('guzzle://stream')); |
|||
self::assertTrue($reader->read()); |
|||
self::assertSame('foo', $reader->name); |
|||
} |
|||
|
|||
/** |
|||
* @requires extension xmlreader |
|||
*/ |
|||
public function testXmlWriterWithStream(): void |
|||
{ |
|||
$stream = Utils::streamFor(fopen('php://memory', 'wb')); |
|||
|
|||
StreamWrapper::register(); |
|||
libxml_set_streams_context(StreamWrapper::createStreamContext($stream)); |
|||
$writer = new \XMLWriter(); |
|||
|
|||
self::assertTrue($writer->openURI('guzzle://stream')); |
|||
self::assertTrue($writer->startDocument()); |
|||
self::assertTrue($writer->writeElement('foo')); |
|||
self::assertTrue($writer->endDocument()); |
|||
|
|||
$stream->rewind(); |
|||
self::assertXmlStringEqualsXmlString('<?xml version="1.0"?><foo />', (string) $stream); |
|||
} |
|||
|
|||
public function testWrappedNullSizedStreamStaysNullSized(): void |
|||
{ |
|||
$nullSizedStream = new Psr7\PumpStream(function () { return ''; }); |
|||
$this->assertNull($nullSizedStream->getSize()); |
|||
|
|||
$resource = StreamWrapper::getResource($nullSizedStream); |
|||
$stream = Utils::streamFor($resource); |
|||
|
|||
$this->assertNull($stream->getSize()); |
|||
} |
|||
} |
@ -1,210 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\Stream; |
|||
use GuzzleHttp\Psr7\UploadedFile; |
|||
use PHPUnit\Framework\TestCase; |
|||
use ReflectionProperty; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\UploadedFile |
|||
*/ |
|||
class UploadedFileTest extends TestCase |
|||
{ |
|||
private $cleanup; |
|||
|
|||
protected function setUp(): void |
|||
{ |
|||
$this->cleanup = []; |
|||
} |
|||
|
|||
protected function tearDown(): void |
|||
{ |
|||
foreach ($this->cleanup as $file) { |
|||
if (is_scalar($file) && file_exists($file)) { |
|||
unlink($file); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public function invalidStreams() |
|||
{ |
|||
return [ |
|||
'null' => [null], |
|||
'true' => [true], |
|||
'false' => [false], |
|||
'int' => [1], |
|||
'float' => [1.1], |
|||
'array' => [['filename']], |
|||
'object' => [(object) ['filename']], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider invalidStreams |
|||
*/ |
|||
public function testRaisesExceptionOnInvalidStreamOrFile($streamOrFile): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
|
|||
new UploadedFile($streamOrFile, 0, UPLOAD_ERR_OK); |
|||
} |
|||
|
|||
public function testGetStreamReturnsOriginalStreamObject(): void |
|||
{ |
|||
$stream = new Stream(fopen('php://temp', 'r')); |
|||
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); |
|||
|
|||
self::assertSame($stream, $upload->getStream()); |
|||
} |
|||
|
|||
public function testGetStreamReturnsWrappedPhpStream(): void |
|||
{ |
|||
$stream = fopen('php://temp', 'wb+'); |
|||
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); |
|||
$uploadStream = $upload->getStream()->detach(); |
|||
|
|||
self::assertSame($stream, $uploadStream); |
|||
} |
|||
|
|||
public function testGetStreamReturnsStreamForFile(): void |
|||
{ |
|||
$this->cleanup[] = $stream = tempnam(sys_get_temp_dir(), 'stream_file'); |
|||
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); |
|||
$uploadStream = $upload->getStream(); |
|||
$r = new ReflectionProperty($uploadStream, 'filename'); |
|||
$r->setAccessible(true); |
|||
|
|||
self::assertSame($stream, $r->getValue($uploadStream)); |
|||
} |
|||
|
|||
public function testSuccessful(): void |
|||
{ |
|||
$stream = \GuzzleHttp\Psr7\Utils::streamFor('Foo bar!'); |
|||
$upload = new UploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, 'filename.txt', 'text/plain'); |
|||
|
|||
self::assertSame($stream->getSize(), $upload->getSize()); |
|||
self::assertSame('filename.txt', $upload->getClientFilename()); |
|||
self::assertSame('text/plain', $upload->getClientMediaType()); |
|||
|
|||
$this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'successful'); |
|||
$upload->moveTo($to); |
|||
self::assertFileExists($to); |
|||
self::assertSame($stream->__toString(), file_get_contents($to)); |
|||
} |
|||
|
|||
public function invalidMovePaths(): iterable |
|||
{ |
|||
return [ |
|||
'null' => [null], |
|||
'true' => [true], |
|||
'false' => [false], |
|||
'int' => [1], |
|||
'float' => [1.1], |
|||
'empty' => [''], |
|||
'array' => [['filename']], |
|||
'object' => [(object) ['filename']], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider invalidMovePaths |
|||
*/ |
|||
public function testMoveRaisesExceptionForInvalidPath($path): void |
|||
{ |
|||
$stream = \GuzzleHttp\Psr7\Utils::streamFor('Foo bar!'); |
|||
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); |
|||
|
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('path'); |
|||
$upload->moveTo($path); |
|||
} |
|||
|
|||
public function testMoveCannotBeCalledMoreThanOnce(): void |
|||
{ |
|||
$stream = \GuzzleHttp\Psr7\Utils::streamFor('Foo bar!'); |
|||
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); |
|||
|
|||
$this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'diac'); |
|||
$upload->moveTo($to); |
|||
self::assertFileExists($to); |
|||
|
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('moved'); |
|||
$upload->moveTo($to); |
|||
} |
|||
|
|||
public function testCannotRetrieveStreamAfterMove(): void |
|||
{ |
|||
$stream = \GuzzleHttp\Psr7\Utils::streamFor('Foo bar!'); |
|||
$upload = new UploadedFile($stream, 0, UPLOAD_ERR_OK); |
|||
|
|||
$this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'diac'); |
|||
$upload->moveTo($to); |
|||
self::assertFileExists($to); |
|||
|
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('moved'); |
|||
$upload->getStream(); |
|||
} |
|||
|
|||
public function nonOkErrorStatus(): iterable |
|||
{ |
|||
return [ |
|||
'UPLOAD_ERR_INI_SIZE' => [UPLOAD_ERR_INI_SIZE], |
|||
'UPLOAD_ERR_FORM_SIZE' => [UPLOAD_ERR_FORM_SIZE], |
|||
'UPLOAD_ERR_PARTIAL' => [UPLOAD_ERR_PARTIAL], |
|||
'UPLOAD_ERR_NO_FILE' => [UPLOAD_ERR_NO_FILE], |
|||
'UPLOAD_ERR_NO_TMP_DIR' => [UPLOAD_ERR_NO_TMP_DIR], |
|||
'UPLOAD_ERR_CANT_WRITE' => [UPLOAD_ERR_CANT_WRITE], |
|||
'UPLOAD_ERR_EXTENSION' => [UPLOAD_ERR_EXTENSION], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider nonOkErrorStatus |
|||
*/ |
|||
public function testConstructorDoesNotRaiseExceptionForInvalidStreamWhenErrorStatusPresent($status): void |
|||
{ |
|||
$uploadedFile = new UploadedFile('not ok', 0, $status); |
|||
self::assertSame($status, $uploadedFile->getError()); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider nonOkErrorStatus |
|||
*/ |
|||
public function testMoveToRaisesExceptionWhenErrorStatusPresent($status): void |
|||
{ |
|||
$uploadedFile = new UploadedFile('not ok', 0, $status); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('upload error'); |
|||
$uploadedFile->moveTo(__DIR__.'/'.bin2hex(random_bytes(20))); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider nonOkErrorStatus |
|||
*/ |
|||
public function testGetStreamRaisesExceptionWhenErrorStatusPresent($status): void |
|||
{ |
|||
$uploadedFile = new UploadedFile('not ok', 0, $status); |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('upload error'); |
|||
$uploadedFile->getStream(); |
|||
} |
|||
|
|||
public function testMoveToCreatesStreamIfOnlyAFilenameWasProvided(): void |
|||
{ |
|||
$this->cleanup[] = $from = tempnam(sys_get_temp_dir(), 'copy_from'); |
|||
$this->cleanup[] = $to = tempnam(sys_get_temp_dir(), 'copy_to'); |
|||
|
|||
copy(__FILE__, $from); |
|||
|
|||
$uploadedFile = new UploadedFile($from, 100, UPLOAD_ERR_OK, basename($from), 'text/plain'); |
|||
$uploadedFile->moveTo($to); |
|||
|
|||
self::assertFileEquals(__FILE__, $to); |
|||
} |
|||
} |
@ -1,45 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\Uri; |
|||
use GuzzleHttp\Psr7\UriComparator; |
|||
use PHPUnit\Framework\TestCase; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\UriComparator |
|||
*/ |
|||
class UriComparatorTest extends TestCase |
|||
{ |
|||
/** |
|||
* @dataProvider getCrossOriginExamples |
|||
*/ |
|||
public function testIsCrossOrigin(string $original, string $modified, bool $expected): void |
|||
{ |
|||
self::assertSame($expected, UriComparator::isCrossOrigin(new Uri($original), new Uri($modified))); |
|||
} |
|||
|
|||
public function getCrossOriginExamples(): array |
|||
{ |
|||
return [ |
|||
['http://example.com/123', 'http://example.com/', false], |
|||
['http://example.com/123', 'http://example.com:80/', false], |
|||
['http://example.com:80/123', 'http://example.com/', false], |
|||
['http://example.com:80/123', 'http://example.com:80/', false], |
|||
['http://example.com/123', 'https://example.com/', true], |
|||
['http://example.com/123', 'http://www.example.com/', true], |
|||
['http://example.com/123', 'http://example.com:81/', true], |
|||
['http://example.com:80/123', 'http://example.com:81/', true], |
|||
['https://example.com/123', 'https://example.com/', false], |
|||
['https://example.com/123', 'https://example.com:443/', false], |
|||
['https://example.com:443/123', 'https://example.com/', false], |
|||
['https://example.com:443/123', 'https://example.com:443/', false], |
|||
['https://example.com/123', 'http://example.com/', true], |
|||
['https://example.com/123', 'https://www.example.com/', true], |
|||
['https://example.com/123', 'https://example.com:444/', true], |
|||
['https://example.com:443/123', 'https://example.com:444/', true], |
|||
]; |
|||
} |
|||
} |
@ -1,180 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\Uri; |
|||
use GuzzleHttp\Psr7\UriNormalizer; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\UriInterface; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\UriNormalizer |
|||
*/ |
|||
class UriNormalizerTest extends TestCase |
|||
{ |
|||
public function testCapitalizePercentEncoding(): void |
|||
{ |
|||
$actualEncoding = 'a%c2%7A%5eb%25%fa%fA%Fa'; |
|||
$expectEncoding = 'a%C2%7A%5Eb%25%FA%FA%FA'; |
|||
$uri = (new Uri())->withPath("/$actualEncoding")->withQuery($actualEncoding); |
|||
|
|||
self::assertSame("/$actualEncoding?$actualEncoding", (string) $uri, 'Not normalized automatically beforehand'); |
|||
|
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::CAPITALIZE_PERCENT_ENCODING); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame("/$expectEncoding?$expectEncoding", (string) $normalizedUri); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider getUnreservedCharacters |
|||
*/ |
|||
public function testDecodeUnreservedCharacters(string $char): void |
|||
{ |
|||
$percentEncoded = '%'.bin2hex($char); |
|||
// Add encoded reserved characters to test that those are not decoded and include the percent-encoded |
|||
// unreserved character both in lower and upper case to test the decoding is case-insensitive. |
|||
$encodedChars = $percentEncoded.'%2F%5B'.strtoupper($percentEncoded); |
|||
$uri = (new Uri())->withPath("/$encodedChars")->withQuery($encodedChars); |
|||
|
|||
self::assertSame("/$encodedChars?$encodedChars", (string) $uri, 'Not normalized automatically beforehand'); |
|||
|
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::DECODE_UNRESERVED_CHARACTERS); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame("/$char%2F%5B$char?$char%2F%5B$char", (string) $normalizedUri); |
|||
} |
|||
|
|||
public function getUnreservedCharacters(): iterable |
|||
{ |
|||
$unreservedChars = array_merge(range('a', 'z'), range('A', 'Z'), range(0, 9), ['-', '.', '_', '~']); |
|||
|
|||
return array_map(function ($char) { |
|||
return [(string) $char]; |
|||
}, $unreservedChars); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider getEmptyPathTestCases |
|||
*/ |
|||
public function testConvertEmptyPath($uri, $expected): void |
|||
{ |
|||
$normalizedUri = UriNormalizer::normalize(new Uri($uri), UriNormalizer::CONVERT_EMPTY_PATH); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame($expected, (string) $normalizedUri); |
|||
} |
|||
|
|||
public function getEmptyPathTestCases(): iterable |
|||
{ |
|||
return [ |
|||
['http://example.org', 'http://example.org/'], |
|||
['https://example.org', 'https://example.org/'], |
|||
['urn://example.org', 'urn://example.org'], |
|||
]; |
|||
} |
|||
|
|||
public function testRemoveDefaultHost(): void |
|||
{ |
|||
$uri = new Uri('file://localhost/myfile'); |
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::REMOVE_DEFAULT_HOST); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame('file:///myfile', (string) $normalizedUri); |
|||
} |
|||
|
|||
public function testRemoveDefaultPort(): void |
|||
{ |
|||
$uri = $this->createMock(UriInterface::class); |
|||
$uri->expects(self::any())->method('getScheme')->willReturn('http'); |
|||
$uri->expects(self::any())->method('getPort')->willReturn(80); |
|||
$uri->expects(self::once())->method('withPort')->with(null)->willReturn(new Uri('http://example.org')); |
|||
|
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::REMOVE_DEFAULT_PORT); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertNull($normalizedUri->getPort()); |
|||
} |
|||
|
|||
public function testRemoveDotSegments(): void |
|||
{ |
|||
$uri = new Uri('http://example.org/../a/b/../c/./d.html'); |
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::REMOVE_DOT_SEGMENTS); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame('http://example.org/a/c/d.html', (string) $normalizedUri); |
|||
} |
|||
|
|||
public function testRemoveDotSegmentsOfAbsolutePathReference(): void |
|||
{ |
|||
$uri = new Uri('/../a/b/../c/./d.html'); |
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::REMOVE_DOT_SEGMENTS); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame('/a/c/d.html', (string) $normalizedUri); |
|||
} |
|||
|
|||
public function testRemoveDotSegmentsOfRelativePathReference(): void |
|||
{ |
|||
$uri = new Uri('../c/./d.html'); |
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::REMOVE_DOT_SEGMENTS); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame('../c/./d.html', (string) $normalizedUri); |
|||
} |
|||
|
|||
public function testRemoveDuplicateSlashes(): void |
|||
{ |
|||
$uri = new Uri('http://example.org//foo///bar/bam.html'); |
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::REMOVE_DUPLICATE_SLASHES); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame('http://example.org/foo/bar/bam.html', (string) $normalizedUri); |
|||
} |
|||
|
|||
public function testSortQueryParameters(): void |
|||
{ |
|||
$uri = new Uri('?lang=en&article=fred'); |
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::SORT_QUERY_PARAMETERS); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame('?article=fred&lang=en', (string) $normalizedUri); |
|||
} |
|||
|
|||
public function testSortQueryParametersWithSameKeys(): void |
|||
{ |
|||
$uri = new Uri('?a=b&b=c&a=a&a&b=a&b=b&a=d&a=c'); |
|||
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::SORT_QUERY_PARAMETERS); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $normalizedUri); |
|||
self::assertSame('?a&a=a&a=b&a=c&a=d&b=a&b=b&b=c', (string) $normalizedUri); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider getEquivalentTestCases |
|||
*/ |
|||
public function testIsEquivalent(string $uri1, string $uri2, bool $expected): void |
|||
{ |
|||
$equivalent = UriNormalizer::isEquivalent(new Uri($uri1), new Uri($uri2)); |
|||
|
|||
self::assertSame($expected, $equivalent); |
|||
} |
|||
|
|||
public function getEquivalentTestCases(): iterable |
|||
{ |
|||
return [ |
|||
['http://example.org', 'http://example.org', true], |
|||
['hTTp://eXaMpLe.org', 'http://example.org', true], |
|||
['http://example.org/path?#', 'http://example.org/path', true], |
|||
['http://example.org:80', 'http://example.org/', true], |
|||
['http://example.org/../a/.././p%61th?%7a=%5e', 'http://example.org/path?z=%5E', true], |
|||
['https://example.org/', 'http://example.org/', false], |
|||
['https://example.org/', '//example.org/', false], |
|||
['//example.org/', '//example.org/', true], |
|||
['file:/myfile', 'file:///myfile', true], |
|||
['file:///myfile', 'file://localhost/myfile', true], |
|||
]; |
|||
} |
|||
} |
@ -1,210 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\Uri; |
|||
use GuzzleHttp\Psr7\UriResolver; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\UriInterface; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\UriResolver |
|||
*/ |
|||
class UriResolverTest extends TestCase |
|||
{ |
|||
private const RFC3986_BASE = 'http://a/b/c/d;p?q'; |
|||
|
|||
/** |
|||
* @dataProvider getResolveTestCases |
|||
*/ |
|||
public function testResolveUri(string $base, string $rel, string $expectedTarget): void |
|||
{ |
|||
$baseUri = new Uri($base); |
|||
$targetUri = UriResolver::resolve($baseUri, new Uri($rel)); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $targetUri); |
|||
self::assertSame($expectedTarget, (string) $targetUri); |
|||
// This ensures there are no test cases that only work in the resolve() direction but not the |
|||
// opposite via relativize(). This can happen when both base and rel URI are relative-path |
|||
// references resulting in another relative-path URI. |
|||
self::assertSame($expectedTarget, (string) UriResolver::resolve($baseUri, $targetUri)); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider getResolveTestCases |
|||
*/ |
|||
public function testRelativizeUri(string $base, string $expectedRelativeReference, string $target): void |
|||
{ |
|||
$baseUri = new Uri($base); |
|||
$relativeUri = UriResolver::relativize($baseUri, new Uri($target)); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $relativeUri); |
|||
// There are test-cases with too many dot-segments and relative references that are equal like "." == "./". |
|||
// So apart from the same-as condition, this alternative success condition is necessary. |
|||
self::assertTrue( |
|||
$expectedRelativeReference === (string) $relativeUri |
|||
|| $target === (string) UriResolver::resolve($baseUri, $relativeUri), |
|||
sprintf( |
|||
'"%s" is not the correct relative reference as it does not resolve to the target URI from the base URI', |
|||
(string) $relativeUri |
|||
) |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider getRelativizeTestCases |
|||
*/ |
|||
public function testRelativizeUriWithUniqueTests(string $base, string $target, string $expectedRelativeReference): void |
|||
{ |
|||
$baseUri = new Uri($base); |
|||
$targetUri = new Uri($target); |
|||
$relativeUri = UriResolver::relativize($baseUri, $targetUri); |
|||
|
|||
self::assertInstanceOf(UriInterface::class, $relativeUri); |
|||
self::assertSame($expectedRelativeReference, (string) $relativeUri); |
|||
|
|||
self::assertSame((string) UriResolver::resolve($baseUri, $targetUri), (string) UriResolver::resolve($baseUri, $relativeUri)); |
|||
} |
|||
|
|||
public function getResolveTestCases(): iterable |
|||
{ |
|||
return [ |
|||
[self::RFC3986_BASE, 'g:h', 'g:h'], |
|||
[self::RFC3986_BASE, 'g', 'http://a/b/c/g'], |
|||
[self::RFC3986_BASE, './g', 'http://a/b/c/g'], |
|||
[self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'], |
|||
[self::RFC3986_BASE, '/g', 'http://a/g'], |
|||
[self::RFC3986_BASE, '//g', 'http://g'], |
|||
[self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'], |
|||
[self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'], |
|||
[self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'], |
|||
[self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'], |
|||
[self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'], |
|||
[self::RFC3986_BASE, ';x', 'http://a/b/c/;x'], |
|||
[self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'], |
|||
[self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'], |
|||
[self::RFC3986_BASE, '', self::RFC3986_BASE], |
|||
[self::RFC3986_BASE, '.', 'http://a/b/c/'], |
|||
[self::RFC3986_BASE, './', 'http://a/b/c/'], |
|||
[self::RFC3986_BASE, '..', 'http://a/b/'], |
|||
[self::RFC3986_BASE, '../', 'http://a/b/'], |
|||
[self::RFC3986_BASE, '../g', 'http://a/b/g'], |
|||
[self::RFC3986_BASE, '../..', 'http://a/'], |
|||
[self::RFC3986_BASE, '../../', 'http://a/'], |
|||
[self::RFC3986_BASE, '../../g', 'http://a/g'], |
|||
[self::RFC3986_BASE, '../../../g', 'http://a/g'], |
|||
[self::RFC3986_BASE, '../../../../g', 'http://a/g'], |
|||
[self::RFC3986_BASE, '/./g', 'http://a/g'], |
|||
[self::RFC3986_BASE, '/../g', 'http://a/g'], |
|||
[self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'], |
|||
[self::RFC3986_BASE, '.g', 'http://a/b/c/.g'], |
|||
[self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'], |
|||
[self::RFC3986_BASE, '..g', 'http://a/b/c/..g'], |
|||
[self::RFC3986_BASE, './../g', 'http://a/b/g'], |
|||
[self::RFC3986_BASE, 'foo////g', 'http://a/b/c/foo////g'], |
|||
[self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'], |
|||
[self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'], |
|||
[self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'], |
|||
[self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'], |
|||
[self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'], |
|||
// dot-segments in the query or fragment |
|||
[self::RFC3986_BASE, 'g?y/./x', 'http://a/b/c/g?y/./x'], |
|||
[self::RFC3986_BASE, 'g?y/../x', 'http://a/b/c/g?y/../x'], |
|||
[self::RFC3986_BASE, 'g#s/./x', 'http://a/b/c/g#s/./x'], |
|||
[self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'], |
|||
[self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'], |
|||
[self::RFC3986_BASE, '?y#s', 'http://a/b/c/d;p?y#s'], |
|||
// base with fragment |
|||
['http://a/b/c?q#s', '?y', 'http://a/b/c?y'], |
|||
// base with user info |
|||
['http://u@a/b/c/d;p?q', '.', 'http://u@a/b/c/'], |
|||
['http://u:p@a/b/c/d;p?q', '.', 'http://u:p@a/b/c/'], |
|||
// path ending with slash or no slash at all |
|||
['http://a/b/c/d/', 'e', 'http://a/b/c/d/e'], |
|||
['urn:no-slash', 'e', 'urn:e'], |
|||
// path ending without slash and multi-segment relative part |
|||
['http://a/b/c', 'd/e', 'http://a/b/d/e'], |
|||
// falsey relative parts |
|||
[self::RFC3986_BASE, '//0', 'http://0'], |
|||
[self::RFC3986_BASE, '0', 'http://a/b/c/0'], |
|||
[self::RFC3986_BASE, '?0', 'http://a/b/c/d;p?0'], |
|||
[self::RFC3986_BASE, '#0', 'http://a/b/c/d;p?q#0'], |
|||
// absolute path base URI |
|||
['/a/b/', '', '/a/b/'], |
|||
['/a/b', '', '/a/b'], |
|||
['/', 'a', '/a'], |
|||
['/', 'a/b', '/a/b'], |
|||
['/a/b', 'g', '/a/g'], |
|||
['/a/b/c', './', '/a/b/'], |
|||
['/a/b/', '../', '/a/'], |
|||
['/a/b/c', '../', '/a/'], |
|||
['/a/b/', '../../x/y/z/', '/x/y/z/'], |
|||
['/a/b/c/d/e', '../../../c/d', '/a/c/d'], |
|||
['/a/b/c//', '../', '/a/b/c/'], |
|||
['/a/b/c/', './/', '/a/b/c//'], |
|||
['/a/b/c', '../../../../a', '/a'], |
|||
['/a/b/c', '../../../..', '/'], |
|||
// not actually a dot-segment |
|||
['/a/b/c', '..a/b..', '/a/b/..a/b..'], |
|||
// '' cannot be used as relative reference as it would inherit the base query component |
|||
['/a/b?q', 'b', '/a/b'], |
|||
['/a/b/?q', './', '/a/b/'], |
|||
// path with colon: "with:colon" would be the wrong relative reference |
|||
['/a/', './with:colon', '/a/with:colon'], |
|||
['/a/', 'b/with:colon', '/a/b/with:colon'], |
|||
['/a/', './:b/', '/a/:b/'], |
|||
// relative path references |
|||
['a', 'a/b', 'a/b'], |
|||
['', '', ''], |
|||
['', '..', ''], |
|||
['/', '..', '/'], |
|||
['urn:a/b', '..//a/b', 'urn:/a/b'], |
|||
// network path references |
|||
// empty base path and relative-path reference |
|||
['//example.com', 'a', '//example.com/a'], |
|||
// path starting with two slashes |
|||
['//example.com//two-slashes', './', '//example.com//'], |
|||
['//example.com', './/', '//example.com//'], |
|||
['//example.com/', './/', '//example.com//'], |
|||
// base URI has less components than relative URI |
|||
['/', '//a/b?q#h', '//a/b?q#h'], |
|||
['/', 'urn:/', 'urn:/'], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* Some additional tests to getResolveTestCases() that only make sense for relativize. |
|||
*/ |
|||
public function getRelativizeTestCases(): iterable |
|||
{ |
|||
return [ |
|||
// targets that are relative-path references are returned as-is |
|||
['a/b', 'b/c', 'b/c'], |
|||
['a/b/c', '../b/c', '../b/c'], |
|||
['a', '', ''], |
|||
['a', './', './'], |
|||
['a', 'a/..', 'a/..'], |
|||
['/a/b/?q', '?q#h', '?q#h'], |
|||
['/a/b/?q', '#h', '#h'], |
|||
['/a/b/?q', 'c#h', 'c#h'], |
|||
// If the base URI has a query but the target has none, we cannot return an empty path reference as it would |
|||
// inherit the base query component when resolving. |
|||
['/a/b/?q', '/a/b/#h', './#h'], |
|||
['/', '/#h', '#h'], |
|||
['/', '/', ''], |
|||
['http://a', 'http://a/', './'], |
|||
['urn:a/b?q', 'urn:x/y?q', '../x/y?q'], |
|||
['urn:', 'urn:/', './/'], |
|||
['urn:a/b?q', 'urn:', '../'], |
|||
// target URI has less components than base URI |
|||
['http://a/b/', '//a/b/c', 'c'], |
|||
['http://a/b/', '/b/c', 'c'], |
|||
['http://a/b/', '/x/y', '../x/y'], |
|||
['http://a/b/', '/', '../'], |
|||
// absolute target URI without authority but base URI has one |
|||
['urn://a/b/', 'urn:/b/', 'urn:/b/'], |
|||
]; |
|||
} |
|||
} |
@ -1,722 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7\Exception\MalformedUriException; |
|||
use GuzzleHttp\Psr7\Uri; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\UriInterface; |
|||
|
|||
/** |
|||
* @covers \GuzzleHttp\Psr7\Uri |
|||
*/ |
|||
class UriTest extends TestCase |
|||
{ |
|||
public function testParsesProvidedUri(): void |
|||
{ |
|||
$uri = new Uri('https://user:pass@example.com:8080/path/123?q=abc#test'); |
|||
|
|||
self::assertSame('https', $uri->getScheme()); |
|||
self::assertSame('user:pass@example.com:8080', $uri->getAuthority()); |
|||
self::assertSame('user:pass', $uri->getUserInfo()); |
|||
self::assertSame('example.com', $uri->getHost()); |
|||
self::assertSame(8080, $uri->getPort()); |
|||
self::assertSame('/path/123', $uri->getPath()); |
|||
self::assertSame('q=abc', $uri->getQuery()); |
|||
self::assertSame('test', $uri->getFragment()); |
|||
self::assertSame('https://user:pass@example.com:8080/path/123?q=abc#test', (string) $uri); |
|||
} |
|||
|
|||
public function testCanTransformAndRetrievePartsIndividually(): void |
|||
{ |
|||
$uri = (new Uri()) |
|||
->withScheme('https') |
|||
->withUserInfo('user', 'pass') |
|||
->withHost('example.com') |
|||
->withPort(8080) |
|||
->withPath('/path/123') |
|||
->withQuery('q=abc') |
|||
->withFragment('test'); |
|||
|
|||
self::assertSame('https', $uri->getScheme()); |
|||
self::assertSame('user:pass@example.com:8080', $uri->getAuthority()); |
|||
self::assertSame('user:pass', $uri->getUserInfo()); |
|||
self::assertSame('example.com', $uri->getHost()); |
|||
self::assertSame(8080, $uri->getPort()); |
|||
self::assertSame('/path/123', $uri->getPath()); |
|||
self::assertSame('q=abc', $uri->getQuery()); |
|||
self::assertSame('test', $uri->getFragment()); |
|||
self::assertSame('https://user:pass@example.com:8080/path/123?q=abc#test', (string) $uri); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider getValidUris |
|||
*/ |
|||
public function testValidUrisStayValid(string $input): void |
|||
{ |
|||
$uri = new Uri($input); |
|||
|
|||
self::assertSame($input, (string) $uri); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider getValidUris |
|||
*/ |
|||
public function testFromParts(string $input): void |
|||
{ |
|||
$uri = Uri::fromParts(parse_url($input)); |
|||
|
|||
self::assertSame($input, (string) $uri); |
|||
} |
|||
|
|||
public function getValidUris(): iterable |
|||
{ |
|||
return [ |
|||
['urn:path-rootless'], |
|||
['urn:path:with:colon'], |
|||
['urn:/path-absolute'], |
|||
['urn:/'], |
|||
// only scheme with empty path |
|||
['urn:'], |
|||
// only path |
|||
['/'], |
|||
['relative/'], |
|||
['0'], |
|||
// same document reference |
|||
[''], |
|||
// network path without scheme |
|||
['//example.org'], |
|||
['//example.org/'], |
|||
['//example.org?q#h'], |
|||
// only query |
|||
['?q'], |
|||
['?q=abc&foo=bar'], |
|||
// only fragment |
|||
['#fragment'], |
|||
// dot segments are not removed automatically |
|||
['./foo/../bar'], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider getInvalidUris |
|||
*/ |
|||
public function testInvalidUrisThrowException(string $invalidUri): void |
|||
{ |
|||
$this->expectException(MalformedUriException::class); |
|||
new Uri($invalidUri); |
|||
} |
|||
|
|||
public function getInvalidUris(): iterable |
|||
{ |
|||
return [ |
|||
// parse_url() requires the host component which makes sense for http(s) |
|||
// but not when the scheme is not known or different. So '//' or '///' is |
|||
// currently invalid as well but should not according to RFC 3986. |
|||
['http://'], |
|||
['urn://host:with:colon'], // host cannot contain ":" |
|||
]; |
|||
} |
|||
|
|||
public function testPortMustBeValid(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Invalid port: 100000. Must be between 0 and 65535'); |
|||
(new Uri())->withPort(100000); |
|||
} |
|||
|
|||
public function testWithPortCannotBeNegative(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Invalid port: -1. Must be between 0 and 65535'); |
|||
(new Uri())->withPort(-1); |
|||
} |
|||
|
|||
public function testParseUriPortCannotBeNegative(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('Unable to parse URI'); |
|||
new Uri('//example.com:-1'); |
|||
} |
|||
|
|||
public function testSchemeMustHaveCorrectType(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
(new Uri())->withScheme([]); |
|||
} |
|||
|
|||
public function testHostMustHaveCorrectType(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
(new Uri())->withHost([]); |
|||
} |
|||
|
|||
public function testPathMustHaveCorrectType(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
(new Uri())->withPath([]); |
|||
} |
|||
|
|||
public function testQueryMustHaveCorrectType(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
(new Uri())->withQuery([]); |
|||
} |
|||
|
|||
public function testFragmentMustHaveCorrectType(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
(new Uri())->withFragment([]); |
|||
} |
|||
|
|||
public function testCanParseFalseyUriParts(): void |
|||
{ |
|||
$uri = new Uri('0://0:0@0/0?0#0'); |
|||
|
|||
self::assertSame('0', $uri->getScheme()); |
|||
self::assertSame('0:0@0', $uri->getAuthority()); |
|||
self::assertSame('0:0', $uri->getUserInfo()); |
|||
self::assertSame('0', $uri->getHost()); |
|||
self::assertSame('/0', $uri->getPath()); |
|||
self::assertSame('0', $uri->getQuery()); |
|||
self::assertSame('0', $uri->getFragment()); |
|||
self::assertSame('0://0:0@0/0?0#0', (string) $uri); |
|||
} |
|||
|
|||
public function testCanConstructFalseyUriParts(): void |
|||
{ |
|||
$uri = (new Uri()) |
|||
->withScheme('0') |
|||
->withUserInfo('0', '0') |
|||
->withHost('0') |
|||
->withPath('/0') |
|||
->withQuery('0') |
|||
->withFragment('0'); |
|||
|
|||
self::assertSame('0', $uri->getScheme()); |
|||
self::assertSame('0:0@0', $uri->getAuthority()); |
|||
self::assertSame('0:0', $uri->getUserInfo()); |
|||
self::assertSame('0', $uri->getHost()); |
|||
self::assertSame('/0', $uri->getPath()); |
|||
self::assertSame('0', $uri->getQuery()); |
|||
self::assertSame('0', $uri->getFragment()); |
|||
self::assertSame('0://0:0@0/0?0#0', (string) $uri); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider getPortTestCases |
|||
*/ |
|||
public function testIsDefaultPort(string $scheme, ?int $port, bool $isDefaultPort): void |
|||
{ |
|||
$uri = $this->createMock(UriInterface::class); |
|||
$uri->expects(self::any())->method('getScheme')->willReturn($scheme); |
|||
$uri->expects(self::any())->method('getPort')->willReturn($port); |
|||
|
|||
self::assertSame($isDefaultPort, Uri::isDefaultPort($uri)); |
|||
} |
|||
|
|||
public function getPortTestCases(): iterable |
|||
{ |
|||
return [ |
|||
['http', null, true], |
|||
['http', 80, true], |
|||
['http', 8080, false], |
|||
['https', null, true], |
|||
['https', 443, true], |
|||
['https', 444, false], |
|||
['ftp', 21, true], |
|||
['gopher', 70, true], |
|||
['nntp', 119, true], |
|||
['news', 119, true], |
|||
['telnet', 23, true], |
|||
['tn3270', 23, true], |
|||
['imap', 143, true], |
|||
['pop', 110, true], |
|||
['ldap', 389, true], |
|||
]; |
|||
} |
|||
|
|||
public function testIsAbsolute(): void |
|||
{ |
|||
self::assertTrue(Uri::isAbsolute(new Uri('http://example.org'))); |
|||
self::assertFalse(Uri::isAbsolute(new Uri('//example.org'))); |
|||
self::assertFalse(Uri::isAbsolute(new Uri('/abs-path'))); |
|||
self::assertFalse(Uri::isAbsolute(new Uri('rel-path'))); |
|||
} |
|||
|
|||
public function testIsNetworkPathReference(): void |
|||
{ |
|||
self::assertFalse(Uri::isNetworkPathReference(new Uri('http://example.org'))); |
|||
self::assertTrue(Uri::isNetworkPathReference(new Uri('//example.org'))); |
|||
self::assertFalse(Uri::isNetworkPathReference(new Uri('/abs-path'))); |
|||
self::assertFalse(Uri::isNetworkPathReference(new Uri('rel-path'))); |
|||
} |
|||
|
|||
public function testIsAbsolutePathReference(): void |
|||
{ |
|||
self::assertFalse(Uri::isAbsolutePathReference(new Uri('http://example.org'))); |
|||
self::assertFalse(Uri::isAbsolutePathReference(new Uri('//example.org'))); |
|||
self::assertTrue(Uri::isAbsolutePathReference(new Uri('/abs-path'))); |
|||
self::assertTrue(Uri::isAbsolutePathReference(new Uri('/'))); |
|||
self::assertFalse(Uri::isAbsolutePathReference(new Uri('rel-path'))); |
|||
} |
|||
|
|||
public function testIsRelativePathReference(): void |
|||
{ |
|||
self::assertFalse(Uri::isRelativePathReference(new Uri('http://example.org'))); |
|||
self::assertFalse(Uri::isRelativePathReference(new Uri('//example.org'))); |
|||
self::assertFalse(Uri::isRelativePathReference(new Uri('/abs-path'))); |
|||
self::assertTrue(Uri::isRelativePathReference(new Uri('rel-path'))); |
|||
self::assertTrue(Uri::isRelativePathReference(new Uri(''))); |
|||
} |
|||
|
|||
public function testIsSameDocumentReference(): void |
|||
{ |
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('http://example.org'))); |
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('//example.org'))); |
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('/abs-path'))); |
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('rel-path'))); |
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('?query'))); |
|||
self::assertTrue(Uri::isSameDocumentReference(new Uri(''))); |
|||
self::assertTrue(Uri::isSameDocumentReference(new Uri('#fragment'))); |
|||
|
|||
$baseUri = new Uri('http://example.org/path?foo=bar'); |
|||
|
|||
self::assertTrue(Uri::isSameDocumentReference(new Uri('#fragment'), $baseUri)); |
|||
self::assertTrue(Uri::isSameDocumentReference(new Uri('?foo=bar#fragment'), $baseUri)); |
|||
self::assertTrue(Uri::isSameDocumentReference(new Uri('/path?foo=bar#fragment'), $baseUri)); |
|||
self::assertTrue(Uri::isSameDocumentReference(new Uri('path?foo=bar#fragment'), $baseUri)); |
|||
self::assertTrue(Uri::isSameDocumentReference(new Uri('//example.org/path?foo=bar#fragment'), $baseUri)); |
|||
self::assertTrue(Uri::isSameDocumentReference(new Uri('http://example.org/path?foo=bar#fragment'), $baseUri)); |
|||
|
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('https://example.org/path?foo=bar'), $baseUri)); |
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('http://example.com/path?foo=bar'), $baseUri)); |
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('http://example.org/'), $baseUri)); |
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('http://example.org'), $baseUri)); |
|||
|
|||
self::assertFalse(Uri::isSameDocumentReference(new Uri('urn:/path'), new Uri('urn://example.com/path'))); |
|||
} |
|||
|
|||
public function testAddAndRemoveQueryValues(): void |
|||
{ |
|||
$uri = new Uri(); |
|||
$uri = Uri::withQueryValue($uri, 'a', 'b'); |
|||
$uri = Uri::withQueryValue($uri, 'c', 'd'); |
|||
$uri = Uri::withQueryValue($uri, 'e', null); |
|||
self::assertSame('a=b&c=d&e', $uri->getQuery()); |
|||
|
|||
$uri = Uri::withoutQueryValue($uri, 'c'); |
|||
self::assertSame('a=b&e', $uri->getQuery()); |
|||
$uri = Uri::withoutQueryValue($uri, 'e'); |
|||
self::assertSame('a=b', $uri->getQuery()); |
|||
$uri = Uri::withoutQueryValue($uri, 'a'); |
|||
self::assertSame('', $uri->getQuery()); |
|||
} |
|||
|
|||
public function testScalarQueryValues(): void |
|||
{ |
|||
$uri = new Uri(); |
|||
$uri = Uri::withQueryValues($uri, [ |
|||
2 => 2, |
|||
1 => true, |
|||
'false' => false, |
|||
'float' => 3.1, |
|||
]); |
|||
|
|||
self::assertSame('2=2&1=1&false=&float=3.1', $uri->getQuery()); |
|||
} |
|||
|
|||
public function testWithQueryValues(): void |
|||
{ |
|||
$uri = new Uri(); |
|||
$uri = Uri::withQueryValues($uri, [ |
|||
'key1' => 'value1', |
|||
'key2' => 'value2', |
|||
]); |
|||
|
|||
self::assertSame('key1=value1&key2=value2', $uri->getQuery()); |
|||
} |
|||
|
|||
public function testWithQueryValuesReplacesSameKeys(): void |
|||
{ |
|||
$uri = new Uri(); |
|||
|
|||
$uri = Uri::withQueryValues($uri, [ |
|||
'key1' => 'value1', |
|||
'key2' => 'value2', |
|||
]); |
|||
|
|||
$uri = Uri::withQueryValues($uri, [ |
|||
'key2' => 'newvalue', |
|||
]); |
|||
|
|||
self::assertSame('key1=value1&key2=newvalue', $uri->getQuery()); |
|||
} |
|||
|
|||
public function testWithQueryValueReplacesSameKeys(): void |
|||
{ |
|||
$uri = new Uri(); |
|||
$uri = Uri::withQueryValue($uri, 'a', 'b'); |
|||
$uri = Uri::withQueryValue($uri, 'c', 'd'); |
|||
$uri = Uri::withQueryValue($uri, 'a', 'e'); |
|||
self::assertSame('c=d&a=e', $uri->getQuery()); |
|||
} |
|||
|
|||
public function testWithoutQueryValueRemovesAllSameKeys(): void |
|||
{ |
|||
$uri = (new Uri())->withQuery('a=b&c=d&a=e'); |
|||
$uri = Uri::withoutQueryValue($uri, 'a'); |
|||
self::assertSame('c=d', $uri->getQuery()); |
|||
} |
|||
|
|||
public function testRemoveNonExistingQueryValue(): void |
|||
{ |
|||
$uri = new Uri(); |
|||
$uri = Uri::withQueryValue($uri, 'a', 'b'); |
|||
$uri = Uri::withoutQueryValue($uri, 'c'); |
|||
self::assertSame('a=b', $uri->getQuery()); |
|||
} |
|||
|
|||
public function testWithQueryValueHandlesEncoding(): void |
|||
{ |
|||
$uri = new Uri(); |
|||
$uri = Uri::withQueryValue($uri, 'E=mc^2', 'ein&stein'); |
|||
self::assertSame('E%3Dmc%5E2=ein%26stein', $uri->getQuery(), 'Decoded key/value get encoded'); |
|||
|
|||
$uri = new Uri(); |
|||
$uri = Uri::withQueryValue($uri, 'E%3Dmc%5e2', 'ein%26stein'); |
|||
self::assertSame('E%3Dmc%5e2=ein%26stein', $uri->getQuery(), 'Encoded key/value do not get double-encoded'); |
|||
} |
|||
|
|||
public function testWithoutQueryValueHandlesEncoding(): void |
|||
{ |
|||
// It also tests that the case of the percent-encoding does not matter, |
|||
// i.e. both lowercase "%3d" and uppercase "%5E" can be removed. |
|||
$uri = (new Uri())->withQuery('E%3dmc%5E2=einstein&foo=bar'); |
|||
$uri = Uri::withoutQueryValue($uri, 'E=mc^2'); |
|||
self::assertSame('foo=bar', $uri->getQuery(), 'Handles key in decoded form'); |
|||
|
|||
$uri = (new Uri())->withQuery('E%3dmc%5E2=einstein&foo=bar'); |
|||
$uri = Uri::withoutQueryValue($uri, 'E%3Dmc%5e2'); |
|||
self::assertSame('foo=bar', $uri->getQuery(), 'Handles key in encoded form'); |
|||
} |
|||
|
|||
public function testSchemeIsNormalizedToLowercase(): void |
|||
{ |
|||
$uri = new Uri('HTTP://example.com'); |
|||
|
|||
self::assertSame('http', $uri->getScheme()); |
|||
self::assertSame('http://example.com', (string) $uri); |
|||
|
|||
$uri = (new Uri('//example.com'))->withScheme('HTTP'); |
|||
|
|||
self::assertSame('http', $uri->getScheme()); |
|||
self::assertSame('http://example.com', (string) $uri); |
|||
} |
|||
|
|||
public function testHostIsNormalizedToLowercase(): void |
|||
{ |
|||
$uri = new Uri('//eXaMpLe.CoM'); |
|||
|
|||
self::assertSame('example.com', $uri->getHost()); |
|||
self::assertSame('//example.com', (string) $uri); |
|||
|
|||
$uri = (new Uri())->withHost('eXaMpLe.CoM'); |
|||
|
|||
self::assertSame('example.com', $uri->getHost()); |
|||
self::assertSame('//example.com', (string) $uri); |
|||
} |
|||
|
|||
public function testPortIsNullIfStandardPortForScheme(): void |
|||
{ |
|||
// HTTPS standard port |
|||
$uri = new Uri('https://example.com:443'); |
|||
self::assertNull($uri->getPort()); |
|||
self::assertSame('example.com', $uri->getAuthority()); |
|||
|
|||
$uri = (new Uri('https://example.com'))->withPort(443); |
|||
self::assertNull($uri->getPort()); |
|||
self::assertSame('example.com', $uri->getAuthority()); |
|||
|
|||
// HTTP standard port |
|||
$uri = new Uri('http://example.com:80'); |
|||
self::assertNull($uri->getPort()); |
|||
self::assertSame('example.com', $uri->getAuthority()); |
|||
|
|||
$uri = (new Uri('http://example.com'))->withPort(80); |
|||
self::assertNull($uri->getPort()); |
|||
self::assertSame('example.com', $uri->getAuthority()); |
|||
} |
|||
|
|||
public function testPortIsReturnedIfSchemeUnknown(): void |
|||
{ |
|||
$uri = (new Uri('//example.com'))->withPort(80); |
|||
|
|||
self::assertSame(80, $uri->getPort()); |
|||
self::assertSame('example.com:80', $uri->getAuthority()); |
|||
} |
|||
|
|||
public function testStandardPortIsNullIfSchemeChanges(): void |
|||
{ |
|||
$uri = new Uri('http://example.com:443'); |
|||
self::assertSame('http', $uri->getScheme()); |
|||
self::assertSame(443, $uri->getPort()); |
|||
|
|||
$uri = $uri->withScheme('https'); |
|||
self::assertNull($uri->getPort()); |
|||
} |
|||
|
|||
public function testPortPassedAsStringIsCastedToInt(): void |
|||
{ |
|||
$uri = (new Uri('//example.com'))->withPort('8080'); |
|||
|
|||
self::assertSame(8080, $uri->getPort(), 'Port is returned as integer'); |
|||
self::assertSame('example.com:8080', $uri->getAuthority()); |
|||
} |
|||
|
|||
public function testPortCanBeRemoved(): void |
|||
{ |
|||
$uri = (new Uri('http://example.com:8080'))->withPort(null); |
|||
|
|||
self::assertNull($uri->getPort()); |
|||
self::assertSame('http://example.com', (string) $uri); |
|||
} |
|||
|
|||
/** |
|||
* In RFC 8986 the host is optional and the authority can only |
|||
* consist of the user info and port. |
|||
*/ |
|||
public function testAuthorityWithUserInfoOrPortButWithoutHost(): void |
|||
{ |
|||
$uri = (new Uri())->withUserInfo('user', 'pass'); |
|||
|
|||
self::assertSame('user:pass', $uri->getUserInfo()); |
|||
self::assertSame('user:pass@', $uri->getAuthority()); |
|||
|
|||
$uri = $uri->withPort(8080); |
|||
self::assertSame(8080, $uri->getPort()); |
|||
self::assertSame('user:pass@:8080', $uri->getAuthority()); |
|||
self::assertSame('//user:pass@:8080', (string) $uri); |
|||
|
|||
$uri = $uri->withUserInfo(''); |
|||
self::assertSame(':8080', $uri->getAuthority()); |
|||
} |
|||
|
|||
public function testHostInHttpUriDefaultsToLocalhost(): void |
|||
{ |
|||
$uri = (new Uri())->withScheme('http'); |
|||
|
|||
self::assertSame('localhost', $uri->getHost()); |
|||
self::assertSame('localhost', $uri->getAuthority()); |
|||
self::assertSame('http://localhost', (string) $uri); |
|||
} |
|||
|
|||
public function testHostInHttpsUriDefaultsToLocalhost(): void |
|||
{ |
|||
$uri = (new Uri())->withScheme('https'); |
|||
|
|||
self::assertSame('localhost', $uri->getHost()); |
|||
self::assertSame('localhost', $uri->getAuthority()); |
|||
self::assertSame('https://localhost', (string) $uri); |
|||
} |
|||
|
|||
public function testFileSchemeWithEmptyHostReconstruction(): void |
|||
{ |
|||
$uri = new Uri('file:///tmp/filename.ext'); |
|||
|
|||
self::assertSame('', $uri->getHost()); |
|||
self::assertSame('', $uri->getAuthority()); |
|||
self::assertSame('file:///tmp/filename.ext', (string) $uri); |
|||
} |
|||
|
|||
public function uriComponentsEncodingProvider(): iterable |
|||
{ |
|||
$unreserved = 'a-zA-Z0-9.-_~!$&\'()*+,;=:@'; |
|||
|
|||
return [ |
|||
// Percent encode spaces |
|||
['/pa th?q=va lue#frag ment', '/pa%20th', 'q=va%20lue', 'frag%20ment', '/pa%20th?q=va%20lue#frag%20ment'], |
|||
// Percent encode multibyte |
|||
['/€?€#€', '/%E2%82%AC', '%E2%82%AC', '%E2%82%AC', '/%E2%82%AC?%E2%82%AC#%E2%82%AC'], |
|||
// Don't encode something that's already encoded |
|||
['/pa%20th?q=va%20lue#frag%20ment', '/pa%20th', 'q=va%20lue', 'frag%20ment', '/pa%20th?q=va%20lue#frag%20ment'], |
|||
// Percent encode invalid percent encodings |
|||
['/pa%2-th?q=va%2-lue#frag%2-ment', '/pa%252-th', 'q=va%252-lue', 'frag%252-ment', '/pa%252-th?q=va%252-lue#frag%252-ment'], |
|||
// Don't encode path segments |
|||
['/pa/th//two?q=va/lue#frag/ment', '/pa/th//two', 'q=va/lue', 'frag/ment', '/pa/th//two?q=va/lue#frag/ment'], |
|||
// Don't encode unreserved chars or sub-delimiters |
|||
["/$unreserved?$unreserved#$unreserved", "/$unreserved", $unreserved, $unreserved, "/$unreserved?$unreserved#$unreserved"], |
|||
// Encoded unreserved chars are not decoded |
|||
['/p%61th?q=v%61lue#fr%61gment', '/p%61th', 'q=v%61lue', 'fr%61gment', '/p%61th?q=v%61lue#fr%61gment'], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider uriComponentsEncodingProvider |
|||
*/ |
|||
public function testUriComponentsGetEncodedProperly(string $input, string $path, string $query, string $fragment, string $output): void |
|||
{ |
|||
$uri = new Uri($input); |
|||
self::assertSame($path, $uri->getPath()); |
|||
self::assertSame($query, $uri->getQuery()); |
|||
self::assertSame($fragment, $uri->getFragment()); |
|||
self::assertSame($output, (string) $uri); |
|||
} |
|||
|
|||
public function testWithPathEncodesProperly(): void |
|||
{ |
|||
$uri = (new Uri())->withPath('/baz?#€/b%61r'); |
|||
// Query and fragment delimiters and multibyte chars are encoded. |
|||
self::assertSame('/baz%3F%23%E2%82%AC/b%61r', $uri->getPath()); |
|||
self::assertSame('/baz%3F%23%E2%82%AC/b%61r', (string) $uri); |
|||
} |
|||
|
|||
public function testWithQueryEncodesProperly(): void |
|||
{ |
|||
$uri = (new Uri())->withQuery('?=#&€=/&b%61r'); |
|||
// A query starting with a "?" is valid and must not be magically removed. Otherwise it would be impossible to |
|||
// construct such an URI. Also the "?" and "/" does not need to be encoded in the query. |
|||
self::assertSame('?=%23&%E2%82%AC=/&b%61r', $uri->getQuery()); |
|||
self::assertSame('??=%23&%E2%82%AC=/&b%61r', (string) $uri); |
|||
} |
|||
|
|||
public function testWithFragmentEncodesProperly(): void |
|||
{ |
|||
$uri = (new Uri())->withFragment('#€?/b%61r'); |
|||
// A fragment starting with a "#" is valid and must not be magically removed. Otherwise it would be impossible to |
|||
// construct such an URI. Also the "?" and "/" does not need to be encoded in the fragment. |
|||
self::assertSame('%23%E2%82%AC?/b%61r', $uri->getFragment()); |
|||
self::assertSame('#%23%E2%82%AC?/b%61r', (string) $uri); |
|||
} |
|||
|
|||
public function testAllowsForRelativeUri(): void |
|||
{ |
|||
$uri = (new Uri())->withPath('foo'); |
|||
self::assertSame('foo', $uri->getPath()); |
|||
self::assertSame('foo', (string) $uri); |
|||
} |
|||
|
|||
public function testRelativePathAndAuthority(): void |
|||
{ |
|||
$uri = (new Uri())->withHost('example.com')->withPath('foo'); |
|||
self::assertSame('foo', $uri->getPath()); |
|||
self::assertSame('//example.com/foo', $uri->__toString()); |
|||
} |
|||
|
|||
public function testPathStartingWithTwoSlashesAndNoAuthorityIsInvalid(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('The path of a URI without an authority must not start with two slashes "//"'); |
|||
// URI "//foo" would be interpreted as network reference and thus change the original path to the host |
|||
(new Uri())->withPath('//foo'); |
|||
} |
|||
|
|||
public function testPathStartingWithTwoSlashes(): void |
|||
{ |
|||
$uri = new Uri('http://example.org//path-not-host.com'); |
|||
self::assertSame('//path-not-host.com', $uri->getPath()); |
|||
|
|||
$uri = $uri->withScheme(''); |
|||
self::assertSame('//example.org//path-not-host.com', (string) $uri); // This is still valid |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$uri->withHost(''); // Now it becomes invalid |
|||
} |
|||
|
|||
public function testRelativeUriWithPathBeginngWithColonSegmentIsInvalid(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
$this->expectExceptionMessage('A relative URI must not have a path beginning with a segment containing a colon'); |
|||
(new Uri())->withPath('mailto:foo'); |
|||
} |
|||
|
|||
public function testRelativeUriWithPathHavingColonSegment(): void |
|||
{ |
|||
$uri = (new Uri('urn:/mailto:foo'))->withScheme(''); |
|||
self::assertSame('/mailto:foo', $uri->getPath()); |
|||
|
|||
$this->expectException(\InvalidArgumentException::class); |
|||
(new Uri('urn:mailto:foo'))->withScheme(''); |
|||
} |
|||
|
|||
public function testDefaultReturnValuesOfGetters(): void |
|||
{ |
|||
$uri = new Uri(); |
|||
|
|||
self::assertSame('', $uri->getScheme()); |
|||
self::assertSame('', $uri->getAuthority()); |
|||
self::assertSame('', $uri->getUserInfo()); |
|||
self::assertSame('', $uri->getHost()); |
|||
self::assertNull($uri->getPort()); |
|||
self::assertSame('', $uri->getPath()); |
|||
self::assertSame('', $uri->getQuery()); |
|||
self::assertSame('', $uri->getFragment()); |
|||
} |
|||
|
|||
public function testImmutability(): void |
|||
{ |
|||
$uri = new Uri(); |
|||
|
|||
self::assertNotSame($uri, $uri->withScheme('https')); |
|||
self::assertNotSame($uri, $uri->withUserInfo('user', 'pass')); |
|||
self::assertNotSame($uri, $uri->withHost('example.com')); |
|||
self::assertNotSame($uri, $uri->withPort(8080)); |
|||
self::assertNotSame($uri, $uri->withPath('/path/123')); |
|||
self::assertNotSame($uri, $uri->withQuery('q=abc')); |
|||
self::assertNotSame($uri, $uri->withFragment('test')); |
|||
} |
|||
|
|||
public function testExtendingClassesInstantiates(): void |
|||
{ |
|||
// The non-standard port triggers a cascade of private methods which |
|||
// should not use late static binding to access private static members. |
|||
// If they do, this will fatal. |
|||
self::assertInstanceOf( |
|||
ExtendedUriTest::class, |
|||
new ExtendedUriTest('http://h:9/') |
|||
); |
|||
} |
|||
|
|||
public function testSpecialCharsOfUserInfo(): void |
|||
{ |
|||
// The `userInfo` must always be URL-encoded. |
|||
$uri = (new Uri())->withUserInfo('foo@bar.com', 'pass#word'); |
|||
self::assertSame('foo%40bar.com:pass%23word', $uri->getUserInfo()); |
|||
|
|||
// The `userInfo` can already be URL-encoded: it should not be encoded twice. |
|||
$uri = (new Uri())->withUserInfo('foo%40bar.com', 'pass%23word'); |
|||
self::assertSame('foo%40bar.com:pass%23word', $uri->getUserInfo()); |
|||
} |
|||
|
|||
public function testInternationalizedDomainName(): void |
|||
{ |
|||
$uri = new Uri('https://яндекс.рф'); |
|||
self::assertSame('яндекс.рф', $uri->getHost()); |
|||
|
|||
$uri = new Uri('https://яндекAс.рф'); |
|||
self::assertSame('яндекaс.рф', $uri->getHost()); |
|||
} |
|||
|
|||
public function testIPv6Host(): void |
|||
{ |
|||
$uri = new Uri('https://[2a00:f48:1008::212:183:10]'); |
|||
self::assertSame('[2a00:f48:1008::212:183:10]', $uri->getHost()); |
|||
|
|||
$uri = new Uri('http://[2a00:f48:1008::212:183:10]:56?foo=bar'); |
|||
self::assertSame('[2a00:f48:1008::212:183:10]', $uri->getHost()); |
|||
self::assertSame(56, $uri->getPort()); |
|||
self::assertSame('foo=bar', $uri->getQuery()); |
|||
} |
|||
|
|||
public function testJsonSerializable(): void |
|||
{ |
|||
$uri = new Uri('https://example.com'); |
|||
|
|||
self::assertSame('{"uri":"https:\/\/example.com"}', \json_encode(['uri' => $uri])); |
|||
} |
|||
} |
|||
|
|||
class ExtendedUriTest extends Uri |
|||
{ |
|||
} |
@ -1,551 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace GuzzleHttp\Tests\Psr7; |
|||
|
|||
use GuzzleHttp\Psr7; |
|||
use GuzzleHttp\Psr7\FnStream; |
|||
use GuzzleHttp\Psr7\NoSeekStream; |
|||
use PHPUnit\Framework\TestCase; |
|||
use Psr\Http\Message\StreamInterface; |
|||
|
|||
class UtilsTest extends TestCase |
|||
{ |
|||
public function testCopiesToString(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor('foobaz'); |
|||
self::assertSame('foobaz', Psr7\Utils::copyToString($s)); |
|||
$s->seek(0); |
|||
self::assertSame('foo', Psr7\Utils::copyToString($s, 3)); |
|||
self::assertSame('baz', Psr7\Utils::copyToString($s, 3)); |
|||
self::assertSame('', Psr7\Utils::copyToString($s)); |
|||
} |
|||
|
|||
public function testCopiesToStringStopsWhenReadFails(): void |
|||
{ |
|||
$s1 = Psr7\Utils::streamFor('foobaz'); |
|||
$s1 = FnStream::decorate($s1, [ |
|||
'read' => function () { |
|||
return ''; |
|||
}, |
|||
]); |
|||
$result = Psr7\Utils::copyToString($s1); |
|||
self::assertSame('', $result); |
|||
} |
|||
|
|||
public function testCopiesToStream(): void |
|||
{ |
|||
$s1 = Psr7\Utils::streamFor('foobaz'); |
|||
$s2 = Psr7\Utils::streamFor(''); |
|||
Psr7\Utils::copyToStream($s1, $s2); |
|||
self::assertSame('foobaz', (string) $s2); |
|||
$s2 = Psr7\Utils::streamFor(''); |
|||
$s1->seek(0); |
|||
Psr7\Utils::copyToStream($s1, $s2, 3); |
|||
self::assertSame('foo', (string) $s2); |
|||
Psr7\Utils::copyToStream($s1, $s2, 3); |
|||
self::assertSame('foobaz', (string) $s2); |
|||
} |
|||
|
|||
public function testStopsCopyToStreamWhenWriteFails(): void |
|||
{ |
|||
$s1 = Psr7\Utils::streamFor('foobaz'); |
|||
$s2 = Psr7\Utils::streamFor(''); |
|||
$s2 = FnStream::decorate($s2, [ |
|||
'write' => function () { |
|||
return 0; |
|||
}, |
|||
]); |
|||
Psr7\Utils::copyToStream($s1, $s2); |
|||
self::assertSame('', (string) $s2); |
|||
} |
|||
|
|||
public function testStopsCopyToSteamWhenWriteFailsWithMaxLen(): void |
|||
{ |
|||
$s1 = Psr7\Utils::streamFor('foobaz'); |
|||
$s2 = Psr7\Utils::streamFor(''); |
|||
$s2 = FnStream::decorate($s2, [ |
|||
'write' => function () { |
|||
return 0; |
|||
}, |
|||
]); |
|||
Psr7\Utils::copyToStream($s1, $s2, 10); |
|||
self::assertSame('', (string) $s2); |
|||
} |
|||
|
|||
public function testCopyToStreamReadsInChunksInsteadOfAllInMemory(): void |
|||
{ |
|||
$sizes = []; |
|||
$s1 = new FnStream([ |
|||
'eof' => function () { |
|||
return false; |
|||
}, |
|||
'read' => function ($size) use (&$sizes) { |
|||
$sizes[] = $size; |
|||
|
|||
return str_repeat('.', $size); |
|||
}, |
|||
]); |
|||
$s2 = Psr7\Utils::streamFor(''); |
|||
Psr7\Utils::copyToStream($s1, $s2, 16394); |
|||
$s2->seek(0); |
|||
self::assertSame(16394, strlen($s2->getContents())); |
|||
self::assertSame(8192, $sizes[0]); |
|||
self::assertSame(8192, $sizes[1]); |
|||
self::assertSame(10, $sizes[2]); |
|||
} |
|||
|
|||
public function testStopsCopyToSteamWhenReadFailsWithMaxLen(): void |
|||
{ |
|||
$s1 = Psr7\Utils::streamFor('foobaz'); |
|||
$s1 = FnStream::decorate($s1, [ |
|||
'read' => function () { |
|||
return ''; |
|||
}, |
|||
]); |
|||
$s2 = Psr7\Utils::streamFor(''); |
|||
Psr7\Utils::copyToStream($s1, $s2, 10); |
|||
self::assertSame('', (string) $s2); |
|||
} |
|||
|
|||
public function testReadsLines(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor("foo\nbaz\nbar"); |
|||
self::assertSame("foo\n", Psr7\Utils::readLine($s)); |
|||
self::assertSame("baz\n", Psr7\Utils::readLine($s)); |
|||
self::assertSame('bar', Psr7\Utils::readLine($s)); |
|||
} |
|||
|
|||
public function testReadsLinesUpToMaxLength(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor("12345\n"); |
|||
self::assertSame('123', Psr7\Utils::readLine($s, 4)); |
|||
self::assertSame("45\n", Psr7\Utils::readLine($s)); |
|||
} |
|||
|
|||
public function testReadLinesEof(): void |
|||
{ |
|||
// Should return empty string on EOF |
|||
$s = Psr7\Utils::streamFor("foo\nbar"); |
|||
while (!$s->eof()) { |
|||
Psr7\Utils::readLine($s); |
|||
} |
|||
self::assertSame('', Psr7\Utils::readLine($s)); |
|||
} |
|||
|
|||
public function testReadsLineUntilEmptyStringReturnedFromRead(): void |
|||
{ |
|||
$s = $this->createMock(StreamInterface::class); |
|||
$s->expects(self::exactly(2)) |
|||
->method('read') |
|||
->willReturnCallback(function () { |
|||
static $called = false; |
|||
if ($called) { |
|||
return ''; |
|||
} |
|||
$called = true; |
|||
|
|||
return 'h'; |
|||
}); |
|||
$s->expects(self::exactly(2)) |
|||
->method('eof') |
|||
->willReturn(false); |
|||
self::assertSame('h', Psr7\Utils::readLine($s)); |
|||
} |
|||
|
|||
public function testRedactUserInfo(): void |
|||
{ |
|||
$uri = new Psr7\Uri('http://my_user:secretPass@localhost/'); |
|||
|
|||
$redactedUri = Psr7\Utils::redactUserInfo($uri); |
|||
|
|||
self::assertSame('http://my_user:***@localhost/', (string) $redactedUri); |
|||
} |
|||
|
|||
public function testCalculatesHash(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor('foobazbar'); |
|||
self::assertSame(md5('foobazbar'), Psr7\Utils::hash($s, 'md5')); |
|||
} |
|||
|
|||
public function testCalculatesHashThrowsWhenSeekFails(): void |
|||
{ |
|||
$s = new NoSeekStream(Psr7\Utils::streamFor('foobazbar')); |
|||
$s->read(2); |
|||
|
|||
$this->expectException(\RuntimeException::class); |
|||
|
|||
Psr7\Utils::hash($s, 'md5'); |
|||
} |
|||
|
|||
public function testCalculatesHashSeeksToOriginalPosition(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor('foobazbar'); |
|||
$s->seek(4); |
|||
self::assertSame(md5('foobazbar'), Psr7\Utils::hash($s, 'md5')); |
|||
self::assertSame(4, $s->tell()); |
|||
} |
|||
|
|||
public function testOpensFilesSuccessfully(): void |
|||
{ |
|||
$r = Psr7\Utils::tryFopen(__FILE__, 'r'); |
|||
self::assertIsResource($r); |
|||
fclose($r); |
|||
} |
|||
|
|||
public function testThrowsExceptionNotWarning(): void |
|||
{ |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Unable to open "/path/to/does/not/exist" using mode "r"'); |
|||
|
|||
Psr7\Utils::tryFopen('/path/to/does/not/exist', 'r'); |
|||
} |
|||
|
|||
public function testThrowsExceptionNotValueError(): void |
|||
{ |
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Unable to open "" using mode "r"'); |
|||
|
|||
Psr7\Utils::tryFopen('', 'r'); |
|||
} |
|||
|
|||
/** |
|||
* @requires PHP 7.4 |
|||
*/ |
|||
public function testGetsContentsThrowExceptionWhenNotReadable(): void |
|||
{ |
|||
$r = fopen(tempnam(sys_get_temp_dir(), 'guzzle-psr7-'), 'w'); |
|||
fwrite($r, 'hello world!'); |
|||
|
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Unable to read stream contents'); |
|||
|
|||
try { |
|||
Psr7\Utils::tryGetContents($r); |
|||
} finally { |
|||
fclose($r); |
|||
} |
|||
} |
|||
|
|||
public function testGetsContentsThrowExceptionWhenCLosed(): void |
|||
{ |
|||
$r = fopen(tempnam(sys_get_temp_dir(), 'guzzle-psr7-'), 'r+'); |
|||
fclose($r); |
|||
|
|||
$this->expectException(\RuntimeException::class); |
|||
$this->expectExceptionMessage('Unable to read stream contents'); |
|||
|
|||
Psr7\Utils::tryGetContents($r); |
|||
} |
|||
|
|||
public function testCreatesUriForValue(): void |
|||
{ |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\Uri', Psr7\Utils::uriFor('/foo')); |
|||
self::assertInstanceOf( |
|||
'GuzzleHttp\Psr7\Uri', |
|||
Psr7\Utils::uriFor(new Psr7\Uri('/foo')) |
|||
); |
|||
} |
|||
|
|||
public function testValidatesUri(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
|
|||
Psr7\Utils::uriFor([]); |
|||
} |
|||
|
|||
public function testKeepsPositionOfResource(): void |
|||
{ |
|||
$h = fopen(__FILE__, 'r'); |
|||
fseek($h, 10); |
|||
$stream = Psr7\Utils::streamFor($h); |
|||
self::assertSame(10, $stream->tell()); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testCreatesWithFactory(): void |
|||
{ |
|||
$stream = Psr7\Utils::streamFor('foo'); |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $stream); |
|||
self::assertSame('foo', $stream->getContents()); |
|||
$stream->close(); |
|||
} |
|||
|
|||
public function testFactoryCreatesFromEmptyString(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor(); |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $s); |
|||
} |
|||
|
|||
public function testFactoryCreatesFromNull(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor(null); |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $s); |
|||
} |
|||
|
|||
public function testFactoryCreatesFromResource(): void |
|||
{ |
|||
$r = fopen(__FILE__, 'r'); |
|||
$s = Psr7\Utils::streamFor($r); |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $s); |
|||
self::assertSame(file_get_contents(__FILE__), (string) $s); |
|||
} |
|||
|
|||
public function testFactoryCreatesFromObjectWithToString(): void |
|||
{ |
|||
$r = new HasToString(); |
|||
$s = Psr7\Utils::streamFor($r); |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $s); |
|||
self::assertSame('foo', (string) $s); |
|||
} |
|||
|
|||
public function testCreatePassesThrough(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor('foo'); |
|||
self::assertSame($s, Psr7\Utils::streamFor($s)); |
|||
} |
|||
|
|||
public function testThrowsExceptionForUnknown(): void |
|||
{ |
|||
$this->expectException(\InvalidArgumentException::class); |
|||
|
|||
Psr7\Utils::streamFor(new \stdClass()); |
|||
} |
|||
|
|||
public function testReturnsCustomMetadata(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor('foo', ['metadata' => ['hwm' => 3]]); |
|||
self::assertSame(3, $s->getMetadata('hwm')); |
|||
self::assertArrayHasKey('hwm', $s->getMetadata()); |
|||
} |
|||
|
|||
public function testCanSetSize(): void |
|||
{ |
|||
$s = Psr7\Utils::streamFor('', ['size' => 10]); |
|||
self::assertSame(10, $s->getSize()); |
|||
} |
|||
|
|||
public function testCanCreateIteratorBasedStream(): void |
|||
{ |
|||
$a = new \ArrayIterator(['foo', 'bar', '123']); |
|||
$p = Psr7\Utils::streamFor($a); |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\PumpStream', $p); |
|||
self::assertSame('foo', $p->read(3)); |
|||
self::assertFalse($p->eof()); |
|||
self::assertSame('b', $p->read(1)); |
|||
self::assertSame('a', $p->read(1)); |
|||
self::assertSame('r12', $p->read(3)); |
|||
self::assertFalse($p->eof()); |
|||
self::assertSame('3', $p->getContents()); |
|||
self::assertTrue($p->eof()); |
|||
self::assertSame(9, $p->tell()); |
|||
} |
|||
|
|||
public function testConvertsRequestsToStrings(): void |
|||
{ |
|||
$request = new Psr7\Request('PUT', 'http://foo.com/hi?123', [ |
|||
'Baz' => 'bar', |
|||
'Qux' => 'ipsum', |
|||
], 'hello', '1.0'); |
|||
self::assertSame( |
|||
"PUT /hi?123 HTTP/1.0\r\nHost: foo.com\r\nBaz: bar\r\nQux: ipsum\r\n\r\nhello", |
|||
Psr7\Message::toString($request) |
|||
); |
|||
} |
|||
|
|||
public function testConvertsResponsesToStrings(): void |
|||
{ |
|||
$response = new Psr7\Response(200, [ |
|||
'Baz' => 'bar', |
|||
'Qux' => 'ipsum', |
|||
], 'hello', '1.0', 'FOO'); |
|||
self::assertSame( |
|||
"HTTP/1.0 200 FOO\r\nBaz: bar\r\nQux: ipsum\r\n\r\nhello", |
|||
Psr7\Message::toString($response) |
|||
); |
|||
} |
|||
|
|||
public function testCorrectlyRendersSetCookieHeadersToString(): void |
|||
{ |
|||
$response = new Psr7\Response(200, [ |
|||
'Set-Cookie' => ['bar', 'baz', 'qux'], |
|||
], 'hello', '1.0', 'FOO'); |
|||
self::assertSame( |
|||
"HTTP/1.0 200 FOO\r\nSet-Cookie: bar\r\nSet-Cookie: baz\r\nSet-Cookie: qux\r\n\r\nhello", |
|||
Psr7\Message::toString($response) |
|||
); |
|||
} |
|||
|
|||
public function testCanModifyRequestWithUri(): void |
|||
{ |
|||
$r1 = new Psr7\Request('GET', 'http://foo.com'); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, [ |
|||
'uri' => new Psr7\Uri('http://www.foo.com'), |
|||
]); |
|||
self::assertSame('http://www.foo.com', (string) $r2->getUri()); |
|||
self::assertSame('www.foo.com', (string) $r2->getHeaderLine('host')); |
|||
} |
|||
|
|||
public function testCanModifyRequestWithUriAndPort(): void |
|||
{ |
|||
$r1 = new Psr7\Request('GET', 'http://foo.com:8000'); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, [ |
|||
'uri' => new Psr7\Uri('http://www.foo.com:8000'), |
|||
]); |
|||
self::assertSame('http://www.foo.com:8000', (string) $r2->getUri()); |
|||
self::assertSame('www.foo.com:8000', (string) $r2->getHeaderLine('host')); |
|||
} |
|||
|
|||
public function testCanModifyRequestWithCaseInsensitiveHeader(): void |
|||
{ |
|||
$r1 = new Psr7\Request('GET', 'http://foo.com', ['User-Agent' => 'foo']); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, ['set_headers' => ['User-agent' => 'bar']]); |
|||
self::assertSame('bar', $r2->getHeaderLine('User-Agent')); |
|||
self::assertSame('bar', $r2->getHeaderLine('User-agent')); |
|||
} |
|||
|
|||
public function testReturnsAsIsWhenNoChanges(): void |
|||
{ |
|||
$r1 = new Psr7\Request('GET', 'http://foo.com'); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, []); |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\Request', $r2); |
|||
|
|||
$r1 = new Psr7\ServerRequest('GET', 'http://foo.com'); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, []); |
|||
self::assertInstanceOf('Psr\Http\Message\ServerRequestInterface', $r2); |
|||
} |
|||
|
|||
public function testReturnsUriAsIsWhenNoChanges(): void |
|||
{ |
|||
$r1 = new Psr7\Request('GET', 'http://foo.com'); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, ['set_headers' => ['foo' => 'bar']]); |
|||
self::assertNotSame($r1, $r2); |
|||
self::assertSame('bar', $r2->getHeaderLine('foo')); |
|||
} |
|||
|
|||
public function testRemovesHeadersFromMessage(): void |
|||
{ |
|||
$r1 = new Psr7\Request('GET', 'http://foo.com', ['foo' => 'bar']); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, ['remove_headers' => ['foo']]); |
|||
self::assertNotSame($r1, $r2); |
|||
self::assertFalse($r2->hasHeader('foo')); |
|||
} |
|||
|
|||
public function testAddsQueryToUri(): void |
|||
{ |
|||
$r1 = new Psr7\Request('GET', 'http://foo.com'); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, ['query' => 'foo=bar']); |
|||
self::assertNotSame($r1, $r2); |
|||
self::assertSame('foo=bar', $r2->getUri()->getQuery()); |
|||
} |
|||
|
|||
public function testModifyRequestKeepInstanceOfRequest(): void |
|||
{ |
|||
$r1 = new Psr7\Request('GET', 'http://foo.com'); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, ['remove_headers' => ['non-existent']]); |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\Request', $r2); |
|||
|
|||
$r1 = new Psr7\ServerRequest('GET', 'http://foo.com'); |
|||
$r2 = Psr7\Utils::modifyRequest($r1, ['remove_headers' => ['non-existent']]); |
|||
self::assertInstanceOf('Psr\Http\Message\ServerRequestInterface', $r2); |
|||
} |
|||
|
|||
public function testModifyServerRequestWithUploadedFiles(): void |
|||
{ |
|||
$request = new Psr7\ServerRequest('GET', 'http://example.com/bla'); |
|||
$file = new Psr7\UploadedFile('Test', 100, \UPLOAD_ERR_OK); |
|||
$request = $request->withUploadedFiles([$file]); |
|||
|
|||
/** @var Psr7\ServerRequest $modifiedRequest */ |
|||
$modifiedRequest = Psr7\Utils::modifyRequest($request, ['set_headers' => ['foo' => 'bar']]); |
|||
|
|||
self::assertCount(1, $modifiedRequest->getUploadedFiles()); |
|||
|
|||
$files = $modifiedRequest->getUploadedFiles(); |
|||
self::assertInstanceOf('GuzzleHttp\Psr7\UploadedFile', $files[0]); |
|||
} |
|||
|
|||
public function testModifyServerRequestWithCookies(): void |
|||
{ |
|||
$request = (new Psr7\ServerRequest('GET', 'http://example.com/bla')) |
|||
->withCookieParams(['name' => 'value']); |
|||
|
|||
/** @var Psr7\ServerRequest $modifiedRequest */ |
|||
$modifiedRequest = Psr7\Utils::modifyRequest($request, ['set_headers' => ['foo' => 'bar']]); |
|||
|
|||
self::assertSame(['name' => 'value'], $modifiedRequest->getCookieParams()); |
|||
} |
|||
|
|||
public function testModifyServerRequestParsedBody(): void |
|||
{ |
|||
$request = (new Psr7\ServerRequest('GET', 'http://example.com/bla')) |
|||
->withParsedBody(['name' => 'value']); |
|||
|
|||
/** @var Psr7\ServerRequest $modifiedRequest */ |
|||
$modifiedRequest = Psr7\Utils::modifyRequest($request, ['set_headers' => ['foo' => 'bar']]); |
|||
|
|||
self::assertSame(['name' => 'value'], $modifiedRequest->getParsedBody()); |
|||
} |
|||
|
|||
public function testModifyServerRequestQueryParams(): void |
|||
{ |
|||
$request = (new Psr7\ServerRequest('GET', 'http://example.com/bla')) |
|||
->withQueryParams(['name' => 'value']); |
|||
|
|||
/** @var Psr7\ServerRequest $modifiedRequest */ |
|||
$modifiedRequest = Psr7\Utils::modifyRequest($request, ['set_headers' => ['foo' => 'bar']]); |
|||
|
|||
self::assertSame(['name' => 'value'], $modifiedRequest->getQueryParams()); |
|||
} |
|||
|
|||
public function testModifyServerRequestRetainsAttributes(): void |
|||
{ |
|||
$request = (new Psr7\ServerRequest('GET', 'http://example.com/bla')) |
|||
->withAttribute('foo', 'bar'); |
|||
|
|||
/** @var Psr7\ServerRequest $modifiedRequest */ |
|||
$modifiedRequest = Psr7\Utils::modifyRequest($request, []); |
|||
|
|||
self::assertSame(['foo' => 'bar'], $modifiedRequest->getAttributes()); |
|||
} |
|||
|
|||
/** |
|||
* @return list<array{0: string[], 1: array, 2: array}> |
|||
*/ |
|||
public function providesCaselessRemoveCases(): array |
|||
{ |
|||
return [ |
|||
[ |
|||
['foo-bar'], |
|||
['Foo-Bar' => 'hello'], |
|||
[], |
|||
], |
|||
[ |
|||
['foo-bar'], |
|||
['hello'], |
|||
['hello'], |
|||
], |
|||
[ |
|||
['foo-Bar'], |
|||
['Foo-Bar' => 'hello', 123 => '', 'Foo-BAR' => 'hello123', 'foobar' => 'baz'], |
|||
[123 => '', 'foobar' => 'baz'], |
|||
], |
|||
[ |
|||
['foo-Bar', 123], |
|||
['Foo-Bar' => 'hello', 123 => '', 'Foo-BAR' => 'hello123', 'foobar' => 'baz'], |
|||
['foobar' => 'baz'], |
|||
], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider providesCaselessRemoveCases |
|||
* |
|||
* @param string[] $keys |
|||
*/ |
|||
public function testCaselessRemove(array $keys, array $data, array $expected): void |
|||
{ |
|||
self::assertSame($expected, Psr7\Utils::caselessRemove($keys, $data)); |
|||
} |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue