Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • dmj/selbstzeugnisse-hab
  • goermar/selbstzeugnisse-hab
2 results
Show changes
Showing
with 926 additions and 384 deletions
<?php
namespace GuzzleHttp\Promise;
/**
......
<?php
namespace GuzzleHttp\Promise;
/**
......
<?php
namespace GuzzleHttp\Promise;
use Exception;
......@@ -9,7 +10,7 @@ use Throwable;
* Creates a promise that is resolved using a generator that yields values or
* promises (somewhat similar to C#'s async keyword).
*
* When called, the coroutine function will start an instance of the generator
* When called, the Coroutine::of method will start an instance of the generator
* and returns a promise that is fulfilled with its final yielded value.
*
* Control is returned back to the generator when the yielded promise settles.
......@@ -22,7 +23,7 @@ use Throwable;
* return new Promise\FulfilledPromise($value);
* }
*
* $promise = Promise\coroutine(function () {
* $promise = Promise\Coroutine::of(function () {
* $value = (yield createPromise('a'));
* try {
* $value = (yield createPromise($value . 'b'));
......@@ -38,6 +39,7 @@ use Throwable;
* @param callable $generatorFn Generator function to wrap into a promise.
*
* @return Promise
*
* @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
*/
final class Coroutine implements PromiseInterface
......@@ -65,7 +67,23 @@ final class Coroutine implements PromiseInterface
$this->currentPromise->wait();
}
});
$this->nextCoroutine($this->generator->current());
try {
$this->nextCoroutine($this->generator->current());
} catch (\Exception $exception) {
$this->result->reject($exception);
} catch (Throwable $throwable) {
$this->result->reject($throwable);
}
}
/**
* Create a new coroutine.
*
* @return self
*/
public static function of(callable $generatorFn)
{
return new self($generatorFn);
}
public function then(
......@@ -108,7 +126,7 @@ final class Coroutine implements PromiseInterface
private function nextCoroutine($yielded)
{
$this->currentPromise = promise_for($yielded)
$this->currentPromise = Create::promiseFor($yielded)
->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
}
......@@ -139,7 +157,7 @@ final class Coroutine implements PromiseInterface
{
unset($this->currentPromise);
try {
$nextYield = $this->generator->throw(exception_for($reason));
$nextYield = $this->generator->throw(Create::exceptionFor($reason));
// The throw was caught, so keep iterating on the coroutine
$this->nextCoroutine($nextYield);
} catch (Exception $exception) {
......
<?php
namespace GuzzleHttp\Promise;
final class Create
{
/**
* Creates a promise for a value if the value is not a promise.
*
* @param mixed $value Promise or value.
*
* @return PromiseInterface
*/
public static function promiseFor($value)
{
if ($value instanceof PromiseInterface) {
return $value;
}
// Return a Guzzle promise that shadows the given promise.
if (is_object($value) && method_exists($value, 'then')) {
$wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
$cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
$promise = new Promise($wfn, $cfn);
$value->then([$promise, 'resolve'], [$promise, 'reject']);
return $promise;
}
return new FulfilledPromise($value);
}
/**
* Creates a rejected promise for a reason if the reason is not a promise.
* If the provided reason is a promise, then it is returned as-is.
*
* @param mixed $reason Promise or reason.
*
* @return PromiseInterface
*/
public static function rejectionFor($reason)
{
if ($reason instanceof PromiseInterface) {
return $reason;
}
return new RejectedPromise($reason);
}
/**
* Create an exception for a rejected promise value.
*
* @param mixed $reason
*
* @return \Exception|\Throwable
*/
public static function exceptionFor($reason)
{
if ($reason instanceof \Exception || $reason instanceof \Throwable) {
return $reason;
}
return new RejectionException($reason);
}
/**
* Returns an iterator for the given value.
*
* @param mixed $value
*
* @return \Iterator
*/
public static function iterFor($value)
{
if ($value instanceof \Iterator) {
return $value;
}
if (is_array($value)) {
return new \ArrayIterator($value);
}
return new \ArrayIterator([$value]);
}
}
<?php
namespace GuzzleHttp\Promise;
final class Each
{
/**
* Given an iterator that yields promises or values, returns a promise that
* is fulfilled with a null value when the iterator has been consumed or
* the aggregate promise has been fulfilled or rejected.
*
* $onFulfilled is a function that accepts the fulfilled value, iterator
* index, and the aggregate promise. The callback can invoke any necessary
* side effects and choose to resolve or reject the aggregate if needed.
*
* $onRejected is a function that accepts the rejection reason, iterator
* index, and the aggregate promise. The callback can invoke any necessary
* side effects and choose to resolve or reject the aggregate if needed.
*
* @param mixed $iterable Iterator or array to iterate over.
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*/
public static function of(
$iterable,
callable $onFulfilled = null,
callable $onRejected = null
) {
return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled,
'rejected' => $onRejected
]))->promise();
}
/**
* Like of, but only allows a certain number of outstanding promises at any
* given time.
*
* $concurrency may be an integer or a function that accepts the number of
* pending promises and returns a numeric concurrency limit value to allow
* for dynamic a concurrency size.
*
* @param mixed $iterable
* @param int|callable $concurrency
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*/
public static function ofLimit(
$iterable,
$concurrency,
callable $onFulfilled = null,
callable $onRejected = null
) {
return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled,
'rejected' => $onRejected,
'concurrency' => $concurrency
]))->promise();
}
/**
* Like limit, but ensures that no promise in the given $iterable argument
* is rejected. If any promise is rejected, then the aggregate promise is
* rejected with the encountered rejection.
*
* @param mixed $iterable
* @param int|callable $concurrency
* @param callable $onFulfilled
*
* @return PromiseInterface
*/
public static function ofLimitAll(
$iterable,
$concurrency,
callable $onFulfilled = null
) {
return each_limit(
$iterable,
$concurrency,
$onFulfilled,
function ($reason, $idx, PromiseInterface $aggregate) {
$aggregate->reject($reason);
}
);
}
}
<?php
namespace GuzzleHttp\Promise;
/**
......@@ -9,22 +10,22 @@ class EachPromise implements PromisorInterface
{
private $pending = [];
/** @var \Iterator */
/** @var \Iterator|null */
private $iterable;
/** @var callable|int */
/** @var callable|int|null */
private $concurrency;
/** @var callable */
/** @var callable|null */
private $onFulfilled;
/** @var callable */
/** @var callable|null */
private $onRejected;
/** @var Promise */
/** @var Promise|null */
private $aggregate;
/** @var bool */
/** @var bool|null */
private $mutex;
/**
......@@ -45,12 +46,12 @@ class EachPromise implements PromisorInterface
* allowed number of outstanding concurrently executing promises,
* creating a capped pool of promises. There is no limit by default.
*
* @param mixed $iterable Promises or values to iterate.
* @param array $config Configuration options
* @param mixed $iterable Promises or values to iterate.
* @param array $config Configuration options
*/
public function __construct($iterable, array $config = [])
{
$this->iterable = iter_for($iterable);
$this->iterable = Create::iterFor($iterable);
if (isset($config['concurrency'])) {
$this->concurrency = $config['concurrency'];
......@@ -65,6 +66,7 @@ class EachPromise implements PromisorInterface
}
}
/** @psalm-suppress InvalidNullableReturnType */
public function promise()
{
if ($this->aggregate) {
......@@ -73,14 +75,29 @@ class EachPromise implements PromisorInterface
try {
$this->createPromise();
/** @psalm-assert Promise $this->aggregate */
$this->iterable->rewind();
$this->refillPending();
if (!$this->checkIfFinished()) {
$this->refillPending();
}
} catch (\Throwable $e) {
/**
* @psalm-suppress NullReference
* @phpstan-ignore-next-line
*/
$this->aggregate->reject($e);
} catch (\Exception $e) {
/**
* @psalm-suppress NullReference
* @phpstan-ignore-next-line
*/
$this->aggregate->reject($e);
}
/**
* @psalm-suppress NullableReturnStatement
* @phpstan-ignore-next-line
*/
return $this->aggregate;
}
......@@ -89,17 +106,12 @@ class EachPromise implements PromisorInterface
$this->mutex = false;
$this->aggregate = new Promise(function () {
reset($this->pending);
if (empty($this->pending) && !$this->iterable->valid()) {
$this->aggregate->resolve(null);
return;
}
// Consume a potentially fluctuating list of promises while
// ensuring that indexes are maintained (precluding array_shift).
while ($promise = current($this->pending)) {
next($this->pending);
$promise->wait();
if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
if (Is::settled($this->aggregate)) {
return;
}
}
......@@ -148,22 +160,34 @@ class EachPromise implements PromisorInterface
return false;
}
$promise = promise_for($this->iterable->current());
$idx = $this->iterable->key();
$promise = Create::promiseFor($this->iterable->current());
$key = $this->iterable->key();
// Iterable keys may not be unique, so we add the promises at the end
// of the pending array and retrieve the array index being used
$this->pending[] = null;
end($this->pending);
$idx = key($this->pending);
$this->pending[$idx] = $promise->then(
function ($value) use ($idx) {
function ($value) use ($idx, $key) {
if ($this->onFulfilled) {
call_user_func(
$this->onFulfilled, $value, $idx, $this->aggregate
$this->onFulfilled,
$value,
$key,
$this->aggregate
);
}
$this->step($idx);
},
function ($reason) use ($idx) {
function ($reason) use ($idx, $key) {
if ($this->onRejected) {
call_user_func(
$this->onRejected, $reason, $idx, $this->aggregate
$this->onRejected,
$reason,
$key,
$this->aggregate
);
}
$this->step($idx);
......@@ -201,7 +225,7 @@ class EachPromise implements PromisorInterface
private function step($idx)
{
// If the promise was already resolved, then ignore this step.
if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
if (Is::settled($this->aggregate)) {
return;
}
......
<?php
namespace GuzzleHttp\Promise;
/**
......@@ -13,9 +14,10 @@ class FulfilledPromise implements PromiseInterface
public function __construct($value)
{
if (method_exists($value, 'then')) {
if (is_object($value) && method_exists($value, 'then')) {
throw new \InvalidArgumentException(
'You cannot create a FulfilledPromise with a promise.');
'You cannot create a FulfilledPromise with a promise.'
);
}
$this->value = $value;
......@@ -30,11 +32,11 @@ class FulfilledPromise implements PromiseInterface
return $this;
}
$queue = queue();
$queue = Utils::queue();
$p = new Promise([$queue, 'run']);
$value = $this->value;
$queue->add(static function () use ($p, $value, $onFulfilled) {
if ($p->getState() === self::PENDING) {
if (Is::pending($p)) {
try {
$p->resolve($onFulfilled($value));
} catch (\Throwable $e) {
......
<?php
namespace GuzzleHttp\Promise;
final class Is
{
/**
* Returns true if a promise is pending.
*
* @return bool
*/
public static function pending(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::PENDING;
}
/**
* Returns true if a promise is fulfilled or rejected.
*
* @return bool
*/
public static function settled(PromiseInterface $promise)
{
return $promise->getState() !== PromiseInterface::PENDING;
}
/**
* Returns true if a promise is fulfilled.
*
* @return bool
*/
public static function fulfilled(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::FULFILLED;
}
/**
* Returns true if a promise is rejected.
*
* @return bool
*/
public static function rejected(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::REJECTED;
}
}
<?php
namespace GuzzleHttp\Promise;
/**
......@@ -41,14 +42,13 @@ class Promise implements PromiseInterface
// Return a fulfilled promise and immediately invoke any callbacks.
if ($this->state === self::FULFILLED) {
return $onFulfilled
? promise_for($this->result)->then($onFulfilled)
: promise_for($this->result);
$promise = Create::promiseFor($this->result);
return $onFulfilled ? $promise->then($onFulfilled) : $promise;
}
// It's either cancelled or rejected, so return a rejected promise
// and immediately invoke any callbacks.
$rejection = rejection_for($this->result);
$rejection = Create::rejectionFor($this->result);
return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
}
......@@ -61,19 +61,15 @@ class Promise implements PromiseInterface
{
$this->waitIfPending();
$inner = $this->result instanceof PromiseInterface
? $this->result->wait($unwrap)
: $this->result;
if ($this->result instanceof PromiseInterface) {
return $this->result->wait($unwrap);
}
if ($unwrap) {
if ($this->result instanceof PromiseInterface
|| $this->state === self::FULFILLED
) {
return $inner;
} else {
// It's rejected so "unwrap" and throw an exception.
throw exception_for($inner);
if ($this->state === self::FULFILLED) {
return $this->result;
}
// It's rejected so "unwrap" and throw an exception.
throw Create::exceptionFor($this->result);
}
}
......@@ -103,6 +99,7 @@ class Promise implements PromiseInterface
}
// Reject the promise only if it wasn't rejected in a then callback.
/** @psalm-suppress RedundantCondition */
if ($this->state === self::PENDING) {
$this->reject(new CancellationException('Promise has been cancelled'));
}
......@@ -148,17 +145,15 @@ class Promise implements PromiseInterface
// If the value was not a settled promise or a thenable, then resolve
// it in the task queue using the correct ID.
if (!method_exists($value, 'then')) {
if (!is_object($value) || !method_exists($value, 'then')) {
$id = $state === self::FULFILLED ? 1 : 2;
// It's a success, so resolve the handlers in the queue.
queue()->add(static function () use ($id, $value, $handlers) {
Utils::queue()->add(static function () use ($id, $value, $handlers) {
foreach ($handlers as $handler) {
self::callHandler($id, $value, $handler);
}
});
} elseif ($value instanceof Promise
&& $value->getState() === self::PENDING
) {
} elseif ($value instanceof Promise && Is::pending($value)) {
// We can just merge our handlers onto the next promise.
$value->handlers = array_merge($value->handlers, $handlers);
} else {
......@@ -184,8 +179,6 @@ class Promise implements PromiseInterface
* @param int $index 1 (resolve) or 2 (reject).
* @param mixed $value Value to pass to the callback.
* @param array $handler Array of handler data (promise and callbacks).
*
* @return array Returns the next group to resolve.
*/
private static function callHandler($index, $value, array $handler)
{
......@@ -194,13 +187,21 @@ class Promise implements PromiseInterface
// The promise may have been cancelled or resolved before placing
// this thunk in the queue.
if ($promise->getState() !== self::PENDING) {
if (Is::settled($promise)) {
return;
}
try {
if (isset($handler[$index])) {
$promise->resolve($handler[$index]($value));
/*
* If $f throws an exception, then $handler will be in the exception
* stack trace. Since $handler contains a reference to the callable
* itself we get a circular reference. We clear the $handler
* here to avoid that memory leak.
*/
$f = $handler[$index];
unset($handler);
$promise->resolve($f($value));
} elseif ($index === 1) {
// Forward resolution values as-is.
$promise->resolve($value);
......@@ -224,15 +225,16 @@ class Promise implements PromiseInterface
} elseif ($this->waitList) {
$this->invokeWaitList();
} else {
// If there's not wait function, then reject the promise.
// If there's no wait function, then reject the promise.
$this->reject('Cannot wait on a promise that has '
. 'no internal wait function. You must provide a wait '
. 'function when constructing the promise to be able to '
. 'wait on a promise.');
}
queue()->run();
Utils::queue()->run();
/** @psalm-suppress RedundantCondition */
if ($this->state === self::PENDING) {
$this->reject('Invoking the wait callback did not resolve the promise');
}
......@@ -263,17 +265,13 @@ class Promise implements PromiseInterface
$this->waitList = null;
foreach ($waitList as $result) {
while (true) {
do {
$result->waitIfPending();
$result = $result->result;
} while ($result instanceof Promise);
if ($result->result instanceof Promise) {
$result = $result->result;
} else {
if ($result->result instanceof PromiseInterface) {
$result->result->wait(false);
}
break;
}
if ($result instanceof PromiseInterface) {
$result->wait(false);
}
}
}
......
<?php
namespace GuzzleHttp\Promise;
/**
......@@ -56,6 +57,7 @@ interface PromiseInterface
* Resolve the promise with the given value.
*
* @param mixed $value
*
* @throws \RuntimeException if the promise is already resolved.
*/
public function resolve($value);
......@@ -64,6 +66,7 @@ interface PromiseInterface
* Reject the promise with the given reason.
*
* @param mixed $reason
*
* @throws \RuntimeException if the promise is already resolved.
*/
public function reject($reason);
......@@ -86,6 +89,7 @@ interface PromiseInterface
* @param bool $unwrap
*
* @return mixed
*
* @throws \LogicException if the promise has no wait function or if the
* promise does not settle after waiting.
*/
......
<?php
namespace GuzzleHttp\Promise;
/**
......
<?php
namespace GuzzleHttp\Promise;
/**
......@@ -13,9 +14,10 @@ class RejectedPromise implements PromiseInterface
public function __construct($reason)
{
if (method_exists($reason, 'then')) {
if (is_object($reason) && method_exists($reason, 'then')) {
throw new \InvalidArgumentException(
'You cannot create a RejectedPromise with a promise.');
'You cannot create a RejectedPromise with a promise.'
);
}
$this->reason = $reason;
......@@ -30,11 +32,11 @@ class RejectedPromise implements PromiseInterface
return $this;
}
$queue = queue();
$queue = Utils::queue();
$reason = $this->reason;
$p = new Promise([$queue, 'run']);
$queue->add(static function () use ($p, $reason, $onRejected) {
if ($p->getState() === self::PENDING) {
if (Is::pending($p)) {
try {
// Return a resolved promise if onRejected does not throw.
$p->resolve($onRejected($reason));
......@@ -59,8 +61,10 @@ class RejectedPromise implements PromiseInterface
public function wait($unwrap = true, $defaultDelivery = null)
{
if ($unwrap) {
throw exception_for($this->reason);
throw Create::exceptionFor($this->reason);
}
return null;
}
public function getState()
......
<?php
namespace GuzzleHttp\Promise;
/**
......@@ -12,7 +13,7 @@ class RejectionException extends \RuntimeException
private $reason;
/**
* @param mixed $reason Rejection reason.
* @param mixed $reason Rejection reason.
* @param string $description Optional description
*/
public function __construct($reason, $description = null)
......
<?php
namespace GuzzleHttp\Promise;
/**
......@@ -8,7 +9,7 @@ namespace GuzzleHttp\Promise;
* maintains a constant stack size. You can use the task queue asynchronously
* by calling the `run()` function of the global task queue in an event loop.
*
* GuzzleHttp\Promise\queue()->run();
* GuzzleHttp\Promise\Utils::queue()->run();
*/
class TaskQueue implements TaskQueueInterface
{
......@@ -42,8 +43,8 @@ class TaskQueue implements TaskQueueInterface
public function run()
{
/** @var callable $task */
while ($task = array_shift($this->queue)) {
/** @var callable $task */
$task();
}
}
......
<?php
namespace GuzzleHttp\Promise;
interface TaskQueueInterface
......@@ -13,8 +14,6 @@ interface TaskQueueInterface
/**
* Adds a task to the queue that will be executed the next time run is
* called.
*
* @param callable $task
*/
public function add(callable $task);
......
<?php
namespace GuzzleHttp\Promise;
final class Utils
{
/**
* Get the global task queue used for promise resolution.
*
* This task queue MUST be run in an event loop in order for promises to be
* settled asynchronously. It will be automatically run when synchronously
* waiting on a promise.
*
* <code>
* while ($eventLoop->isRunning()) {
* GuzzleHttp\Promise\Utils::queue()->run();
* }
* </code>
*
* @param TaskQueueInterface $assign Optionally specify a new queue instance.
*
* @return TaskQueueInterface
*/
public static function queue(TaskQueueInterface $assign = null)
{
static $queue;
if ($assign) {
$queue = $assign;
} elseif (!$queue) {
$queue = new TaskQueue();
}
return $queue;
}
/**
* Adds a function to run in the task queue when it is next `run()` and
* returns a promise that is fulfilled or rejected with the result.
*
* @param callable $task Task function to run.
*
* @return PromiseInterface
*/
public static function task(callable $task)
{
$queue = self::queue();
$promise = new Promise([$queue, 'run']);
$queue->add(function () use ($task, $promise) {
try {
$promise->resolve($task());
} catch (\Throwable $e) {
$promise->reject($e);
} catch (\Exception $e) {
$promise->reject($e);
}
});
return $promise;
}
/**
* Synchronously waits on a promise to resolve and returns an inspection
* state array.
*
* Returns a state associative array containing a "state" key mapping to a
* valid promise state. If the state of the promise is "fulfilled", the
* array will contain a "value" key mapping to the fulfilled value of the
* promise. If the promise is rejected, the array will contain a "reason"
* key mapping to the rejection reason of the promise.
*
* @param PromiseInterface $promise Promise or value.
*
* @return array
*/
public static function inspect(PromiseInterface $promise)
{
try {
return [
'state' => PromiseInterface::FULFILLED,
'value' => $promise->wait()
];
} catch (RejectionException $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
} catch (\Throwable $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
} catch (\Exception $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
}
}
/**
* Waits on all of the provided promises, but does not unwrap rejected
* promises as thrown exception.
*
* Returns an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param PromiseInterface[] $promises Traversable of promises to wait upon.
*
* @return array
*/
public static function inspectAll($promises)
{
$results = [];
foreach ($promises as $key => $promise) {
$results[$key] = inspect($promise);
}
return $results;
}
/**
* Waits on all of the provided promises and returns the fulfilled values.
*
* Returns an array that contains the value of each promise (in the same
* order the promises were provided). An exception is thrown if any of the
* promises are rejected.
*
* @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
*
* @return array
*
* @throws \Exception on error
* @throws \Throwable on error in PHP >=7
*/
public static function unwrap($promises)
{
$results = [];
foreach ($promises as $key => $promise) {
$results[$key] = $promise->wait();
}
return $results;
}
/**
* Given an array of promises, return a promise that is fulfilled when all
* the items in the array are fulfilled.
*
* The promise's fulfillment value is an array with fulfillment values at
* respective positions to the original array. If any promise in the array
* rejects, the returned promise is rejected with the rejection reason.
*
* @param mixed $promises Promises or values.
* @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
*
* @return PromiseInterface
*/
public static function all($promises, $recursive = false)
{
$results = [];
$promise = Each::of(
$promises,
function ($value, $idx) use (&$results) {
$results[$idx] = $value;
},
function ($reason, $idx, Promise $aggregate) {
$aggregate->reject($reason);
}
)->then(function () use (&$results) {
ksort($results);
return $results;
});
if (true === $recursive) {
$promise = $promise->then(function ($results) use ($recursive, &$promises) {
foreach ($promises as $promise) {
if (Is::pending($promise)) {
return self::all($promises, $recursive);
}
}
return $results;
});
}
return $promise;
}
/**
* Initiate a competitive race between multiple promises or values (values
* will become immediately fulfilled promises).
*
* When count amount of promises have been fulfilled, the returned promise
* is fulfilled with an array that contains the fulfillment values of the
* winners in order of resolution.
*
* This promise is rejected with a {@see AggregateException} if the number
* of fulfilled promises is less than the desired $count.
*
* @param int $count Total number of promises.
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*/
public static function some($count, $promises)
{
$results = [];
$rejections = [];
return Each::of(
$promises,
function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
if (Is::settled($p)) {
return;
}
$results[$idx] = $value;
if (count($results) >= $count) {
$p->resolve(null);
}
},
function ($reason) use (&$rejections) {
$rejections[] = $reason;
}
)->then(
function () use (&$results, &$rejections, $count) {
if (count($results) !== $count) {
throw new AggregateException(
'Not enough promises to fulfill count',
$rejections
);
}
ksort($results);
return array_values($results);
}
);
}
/**
* Like some(), with 1 as count. However, if the promise fulfills, the
* fulfillment value is not an array of 1 but the value directly.
*
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*/
public static function any($promises)
{
return self::some(1, $promises)->then(function ($values) {
return $values[0];
});
}
/**
* Returns a promise that is fulfilled when all of the provided promises have
* been fulfilled or rejected.
*
* The returned promise is fulfilled with an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*/
public static function settle($promises)
{
$results = [];
return Each::of(
$promises,
function ($value, $idx) use (&$results) {
$results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
},
function ($reason, $idx) use (&$results) {
$results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
}
)->then(function () use (&$results) {
ksort($results);
return $results;
});
}
}
<?php
namespace GuzzleHttp\Promise;
/**
......@@ -17,18 +18,12 @@ namespace GuzzleHttp\Promise;
* @param TaskQueueInterface $assign Optionally specify a new queue instance.
*
* @return TaskQueueInterface
*
* @deprecated queue will be removed in guzzlehttp/promises:2.0. Use Utils::queue instead.
*/
function queue(TaskQueueInterface $assign = null)
{
static $queue;
if ($assign) {
$queue = $assign;
} elseif (!$queue) {
$queue = new TaskQueue();
}
return $queue;
return Utils::queue($assign);
}
/**
......@@ -38,22 +33,12 @@ function queue(TaskQueueInterface $assign = null)
* @param callable $task Task function to run.
*
* @return PromiseInterface
*
* @deprecated task will be removed in guzzlehttp/promises:2.0. Use Utils::task instead.
*/
function task(callable $task)
{
$queue = queue();
$promise = new Promise([$queue, 'run']);
$queue->add(function () use ($task, $promise) {
try {
$promise->resolve($task());
} catch (\Throwable $e) {
$promise->reject($e);
} catch (\Exception $e) {
$promise->reject($e);
}
});
return $promise;
return Utils::task($task);
}
/**
......@@ -62,23 +47,12 @@ function task(callable $task)
* @param mixed $value Promise or value.
*
* @return PromiseInterface
*
* @deprecated promise_for will be removed in guzzlehttp/promises:2.0. Use Create::promiseFor instead.
*/
function promise_for($value)
{
if ($value instanceof PromiseInterface) {
return $value;
}
// Return a Guzzle promise that shadows the given promise.
if (method_exists($value, 'then')) {
$wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
$cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
$promise = new Promise($wfn, $cfn);
$value->then([$promise, 'resolve'], [$promise, 'reject']);
return $promise;
}
return new FulfilledPromise($value);
return Create::promiseFor($value);
}
/**
......@@ -88,14 +62,12 @@ function promise_for($value)
* @param mixed $reason Promise or reason.
*
* @return PromiseInterface
*
* @deprecated rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead.
*/
function rejection_for($reason)
{
if ($reason instanceof PromiseInterface) {
return $reason;
}
return new RejectedPromise($reason);
return Create::rejectionFor($reason);
}
/**
......@@ -104,12 +76,12 @@ function rejection_for($reason)
* @param mixed $reason
*
* @return \Exception|\Throwable
*
* @deprecated exception_for will be removed in guzzlehttp/promises:2.0. Use Create::exceptionFor instead.
*/
function exception_for($reason)
{
return $reason instanceof \Exception || $reason instanceof \Throwable
? $reason
: new RejectionException($reason);
return Create::exceptionFor($reason);
}
/**
......@@ -118,16 +90,12 @@ function exception_for($reason)
* @param mixed $value
*
* @return \Iterator
*
* @deprecated iter_for will be removed in guzzlehttp/promises:2.0. Use Create::iterFor instead.
*/
function iter_for($value)
{
if ($value instanceof \Iterator) {
return $value;
} elseif (is_array($value)) {
return new \ArrayIterator($value);
} else {
return new \ArrayIterator([$value]);
}
return Create::iterFor($value);
}
/**
......@@ -143,21 +111,12 @@ function iter_for($value)
* @param PromiseInterface $promise Promise or value.
*
* @return array
*
* @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspect instead.
*/
function inspect(PromiseInterface $promise)
{
try {
return [
'state' => PromiseInterface::FULFILLED,
'value' => $promise->wait()
];
} catch (RejectionException $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
} catch (\Throwable $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
} catch (\Exception $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
}
return Utils::inspect($promise);
}
/**
......@@ -166,19 +125,17 @@ function inspect(PromiseInterface $promise)
*
* Returns an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param PromiseInterface[] $promises Traversable of promises to wait upon.
*
* @return array
* @see GuzzleHttp\Promise\inspect for the inspection state array format.
*
* @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspectAll instead.
*/
function inspect_all($promises)
{
$results = [];
foreach ($promises as $key => $promise) {
$results[$key] = inspect($promise);
}
return $results;
return Utils::inspectAll($promises);
}
/**
......@@ -188,20 +145,18 @@ function inspect_all($promises)
* the promises were provided). An exception is thrown if any of the promises
* are rejected.
*
* @param mixed $promises Iterable of PromiseInterface objects to wait on.
* @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
*
* @return array
*
* @throws \Exception on error
* @throws \Throwable on error in PHP >=7
*
* @deprecated unwrap will be removed in guzzlehttp/promises:2.0. Use Utils::unwrap instead.
*/
function unwrap($promises)
{
$results = [];
foreach ($promises as $key => $promise) {
$results[$key] = $promise->wait();
}
return $results;
return Utils::unwrap($promises);
}
/**
......@@ -212,25 +167,16 @@ function unwrap($promises)
* respective positions to the original array. If any promise in the array
* rejects, the returned promise is rejected with the rejection reason.
*
* @param mixed $promises Promises or values.
* @param mixed $promises Promises or values.
* @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
*
* @return PromiseInterface
*
* @deprecated all will be removed in guzzlehttp/promises:2.0. Use Utils::all instead.
*/
function all($promises)
function all($promises, $recursive = false)
{
$results = [];
return each(
$promises,
function ($value, $idx) use (&$results) {
$results[$idx] = $value;
},
function ($reason, $idx, Promise $aggregate) {
$aggregate->reject($reason);
}
)->then(function () use (&$results) {
ksort($results);
return $results;
});
return Utils::all($promises, $recursive);
}
/**
......@@ -241,45 +187,19 @@ function all($promises)
* fulfilled with an array that contains the fulfillment values of the winners
* in order of resolution.
*
* This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException}
* if the number of fulfilled promises is less than the desired $count.
* This promise is rejected with a {@see AggregateException} if the number of
* fulfilled promises is less than the desired $count.
*
* @param int $count Total number of promises.
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*
* @deprecated some will be removed in guzzlehttp/promises:2.0. Use Utils::some instead.
*/
function some($count, $promises)
{
$results = [];
$rejections = [];
return each(
$promises,
function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
if ($p->getState() !== PromiseInterface::PENDING) {
return;
}
$results[$idx] = $value;
if (count($results) >= $count) {
$p->resolve(null);
}
},
function ($reason) use (&$rejections) {
$rejections[] = $reason;
}
)->then(
function () use (&$results, &$rejections, $count) {
if (count($results) !== $count) {
throw new AggregateException(
'Not enough promises to fulfill count',
$rejections
);
}
ksort($results);
return array_values($results);
}
);
return Utils::some($count, $promises);
}
/**
......@@ -289,10 +209,12 @@ function some($count, $promises)
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*
* @deprecated any will be removed in guzzlehttp/promises:2.0. Use Utils::any instead.
*/
function any($promises)
{
return some(1, $promises)->then(function ($values) { return $values[0]; });
return Utils::any($promises);
}
/**
......@@ -301,27 +223,17 @@ function any($promises)
*
* The returned promise is fulfilled with an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
* @see GuzzleHttp\Promise\inspect for the inspection state array format.
*
* @deprecated settle will be removed in guzzlehttp/promises:2.0. Use Utils::settle instead.
*/
function settle($promises)
{
$results = [];
return each(
$promises,
function ($value, $idx) use (&$results) {
$results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
},
function ($reason, $idx) use (&$results) {
$results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
}
)->then(function () use (&$results) {
ksort($results);
return $results;
});
return Utils::settle($promises);
}
/**
......@@ -329,29 +241,28 @@ function settle($promises)
* fulfilled with a null value when the iterator has been consumed or the
* aggregate promise has been fulfilled or rejected.
*
* $onFulfilled is a function that accepts the fulfilled value, iterator
* index, and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate promise if needed.
* $onFulfilled is a function that accepts the fulfilled value, iterator index,
* and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate if needed.
*
* $onRejected is a function that accepts the rejection reason, iterator
* index, and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate promise if needed.
* $onRejected is a function that accepts the rejection reason, iterator index,
* and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate if needed.
*
* @param mixed $iterable Iterator or array to iterate over.
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*
* @deprecated each will be removed in guzzlehttp/promises:2.0. Use Each::of instead.
*/
function each(
$iterable,
callable $onFulfilled = null,
callable $onRejected = null
) {
return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled,
'rejected' => $onRejected
]))->promise();
return Each::of($iterable, $onFulfilled, $onRejected);
}
/**
......@@ -368,6 +279,8 @@ function each(
* @param callable $onRejected
*
* @return PromiseInterface
*
* @deprecated each_limit will be removed in guzzlehttp/promises:2.0. Use Each::ofLimit instead.
*/
function each_limit(
$iterable,
......@@ -375,11 +288,7 @@ function each_limit(
callable $onFulfilled = null,
callable $onRejected = null
) {
return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled,
'rejected' => $onRejected,
'concurrency' => $concurrency
]))->promise();
return Each::ofLimit($iterable, $concurrency, $onFulfilled, $onRejected);
}
/**
......@@ -392,66 +301,63 @@ function each_limit(
* @param callable $onFulfilled
*
* @return PromiseInterface
*
* @deprecated each_limit_all will be removed in guzzlehttp/promises:2.0. Use Each::ofLimitAll instead.
*/
function each_limit_all(
$iterable,
$concurrency,
callable $onFulfilled = null
) {
return each_limit(
$iterable,
$concurrency,
$onFulfilled,
function ($reason, $idx, PromiseInterface $aggregate) {
$aggregate->reject($reason);
}
);
return Each::ofLimitAll($iterable, $concurrency, $onFulfilled);
}
/**
* Returns true if a promise is fulfilled.
*
* @param PromiseInterface $promise
*
* @return bool
*
* @deprecated is_fulfilled will be removed in guzzlehttp/promises:2.0. Use Is::fulfilled instead.
*/
function is_fulfilled(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::FULFILLED;
return Is::fulfilled($promise);
}
/**
* Returns true if a promise is rejected.
*
* @param PromiseInterface $promise
*
* @return bool
*
* @deprecated is_rejected will be removed in guzzlehttp/promises:2.0. Use Is::rejected instead.
*/
function is_rejected(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::REJECTED;
return Is::rejected($promise);
}
/**
* Returns true if a promise is fulfilled or rejected.
*
* @param PromiseInterface $promise
*
* @return bool
*
* @deprecated is_settled will be removed in guzzlehttp/promises:2.0. Use Is::settled instead.
*/
function is_settled(PromiseInterface $promise)
{
return $promise->getState() !== PromiseInterface::PENDING;
return Is::settled($promise);
}
/**
* @see Coroutine
* Create a new coroutine.
*
* @param callable $generatorFn
* @see Coroutine
*
* @return PromiseInterface
*
* @deprecated coroutine will be removed in guzzlehttp/promises:2.0. Use Coroutine::of instead.
*/
function coroutine(callable $generatorFn)
{
return new Coroutine($generatorFn);
return Coroutine::of($generatorFn);
}
......@@ -9,8 +9,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
## [1.7.0] - 2020-09-30
## [1.6.0]
### Added
- Replaced functions by static methods
### Fixed
- Converting a non-seekable stream to a string
- Handle multiple Set-Cookie correctly
- Ignore array keys in header values when merging
- Allow multibyte characters to be parsed in `Message:bodySummary()`
### Changed
- Restored partial HHVM 3 support
## [1.6.1] - 2019-07-02
### Fixed
- Accept null and bool header values again
## [1.6.0] - 2019-06-30
### Added
......
......@@ -23,11 +23,11 @@ Reads from multiple streams, one after the other.
```php
use GuzzleHttp\Psr7;
$a = Psr7\stream_for('abc, ');
$b = Psr7\stream_for('123.');
$a = Psr7\Utils::streamFor('abc, ');
$b = Psr7\Utils::streamFor('123.');
$composed = new Psr7\AppendStream([$a, $b]);
$composed->addStream(Psr7\stream_for(' Above all listen to me'));
$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));
echo $composed; // abc, 123. Above all listen to me.
```
......@@ -65,7 +65,7 @@ then on disk.
```php
use GuzzleHttp\Psr7;
$original = Psr7\stream_for(fopen('http://www.google.com', 'r'));
$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
$stream = new Psr7\CachingStream($original);
$stream->read(1024);
......@@ -89,7 +89,7 @@ stream becomes too full.
use GuzzleHttp\Psr7;
// Create an empty stream
$stream = Psr7\stream_for();
$stream = Psr7\Utils::streamFor();
// Start dropping data when the stream has more than 10 bytes
$dropping = new Psr7\DroppingStream($stream, 10);
......@@ -112,7 +112,7 @@ to create a concrete class for a simple extension point.
use GuzzleHttp\Psr7;
$stream = Psr7\stream_for('hi');
$stream = Psr7\Utils::streamFor('hi');
$fnStream = Psr7\FnStream::decorate($stream, [
'rewind' => function () use ($stream) {
echo 'About to rewind - ';
......@@ -167,7 +167,7 @@ chunks (e.g. Amazon S3's multipart upload API).
```php
use GuzzleHttp\Psr7;
$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+'));
$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
echo $original->getSize();
// >>> 1048576
......@@ -197,7 +197,7 @@ NoSeekStream wraps a stream and does not allow seeking.
```php
use GuzzleHttp\Psr7;
$original = Psr7\stream_for('foo');
$original = Psr7\Utils::streamFor('foo');
$noSeek = new Psr7\NoSeekStream($original);
echo $noSeek->read(3);
......@@ -271,7 +271,7 @@ This decorator could be added to any existing stream and used like so:
```php
use GuzzleHttp\Psr7;
$original = Psr7\stream_for('foo');
$original = Psr7\Utils::streamFor('foo');
$eofStream = new EofCallbackStream($original, function () {
echo 'EOF!';
......@@ -297,228 +297,292 @@ stream from a PSR-7 stream.
```php
use GuzzleHttp\Psr7\StreamWrapper;
$stream = GuzzleHttp\Psr7\stream_for('hello!');
$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
$resource = StreamWrapper::getResource($stream);
echo fread($resource, 6); // outputs hello!
```
# Function API
# Static API
There are various functions available under the `GuzzleHttp\Psr7` namespace.
There are various static methods available under the `GuzzleHttp\Psr7` namespace.
## `function str`
## `GuzzleHttp\Psr7\Message::toString`
`function str(MessageInterface $message)`
`public static function toString(MessageInterface $message): string`
Returns the string representation of an HTTP message.
```php
$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
echo GuzzleHttp\Psr7\str($request);
echo GuzzleHttp\Psr7\Message::toString($request);
```
## `function uri_for`
## `GuzzleHttp\Psr7\Message::bodySummary`
`function uri_for($uri)`
`public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null`
This function accepts a string or `Psr\Http\Message\UriInterface` and returns a
UriInterface for the given value. If the value is already a `UriInterface`, it
is returned as-is.
Get a short summary of the message body.
```php
$uri = GuzzleHttp\Psr7\uri_for('http://example.com');
assert($uri === GuzzleHttp\Psr7\uri_for($uri));
```
Will return `null` if the response is not printable.
## `function stream_for`
## `GuzzleHttp\Psr7\Message::rewindBody`
`function stream_for($resource = '', array $options = [])`
`public static function rewindBody(MessageInterface $message): void`
Create a new stream based on the input type.
Attempts to rewind a message body and throws an exception on failure.
Options is an associative array that can contain the following keys:
The body of the message will only be rewound if a call to `tell()`
returns a value other than `0`.
* - metadata: Array of custom metadata.
* - size: Size of the stream.
This method accepts the following `$resource` types:
## `GuzzleHttp\Psr7\Message::parseMessage`
- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
- `string`: Creates a stream object that uses the given string as the contents.
- `resource`: Creates a stream object that wraps the given PHP stream resource.
- `Iterator`: If the provided value implements `Iterator`, then a read-only
stream object will be created that wraps the given iterable. Each time the
stream is read from, data from the iterator will fill a buffer and will be
continuously called until the buffer is equal to the requested read size.
Subsequent read calls will first read from the buffer and then call `next`
on the underlying iterator until it is exhausted.
- `object` with `__toString()`: If the object has the `__toString()` method,
the object will be cast to a string and then a stream will be returned that
uses the string value.
- `NULL`: When `null` is passed, an empty stream object is returned.
- `callable` When a callable is passed, a read-only stream object will be
created that invokes the given callable. The callable is invoked with the
number of suggested bytes to read. The callable can return any number of
bytes, but MUST return `false` when there is no more data to return. The
stream object that wraps the callable will invoke the callable until the
number of requested bytes are available. Any additional bytes will be
buffered and used in subsequent reads.
`public static function parseMessage(string $message): array`
```php
$stream = GuzzleHttp\Psr7\stream_for('foo');
$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r'));
Parses an HTTP message into an associative array.
$generator = function ($bytes) {
for ($i = 0; $i < $bytes; $i++) {
yield ' ';
}
}
The array contains the "start-line" key containing the start line of
the message, "headers" key containing an associative array of header
array values, and a "body" key containing the body of the message.
$stream = GuzzleHttp\Psr7\stream_for($generator(100));
```
## `GuzzleHttp\Psr7\Message::parseRequestUri`
## `function parse_header`
`public static function parseRequestUri(string $path, array $headers): string`
`function parse_header($header)`
Constructs a URI for an HTTP request message.
Parse an array of header values containing ";" separated data into an array of
associative arrays representing the header key value pair data of the header.
When a parameter does not contain a value, but just contains a key, this
function will inject a key with a '' string value.
## `GuzzleHttp\Psr7\Message::parseRequest`
## `function normalize_header`
`public static function parseRequest(string $message): Request`
`function normalize_header($header)`
Parses a request message string into a request object.
Converts an array of header values that may contain comma separated headers
into an array of headers with no comma separated values.
## `GuzzleHttp\Psr7\Message::parseResponse`
## `function modify_request`
`public static function parseResponse(string $message): Response`
`function modify_request(RequestInterface $request, array $changes)`
Parses a response message string into a response object.
Clone and modify a request with the given changes. This method is useful for
reducing the number of clones needed to mutate a message.
The changes can be one of:
## `GuzzleHttp\Psr7\Header::parse`
- method: (string) Changes the HTTP method.
- set_headers: (array) Sets the given headers.
- remove_headers: (array) Remove the given headers.
- body: (mixed) Sets the given body.
- uri: (UriInterface) Set the URI.
- query: (string) Set the query string value of the URI.
- version: (string) Set the protocol version.
`public static function parse(string|array $header): array`
Parse an array of header values containing ";" separated data into an
array of associative arrays representing the header key value pair data
of the header. When a parameter does not contain a value, but just
contains a key, this function will inject a key with a '' string value.
## `function rewind_body`
`function rewind_body(MessageInterface $message)`
## `GuzzleHttp\Psr7\Header::normalize`
Attempts to rewind a message body and throws an exception on failure. The body
of the message will only be rewound if a call to `tell()` returns a value other
than `0`.
`public static function normalize(string|array $header): array`
Converts an array of header values that may contain comma separated
headers into an array of headers with no comma separated values.
## `function try_fopen`
`function try_fopen($filename, $mode)`
## `GuzzleHttp\Psr7\Query::parse`
Safely opens a PHP stream resource using a filename.
`public static function parse(string $str, int|bool $urlEncoding = true): array`
Parse a query string into an associative array.
If multiple values are found for the same key, the value of that key
value pair will become an array. This function does not parse nested
PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
## `GuzzleHttp\Psr7\Query::build`
`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string`
Build a query string from an array of key value pairs.
This function can use the return value of `parse()` to build a query
string. This function does not modify the provided keys when an array is
encountered (like `http_build_query()` would).
## `GuzzleHttp\Psr7\Utils::caselessRemove`
`public static function caselessRemove(iterable<string> $keys, $keys, array $data): array`
When fopen fails, PHP normally raises a warning. This function adds an error
handler that checks for errors and throws an exception instead.
Remove the items given by the keys, case insensitively from the data.
## `function copy_to_string`
## `GuzzleHttp\Psr7\Utils::copyToStream`
`function copy_to_string(StreamInterface $stream, $maxLen = -1)`
`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void`
Copy the contents of a stream into a string until the given number of bytes
have been read.
Copy the contents of a stream into another stream until the given number
of bytes have been read.
## `function copy_to_stream`
## `GuzzleHttp\Psr7\Utils::copyToString`
`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)`
`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`
Copy the contents of a stream into another stream until the given number of
Copy the contents of a stream into a string until the given number of
bytes have been read.
## `function hash`
## `GuzzleHttp\Psr7\Utils::hash`
`function hash(StreamInterface $stream, $algo, $rawOutput = false)`
`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`
Calculate a hash of a Stream. This method reads the entire stream to calculate
a rolling hash (based on PHP's hash_init functions).
Calculate a hash of a stream.
This method reads the entire stream to calculate a rolling hash, based on
PHP's `hash_init` functions.
## `function readline`
`function readline(StreamInterface $stream, $maxLength = null)`
## `GuzzleHttp\Psr7\Utils::modifyRequest`
`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`
Clone and modify a request with the given changes.
This method is useful for reducing the number of clones needed to mutate
a message.
- method: (string) Changes the HTTP method.
- set_headers: (array) Sets the given headers.
- remove_headers: (array) Remove the given headers.
- body: (mixed) Sets the given body.
- uri: (UriInterface) Set the URI.
- query: (string) Set the query string value of the URI.
- version: (string) Set the protocol version.
## `GuzzleHttp\Psr7\Utils::readLine`
`public static function readLine(StreamInterface $stream, int $maxLength = null): string`
Read a line from the stream up to the maximum allowed buffer length.
## `function parse_request`
## `GuzzleHttp\Psr7\Utils::streamFor`
`function parse_request($message)`
`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`
Parses a request message string into a request object.
Create a new stream based on the input type.
Options is an associative array that can contain the following keys:
## `function parse_response`
- metadata: Array of custom metadata.
- size: Size of the stream.
`function parse_response($message)`
This method accepts the following `$resource` types:
Parses a response message string into a response object.
- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
- `string`: Creates a stream object that uses the given string as the contents.
- `resource`: Creates a stream object that wraps the given PHP stream resource.
- `Iterator`: If the provided value implements `Iterator`, then a read-only
stream object will be created that wraps the given iterable. Each time the
stream is read from, data from the iterator will fill a buffer and will be
continuously called until the buffer is equal to the requested read size.
Subsequent read calls will first read from the buffer and then call `next`
on the underlying iterator until it is exhausted.
- `object` with `__toString()`: If the object has the `__toString()` method,
the object will be cast to a string and then a stream will be returned that
uses the string value.
- `NULL`: When `null` is passed, an empty stream object is returned.
- `callable` When a callable is passed, a read-only stream object will be
created that invokes the given callable. The callable is invoked with the
number of suggested bytes to read. The callable can return any number of
bytes, but MUST return `false` when there is no more data to return. The
stream object that wraps the callable will invoke the callable until the
number of requested bytes are available. Any additional bytes will be
buffered and used in subsequent reads.
```php
$stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));
## `function parse_query`
$generator = function ($bytes) {
for ($i = 0; $i < $bytes; $i++) {
yield ' ';
}
}
`function parse_query($str, $urlEncoding = true)`
$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
```
Parse a query string into an associative array.
If multiple values are found for the same key, the value of that key value pair
will become an array. This function does not parse nested PHP style arrays into
an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into
`['foo[a]' => '1', 'foo[b]' => '2']`).
## `GuzzleHttp\Psr7\Utils::tryFopen`
`public static function tryFopen(string $filename, string $mode): resource`
## `function build_query`
Safely opens a PHP stream resource using a filename.
`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)`
When fopen fails, PHP normally raises a warning. This function adds an
error handler that checks for errors and throws an exception instead.
Build a query string from an array of key value pairs.
This function can use the return value of parse_query() to build a query string.
This function does not modify the provided keys when an array is encountered
(like http_build_query would).
## `GuzzleHttp\Psr7\Utils::uriFor`
`public static function uriFor(string|UriInterface $uri): UriInterface`
Returns a UriInterface for the given value.
This function accepts a string or UriInterface and returns a
UriInterface for the given value. If the value is already a
UriInterface, it is returned as-is.
## `function mimetype_from_filename`
`function mimetype_from_filename($filename)`
## `GuzzleHttp\Psr7\MimeType::fromFilename`
`public static function fromFilename(string $filename): string|null`
Determines the mimetype of a file by looking at its extension.
## `function mimetype_from_extension`
## `GuzzleHttp\Psr7\MimeType::fromExtension`
`function mimetype_from_extension($extension)`
`public static function fromExtension(string $extension): string|null`
Maps a file extensions to a mimetype.
## Upgrading from Function API
The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API will be removed in 2.0.0. A migration table has been provided here for your convenience:
| Original Function | Replacement Method |
|----------------|----------------|
| `str` | `Message::toString` |
| `uri_for` | `Utils::uriFor` |
| `stream_for` | `Utils::streamFor` |
| `parse_header` | `Header::parse` |
| `normalize_header` | `Header::normalize` |
| `modify_request` | `Utils::modifyRequest` |
| `rewind_body` | `Message::rewindBody` |
| `try_fopen` | `Utils::tryFopen` |
| `copy_to_string` | `Utils::copyToString` |
| `copy_to_stream` | `Utils::copyToStream` |
| `hash` | `Utils::hash` |
| `readline` | `Utils::readLine` |
| `parse_request` | `Message::parseRequest` |
| `parse_response` | `Message::parseResponse` |
| `parse_query` | `Query::parse` |
| `build_query` | `Query::build` |
| `mimetype_from_filename` | `MimeType::fromFilename` |
| `mimetype_from_extension` | `MimeType::fromExtension` |
| `_parse_message` | `Message::parseMessage` |
| `_parse_request_uri` | `Message::parseRequestUri` |
| `get_message_body_summary` | `Message::bodySummary` |
| `_caseless_remove` | `Utils::caselessRemove` |
# Additional URI Methods
Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
......
......@@ -21,14 +21,14 @@
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10",
"ext-zlib": "*"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"suggest": {
"zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"autoload": {
"psr-4": {
......@@ -43,7 +43,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.6-dev"
"dev-master": "1.7-dev"
}
}
}