jaeger-client -> opentelemetry

This commit is contained in:
Andrew Dolgov
2023-10-20 17:12:29 +03:00
parent 45a9ff0c88
commit cdd7ad020e
1159 changed files with 93352 additions and 24252 deletions

View File

@@ -0,0 +1,16 @@
<?php
$finder = PhpCsFixer\Finder::create()
->in(__DIR__.'/src')
->name('*.php')
;
$config = (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@Symfony' => true,
])
->setFinder($finder)
;
return $config;

374
vendor/php-http/discovery/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,374 @@
# Change Log
## 1.19.1 - 2023-07-11
- [#250](https://github.com/php-http/discovery/pull/250) - Fix: Buzz client instantiation using deprecated Message Factory Discovery, use PSR-17 factory discovery instead.
## 1.19.0 - 2023-06-19
- [#249](https://github.com/php-http/discovery/pull/249) - Have composer plugin correctly install Symfony http client when nothing explicitly requires psr 18 resp. httplug.
- [#241](https://github.com/php-http/discovery/pull/241) - Support discovering PSR-17 factories of `httpsoft/http-message` package
## 1.18.1 - 2023-05-17
- [#242](https://github.com/php-http/discovery/pull/242) - Better exception message when no legacy php-http message factories can be built. Also needs php-http/message-factory package and they are deprecated in favor of PSR-17 anyways.
## 1.18.0 - 2023-05-03
- [#235](https://github.com/php-http/discovery/pull/235) - Deprecate HttpClientDiscovery, use Psr18ClientDiscovery instead
- [#238](https://github.com/php-http/discovery/pull/238) - Skip requiring php-http/message-factory when installing symfony/http-client 6.3+
- [#239](https://github.com/php-http/discovery/pull/239) - Skip auto-installing when the root package's extra.discovery is enough
## 1.17.0 - 2023-04-26
- [#230](https://github.com/php-http/discovery/pull/230) - Add Psr18Client to make it straightforward to use PSR-18
- [#232](https://github.com/php-http/discovery/pull/232) - Allow pinning the preferred implementations in composer.json
- [#233](https://github.com/php-http/discovery/pull/233) - Fix Psr17Factory::createServerRequestFromGlobals() when uploaded files have been moved
## 1.16.0 - 2023-04-26
- [#225](https://github.com/php-http/discovery/pull/225) - Remove support for the abandoned Zend Diactoros which has been replaced with Laminas Diactoros; marked the zend library as conflict in composer.json to avoid confusion
- [#227](https://github.com/php-http/discovery/pull/227) - Fix handling requests with nested files
## 1.15.3 - 2023-03-31
- [#224](https://github.com/php-http/discovery/pull/224) - Fix regression with Magento classloader
## 1.15.2 - 2023-02-11
- [#219](https://github.com/php-http/discovery/pull/219) - Fix handling of replaced packages
## 1.15.1 - 2023-02-10
- [#214](https://github.com/php-http/discovery/pull/214) - Fix resolving deps for psr/http-message-implementation
- [#216](https://github.com/php-http/discovery/pull/216) - Fix keeping platform requirements when rebooting composer
- [#217](https://github.com/php-http/discovery/pull/217) - Set extra.plugin-optional composer flag
## 1.15.0 - 2023-02-09
- [#209](https://github.com/php-http/discovery/pull/209) - Add generic `Psr17Factory` class
- [#208](https://github.com/php-http/discovery/pull/208) - Add composer plugin to auto-install missing implementations.
When libraries require an http implementation but no packages providing that implementation is installed in the application, the plugin will automatically install one.
This is only done for libraries that directly require php-http/discovery to avoid unexpected dependency installation.
## 1.14.3 - 2022-07-11
- [#207](https://github.com/php-http/discovery/pull/207) - Updates Exception to extend Throwable solving static analysis errors for consumers
## 1.14.2 - 2022-05-25
- [#202](https://github.com/php-http/discovery/pull/202) - Avoid error when the Symfony PSR-18 client exists but its dependencies are not installed
## 1.14.1 - 2021-09-18
- [#199](https://github.com/php-http/discovery/pull/199) - Fixes message factory discovery for `laminas-diactoros ^2.7`
## 1.14.0 - 2021-06-21
- Deprecate puli as it has been unmaintained for a long time and is not compatible with composer 2 https://github.com/php-http/discovery/pull/195
## 1.13.0 - 2020-11-27
- Support discovering PSR-17 factories of `slim/psr7` package https://github.com/php-http/discovery/pull/192
## 1.12.0 - 2020-09-22
- Support discovering HttpClient of `php-http/guzzle7-adapter` https://github.com/php-http/discovery/pull/189
## 1.11.0 - 2020-09-22
- Use correct method name to find Uri Factory in PSR17 https://github.com/php-http/discovery/pull/181
## 1.10.0 - 2020-09-04
- Discover PSR-18 implementation of phalcon
## 1.9.1 - 2020-07-13
### Fixed
- Support PHP 7.4 and 8.0
## 1.9.0 - 2020-07-02
### Added
- Support discovering PSR-18 factories of `guzzlehttp/guzzle` 7+
## 1.8.0 - 2020-06-14
### Added
- Support discovering PSR-17 factories of `guzzlehttp/psr7` package
- Support discovering PSR-17 factories of `laminas/laminas-diactoros` package
- `ClassDiscovery::getStrategies()` to retrieve the list of current strategies.
### Fixed
- Ignore exception during discovery when Symfony HttplugClient checks if HTTPlug is available.
## 1.7.4 - 2020-01-03
### Fixed
- Improve conditions on Symfony's async HTTPlug client.
## 1.7.3 - 2019-12-27
### Fixed
- Enough conditions to only use Symfony HTTP client if all needed components are available.
## 1.7.2 - 2019-12-27
### Fixed
- Allow a condition to specify an interface and not just classes.
## 1.7.1 - 2019-12-26
### Fixed
- Better conditions to see if Symfony's HTTP clients are available.
## 1.7.0 - 2019-06-30
### Added
- Dropped support for PHP < 7.1
- Support for `symfony/http-client`
## 1.6.1 - 2019-02-23
### Fixed
- MockClientStrategy also provides the mock client when requesting an async client
## 1.6.0 - 2019-01-23
### Added
- Support for PSR-17 factories
- Support for PSR-18 clients
## 1.5.2 - 2018-12-31
Corrected mistakes in 1.5.1. The different between 1.5.2 and 1.5.0 is that
we removed some PHP 7 code.
https://github.com/php-http/discovery/compare/1.5.0...1.5.2
## 1.5.1 - 2018-12-31
This version added new features by mistake. These are reverted in 1.5.2.
Do not use 1.5.1.
### Fixed
- Removed PHP 7 code
## 1.5.0 - 2018-12-30
### Added
- Support for `nyholm/psr7` version 1.0.
- `ClassDiscovery::safeClassExists` which will help Magento users.
- Support for HTTPlug 2.0
- Support for Buzz 1.0
- Better error message when nothing found by introducing a new exception: `NoCandidateFoundException`.
### Fixed
- Fixed condition evaluation, it should stop after first invalid condition.
## 1.4.0 - 2018-02-06
### Added
- Discovery support for nyholm/psr7
## 1.3.0 - 2017-08-03
### Added
- Discovery support for CakePHP adapter
- Discovery support for Zend adapter
- Discovery support for Artax adapter
## 1.2.1 - 2017-03-02
### Fixed
- Fixed minor issue with `MockClientStrategy`, also added more tests.
## 1.2.0 - 2017-02-12
### Added
- MockClientStrategy class.
## 1.1.1 - 2016-11-27
### Changed
- Made exception messages clearer. `StrategyUnavailableException` is no longer the previous exception to `DiscoveryFailedException`.
- `CommonClassesStrategy` is using `self` instead of `static`. Using `static` makes no sense when `CommonClassesStrategy` is final.
## 1.1.0 - 2016-10-20
### Added
- Discovery support for Slim Framework factories
## 1.0.0 - 2016-07-18
### Added
- Added back `Http\Discovery\NotFoundException` to preserve BC with 0.8 version. You may upgrade from 0.8.x and 0.9.x to 1.0.0 without any BC breaks.
- Added interface `Http\Discovery\Exception` which is implemented by all our exceptions
### Changed
- Puli strategy renamed to Puli Beta strategy to prevent incompatibility with a future Puli stable
### Deprecated
- For BC reasons, the old `Http\Discovery\NotFoundException` (extending the new exception) will be thrown until version 2.0
## 0.9.1 - 2016-06-28
### Changed
- Dropping PHP 5.4 support because we use the ::class constant.
## 0.9.0 - 2016-06-25
### Added
- Discovery strategies to find classes
### Changed
- [Puli](http://puli.io) made optional
- Improved exceptions
- **[BC] `NotFoundException` moved to `Http\Discovery\Exception\NotFoundException`**
## 0.8.0 - 2016-02-11
### Changed
- Puli composer plugin must be installed separately
## 0.7.0 - 2016-01-15
### Added
- Temporary puli.phar (Beta 10) executable
### Changed
- Updated HTTPlug dependencies
- Updated Puli dependencies
- Local configuration to make tests passing
### Removed
- Puli CLI dependency
## 0.6.4 - 2016-01-07
### Fixed
- Puli [not working](https://twitter.com/PuliPHP/status/685132540588507137) with the latest json-schema
## 0.6.3 - 2016-01-04
### Changed
- Adjust Puli dependencies
## 0.6.2 - 2016-01-04
### Changed
- Make Puli CLI a requirement
## 0.6.1 - 2016-01-03
### Changed
- More flexible Puli requirement
## 0.6.0 - 2015-12-30
### Changed
- Use [Puli](http://puli.io) for discovery
- Improved exception messages
## 0.5.0 - 2015-12-25
### Changed
- Updated message factory dependency (php-http/message)
## 0.4.0 - 2015-12-17
### Added
- Array condition evaluation in the Class Discovery
### Removed
- Message factories (moved to php-http/utils)
## 0.3.0 - 2015-11-18
### Added
- HTTP Async Client Discovery
- Stream factories
### Changed
- Discoveries and Factories are final
- Message and Uri factories have the type in their names
- Diactoros Message factory uses Stream factory internally
### Fixed
- Improved docblocks for API documentation generation
## 0.2.0 - 2015-10-31
### Changed
- Renamed AdapterDiscovery to ClientDiscovery
## 0.1.1 - 2015-06-13
### Fixed
- Bad HTTP Adapter class name for Guzzle 5
## 0.1.0 - 2015-06-12
### Added
- Initial release

19
vendor/php-http/discovery/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2015-2016 PHP HTTP Team <team@php-http.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

122
vendor/php-http/discovery/README.md vendored Normal file
View File

@@ -0,0 +1,122 @@
# HTTPlug Discovery
[![Latest Version](https://img.shields.io/github/release/php-http/discovery.svg?style=flat-square)](https://github.com/php-http/discovery/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Tests](https://github.com/php-http/discovery/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/php-http/discovery/actions/workflows/ci.yml?query=branch%3Amaster)
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/discovery.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/discovery)
[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/discovery.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/discovery)
[![Total Downloads](https://img.shields.io/packagist/dt/php-http/discovery.svg?style=flat-square)](https://packagist.org/packages/php-http/discovery)
**This library provides auto-discovery and auto-installation of well-known PSR-17, PSR-18 and HTTPlug implementations.**
## Install
Via Composer
``` bash
composer require php-http/discovery
```
## Usage as a library author
Please see the [official documentation](http://php-http.readthedocs.org/en/latest/discovery.html).
If your library/SDK needs a PSR-18 client, here is a quick example.
First, you need to install a PSR-18 client and a PSR-17 factory implementations.
This should be done only for dev dependencies as you don't want to force a
specific implementation on your users:
```bash
composer require --dev symfony/http-client
composer require --dev nyholm/psr7
```
Then, you can disable the Composer plugin embeded in `php-http/discovery`
because you just installed the dev dependencies you need for testing:
```bash
composer config allow-plugins.php-http/discovery false
```
Finally, you need to require `php-http/discovery` and the generic implementations
that your library is going to need:
```bash
composer require 'php-http/discovery:^1.17'
composer require 'psr/http-client-implementation:*'
composer require 'psr/http-factory-implementation:*'
```
Now, you're ready to make an HTTP request:
```php
use Http\Discovery\Psr18Client;
$client = new Psr18Client();
$request = $client->createRequest('GET', 'https://example.com');
$response = $client->sendRequest($request);
```
Internally, this code will use whatever PSR-7, PSR-17 and PSR-18 implementations
that your users have installed.
## Usage as a library user
If you use a library/SDK that requires `php-http/discovery`, you can configure
the auto-discovery mechanism to use a specific implementation when many are
available in your project.
For example, if you have both `nyholm/psr7` and `guzzlehttp/guzzle` in your
project, you can tell `php-http/discovery` to use `guzzlehttp/guzzle` instead of
`nyholm/psr7` by running the following command:
```bash
composer config extra.discovery.psr/http-factory-implementation GuzzleHttp\\Psr7\\HttpFactory
```
This will update your `composer.json` file to add the following configuration:
```json
{
"extra": {
"discovery": {
"psr/http-factory-implementation": "GuzzleHttp\\Psr7\\HttpFactory"
}
}
}
```
Don't forget to run `composer install` to apply the changes, and ensure that
the composer plugin is enabled:
```bash
composer config allow-plugins.php-http/discovery true
composer install
```
## Testing
``` bash
composer test
```
## Contributing
Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
## Security
If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
## License
The MIT License (MIT). Please see [License File](LICENSE) for more information.

63
vendor/php-http/discovery/composer.json vendored Normal file
View File

@@ -0,0 +1,63 @@
{
"name": "php-http/discovery",
"description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations",
"type": "composer-plugin",
"license": "MIT",
"keywords": ["http", "discovery", "client", "adapter", "message", "factory", "psr7", "psr17"],
"homepage": "http://php-http.org",
"authors": [
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
}
],
"provide": {
"php-http/async-client-implementation": "*",
"php-http/client-implementation": "*",
"psr/http-client-implementation": "*",
"psr/http-factory-implementation": "*",
"psr/http-message-implementation": "*"
},
"require": {
"php": "^7.1 || ^8.0",
"composer-plugin-api": "^1.0|^2.0"
},
"require-dev": {
"composer/composer": "^1.0.2|^2.0",
"graham-campbell/phpspec-skip-example-extension": "^5.0",
"php-http/httplug": "^1.0 || ^2.0",
"php-http/message-factory": "^1.0",
"phpspec/phpspec": "^5.1 || ^6.1 || ^7.3",
"symfony/phpunit-bridge": "^6.2"
},
"autoload": {
"psr-4": {
"Http\\Discovery\\": "src/"
},
"exclude-from-classmap": [
"src/Composer/Plugin.php"
]
},
"autoload-dev": {
"psr-4": {
"spec\\Http\\Discovery\\": "spec/"
}
},
"scripts": {
"test": [
"vendor/bin/phpspec run",
"vendor/bin/simple-phpunit --group NothingInstalled"
],
"test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
},
"extra": {
"class": "Http\\Discovery\\Composer\\Plugin",
"plugin-optional": true
},
"conflict": {
"nyholm/psr7": "<1.0",
"zendframework/zend-diactoros": "*"
},
"prefer-stable": true,
"minimum-stability": "beta"
}

View File

@@ -0,0 +1,255 @@
<?php
namespace Http\Discovery;
use Http\Discovery\Exception\ClassInstantiationFailedException;
use Http\Discovery\Exception\DiscoveryFailedException;
use Http\Discovery\Exception\NoCandidateFoundException;
use Http\Discovery\Exception\StrategyUnavailableException;
use Http\Discovery\Strategy\DiscoveryStrategy;
/**
* Registry that based find results on class existence.
*
* @author David de Boer <david@ddeboer.nl>
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
abstract class ClassDiscovery
{
/**
* A list of strategies to find classes.
*
* @var DiscoveryStrategy[]
*/
private static $strategies = [
Strategy\GeneratedDiscoveryStrategy::class,
Strategy\CommonClassesStrategy::class,
Strategy\CommonPsr17ClassesStrategy::class,
Strategy\PuliBetaStrategy::class,
];
private static $deprecatedStrategies = [
Strategy\PuliBetaStrategy::class => true,
];
/**
* Discovery cache to make the second time we use discovery faster.
*
* @var array
*/
private static $cache = [];
/**
* Finds a class.
*
* @param string $type
*
* @return string|\Closure
*
* @throws DiscoveryFailedException
*/
protected static function findOneByType($type)
{
// Look in the cache
if (null !== ($class = self::getFromCache($type))) {
return $class;
}
static $skipStrategy;
$skipStrategy ?? $skipStrategy = self::safeClassExists(Strategy\GeneratedDiscoveryStrategy::class) ? false : Strategy\GeneratedDiscoveryStrategy::class;
$exceptions = [];
foreach (self::$strategies as $strategy) {
if ($skipStrategy === $strategy) {
continue;
}
try {
$candidates = $strategy::getCandidates($type);
} catch (StrategyUnavailableException $e) {
if (!isset(self::$deprecatedStrategies[$strategy])) {
$exceptions[] = $e;
}
continue;
}
foreach ($candidates as $candidate) {
if (isset($candidate['condition'])) {
if (!self::evaluateCondition($candidate['condition'])) {
continue;
}
}
// save the result for later use
self::storeInCache($type, $candidate);
return $candidate['class'];
}
$exceptions[] = new NoCandidateFoundException($strategy, $candidates);
}
throw DiscoveryFailedException::create($exceptions);
}
/**
* Get a value from cache.
*
* @param string $type
*
* @return string|null
*/
private static function getFromCache($type)
{
if (!isset(self::$cache[$type])) {
return;
}
$candidate = self::$cache[$type];
if (isset($candidate['condition'])) {
if (!self::evaluateCondition($candidate['condition'])) {
return;
}
}
return $candidate['class'];
}
/**
* Store a value in cache.
*
* @param string $type
* @param string $class
*/
private static function storeInCache($type, $class)
{
self::$cache[$type] = $class;
}
/**
* Set new strategies and clear the cache.
*
* @param string[] $strategies list of fully qualified class names that implement DiscoveryStrategy
*/
public static function setStrategies(array $strategies)
{
self::$strategies = $strategies;
self::clearCache();
}
/**
* Returns the currently configured discovery strategies as fully qualified class names.
*
* @return string[]
*/
public static function getStrategies(): iterable
{
return self::$strategies;
}
/**
* Append a strategy at the end of the strategy queue.
*
* @param string $strategy Fully qualified class name of a DiscoveryStrategy
*/
public static function appendStrategy($strategy)
{
self::$strategies[] = $strategy;
self::clearCache();
}
/**
* Prepend a strategy at the beginning of the strategy queue.
*
* @param string $strategy Fully qualified class name to a DiscoveryStrategy
*/
public static function prependStrategy($strategy)
{
array_unshift(self::$strategies, $strategy);
self::clearCache();
}
public static function clearCache()
{
self::$cache = [];
}
/**
* Evaluates conditions to boolean.
*
* @return bool
*/
protected static function evaluateCondition($condition)
{
if (is_string($condition)) {
// Should be extended for functions, extensions???
return self::safeClassExists($condition);
}
if (is_callable($condition)) {
return (bool) $condition();
}
if (is_bool($condition)) {
return $condition;
}
if (is_array($condition)) {
foreach ($condition as $c) {
if (false === static::evaluateCondition($c)) {
// Immediately stop execution if the condition is false
return false;
}
}
return true;
}
return false;
}
/**
* Get an instance of the $class.
*
* @param string|\Closure $class a FQCN of a class or a closure that instantiate the class
*
* @return object
*
* @throws ClassInstantiationFailedException
*/
protected static function instantiateClass($class)
{
try {
if (is_string($class)) {
return new $class();
}
if (is_callable($class)) {
return $class();
}
} catch (\Exception $e) {
throw new ClassInstantiationFailedException('Unexpected exception when instantiating class.', 0, $e);
}
throw new ClassInstantiationFailedException('Could not instantiate class because parameter is neither a callable nor a string');
}
/**
* We need a "safe" version of PHP's "class_exists" because Magento has a bug
* (or they call it a "feature"). Magento is throwing an exception if you do class_exists()
* on a class that ends with "Factory" and if that file does not exits.
*
* This function catches all potential exceptions and makes sure to always return a boolean.
*
* @param string $class
*
* @return bool
*/
public static function safeClassExists($class)
{
try {
return class_exists($class) || interface_exists($class);
} catch (\Exception $e) {
return false;
}
}
}

View File

@@ -0,0 +1,465 @@
<?php
namespace Http\Discovery\Composer;
use Composer\Composer;
use Composer\DependencyResolver\Pool;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Factory;
use Composer\Installer;
use Composer\IO\IOInterface;
use Composer\Json\JsonFile;
use Composer\Json\JsonManipulator;
use Composer\Package\Locker;
use Composer\Package\Version\VersionParser;
use Composer\Package\Version\VersionSelector;
use Composer\Plugin\PluginInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Repository\RepositorySet;
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use Composer\Util\Filesystem;
use Http\Discovery\ClassDiscovery;
/**
* Auto-installs missing implementations.
*
* When a dependency requires both this package and one of the supported `*-implementation`
* virtual packages, this plugin will auto-install a well-known implementation if none is
* found. The plugin will first look at already installed packages and figure out the
* preferred implementation to install based on the below stickyness rules (or on the first
* listed implementation if no rules match.)
*
* Don't miss updating src/Strategy/Common*Strategy.php when adding a new supported package.
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
class Plugin implements PluginInterface, EventSubscriberInterface
{
/**
* Describes, for every supported virtual implementation, which packages
* provide said implementation and which extra dependencies each package
* requires to provide the implementation.
*/
private const PROVIDE_RULES = [
'php-http/async-client-implementation' => [
'symfony/http-client:>=6.3' => ['guzzlehttp/promises', 'psr/http-factory-implementation', 'php-http/httplug'],
'symfony/http-client' => ['guzzlehttp/promises', 'php-http/message-factory', 'psr/http-factory-implementation', 'php-http/httplug'],
'php-http/guzzle7-adapter' => [],
'php-http/guzzle6-adapter' => [],
'php-http/curl-client' => [],
'php-http/react-adapter' => [],
],
'php-http/client-implementation' => [
'symfony/http-client:>=6.3' => ['psr/http-factory-implementation', 'php-http/httplug'],
'symfony/http-client' => ['php-http/message-factory', 'psr/http-factory-implementation', 'php-http/httplug'],
'php-http/guzzle7-adapter' => [],
'php-http/guzzle6-adapter' => [],
'php-http/cakephp-adapter' => [],
'php-http/curl-client' => [],
'php-http/react-adapter' => [],
'php-http/buzz-adapter' => [],
'php-http/artax-adapter' => [],
'kriswallsmith/buzz:^1' => [],
],
'psr/http-client-implementation' => [
'symfony/http-client' => ['psr/http-factory-implementation', 'psr/http-client'],
'guzzlehttp/guzzle' => [],
'kriswallsmith/buzz:^1' => [],
],
'psr/http-message-implementation' => [
'php-http/discovery' => ['psr/http-factory-implementation'],
],
'psr/http-factory-implementation' => [
'nyholm/psr7' => [],
'guzzlehttp/psr7:>=2' => [],
'slim/psr7' => [],
'laminas/laminas-diactoros' => [],
'phalcon/cphalcon:^4' => [],
'http-interop/http-factory-guzzle' => [],
'http-interop/http-factory-diactoros' => [],
'http-interop/http-factory-slim' => [],
'httpsoft/http-message' => [],
],
];
/**
* Describes which package should be preferred on the left side
* depending on which one is already installed on the right side.
*/
private const STICKYNESS_RULES = [
'symfony/http-client' => 'symfony/framework-bundle',
'php-http/guzzle7-adapter' => 'guzzlehttp/guzzle:^7',
'php-http/guzzle6-adapter' => 'guzzlehttp/guzzle:^6',
'php-http/guzzle5-adapter' => 'guzzlehttp/guzzle:^5',
'php-http/cakephp-adapter' => 'cakephp/cakephp',
'php-http/react-adapter' => 'react/event-loop',
'php-http/buzz-adapter' => 'kriswallsmith/buzz:^0.15.1',
'php-http/artax-adapter' => 'amphp/artax:^3',
'http-interop/http-factory-guzzle' => 'guzzlehttp/psr7:^1',
'http-interop/http-factory-slim' => 'slim/slim:^3',
];
private const INTERFACE_MAP = [
'php-http/async-client-implementation' => [
'Http\Client\HttpAsyncClient',
],
'php-http/client-implementation' => [
'Http\Client\HttpClient',
],
'psr/http-client-implementation' => [
'Psr\Http\Client\ClientInterface',
],
'psr/http-factory-implementation' => [
'Psr\Http\Message\RequestFactoryInterface',
'Psr\Http\Message\ResponseFactoryInterface',
'Psr\Http\Message\ServerRequestFactoryInterface',
'Psr\Http\Message\StreamFactoryInterface',
'Psr\Http\Message\UploadedFileFactoryInterface',
'Psr\Http\Message\UriFactoryInterface',
],
];
public static function getSubscribedEvents(): array
{
return [
ScriptEvents::PRE_AUTOLOAD_DUMP => 'preAutoloadDump',
ScriptEvents::POST_UPDATE_CMD => 'postUpdate',
];
}
public function activate(Composer $composer, IOInterface $io): void
{
}
public function deactivate(Composer $composer, IOInterface $io)
{
}
public function uninstall(Composer $composer, IOInterface $io)
{
}
public function postUpdate(Event $event)
{
$composer = $event->getComposer();
$repo = $composer->getRepositoryManager()->getLocalRepository();
$requires = [
$composer->getPackage()->getRequires(),
$composer->getPackage()->getDevRequires(),
];
$pinnedAbstractions = [];
$pinned = $composer->getPackage()->getExtra()['discovery'] ?? [];
foreach (self::INTERFACE_MAP as $abstraction => $interfaces) {
foreach (isset($pinned[$abstraction]) ? [] : $interfaces as $interface) {
if (!isset($pinned[$interface])) {
continue 2;
}
}
$pinnedAbstractions[$abstraction] = true;
}
$missingRequires = $this->getMissingRequires($repo, $requires, 'project' === $composer->getPackage()->getType(), $pinnedAbstractions);
$missingRequires = [
'require' => array_fill_keys(array_merge([], ...array_values($missingRequires[0])), '*'),
'require-dev' => array_fill_keys(array_merge([], ...array_values($missingRequires[1])), '*'),
'remove' => array_fill_keys(array_merge([], ...array_values($missingRequires[2])), '*'),
];
if (!$missingRequires = array_filter($missingRequires)) {
return;
}
$composerJsonContents = file_get_contents(Factory::getComposerFile());
$this->updateComposerJson($missingRequires, $composer->getConfig()->get('sort-packages'));
$installer = null;
// Find the composer installer, hack borrowed from symfony/flex
foreach (debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT) as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof Installer) {
$installer = $trace['object'];
break;
}
}
if (!$installer) {
return;
}
$event->stopPropagation();
$dispatcher = $composer->getEventDispatcher();
$disableScripts = !method_exists($dispatcher, 'setRunScripts') || !((array) $dispatcher)["\0*\0runScripts"];
$composer = Factory::create($event->getIO(), null, false, $disableScripts);
/** @var Installer $installer */
$installer = clone $installer;
if (method_exists($installer, 'setAudit')) {
$trace['object']->setAudit(false);
}
// we need a clone of the installer to preserve its configuration state but with our own service objects
$installer->__construct(
$event->getIO(),
$composer->getConfig(),
$composer->getPackage(),
$composer->getDownloadManager(),
$composer->getRepositoryManager(),
$composer->getLocker(),
$composer->getInstallationManager(),
$composer->getEventDispatcher(),
$composer->getAutoloadGenerator()
);
if (method_exists($installer, 'setPlatformRequirementFilter')) {
$installer->setPlatformRequirementFilter(((array) $trace['object'])["\0*\0platformRequirementFilter"]);
}
if (0 !== $installer->run()) {
file_put_contents(Factory::getComposerFile(), $composerJsonContents);
return;
}
$versionSelector = new VersionSelector(ClassDiscovery::safeClassExists(RepositorySet::class) ? new RepositorySet() : new Pool());
$updateComposerJson = false;
foreach ($composer->getRepositoryManager()->getLocalRepository()->getPackages() as $package) {
foreach (['require', 'require-dev'] as $key) {
if (!isset($missingRequires[$key][$package->getName()])) {
continue;
}
$updateComposerJson = true;
$missingRequires[$key][$package->getName()] = $versionSelector->findRecommendedRequireVersion($package);
}
}
if ($updateComposerJson) {
$this->updateComposerJson($missingRequires, $composer->getConfig()->get('sort-packages'));
$this->updateComposerLock($composer, $event->getIO());
}
}
public function getMissingRequires(InstalledRepositoryInterface $repo, array $requires, bool $isProject, array $pinnedAbstractions): array
{
$allPackages = [];
$devPackages = method_exists($repo, 'getDevPackageNames') ? array_fill_keys($repo->getDevPackageNames(), true) : [];
// One must require "php-http/discovery"
// to opt-in for auto-installation of virtual package implementations
if (!isset($requires[0]['php-http/discovery'])) {
$requires = [[], []];
}
foreach ($repo->getPackages() as $package) {
$allPackages[$package->getName()] = true;
if (1 < \count($names = $package->getNames(false))) {
$allPackages += array_fill_keys($names, false);
if (isset($devPackages[$package->getName()])) {
$devPackages += $names;
}
}
if (isset($package->getRequires()['php-http/discovery'])) {
$requires[(int) isset($devPackages[$package->getName()])] += $package->getRequires();
}
}
$missingRequires = [[], [], []];
$versionParser = new VersionParser();
if (ClassDiscovery::safeClassExists(\Phalcon\Http\Message\RequestFactory::class, false)) {
$missingRequires[0]['psr/http-factory-implementation'] = [];
$missingRequires[1]['psr/http-factory-implementation'] = [];
}
foreach ($requires as $dev => $rules) {
$abstractions = [];
$rules = array_intersect_key(self::PROVIDE_RULES, $rules);
while ($rules) {
$abstraction = key($rules);
if (isset($pinnedAbstractions[$abstraction])) {
unset($rules[$abstraction]);
continue;
}
$abstractions[] = $abstraction;
foreach (array_shift($rules) as $candidate => $deps) {
[$candidate, $version] = explode(':', $candidate, 2) + [1 => null];
if (!isset($allPackages[$candidate])) {
continue;
}
if (null !== $version && !$repo->findPackage($candidate, $versionParser->parseConstraints($version))) {
continue;
}
if ($isProject && !$dev && isset($devPackages[$candidate])) {
$missingRequires[0][$abstraction] = [$candidate];
$missingRequires[2][$abstraction] = [$candidate];
} else {
$missingRequires[$dev][$abstraction] = [];
}
foreach ($deps as $dep) {
if (isset(self::PROVIDE_RULES[$dep])) {
$rules[$dep] = self::PROVIDE_RULES[$dep];
} elseif (!isset($allPackages[$dep])) {
$missingRequires[$dev][$abstraction][] = $dep;
} elseif ($isProject && !$dev && isset($devPackages[$dep])) {
$missingRequires[0][$abstraction][] = $dep;
$missingRequires[2][$abstraction][] = $dep;
}
}
break;
}
}
while ($abstractions) {
$abstraction = array_shift($abstractions);
if (isset($missingRequires[$dev][$abstraction])) {
continue;
}
$candidates = self::PROVIDE_RULES[$abstraction];
foreach ($candidates as $candidate => $deps) {
[$candidate, $version] = explode(':', $candidate, 2) + [1 => null];
if (null !== $version && !$repo->findPackage($candidate, $versionParser->parseConstraints($version))) {
continue;
}
if (isset($allPackages[$candidate]) && (!$isProject || $dev || !isset($devPackages[$candidate]))) {
continue 2;
}
}
foreach (array_intersect_key(self::STICKYNESS_RULES, $candidates) as $candidate => $stickyRule) {
[$stickyName, $stickyVersion] = explode(':', $stickyRule, 2) + [1 => null];
if (!isset($allPackages[$stickyName]) || ($isProject && !$dev && isset($devPackages[$stickyName]))) {
continue;
}
if (null !== $stickyVersion && !$repo->findPackage($stickyName, $versionParser->parseConstraints($stickyVersion))) {
continue;
}
$candidates = [$candidate => $candidates[$candidate]];
break;
}
$dep = key($candidates);
[$dep] = explode(':', $dep, 2);
$missingRequires[$dev][$abstraction] = [$dep];
if ($isProject && !$dev && isset($devPackages[$dep])) {
$missingRequires[2][$abstraction][] = $dep;
}
}
}
$missingRequires[1] = array_diff_key($missingRequires[1], $missingRequires[0]);
return $missingRequires;
}
public function preAutoloadDump(Event $event)
{
$filesystem = new Filesystem();
// Double realpath() on purpose, see https://bugs.php.net/72738
$vendorDir = $filesystem->normalizePath(realpath(realpath($event->getComposer()->getConfig()->get('vendor-dir'))));
$filesystem->ensureDirectoryExists($vendorDir.'/composer');
$pinned = $event->getComposer()->getPackage()->getExtra()['discovery'] ?? [];
$candidates = [];
$allInterfaces = array_merge(...array_values(self::INTERFACE_MAP));
foreach ($pinned as $abstraction => $class) {
if (isset(self::INTERFACE_MAP[$abstraction])) {
$interfaces = self::INTERFACE_MAP[$abstraction];
} elseif (false !== $k = array_search($abstraction, $allInterfaces, true)) {
$interfaces = [$allInterfaces[$k]];
} else {
throw new \UnexpectedValueException(sprintf('Invalid "extra.discovery" pinned in composer.json: "%s" is not one of ["%s"].', $abstraction, implode('", "', array_keys(self::INTERFACE_MAP))));
}
foreach ($interfaces as $interface) {
$candidates[] = sprintf("case %s: return [['class' => %s]];\n", var_export($interface, true), var_export($class, true));
}
}
$file = $vendorDir.'/composer/GeneratedDiscoveryStrategy.php';
if (!$candidates) {
if (file_exists($file)) {
unlink($file);
}
return;
}
$candidates = implode(' ', $candidates);
$code = <<<EOPHP
<?php
namespace Http\Discovery\Strategy;
class GeneratedDiscoveryStrategy implements DiscoveryStrategy
{
public static function getCandidates(\$type)
{
switch (\$type) {
$candidates
default: return [];
}
}
}
EOPHP
;
if (!file_exists($file) || $code !== file_get_contents($file)) {
file_put_contents($file, $code);
}
$rootPackage = $event->getComposer()->getPackage();
$autoload = $rootPackage->getAutoload();
$autoload['classmap'][] = $vendorDir.'/composer/GeneratedDiscoveryStrategy.php';
$rootPackage->setAutoload($autoload);
}
private function updateComposerJson(array $missingRequires, bool $sortPackages)
{
$file = Factory::getComposerFile();
$contents = file_get_contents($file);
$manipulator = new JsonManipulator($contents);
foreach ($missingRequires as $key => $packages) {
foreach ($packages as $package => $constraint) {
if ('remove' === $key) {
$manipulator->removeSubNode('require-dev', $package);
} else {
$manipulator->addLink($key, $package, $constraint, $sortPackages);
}
}
}
file_put_contents($file, $manipulator->getContents());
}
private function updateComposerLock(Composer $composer, IOInterface $io)
{
$lock = substr(Factory::getComposerFile(), 0, -4).'lock';
$composerJson = file_get_contents(Factory::getComposerFile());
$lockFile = new JsonFile($lock, null, $io);
$locker = ClassDiscovery::safeClassExists(RepositorySet::class)
? new Locker($io, $lockFile, $composer->getInstallationManager(), $composerJson)
: new Locker($io, $lockFile, $composer->getRepositoryManager(), $composer->getInstallationManager(), $composerJson);
$lockData = $locker->getLockData();
$lockData['content-hash'] = Locker::getContentHash($composerJson);
$lockFile->write($lockData);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Http\Discovery;
/**
* An interface implemented by all discovery related exceptions.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface Exception extends \Throwable
{
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Http\Discovery\Exception;
use Http\Discovery\Exception;
/**
* Thrown when a class fails to instantiate.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class ClassInstantiationFailedException extends \RuntimeException implements Exception
{
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Http\Discovery\Exception;
use Http\Discovery\Exception;
/**
* Thrown when all discovery strategies fails to find a resource.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class DiscoveryFailedException extends \Exception implements Exception
{
/**
* @var \Exception[]
*/
private $exceptions;
/**
* @param string $message
* @param \Exception[] $exceptions
*/
public function __construct($message, array $exceptions = [])
{
$this->exceptions = $exceptions;
parent::__construct($message);
}
/**
* @param \Exception[] $exceptions
*/
public static function create($exceptions)
{
$message = 'Could not find resource using any discovery strategy. Find more information at http://docs.php-http.org/en/latest/discovery.html#common-errors';
foreach ($exceptions as $e) {
$message .= "\n - ".$e->getMessage();
}
$message .= "\n\n";
return new self($message, $exceptions);
}
/**
* @return \Exception[]
*/
public function getExceptions()
{
return $this->exceptions;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Http\Discovery\Exception;
use Http\Discovery\Exception;
/**
* When we have used a strategy but no candidates provided by that strategy could be used.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class NoCandidateFoundException extends \Exception implements Exception
{
/**
* @param string $strategy
*/
public function __construct($strategy, array $candidates)
{
$classes = array_map(
function ($a) {
return $a['class'];
},
$candidates
);
$message = sprintf(
'No valid candidate found using strategy "%s". We tested the following candidates: %s.',
$strategy,
implode(', ', array_map([$this, 'stringify'], $classes))
);
parent::__construct($message);
}
private function stringify($mixed)
{
if (is_string($mixed)) {
return $mixed;
}
if (is_array($mixed) && 2 === count($mixed)) {
return sprintf('%s::%s', $this->stringify($mixed[0]), $mixed[1]);
}
return is_object($mixed) ? get_class($mixed) : gettype($mixed);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Http\Discovery\Exception;
use Http\Discovery\Exception;
/**
* Thrown when a discovery does not find any matches.
*
* @final do NOT extend this class, not final for BC reasons
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
/* final */ class NotFoundException extends \RuntimeException implements Exception
{
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Http\Discovery\Exception;
/**
* Thrown when we can't use Puli for discovery.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class PuliUnavailableException extends StrategyUnavailableException
{
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Http\Discovery\Exception;
use Http\Discovery\Exception;
/**
* This exception is thrown when we cannot use a discovery strategy. This is *not* thrown when
* the discovery fails to find a class.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class StrategyUnavailableException extends \RuntimeException implements Exception
{
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Http\Discovery;
use Http\Client\HttpAsyncClient;
use Http\Discovery\Exception\DiscoveryFailedException;
/**
* Finds an HTTP Asynchronous Client.
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
*/
final class HttpAsyncClientDiscovery extends ClassDiscovery
{
/**
* Finds an HTTP Async Client.
*
* @return HttpAsyncClient
*
* @throws Exception\NotFoundException
*/
public static function find()
{
try {
$asyncClient = static::findOneByType(HttpAsyncClient::class);
} catch (DiscoveryFailedException $e) {
throw new NotFoundException('No HTTPlug async clients found. Make sure to install a package providing "php-http/async-client-implementation". Example: "php-http/guzzle6-adapter".', 0, $e);
}
return static::instantiateClass($asyncClient);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Http\Discovery;
use Http\Client\HttpClient;
use Http\Discovery\Exception\DiscoveryFailedException;
/**
* Finds an HTTP Client.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*
* @deprecated This will be removed in 2.0. Consider using Psr18ClientDiscovery.
*/
final class HttpClientDiscovery extends ClassDiscovery
{
/**
* Finds an HTTP Client.
*
* @return HttpClient
*
* @throws Exception\NotFoundException
*/
public static function find()
{
try {
$client = static::findOneByType(HttpClient::class);
} catch (DiscoveryFailedException $e) {
throw new NotFoundException('No HTTPlug clients found. Make sure to install a package providing "php-http/client-implementation". Example: "php-http/guzzle6-adapter".', 0, $e);
}
return static::instantiateClass($client);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Http\Discovery;
use Http\Discovery\Exception\DiscoveryFailedException;
use Http\Message\MessageFactory;
/**
* Finds a Message Factory.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*
* @deprecated This will be removed in 2.0. Consider using Psr17FactoryDiscovery.
*/
final class MessageFactoryDiscovery extends ClassDiscovery
{
/**
* Finds a Message Factory.
*
* @return MessageFactory
*
* @throws Exception\NotFoundException
*/
public static function find()
{
try {
$messageFactory = static::findOneByType(MessageFactory::class);
} catch (DiscoveryFailedException $e) {
throw new NotFoundException('No php-http message factories found. Note that the php-http message factories are deprecated in favor of the PSR-17 message factories. To use the legacy Guzzle, Diactoros or Slim Framework factories of php-http, install php-http/message and php-http/message-factory and the chosen message implementation.', 0, $e);
}
return static::instantiateClass($messageFactory);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Http\Discovery;
/**
* Thrown when a discovery does not find any matches.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*
* @deprecated since since version 1.0, and will be removed in 2.0. Use {@link \Http\Discovery\Exception\NotFoundException} instead.
*/
final class NotFoundException extends \Http\Discovery\Exception\NotFoundException
{
}

View File

@@ -0,0 +1,303 @@
<?php
namespace Http\Discovery;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UploadedFileInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
/**
* A generic PSR-17 implementation.
*
* You can create this class with concrete factory instances or let
* it use discovery to find suitable implementations as needed.
*
* This class also provides two additional methods that are not in PSR17,
* to help with creating PSR-7 objects from PHP superglobals:
* - createServerRequestFromGlobals()
* - createUriFromGlobals()
*
* The code in this class is inspired by the "nyholm/psr7", "guzzlehttp/psr7"
* and "symfony/http-foundation" packages, all licenced under MIT.
*
* Copyright (c) 2004-2023 Fabien Potencier <fabien@symfony.com>
* Copyright (c) 2015 Michael Dowling <mtdowling@gmail.com>
* Copyright (c) 2015 Márk Sági-Kazár <mark.sagikazar@gmail.com>
* Copyright (c) 2015 Graham Campbell <hello@gjcampbell.co.uk>
* Copyright (c) 2016 Tobias Schultze <webmaster@tubo-world.de>
* Copyright (c) 2016 George Mponos <gmponos@gmail.com>
* Copyright (c) 2016-2018 Tobias Nyholm <tobias.nyholm@gmail.com>
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class Psr17Factory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
{
private $requestFactory;
private $responseFactory;
private $serverRequestFactory;
private $streamFactory;
private $uploadedFileFactory;
private $uriFactory;
public function __construct(
RequestFactoryInterface $requestFactory = null,
ResponseFactoryInterface $responseFactory = null,
ServerRequestFactoryInterface $serverRequestFactory = null,
StreamFactoryInterface $streamFactory = null,
UploadedFileFactoryInterface $uploadedFileFactory = null,
UriFactoryInterface $uriFactory = null
) {
$this->requestFactory = $requestFactory;
$this->responseFactory = $responseFactory;
$this->serverRequestFactory = $serverRequestFactory;
$this->streamFactory = $streamFactory;
$this->uploadedFileFactory = $uploadedFileFactory;
$this->uriFactory = $uriFactory;
$this->setFactory($requestFactory);
$this->setFactory($responseFactory);
$this->setFactory($serverRequestFactory);
$this->setFactory($streamFactory);
$this->setFactory($uploadedFileFactory);
$this->setFactory($uriFactory);
}
/**
* @param UriInterface|string $uri
*/
public function createRequest(string $method, $uri): RequestInterface
{
$factory = $this->requestFactory ?? $this->setFactory(Psr17FactoryDiscovery::findRequestFactory());
return $factory->createRequest(...\func_get_args());
}
public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
{
$factory = $this->responseFactory ?? $this->setFactory(Psr17FactoryDiscovery::findResponseFactory());
return $factory->createResponse(...\func_get_args());
}
/**
* @param UriInterface|string $uri
*/
public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
{
$factory = $this->serverRequestFactory ?? $this->setFactory(Psr17FactoryDiscovery::findServerRequestFactory());
return $factory->createServerRequest(...\func_get_args());
}
public function createServerRequestFromGlobals(array $server = null, array $get = null, array $post = null, array $cookie = null, array $files = null, StreamInterface $body = null): ServerRequestInterface
{
$server = $server ?? $_SERVER;
$request = $this->createServerRequest($server['REQUEST_METHOD'] ?? 'GET', $this->createUriFromGlobals($server), $server);
return $this->buildServerRequestFromGlobals($request, $server, $files ?? $_FILES)
->withQueryParams($get ?? $_GET)
->withParsedBody($post ?? $_POST)
->withCookieParams($cookie ?? $_COOKIE)
->withBody($body ?? $this->createStreamFromFile('php://input', 'r+'));
}
public function createStream(string $content = ''): StreamInterface
{
$factory = $this->streamFactory ?? $this->setFactory(Psr17FactoryDiscovery::findStreamFactory());
return $factory->createStream($content);
}
public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface
{
$factory = $this->streamFactory ?? $this->setFactory(Psr17FactoryDiscovery::findStreamFactory());
return $factory->createStreamFromFile($filename, $mode);
}
/**
* @param resource $resource
*/
public function createStreamFromResource($resource): StreamInterface
{
$factory = $this->streamFactory ?? $this->setFactory(Psr17FactoryDiscovery::findStreamFactory());
return $factory->createStreamFromResource($resource);
}
public function createUploadedFile(StreamInterface $stream, int $size = null, int $error = \UPLOAD_ERR_OK, string $clientFilename = null, string $clientMediaType = null): UploadedFileInterface
{
$factory = $this->uploadedFileFactory ?? $this->setFactory(Psr17FactoryDiscovery::findUploadedFileFactory());
return $factory->createUploadedFile(...\func_get_args());
}
public function createUri(string $uri = ''): UriInterface
{
$factory = $this->uriFactory ?? $this->setFactory(Psr17FactoryDiscovery::findUriFactory());
return $factory->createUri(...\func_get_args());
}
public function createUriFromGlobals(array $server = null): UriInterface
{
return $this->buildUriFromGlobals($this->createUri(''), $server ?? $_SERVER);
}
private function setFactory($factory)
{
if (!$this->requestFactory && $factory instanceof RequestFactoryInterface) {
$this->requestFactory = $factory;
}
if (!$this->responseFactory && $factory instanceof ResponseFactoryInterface) {
$this->responseFactory = $factory;
}
if (!$this->serverRequestFactory && $factory instanceof ServerRequestFactoryInterface) {
$this->serverRequestFactory = $factory;
}
if (!$this->streamFactory && $factory instanceof StreamFactoryInterface) {
$this->streamFactory = $factory;
}
if (!$this->uploadedFileFactory && $factory instanceof UploadedFileFactoryInterface) {
$this->uploadedFileFactory = $factory;
}
if (!$this->uriFactory && $factory instanceof UriFactoryInterface) {
$this->uriFactory = $factory;
}
return $factory;
}
private function buildServerRequestFromGlobals(ServerRequestInterface $request, array $server, array $files): ServerRequestInterface
{
$request = $request
->withProtocolVersion(isset($server['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $server['SERVER_PROTOCOL']) : '1.1')
->withUploadedFiles($this->normalizeFiles($files));
$headers = [];
foreach ($server as $k => $v) {
if (0 === strpos($k, 'HTTP_')) {
$k = substr($k, 5);
} elseif (!\in_array($k, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) {
continue;
}
$k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $k))));
$headers[$k] = $v;
}
if (!isset($headers['Authorization'])) {
if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
$headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
$headers['Authorization'] = 'Basic '.base64_encode($_SERVER['PHP_AUTH_USER'].':'.($_SERVER['PHP_AUTH_PW'] ?? ''));
} elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
$headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
}
}
foreach ($headers as $k => $v) {
try {
$request = $request->withHeader($k, $v);
} catch (\InvalidArgumentException $e) {
// ignore invalid headers
}
}
return $request;
}
private function buildUriFromGlobals(UriInterface $uri, array $server): UriInterface
{
$uri = $uri->withScheme(!empty($server['HTTPS']) && 'off' !== strtolower($server['HTTPS']) ? 'https' : 'http');
$hasPort = false;
if (isset($server['HTTP_HOST'])) {
$parts = parse_url('http://'.$server['HTTP_HOST']);
$uri = $uri->withHost($parts['host'] ?? 'localhost');
if ($parts['port'] ?? false) {
$hasPort = true;
$uri = $uri->withPort($parts['port']);
}
} else {
$uri = $uri->withHost($server['SERVER_NAME'] ?? $server['SERVER_ADDR'] ?? 'localhost');
}
if (!$hasPort && isset($server['SERVER_PORT'])) {
$uri = $uri->withPort($server['SERVER_PORT']);
}
$hasQuery = false;
if (isset($server['REQUEST_URI'])) {
$requestUriParts = explode('?', $server['REQUEST_URI'], 2);
$uri = $uri->withPath($requestUriParts[0]);
if (isset($requestUriParts[1])) {
$hasQuery = true;
$uri = $uri->withQuery($requestUriParts[1]);
}
}
if (!$hasQuery && isset($server['QUERY_STRING'])) {
$uri = $uri->withQuery($server['QUERY_STRING']);
}
return $uri;
}
private function normalizeFiles(array $files): array
{
foreach ($files as $k => $v) {
if ($v instanceof UploadedFileInterface) {
continue;
}
if (!\is_array($v)) {
unset($files[$k]);
} elseif (!isset($v['tmp_name'])) {
$files[$k] = $this->normalizeFiles($v);
} else {
$files[$k] = $this->createUploadedFileFromSpec($v);
}
}
return $files;
}
/**
* Create and return an UploadedFile instance from a $_FILES specification.
*
* @param array $value $_FILES struct
*
* @return UploadedFileInterface|UploadedFileInterface[]
*/
private function createUploadedFileFromSpec(array $value)
{
if (!is_array($tmpName = $value['tmp_name'])) {
$file = is_file($tmpName) ? $this->createStreamFromFile($tmpName, 'r') : $this->createStream();
return $this->createUploadedFile($file, $value['size'], $value['error'], $value['name'], $value['type']);
}
foreach ($tmpName as $k => $v) {
$tmpName[$k] = $this->createUploadedFileFromSpec([
'tmp_name' => $v,
'size' => $value['size'][$k] ?? null,
'error' => $value['error'][$k] ?? null,
'name' => $value['name'][$k] ?? null,
'type' => $value['type'][$k] ?? null,
]);
}
return $tmpName;
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace Http\Discovery;
use Http\Discovery\Exception\DiscoveryFailedException;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
/**
* Finds PSR-17 factories.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class Psr17FactoryDiscovery extends ClassDiscovery
{
private static function createException($type, Exception $e)
{
return new \Http\Discovery\Exception\NotFoundException(
'No PSR-17 '.$type.' found. Install a package from this list: https://packagist.org/providers/psr/http-factory-implementation',
0,
$e
);
}
/**
* @return RequestFactoryInterface
*
* @throws Exception\NotFoundException
*/
public static function findRequestFactory()
{
try {
$messageFactory = static::findOneByType(RequestFactoryInterface::class);
} catch (DiscoveryFailedException $e) {
throw self::createException('request factory', $e);
}
return static::instantiateClass($messageFactory);
}
/**
* @return ResponseFactoryInterface
*
* @throws Exception\NotFoundException
*/
public static function findResponseFactory()
{
try {
$messageFactory = static::findOneByType(ResponseFactoryInterface::class);
} catch (DiscoveryFailedException $e) {
throw self::createException('response factory', $e);
}
return static::instantiateClass($messageFactory);
}
/**
* @return ServerRequestFactoryInterface
*
* @throws Exception\NotFoundException
*/
public static function findServerRequestFactory()
{
try {
$messageFactory = static::findOneByType(ServerRequestFactoryInterface::class);
} catch (DiscoveryFailedException $e) {
throw self::createException('server request factory', $e);
}
return static::instantiateClass($messageFactory);
}
/**
* @return StreamFactoryInterface
*
* @throws Exception\NotFoundException
*/
public static function findStreamFactory()
{
try {
$messageFactory = static::findOneByType(StreamFactoryInterface::class);
} catch (DiscoveryFailedException $e) {
throw self::createException('stream factory', $e);
}
return static::instantiateClass($messageFactory);
}
/**
* @return UploadedFileFactoryInterface
*
* @throws Exception\NotFoundException
*/
public static function findUploadedFileFactory()
{
try {
$messageFactory = static::findOneByType(UploadedFileFactoryInterface::class);
} catch (DiscoveryFailedException $e) {
throw self::createException('uploaded file factory', $e);
}
return static::instantiateClass($messageFactory);
}
/**
* @return UriFactoryInterface
*
* @throws Exception\NotFoundException
*/
public static function findUriFactory()
{
try {
$messageFactory = static::findOneByType(UriFactoryInterface::class);
} catch (DiscoveryFailedException $e) {
throw self::createException('url factory', $e);
}
return static::instantiateClass($messageFactory);
}
/**
* @return UriFactoryInterface
*
* @throws Exception\NotFoundException
*
* @deprecated This will be removed in 2.0. Consider using the findUriFactory() method.
*/
public static function findUrlFactory()
{
return static::findUriFactory();
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Http\Discovery;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
/**
* A generic PSR-18 and PSR-17 implementation.
*
* You can create this class with concrete client and factory instances
* or let it use discovery to find suitable implementations as needed.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class Psr18Client extends Psr17Factory implements ClientInterface
{
private $client;
public function __construct(
ClientInterface $client = null,
RequestFactoryInterface $requestFactory = null,
ResponseFactoryInterface $responseFactory = null,
ServerRequestFactoryInterface $serverRequestFactory = null,
StreamFactoryInterface $streamFactory = null,
UploadedFileFactoryInterface $uploadedFileFactory = null,
UriFactoryInterface $uriFactory = null
) {
parent::__construct($requestFactory, $responseFactory, $serverRequestFactory, $streamFactory, $uploadedFileFactory, $uriFactory);
$this->client = $client ?? Psr18ClientDiscovery::find();
}
public function sendRequest(RequestInterface $request): ResponseInterface
{
return $this->client->sendRequest($request);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Http\Discovery;
use Http\Discovery\Exception\DiscoveryFailedException;
use Psr\Http\Client\ClientInterface;
/**
* Finds a PSR-18 HTTP Client.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class Psr18ClientDiscovery extends ClassDiscovery
{
/**
* Finds a PSR-18 HTTP Client.
*
* @return ClientInterface
*
* @throws Exception\NotFoundException
*/
public static function find()
{
try {
$client = static::findOneByType(ClientInterface::class);
} catch (DiscoveryFailedException $e) {
throw new \Http\Discovery\Exception\NotFoundException('No PSR-18 clients found. Make sure to install a package providing "psr/http-client-implementation". Example: "php-http/guzzle7-adapter".', 0, $e);
}
return static::instantiateClass($client);
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace Http\Discovery\Strategy;
use GuzzleHttp\Client as GuzzleHttp;
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Psr7\Request as GuzzleRequest;
use Http\Adapter\Artax\Client as Artax;
use Http\Adapter\Buzz\Client as Buzz;
use Http\Adapter\Cake\Client as Cake;
use Http\Adapter\Guzzle5\Client as Guzzle5;
use Http\Adapter\Guzzle6\Client as Guzzle6;
use Http\Adapter\Guzzle7\Client as Guzzle7;
use Http\Adapter\React\Client as React;
use Http\Client\Curl\Client as Curl;
use Http\Client\HttpAsyncClient;
use Http\Client\HttpClient;
use Http\Client\Socket\Client as Socket;
use Http\Discovery\ClassDiscovery;
use Http\Discovery\Exception\NotFoundException;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Message\MessageFactory;
use Http\Message\MessageFactory\DiactorosMessageFactory;
use Http\Message\MessageFactory\GuzzleMessageFactory;
use Http\Message\MessageFactory\SlimMessageFactory;
use Http\Message\StreamFactory;
use Http\Message\StreamFactory\DiactorosStreamFactory;
use Http\Message\StreamFactory\GuzzleStreamFactory;
use Http\Message\StreamFactory\SlimStreamFactory;
use Http\Message\UriFactory;
use Http\Message\UriFactory\DiactorosUriFactory;
use Http\Message\UriFactory\GuzzleUriFactory;
use Http\Message\UriFactory\SlimUriFactory;
use Laminas\Diactoros\Request as DiactorosRequest;
use Nyholm\Psr7\Factory\HttplugFactory as NyholmHttplugFactory;
use Psr\Http\Client\ClientInterface as Psr18Client;
use Psr\Http\Message\RequestFactoryInterface as Psr17RequestFactory;
use Slim\Http\Request as SlimRequest;
use Symfony\Component\HttpClient\HttplugClient as SymfonyHttplug;
use Symfony\Component\HttpClient\Psr18Client as SymfonyPsr18;
/**
* @internal
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*
* Don't miss updating src/Composer/Plugin.php when adding a new supported class.
*/
final class CommonClassesStrategy implements DiscoveryStrategy
{
/**
* @var array
*/
private static $classes = [
MessageFactory::class => [
['class' => NyholmHttplugFactory::class, 'condition' => [NyholmHttplugFactory::class]],
['class' => GuzzleMessageFactory::class, 'condition' => [GuzzleRequest::class, GuzzleMessageFactory::class]],
['class' => DiactorosMessageFactory::class, 'condition' => [DiactorosRequest::class, DiactorosMessageFactory::class]],
['class' => SlimMessageFactory::class, 'condition' => [SlimRequest::class, SlimMessageFactory::class]],
],
StreamFactory::class => [
['class' => NyholmHttplugFactory::class, 'condition' => [NyholmHttplugFactory::class]],
['class' => GuzzleStreamFactory::class, 'condition' => [GuzzleRequest::class, GuzzleStreamFactory::class]],
['class' => DiactorosStreamFactory::class, 'condition' => [DiactorosRequest::class, DiactorosStreamFactory::class]],
['class' => SlimStreamFactory::class, 'condition' => [SlimRequest::class, SlimStreamFactory::class]],
],
UriFactory::class => [
['class' => NyholmHttplugFactory::class, 'condition' => [NyholmHttplugFactory::class]],
['class' => GuzzleUriFactory::class, 'condition' => [GuzzleRequest::class, GuzzleUriFactory::class]],
['class' => DiactorosUriFactory::class, 'condition' => [DiactorosRequest::class, DiactorosUriFactory::class]],
['class' => SlimUriFactory::class, 'condition' => [SlimRequest::class, SlimUriFactory::class]],
],
HttpAsyncClient::class => [
['class' => SymfonyHttplug::class, 'condition' => [SymfonyHttplug::class, Promise::class, [self::class, 'isPsr17FactoryInstalled']]],
['class' => Guzzle7::class, 'condition' => Guzzle7::class],
['class' => Guzzle6::class, 'condition' => Guzzle6::class],
['class' => Curl::class, 'condition' => Curl::class],
['class' => React::class, 'condition' => React::class],
],
HttpClient::class => [
['class' => SymfonyHttplug::class, 'condition' => [SymfonyHttplug::class, [self::class, 'isPsr17FactoryInstalled']]],
['class' => Guzzle7::class, 'condition' => Guzzle7::class],
['class' => Guzzle6::class, 'condition' => Guzzle6::class],
['class' => Guzzle5::class, 'condition' => Guzzle5::class],
['class' => Curl::class, 'condition' => Curl::class],
['class' => Socket::class, 'condition' => Socket::class],
['class' => Buzz::class, 'condition' => Buzz::class],
['class' => React::class, 'condition' => React::class],
['class' => Cake::class, 'condition' => Cake::class],
['class' => Artax::class, 'condition' => Artax::class],
[
'class' => [self::class, 'buzzInstantiate'],
'condition' => [\Buzz\Client\FileGetContents::class, \Buzz\Message\ResponseBuilder::class],
],
],
Psr18Client::class => [
[
'class' => [self::class, 'symfonyPsr18Instantiate'],
'condition' => [SymfonyPsr18::class, Psr17RequestFactory::class],
],
[
'class' => GuzzleHttp::class,
'condition' => [self::class, 'isGuzzleImplementingPsr18'],
],
[
'class' => [self::class, 'buzzInstantiate'],
'condition' => [\Buzz\Client\FileGetContents::class, \Buzz\Message\ResponseBuilder::class],
],
],
];
public static function getCandidates($type)
{
if (Psr18Client::class === $type) {
return self::getPsr18Candidates();
}
return self::$classes[$type] ?? [];
}
/**
* @return array The return value is always an array with zero or more elements. Each
* element is an array with two keys ['class' => string, 'condition' => mixed].
*/
private static function getPsr18Candidates()
{
$candidates = self::$classes[Psr18Client::class];
// HTTPlug 2.0 clients implements PSR18Client too.
foreach (self::$classes[HttpClient::class] as $c) {
if (!is_string($c['class'])) {
continue;
}
try {
if (ClassDiscovery::safeClassExists($c['class']) && is_subclass_of($c['class'], Psr18Client::class)) {
$candidates[] = $c;
}
} catch (\Throwable $e) {
trigger_error(sprintf('Got exception "%s (%s)" while checking if a PSR-18 Client is available', get_class($e), $e->getMessage()), E_USER_WARNING);
}
}
return $candidates;
}
public static function buzzInstantiate()
{
return new \Buzz\Client\FileGetContents(Psr17FactoryDiscovery::findResponseFactory());
}
public static function symfonyPsr18Instantiate()
{
return new SymfonyPsr18(null, Psr17FactoryDiscovery::findResponseFactory(), Psr17FactoryDiscovery::findStreamFactory());
}
public static function isGuzzleImplementingPsr18()
{
return defined('GuzzleHttp\ClientInterface::MAJOR_VERSION');
}
/**
* Can be used as a condition.
*
* @return bool
*/
public static function isPsr17FactoryInstalled()
{
try {
Psr17FactoryDiscovery::findResponseFactory();
} catch (NotFoundException $e) {
return false;
} catch (\Throwable $e) {
trigger_error(sprintf('Got exception "%s (%s)" while checking if a PSR-17 ResponseFactory is available', get_class($e), $e->getMessage()), E_USER_WARNING);
return false;
}
return true;
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace Http\Discovery\Strategy;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
/**
* @internal
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*
* Don't miss updating src/Composer/Plugin.php when adding a new supported class.
*/
final class CommonPsr17ClassesStrategy implements DiscoveryStrategy
{
/**
* @var array
*/
private static $classes = [
RequestFactoryInterface::class => [
'Phalcon\Http\Message\RequestFactory',
'Nyholm\Psr7\Factory\Psr17Factory',
'GuzzleHttp\Psr7\HttpFactory',
'Http\Factory\Diactoros\RequestFactory',
'Http\Factory\Guzzle\RequestFactory',
'Http\Factory\Slim\RequestFactory',
'Laminas\Diactoros\RequestFactory',
'Slim\Psr7\Factory\RequestFactory',
'HttpSoft\Message\RequestFactory',
],
ResponseFactoryInterface::class => [
'Phalcon\Http\Message\ResponseFactory',
'Nyholm\Psr7\Factory\Psr17Factory',
'GuzzleHttp\Psr7\HttpFactory',
'Http\Factory\Diactoros\ResponseFactory',
'Http\Factory\Guzzle\ResponseFactory',
'Http\Factory\Slim\ResponseFactory',
'Laminas\Diactoros\ResponseFactory',
'Slim\Psr7\Factory\ResponseFactory',
'HttpSoft\Message\ResponseFactory',
],
ServerRequestFactoryInterface::class => [
'Phalcon\Http\Message\ServerRequestFactory',
'Nyholm\Psr7\Factory\Psr17Factory',
'GuzzleHttp\Psr7\HttpFactory',
'Http\Factory\Diactoros\ServerRequestFactory',
'Http\Factory\Guzzle\ServerRequestFactory',
'Http\Factory\Slim\ServerRequestFactory',
'Laminas\Diactoros\ServerRequestFactory',
'Slim\Psr7\Factory\ServerRequestFactory',
'HttpSoft\Message\ServerRequestFactory',
],
StreamFactoryInterface::class => [
'Phalcon\Http\Message\StreamFactory',
'Nyholm\Psr7\Factory\Psr17Factory',
'GuzzleHttp\Psr7\HttpFactory',
'Http\Factory\Diactoros\StreamFactory',
'Http\Factory\Guzzle\StreamFactory',
'Http\Factory\Slim\StreamFactory',
'Laminas\Diactoros\StreamFactory',
'Slim\Psr7\Factory\StreamFactory',
'HttpSoft\Message\StreamFactory',
],
UploadedFileFactoryInterface::class => [
'Phalcon\Http\Message\UploadedFileFactory',
'Nyholm\Psr7\Factory\Psr17Factory',
'GuzzleHttp\Psr7\HttpFactory',
'Http\Factory\Diactoros\UploadedFileFactory',
'Http\Factory\Guzzle\UploadedFileFactory',
'Http\Factory\Slim\UploadedFileFactory',
'Laminas\Diactoros\UploadedFileFactory',
'Slim\Psr7\Factory\UploadedFileFactory',
'HttpSoft\Message\UploadedFileFactory',
],
UriFactoryInterface::class => [
'Phalcon\Http\Message\UriFactory',
'Nyholm\Psr7\Factory\Psr17Factory',
'GuzzleHttp\Psr7\HttpFactory',
'Http\Factory\Diactoros\UriFactory',
'Http\Factory\Guzzle\UriFactory',
'Http\Factory\Slim\UriFactory',
'Laminas\Diactoros\UriFactory',
'Slim\Psr7\Factory\UriFactory',
'HttpSoft\Message\UriFactory',
],
];
public static function getCandidates($type)
{
$candidates = [];
if (isset(self::$classes[$type])) {
foreach (self::$classes[$type] as $class) {
$candidates[] = ['class' => $class, 'condition' => [$class]];
}
}
return $candidates;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Http\Discovery\Strategy;
use Http\Discovery\Exception\StrategyUnavailableException;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface DiscoveryStrategy
{
/**
* Find a resource of a specific type.
*
* @param string $type
*
* @return array The return value is always an array with zero or more elements. Each
* element is an array with two keys ['class' => string, 'condition' => mixed].
*
* @throws StrategyUnavailableException if we cannot use this strategy
*/
public static function getCandidates($type);
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Http\Discovery\Strategy;
use Http\Client\HttpAsyncClient;
use Http\Client\HttpClient;
use Http\Mock\Client as Mock;
/**
* Find the Mock client.
*
* @author Sam Rapaport <me@samrapdev.com>
*/
final class MockClientStrategy implements DiscoveryStrategy
{
public static function getCandidates($type)
{
if (is_a(HttpClient::class, $type, true) || is_a(HttpAsyncClient::class, $type, true)) {
return [['class' => Mock::class, 'condition' => Mock::class]];
}
return [];
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Http\Discovery\Strategy;
use Http\Discovery\ClassDiscovery;
use Http\Discovery\Exception\PuliUnavailableException;
use Puli\Discovery\Api\Discovery;
use Puli\GeneratedPuliFactory;
/**
* Find candidates using Puli.
*
* @internal
*
* @final
*
* @author David de Boer <david@ddeboer.nl>
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
class PuliBetaStrategy implements DiscoveryStrategy
{
/**
* @var GeneratedPuliFactory
*/
protected static $puliFactory;
/**
* @var Discovery
*/
protected static $puliDiscovery;
/**
* @return GeneratedPuliFactory
*
* @throws PuliUnavailableException
*/
private static function getPuliFactory()
{
if (null === self::$puliFactory) {
if (!defined('PULI_FACTORY_CLASS')) {
throw new PuliUnavailableException('Puli Factory is not available');
}
$puliFactoryClass = PULI_FACTORY_CLASS;
if (!ClassDiscovery::safeClassExists($puliFactoryClass)) {
throw new PuliUnavailableException('Puli Factory class does not exist');
}
self::$puliFactory = new $puliFactoryClass();
}
return self::$puliFactory;
}
/**
* Returns the Puli discovery layer.
*
* @return Discovery
*
* @throws PuliUnavailableException
*/
private static function getPuliDiscovery()
{
if (!isset(self::$puliDiscovery)) {
$factory = self::getPuliFactory();
$repository = $factory->createRepository();
self::$puliDiscovery = $factory->createDiscovery($repository);
}
return self::$puliDiscovery;
}
public static function getCandidates($type)
{
$returnData = [];
$bindings = self::getPuliDiscovery()->findBindings($type);
foreach ($bindings as $binding) {
$condition = true;
if ($binding->hasParameterValue('depends')) {
$condition = $binding->getParameterValue('depends');
}
$returnData[] = ['class' => $binding->getClassName(), 'condition' => $condition];
}
return $returnData;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Http\Discovery;
use Http\Discovery\Exception\DiscoveryFailedException;
use Http\Message\StreamFactory;
/**
* Finds a Stream Factory.
*
* @author Михаил Красильников <m.krasilnikov@yandex.ru>
*
* @deprecated This will be removed in 2.0. Consider using Psr17FactoryDiscovery.
*/
final class StreamFactoryDiscovery extends ClassDiscovery
{
/**
* Finds a Stream Factory.
*
* @return StreamFactory
*
* @throws Exception\NotFoundException
*/
public static function find()
{
try {
$streamFactory = static::findOneByType(StreamFactory::class);
} catch (DiscoveryFailedException $e) {
throw new NotFoundException('No stream factories found. To use Guzzle, Diactoros or Slim Framework factories install php-http/message and the chosen message implementation.', 0, $e);
}
return static::instantiateClass($streamFactory);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Http\Discovery;
use Http\Discovery\Exception\DiscoveryFailedException;
use Http\Message\UriFactory;
/**
* Finds a URI Factory.
*
* @author David de Boer <david@ddeboer.nl>
*
* @deprecated This will be removed in 2.0. Consider using Psr17FactoryDiscovery.
*/
final class UriFactoryDiscovery extends ClassDiscovery
{
/**
* Finds a URI Factory.
*
* @return UriFactory
*
* @throws Exception\NotFoundException
*/
public static function find()
{
try {
$uriFactory = static::findOneByType(UriFactory::class);
} catch (DiscoveryFailedException $e) {
throw new NotFoundException('No uri factories found. To use Guzzle, Diactoros or Slim Framework factories install php-http/message and the chosen message implementation.', 0, $e);
}
return static::instantiateClass($uriFactory);
}
}

View File

@@ -0,0 +1,18 @@
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.0.0] - 2021-03-09
- Stable release - no changes since 0.1.1
## [0.1.1] - 2020-10-21
* Allow installation with PHP 8
## [0.1.0] - 2020-08-16
First release

19
vendor/php-http/guzzle7-adapter/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2020 PHP HTTP Team <team@php-http.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,45 @@
# Guzzle 7 HTTP Adapter
[![Latest Version](https://img.shields.io/github/release/php-http/guzzle7-adapter.svg?style=flat-square)](https://github.com/php-http/guzzle7-adapter/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Total Downloads](https://img.shields.io/packagist/dt/php-http/guzzle7-adapter.svg?style=flat-square)](https://packagist.org/packages/php-http/guzzle7-adapter)
**Guzzle 7 HTTP Adapter.**
## Install
Via Composer
``` bash
$ composer require php-http/guzzle7-adapter
```
## Documentation
Please see the [official documentation](http://docs.php-http.org/en/latest/clients/guzzle7-adapter.html).
## Testing
First launch the http server:
```bash
$ ./vendor/bin/http_test_server > /dev/null 2>&1 &
```
Then the test suite:
``` bash
$ composer test
```
## Contributing
Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
## Security
If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
## License
The MIT License (MIT). Please see [License File](LICENSE) for more information.

View File

@@ -0,0 +1,43 @@
{
"name": "php-http/guzzle7-adapter",
"description": "Guzzle 7 HTTP Adapter",
"license": "MIT",
"keywords": ["guzzle", "http"],
"homepage": "http://httplug.io",
"authors": [
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com"
}
],
"require": {
"php": "^7.2 | ^8.0",
"php-http/httplug": "^2.0",
"psr/http-client": "^1.0",
"guzzlehttp/guzzle": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "^8.0|^9.3",
"php-http/client-integration-tests": "^3.0"
},
"provide": {
"php-http/client-implementation": "1.0",
"php-http/async-client-implementation": "1.0",
"psr/http-client-implementation": "1.0"
},
"autoload": {
"psr-4": {
"Http\\Adapter\\Guzzle7\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Http\\Adapter\\Guzzle7\\Tests\\": "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "0.2.x-dev"
}
}
}

View File

@@ -0,0 +1,5 @@
parameters:
level: 5
reportUnmatchedIgnoredErrors: false
paths:
- src

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="3.14.2@3538fe1955d47f6ee926c0769d71af6db08aa488">
<file src="src/Promise.php">
<PossiblyNullArgument occurrences="1">
<code>$exception-&gt;getResponse()</code>
</PossiblyNullArgument>
</file>
</files>

View File

@@ -0,0 +1,16 @@
<?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"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace Http\Adapter\Guzzle7;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Utils;
use Http\Client\HttpAsyncClient;
use Http\Client\HttpClient;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* HTTP Adapter for Guzzle 7.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class Client implements HttpClient, HttpAsyncClient
{
/**
* @var ClientInterface
*/
private $guzzle;
public function __construct(?ClientInterface $guzzle = null)
{
if (!$guzzle) {
$guzzle = self::buildClient();
}
$this->guzzle = $guzzle;
}
/**
* Factory method to create the Guzzle 7 adapter with custom Guzzle configuration.
*/
public static function createWithConfig(array $config): Client
{
return new self(self::buildClient($config));
}
/**
* {@inheritdoc}
*/
public function sendRequest(RequestInterface $request): ResponseInterface
{
return $this->sendAsyncRequest($request)->wait();
}
/**
* {@inheritdoc}
*/
public function sendAsyncRequest(RequestInterface $request)
{
$promise = $this->guzzle->sendAsync($request);
return new Promise($promise, $request);
}
/**
* Build the Guzzle client instance.
*/
private static function buildClient(array $config = []): GuzzleClient
{
$handlerStack = new HandlerStack(Utils::chooseHandler());
$handlerStack->push(Middleware::prepareBody(), 'prepare_body');
$config = array_merge(['handler' => $handlerStack], $config);
return new GuzzleClient($config);
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Http\Adapter\Guzzle7\Exception;
use Http\Client\Exception;
final class UnexpectedValueException extends \UnexpectedValueException implements Exception
{
}

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace Http\Adapter\Guzzle7;
use GuzzleHttp\Exception as GuzzleExceptions;
use GuzzleHttp\Promise\PromiseInterface;
use Http\Adapter\Guzzle7\Exception\UnexpectedValueException;
use Http\Client\Exception as HttplugException;
use Http\Promise\Promise as HttpPromise;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Wrapper around Guzzle promises.
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
*/
final class Promise implements HttpPromise
{
/**
* @var PromiseInterface
*/
private $promise;
/**
* @var string State of the promise
*/
private $state;
/**
* @var ResponseInterface
*/
private $response;
/**
* @var HttplugException
*/
private $exception;
/**
* @var RequestInterface
*/
private $request;
public function __construct(PromiseInterface $promise, RequestInterface $request)
{
$this->request = $request;
$this->state = self::PENDING;
$this->promise = $promise->then(function ($response) {
$this->response = $response;
$this->state = self::FULFILLED;
return $response;
}, function ($reason) use ($request) {
$this->state = self::REJECTED;
if ($reason instanceof HttplugException) {
$this->exception = $reason;
} elseif ($reason instanceof GuzzleExceptions\GuzzleException) {
$this->exception = $this->handleException($reason, $request);
} elseif ($reason instanceof \Throwable) {
$this->exception = new HttplugException\TransferException('Invalid exception returned from Guzzle7', 0, $reason);
} else {
$this->exception = new UnexpectedValueException('Reason returned from Guzzle7 must be an Exception');
}
throw $this->exception;
});
}
/**
* {@inheritdoc}
*/
public function then(callable $onFulfilled = null, callable $onRejected = null)
{
return new static($this->promise->then($onFulfilled, $onRejected), $this->request);
}
/**
* {@inheritdoc}
*/
public function getState()
{
return $this->state;
}
/**
* {@inheritdoc}
*/
public function wait($unwrap = true)
{
$this->promise->wait(false);
if ($unwrap) {
if (self::REJECTED == $this->getState()) {
throw $this->exception;
}
return $this->response;
}
}
/**
* Converts a Guzzle exception into an Httplug exception.
*
* @return HttplugException
*/
private function handleException(GuzzleExceptions\GuzzleException $exception, RequestInterface $request)
{
if ($exception instanceof GuzzleExceptions\ConnectException) {
return new HttplugException\NetworkException($exception->getMessage(), $exception->getRequest(), $exception);
}
if ($exception instanceof GuzzleExceptions\RequestException) {
// Make sure we have a response for the HttpException
if ($exception->hasResponse()) {
return new HttplugException\HttpException(
$exception->getMessage(),
$exception->getRequest(),
$exception->getResponse(),
$exception
);
}
return new HttplugException\RequestException($exception->getMessage(), $exception->getRequest(), $exception);
}
return new HttplugException\TransferException($exception->getMessage(), 0, $exception);
}
}

View File

@@ -0,0 +1,16 @@
<?php
$finder = PhpCsFixer\Finder::create()
->in(__DIR__.'/src')
->name('*.php')
;
$config = (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'@Symfony' => true,
])
->setFinder($finder)
;
return $config;

141
vendor/php-http/httplug/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,141 @@
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [2.4.0] - 2023-04-14
### Changed
- Allow `psr/http-message` v2 in addition to v1
- Deprecate `Http\Client\HttpClient`, use [PSR-18](https://www.php-fig.org/psr/psr-18/) instead
## [2.3.0] - 2022-02-21
### Changed
- Enabled the `$onRejected` callback of `HttpRejectedPromise` to return a promise for implementing a retry
mechanism [#168](https://github.com/php-http/httplug/pull/168)
## [2.2.0] - 2020-07-13
### Changed
- Support PHP 7.1-8.0
## [2.1.0] - 2019-12-27
### Changed
- `Http\Client\Exception\NetworkException` no longer extends `Http\Client\Exception\RequestException`,
in accordance with [PSR-18](https://www.php-fig.org/psr/psr-18/)
## [2.0.0] - 2018-10-31
This version is no BC break for consumers using HTTPlug. However, HTTP clients that
implement HTTPlug need to adjust because we add return type declarations.
### Added
- Support for PSR-18 (HTTP client).
### Changed
- **BC Break:** `HttpClient::sendRequest(RequestInterface $request)` has a return type annotation. The new
signature is `HttpClient::sendRequest(RequestInterface $request): ResponseInterface`.
- **BC Break:** `RequestException::getRequest()` has a return type annotation. The new
signature is `RequestException::getRequest(): RequestInterface`.
### Removed
- PHP 5 support
## [1.1.0] - 2016-08-31
### Added
- HttpFulfilledPromise and HttpRejectedPromise which respect the HttpAsyncClient interface
## [1.0.0] - 2016-01-26
### Removed
- Stability configuration from composer
## [1.0.0-RC1] - 2016-01-12
### Changed
- Updated package files
- Updated promise dependency to RC1
## [1.0.0-beta] - 2015-12-17
### Added
- Puli configuration and binding types
### Changed
- Exception concept
## [1.0.0-alpha3] - 2015-12-13
### Changed
- Async client does not throw exceptions
### Removed
- Promise interface moved to its own repository: [php-http/promise](https://github.com/php-http/promise)
## [1.0.0-alpha2] - 2015-11-16
### Added
- Async client and Promise interface
## [1.0.0-alpha] - 2015-10-26
### Added
- Better domain exceptions.
### Changed
- Purpose of the library: general HTTP CLient abstraction.
### Removed
- Request options: they should be configured at construction time.
- Multiple request sending: should be done asynchronously using Async Client.
- `getName` method
## 0.1.0 - 2015-06-03
### Added
- Initial release
[Unreleased]: https://github.com/php-http/httplug/compare/v2.0.0...HEAD
[2.0.0]: https://github.com/php-http/httplug/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/php-http/httplug/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/php-http/httplug/compare/v1.0.0-RC1...v1.0.0
[1.0.0-RC1]: https://github.com/php-http/httplug/compare/v1.0.0-beta...v1.0.0-RC1
[1.0.0-beta]: https://github.com/php-http/httplug/compare/v1.0.0-alpha3...v1.0.0-beta
[1.0.0-alpha3]: https://github.com/php-http/httplug/compare/v1.0.0-alpha2...v1.0.0-alpha3
[1.0.0-alpha2]: https://github.com/php-http/httplug/compare/v1.0.0-alpha...v1.0.0-alpha2
[1.0.0-alpha]: https://github.com/php-http/httplug/compare/v0.1.0...v1.0.0-alpha

20
vendor/php-http/httplug/LICENSE vendored Normal file
View File

@@ -0,0 +1,20 @@
Copyright (c) 2014 Eric GELOEN <geloen.eric@gmail.com>
Copyright (c) 2015 PHP HTTP Team <team@php-http.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

54
vendor/php-http/httplug/README.md vendored Normal file
View File

@@ -0,0 +1,54 @@
# HTTPlug
[![Latest Version](https://img.shields.io/github/release/php-http/httplug.svg?style=flat-square)](https://github.com/php-http/httplug/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Build Status](https://github.com/php-http/httplug/actions/workflows/ci.yml/badge.svg)](https://github.com/php-http/httplug/actions/workflows/ci.yml)
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/httplug.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/httplug)
[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/httplug.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/httplug)
[![Total Downloads](https://img.shields.io/packagist/dt/php-http/httplug.svg?style=flat-square)](https://packagist.org/packages/php-http/httplug)
[![Email](https://img.shields.io/badge/email-team@httplug.io-blue.svg?style=flat-square)](mailto:team@httplug.io)
**HTTPlug, the HTTP client abstraction for PHP.**
## Intro
HTTP client standard built on [PSR-7](http://www.php-fig.org/psr/psr-7/) HTTP
messages. The HttpAsyncClient defines an asynchronous HTTP client for PHP.
This package also provides a synchronous HttpClient interface with the same
method signature as the [PSR-18](http://www.php-fig.org/psr/psr-18/) client.
For synchronous requests, we recommend using PSR-18 directly.
## History
HTTPlug is the official successor of the [ivory http adapter](https://github.com/egeloen/ivory-http-adapter).
HTTPlug is a predecessor of [PSR-18](http://www.php-fig.org/psr/psr-18/)
## Install
Via Composer
``` bash
$ composer require php-http/httplug
```
## Documentation
Please see the [official documentation](http://docs.php-http.org).
## Testing
``` bash
$ composer test
```
## License
The MIT License (MIT). Please see [License File](LICENSE) for more information.

40
vendor/php-http/httplug/composer.json vendored Normal file
View File

@@ -0,0 +1,40 @@
{
"name": "php-http/httplug",
"description": "HTTPlug, the HTTP client abstraction for PHP",
"keywords": [
"http",
"client"
],
"homepage": "http://httplug.io",
"license": "MIT",
"authors": [
{
"name": "Eric GELOEN",
"email": "geloen.eric@gmail.com"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://sagikazarmark.hu"
}
],
"require": {
"php": "^7.1 || ^8.0",
"php-http/promise": "^1.1",
"psr/http-client": "^1.0",
"psr/http-message": "^1.0 || ^2.0"
},
"require-dev": {
"friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0",
"phpspec/phpspec": "^5.1 || ^6.0 || ^7.0"
},
"autoload": {
"psr-4": {
"Http\\Client\\": "src/"
}
},
"scripts": {
"test": "vendor/bin/phpspec run",
"test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
}
}

12
vendor/php-http/httplug/puli.json vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"version": "1.0",
"name": "php-http/httplug",
"binding-types": {
"Http\\Client\\HttpAsyncClient": {
"description": "Async HTTP Client"
},
"Http\\Client\\HttpClient": {
"description": "HTTP Client"
}
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Http\Client;
use Psr\Http\Client\ClientExceptionInterface as PsrClientException;
/**
* Every HTTP Client related Exception must implement this interface.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
interface Exception extends PsrClientException
{
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Http\Client\Exception;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Thrown when a response was received but the request itself failed.
*
* In addition to the request, this exception always provides access to the response object.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
class HttpException extends RequestException
{
/**
* @var ResponseInterface
*/
protected $response;
/**
* @param string $message
*/
public function __construct(
$message,
RequestInterface $request,
ResponseInterface $response,
\Exception $previous = null
) {
parent::__construct($message, $request, $previous);
$this->response = $response;
$this->code = $response->getStatusCode();
}
/**
* Returns the response.
*
* @return ResponseInterface
*/
public function getResponse()
{
return $this->response;
}
/**
* Factory method to create a new exception with a normalized error message.
*/
public static function create(
RequestInterface $request,
ResponseInterface $response,
\Exception $previous = null
) {
$message = sprintf(
'[url] %s [http method] %s [status code] %s [reason phrase] %s',
$request->getRequestTarget(),
$request->getMethod(),
$response->getStatusCode(),
$response->getReasonPhrase()
);
return new static($message, $request, $response, $previous);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Http\Client\Exception;
use Psr\Http\Client\NetworkExceptionInterface as PsrNetworkException;
use Psr\Http\Message\RequestInterface;
/**
* Thrown when the request cannot be completed because of network issues.
*
* There is no response object as this exception is thrown when no response has been received.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
class NetworkException extends TransferException implements PsrNetworkException
{
use RequestAwareTrait;
/**
* @param string $message
*/
public function __construct($message, RequestInterface $request, \Exception $previous = null)
{
$this->setRequest($request);
parent::__construct($message, 0, $previous);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Http\Client\Exception;
use Psr\Http\Message\RequestInterface;
trait RequestAwareTrait
{
/**
* @var RequestInterface
*/
private $request;
private function setRequest(RequestInterface $request)
{
$this->request = $request;
}
/**
* {@inheritdoc}
*/
public function getRequest(): RequestInterface
{
return $this->request;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Http\Client\Exception;
use Psr\Http\Client\RequestExceptionInterface as PsrRequestException;
use Psr\Http\Message\RequestInterface;
/**
* Exception for when a request failed, providing access to the failed request.
*
* This could be due to an invalid request, or one of the extending exceptions
* for network errors or HTTP error responses.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
class RequestException extends TransferException implements PsrRequestException
{
use RequestAwareTrait;
/**
* @param string $message
*/
public function __construct($message, RequestInterface $request, \Exception $previous = null)
{
$this->setRequest($request);
parent::__construct($message, 0, $previous);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Http\Client\Exception;
use Http\Client\Exception;
/**
* Base exception for transfer related exceptions.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
class TransferException extends \RuntimeException implements Exception
{
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Http\Client;
use Http\Promise\Promise;
use Psr\Http\Message\RequestInterface;
/**
* Sends a PSR-7 Request in an asynchronous way by returning a Promise.
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
*/
interface HttpAsyncClient
{
/**
* Sends a PSR-7 request in an asynchronous way.
*
* Exceptions related to processing the request are available from the returned Promise.
*
* @return Promise resolves a PSR-7 Response or fails with an Http\Client\Exception
*
* @throws \Exception If processing the request is impossible (eg. bad configuration).
*/
public function sendAsyncRequest(RequestInterface $request);
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Http\Client;
use Psr\Http\Client\ClientInterface;
/**
* {@inheritdoc}
*
* Provide the Httplug HttpClient interface for BC.
* You should typehint Psr\Http\Client\ClientInterface in new code
*
* @deprecated since version 2.4, use Psr\Http\Client\ClientInterface instead; see https://www.php-fig.org/psr/psr-18/
*/
interface HttpClient extends ClientInterface
{
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Http\Client\Promise;
use Http\Client\Exception;
use Http\Promise\Promise;
use Psr\Http\Message\ResponseInterface;
final class HttpFulfilledPromise implements Promise
{
/**
* @var ResponseInterface
*/
private $response;
public function __construct(ResponseInterface $response)
{
$this->response = $response;
}
/**
* {@inheritdoc}
*/
public function then(callable $onFulfilled = null, callable $onRejected = null)
{
if (null === $onFulfilled) {
return $this;
}
try {
return new self($onFulfilled($this->response));
} catch (Exception $e) {
return new HttpRejectedPromise($e);
}
}
/**
* {@inheritdoc}
*/
public function getState()
{
return Promise::FULFILLED;
}
/**
* {@inheritdoc}
*/
public function wait($unwrap = true)
{
if ($unwrap) {
return $this->response;
}
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Http\Client\Promise;
use Http\Client\Exception;
use Http\Promise\Promise;
final class HttpRejectedPromise implements Promise
{
/**
* @var Exception
*/
private $exception;
public function __construct(Exception $exception)
{
$this->exception = $exception;
}
/**
* {@inheritdoc}
*/
public function then(callable $onFulfilled = null, callable $onRejected = null)
{
if (null === $onRejected) {
return $this;
}
try {
$result = $onRejected($this->exception);
if ($result instanceof Promise) {
return $result;
}
return new HttpFulfilledPromise($result);
} catch (Exception $e) {
return new self($e);
}
}
/**
* {@inheritdoc}
*/
public function getState()
{
return Promise::REJECTED;
}
/**
* {@inheritdoc}
*/
public function wait($unwrap = true)
{
if ($unwrap) {
throw $this->exception;
}
}
}

48
vendor/php-http/promise/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,48 @@
# Change Log
## 1.1.0 - 2020-07-07
### Added
- Test with PHP 7.1, 7.2, 7.3, 7.4 and 8.0
### Removed
- PHP 5 and 7.0 support
### Fixed
- Fixed PHPDoc for `Promise::then`
## 1.0.0 - 2016-01-26
### Removed
- PSR-7 dependency
## 1.0.0-RC1 - 2016-01-12
### Added
- Tests for full coverage
## Changed
- Updated package files
- Clarified wait method behavior
- Contributing guide moved to the documentation
## 0.1.1 - 2015-12-24
## Added
- Fulfilled and Rejected promise implementations
## 0.1.0 - 2015-12-13
## Added
- Promise interface

19
vendor/php-http/promise/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2015-2016 PHP HTTP Team <team@php-http.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

48
vendor/php-http/promise/README.md vendored Normal file
View File

@@ -0,0 +1,48 @@
# Promise
[![Latest Version](https://img.shields.io/github/release/php-http/promise.svg?style=flat-square)](https://github.com/php-http/promise/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Build Status](https://img.shields.io/travis/php-http/promise.svg?style=flat-square)](https://travis-ci.org/php-http/promise)
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/promise.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/promise)
[![Quality Score](https://img.shields.io/scrutinizer/g/php-http/promise.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/promise)
[![Total Downloads](https://img.shields.io/packagist/dt/php-http/promise.svg?style=flat-square)](https://packagist.org/packages/php-http/promise)
**Promise used for asynchronous HTTP requests.**
**Note:** This will eventually be removed/deprecated and replaced with the upcoming Promise PSR.
## Install
Via Composer
``` bash
$ composer require php-http/promise
```
## Documentation
Please see the [official documentation](http://docs.php-http.org/en/latest/components/promise.html).
## Testing
``` bash
$ composer test
```
## Contributing
Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
## Security
If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
## License
The MIT License (MIT). Please see [License File](LICENSE) for more information.

38
vendor/php-http/promise/composer.json vendored Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "php-http/promise",
"description": "Promise used for asynchronous HTTP requests",
"license": "MIT",
"keywords": ["promise"],
"homepage": "http://httplug.io",
"authors": [
{
"name": "Joel Wurtz",
"email": "joel.wurtz@gmail.com"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
}
],
"require": {
"php" : "^7.1 || ^8.0"
},
"require-dev": {
"friends-of-phpspec/phpspec-code-coverage" : "^4.3.2",
"phpspec/phpspec": "^5.1.2 || ^6.2"
},
"autoload": {
"psr-4": {
"Http\\Promise\\": "src/"
}
},
"scripts": {
"test": "vendor/bin/phpspec run",
"test-ci": "vendor/bin/phpspec run -c phpspec.yml.ci"
},
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Http\Promise;
/**
* A promise already fulfilled.
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
*/
final class FulfilledPromise implements Promise
{
/**
* @var mixed
*/
private $result;
/**
* @param $result
*/
public function __construct($result)
{
$this->result = $result;
}
/**
* {@inheritdoc}
*/
public function then(callable $onFulfilled = null, callable $onRejected = null)
{
if (null === $onFulfilled) {
return $this;
}
try {
return new self($onFulfilled($this->result));
} catch (\Exception $e) {
return new RejectedPromise($e);
}
}
/**
* {@inheritdoc}
*/
public function getState()
{
return Promise::FULFILLED;
}
/**
* {@inheritdoc}
*/
public function wait($unwrap = true)
{
if ($unwrap) {
return $this->result;
}
}
}

69
vendor/php-http/promise/src/Promise.php vendored Normal file
View File

@@ -0,0 +1,69 @@
<?php
namespace Http\Promise;
/**
* Promise represents a value that may not be available yet, but will be resolved at some point in future.
* It acts like a proxy to the actual value.
*
* This interface is an extension of the promises/a+ specification.
*
* @see https://promisesaplus.com/
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
interface Promise
{
/**
* Promise has not been fulfilled or rejected.
*/
const PENDING = 'pending';
/**
* Promise has been fulfilled.
*/
const FULFILLED = 'fulfilled';
/**
* Promise has been rejected.
*/
const REJECTED = 'rejected';
/**
* Adds behavior for when the promise is resolved or rejected (response will be available, or error happens).
*
* If you do not care about one of the cases, you can set the corresponding callable to null
* The callback will be called when the value arrived and never more than once.
*
* @param callable|null $onFulfilled called when a response will be available
* @param callable|null $onRejected called when an exception occurs
*
* @return Promise a new resolved promise with value of the executed callback (onFulfilled / onRejected)
*/
public function then(callable $onFulfilled = null, callable $onRejected = null);
/**
* Returns the state of the promise, one of PENDING, FULFILLED or REJECTED.
*
* @return string
*/
public function getState();
/**
* Wait for the promise to be fulfilled or rejected.
*
* When this method returns, the request has been resolved and if callables have been
* specified, the appropriate one has terminated.
*
* When $unwrap is true (the default), the response is returned, or the exception thrown
* on failure. Otherwise, nothing is returned or thrown.
*
* @param bool $unwrap Whether to return resolved value / throw reason or not
*
* @return mixed Resolved value, null if $unwrap is set to false
*
* @throws \Exception the rejection reason if $unwrap is set to true and the request failed
*/
public function wait($unwrap = true);
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Http\Promise;
/**
* A rejected promise.
*
* @author Joel Wurtz <joel.wurtz@gmail.com>
*/
final class RejectedPromise implements Promise
{
/**
* @var \Exception
*/
private $exception;
/**
* @param \Exception $exception
*/
public function __construct(\Exception $exception)
{
$this->exception = $exception;
}
/**
* {@inheritdoc}
*/
public function then(callable $onFulfilled = null, callable $onRejected = null)
{
if (null === $onRejected) {
return $this;
}
try {
return new FulfilledPromise($onRejected($this->exception));
} catch (\Exception $e) {
return new self($e);
}
}
/**
* {@inheritdoc}
*/
public function getState()
{
return Promise::REJECTED;
}
/**
* {@inheritdoc}
*/
public function wait($unwrap = true)
{
if ($unwrap) {
throw $this->exception;
}
}
}