Version Description
05-12-2019 =
Fix - Mollie Components request multiple times the merchant profile ID via API
Download this release
Release Info
Developer | wido |
Plugin | Mollie Payments for WooCommerce |
Version | 5.4.1 |
Comparing to | |
See all releases |
Code changes from version 5.4.0 to 5.4.1
- inc/utils.php +25 -3
- mollie-payments-for-woocommerce.php +1 -1
- readme.txt +5 -1
- src/Mollie/WC/Helper/Settings.php +44 -6
- src/Mollie/WC/Plugin.php +7 -1
- vendor/autoload.php +1 -1
- vendor/composer/autoload_real.php +7 -7
- vendor/composer/autoload_static.php +5 -5
- vendor/guzzlehttp/promises/README.md +504 -0
- vendor/guzzlehttp/psr7/README.md +745 -0
- vendor/ralouphie/getallheaders/composer.json +26 -0
inc/utils.php
CHANGED
@@ -49,7 +49,7 @@ function merchantProfile()
|
|
49 |
{
|
50 |
static $profile = null;
|
51 |
|
52 |
-
if (
|
53 |
$isTestMode = isTestModeEnabled();
|
54 |
|
55 |
$apiHelper = Mollie_WC_Plugin::getApiHelper();
|
@@ -67,9 +67,31 @@ function merchantProfile()
|
|
67 |
*/
|
68 |
function merchantProfileId()
|
69 |
{
|
70 |
-
$
|
|
|
71 |
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
74 |
|
75 |
/**
|
49 |
{
|
50 |
static $profile = null;
|
51 |
|
52 |
+
if ($profile === null) {
|
53 |
$isTestMode = isTestModeEnabled();
|
54 |
|
55 |
$apiHelper = Mollie_WC_Plugin::getApiHelper();
|
67 |
*/
|
68 |
function merchantProfileId()
|
69 |
{
|
70 |
+
static $merchantProfileId = null;
|
71 |
+
$merchantProfileIdOptionKey = Mollie_WC_Plugin::PLUGIN_ID . '_merchant_profile_id';
|
72 |
|
73 |
+
if ($merchantProfileId === null) {
|
74 |
+
$merchantProfileId = get_option($merchantProfileIdOptionKey, '');
|
75 |
+
|
76 |
+
/*
|
77 |
+
* Try to retrieve the merchant profile ID from an Api Request if not stored already,
|
78 |
+
* then store it into the database
|
79 |
+
*/
|
80 |
+
if (!$merchantProfileId) {
|
81 |
+
try {
|
82 |
+
$merchantProfile = merchantProfile();
|
83 |
+
$merchantProfileId = isset($merchantProfile->id) ? $merchantProfile->id : '';
|
84 |
+
} catch (ApiException $exception) {
|
85 |
+
$merchantProfileId = '';
|
86 |
+
}
|
87 |
+
|
88 |
+
if ($merchantProfileId) {
|
89 |
+
update_option($merchantProfileIdOptionKey, $merchantProfileId, false);
|
90 |
+
}
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
return $merchantProfileId;
|
95 |
}
|
96 |
|
97 |
/**
|
mollie-payments-for-woocommerce.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
* Plugin Name: Mollie Payments for WooCommerce
|
4 |
* Plugin URI: https://www.mollie.com
|
5 |
* Description: Accept payments in WooCommerce with the official Mollie plugin
|
6 |
-
* Version: 5.4.
|
7 |
* Author: Mollie
|
8 |
* Author URI: https://www.mollie.com
|
9 |
* Requires at least: 3.8
|
3 |
* Plugin Name: Mollie Payments for WooCommerce
|
4 |
* Plugin URI: https://www.mollie.com
|
5 |
* Description: Accept payments in WooCommerce with the official Mollie plugin
|
6 |
+
* Version: 5.4.1
|
7 |
* Author: Mollie
|
8 |
* Author URI: https://www.mollie.com
|
9 |
* Requires at least: 3.8
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: daanvm, danielhuesken, davdebcom, dinamiko, inpsyde, l.vangunst, n
|
|
3 |
Tags: mollie, payments, payment gateway, woocommerce, credit card, ideal, bancontact, klarna, sofort, giropay, woocommerce subscriptions
|
4 |
Requires at least: 3.8
|
5 |
Tested up to: 5.3
|
6 |
-
Stable tag: 5.4.
|
7 |
Requires PHP: 5.6
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
@@ -181,6 +181,10 @@ Automatic updates should work like a charm; as always though, ensure you backup
|
|
181 |
|
182 |
== Changelog ==
|
183 |
|
|
|
|
|
|
|
|
|
184 |
= 5.4.0 - 04-12-2019 =
|
185 |
|
186 |
* Fix - Apple Pay Gateway is removed from available gateways during WooCommerce Api calls
|
3 |
Tags: mollie, payments, payment gateway, woocommerce, credit card, ideal, bancontact, klarna, sofort, giropay, woocommerce subscriptions
|
4 |
Requires at least: 3.8
|
5 |
Tested up to: 5.3
|
6 |
+
Stable tag: 5.4.1
|
7 |
Requires PHP: 5.6
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
181 |
|
182 |
== Changelog ==
|
183 |
|
184 |
+
= 5.4.1 - 05-12-2019 =
|
185 |
+
|
186 |
+
* Fix - Mollie Components request multiple times the merchant profile ID via API
|
187 |
+
|
188 |
= 5.4.0 - 04-12-2019 =
|
189 |
|
190 |
* Fix - Apple Pay Gateway is removed from available gateways during WooCommerce Api calls
|
src/Mollie/WC/Helper/Settings.php
CHANGED
@@ -1,4 +1,7 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
2 |
class Mollie_WC_Helper_Settings
|
3 |
{
|
4 |
const FILTER_ALLOWED_LANGUAGE_CODE_SETTING = 'mollie.allowed_language_code_setting';
|
@@ -51,7 +54,14 @@ class Mollie_WC_Helper_Settings
|
|
51 |
{
|
52 |
$setting_id = $test_mode ? 'test_api_key' : 'live_api_key';
|
53 |
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
}
|
56 |
|
57 |
/**
|
@@ -133,11 +143,37 @@ class Mollie_WC_Helper_Settings
|
|
133 |
/**
|
134 |
* @return string
|
135 |
*/
|
136 |
-
public function getLogsUrl
|
137 |
{
|
138 |
return admin_url('admin.php?page=wc-status&tab=logs');
|
139 |
}
|
140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
/**
|
142 |
* Get plugin status
|
143 |
*
|
@@ -146,16 +182,18 @@ class Mollie_WC_Helper_Settings
|
|
146 |
*
|
147 |
* @return string
|
148 |
*/
|
149 |
-
protected function getPluginStatus
|
150 |
{
|
151 |
$status = Mollie_WC_Plugin::getStatusHelper();
|
152 |
|
153 |
-
if (!$status->isCompatible())
|
154 |
-
{
|
155 |
// Just stop here!
|
156 |
return ''
|
157 |
. '<div class="notice notice-error">'
|
158 |
-
. '<p><strong>' . __(
|
|
|
|
|
|
|
159 |
. '</p></div>';
|
160 |
}
|
161 |
|
1 |
<?php
|
2 |
+
|
3 |
+
use Mollie\Api\Exceptions\ApiException;
|
4 |
+
|
5 |
class Mollie_WC_Helper_Settings
|
6 |
{
|
7 |
const FILTER_ALLOWED_LANGUAGE_CODE_SETTING = 'mollie.allowed_language_code_setting';
|
54 |
{
|
55 |
$setting_id = $test_mode ? 'test_api_key' : 'live_api_key';
|
56 |
|
57 |
+
$apiKeyId = $this->getSettingId($setting_id);
|
58 |
+
$apiKey = get_option($apiKeyId);
|
59 |
+
|
60 |
+
if (!$apiKey && is_admin()) {
|
61 |
+
$apiKey = filter_input(INPUT_POST, $apiKeyId, FILTER_SANITIZE_STRING);
|
62 |
+
}
|
63 |
+
|
64 |
+
return trim($apiKey);
|
65 |
}
|
66 |
|
67 |
/**
|
143 |
/**
|
144 |
* @return string
|
145 |
*/
|
146 |
+
public function getLogsUrl()
|
147 |
{
|
148 |
return admin_url('admin.php?page=wc-status&tab=logs');
|
149 |
}
|
150 |
|
151 |
+
public function updateMerchantIdOnApiKeyChanges($optionValue, $optionName)
|
152 |
+
{
|
153 |
+
$optionId = isset($optionName['id']) ? $optionName['id'] : '';
|
154 |
+
$allowedOptionsId = [
|
155 |
+
$this->getSettingId('live_api_key'),
|
156 |
+
$this->getSettingId('test_api_key'),
|
157 |
+
];
|
158 |
+
|
159 |
+
if (!in_array($optionId, $allowedOptionsId, true)) {
|
160 |
+
return $optionValue;
|
161 |
+
}
|
162 |
+
|
163 |
+
$merchantProfileIdOptionKey = Mollie_WC_Plugin::PLUGIN_ID . '_merchant_profile_id';
|
164 |
+
|
165 |
+
try {
|
166 |
+
$merchantProfile = merchantProfile();
|
167 |
+
$merchantProfileId = isset($merchantProfile->id) ? $merchantProfile->id : '';
|
168 |
+
} catch (ApiException $exception) {
|
169 |
+
$merchantProfileId = '';
|
170 |
+
}
|
171 |
+
|
172 |
+
update_option($merchantProfileIdOptionKey, $merchantProfileId, false);
|
173 |
+
|
174 |
+
return $optionValue;
|
175 |
+
}
|
176 |
+
|
177 |
/**
|
178 |
* Get plugin status
|
179 |
*
|
182 |
*
|
183 |
* @return string
|
184 |
*/
|
185 |
+
protected function getPluginStatus()
|
186 |
{
|
187 |
$status = Mollie_WC_Plugin::getStatusHelper();
|
188 |
|
189 |
+
if (!$status->isCompatible()) {
|
|
|
190 |
// Just stop here!
|
191 |
return ''
|
192 |
. '<div class="notice notice-error">'
|
193 |
+
. '<p><strong>' . __(
|
194 |
+
'Error',
|
195 |
+
'mollie-payments-for-woocommerce'
|
196 |
+
) . ':</strong> ' . implode('<br/>', $status->getErrors())
|
197 |
. '</p></div>';
|
198 |
}
|
199 |
|
src/Mollie/WC/Plugin.php
CHANGED
@@ -8,7 +8,7 @@ class Mollie_WC_Plugin
|
|
8 |
{
|
9 |
const PLUGIN_ID = 'mollie-payments-for-woocommerce';
|
10 |
const PLUGIN_TITLE = 'Mollie Payments for WooCommerce';
|
11 |
-
const PLUGIN_VERSION = '5.4.
|
12 |
|
13 |
const DB_VERSION = '1.0';
|
14 |
const DB_VERSION_PARAM_NAME = 'mollie-db-version';
|
@@ -205,6 +205,12 @@ class Mollie_WC_Plugin
|
|
205 |
}
|
206 |
}
|
207 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
|
209 |
// Add settings link to plugins page
|
210 |
add_filter( 'plugin_action_links_' . $plugin_basename, array ( __CLASS__, 'addPluginActionLinks' ) );
|
8 |
{
|
9 |
const PLUGIN_ID = 'mollie-payments-for-woocommerce';
|
10 |
const PLUGIN_TITLE = 'Mollie Payments for WooCommerce';
|
11 |
+
const PLUGIN_VERSION = '5.4.1';
|
12 |
|
13 |
const DB_VERSION = '1.0';
|
14 |
const DB_VERSION_PARAM_NAME = 'mollie-db-version';
|
205 |
}
|
206 |
}
|
207 |
);
|
208 |
+
add_action(
|
209 |
+
'woocommerce_admin_settings_sanitize_option',
|
210 |
+
[$settings_helper, 'updateMerchantIdOnApiKeyChanges'],
|
211 |
+
10,
|
212 |
+
2
|
213 |
+
);
|
214 |
|
215 |
// Add settings link to plugins page
|
216 |
add_filter( 'plugin_action_links_' . $plugin_basename, array ( __CLASS__, 'addPluginActionLinks' ) );
|
vendor/autoload.php
CHANGED
@@ -4,4 +4,4 @@
|
|
4 |
|
5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
|
7 |
-
return
|
4 |
|
5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
|
7 |
+
return ComposerAutoloaderInit822e8679465379306e77c697814f1c55::getLoader();
|
vendor/composer/autoload_real.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
// autoload_real.php @generated by Composer
|
4 |
|
5 |
-
class
|
6 |
{
|
7 |
private static $loader;
|
8 |
|
@@ -19,15 +19,15 @@ class ComposerAutoloaderInit3ea1880a2740a3571db7cff67650474a
|
|
19 |
return self::$loader;
|
20 |
}
|
21 |
|
22 |
-
spl_autoload_register(array('
|
23 |
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
-
spl_autoload_unregister(array('
|
25 |
|
26 |
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
27 |
if ($useStaticLoader) {
|
28 |
require_once __DIR__ . '/autoload_static.php';
|
29 |
|
30 |
-
call_user_func(\Composer\Autoload\
|
31 |
} else {
|
32 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
33 |
foreach ($map as $namespace => $path) {
|
@@ -48,19 +48,19 @@ class ComposerAutoloaderInit3ea1880a2740a3571db7cff67650474a
|
|
48 |
$loader->register(true);
|
49 |
|
50 |
if ($useStaticLoader) {
|
51 |
-
$includeFiles = Composer\Autoload\
|
52 |
} else {
|
53 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
54 |
}
|
55 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
56 |
-
|
57 |
}
|
58 |
|
59 |
return $loader;
|
60 |
}
|
61 |
}
|
62 |
|
63 |
-
function
|
64 |
{
|
65 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
66 |
require $file;
|
2 |
|
3 |
// autoload_real.php @generated by Composer
|
4 |
|
5 |
+
class ComposerAutoloaderInit822e8679465379306e77c697814f1c55
|
6 |
{
|
7 |
private static $loader;
|
8 |
|
19 |
return self::$loader;
|
20 |
}
|
21 |
|
22 |
+
spl_autoload_register(array('ComposerAutoloaderInit822e8679465379306e77c697814f1c55', 'loadClassLoader'), true, true);
|
23 |
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit822e8679465379306e77c697814f1c55', 'loadClassLoader'));
|
25 |
|
26 |
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
27 |
if ($useStaticLoader) {
|
28 |
require_once __DIR__ . '/autoload_static.php';
|
29 |
|
30 |
+
call_user_func(\Composer\Autoload\ComposerStaticInit822e8679465379306e77c697814f1c55::getInitializer($loader));
|
31 |
} else {
|
32 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
33 |
foreach ($map as $namespace => $path) {
|
48 |
$loader->register(true);
|
49 |
|
50 |
if ($useStaticLoader) {
|
51 |
+
$includeFiles = Composer\Autoload\ComposerStaticInit822e8679465379306e77c697814f1c55::$files;
|
52 |
} else {
|
53 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
54 |
}
|
55 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
56 |
+
composerRequire822e8679465379306e77c697814f1c55($fileIdentifier, $file);
|
57 |
}
|
58 |
|
59 |
return $loader;
|
60 |
}
|
61 |
}
|
62 |
|
63 |
+
function composerRequire822e8679465379306e77c697814f1c55($fileIdentifier, $file)
|
64 |
{
|
65 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
66 |
require $file;
|
vendor/composer/autoload_static.php
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
|
5 |
namespace Composer\Autoload;
|
6 |
|
7 |
-
class
|
8 |
{
|
9 |
public static $files = array (
|
10 |
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
@@ -286,10 +286,10 @@ class ComposerStaticInit3ea1880a2740a3571db7cff67650474a
|
|
286 |
public static function getInitializer(ClassLoader $loader)
|
287 |
{
|
288 |
return \Closure::bind(function () use ($loader) {
|
289 |
-
$loader->prefixLengthsPsr4 =
|
290 |
-
$loader->prefixDirsPsr4 =
|
291 |
-
$loader->prefixesPsr0 =
|
292 |
-
$loader->classMap =
|
293 |
|
294 |
}, null, ClassLoader::class);
|
295 |
}
|
4 |
|
5 |
namespace Composer\Autoload;
|
6 |
|
7 |
+
class ComposerStaticInit822e8679465379306e77c697814f1c55
|
8 |
{
|
9 |
public static $files = array (
|
10 |
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
286 |
public static function getInitializer(ClassLoader $loader)
|
287 |
{
|
288 |
return \Closure::bind(function () use ($loader) {
|
289 |
+
$loader->prefixLengthsPsr4 = ComposerStaticInit822e8679465379306e77c697814f1c55::$prefixLengthsPsr4;
|
290 |
+
$loader->prefixDirsPsr4 = ComposerStaticInit822e8679465379306e77c697814f1c55::$prefixDirsPsr4;
|
291 |
+
$loader->prefixesPsr0 = ComposerStaticInit822e8679465379306e77c697814f1c55::$prefixesPsr0;
|
292 |
+
$loader->classMap = ComposerStaticInit822e8679465379306e77c697814f1c55::$classMap;
|
293 |
|
294 |
}, null, ClassLoader::class);
|
295 |
}
|
vendor/guzzlehttp/promises/README.md
ADDED
@@ -0,0 +1,504 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Guzzle Promises
|
2 |
+
|
3 |
+
[Promises/A+](https://promisesaplus.com/) implementation that handles promise
|
4 |
+
chaining and resolution iteratively, allowing for "infinite" promise chaining
|
5 |
+
while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
|
6 |
+
for a general introduction to promises.
|
7 |
+
|
8 |
+
- [Features](#features)
|
9 |
+
- [Quick start](#quick-start)
|
10 |
+
- [Synchronous wait](#synchronous-wait)
|
11 |
+
- [Cancellation](#cancellation)
|
12 |
+
- [API](#api)
|
13 |
+
- [Promise](#promise)
|
14 |
+
- [FulfilledPromise](#fulfilledpromise)
|
15 |
+
- [RejectedPromise](#rejectedpromise)
|
16 |
+
- [Promise interop](#promise-interop)
|
17 |
+
- [Implementation notes](#implementation-notes)
|
18 |
+
|
19 |
+
|
20 |
+
# Features
|
21 |
+
|
22 |
+
- [Promises/A+](https://promisesaplus.com/) implementation.
|
23 |
+
- Promise resolution and chaining is handled iteratively, allowing for
|
24 |
+
"infinite" promise chaining.
|
25 |
+
- Promises have a synchronous `wait` method.
|
26 |
+
- Promises can be cancelled.
|
27 |
+
- Works with any object that has a `then` function.
|
28 |
+
- C# style async/await coroutine promises using
|
29 |
+
`GuzzleHttp\Promise\coroutine()`.
|
30 |
+
|
31 |
+
|
32 |
+
# Quick start
|
33 |
+
|
34 |
+
A *promise* represents the eventual result of an asynchronous operation. The
|
35 |
+
primary way of interacting with a promise is through its `then` method, which
|
36 |
+
registers callbacks to receive either a promise's eventual value or the reason
|
37 |
+
why the promise cannot be fulfilled.
|
38 |
+
|
39 |
+
|
40 |
+
## Callbacks
|
41 |
+
|
42 |
+
Callbacks are registered with the `then` method by providing an optional
|
43 |
+
`$onFulfilled` followed by an optional `$onRejected` function.
|
44 |
+
|
45 |
+
|
46 |
+
```php
|
47 |
+
use GuzzleHttp\Promise\Promise;
|
48 |
+
|
49 |
+
$promise = new Promise();
|
50 |
+
$promise->then(
|
51 |
+
// $onFulfilled
|
52 |
+
function ($value) {
|
53 |
+
echo 'The promise was fulfilled.';
|
54 |
+
},
|
55 |
+
// $onRejected
|
56 |
+
function ($reason) {
|
57 |
+
echo 'The promise was rejected.';
|
58 |
+
}
|
59 |
+
);
|
60 |
+
```
|
61 |
+
|
62 |
+
*Resolving* a promise means that you either fulfill a promise with a *value* or
|
63 |
+
reject a promise with a *reason*. Resolving a promises triggers callbacks
|
64 |
+
registered with the promises's `then` method. These callbacks are triggered
|
65 |
+
only once and in the order in which they were added.
|
66 |
+
|
67 |
+
|
68 |
+
## Resolving a promise
|
69 |
+
|
70 |
+
Promises are fulfilled using the `resolve($value)` method. Resolving a promise
|
71 |
+
with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
|
72 |
+
all of the onFulfilled callbacks (resolving a promise with a rejected promise
|
73 |
+
will reject the promise and trigger the `$onRejected` callbacks).
|
74 |
+
|
75 |
+
```php
|
76 |
+
use GuzzleHttp\Promise\Promise;
|
77 |
+
|
78 |
+
$promise = new Promise();
|
79 |
+
$promise
|
80 |
+
->then(function ($value) {
|
81 |
+
// Return a value and don't break the chain
|
82 |
+
return "Hello, " . $value;
|
83 |
+
})
|
84 |
+
// This then is executed after the first then and receives the value
|
85 |
+
// returned from the first then.
|
86 |
+
->then(function ($value) {
|
87 |
+
echo $value;
|
88 |
+
});
|
89 |
+
|
90 |
+
// Resolving the promise triggers the $onFulfilled callbacks and outputs
|
91 |
+
// "Hello, reader."
|
92 |
+
$promise->resolve('reader.');
|
93 |
+
```
|
94 |
+
|
95 |
+
|
96 |
+
## Promise forwarding
|
97 |
+
|
98 |
+
Promises can be chained one after the other. Each then in the chain is a new
|
99 |
+
promise. The return value of a promise is what's forwarded to the next
|
100 |
+
promise in the chain. Returning a promise in a `then` callback will cause the
|
101 |
+
subsequent promises in the chain to only be fulfilled when the returned promise
|
102 |
+
has been fulfilled. The next promise in the chain will be invoked with the
|
103 |
+
resolved value of the promise.
|
104 |
+
|
105 |
+
```php
|
106 |
+
use GuzzleHttp\Promise\Promise;
|
107 |
+
|
108 |
+
$promise = new Promise();
|
109 |
+
$nextPromise = new Promise();
|
110 |
+
|
111 |
+
$promise
|
112 |
+
->then(function ($value) use ($nextPromise) {
|
113 |
+
echo $value;
|
114 |
+
return $nextPromise;
|
115 |
+
})
|
116 |
+
->then(function ($value) {
|
117 |
+
echo $value;
|
118 |
+
});
|
119 |
+
|
120 |
+
// Triggers the first callback and outputs "A"
|
121 |
+
$promise->resolve('A');
|
122 |
+
// Triggers the second callback and outputs "B"
|
123 |
+
$nextPromise->resolve('B');
|
124 |
+
```
|
125 |
+
|
126 |
+
## Promise rejection
|
127 |
+
|
128 |
+
When a promise is rejected, the `$onRejected` callbacks are invoked with the
|
129 |
+
rejection reason.
|
130 |
+
|
131 |
+
```php
|
132 |
+
use GuzzleHttp\Promise\Promise;
|
133 |
+
|
134 |
+
$promise = new Promise();
|
135 |
+
$promise->then(null, function ($reason) {
|
136 |
+
echo $reason;
|
137 |
+
});
|
138 |
+
|
139 |
+
$promise->reject('Error!');
|
140 |
+
// Outputs "Error!"
|
141 |
+
```
|
142 |
+
|
143 |
+
## Rejection forwarding
|
144 |
+
|
145 |
+
If an exception is thrown in an `$onRejected` callback, subsequent
|
146 |
+
`$onRejected` callbacks are invoked with the thrown exception as the reason.
|
147 |
+
|
148 |
+
```php
|
149 |
+
use GuzzleHttp\Promise\Promise;
|
150 |
+
|
151 |
+
$promise = new Promise();
|
152 |
+
$promise->then(null, function ($reason) {
|
153 |
+
throw new \Exception($reason);
|
154 |
+
})->then(null, function ($reason) {
|
155 |
+
assert($reason->getMessage() === 'Error!');
|
156 |
+
});
|
157 |
+
|
158 |
+
$promise->reject('Error!');
|
159 |
+
```
|
160 |
+
|
161 |
+
You can also forward a rejection down the promise chain by returning a
|
162 |
+
`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
|
163 |
+
`$onRejected` callback.
|
164 |
+
|
165 |
+
```php
|
166 |
+
use GuzzleHttp\Promise\Promise;
|
167 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
168 |
+
|
169 |
+
$promise = new Promise();
|
170 |
+
$promise->then(null, function ($reason) {
|
171 |
+
return new RejectedPromise($reason);
|
172 |
+
})->then(null, function ($reason) {
|
173 |
+
assert($reason === 'Error!');
|
174 |
+
});
|
175 |
+
|
176 |
+
$promise->reject('Error!');
|
177 |
+
```
|
178 |
+
|
179 |
+
If an exception is not thrown in a `$onRejected` callback and the callback
|
180 |
+
does not return a rejected promise, downstream `$onFulfilled` callbacks are
|
181 |
+
invoked using the value returned from the `$onRejected` callback.
|
182 |
+
|
183 |
+
```php
|
184 |
+
use GuzzleHttp\Promise\Promise;
|
185 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
186 |
+
|
187 |
+
$promise = new Promise();
|
188 |
+
$promise
|
189 |
+
->then(null, function ($reason) {
|
190 |
+
return "It's ok";
|
191 |
+
})
|
192 |
+
->then(function ($value) {
|
193 |
+
assert($value === "It's ok");
|
194 |
+
});
|
195 |
+
|
196 |
+
$promise->reject('Error!');
|
197 |
+
```
|
198 |
+
|
199 |
+
# Synchronous wait
|
200 |
+
|
201 |
+
You can synchronously force promises to complete using a promise's `wait`
|
202 |
+
method. When creating a promise, you can provide a wait function that is used
|
203 |
+
to synchronously force a promise to complete. When a wait function is invoked
|
204 |
+
it is expected to deliver a value to the promise or reject the promise. If the
|
205 |
+
wait function does not deliver a value, then an exception is thrown. The wait
|
206 |
+
function provided to a promise constructor is invoked when the `wait` function
|
207 |
+
of the promise is called.
|
208 |
+
|
209 |
+
```php
|
210 |
+
$promise = new Promise(function () use (&$promise) {
|
211 |
+
$promise->resolve('foo');
|
212 |
+
});
|
213 |
+
|
214 |
+
// Calling wait will return the value of the promise.
|
215 |
+
echo $promise->wait(); // outputs "foo"
|
216 |
+
```
|
217 |
+
|
218 |
+
If an exception is encountered while invoking the wait function of a promise,
|
219 |
+
the promise is rejected with the exception and the exception is thrown.
|
220 |
+
|
221 |
+
```php
|
222 |
+
$promise = new Promise(function () use (&$promise) {
|
223 |
+
throw new \Exception('foo');
|
224 |
+
});
|
225 |
+
|
226 |
+
$promise->wait(); // throws the exception.
|
227 |
+
```
|
228 |
+
|
229 |
+
Calling `wait` on a promise that has been fulfilled will not trigger the wait
|
230 |
+
function. It will simply return the previously resolved value.
|
231 |
+
|
232 |
+
```php
|
233 |
+
$promise = new Promise(function () { die('this is not called!'); });
|
234 |
+
$promise->resolve('foo');
|
235 |
+
echo $promise->wait(); // outputs "foo"
|
236 |
+
```
|
237 |
+
|
238 |
+
Calling `wait` on a promise that has been rejected will throw an exception. If
|
239 |
+
the rejection reason is an instance of `\Exception` the reason is thrown.
|
240 |
+
Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
|
241 |
+
can be obtained by calling the `getReason` method of the exception.
|
242 |
+
|
243 |
+
```php
|
244 |
+
$promise = new Promise();
|
245 |
+
$promise->reject('foo');
|
246 |
+
$promise->wait();
|
247 |
+
```
|
248 |
+
|
249 |
+
> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
|
250 |
+
|
251 |
+
|
252 |
+
## Unwrapping a promise
|
253 |
+
|
254 |
+
When synchronously waiting on a promise, you are joining the state of the
|
255 |
+
promise into the current state of execution (i.e., return the value of the
|
256 |
+
promise if it was fulfilled or throw an exception if it was rejected). This is
|
257 |
+
called "unwrapping" the promise. Waiting on a promise will by default unwrap
|
258 |
+
the promise state.
|
259 |
+
|
260 |
+
You can force a promise to resolve and *not* unwrap the state of the promise
|
261 |
+
by passing `false` to the first argument of the `wait` function:
|
262 |
+
|
263 |
+
```php
|
264 |
+
$promise = new Promise();
|
265 |
+
$promise->reject('foo');
|
266 |
+
// This will not throw an exception. It simply ensures the promise has
|
267 |
+
// been resolved.
|
268 |
+
$promise->wait(false);
|
269 |
+
```
|
270 |
+
|
271 |
+
When unwrapping a promise, the resolved value of the promise will be waited
|
272 |
+
upon until the unwrapped value is not a promise. This means that if you resolve
|
273 |
+
promise A with a promise B and unwrap promise A, the value returned by the
|
274 |
+
wait function will be the value delivered to promise B.
|
275 |
+
|
276 |
+
**Note**: when you do not unwrap the promise, no value is returned.
|
277 |
+
|
278 |
+
|
279 |
+
# Cancellation
|
280 |
+
|
281 |
+
You can cancel a promise that has not yet been fulfilled using the `cancel()`
|
282 |
+
method of a promise. When creating a promise you can provide an optional
|
283 |
+
cancel function that when invoked cancels the action of computing a resolution
|
284 |
+
of the promise.
|
285 |
+
|
286 |
+
|
287 |
+
# API
|
288 |
+
|
289 |
+
|
290 |
+
## Promise
|
291 |
+
|
292 |
+
When creating a promise object, you can provide an optional `$waitFn` and
|
293 |
+
`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
|
294 |
+
expected to resolve the promise. `$cancelFn` is a function with no arguments
|
295 |
+
that is expected to cancel the computation of a promise. It is invoked when the
|
296 |
+
`cancel()` method of a promise is called.
|
297 |
+
|
298 |
+
```php
|
299 |
+
use GuzzleHttp\Promise\Promise;
|
300 |
+
|
301 |
+
$promise = new Promise(
|
302 |
+
function () use (&$promise) {
|
303 |
+
$promise->resolve('waited');
|
304 |
+
},
|
305 |
+
function () {
|
306 |
+
// do something that will cancel the promise computation (e.g., close
|
307 |
+
// a socket, cancel a database query, etc...)
|
308 |
+
}
|
309 |
+
);
|
310 |
+
|
311 |
+
assert('waited' === $promise->wait());
|
312 |
+
```
|
313 |
+
|
314 |
+
A promise has the following methods:
|
315 |
+
|
316 |
+
- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
|
317 |
+
|
318 |
+
Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.
|
319 |
+
|
320 |
+
- `otherwise(callable $onRejected) : PromiseInterface`
|
321 |
+
|
322 |
+
Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.
|
323 |
+
|
324 |
+
- `wait($unwrap = true) : mixed`
|
325 |
+
|
326 |
+
Synchronously waits on the promise to complete.
|
327 |
+
|
328 |
+
`$unwrap` controls whether or not the value of the promise is returned for a
|
329 |
+
fulfilled promise or if an exception is thrown if the promise is rejected.
|
330 |
+
This is set to `true` by default.
|
331 |
+
|
332 |
+
- `cancel()`
|
333 |
+
|
334 |
+
Attempts to cancel the promise if possible. The promise being cancelled and
|
335 |
+
the parent most ancestor that has not yet been resolved will also be
|
336 |
+
cancelled. Any promises waiting on the cancelled promise to resolve will also
|
337 |
+
be cancelled.
|
338 |
+
|
339 |
+
- `getState() : string`
|
340 |
+
|
341 |
+
Returns the state of the promise. One of `pending`, `fulfilled`, or
|
342 |
+
`rejected`.
|
343 |
+
|
344 |
+
- `resolve($value)`
|
345 |
+
|
346 |
+
Fulfills the promise with the given `$value`.
|
347 |
+
|
348 |
+
- `reject($reason)`
|
349 |
+
|
350 |
+
Rejects the promise with the given `$reason`.
|
351 |
+
|
352 |
+
|
353 |
+
## FulfilledPromise
|
354 |
+
|
355 |
+
A fulfilled promise can be created to represent a promise that has been
|
356 |
+
fulfilled.
|
357 |
+
|
358 |
+
```php
|
359 |
+
use GuzzleHttp\Promise\FulfilledPromise;
|
360 |
+
|
361 |
+
$promise = new FulfilledPromise('value');
|
362 |
+
|
363 |
+
// Fulfilled callbacks are immediately invoked.
|
364 |
+
$promise->then(function ($value) {
|
365 |
+
echo $value;
|
366 |
+
});
|
367 |
+
```
|
368 |
+
|
369 |
+
|
370 |
+
## RejectedPromise
|
371 |
+
|
372 |
+
A rejected promise can be created to represent a promise that has been
|
373 |
+
rejected.
|
374 |
+
|
375 |
+
```php
|
376 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
377 |
+
|
378 |
+
$promise = new RejectedPromise('Error');
|
379 |
+
|
380 |
+
// Rejected callbacks are immediately invoked.
|
381 |
+
$promise->then(null, function ($reason) {
|
382 |
+
echo $reason;
|
383 |
+
});
|
384 |
+
```
|
385 |
+
|
386 |
+
|
387 |
+
# Promise interop
|
388 |
+
|
389 |
+
This library works with foreign promises that have a `then` method. This means
|
390 |
+
you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
|
391 |
+
for example. When a foreign promise is returned inside of a then method
|
392 |
+
callback, promise resolution will occur recursively.
|
393 |
+
|
394 |
+
```php
|
395 |
+
// Create a React promise
|
396 |
+
$deferred = new React\Promise\Deferred();
|
397 |
+
$reactPromise = $deferred->promise();
|
398 |
+
|
399 |
+
// Create a Guzzle promise that is fulfilled with a React promise.
|
400 |
+
$guzzlePromise = new \GuzzleHttp\Promise\Promise();
|
401 |
+
$guzzlePromise->then(function ($value) use ($reactPromise) {
|
402 |
+
// Do something something with the value...
|
403 |
+
// Return the React promise
|
404 |
+
return $reactPromise;
|
405 |
+
});
|
406 |
+
```
|
407 |
+
|
408 |
+
Please note that wait and cancel chaining is no longer possible when forwarding
|
409 |
+
a foreign promise. You will need to wrap a third-party promise with a Guzzle
|
410 |
+
promise in order to utilize wait and cancel functions with foreign promises.
|
411 |
+
|
412 |
+
|
413 |
+
## Event Loop Integration
|
414 |
+
|
415 |
+
In order to keep the stack size constant, Guzzle promises are resolved
|
416 |
+
asynchronously using a task queue. When waiting on promises synchronously, the
|
417 |
+
task queue will be automatically run to ensure that the blocking promise and
|
418 |
+
any forwarded promises are resolved. When using promises asynchronously in an
|
419 |
+
event loop, you will need to run the task queue on each tick of the loop. If
|
420 |
+
you do not run the task queue, then promises will not be resolved.
|
421 |
+
|
422 |
+
You can run the task queue using the `run()` method of the global task queue
|
423 |
+
instance.
|
424 |
+
|
425 |
+
```php
|
426 |
+
// Get the global task queue
|
427 |
+
$queue = \GuzzleHttp\Promise\queue();
|
428 |
+
$queue->run();
|
429 |
+
```
|
430 |
+
|
431 |
+
For example, you could use Guzzle promises with React using a periodic timer:
|
432 |
+
|
433 |
+
```php
|
434 |
+
$loop = React\EventLoop\Factory::create();
|
435 |
+
$loop->addPeriodicTimer(0, [$queue, 'run']);
|
436 |
+
```
|
437 |
+
|
438 |
+
*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
|
439 |
+
|
440 |
+
|
441 |
+
# Implementation notes
|
442 |
+
|
443 |
+
|
444 |
+
## Promise resolution and chaining is handled iteratively
|
445 |
+
|
446 |
+
By shuffling pending handlers from one owner to another, promises are
|
447 |
+
resolved iteratively, allowing for "infinite" then chaining.
|
448 |
+
|
449 |
+
```php
|
450 |
+
<?php
|
451 |
+
require 'vendor/autoload.php';
|
452 |
+
|
453 |
+
use GuzzleHttp\Promise\Promise;
|
454 |
+
|
455 |
+
$parent = new Promise();
|
456 |
+
$p = $parent;
|
457 |
+
|
458 |
+
for ($i = 0; $i < 1000; $i++) {
|
459 |
+
$p = $p->then(function ($v) {
|
460 |
+
// The stack size remains constant (a good thing)
|
461 |
+
echo xdebug_get_stack_depth() . ', ';
|
462 |
+
return $v + 1;
|
463 |
+
});
|
464 |
+
}
|
465 |
+
|
466 |
+
$parent->resolve(0);
|
467 |
+
var_dump($p->wait()); // int(1000)
|
468 |
+
|
469 |
+
```
|
470 |
+
|
471 |
+
When a promise is fulfilled or rejected with a non-promise value, the promise
|
472 |
+
then takes ownership of the handlers of each child promise and delivers values
|
473 |
+
down the chain without using recursion.
|
474 |
+
|
475 |
+
When a promise is resolved with another promise, the original promise transfers
|
476 |
+
all of its pending handlers to the new promise. When the new promise is
|
477 |
+
eventually resolved, all of the pending handlers are delivered the forwarded
|
478 |
+
value.
|
479 |
+
|
480 |
+
|
481 |
+
## A promise is the deferred.
|
482 |
+
|
483 |
+
Some promise libraries implement promises using a deferred object to represent
|
484 |
+
a computation and a promise object to represent the delivery of the result of
|
485 |
+
the computation. This is a nice separation of computation and delivery because
|
486 |
+
consumers of the promise cannot modify the value that will be eventually
|
487 |
+
delivered.
|
488 |
+
|
489 |
+
One side effect of being able to implement promise resolution and chaining
|
490 |
+
iteratively is that you need to be able for one promise to reach into the state
|
491 |
+
of another promise to shuffle around ownership of handlers. In order to achieve
|
492 |
+
this without making the handlers of a promise publicly mutable, a promise is
|
493 |
+
also the deferred value, allowing promises of the same parent class to reach
|
494 |
+
into and modify the private properties of promises of the same type. While this
|
495 |
+
does allow consumers of the value to modify the resolution or rejection of the
|
496 |
+
deferred, it is a small price to pay for keeping the stack size constant.
|
497 |
+
|
498 |
+
```php
|
499 |
+
$promise = new Promise();
|
500 |
+
$promise->then(function ($value) { echo $value; });
|
501 |
+
// The promise is the deferred value, so you can deliver a value to it.
|
502 |
+
$promise->resolve('foo');
|
503 |
+
// prints "foo"
|
504 |
+
```
|
vendor/guzzlehttp/psr7/README.md
ADDED
@@ -0,0 +1,745 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# PSR-7 Message Implementation
|
2 |
+
|
3 |
+
This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/)
|
4 |
+
message implementation, several stream decorators, and some helpful
|
5 |
+
functionality like query string parsing.
|
6 |
+
|
7 |
+
|
8 |
+
[![Build Status](https://travis-ci.org/guzzle/psr7.svg?branch=master)](https://travis-ci.org/guzzle/psr7)
|
9 |
+
|
10 |
+
|
11 |
+
# Stream implementation
|
12 |
+
|
13 |
+
This package comes with a number of stream implementations and stream
|
14 |
+
decorators.
|
15 |
+
|
16 |
+
|
17 |
+
## AppendStream
|
18 |
+
|
19 |
+
`GuzzleHttp\Psr7\AppendStream`
|
20 |
+
|
21 |
+
Reads from multiple streams, one after the other.
|
22 |
+
|
23 |
+
```php
|
24 |
+
use GuzzleHttp\Psr7;
|
25 |
+
|
26 |
+
$a = Psr7\stream_for('abc, ');
|
27 |
+
$b = Psr7\stream_for('123.');
|
28 |
+
$composed = new Psr7\AppendStream([$a, $b]);
|
29 |
+
|
30 |
+
$composed->addStream(Psr7\stream_for(' Above all listen to me'));
|
31 |
+
|
32 |
+
echo $composed; // abc, 123. Above all listen to me.
|
33 |
+
```
|
34 |
+
|
35 |
+
|
36 |
+
## BufferStream
|
37 |
+
|
38 |
+
`GuzzleHttp\Psr7\BufferStream`
|
39 |
+
|
40 |
+
Provides a buffer stream that can be written to fill a buffer, and read
|
41 |
+
from to remove bytes from the buffer.
|
42 |
+
|
43 |
+
This stream returns a "hwm" metadata value that tells upstream consumers
|
44 |
+
what the configured high water mark of the stream is, or the maximum
|
45 |
+
preferred size of the buffer.
|
46 |
+
|
47 |
+
```php
|
48 |
+
use GuzzleHttp\Psr7;
|
49 |
+
|
50 |
+
// When more than 1024 bytes are in the buffer, it will begin returning
|
51 |
+
// false to writes. This is an indication that writers should slow down.
|
52 |
+
$buffer = new Psr7\BufferStream(1024);
|
53 |
+
```
|
54 |
+
|
55 |
+
|
56 |
+
## CachingStream
|
57 |
+
|
58 |
+
The CachingStream is used to allow seeking over previously read bytes on
|
59 |
+
non-seekable streams. This can be useful when transferring a non-seekable
|
60 |
+
entity body fails due to needing to rewind the stream (for example, resulting
|
61 |
+
from a redirect). Data that is read from the remote stream will be buffered in
|
62 |
+
a PHP temp stream so that previously read bytes are cached first in memory,
|
63 |
+
then on disk.
|
64 |
+
|
65 |
+
```php
|
66 |
+
use GuzzleHttp\Psr7;
|
67 |
+
|
68 |
+
$original = Psr7\stream_for(fopen('http://www.google.com', 'r'));
|
69 |
+
$stream = new Psr7\CachingStream($original);
|
70 |
+
|
71 |
+
$stream->read(1024);
|
72 |
+
echo $stream->tell();
|
73 |
+
// 1024
|
74 |
+
|
75 |
+
$stream->seek(0);
|
76 |
+
echo $stream->tell();
|
77 |
+
// 0
|
78 |
+
```
|
79 |
+
|
80 |
+
|
81 |
+
## DroppingStream
|
82 |
+
|
83 |
+
`GuzzleHttp\Psr7\DroppingStream`
|
84 |
+
|
85 |
+
Stream decorator that begins dropping data once the size of the underlying
|
86 |
+
stream becomes too full.
|
87 |
+
|
88 |
+
```php
|
89 |
+
use GuzzleHttp\Psr7;
|
90 |
+
|
91 |
+
// Create an empty stream
|
92 |
+
$stream = Psr7\stream_for();
|
93 |
+
|
94 |
+
// Start dropping data when the stream has more than 10 bytes
|
95 |
+
$dropping = new Psr7\DroppingStream($stream, 10);
|
96 |
+
|
97 |
+
$dropping->write('01234567890123456789');
|
98 |
+
echo $stream; // 0123456789
|
99 |
+
```
|
100 |
+
|
101 |
+
|
102 |
+
## FnStream
|
103 |
+
|
104 |
+
`GuzzleHttp\Psr7\FnStream`
|
105 |
+
|
106 |
+
Compose stream implementations based on a hash of functions.
|
107 |
+
|
108 |
+
Allows for easy testing and extension of a provided stream without needing
|
109 |
+
to create a concrete class for a simple extension point.
|
110 |
+
|
111 |
+
```php
|
112 |
+
|
113 |
+
use GuzzleHttp\Psr7;
|
114 |
+
|
115 |
+
$stream = Psr7\stream_for('hi');
|
116 |
+
$fnStream = Psr7\FnStream::decorate($stream, [
|
117 |
+
'rewind' => function () use ($stream) {
|
118 |
+
echo 'About to rewind - ';
|
119 |
+
$stream->rewind();
|
120 |
+
echo 'rewound!';
|
121 |
+
}
|
122 |
+
]);
|
123 |
+
|
124 |
+
$fnStream->rewind();
|
125 |
+
// Outputs: About to rewind - rewound!
|
126 |
+
```
|
127 |
+
|
128 |
+
|
129 |
+
## InflateStream
|
130 |
+
|
131 |
+
`GuzzleHttp\Psr7\InflateStream`
|
132 |
+
|
133 |
+
Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
|
134 |
+
|
135 |
+
This stream decorator skips the first 10 bytes of the given stream to remove
|
136 |
+
the gzip header, converts the provided stream to a PHP stream resource,
|
137 |
+
then appends the zlib.inflate filter. The stream is then converted back
|
138 |
+
to a Guzzle stream resource to be used as a Guzzle stream.
|
139 |
+
|
140 |
+
|
141 |
+
## LazyOpenStream
|
142 |
+
|
143 |
+
`GuzzleHttp\Psr7\LazyOpenStream`
|
144 |
+
|
145 |
+
Lazily reads or writes to a file that is opened only after an IO operation
|
146 |
+
take place on the stream.
|
147 |
+
|
148 |
+
```php
|
149 |
+
use GuzzleHttp\Psr7;
|
150 |
+
|
151 |
+
$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
|
152 |
+
// The file has not yet been opened...
|
153 |
+
|
154 |
+
echo $stream->read(10);
|
155 |
+
// The file is opened and read from only when needed.
|
156 |
+
```
|
157 |
+
|
158 |
+
|
159 |
+
## LimitStream
|
160 |
+
|
161 |
+
`GuzzleHttp\Psr7\LimitStream`
|
162 |
+
|
163 |
+
LimitStream can be used to read a subset or slice of an existing stream object.
|
164 |
+
This can be useful for breaking a large file into smaller pieces to be sent in
|
165 |
+
chunks (e.g. Amazon S3's multipart upload API).
|
166 |
+
|
167 |
+
```php
|
168 |
+
use GuzzleHttp\Psr7;
|
169 |
+
|
170 |
+
$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+'));
|
171 |
+
echo $original->getSize();
|
172 |
+
// >>> 1048576
|
173 |
+
|
174 |
+
// Limit the size of the body to 1024 bytes and start reading from byte 2048
|
175 |
+
$stream = new Psr7\LimitStream($original, 1024, 2048);
|
176 |
+
echo $stream->getSize();
|
177 |
+
// >>> 1024
|
178 |
+
echo $stream->tell();
|
179 |
+
// >>> 0
|
180 |
+
```
|
181 |
+
|
182 |
+
|
183 |
+
## MultipartStream
|
184 |
+
|
185 |
+
`GuzzleHttp\Psr7\MultipartStream`
|
186 |
+
|
187 |
+
Stream that when read returns bytes for a streaming multipart or
|
188 |
+
multipart/form-data stream.
|
189 |
+
|
190 |
+
|
191 |
+
## NoSeekStream
|
192 |
+
|
193 |
+
`GuzzleHttp\Psr7\NoSeekStream`
|
194 |
+
|
195 |
+
NoSeekStream wraps a stream and does not allow seeking.
|
196 |
+
|
197 |
+
```php
|
198 |
+
use GuzzleHttp\Psr7;
|
199 |
+
|
200 |
+
$original = Psr7\stream_for('foo');
|
201 |
+
$noSeek = new Psr7\NoSeekStream($original);
|
202 |
+
|
203 |
+
echo $noSeek->read(3);
|
204 |
+
// foo
|
205 |
+
var_export($noSeek->isSeekable());
|
206 |
+
// false
|
207 |
+
$noSeek->seek(0);
|
208 |
+
var_export($noSeek->read(3));
|
209 |
+
// NULL
|
210 |
+
```
|
211 |
+
|
212 |
+
|
213 |
+
## PumpStream
|
214 |
+
|
215 |
+
`GuzzleHttp\Psr7\PumpStream`
|
216 |
+
|
217 |
+
Provides a read only stream that pumps data from a PHP callable.
|
218 |
+
|
219 |
+
When invoking the provided callable, the PumpStream will pass the amount of
|
220 |
+
data requested to read to the callable. The callable can choose to ignore
|
221 |
+
this value and return fewer or more bytes than requested. Any extra data
|
222 |
+
returned by the provided callable is buffered internally until drained using
|
223 |
+
the read() function of the PumpStream. The provided callable MUST return
|
224 |
+
false when there is no more data to read.
|
225 |
+
|
226 |
+
|
227 |
+
## Implementing stream decorators
|
228 |
+
|
229 |
+
Creating a stream decorator is very easy thanks to the
|
230 |
+
`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
|
231 |
+
implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
|
232 |
+
stream. Just `use` the `StreamDecoratorTrait` and implement your custom
|
233 |
+
methods.
|
234 |
+
|
235 |
+
For example, let's say we wanted to call a specific function each time the last
|
236 |
+
byte is read from a stream. This could be implemented by overriding the
|
237 |
+
`read()` method.
|
238 |
+
|
239 |
+
```php
|
240 |
+
use Psr\Http\Message\StreamInterface;
|
241 |
+
use GuzzleHttp\Psr7\StreamDecoratorTrait;
|
242 |
+
|
243 |
+
class EofCallbackStream implements StreamInterface
|
244 |
+
{
|
245 |
+
use StreamDecoratorTrait;
|
246 |
+
|
247 |
+
private $callback;
|
248 |
+
|
249 |
+
public function __construct(StreamInterface $stream, callable $cb)
|
250 |
+
{
|
251 |
+
$this->stream = $stream;
|
252 |
+
$this->callback = $cb;
|
253 |
+
}
|
254 |
+
|
255 |
+
public function read($length)
|
256 |
+
{
|
257 |
+
$result = $this->stream->read($length);
|
258 |
+
|
259 |
+
// Invoke the callback when EOF is hit.
|
260 |
+
if ($this->eof()) {
|
261 |
+
call_user_func($this->callback);
|
262 |
+
}
|
263 |
+
|
264 |
+
return $result;
|
265 |
+
}
|
266 |
+
}
|
267 |
+
```
|
268 |
+
|
269 |
+
This decorator could be added to any existing stream and used like so:
|
270 |
+
|
271 |
+
```php
|
272 |
+
use GuzzleHttp\Psr7;
|
273 |
+
|
274 |
+
$original = Psr7\stream_for('foo');
|
275 |
+
|
276 |
+
$eofStream = new EofCallbackStream($original, function () {
|
277 |
+
echo 'EOF!';
|
278 |
+
});
|
279 |
+
|
280 |
+
$eofStream->read(2);
|
281 |
+
$eofStream->read(1);
|
282 |
+
// echoes "EOF!"
|
283 |
+
$eofStream->seek(0);
|
284 |
+
$eofStream->read(3);
|
285 |
+
// echoes "EOF!"
|
286 |
+
```
|
287 |
+
|
288 |
+
|
289 |
+
## PHP StreamWrapper
|
290 |
+
|
291 |
+
You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
|
292 |
+
PSR-7 stream as a PHP stream resource.
|
293 |
+
|
294 |
+
Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
|
295 |
+
stream from a PSR-7 stream.
|
296 |
+
|
297 |
+
```php
|
298 |
+
use GuzzleHttp\Psr7\StreamWrapper;
|
299 |
+
|
300 |
+
$stream = GuzzleHttp\Psr7\stream_for('hello!');
|
301 |
+
$resource = StreamWrapper::getResource($stream);
|
302 |
+
echo fread($resource, 6); // outputs hello!
|
303 |
+
```
|
304 |
+
|
305 |
+
|
306 |
+
# Function API
|
307 |
+
|
308 |
+
There are various functions available under the `GuzzleHttp\Psr7` namespace.
|
309 |
+
|
310 |
+
|
311 |
+
## `function str`
|
312 |
+
|
313 |
+
`function str(MessageInterface $message)`
|
314 |
+
|
315 |
+
Returns the string representation of an HTTP message.
|
316 |
+
|
317 |
+
```php
|
318 |
+
$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
|
319 |
+
echo GuzzleHttp\Psr7\str($request);
|
320 |
+
```
|
321 |
+
|
322 |
+
|
323 |
+
## `function uri_for`
|
324 |
+
|
325 |
+
`function uri_for($uri)`
|
326 |
+
|
327 |
+
This function accepts a string or `Psr\Http\Message\UriInterface` and returns a
|
328 |
+
UriInterface for the given value. If the value is already a `UriInterface`, it
|
329 |
+
is returned as-is.
|
330 |
+
|
331 |
+
```php
|
332 |
+
$uri = GuzzleHttp\Psr7\uri_for('http://example.com');
|
333 |
+
assert($uri === GuzzleHttp\Psr7\uri_for($uri));
|
334 |
+
```
|
335 |
+
|
336 |
+
|
337 |
+
## `function stream_for`
|
338 |
+
|
339 |
+
`function stream_for($resource = '', array $options = [])`
|
340 |
+
|
341 |
+
Create a new stream based on the input type.
|
342 |
+
|
343 |
+
Options is an associative array that can contain the following keys:
|
344 |
+
|
345 |
+
* - metadata: Array of custom metadata.
|
346 |
+
* - size: Size of the stream.
|
347 |
+
|
348 |
+
This method accepts the following `$resource` types:
|
349 |
+
|
350 |
+
- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
|
351 |
+
- `string`: Creates a stream object that uses the given string as the contents.
|
352 |
+
- `resource`: Creates a stream object that wraps the given PHP stream resource.
|
353 |
+
- `Iterator`: If the provided value implements `Iterator`, then a read-only
|
354 |
+
stream object will be created that wraps the given iterable. Each time the
|
355 |
+
stream is read from, data from the iterator will fill a buffer and will be
|
356 |
+
continuously called until the buffer is equal to the requested read size.
|
357 |
+
Subsequent read calls will first read from the buffer and then call `next`
|
358 |
+
on the underlying iterator until it is exhausted.
|
359 |
+
- `object` with `__toString()`: If the object has the `__toString()` method,
|
360 |
+
the object will be cast to a string and then a stream will be returned that
|
361 |
+
uses the string value.
|
362 |
+
- `NULL`: When `null` is passed, an empty stream object is returned.
|
363 |
+
- `callable` When a callable is passed, a read-only stream object will be
|
364 |
+
created that invokes the given callable. The callable is invoked with the
|
365 |
+
number of suggested bytes to read. The callable can return any number of
|
366 |
+
bytes, but MUST return `false` when there is no more data to return. The
|
367 |
+
stream object that wraps the callable will invoke the callable until the
|
368 |
+
number of requested bytes are available. Any additional bytes will be
|
369 |
+
buffered and used in subsequent reads.
|
370 |
+
|
371 |
+
```php
|
372 |
+
$stream = GuzzleHttp\Psr7\stream_for('foo');
|
373 |
+
$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r'));
|
374 |
+
|
375 |
+
$generator = function ($bytes) {
|
376 |
+
for ($i = 0; $i < $bytes; $i++) {
|
377 |
+
yield ' ';
|
378 |
+
}
|
379 |
+
}
|
380 |
+
|
381 |
+
$stream = GuzzleHttp\Psr7\stream_for($generator(100));
|
382 |
+
```
|
383 |
+
|
384 |
+
|
385 |
+
## `function parse_header`
|
386 |
+
|
387 |
+
`function parse_header($header)`
|
388 |
+
|
389 |
+
Parse an array of header values containing ";" separated data into an array of
|
390 |
+
associative arrays representing the header key value pair data of the header.
|
391 |
+
When a parameter does not contain a value, but just contains a key, this
|
392 |
+
function will inject a key with a '' string value.
|
393 |
+
|
394 |
+
|
395 |
+
## `function normalize_header`
|
396 |
+
|
397 |
+
`function normalize_header($header)`
|
398 |
+
|
399 |
+
Converts an array of header values that may contain comma separated headers
|
400 |
+
into an array of headers with no comma separated values.
|
401 |
+
|
402 |
+
|
403 |
+
## `function modify_request`
|
404 |
+
|
405 |
+
`function modify_request(RequestInterface $request, array $changes)`
|
406 |
+
|
407 |
+
Clone and modify a request with the given changes. This method is useful for
|
408 |
+
reducing the number of clones needed to mutate a message.
|
409 |
+
|
410 |
+
The changes can be one of:
|
411 |
+
|
412 |
+
- method: (string) Changes the HTTP method.
|
413 |
+
- set_headers: (array) Sets the given headers.
|
414 |
+
- remove_headers: (array) Remove the given headers.
|
415 |
+
- body: (mixed) Sets the given body.
|
416 |
+
- uri: (UriInterface) Set the URI.
|
417 |
+
- query: (string) Set the query string value of the URI.
|
418 |
+
- version: (string) Set the protocol version.
|
419 |
+
|
420 |
+
|
421 |
+
## `function rewind_body`
|
422 |
+
|
423 |
+
`function rewind_body(MessageInterface $message)`
|
424 |
+
|
425 |
+
Attempts to rewind a message body and throws an exception on failure. The body
|
426 |
+
of the message will only be rewound if a call to `tell()` returns a value other
|
427 |
+
than `0`.
|
428 |
+
|
429 |
+
|
430 |
+
## `function try_fopen`
|
431 |
+
|
432 |
+
`function try_fopen($filename, $mode)`
|
433 |
+
|
434 |
+
Safely opens a PHP stream resource using a filename.
|
435 |
+
|
436 |
+
When fopen fails, PHP normally raises a warning. This function adds an error
|
437 |
+
handler that checks for errors and throws an exception instead.
|
438 |
+
|
439 |
+
|
440 |
+
## `function copy_to_string`
|
441 |
+
|
442 |
+
`function copy_to_string(StreamInterface $stream, $maxLen = -1)`
|
443 |
+
|
444 |
+
Copy the contents of a stream into a string until the given number of bytes
|
445 |
+
have been read.
|
446 |
+
|
447 |
+
|
448 |
+
## `function copy_to_stream`
|
449 |
+
|
450 |
+
`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)`
|
451 |
+
|
452 |
+
Copy the contents of a stream into another stream until the given number of
|
453 |
+
bytes have been read.
|
454 |
+
|
455 |
+
|
456 |
+
## `function hash`
|
457 |
+
|
458 |
+
`function hash(StreamInterface $stream, $algo, $rawOutput = false)`
|
459 |
+
|
460 |
+
Calculate a hash of a Stream. This method reads the entire stream to calculate
|
461 |
+
a rolling hash (based on PHP's hash_init functions).
|
462 |
+
|
463 |
+
|
464 |
+
## `function readline`
|
465 |
+
|
466 |
+
`function readline(StreamInterface $stream, $maxLength = null)`
|
467 |
+
|
468 |
+
Read a line from the stream up to the maximum allowed buffer length.
|
469 |
+
|
470 |
+
|
471 |
+
## `function parse_request`
|
472 |
+
|
473 |
+
`function parse_request($message)`
|
474 |
+
|
475 |
+
Parses a request message string into a request object.
|
476 |
+
|
477 |
+
|
478 |
+
## `function parse_response`
|
479 |
+
|
480 |
+
`function parse_response($message)`
|
481 |
+
|
482 |
+
Parses a response message string into a response object.
|
483 |
+
|
484 |
+
|
485 |
+
## `function parse_query`
|
486 |
+
|
487 |
+
`function parse_query($str, $urlEncoding = true)`
|
488 |
+
|
489 |
+
Parse a query string into an associative array.
|
490 |
+
|
491 |
+
If multiple values are found for the same key, the value of that key value pair
|
492 |
+
will become an array. This function does not parse nested PHP style arrays into
|
493 |
+
an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into
|
494 |
+
`['foo[a]' => '1', 'foo[b]' => '2']`).
|
495 |
+
|
496 |
+
|
497 |
+
## `function build_query`
|
498 |
+
|
499 |
+
`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)`
|
500 |
+
|
501 |
+
Build a query string from an array of key value pairs.
|
502 |
+
|
503 |
+
This function can use the return value of parse_query() to build a query string.
|
504 |
+
This function does not modify the provided keys when an array is encountered
|
505 |
+
(like http_build_query would).
|
506 |
+
|
507 |
+
|
508 |
+
## `function mimetype_from_filename`
|
509 |
+
|
510 |
+
`function mimetype_from_filename($filename)`
|
511 |
+
|
512 |
+
Determines the mimetype of a file by looking at its extension.
|
513 |
+
|
514 |
+
|
515 |
+
## `function mimetype_from_extension`
|
516 |
+
|
517 |
+
`function mimetype_from_extension($extension)`
|
518 |
+
|
519 |
+
Maps a file extensions to a mimetype.
|
520 |
+
|
521 |
+
|
522 |
+
# Additional URI Methods
|
523 |
+
|
524 |
+
Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
|
525 |
+
this library also provides additional functionality when working with URIs as static methods.
|
526 |
+
|
527 |
+
## URI Types
|
528 |
+
|
529 |
+
An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
|
530 |
+
An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
|
531 |
+
the base URI. Relative references can be divided into several forms according to
|
532 |
+
[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):
|
533 |
+
|
534 |
+
- network-path references, e.g. `//example.com/path`
|
535 |
+
- absolute-path references, e.g. `/path`
|
536 |
+
- relative-path references, e.g. `subpath`
|
537 |
+
|
538 |
+
The following methods can be used to identify the type of the URI.
|
539 |
+
|
540 |
+
### `GuzzleHttp\Psr7\Uri::isAbsolute`
|
541 |
+
|
542 |
+
`public static function isAbsolute(UriInterface $uri): bool`
|
543 |
+
|
544 |
+
Whether the URI is absolute, i.e. it has a scheme.
|
545 |
+
|
546 |
+
### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
|
547 |
+
|
548 |
+
`public static function isNetworkPathReference(UriInterface $uri): bool`
|
549 |
+
|
550 |
+
Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
|
551 |
+
termed an network-path reference.
|
552 |
+
|
553 |
+
### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
|
554 |
+
|
555 |
+
`public static function isAbsolutePathReference(UriInterface $uri): bool`
|
556 |
+
|
557 |
+
Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
|
558 |
+
termed an absolute-path reference.
|
559 |
+
|
560 |
+
### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
|
561 |
+
|
562 |
+
`public static function isRelativePathReference(UriInterface $uri): bool`
|
563 |
+
|
564 |
+
Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
|
565 |
+
termed a relative-path reference.
|
566 |
+
|
567 |
+
### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
|
568 |
+
|
569 |
+
`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
|
570 |
+
|
571 |
+
Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
|
572 |
+
fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
|
573 |
+
(apart from its fragment) is considered a same-document reference.
|
574 |
+
|
575 |
+
## URI Components
|
576 |
+
|
577 |
+
Additional methods to work with URI components.
|
578 |
+
|
579 |
+
### `GuzzleHttp\Psr7\Uri::isDefaultPort`
|
580 |
+
|
581 |
+
`public static function isDefaultPort(UriInterface $uri): bool`
|
582 |
+
|
583 |
+
Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
|
584 |
+
or the standard port. This method can be used independently of the implementation.
|
585 |
+
|
586 |
+
### `GuzzleHttp\Psr7\Uri::composeComponents`
|
587 |
+
|
588 |
+
`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
|
589 |
+
|
590 |
+
Composes a URI reference string from its various components according to
|
591 |
+
[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
|
592 |
+
manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
|
593 |
+
|
594 |
+
### `GuzzleHttp\Psr7\Uri::fromParts`
|
595 |
+
|
596 |
+
`public static function fromParts(array $parts): UriInterface`
|
597 |
+
|
598 |
+
Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components.
|
599 |
+
|
600 |
+
|
601 |
+
### `GuzzleHttp\Psr7\Uri::withQueryValue`
|
602 |
+
|
603 |
+
`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
|
604 |
+
|
605 |
+
Creates a new URI with a specific query string value. Any existing query string values that exactly match the
|
606 |
+
provided key are removed and replaced with the given key value pair. A value of null will set the query string
|
607 |
+
key without a value, e.g. "key" instead of "key=value".
|
608 |
+
|
609 |
+
### `GuzzleHttp\Psr7\Uri::withQueryValues`
|
610 |
+
|
611 |
+
`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface`
|
612 |
+
|
613 |
+
Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an
|
614 |
+
associative array of key => value.
|
615 |
+
|
616 |
+
### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
|
617 |
+
|
618 |
+
`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
|
619 |
+
|
620 |
+
Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
|
621 |
+
provided key are removed.
|
622 |
+
|
623 |
+
## Reference Resolution
|
624 |
+
|
625 |
+
`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
|
626 |
+
to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
|
627 |
+
do when resolving a link in a website based on the current request URI.
|
628 |
+
|
629 |
+
### `GuzzleHttp\Psr7\UriResolver::resolve`
|
630 |
+
|
631 |
+
`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
|
632 |
+
|
633 |
+
Converts the relative URI into a new URI that is resolved against the base URI.
|
634 |
+
|
635 |
+
### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
|
636 |
+
|
637 |
+
`public static function removeDotSegments(string $path): string`
|
638 |
+
|
639 |
+
Removes dot segments from a path and returns the new path according to
|
640 |
+
[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).
|
641 |
+
|
642 |
+
### `GuzzleHttp\Psr7\UriResolver::relativize`
|
643 |
+
|
644 |
+
`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
|
645 |
+
|
646 |
+
Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
|
647 |
+
|
648 |
+
```php
|
649 |
+
(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
|
650 |
+
```
|
651 |
+
|
652 |
+
One use-case is to use the current request URI as base URI and then generate relative links in your documents
|
653 |
+
to reduce the document size or offer self-contained downloadable document archives.
|
654 |
+
|
655 |
+
```php
|
656 |
+
$base = new Uri('http://example.com/a/b/');
|
657 |
+
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
|
658 |
+
echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
|
659 |
+
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
|
660 |
+
echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
|
661 |
+
```
|
662 |
+
|
663 |
+
## Normalization and Comparison
|
664 |
+
|
665 |
+
`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
|
666 |
+
[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).
|
667 |
+
|
668 |
+
### `GuzzleHttp\Psr7\UriNormalizer::normalize`
|
669 |
+
|
670 |
+
`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
|
671 |
+
|
672 |
+
Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
|
673 |
+
This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
|
674 |
+
of normalizations to apply. The following normalizations are available:
|
675 |
+
|
676 |
+
- `UriNormalizer::PRESERVING_NORMALIZATIONS`
|
677 |
+
|
678 |
+
Default normalizations which only include the ones that preserve semantics.
|
679 |
+
|
680 |
+
- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
|
681 |
+
|
682 |
+
All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
|
683 |
+
|
684 |
+
Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
|
685 |
+
|
686 |
+
- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
|
687 |
+
|
688 |
+
Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
|
689 |
+
ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
|
690 |
+
not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
|
691 |
+
characters by URI normalizers.
|
692 |
+
|
693 |
+
Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
|
694 |
+
|
695 |
+
- `UriNormalizer::CONVERT_EMPTY_PATH`
|
696 |
+
|
697 |
+
Converts the empty path to "/" for http and https URIs.
|
698 |
+
|
699 |
+
Example: `http://example.org` → `http://example.org/`
|
700 |
+
|
701 |
+
- `UriNormalizer::REMOVE_DEFAULT_HOST`
|
702 |
+
|
703 |
+
Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
|
704 |
+
"localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
|
705 |
+
RFC 3986.
|
706 |
+
|
707 |
+
Example: `file://localhost/myfile` → `file:///myfile`
|
708 |
+
|
709 |
+
- `UriNormalizer::REMOVE_DEFAULT_PORT`
|
710 |
+
|
711 |
+
Removes the default port of the given URI scheme from the URI.
|
712 |
+
|
713 |
+
Example: `http://example.org:80/` → `http://example.org/`
|
714 |
+
|
715 |
+
- `UriNormalizer::REMOVE_DOT_SEGMENTS`
|
716 |
+
|
717 |
+
Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
|
718 |
+
change the semantics of the URI reference.
|
719 |
+
|
720 |
+
Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
|
721 |
+
|
722 |
+
- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
|
723 |
+
|
724 |
+
Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
|
725 |
+
and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
|
726 |
+
may change the semantics. Encoded slashes (%2F) are not removed.
|
727 |
+
|
728 |
+
Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
|
729 |
+
|
730 |
+
- `UriNormalizer::SORT_QUERY_PARAMETERS`
|
731 |
+
|
732 |
+
Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
|
733 |
+
significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
|
734 |
+
of the URI.
|
735 |
+
|
736 |
+
Example: `?lang=en&article=fred` → `?article=fred&lang=en`
|
737 |
+
|
738 |
+
### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
|
739 |
+
|
740 |
+
`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
|
741 |
+
|
742 |
+
Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
|
743 |
+
`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
|
744 |
+
This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
|
745 |
+
equivalence or difference of relative references does not mean anything.
|
vendor/ralouphie/getallheaders/composer.json
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "ralouphie/getallheaders",
|
3 |
+
"description": "A polyfill for getallheaders.",
|
4 |
+
"license": "MIT",
|
5 |
+
"authors": [
|
6 |
+
{
|
7 |
+
"name": "Ralph Khattar",
|
8 |
+
"email": "ralph.khattar@gmail.com"
|
9 |
+
}
|
10 |
+
],
|
11 |
+
"require": {
|
12 |
+
"php": ">=5.6"
|
13 |
+
},
|
14 |
+
"require-dev": {
|
15 |
+
"phpunit/phpunit": "^5 || ^6.5",
|
16 |
+
"php-coveralls/php-coveralls": "^2.1"
|
17 |
+
},
|
18 |
+
"autoload": {
|
19 |
+
"files": ["src/getallheaders.php"]
|
20 |
+
},
|
21 |
+
"autoload-dev": {
|
22 |
+
"psr-4": {
|
23 |
+
"getallheaders\\Tests\\": "tests/"
|
24 |
+
}
|
25 |
+
}
|
26 |
+
}
|