v1.3.0
This commit is contained in:
parent
a97911a978
commit
916d7845be
91 changed files with 6861 additions and 6907 deletions
|
|
@ -1,5 +1,40 @@
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.3.0 =
|
||||||
|
|
||||||
|
_Release date: 2026-07-18_
|
||||||
|
|
||||||
|
**Highlights**
|
||||||
|
|
||||||
|
* New image sub-sizes control: ship less data per upload, ideal for large camera files. Reduces work in both the WP 7.1 client-side path (browser) and the server-side path.
|
||||||
|
|
||||||
|
**Added**
|
||||||
|
|
||||||
|
* Settings field **Image Sub-sizes** under Settings → iDrivee2 with three modes: `all` (default), `thumbnail` (only the default thumbnail), `none` (no sub-sizes).
|
||||||
|
* `IDRIVEE2_MEDIA_SUBSIZES_MODE` wp-config.php constant — same priority pattern as the S3 credentials; the field becomes read-only when set.
|
||||||
|
* In `thumbnail` and `none` modes the `big_image_size_threshold` filter is also disabled, so WordPress no longer creates a `-scaled` derivative for images larger than 2560px.
|
||||||
|
|
||||||
|
**Security**
|
||||||
|
|
||||||
|
* `composer audit` CVEs resolved: `guzzlehttp/guzzle` 7.11 → 7.15, `guzzlehttp/psr7` 2.11 → 2.13, `mtdowling/jmespath.php` 2.8 → 2.9 (CVE-2026-55767 / 55568 / 55766 / 54133).
|
||||||
|
|
||||||
|
**Tooling**
|
||||||
|
|
||||||
|
* `bin/preflight.sh` added — automated pre-deploy verification per AGENTS-testing-build-deployment.md (PHPCS, PHPStan, PHPCompatibility, PHPUnit + coverage, composer audit, candidate ZIP inspection).
|
||||||
|
* `.claude/settings.json` added — mechanical deny rules for `deploy.sh`, `git push/tag/merge` per AGENTS.md.
|
||||||
|
|
||||||
|
**Compatibility**
|
||||||
|
|
||||||
|
* WordPress: 4.1 - 7.1
|
||||||
|
* PHP: 8.1 - 8.5
|
||||||
|
|
||||||
|
**Tests**
|
||||||
|
|
||||||
|
* PHP Coding Standards: 3.13.5 (0 errors)
|
||||||
|
* WordPress Coding Standards: 3.3.0 (0 violations)
|
||||||
|
* PHPStan: Level 9, 0 errors
|
||||||
|
* PHPUnit: 38 tests, 60 assertions
|
||||||
|
|
||||||
= 1.2.1 =
|
= 1.2.1 =
|
||||||
|
|
||||||
_Release date: 2026-06-05_
|
_Release date: 2026-06-05_
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
* Gitea Plugin URI: https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload
|
* Gitea Plugin URI: https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload
|
||||||
* Primary Branch: main
|
* Primary Branch: main
|
||||||
* Description: Uploads media files to iDrivee2 (S3-compatible) with enterprise-grade security and logging.
|
* Description: Uploads media files to iDrivee2 (S3-compatible) with enterprise-grade security and logging.
|
||||||
* Version: 1.2.1
|
* Version: 1.3.0
|
||||||
* Requires at least: 4.1
|
* Requires at least: 4.1
|
||||||
* Requires PHP: 8.1
|
* Requires PHP: 8.1
|
||||||
* Author: ROBOTSTXT
|
* Author: ROBOTSTXT
|
||||||
|
|
@ -36,7 +36,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||||
*
|
*
|
||||||
* @since 1.1.4
|
* @since 1.1.4
|
||||||
*/
|
*/
|
||||||
define( 'IDRIVEE2_MEDIA_VERSION', '1.2.1' );
|
define( 'IDRIVEE2_MEDIA_VERSION', '1.3.0' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load Composer autoloader if available.
|
* Load Composer autoloader if available.
|
||||||
|
|
@ -60,6 +60,7 @@ require_once __DIR__ . '/includes/class-s3-client-factory.php';
|
||||||
require_once __DIR__ . '/includes/class-url-rewriter.php';
|
require_once __DIR__ . '/includes/class-url-rewriter.php';
|
||||||
require_once __DIR__ . '/includes/class-media-uploader.php';
|
require_once __DIR__ . '/includes/class-media-uploader.php';
|
||||||
require_once __DIR__ . '/includes/class-admin-page.php';
|
require_once __DIR__ . '/includes/class-admin-page.php';
|
||||||
|
require_once __DIR__ . '/includes/class-subsize-filter.php';
|
||||||
require_once __DIR__ . '/includes/class-plugin.php';
|
require_once __DIR__ . '/includes/class-plugin.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -526,6 +526,11 @@ class Admin_Page {
|
||||||
$sanitized['region'] = isset( $input['region'] ) ? sanitize_text_field( $input['region'] ) : '';
|
$sanitized['region'] = isset( $input['region'] ) ? sanitize_text_field( $input['region'] ) : '';
|
||||||
$sanitized['domain'] = isset( $input['domain'] ) ? sanitize_text_field( $input['domain'] ) : '';
|
$sanitized['domain'] = isset( $input['domain'] ) ? sanitize_text_field( $input['domain'] ) : '';
|
||||||
|
|
||||||
|
// Sub-sizes mode: strict whitelist, default 'all'.
|
||||||
|
$raw_mode = isset( $input['subsizes_mode'] ) ? strtolower( (string) sanitize_text_field( $input['subsizes_mode'] ) ) : '';
|
||||||
|
$allowed_modes = array( 'all', 'thumbnail', 'none' );
|
||||||
|
$sanitized['subsizes_mode'] = in_array( $raw_mode, $allowed_modes, true ) ? $raw_mode : 'all';
|
||||||
|
|
||||||
return $sanitized;
|
return $sanitized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -571,6 +576,7 @@ class Admin_Page {
|
||||||
$bucket = $this->config->get_bucket();
|
$bucket = $this->config->get_bucket();
|
||||||
$region = $this->config->get_region();
|
$region = $this->config->get_region();
|
||||||
$domain = $this->config->get_domain();
|
$domain = $this->config->get_domain();
|
||||||
|
$subsizes_mode = $this->config->get_subsizes_mode();
|
||||||
|
|
||||||
$is_configured = $this->config->is_configured();
|
$is_configured = $this->config->is_configured();
|
||||||
|
|
||||||
|
|
@ -581,8 +587,9 @@ class Admin_Page {
|
||||||
$bucket_in_config = $this->config->is_defined_in_wp_config( 'bucket' );
|
$bucket_in_config = $this->config->is_defined_in_wp_config( 'bucket' );
|
||||||
$region_in_config = $this->config->is_defined_in_wp_config( 'region' );
|
$region_in_config = $this->config->is_defined_in_wp_config( 'region' );
|
||||||
$domain_in_config = $this->config->is_defined_in_wp_config( 'domain' );
|
$domain_in_config = $this->config->is_defined_in_wp_config( 'domain' );
|
||||||
|
$subsizes_mode_in_config = $this->config->is_defined_in_wp_config( 'subsizes_mode' );
|
||||||
|
|
||||||
$any_in_config = $host_in_config || $key_in_config || $secret_in_config || $bucket_in_config || $region_in_config || $domain_in_config;
|
$any_in_config = $host_in_config || $key_in_config || $secret_in_config || $bucket_in_config || $region_in_config || $domain_in_config || $subsizes_mode_in_config;
|
||||||
?>
|
?>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h1><?php esc_html_e( 'iDrivee2 Media Upload Settings', 'idrivee2-media-upload' ); ?></h1>
|
<h1><?php esc_html_e( 'iDrivee2 Media Upload Settings', 'idrivee2-media-upload' ); ?></h1>
|
||||||
|
|
@ -756,6 +763,54 @@ class Admin_Page {
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
<label for="idrivee2_subsizes_mode"><?php esc_html_e( 'Image Sub-sizes', 'idrivee2-media-upload' ); ?></label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<select
|
||||||
|
id="idrivee2_subsizes_mode"
|
||||||
|
name="idrivee2_media_settings[subsizes_mode]"
|
||||||
|
<?php echo $subsizes_mode_in_config ? 'disabled' : ''; ?>
|
||||||
|
>
|
||||||
|
<?php
|
||||||
|
$mode_options = array(
|
||||||
|
'all' => __( 'All registered sizes (WordPress default)', 'idrivee2-media-upload' ),
|
||||||
|
'thumbnail' => __( 'Only thumbnail (faster uploads)', 'idrivee2-media-upload' ),
|
||||||
|
'none' => __( 'No sub-sizes (original only)', 'idrivee2-media-upload' ),
|
||||||
|
);
|
||||||
|
foreach ( $mode_options as $value => $label ) {
|
||||||
|
printf(
|
||||||
|
'<option value="%s"%s>%s</option>',
|
||||||
|
esc_attr( $value ),
|
||||||
|
selected( $subsizes_mode, $value, false ),
|
||||||
|
esc_html( $label )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
<?php
|
||||||
|
// Disabled selects are not submitted with the form; preserve the
|
||||||
|
// constant-defined value via a hidden input so it is not lost on save.
|
||||||
|
if ( $subsizes_mode_in_config ) {
|
||||||
|
printf(
|
||||||
|
'<input type="hidden" name="idrivee2_media_settings[subsizes_mode]" value="%s" />',
|
||||||
|
esc_attr( $subsizes_mode )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<p class="description">
|
||||||
|
<?php
|
||||||
|
if ( $subsizes_mode_in_config ) {
|
||||||
|
esc_html_e( 'Defined in wp-config.php (IDRIVEE2_MEDIA_SUBSIZES_MODE)', 'idrivee2-media-upload' );
|
||||||
|
} else {
|
||||||
|
esc_html_e( 'Reduces work for large uploads (e.g. camera files). Also disables the -scaled derivative in thumbnail/none modes.', 'idrivee2-media-upload' );
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
@ -767,7 +822,7 @@ class Admin_Page {
|
||||||
</p>
|
</p>
|
||||||
<?php
|
<?php
|
||||||
// Show submit button only if at least one field can be edited.
|
// Show submit button only if at least one field can be edited.
|
||||||
$can_edit = ! $host_in_config || ! $key_in_config || ! $secret_in_config || ! $bucket_in_config || ! $region_in_config || ! $domain_in_config;
|
$can_edit = ! $host_in_config || ! $key_in_config || ! $secret_in_config || ! $bucket_in_config || ! $region_in_config || ! $domain_in_config || ! $subsizes_mode_in_config;
|
||||||
if ( $can_edit ) :
|
if ( $can_edit ) :
|
||||||
?>
|
?>
|
||||||
<?php submit_button( __( 'Save Settings', 'idrivee2-media-upload' ) ); ?>
|
<?php submit_button( __( 'Save Settings', 'idrivee2-media-upload' ) ); ?>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration validator and accessor for iDrivee2 constants.
|
* Configuration validator and accessor for iDrivee2 constants.
|
||||||
*
|
*
|
||||||
* Validates and provides access to the five required configuration constants
|
* Validates and provides access to the five required configuration constants
|
||||||
|
|
@ -46,6 +46,25 @@ class Config {
|
||||||
*/
|
*/
|
||||||
private const OPTION_NAME = 'idrivee2_media_settings';
|
private const OPTION_NAME = 'idrivee2_media_settings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of config keys to wp-config.php constant names.
|
||||||
|
*
|
||||||
|
* Centralised so is_defined_in_wp_config() and get_value() cannot drift.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private const CONSTANT_MAP = array(
|
||||||
|
'host' => 'IDRIVEE2_MEDIA_HOST',
|
||||||
|
'key' => 'IDRIVEE2_MEDIA_KEY',
|
||||||
|
'secret' => 'IDRIVEE2_MEDIA_SECRET',
|
||||||
|
'bucket' => 'IDRIVEE2_MEDIA_BUCKET',
|
||||||
|
'region' => 'IDRIVEE2_MEDIA_REGION',
|
||||||
|
'domain' => 'IDRIVEE2_MEDIA_DOMAIN',
|
||||||
|
'subsizes_mode' => 'IDRIVEE2_MEDIA_SUBSIZES_MODE',
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger instance.
|
* Logger instance.
|
||||||
*
|
*
|
||||||
|
|
@ -92,17 +111,8 @@ class Config {
|
||||||
* @return bool True if defined in wp-config.php, false otherwise.
|
* @return bool True if defined in wp-config.php, false otherwise.
|
||||||
*/
|
*/
|
||||||
public function is_defined_in_wp_config( string $key ): bool {
|
public function is_defined_in_wp_config( string $key ): bool {
|
||||||
$constant_map = array(
|
$constant_name = self::CONSTANT_MAP[ $key ] ?? '';
|
||||||
'host' => 'IDRIVEE2_MEDIA_HOST',
|
return '' !== $constant_name && defined( $constant_name );
|
||||||
'key' => 'IDRIVEE2_MEDIA_KEY',
|
|
||||||
'secret' => 'IDRIVEE2_MEDIA_SECRET',
|
|
||||||
'bucket' => 'IDRIVEE2_MEDIA_BUCKET',
|
|
||||||
'region' => 'IDRIVEE2_MEDIA_REGION',
|
|
||||||
'domain' => 'IDRIVEE2_MEDIA_DOMAIN',
|
|
||||||
);
|
|
||||||
|
|
||||||
$constant_name = $constant_map[ $key ] ?? '';
|
|
||||||
return $constant_name && defined( $constant_name );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -116,18 +126,9 @@ class Config {
|
||||||
* @return string The configuration value, or empty string if not set.
|
* @return string The configuration value, or empty string if not set.
|
||||||
*/
|
*/
|
||||||
private function get_value( string $key ): string {
|
private function get_value( string $key ): string {
|
||||||
$constant_map = array(
|
|
||||||
'host' => 'IDRIVEE2_MEDIA_HOST',
|
|
||||||
'key' => 'IDRIVEE2_MEDIA_KEY',
|
|
||||||
'secret' => 'IDRIVEE2_MEDIA_SECRET',
|
|
||||||
'bucket' => 'IDRIVEE2_MEDIA_BUCKET',
|
|
||||||
'region' => 'IDRIVEE2_MEDIA_REGION',
|
|
||||||
'domain' => 'IDRIVEE2_MEDIA_DOMAIN',
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check wp-config.php constant first (highest priority).
|
// Check wp-config.php constant first (highest priority).
|
||||||
$constant_name = $constant_map[ $key ] ?? '';
|
$constant_name = self::CONSTANT_MAP[ $key ] ?? '';
|
||||||
if ( $constant_name && defined( $constant_name ) ) {
|
if ( '' !== $constant_name && defined( $constant_name ) ) {
|
||||||
return (string) constant( $constant_name );
|
return (string) constant( $constant_name );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,6 +217,29 @@ class Config {
|
||||||
return ! empty( $this->get_domain() );
|
return ! empty( $this->get_domain() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configured sub-sizes mode.
|
||||||
|
*
|
||||||
|
* Controls which image sub-sizes WordPress generates. Returns one of
|
||||||
|
* 'all', 'thumbnail', or 'none'; falls back to 'all' for any value that
|
||||||
|
* is not on the whitelist (including unset/empty).
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @return string The sub-sizes mode.
|
||||||
|
*/
|
||||||
|
public function get_subsizes_mode(): string {
|
||||||
|
$value = $this->get_value( 'subsizes_mode' );
|
||||||
|
|
||||||
|
$allowed = array(
|
||||||
|
'all',
|
||||||
|
'thumbnail',
|
||||||
|
'none',
|
||||||
|
);
|
||||||
|
|
||||||
|
return in_array( $value, $allowed, true ) ? $value : 'all';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update configuration options in WordPress database.
|
* Update configuration options in WordPress database.
|
||||||
*
|
*
|
||||||
|
|
@ -239,6 +263,11 @@ class Config {
|
||||||
'domain' => isset( $data['domain'] ) ? sanitize_text_field( $data['domain'] ) : '',
|
'domain' => isset( $data['domain'] ) ? sanitize_text_field( $data['domain'] ) : '',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Sub-sizes mode: strict whitelist, default 'all'.
|
||||||
|
$raw_mode = isset( $data['subsizes_mode'] ) ? strtolower( (string) sanitize_text_field( $data['subsizes_mode'] ) ) : '';
|
||||||
|
$allowed_modes = array( 'all', 'thumbnail', 'none' );
|
||||||
|
$options['subsizes_mode'] = in_array( $raw_mode, $allowed_modes, true ) ? $raw_mode : 'all';
|
||||||
|
|
||||||
// Log configuration changes.
|
// Log configuration changes.
|
||||||
if ( $this->logger ) {
|
if ( $this->logger ) {
|
||||||
foreach ( $options as $key => $new_value ) {
|
foreach ( $options as $key => $new_value ) {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class Media_Uploader {
|
||||||
/**
|
/**
|
||||||
* Attachment IDs currently being uploaded, to prevent re-entrant calls.
|
* Attachment IDs currently being uploaded, to prevent re-entrant calls.
|
||||||
*
|
*
|
||||||
* wp_update_post() (used to update the attachment GUID) fires edit_attachment,
|
* Calling wp_update_post() to update the attachment GUID fires edit_attachment,
|
||||||
* which would re-trigger upload_attachment_to_idrivee2() causing infinite recursion.
|
* which would re-trigger upload_attachment_to_idrivee2() causing infinite recursion.
|
||||||
*
|
*
|
||||||
* @var array<int, bool>
|
* @var array<int, bool>
|
||||||
|
|
@ -145,7 +145,7 @@ class Media_Uploader {
|
||||||
$base_path = path_join( $basedir, $meta_file );
|
$base_path = path_join( $basedir, $meta_file );
|
||||||
$meta_sizes = isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ? $meta['sizes'] : array();
|
$meta_sizes = isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ? $meta['sizes'] : array();
|
||||||
|
|
||||||
/** @var array<string, string> $files */
|
/* @var array<string, string> $files */
|
||||||
$files = array( 'original' => $base_path );
|
$files = array( 'original' => $base_path );
|
||||||
foreach ( $meta_sizes as $size ) {
|
foreach ( $meta_sizes as $size ) {
|
||||||
if ( is_array( $size ) && isset( $size['file'] ) && is_string( $size['file'] ) ) {
|
if ( is_array( $size ) && isset( $size['file'] ) && is_string( $size['file'] ) ) {
|
||||||
|
|
@ -157,8 +157,8 @@ class Media_Uploader {
|
||||||
$client = $this->client_factory->create();
|
$client = $this->client_factory->create();
|
||||||
$bucket = $this->config->get_bucket();
|
$bucket = $this->config->get_bucket();
|
||||||
$commands = array();
|
$commands = array();
|
||||||
$key_map = array(); // int index -> file metadata
|
$key_map = array(); // int index -> file metadata.
|
||||||
$handles = array(); // int index -> resource
|
$handles = array(); // int index -> resource.
|
||||||
|
|
||||||
$real_basedir = realpath( $basedir );
|
$real_basedir = realpath( $basedir );
|
||||||
$idx = 0;
|
$idx = 0;
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,13 @@ class Plugin {
|
||||||
*/
|
*/
|
||||||
private $url_rewriter;
|
private $url_rewriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-size filter.
|
||||||
|
*
|
||||||
|
* @var Subsize_Filter
|
||||||
|
*/
|
||||||
|
private $subsize_filter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin file path.
|
* Plugin file path.
|
||||||
*
|
*
|
||||||
|
|
@ -110,6 +117,7 @@ class Plugin {
|
||||||
$this->admin_page = new Admin_Page( $this->config, $this->client_factory, $this->logger, $this->rate_limiter, $plugin_file );
|
$this->admin_page = new Admin_Page( $this->config, $this->client_factory, $this->logger, $this->rate_limiter, $plugin_file );
|
||||||
$this->media_uploader = new Media_Uploader( $this->config, $this->client_factory, $this->logger );
|
$this->media_uploader = new Media_Uploader( $this->config, $this->client_factory, $this->logger );
|
||||||
$this->url_rewriter = new URL_Rewriter( $this->config );
|
$this->url_rewriter = new URL_Rewriter( $this->config );
|
||||||
|
$this->subsize_filter = new Subsize_Filter( $this->config );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -145,6 +153,7 @@ class Plugin {
|
||||||
$this->admin_page->register();
|
$this->admin_page->register();
|
||||||
$this->media_uploader->register();
|
$this->media_uploader->register();
|
||||||
$this->url_rewriter->register();
|
$this->url_rewriter->register();
|
||||||
|
$this->subsize_filter->register();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
168
includes/class-subsize-filter.php
Normal file
168
includes/class-subsize-filter.php
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Sub-size filter for iDrivee2 Media Upload.
|
||||||
|
*
|
||||||
|
* @package iDrivee2Media
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace iDrivee2Media;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent direct access to this file.
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the image sub-sizes generated by WordPress.
|
||||||
|
*
|
||||||
|
* Reduces the number of sub-sizes generated per uploaded image. Affects both
|
||||||
|
* the WP 7.1 client-side path (the browser reads the registered sub-sizes via
|
||||||
|
* the REST index) and the traditional server-side path
|
||||||
|
* (intermediate_image_sizes_advanced consumes the same list).
|
||||||
|
*
|
||||||
|
* Modes:
|
||||||
|
* - 'all' Default WordPress behavior; all registered sizes are generated.
|
||||||
|
* - 'thumbnail' Only the 'thumbnail' sub-size is generated.
|
||||||
|
* - 'none' No sub-sizes are generated; only the original is kept.
|
||||||
|
*
|
||||||
|
* In 'thumbnail' and 'none' modes the big_image_size_threshold is also
|
||||||
|
* disabled, so WordPress does not create a '-scaled' derivative for images
|
||||||
|
* larger than 2560px.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
class Subsize_Filter {
|
||||||
|
/**
|
||||||
|
* Mode: generate all registered sub-sizes (default).
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public const MODE_ALL = 'all';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode: generate only the 'thumbnail' sub-size.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public const MODE_THUMBNAIL = 'thumbnail';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode: generate no sub-sizes.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public const MODE_NONE = 'none';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed mode values.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
private const ALLOWED_MODES = array(
|
||||||
|
self::MODE_ALL,
|
||||||
|
self::MODE_THUMBNAIL,
|
||||||
|
self::MODE_NONE,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration instance.
|
||||||
|
*
|
||||||
|
* @var Config
|
||||||
|
*/
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param Config $config Configuration instance.
|
||||||
|
*/
|
||||||
|
public function __construct( Config $config ) {
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the WordPress filters.
|
||||||
|
*
|
||||||
|
* Runs at priority 90 so user-supplied filters at the default priority
|
||||||
|
* (10) and at WP core's own guard priority (100) are not pre-empted.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register(): void {
|
||||||
|
add_filter( 'intermediate_image_sizes', array( $this, 'filter_intermediate_image_sizes' ), 90 );
|
||||||
|
add_filter( 'big_image_size_threshold', array( $this, 'filter_big_image_threshold' ), 90, 4 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the list of intermediate image sizes.
|
||||||
|
*
|
||||||
|
* This filter is the upstream source of truth consumed by
|
||||||
|
* wp_get_registered_image_subsizes(), which feeds both the REST index
|
||||||
|
* response (client-side path) and intermediate_image_sizes_advanced
|
||||||
|
* (server-side path).
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param array<int, string> $sizes Registered image size names.
|
||||||
|
* @return array<int, string> Filtered size names.
|
||||||
|
*/
|
||||||
|
public function filter_intermediate_image_sizes( array $sizes ): array {
|
||||||
|
$mode = $this->effective_mode();
|
||||||
|
|
||||||
|
if ( self::MODE_ALL === $mode ) {
|
||||||
|
return $sizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( self::MODE_THUMBNAIL === $mode ) {
|
||||||
|
return in_array( 'thumbnail', $sizes, true ) ? array( 'thumbnail' ) : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the big image size threshold.
|
||||||
|
*
|
||||||
|
* Returns 0 in 'thumbnail' and 'none' modes to disable the '-scaled'
|
||||||
|
* derivative that WordPress creates for images larger than the threshold.
|
||||||
|
*
|
||||||
|
* Note: WordPress fires this filter since 5.3. On older versions the
|
||||||
|
* callback is a no-op; the mode still applies via intermediate_image_sizes.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @param int $threshold Threshold in pixels. Default 2560.
|
||||||
|
* @param array<int, int> $imagesize Indexed array of width and height.
|
||||||
|
* @param string $file Path to the uploaded file.
|
||||||
|
* @param int $attachment_id Attachment post ID.
|
||||||
|
* @return int Threshold in pixels, or 0 to disable.
|
||||||
|
*/
|
||||||
|
public function filter_big_image_threshold( int $threshold, array $imagesize = array(), string $file = '', int $attachment_id = 0 ): int {
|
||||||
|
if ( self::MODE_ALL === $this->effective_mode() ) {
|
||||||
|
return $threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the effective mode, defaulting to 'all' for any value that is
|
||||||
|
* not on the whitelist.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*
|
||||||
|
* @return string One of the MODE_* constants.
|
||||||
|
*/
|
||||||
|
private function effective_mode(): string {
|
||||||
|
$mode = $this->config->get_subsizes_mode();
|
||||||
|
return in_array( $mode, self::ALLOWED_MODES, true ) ? $mode : self::MODE_ALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
languages/idrivee2-media-upload-ca.mo
Normal file
BIN
languages/idrivee2-media-upload-ca.mo
Normal file
Binary file not shown.
230
languages/idrivee2-media-upload-ca.po
Normal file
230
languages/idrivee2-media-upload-ca.po
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
# Translation of iDrivee2 Media Upload 1.3.0 in Catalan.
|
||||||
|
# Copyright (C) 2026 ROBOTSTXT
|
||||||
|
# This file is distributed under the GPL-3.0-or-later license.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: iDrivee2 Media Upload 1.3.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/idrivee2-media-upload\n"
|
||||||
|
"POT-Creation-Date: 2026-07-18T13:20:23+00:00\n"
|
||||||
|
"PO-Revision-Date: 2026-07-18T13:30:00+00:00\n"
|
||||||
|
"Last-Translator: ROBOTSTXT <hola@robotstxt.es>\n"
|
||||||
|
"Language-Team: Català <ca@li.org>\n"
|
||||||
|
"Language: ca\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: WP-CLI 2.12.0\n"
|
||||||
|
"X-Domain: idrivee2-media-upload\n"
|
||||||
|
|
||||||
|
#. Plugin Name of the plugin
|
||||||
|
msgid "iDrivee2 Media Upload"
|
||||||
|
msgstr "iDrivee2 Media Upload"
|
||||||
|
|
||||||
|
#. Plugin URI of the plugin
|
||||||
|
msgid "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload"
|
||||||
|
msgstr "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload"
|
||||||
|
|
||||||
|
#. Description of the plugin
|
||||||
|
msgid ""
|
||||||
|
"Uploads media files to iDrivee2 (S3-compatible) with enterprise-grade "
|
||||||
|
"security and logging."
|
||||||
|
msgstr ""
|
||||||
|
"Puja fitxers multimèdia a iDrivee2 (compatible amb S3) amb seguretat de "
|
||||||
|
"nivell empresarial i registre."
|
||||||
|
|
||||||
|
#. Author of the plugin
|
||||||
|
msgid "ROBOTSTXT"
|
||||||
|
msgstr "ROBOTSTXT"
|
||||||
|
|
||||||
|
#. Author URI of the plugin
|
||||||
|
msgid "https://www.robotstxt.es/"
|
||||||
|
msgstr "https://www.robotstxt.es/"
|
||||||
|
|
||||||
|
#. translators: Admin menu title.
|
||||||
|
msgid "iDrivee2 Media Upload Settings"
|
||||||
|
msgstr "Ajusts d'iDrivee2 Media Upload"
|
||||||
|
|
||||||
|
#. translators: Admin menu label.
|
||||||
|
msgid "iDrivee2"
|
||||||
|
msgstr "iDrivee2"
|
||||||
|
|
||||||
|
msgid "You do not have sufficient permissions to access this page."
|
||||||
|
msgstr "No teniu permisos suficients per a accedir a aquesta pàgina."
|
||||||
|
|
||||||
|
#. translators: %d is the number of seconds to wait.
|
||||||
|
#, php-format
|
||||||
|
msgid "Please wait %d seconds before testing again."
|
||||||
|
msgstr "Espereu %d segons abans de tornar a provar-ho."
|
||||||
|
|
||||||
|
msgid "Configuration is incomplete. Please check your settings."
|
||||||
|
msgstr "La configuració és incompleta. Comproveu els ajusts."
|
||||||
|
|
||||||
|
msgid "Connection successful! Your S3 configuration is working correctly."
|
||||||
|
msgstr "S'ha connectat correctament! La configuració S3 funciona correctament."
|
||||||
|
|
||||||
|
#. translators: %s is the error message from AWS.
|
||||||
|
#, php-format
|
||||||
|
msgid "AWS Error: %s"
|
||||||
|
msgstr "Error d'AWS: %s"
|
||||||
|
|
||||||
|
msgid "Unknown error"
|
||||||
|
msgstr "Error desconegut"
|
||||||
|
|
||||||
|
#. translators: %s is the error message.
|
||||||
|
#, php-format
|
||||||
|
msgid "Error: %s"
|
||||||
|
msgstr "Error: %s"
|
||||||
|
|
||||||
|
#. translators: %d is the number of seconds to wait.
|
||||||
|
#, php-format
|
||||||
|
msgid "Please wait %d seconds before uploading again."
|
||||||
|
msgstr "Espereu %d segons abans de tornar a pujar-ne."
|
||||||
|
|
||||||
|
msgid "File uploaded successfully!"
|
||||||
|
msgstr "S'ha pujat el fitxer correctament!"
|
||||||
|
|
||||||
|
msgid "Invalid test file name format."
|
||||||
|
msgstr "El format del nom del fitxer de prova no és vàlid."
|
||||||
|
|
||||||
|
#. translators: %s is the file name.
|
||||||
|
#, php-format
|
||||||
|
msgid "File %s deleted successfully."
|
||||||
|
msgstr "S'ha eliminat el fitxer %s correctament."
|
||||||
|
|
||||||
|
msgid "Host URL must start with \"https://\"."
|
||||||
|
msgstr "La URL de l'amfitrió ha de començar per «https://»."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Some settings are defined in wp-config.php and cannot be changed here. "
|
||||||
|
"These fields are shown as read-only."
|
||||||
|
msgstr ""
|
||||||
|
"Alguns ajusts estan definits a wp-config.php i no es poden canviar aquí. "
|
||||||
|
"Aquests camps es mostren com a només de lectura."
|
||||||
|
|
||||||
|
msgid "S3 Host"
|
||||||
|
msgstr "Amfitrió d'S3"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_HOST)"
|
||||||
|
msgstr "Definit a wp-config.php (IDRIVEE2_MEDIA_HOST)"
|
||||||
|
|
||||||
|
msgid "S3-compatible endpoint URL. Must start with \"https://\"."
|
||||||
|
msgstr "URL de l'endpoint compatible amb S3. Ha de començar per «https://»."
|
||||||
|
|
||||||
|
msgid "Access Key ID"
|
||||||
|
msgstr "ID de la clau d'accés"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_KEY)"
|
||||||
|
msgstr "Definit a wp-config.php (IDRIVEE2_MEDIA_KEY)"
|
||||||
|
|
||||||
|
msgid "Your S3 access key ID."
|
||||||
|
msgstr "L'ID de la clau d'accés d'S3."
|
||||||
|
|
||||||
|
msgid "Secret Access Key"
|
||||||
|
msgstr "Clau d'accés secreta"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_SECRET)"
|
||||||
|
msgstr "Definit a wp-config.php (IDRIVEE2_MEDIA_SECRET)"
|
||||||
|
|
||||||
|
msgid "Your S3 secret access key."
|
||||||
|
msgstr "La clau d'accés secreta d'S3."
|
||||||
|
|
||||||
|
msgid "Bucket Name"
|
||||||
|
msgstr "Nom del dipòsit"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_BUCKET)"
|
||||||
|
msgstr "Definit a wp-config.php (IDRIVEE2_MEDIA_BUCKET)"
|
||||||
|
|
||||||
|
msgid "The name of your S3 bucket."
|
||||||
|
msgstr "El nom del dipòsit S3."
|
||||||
|
|
||||||
|
msgid "Region"
|
||||||
|
msgstr "Regió"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_REGION)"
|
||||||
|
msgstr "Definit a wp-config.php (IDRIVEE2_MEDIA_REGION)"
|
||||||
|
|
||||||
|
msgid "AWS region (e.g., us-east-1, eu-west-1)."
|
||||||
|
msgstr "Regió d'AWS (per exemple, us-east-1, eu-west-1)."
|
||||||
|
|
||||||
|
msgid "Custom CDN Domain"
|
||||||
|
msgstr "Domini CDN personalitzat"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_DOMAIN)"
|
||||||
|
msgstr "Definit a wp-config.php (IDRIVEE2_MEDIA_DOMAIN)"
|
||||||
|
|
||||||
|
msgid "Optional: Custom domain for serving media files."
|
||||||
|
msgstr "Opcional: domini personalitzat per a servir fitxers multimèdia."
|
||||||
|
|
||||||
|
msgid "Image Sub-sizes"
|
||||||
|
msgstr "Submides de la imatge"
|
||||||
|
|
||||||
|
msgid "All registered sizes (WordPress default)"
|
||||||
|
msgstr "Totes les mides registrades (predeterminat del WordPress)"
|
||||||
|
|
||||||
|
msgid "Only thumbnail (faster uploads)"
|
||||||
|
msgstr "Només la miniatura (pujades més ràpides)"
|
||||||
|
|
||||||
|
msgid "No sub-sizes (original only)"
|
||||||
|
msgstr "Sense submides (només l'original)"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_SUBSIZES_MODE)"
|
||||||
|
msgstr "Definit a wp-config.php (IDRIVEE2_MEDIA_SUBSIZES_MODE)"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Reduces work for large uploads (e.g. camera files). Also disables the -"
|
||||||
|
"scaled derivative in thumbnail/none modes."
|
||||||
|
msgstr ""
|
||||||
|
"Redueix la feina per a pujades grans (per exemple, fitxers de càmera). "
|
||||||
|
"També desactiva la derivada -scaled en els modes miniatura i cap."
|
||||||
|
|
||||||
|
msgid "Save Settings"
|
||||||
|
msgstr "Desa els ajusts"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"To modify settings defined in wp-config.php, please edit your wp-config.php "
|
||||||
|
"file directly."
|
||||||
|
msgstr ""
|
||||||
|
"Per a modificar els ajusts definits a wp-config.php, editeu directament "
|
||||||
|
"el fitxer wp-config.php."
|
||||||
|
|
||||||
|
msgid "Test Connection"
|
||||||
|
msgstr "Prova la connexió"
|
||||||
|
|
||||||
|
msgid "Test your S3 configuration to ensure everything is working correctly."
|
||||||
|
msgstr "Proveu la configuració S3 per a assegurar-vos que tot funciona correctament."
|
||||||
|
|
||||||
|
#. translators: %s is the file name.
|
||||||
|
#, php-format
|
||||||
|
msgid "File: %s"
|
||||||
|
msgstr "Fitxer: %s"
|
||||||
|
|
||||||
|
msgid "URL:"
|
||||||
|
msgstr "URL:"
|
||||||
|
|
||||||
|
msgid "Delete this file"
|
||||||
|
msgstr "Elimina aquest fitxer"
|
||||||
|
|
||||||
|
msgid "Test S3 Connection"
|
||||||
|
msgstr "Prova la connexió S3"
|
||||||
|
|
||||||
|
msgid "Verify that your S3 bucket is accessible with the configured credentials."
|
||||||
|
msgstr "Verifiqueu que el dipòsit S3 és accessible amb les credencials configurades."
|
||||||
|
|
||||||
|
msgid "Upload Test File"
|
||||||
|
msgstr "Puja un fitxer de prova"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Upload a test file to S3 with a timestamped name (test-YYYYMMDDHHMMSS.txt). "
|
||||||
|
"The file will remain in S3 until manually deleted."
|
||||||
|
msgstr ""
|
||||||
|
"Puja un fitxer de prova a S3 amb un nom amb marca temporal "
|
||||||
|
"(test-YYYYMMDDHHMMSS.txt). El fitxer romandrà a S3 fins que s'elimini "
|
||||||
|
"manualment."
|
||||||
|
|
||||||
|
msgid "Every 5 Minutes"
|
||||||
|
msgstr "Cada 5 minuts"
|
||||||
|
|
||||||
|
msgid "Security check failed"
|
||||||
|
msgstr "Ha fallat la comprovació de seguretat"
|
||||||
BIN
languages/idrivee2-media-upload-es_ES.mo
Normal file
BIN
languages/idrivee2-media-upload-es_ES.mo
Normal file
Binary file not shown.
230
languages/idrivee2-media-upload-es_ES.po
Normal file
230
languages/idrivee2-media-upload-es_ES.po
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
# Translation of iDrivee2 Media Upload 1.3.0 in Spanish (Spain).
|
||||||
|
# Copyright (C) 2026 ROBOTSTXT
|
||||||
|
# This file is distributed under the GPL-3.0-or-later license.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: iDrivee2 Media Upload 1.3.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/idrivee2-media-upload\n"
|
||||||
|
"POT-Creation-Date: 2026-07-18T13:20:23+00:00\n"
|
||||||
|
"PO-Revision-Date: 2026-07-18T13:30:00+00:00\n"
|
||||||
|
"Last-Translator: ROBOTSTXT <hola@robotstxt.es>\n"
|
||||||
|
"Language-Team: Español (España) <es@li.org>\n"
|
||||||
|
"Language: es_ES\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: WP-CLI 2.12.0\n"
|
||||||
|
"X-Domain: idrivee2-media-upload\n"
|
||||||
|
|
||||||
|
#. Plugin Name of the plugin
|
||||||
|
msgid "iDrivee2 Media Upload"
|
||||||
|
msgstr "iDrivee2 Media Upload"
|
||||||
|
|
||||||
|
#. Plugin URI of the plugin
|
||||||
|
msgid "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload"
|
||||||
|
msgstr "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload"
|
||||||
|
|
||||||
|
#. Description of the plugin
|
||||||
|
msgid ""
|
||||||
|
"Uploads media files to iDrivee2 (S3-compatible) with enterprise-grade "
|
||||||
|
"security and logging."
|
||||||
|
msgstr ""
|
||||||
|
"Sube archivos multimedia a iDrivee2 (compatible con S3) con seguridad de "
|
||||||
|
"nivel empresarial y registro."
|
||||||
|
|
||||||
|
#. Author of the plugin
|
||||||
|
msgid "ROBOTSTXT"
|
||||||
|
msgstr "ROBOTSTXT"
|
||||||
|
|
||||||
|
#. Author URI of the plugin
|
||||||
|
msgid "https://www.robotstxt.es/"
|
||||||
|
msgstr "https://www.robotstxt.es/"
|
||||||
|
|
||||||
|
#. translators: Admin menu title.
|
||||||
|
msgid "iDrivee2 Media Upload Settings"
|
||||||
|
msgstr "Ajustes de iDrivee2 Media Upload"
|
||||||
|
|
||||||
|
#. translators: Admin menu label.
|
||||||
|
msgid "iDrivee2"
|
||||||
|
msgstr "iDrivee2"
|
||||||
|
|
||||||
|
msgid "You do not have sufficient permissions to access this page."
|
||||||
|
msgstr "No tienes permisos suficientes para acceder a esta página."
|
||||||
|
|
||||||
|
#. translators: %d is the number of seconds to wait.
|
||||||
|
#, php-format
|
||||||
|
msgid "Please wait %d seconds before testing again."
|
||||||
|
msgstr "Espera %d segundos antes de volver a probar."
|
||||||
|
|
||||||
|
msgid "Configuration is incomplete. Please check your settings."
|
||||||
|
msgstr "La configuración está incompleta. Revisa tus ajustes."
|
||||||
|
|
||||||
|
msgid "Connection successful! Your S3 configuration is working correctly."
|
||||||
|
msgstr "¡Conexión correcta! Tu configuración S3 funciona correctamente."
|
||||||
|
|
||||||
|
#. translators: %s is the error message from AWS.
|
||||||
|
#, php-format
|
||||||
|
msgid "AWS Error: %s"
|
||||||
|
msgstr "Error de AWS: %s"
|
||||||
|
|
||||||
|
msgid "Unknown error"
|
||||||
|
msgstr "Error desconocido"
|
||||||
|
|
||||||
|
#. translators: %s is the error message.
|
||||||
|
#, php-format
|
||||||
|
msgid "Error: %s"
|
||||||
|
msgstr "Error: %s"
|
||||||
|
|
||||||
|
#. translators: %d is the number of seconds to wait.
|
||||||
|
#, php-format
|
||||||
|
msgid "Please wait %d seconds before uploading again."
|
||||||
|
msgstr "Espera %d segundos antes de volver a subir."
|
||||||
|
|
||||||
|
msgid "File uploaded successfully!"
|
||||||
|
msgstr "¡Archivo subido correctamente!"
|
||||||
|
|
||||||
|
msgid "Invalid test file name format."
|
||||||
|
msgstr "Formato de nombre de archivo de prueba no válido."
|
||||||
|
|
||||||
|
#. translators: %s is the file name.
|
||||||
|
#, php-format
|
||||||
|
msgid "File %s deleted successfully."
|
||||||
|
msgstr "Archivo %s eliminado correctamente."
|
||||||
|
|
||||||
|
msgid "Host URL must start with \"https://\"."
|
||||||
|
msgstr "La URL del host debe empezar por «https://»."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Some settings are defined in wp-config.php and cannot be changed here. "
|
||||||
|
"These fields are shown as read-only."
|
||||||
|
msgstr ""
|
||||||
|
"Algunos ajustes están definidos en wp-config.php y no se pueden cambiar "
|
||||||
|
"aquí. Estos campos se muestran como solo lectura."
|
||||||
|
|
||||||
|
msgid "S3 Host"
|
||||||
|
msgstr "Host de S3"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_HOST)"
|
||||||
|
msgstr "Definido en wp-config.php (IDRIVEE2_MEDIA_HOST)"
|
||||||
|
|
||||||
|
msgid "S3-compatible endpoint URL. Must start with \"https://\"."
|
||||||
|
msgstr "URL del endpoint compatible con S3. Debe empezar por «https://»."
|
||||||
|
|
||||||
|
msgid "Access Key ID"
|
||||||
|
msgstr "ID de la clave de acceso"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_KEY)"
|
||||||
|
msgstr "Definido en wp-config.php (IDRIVEE2_MEDIA_KEY)"
|
||||||
|
|
||||||
|
msgid "Your S3 access key ID."
|
||||||
|
msgstr "El ID de tu clave de acceso de S3."
|
||||||
|
|
||||||
|
msgid "Secret Access Key"
|
||||||
|
msgstr "Clave de acceso secreta"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_SECRET)"
|
||||||
|
msgstr "Definido en wp-config.php (IDRIVEE2_MEDIA_SECRET)"
|
||||||
|
|
||||||
|
msgid "Your S3 secret access key."
|
||||||
|
msgstr "Tu clave de acceso secreta de S3."
|
||||||
|
|
||||||
|
msgid "Bucket Name"
|
||||||
|
msgstr "Nombre del depósito"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_BUCKET)"
|
||||||
|
msgstr "Definido en wp-config.php (IDRIVEE2_MEDIA_BUCKET)"
|
||||||
|
|
||||||
|
msgid "The name of your S3 bucket."
|
||||||
|
msgstr "El nombre de tu depósito S3."
|
||||||
|
|
||||||
|
msgid "Region"
|
||||||
|
msgstr "Región"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_REGION)"
|
||||||
|
msgstr "Definido en wp-config.php (IDRIVEE2_MEDIA_REGION)"
|
||||||
|
|
||||||
|
msgid "AWS region (e.g., us-east-1, eu-west-1)."
|
||||||
|
msgstr "Región de AWS (por ejemplo, us-east-1, eu-west-1)."
|
||||||
|
|
||||||
|
msgid "Custom CDN Domain"
|
||||||
|
msgstr "Dominio CDN personalizado"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_DOMAIN)"
|
||||||
|
msgstr "Definido en wp-config.php (IDRIVEE2_MEDIA_DOMAIN)"
|
||||||
|
|
||||||
|
msgid "Optional: Custom domain for serving media files."
|
||||||
|
msgstr "Opcional: dominio personalizado para servir archivos multimedia."
|
||||||
|
|
||||||
|
msgid "Image Sub-sizes"
|
||||||
|
msgstr "Subtamaños de imagen"
|
||||||
|
|
||||||
|
msgid "All registered sizes (WordPress default)"
|
||||||
|
msgstr "Todos los tamaños registrados (predeterminado de WordPress)"
|
||||||
|
|
||||||
|
msgid "Only thumbnail (faster uploads)"
|
||||||
|
msgstr "Solo miniatura (subidas más rápidas)"
|
||||||
|
|
||||||
|
msgid "No sub-sizes (original only)"
|
||||||
|
msgstr "Sin subtamaños (solo el original)"
|
||||||
|
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_SUBSIZES_MODE)"
|
||||||
|
msgstr "Definido en wp-config.php (IDRIVEE2_MEDIA_SUBSIZES_MODE)"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Reduces work for large uploads (e.g. camera files). Also disables the -"
|
||||||
|
"scaled derivative in thumbnail/none modes."
|
||||||
|
msgstr ""
|
||||||
|
"Reduce el trabajo en subidas grandes (por ejemplo, archivos de cámara). "
|
||||||
|
"También desactiva la derivada -scaled en los modos miniatura y ninguno."
|
||||||
|
|
||||||
|
msgid "Save Settings"
|
||||||
|
msgstr "Guardar ajustes"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"To modify settings defined in wp-config.php, please edit your wp-config.php "
|
||||||
|
"file directly."
|
||||||
|
msgstr ""
|
||||||
|
"Para modificar los ajustes definidos en wp-config.php, edita el archivo "
|
||||||
|
"wp-config.php directamente."
|
||||||
|
|
||||||
|
msgid "Test Connection"
|
||||||
|
msgstr "Probar conexión"
|
||||||
|
|
||||||
|
msgid "Test your S3 configuration to ensure everything is working correctly."
|
||||||
|
msgstr "Prueba tu configuración S3 para asegurarte de que todo funciona correctamente."
|
||||||
|
|
||||||
|
#. translators: %s is the file name.
|
||||||
|
#, php-format
|
||||||
|
msgid "File: %s"
|
||||||
|
msgstr "Archivo: %s"
|
||||||
|
|
||||||
|
msgid "URL:"
|
||||||
|
msgstr "URL:"
|
||||||
|
|
||||||
|
msgid "Delete this file"
|
||||||
|
msgstr "Eliminar este archivo"
|
||||||
|
|
||||||
|
msgid "Test S3 Connection"
|
||||||
|
msgstr "Probar conexión S3"
|
||||||
|
|
||||||
|
msgid "Verify that your S3 bucket is accessible with the configured credentials."
|
||||||
|
msgstr "Verifica que se puede acceder a tu depósito S3 con las credenciales configuradas."
|
||||||
|
|
||||||
|
msgid "Upload Test File"
|
||||||
|
msgstr "Subir archivo de prueba"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Upload a test file to S3 with a timestamped name (test-YYYYMMDDHHMMSS.txt). "
|
||||||
|
"The file will remain in S3 until manually deleted."
|
||||||
|
msgstr ""
|
||||||
|
"Sube un archivo de prueba a S3 con un nombre con marca temporal "
|
||||||
|
"(test-YYYYMMDDHHMMSS.txt). El archivo permanecerá en S3 hasta que se "
|
||||||
|
"elimine manualmente."
|
||||||
|
|
||||||
|
msgid "Every 5 Minutes"
|
||||||
|
msgstr "Cada 5 minutos"
|
||||||
|
|
||||||
|
msgid "Security check failed"
|
||||||
|
msgstr "Ha fallado la comprobación de seguridad"
|
||||||
|
|
@ -1,120 +1,275 @@
|
||||||
#, fuzzy
|
# Copyright (C) 2026 ROBOTSTXT
|
||||||
|
# This file is distributed under the GPL-3.0-or-later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: iDrivee2 Media Upload\n"
|
"Project-Id-Version: iDrivee2 Media Upload 1.3.0\n"
|
||||||
"POT-Creation-Date: 2025-08-06 15:40+0200\n"
|
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/idrivee2-media-upload\n"
|
||||||
"PO-Revision-Date: 2025-08-06 15:40+0200\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Last-Translator: \n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"Language-Team: \n"
|
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
"POT-Creation-Date: 2026-07-18T13:20:23+00:00\n"
|
||||||
"X-Generator: Poedit 3.6\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"X-Poedit-Basepath: ..\n"
|
"X-Generator: WP-CLI 2.12.0\n"
|
||||||
"X-Poedit-Flags-xgettext: --add-comments=translators:\n"
|
"X-Domain: idrivee2-media-upload\n"
|
||||||
"X-Poedit-WPHeader: idrivee2-media-upload.php\n"
|
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
|
||||||
"X-Poedit-KeywordsList: "
|
|
||||||
"__;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
|
|
||||||
"X-Poedit-SearchPath-0: .\n"
|
|
||||||
"X-Poedit-SearchPathExcluded-0: *.min.js\n"
|
|
||||||
"X-Poedit-SearchPathExcluded-1: vendor\n"
|
|
||||||
|
|
||||||
#. translators: Admin menu title.
|
#. Plugin Name of the plugin
|
||||||
#. translators: Admin menu label.
|
#: idrivee2-media-upload.php
|
||||||
#: idrivee2-media-upload.php:77 idrivee2-media-upload.php:79
|
|
||||||
msgid "iDrivee2"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:122 idrivee2-media-upload.php:524
|
|
||||||
msgid "Test S3 Connection"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:123
|
|
||||||
msgid "Testing..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:124 idrivee2-media-upload.php:529
|
|
||||||
msgid "Upload Test File"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:125
|
|
||||||
msgid "Uploading..."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. translators: %s is the name of the missing constant.
|
|
||||||
#: idrivee2-media-upload.php:162
|
|
||||||
#, php-format
|
|
||||||
msgid "Missing constant %s."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:184
|
|
||||||
msgid "Connection successful."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. translators: Error message when no file is provided.
|
|
||||||
#: idrivee2-media-upload.php:215
|
|
||||||
msgid "No file provided."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:472
|
|
||||||
msgid "iDrivee2 Media Upload Settings"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:476
|
|
||||||
msgid ""
|
|
||||||
"To use iDrivee2 Media Upload, please add these constants to wp-config.php:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:477
|
|
||||||
msgid "HOST must begin with \"https://\"."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:489
|
|
||||||
msgid "Host"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:493
|
|
||||||
msgid "Access Key"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:497
|
|
||||||
msgid "Secret Key"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:501
|
|
||||||
msgid "Bucket"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:505
|
|
||||||
msgid "Region"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:509
|
|
||||||
msgid "Domain"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: idrivee2-media-upload.php:515
|
|
||||||
msgid "Not defined"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. Plugin Name of the plugin/theme
|
|
||||||
msgid "iDrivee2 Media Upload"
|
msgid "iDrivee2 Media Upload"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Plugin URI of the plugin/theme
|
#. Plugin URI of the plugin
|
||||||
|
#: idrivee2-media-upload.php
|
||||||
msgid "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload"
|
msgid "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Description of the plugin/theme
|
#. Description of the plugin
|
||||||
msgid "Uploads media files to iDrivee2 (S3-compatible)."
|
#: idrivee2-media-upload.php
|
||||||
|
msgid "Uploads media files to iDrivee2 (S3-compatible) with enterprise-grade security and logging."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Author of the plugin/theme
|
#. Author of the plugin
|
||||||
|
#: idrivee2-media-upload.php
|
||||||
msgid "ROBOTSTXT"
|
msgid "ROBOTSTXT"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Author URI of the plugin/theme
|
#. Author URI of the plugin
|
||||||
|
#: idrivee2-media-upload.php
|
||||||
msgid "https://www.robotstxt.es/"
|
msgid "https://www.robotstxt.es/"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: Admin menu title.
|
||||||
|
#: includes/class-admin-page.php:109
|
||||||
|
#: includes/class-admin-page.php:595
|
||||||
|
msgid "iDrivee2 Media Upload Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: Admin menu label.
|
||||||
|
#: includes/class-admin-page.php:111
|
||||||
|
msgid "iDrivee2"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:156
|
||||||
|
#: includes/class-admin-page.php:168
|
||||||
|
#: includes/class-admin-page.php:180
|
||||||
|
#: robotstxt-updater.php:394
|
||||||
|
msgid "You do not have sufficient permissions to access this page."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: %d is the number of seconds to wait.
|
||||||
|
#: includes/class-admin-page.php:206
|
||||||
|
#, php-format
|
||||||
|
msgid "Please wait %d seconds before testing again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:222
|
||||||
|
msgid "Configuration is incomplete. Please check your settings."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:243
|
||||||
|
msgid "Connection successful! Your S3 configuration is working correctly."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: %s is the error message from AWS.
|
||||||
|
#: includes/class-admin-page.php:256
|
||||||
|
#: includes/class-admin-page.php:361
|
||||||
|
#: includes/class-admin-page.php:471
|
||||||
|
#, php-format
|
||||||
|
msgid "AWS Error: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:257
|
||||||
|
#: includes/class-admin-page.php:362
|
||||||
|
#: includes/class-admin-page.php:472
|
||||||
|
msgid "Unknown error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: %s is the error message.
|
||||||
|
#: includes/class-admin-page.php:271
|
||||||
|
#: includes/class-admin-page.php:376
|
||||||
|
#: includes/class-admin-page.php:486
|
||||||
|
#, php-format
|
||||||
|
msgid "Error: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: %d is the number of seconds to wait.
|
||||||
|
#: includes/class-admin-page.php:300
|
||||||
|
#, php-format
|
||||||
|
msgid "Please wait %d seconds before uploading again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:345
|
||||||
|
msgid "File uploaded successfully!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:426
|
||||||
|
msgid "Invalid test file name format."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: %s is the file name.
|
||||||
|
#: includes/class-admin-page.php:455
|
||||||
|
#, php-format
|
||||||
|
msgid "File %s deleted successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:517
|
||||||
|
msgid "Host URL must start with \"https://\"."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:601
|
||||||
|
msgid "Some settings are defined in wp-config.php and cannot be changed here. These fields are shown as read-only."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:612
|
||||||
|
msgid "S3 Host"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:627
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_HOST)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:629
|
||||||
|
msgid "S3-compatible endpoint URL. Must start with \"https://\"."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:638
|
||||||
|
msgid "Access Key ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:653
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_KEY)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:655
|
||||||
|
msgid "Your S3 access key ID."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:664
|
||||||
|
msgid "Secret Access Key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:679
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_SECRET)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:681
|
||||||
|
msgid "Your S3 secret access key."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:690
|
||||||
|
msgid "Bucket Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:705
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_BUCKET)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:707
|
||||||
|
msgid "The name of your S3 bucket."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:716
|
||||||
|
msgid "Region"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:732
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_REGION)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:734
|
||||||
|
msgid "AWS region (e.g., us-east-1, eu-west-1)."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:743
|
||||||
|
msgid "Custom CDN Domain"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:758
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_DOMAIN)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:760
|
||||||
|
msgid "Optional: Custom domain for serving media files."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:769
|
||||||
|
msgid "Image Sub-sizes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:779
|
||||||
|
msgid "All registered sizes (WordPress default)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:780
|
||||||
|
msgid "Only thumbnail (faster uploads)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:781
|
||||||
|
msgid "No sub-sizes (original only)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:806
|
||||||
|
msgid "Defined in wp-config.php (IDRIVEE2_MEDIA_SUBSIZES_MODE)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:808
|
||||||
|
msgid "Reduces work for large uploads (e.g. camera files). Also disables the -scaled derivative in thumbnail/none modes."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:818
|
||||||
|
#: includes/class-admin-page.php:828
|
||||||
|
msgid "Save Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:821
|
||||||
|
msgid "To modify settings defined in wp-config.php, please edit your wp-config.php file directly."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:836
|
||||||
|
#: includes/class-admin-page.php:887
|
||||||
|
msgid "Test Connection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:838
|
||||||
|
msgid "Test your S3 configuration to ensure everything is working correctly."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. translators: %s is the file name.
|
||||||
|
#: includes/class-admin-page.php:860
|
||||||
|
#, php-format
|
||||||
|
msgid "File: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:866
|
||||||
|
msgid "URL:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:875
|
||||||
|
msgid "Delete this file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:892
|
||||||
|
msgid "Test S3 Connection"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:895
|
||||||
|
msgid "Verify that your S3 bucket is accessible with the configured credentials."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:901
|
||||||
|
#: includes/class-admin-page.php:906
|
||||||
|
msgid "Upload Test File"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-admin-page.php:909
|
||||||
|
msgid "Upload a test file to S3 with a timestamped name (test-YYYYMMDDHHMMSS.txt). The file will remain in S3 until manually deleted."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-plugin.php:170
|
||||||
|
msgid "Every 5 Minutes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: robotstxt-updater.php:389
|
||||||
|
msgid "Security check failed"
|
||||||
|
msgstr ""
|
||||||
|
|
|
||||||
39
readme.txt
39
readme.txt
|
|
@ -3,9 +3,9 @@ Contributors: robotstxt, javiercasares
|
||||||
Tags: media, upload, s3, cdn, storage, idrivee2, cloud
|
Tags: media, upload, s3, cdn, storage, idrivee2, cloud
|
||||||
Requires at least: 4.1
|
Requires at least: 4.1
|
||||||
Tested up to: 7.1
|
Tested up to: 7.1
|
||||||
Stable tag: 1.2.1
|
Stable tag: 1.3.0
|
||||||
Requires PHP: 8.1
|
Requires PHP: 8.1
|
||||||
Version: 1.2.1
|
Version: 1.3.0
|
||||||
License: GPL-3.0-or-later
|
License: GPL-3.0-or-later
|
||||||
License URI: https://www.gnu.org/licenses/gpl-3.0.txt
|
License URI: https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
|
||||||
|
|
@ -199,6 +199,41 @@ PHP 8.2 or higher is required. The plugin uses strict type declarations and is t
|
||||||
|
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.3.0 =
|
||||||
|
|
||||||
|
_Release date: 2026-07-18_
|
||||||
|
|
||||||
|
**Highlights**
|
||||||
|
|
||||||
|
* New image sub-sizes control — ship less data per upload, ideal for large camera files. Reduces work in both the WP 7.1 client-side path (browser) and the traditional server-side path.
|
||||||
|
|
||||||
|
**Added**
|
||||||
|
|
||||||
|
* Settings field **Image Sub-sizes** under Settings → iDrivee2 with three modes: `all` (default), `thumbnail` (only the default thumbnail), `none` (no sub-sizes).
|
||||||
|
* `IDRIVEE2_MEDIA_SUBSIZES_MODE` wp-config.php constant — same priority pattern as the S3 credentials; the field becomes read-only when set.
|
||||||
|
* In `thumbnail` and `none` modes the `big_image_size_threshold` filter is also disabled, so WordPress no longer creates a `-scaled` derivative for images larger than 2560px. (Requires WordPress 5.3+ for the scaled-disabling; on older WP the mode still applies via `intermediate_image_sizes`.)
|
||||||
|
|
||||||
|
**Security**
|
||||||
|
|
||||||
|
* `composer audit` CVEs resolved: `guzzlehttp/guzzle` 7.11 → 7.15, `guzzlehttp/psr7` 2.11 → 2.13, `mtdowling/jmespath.php` 2.8 → 2.9 (CVE-2026-55767 / 55568 / 55766 / 54133).
|
||||||
|
|
||||||
|
**Tooling**
|
||||||
|
|
||||||
|
* `bin/preflight.sh` added — automated pre-deploy verification per AGENTS-testing-build-deployment.md (PHPCS, PHPStan, PHPCompatibility, PHPUnit + coverage, composer audit, candidate ZIP inspection).
|
||||||
|
* `.claude/settings.json` added — mechanical deny rules for `deploy.sh`, `git push/tag/merge` per AGENTS.md.
|
||||||
|
|
||||||
|
**Compatibility**
|
||||||
|
|
||||||
|
* WordPress: 4.1 - 7.1
|
||||||
|
* PHP: 8.1 - 8.5
|
||||||
|
|
||||||
|
**Tests**
|
||||||
|
|
||||||
|
* PHP Coding Standards: 3.13.5 (0 errors)
|
||||||
|
* WordPress Coding Standards: 3.3.0 (0 violations)
|
||||||
|
* PHPStan: Level 9, 0 errors
|
||||||
|
* PHPUnit: 38 tests, 60 assertions
|
||||||
|
|
||||||
= 1.2.1 =
|
= 1.2.1 =
|
||||||
|
|
||||||
_Release date: 2026-06-05_
|
_Release date: 2026-06-05_
|
||||||
|
|
|
||||||
10
update.json
10
update.json
|
|
@ -1,20 +1,20 @@
|
||||||
{
|
{
|
||||||
"name": "iDrivee2 Media Upload",
|
"name": "iDrivee2 Media Upload",
|
||||||
"slug": "idrivee2-media-upload",
|
"slug": "idrivee2-media-upload",
|
||||||
"version": "1.2.1",
|
"version": "1.3.0",
|
||||||
"download_url": "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload/releases/download/1.2.1/idrivee2-media-upload-1.2.1.zip",
|
"download_url": "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload/releases/download/1.3.0/idrivee2-media-upload-1.3.0.zip",
|
||||||
"requires": "4.1",
|
"requires": "4.1",
|
||||||
"requires_php": "8.1",
|
"requires_php": "8.1",
|
||||||
"tested": "7.1",
|
"tested": "7.1",
|
||||||
"last_updated": "2026-06-05",
|
"last_updated": "2026-07-18",
|
||||||
"author": "ROBOTSTXT",
|
"author": "ROBOTSTXT",
|
||||||
"author_profile": "https://www.robotstxt.es/",
|
"author_profile": "https://www.robotstxt.es/",
|
||||||
"homepage": "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload",
|
"homepage": "https://git.robotstxt.es/ROBOTSTXT/idrivee2-media-upload",
|
||||||
"description": "Uploads media files to iDrivee2 (S3-compatible) with enterprise-grade security and logging.",
|
"description": "Uploads media files to iDrivee2 (S3-compatible) with enterprise-grade security and logging.",
|
||||||
"changelog": "<h3>1.2.1 - 2026-06-05</h3><ul><li><strong>Fixed:</strong> Infinite recursion on image upload — wp_update_post() fired edit_attachment which re-triggered the upload method. Removed edit_attachment hook, added re-entry guard.</li><li><strong>Fixed:</strong> Fatal TypeError: fclose() on already-closed stream — AWS SDK closes streams after upload; added is_resource() check before fclose().</li></ul><h3>1.2.0 - 2026-06-02</h3><ul><li><strong>Performance:</strong> Concurrent S3 uploads via AWS CommandPool (default 5, tunable via IDRIVEE2_UPLOAD_CONCURRENCY)</li><li><strong>Performance:</strong> Stream files directly from disk — no full load into memory</li><li><strong>Performance:</strong> Removed per-file headObject pre-check — single batch DB write for stats</li><li><strong>Changed:</strong> Hook priority lowered from 999 to 10</li></ul><h3>1.1.4 - 2026-06-02</h3><ul><li><strong>Added:</strong> Full dev tooling: PHPCS, PHPStan level 9, PHPUnit test suite (22 tests)</li><li><strong>Fixed:</strong> WP_Filesystem null guard, type safety on get_option/get_transient, dynamic asset version</li><li><strong>Changed:</strong> Tested up to WordPress 7.1, PHP 8.1-8.5, Requires at least 4.1</li></ul><h3>1.1.3 - 2026-02-04</h3><ul><li><strong>Fixed:</strong> Critical namespace issue with Robotstxt_Updater class causing fatal error</li><li><strong>Fixed:</strong> Plugin now loads correctly without PHP fatal errors</li></ul><h3>1.1.2 - 2026-02-04</h3><ul><li><strong>Changed:</strong> Deployment script updated to use PHP 8.2 as platform base for production builds</li><li><strong>Changed:</strong> Now uses composer update --no-dev for consistent dependency resolution</li><li><strong>Improved:</strong> Production packages guarantee PHP 8.2+ compatibility regardless of dev environment</li></ul><h3>1.1.1 - 2026-02-04</h3><ul><li><strong>Fixed:</strong> Deployment script now includes essential files (update.json, robotstxt-updater.php, readme.txt, changelog.txt)</li><li><strong>Improved:</strong> Production packages now contain all files required for automatic updates from Gitea</li></ul><h3>1.1.0 - 2026-02-04</h3><ul><li><strong>Changed:</strong> Added explicit PHP version requirement (>=8.2) to composer.json</li><li><strong>Changed:</strong> Updated update.json with correct plugin information</li><li><strong>Changed:</strong> Fixed Text Domain in robotstxt-updater.php to match plugin slug</li><li><strong>Fixed:</strong> Composer now validates PHP version during dependency installation</li><li><strong>Fixed:</strong> Plugin update system correctly identifies the plugin</li><li><strong>Fixed:</strong> Translations properly loaded for updater error messages</li><li><strong>Improved:</strong> All text domains now consistently use 'idrivee2-media-upload'</li></ul><h3>1.0.0 - 2026-02-03</h3><ul><li><strong>Release:</strong> First stable release</li><li><strong>Feature:</strong> Automatic upload of media files to iDrivee2 (S3-compatible storage)</li><li><strong>Feature:</strong> URL rewriting to serve media from CDN</li><li><strong>Feature:</strong> Local file deletion after successful upload</li><li><strong>Feature:</strong> Admin interface with connection and upload testing</li><li><strong>Security:</strong> Enterprise-grade security with nonce validation</li><li><strong>Architecture:</strong> Class-based modular architecture with dependency injection</li><li><strong>Testing:</strong> PHPUnit test structure and PHPStan static analysis</li><li><strong>Compatibility:</strong> WordPress 6.8+ and PHP 8.2+</li></ul>",
|
"changelog": "<h3>1.3.0 - 2026-07-18</h3><ul><li><strong>Added:</strong> Image sub-sizes control under Settings → iDrivee2 — three modes: all (default), thumbnail, none.</li><li><strong>Added:</strong> IDRIVEE2_MEDIA_SUBSIZES_MODE wp-config.php constant; UI field becomes read-only when set.</li><li><strong>Added:</strong> In thumbnail/none modes, the -scaled derivative (big_image_size_threshold) is also disabled.</li><li><strong>Security:</strong> composer audit CVEs resolved — guzzlehttp/guzzle 7.11→7.15, guzzlehttp/psr7 2.11→2.13, mtdowling/jmespath.php 2.8→2.9 (CVE-2026-55767/55568/55766/54133).</li></ul><h3>1.2.1 - 2026-06-05</h3><ul><li><strong>Fixed:</strong> Infinite recursion on image upload — wp_update_post() fired edit_attachment which re-triggered the upload method. Removed edit_attachment hook, added re-entry guard.</li><li><strong>Fixed:</strong> Fatal TypeError: fclose() on already-closed stream — AWS SDK closes streams after upload; added is_resource() check before fclose().</li></ul><h3>1.2.0 - 2026-06-02</h3><ul><li><strong>Performance:</strong> Concurrent S3 uploads via AWS CommandPool (default 5, tunable via IDRIVEE2_UPLOAD_CONCURRENCY)</li><li><strong>Performance:</strong> Stream files directly from disk — no full load into memory</li><li><strong>Performance:</strong> Removed per-file headObject pre-check — single batch DB write for stats</li><li><strong>Changed:</strong> Hook priority lowered from 999 to 10</li></ul><h3>1.1.4 - 2026-06-02</h3><ul><li><strong>Added:</strong> Full dev tooling: PHPCS, PHPStan level 9, PHPUnit test suite (22 tests)</li><li><strong>Fixed:</strong> WP_Filesystem null guard, type safety on get_option/get_transient, dynamic asset version</li><li><strong>Changed:</strong> Tested up to WordPress 7.1, PHP 8.1-8.5, Requires at least 4.1</li></ul><h3>1.1.3 - 2026-02-04</h3><ul><li><strong>Fixed:</strong> Critical namespace issue with Robotstxt_Updater class causing fatal error</li><li><strong>Fixed:</strong> Plugin now loads correctly without PHP fatal errors</li></ul><h3>1.1.2 - 2026-02-04</h3><ul><li><strong>Changed:</strong> Deployment script updated to use PHP 8.2 as platform base for production builds</li><li><strong>Changed:</strong> Now uses composer update --no-dev for consistent dependency resolution</li><li><strong>Improved:</strong> Production packages guarantee PHP 8.2+ compatibility regardless of dev environment</li></ul><h3>1.1.1 - 2026-02-04</h3><ul><li><strong>Fixed:</strong> Deployment script now includes essential files (update.json, robotstxt-updater.php, readme.txt, changelog.txt)</li><li><strong>Improved:</strong> Production packages now contain all files required for automatic updates from Gitea</li></ul><h3>1.1.0 - 2026-02-04</h3><ul><li><strong>Changed:</strong> Added explicit PHP version requirement (>=8.2) to composer.json</li><li><strong>Changed:</strong> Updated update.json with correct plugin information</li><li><strong>Changed:</strong> Fixed Text Domain in robotstxt-updater.php to match plugin slug</li><li><strong>Fixed:</strong> Composer now validates PHP version during dependency installation</li><li><strong>Fixed:</strong> Plugin update system correctly identifies the plugin</li><li><strong>Fixed:</strong> Translations properly loaded for updater error messages</li><li><strong>Improved:</strong> All text domains now consistently use 'idrivee2-media-upload'</li></ul><h3>1.0.0 - 2026-02-03</h3><ul><li><strong>Release:</strong> First stable release</li><li><strong>Feature:</strong> Automatic upload of media files to iDrivee2 (S3-compatible storage)</li><li><strong>Feature:</strong> URL rewriting to serve media from CDN</li><li><strong>Feature:</strong> Local file deletion after successful upload</li><li><strong>Feature:</strong> Admin interface with connection and upload testing</li><li><strong>Security:</strong> Enterprise-grade security with nonce validation</li><li><strong>Architecture:</strong> Class-based modular architecture with dependency injection</li><li><strong>Testing:</strong> PHPUnit test structure and PHPStan static analysis</li><li><strong>Compatibility:</strong> WordPress 6.8+ and PHP 8.2+</li></ul>",
|
||||||
"sections": {
|
"sections": {
|
||||||
"description": "Uploads media files to iDrivee2 (S3-compatible) with enterprise-grade security and logging. The plugin intercepts WordPress media uploads, pushes files to an S3-compatible bucket, deletes local copies, and rewrites URLs to serve media from the CDN.",
|
"description": "Uploads media files to iDrivee2 (S3-compatible) with enterprise-grade security and logging. The plugin intercepts WordPress media uploads, pushes files to an S3-compatible bucket, deletes local copies, and rewrites URLs to serve media from the CDN.",
|
||||||
"changelog": "<h3>1.2.1 - 2026-06-05</h3><ul><li><strong>Fixed:</strong> Infinite recursion on image upload — wp_update_post() fired edit_attachment which re-triggered the upload method. Removed edit_attachment hook, added re-entry guard.</li><li><strong>Fixed:</strong> Fatal TypeError: fclose() on already-closed stream — AWS SDK closes streams after upload; added is_resource() check before fclose().</li></ul><h3>1.2.0 - 2026-06-02</h3><ul><li><strong>Performance:</strong> Concurrent S3 uploads via AWS CommandPool (default 5, tunable via IDRIVEE2_UPLOAD_CONCURRENCY)</li><li><strong>Performance:</strong> Stream files directly from disk — no full load into memory</li><li><strong>Performance:</strong> Removed per-file headObject pre-check — single batch DB write for stats</li><li><strong>Changed:</strong> Hook priority lowered from 999 to 10</li></ul><h3>1.1.4 - 2026-06-02</h3><ul><li><strong>Added:</strong> Full dev tooling: PHPCS, PHPStan level 9, PHPUnit test suite (22 tests)</li><li><strong>Fixed:</strong> WP_Filesystem null guard, type safety on get_option/get_transient, dynamic asset version</li><li><strong>Changed:</strong> Tested up to WordPress 7.1, PHP 8.1-8.5, Requires at least 4.1</li></ul><h3>1.1.3 - 2026-02-04</h3><ul><li><strong>Fixed:</strong> Critical namespace issue with Robotstxt_Updater class causing fatal error</li><li><strong>Fixed:</strong> Plugin now loads correctly without PHP fatal errors</li></ul><h3>1.1.2 - 2026-02-04</h3><ul><li><strong>Changed:</strong> Deployment script updated to use PHP 8.2 as platform base for production builds</li><li><strong>Changed:</strong> Now uses composer update --no-dev for consistent dependency resolution</li><li><strong>Improved:</strong> Production packages guarantee PHP 8.2+ compatibility regardless of dev environment</li></ul><h3>1.1.1 - 2026-02-04</h3><ul><li><strong>Fixed:</strong> Deployment script now includes essential files (update.json, robotstxt-updater.php, readme.txt, changelog.txt)</li><li><strong>Improved:</strong> Production packages now contain all files required for automatic updates from Gitea</li></ul><h3>1.1.0 - 2026-02-04</h3><ul><li><strong>Changed:</strong> Added explicit PHP version requirement (>=8.2) to composer.json</li><li><strong>Changed:</strong> Updated update.json with correct plugin information</li><li><strong>Changed:</strong> Fixed Text Domain in robotstxt-updater.php to match plugin slug</li><li><strong>Fixed:</strong> Composer now validates PHP version during dependency installation</li><li><strong>Fixed:</strong> Plugin update system correctly identifies the plugin</li><li><strong>Fixed:</strong> Translations properly loaded for updater error messages</li><li><strong>Improved:</strong> All text domains now consistently use 'idrivee2-media-upload'</li></ul><h3>1.0.0 - 2026-02-03</h3><ul><li><strong>Release:</strong> First stable release</li><li><strong>Feature:</strong> Automatic upload of media files to iDrivee2 (S3-compatible storage)</li><li><strong>Feature:</strong> URL rewriting to serve media from CDN</li><li><strong>Feature:</strong> Local file deletion after successful upload</li><li><strong>Feature:</strong> Admin interface with connection and upload testing</li><li><strong>Security:</strong> Enterprise-grade security with nonce validation</li><li><strong>Architecture:</strong> Class-based modular architecture with dependency injection</li><li><strong>Testing:</strong> PHPUnit test structure and PHPStan static analysis</li><li><strong>Compatibility:</strong> WordPress 6.8+ and PHP 8.2+</li></ul>"
|
"changelog": "<h3>1.3.0 - 2026-07-18</h3><ul><li><strong>Added:</strong> Image sub-sizes control under Settings → iDrivee2 — three modes: all (default), thumbnail, none.</li><li><strong>Added:</strong> IDRIVEE2_MEDIA_SUBSIZES_MODE wp-config.php constant; UI field becomes read-only when set.</li><li><strong>Added:</strong> In thumbnail/none modes, the -scaled derivative (big_image_size_threshold) is also disabled.</li><li><strong>Security:</strong> composer audit CVEs resolved — guzzlehttp/guzzle 7.11→7.15, guzzlehttp/psr7 2.11→2.13, mtdowling/jmespath.php 2.8→2.9 (CVE-2026-55767/55568/55766/54133).</li></ul><h3>1.2.1 - 2026-06-05</h3><ul><li><strong>Fixed:</strong> Infinite recursion on image upload — wp_update_post() fired edit_attachment which re-triggered the upload method. Removed edit_attachment hook, added re-entry guard.</li><li><strong>Fixed:</strong> Fatal TypeError: fclose() on already-closed stream — AWS SDK closes streams after upload; added is_resource() check before fclose().</li></ul><h3>1.2.0 - 2026-06-02</h3><ul><li><strong>Performance:</strong> Concurrent S3 uploads via AWS CommandPool (default 5, tunable via IDRIVEE2_UPLOAD_CONCURRENCY)</li><li><strong>Performance:</strong> Stream files directly from disk — no full load into memory</li><li><strong>Performance:</strong> Removed per-file headObject pre-check — single batch DB write for stats</li><li><strong>Changed:</strong> Hook priority lowered from 999 to 10</li></ul><h3>1.1.4 - 2026-06-02</h3><ul><li><strong>Added:</strong> Full dev tooling: PHPCS, PHPStan level 9, PHPUnit test suite (22 tests)</li><li><strong>Fixed:</strong> WP_Filesystem null guard, type safety on get_option/get_transient, dynamic asset version</li><li><strong>Changed:</strong> Tested up to WordPress 7.1, PHP 8.1-8.5, Requires at least 4.1</li></ul><h3>1.1.3 - 2026-02-04</h3><ul><li><strong>Fixed:</strong> Critical namespace issue with Robotstxt_Updater class causing fatal error</li><li><strong>Fixed:</strong> Plugin now loads correctly without PHP fatal errors</li></ul><h3>1.1.2 - 2026-02-04</h3><ul><li><strong>Changed:</strong> Deployment script updated to use PHP 8.2 as platform base for production builds</li><li><strong>Changed:</strong> Now uses composer update --no-dev for consistent dependency resolution</li><li><strong>Improved:</strong> Production packages guarantee PHP 8.2+ compatibility regardless of dev environment</li></ul><h3>1.1.1 - 2026-02-04</h3><ul><li><strong>Fixed:</strong> Deployment script now includes essential files (update.json, robotstxt-updater.php, readme.txt, changelog.txt)</li><li><strong>Improved:</strong> Production packages now contain all files required for automatic updates from Gitea</li></ul><h3>1.1.0 - 2026-02-04</h3><ul><li><strong>Changed:</strong> Added explicit PHP version requirement (>=8.2) to composer.json</li><li><strong>Changed:</strong> Updated update.json with correct plugin information</li><li><strong>Changed:</strong> Fixed Text Domain in robotstxt-updater.php to match plugin slug</li><li><strong>Fixed:</strong> Composer now validates PHP version during dependency installation</li><li><strong>Fixed:</strong> Plugin update system correctly identifies the plugin</li><li><strong>Fixed:</strong> Translations properly loaded for updater error messages</li><li><strong>Improved:</strong> All text domains now consistently use 'idrivee2-media-upload'</li></ul><h3>1.0.0 - 2026-02-03</h3><ul><li><strong>Release:</strong> First stable release</li><li><strong>Feature:</strong> Automatic upload of media files to iDrivee2 (S3-compatible storage)</li><li><strong>Feature:</strong> URL rewriting to serve media from CDN</li><li><strong>Feature:</strong> Local file deletion after successful upload</li><li><strong>Feature:</strong> Admin interface with connection and upload testing</li><li><strong>Security:</strong> Enterprise-grade security with nonce validation</li><li><strong>Architecture:</strong> Class-based modular architecture with dependency injection</li><li><strong>Testing:</strong> PHPUnit test structure and PHPStan static analysis</li><li><strong>Compatibility:</strong> WordPress 6.8+ and PHP 8.2+</li></ul>"
|
||||||
},
|
},
|
||||||
"banners": {
|
"banners": {
|
||||||
"low": "",
|
"low": "",
|
||||||
|
|
|
||||||
4
vendor/aws/aws-sdk-php/CODE_OF_CONDUCT.md
vendored
4
vendor/aws/aws-sdk-php/CODE_OF_CONDUCT.md
vendored
|
|
@ -1,4 +0,0 @@
|
||||||
## Code of Conduct
|
|
||||||
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
|
|
||||||
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
|
|
||||||
opensource-codeofconduct@amazon.com with any additional questions or comments.
|
|
||||||
4
vendor/aws/aws-sdk-php/CRT_INSTRUCTIONS.md
vendored
4
vendor/aws/aws-sdk-php/CRT_INSTRUCTIONS.md
vendored
|
|
@ -1,4 +0,0 @@
|
||||||
## Building and enabling the Common Run Time
|
|
||||||
|
|
||||||
1. **Follow instructions on crt repo** – Clone and build the repo as shown [here][https://github.com/awslabs/aws-crt-php].
|
|
||||||
1. **Enable the CRT** – add the following line to your php.ini file `extension=path/to/aws-crt-php/modules/awscrt.so`
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
# Overview
|
|
||||||
|
|
||||||
This page describes the support policy for the Amazon S3 Encryption Client for PHP. We regularly provide the Amazon S3 Encryption Client for PHP with updates that may contain support for new or updated APIs, new features, enhancements, bug fixes, security patches, or documentation updates. Updates may also address changes with dependencies, language runtimes, and operating systems.
|
|
||||||
|
|
||||||
We recommend users to stay up-to-date with Amazon S3 Encryption Client for PHP releases to keep up with the latest features, security updates, and underlying dependencies. Continued use of an unsupported SDK version is not recommended and is done at the user's discretion.
|
|
||||||
|
|
||||||
# Major Version Lifecycle
|
|
||||||
|
|
||||||
The Amazon S3 Encryption Client for Go follows the same major version lifecycle as the AWS SDK. For details on this lifecycle, see [AWS SDKs and Tools Maintenance Policy](https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html#version-life-cycle).
|
|
||||||
|
|
||||||
# Version Support Matrix
|
|
||||||
|
|
||||||
This table describes the current support status of each major version of the Amazon S3 Encryption Client for PHP. It also shows the next status each major version will transition to, and the date at which that transition will happen.
|
|
||||||
|
|
||||||
| Major version | Current status | Next status | Next status date |
|
|
||||||
|--------------|----------------|-------------|------------------|
|
|
||||||
| 3.x | General Availability | - | - |
|
|
||||||
| 2.x | General Availability | Maintenance | 2026-06-15 |
|
|
||||||
| 1.x | End of Support | - | - |
|
|
||||||
5
vendor/composer/autoload_classmap.php
vendored
5
vendor/composer/autoload_classmap.php
vendored
|
|
@ -1299,14 +1299,18 @@ return array(
|
||||||
'GuzzleHttp\\Handler\\CurlHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php',
|
'GuzzleHttp\\Handler\\CurlHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php',
|
||||||
'GuzzleHttp\\Handler\\CurlMultiHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php',
|
'GuzzleHttp\\Handler\\CurlMultiHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php',
|
||||||
'GuzzleHttp\\Handler\\CurlShareHandleState' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php',
|
'GuzzleHttp\\Handler\\CurlShareHandleState' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php',
|
||||||
|
'GuzzleHttp\\Handler\\CurlVersion' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlVersion.php',
|
||||||
'GuzzleHttp\\Handler\\EasyHandle' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php',
|
'GuzzleHttp\\Handler\\EasyHandle' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php',
|
||||||
'GuzzleHttp\\Handler\\HeaderProcessor' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php',
|
'GuzzleHttp\\Handler\\HeaderProcessor' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php',
|
||||||
'GuzzleHttp\\Handler\\MockHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/MockHandler.php',
|
'GuzzleHttp\\Handler\\MockHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/MockHandler.php',
|
||||||
'GuzzleHttp\\Handler\\Proxy' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/Proxy.php',
|
'GuzzleHttp\\Handler\\Proxy' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/Proxy.php',
|
||||||
|
'GuzzleHttp\\Handler\\ProxyEnvironment' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/ProxyEnvironment.php',
|
||||||
'GuzzleHttp\\Handler\\StreamHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php',
|
'GuzzleHttp\\Handler\\StreamHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php',
|
||||||
|
'GuzzleHttp\\Handler\\TlsVersion' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/TlsVersion.php',
|
||||||
'GuzzleHttp\\MessageFormatter' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatter.php',
|
'GuzzleHttp\\MessageFormatter' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatter.php',
|
||||||
'GuzzleHttp\\MessageFormatterInterface' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatterInterface.php',
|
'GuzzleHttp\\MessageFormatterInterface' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatterInterface.php',
|
||||||
'GuzzleHttp\\Middleware' => $vendorDir . '/guzzlehttp/guzzle/src/Middleware.php',
|
'GuzzleHttp\\Middleware' => $vendorDir . '/guzzlehttp/guzzle/src/Middleware.php',
|
||||||
|
'GuzzleHttp\\Multiplexing' => $vendorDir . '/guzzlehttp/guzzle/src/Multiplexing.php',
|
||||||
'GuzzleHttp\\Pool' => $vendorDir . '/guzzlehttp/guzzle/src/Pool.php',
|
'GuzzleHttp\\Pool' => $vendorDir . '/guzzlehttp/guzzle/src/Pool.php',
|
||||||
'GuzzleHttp\\PrepareBodyMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php',
|
'GuzzleHttp\\PrepareBodyMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php',
|
||||||
'GuzzleHttp\\Promise\\AggregateException' => $vendorDir . '/guzzlehttp/promises/src/AggregateException.php',
|
'GuzzleHttp\\Promise\\AggregateException' => $vendorDir . '/guzzlehttp/promises/src/AggregateException.php',
|
||||||
|
|
@ -1414,5 +1418,6 @@ return array(
|
||||||
'iDrivee2Media\\Plugin' => $baseDir . '/includes/class-plugin.php',
|
'iDrivee2Media\\Plugin' => $baseDir . '/includes/class-plugin.php',
|
||||||
'iDrivee2Media\\Rate_Limiter' => $baseDir . '/includes/class-rate-limiter.php',
|
'iDrivee2Media\\Rate_Limiter' => $baseDir . '/includes/class-rate-limiter.php',
|
||||||
'iDrivee2Media\\S3_Client_Factory' => $baseDir . '/includes/class-s3-client-factory.php',
|
'iDrivee2Media\\S3_Client_Factory' => $baseDir . '/includes/class-s3-client-factory.php',
|
||||||
|
'iDrivee2Media\\Subsize_Filter' => $baseDir . '/includes/class-subsize-filter.php',
|
||||||
'iDrivee2Media\\URL_Rewriter' => $baseDir . '/includes/class-url-rewriter.php',
|
'iDrivee2Media\\URL_Rewriter' => $baseDir . '/includes/class-url-rewriter.php',
|
||||||
);
|
);
|
||||||
|
|
|
||||||
5
vendor/composer/autoload_static.php
vendored
5
vendor/composer/autoload_static.php
vendored
|
|
@ -1388,14 +1388,18 @@ class ComposerStaticInite60858b25bb9b11d51011ff69c492447
|
||||||
'GuzzleHttp\\Handler\\CurlHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php',
|
'GuzzleHttp\\Handler\\CurlHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php',
|
||||||
'GuzzleHttp\\Handler\\CurlMultiHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php',
|
'GuzzleHttp\\Handler\\CurlMultiHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php',
|
||||||
'GuzzleHttp\\Handler\\CurlShareHandleState' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php',
|
'GuzzleHttp\\Handler\\CurlShareHandleState' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php',
|
||||||
|
'GuzzleHttp\\Handler\\CurlVersion' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlVersion.php',
|
||||||
'GuzzleHttp\\Handler\\EasyHandle' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php',
|
'GuzzleHttp\\Handler\\EasyHandle' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php',
|
||||||
'GuzzleHttp\\Handler\\HeaderProcessor' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php',
|
'GuzzleHttp\\Handler\\HeaderProcessor' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php',
|
||||||
'GuzzleHttp\\Handler\\MockHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/MockHandler.php',
|
'GuzzleHttp\\Handler\\MockHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/MockHandler.php',
|
||||||
'GuzzleHttp\\Handler\\Proxy' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/Proxy.php',
|
'GuzzleHttp\\Handler\\Proxy' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/Proxy.php',
|
||||||
|
'GuzzleHttp\\Handler\\ProxyEnvironment' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/ProxyEnvironment.php',
|
||||||
'GuzzleHttp\\Handler\\StreamHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php',
|
'GuzzleHttp\\Handler\\StreamHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php',
|
||||||
|
'GuzzleHttp\\Handler\\TlsVersion' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/TlsVersion.php',
|
||||||
'GuzzleHttp\\MessageFormatter' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatter.php',
|
'GuzzleHttp\\MessageFormatter' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatter.php',
|
||||||
'GuzzleHttp\\MessageFormatterInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatterInterface.php',
|
'GuzzleHttp\\MessageFormatterInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatterInterface.php',
|
||||||
'GuzzleHttp\\Middleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Middleware.php',
|
'GuzzleHttp\\Middleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Middleware.php',
|
||||||
|
'GuzzleHttp\\Multiplexing' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Multiplexing.php',
|
||||||
'GuzzleHttp\\Pool' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Pool.php',
|
'GuzzleHttp\\Pool' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Pool.php',
|
||||||
'GuzzleHttp\\PrepareBodyMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php',
|
'GuzzleHttp\\PrepareBodyMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php',
|
||||||
'GuzzleHttp\\Promise\\AggregateException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/AggregateException.php',
|
'GuzzleHttp\\Promise\\AggregateException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/AggregateException.php',
|
||||||
|
|
@ -1503,6 +1507,7 @@ class ComposerStaticInite60858b25bb9b11d51011ff69c492447
|
||||||
'iDrivee2Media\\Plugin' => __DIR__ . '/../..' . '/includes/class-plugin.php',
|
'iDrivee2Media\\Plugin' => __DIR__ . '/../..' . '/includes/class-plugin.php',
|
||||||
'iDrivee2Media\\Rate_Limiter' => __DIR__ . '/../..' . '/includes/class-rate-limiter.php',
|
'iDrivee2Media\\Rate_Limiter' => __DIR__ . '/../..' . '/includes/class-rate-limiter.php',
|
||||||
'iDrivee2Media\\S3_Client_Factory' => __DIR__ . '/../..' . '/includes/class-s3-client-factory.php',
|
'iDrivee2Media\\S3_Client_Factory' => __DIR__ . '/../..' . '/includes/class-s3-client-factory.php',
|
||||||
|
'iDrivee2Media\\Subsize_Filter' => __DIR__ . '/../..' . '/includes/class-subsize-filter.php',
|
||||||
'iDrivee2Media\\URL_Rewriter' => __DIR__ . '/../..' . '/includes/class-url-rewriter.php',
|
'iDrivee2Media\\URL_Rewriter' => __DIR__ . '/../..' . '/includes/class-url-rewriter.php',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
100
vendor/composer/installed.json
vendored
100
vendor/composer/installed.json
vendored
|
|
@ -159,27 +159,27 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/guzzle",
|
"name": "guzzlehttp/guzzle",
|
||||||
"version": "7.11.0",
|
"version": "7.15.0",
|
||||||
"version_normalized": "7.11.0.0",
|
"version_normalized": "7.15.0.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/guzzle.git",
|
"url": "https://github.com/guzzle/guzzle.git",
|
||||||
"reference": "c987f8ce84b8434fa430795eca0f3430663da72b"
|
"reference": "90bd104afeb0fcc2190c9eb6fd8a441447e4b30d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/c987f8ce84b8434fa430795eca0f3430663da72b",
|
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/90bd104afeb0fcc2190c9eb6fd8a441447e4b30d",
|
||||||
"reference": "c987f8ce84b8434fa430795eca0f3430663da72b",
|
"reference": "90bd104afeb0fcc2190c9eb6fd8a441447e4b30d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"guzzlehttp/promises": "^2.5",
|
"guzzlehttp/promises": "^2.5.1",
|
||||||
"guzzlehttp/psr7": "^2.11",
|
"guzzlehttp/psr7": "^2.13",
|
||||||
"php": "^7.2.5 || ^8.0",
|
"php": "^7.2.5 || ^8.0",
|
||||||
"psr/http-client": "^1.0",
|
"psr/http-client": "^1.0",
|
||||||
"symfony/deprecation-contracts": "^2.5 || ^3.0",
|
"symfony/deprecation-contracts": "^2.5 || ^3.0",
|
||||||
"symfony/polyfill-php80": "^1.24"
|
"symfony/polyfill-php80": "^1.25"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"psr/http-client-implementation": "1.0"
|
"psr/http-client-implementation": "1.0"
|
||||||
|
|
@ -187,8 +187,8 @@
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
"guzzle/client-integration-tests": "3.0.2",
|
"guzzle/client-integration-tests": "3.0.3",
|
||||||
"guzzlehttp/test-server": "^0.4",
|
"guzzlehttp/test-server": "^0.7",
|
||||||
"php-http/message-factory": "^1.1",
|
"php-http/message-factory": "^1.1",
|
||||||
"phpunit/phpunit": "^8.5.52 || ^9.6.34",
|
"phpunit/phpunit": "^8.5.52 || ^9.6.34",
|
||||||
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||||
|
|
@ -198,7 +198,7 @@
|
||||||
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
|
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
|
||||||
"psr/log": "Required for using the Log middleware"
|
"psr/log": "Required for using the Log middleware"
|
||||||
},
|
},
|
||||||
"time": "2026-06-02T12:40:51+00:00",
|
"time": "2026-07-17T12:26:48+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"bamarni-bin": {
|
"bamarni-bin": {
|
||||||
|
|
@ -270,7 +270,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||||
"source": "https://github.com/guzzle/guzzle/tree/7.11.0"
|
"source": "https://github.com/guzzle/guzzle/tree/7.15.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -290,17 +290,17 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/promises",
|
"name": "guzzlehttp/promises",
|
||||||
"version": "2.5.0",
|
"version": "2.5.1",
|
||||||
"version_normalized": "2.5.0.0",
|
"version_normalized": "2.5.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/promises.git",
|
"url": "https://github.com/guzzle/promises.git",
|
||||||
"reference": "4360e982f87f5f258bf872d094647791db2f4c8e"
|
"reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e",
|
"url": "https://api.github.com/repos/guzzle/promises/zipball/9ad1e4fc607446a055b95870c7f668e93b5cff29",
|
||||||
"reference": "4360e982f87f5f258bf872d094647791db2f4c8e",
|
"reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -311,7 +311,7 @@
|
||||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
"phpunit/phpunit": "^8.5.52 || ^9.6.34"
|
"phpunit/phpunit": "^8.5.52 || ^9.6.34"
|
||||||
},
|
},
|
||||||
"time": "2026-06-02T12:23:43+00:00",
|
"time": "2026-07-08T15:48:39+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"bamarni-bin": {
|
"bamarni-bin": {
|
||||||
|
|
@ -357,7 +357,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/promises/issues",
|
"issues": "https://github.com/guzzle/promises/issues",
|
||||||
"source": "https://github.com/guzzle/promises/tree/2.5.0"
|
"source": "https://github.com/guzzle/promises/tree/2.5.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -377,17 +377,17 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/psr7",
|
"name": "guzzlehttp/psr7",
|
||||||
"version": "2.11.0",
|
"version": "2.13.0",
|
||||||
"version_normalized": "2.11.0.0",
|
"version_normalized": "2.13.0.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/psr7.git",
|
"url": "https://github.com/guzzle/psr7.git",
|
||||||
"reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f"
|
"reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/bbb5e61349fa5cb822b3e87842b951088b76b81f",
|
"url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4",
|
||||||
"reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f",
|
"reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -396,7 +396,7 @@
|
||||||
"psr/http-message": "^1.1 || ^2.0",
|
"psr/http-message": "^1.1 || ^2.0",
|
||||||
"ralouphie/getallheaders": "^3.0",
|
"ralouphie/getallheaders": "^3.0",
|
||||||
"symfony/deprecation-contracts": "^2.5 || ^3.0",
|
"symfony/deprecation-contracts": "^2.5 || ^3.0",
|
||||||
"symfony/polyfill-php80": "^1.24"
|
"symfony/polyfill-php80": "^1.25"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"psr/http-factory-implementation": "1.0",
|
"psr/http-factory-implementation": "1.0",
|
||||||
|
|
@ -411,7 +411,7 @@
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||||
},
|
},
|
||||||
"time": "2026-06-02T12:30:48+00:00",
|
"time": "2026-07-16T22:23:49+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"bamarni-bin": {
|
"bamarni-bin": {
|
||||||
|
|
@ -479,7 +479,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/psr7/issues",
|
"issues": "https://github.com/guzzle/psr7/issues",
|
||||||
"source": "https://github.com/guzzle/psr7/tree/2.11.0"
|
"source": "https://github.com/guzzle/psr7/tree/2.13.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -499,17 +499,17 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mtdowling/jmespath.php",
|
"name": "mtdowling/jmespath.php",
|
||||||
"version": "2.8.0",
|
"version": "2.9.2",
|
||||||
"version_normalized": "2.8.0.0",
|
"version_normalized": "2.9.2.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/jmespath/jmespath.php.git",
|
"url": "https://github.com/jmespath/jmespath.php.git",
|
||||||
"reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc"
|
"reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc",
|
"url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/2157c5e50e813ec6a96c1eed3be7f64a20fb32a8",
|
||||||
"reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc",
|
"reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -518,16 +518,16 @@
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"composer/xdebug-handler": "^3.0.3",
|
"composer/xdebug-handler": "^3.0.3",
|
||||||
"phpunit/phpunit": "^8.5.33"
|
"phpunit/phpunit": "^8.5.52"
|
||||||
},
|
},
|
||||||
"time": "2024-09-04T18:46:31+00:00",
|
"time": "2026-07-06T18:56:19+00:00",
|
||||||
"bin": [
|
"bin": [
|
||||||
"bin/jp.php"
|
"bin/jp.php"
|
||||||
],
|
],
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "2.8-dev"
|
"dev-master": "2.9-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"installation-source": "dist",
|
"installation-source": "dist",
|
||||||
|
|
@ -562,7 +562,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/jmespath/jmespath.php/issues",
|
"issues": "https://github.com/jmespath/jmespath.php/issues",
|
||||||
"source": "https://github.com/jmespath/jmespath.php/tree/2.8.0"
|
"source": "https://github.com/jmespath/jmespath.php/tree/2.9.2"
|
||||||
},
|
},
|
||||||
"install-path": "../mtdowling/jmespath.php"
|
"install-path": "../mtdowling/jmespath.php"
|
||||||
},
|
},
|
||||||
|
|
@ -784,23 +784,23 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/deprecation-contracts",
|
"name": "symfony/deprecation-contracts",
|
||||||
"version": "v3.7.0",
|
"version": "v3.7.1",
|
||||||
"version_normalized": "3.7.0.0",
|
"version_normalized": "3.7.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||||
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b"
|
"reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b",
|
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d",
|
||||||
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b",
|
"reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.1"
|
"php": ">=8.1"
|
||||||
},
|
},
|
||||||
"time": "2026-04-13T15:52:40+00:00",
|
"time": "2026-06-05T06:23:12+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"thanks": {
|
"thanks": {
|
||||||
|
|
@ -834,7 +834,7 @@
|
||||||
"description": "A generic function and convention to trigger deprecation notices",
|
"description": "A generic function and convention to trigger deprecation notices",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0"
|
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
@ -1018,17 +1018,17 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.38.1",
|
"version": "v1.38.2",
|
||||||
"version_normalized": "1.38.1.0",
|
"version_normalized": "1.38.2.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
"reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92"
|
"reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92",
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
|
||||||
"reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92",
|
"reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -1041,7 +1041,7 @@
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-mbstring": "For best performance"
|
"ext-mbstring": "For best performance"
|
||||||
},
|
},
|
||||||
"time": "2026-05-26T12:51:13+00:00",
|
"time": "2026-05-27T06:59:30+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"thanks": {
|
"thanks": {
|
||||||
|
|
@ -1082,7 +1082,7 @@
|
||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1"
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
36
vendor/composer/installed.php
vendored
36
vendor/composer/installed.php
vendored
|
|
@ -29,36 +29,36 @@
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'guzzlehttp/guzzle' => array(
|
'guzzlehttp/guzzle' => array(
|
||||||
'pretty_version' => '7.11.0',
|
'pretty_version' => '7.15.0',
|
||||||
'version' => '7.11.0.0',
|
'version' => '7.15.0.0',
|
||||||
'reference' => 'c987f8ce84b8434fa430795eca0f3430663da72b',
|
'reference' => '90bd104afeb0fcc2190c9eb6fd8a441447e4b30d',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
|
'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'guzzlehttp/promises' => array(
|
'guzzlehttp/promises' => array(
|
||||||
'pretty_version' => '2.5.0',
|
'pretty_version' => '2.5.1',
|
||||||
'version' => '2.5.0.0',
|
'version' => '2.5.1.0',
|
||||||
'reference' => '4360e982f87f5f258bf872d094647791db2f4c8e',
|
'reference' => '9ad1e4fc607446a055b95870c7f668e93b5cff29',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../guzzlehttp/promises',
|
'install_path' => __DIR__ . '/../guzzlehttp/promises',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'guzzlehttp/psr7' => array(
|
'guzzlehttp/psr7' => array(
|
||||||
'pretty_version' => '2.11.0',
|
'pretty_version' => '2.13.0',
|
||||||
'version' => '2.11.0.0',
|
'version' => '2.13.0.0',
|
||||||
'reference' => 'bbb5e61349fa5cb822b3e87842b951088b76b81f',
|
'reference' => 'dad89620b7a6edb60c15858442eb2e408b45d8f4',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
|
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'mtdowling/jmespath.php' => array(
|
'mtdowling/jmespath.php' => array(
|
||||||
'pretty_version' => '2.8.0',
|
'pretty_version' => '2.9.2',
|
||||||
'version' => '2.8.0.0',
|
'version' => '2.9.2.0',
|
||||||
'reference' => 'a2a865e05d5f420b50cc2f85bb78d565db12a6bc',
|
'reference' => '2157c5e50e813ec6a96c1eed3be7f64a20fb32a8',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../mtdowling/jmespath.php',
|
'install_path' => __DIR__ . '/../mtdowling/jmespath.php',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
|
@ -128,9 +128,9 @@
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/deprecation-contracts' => array(
|
'symfony/deprecation-contracts' => array(
|
||||||
'pretty_version' => 'v3.7.0',
|
'pretty_version' => 'v3.7.1',
|
||||||
'version' => '3.7.0.0',
|
'version' => '3.7.1.0',
|
||||||
'reference' => '50f59d1f3ca46d41ac911f97a78626b6756af35b',
|
'reference' => 'f3202fa1b5097b0af062dc978b32ecf63404e31d',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
|
@ -155,9 +155,9 @@
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-mbstring' => array(
|
'symfony/polyfill-mbstring' => array(
|
||||||
'pretty_version' => 'v1.38.1',
|
'pretty_version' => 'v1.38.2',
|
||||||
'version' => '1.38.1.0',
|
'version' => '1.38.2.0',
|
||||||
'reference' => '14c5439eec4ccff081ac14eca2dc57feb2a66d92',
|
'reference' => 'd3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
|
|
||||||
1773
vendor/guzzlehttp/guzzle/CHANGELOG.md
vendored
1773
vendor/guzzlehttp/guzzle/CHANGELOG.md
vendored
File diff suppressed because it is too large
Load diff
94
vendor/guzzlehttp/guzzle/README.md
vendored
94
vendor/guzzlehttp/guzzle/README.md
vendored
|
|
@ -1,94 +0,0 @@
|
||||||

|
|
||||||
|
|
||||||
# Guzzle, PHP HTTP client
|
|
||||||
|
|
||||||
[](https://github.com/guzzle/guzzle/releases)
|
|
||||||
[](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
|
|
||||||
[](https://packagist.org/packages/guzzlehttp/guzzle)
|
|
||||||
|
|
||||||
Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
|
|
||||||
trivial to integrate with web services.
|
|
||||||
|
|
||||||
- Simple interface for building query strings, POST requests, streaming large
|
|
||||||
uploads, streaming large downloads, using HTTP cookies, uploading JSON data,
|
|
||||||
etc...
|
|
||||||
- Can send both synchronous and asynchronous requests using the same interface.
|
|
||||||
- Uses PSR-7 interfaces for requests, responses, and streams. This allows you
|
|
||||||
to utilize other PSR-7 compatible libraries with Guzzle.
|
|
||||||
- Supports PSR-18 allowing interoperability between other PSR-18 HTTP Clients.
|
|
||||||
- Abstracts away the underlying HTTP transport, allowing you to write
|
|
||||||
environment and transport agnostic code; i.e., no hard dependency on cURL,
|
|
||||||
PHP streams, sockets, or non-blocking event loops.
|
|
||||||
- Middleware system allows you to augment and compose client behavior.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$client = new \GuzzleHttp\Client();
|
|
||||||
$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');
|
|
||||||
|
|
||||||
echo $response->getStatusCode(); // 200
|
|
||||||
echo $response->getHeaderLine('content-type'); // 'application/json; charset=utf8'
|
|
||||||
echo $response->getBody(); // '{"id": 1420053, "name": "guzzle", ...}'
|
|
||||||
|
|
||||||
// Send an asynchronous request.
|
|
||||||
$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
|
|
||||||
$promise = $client->sendAsync($request)->then(function ($response) {
|
|
||||||
echo 'I completed! ' . $response->getBody();
|
|
||||||
});
|
|
||||||
|
|
||||||
$promise->wait();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Help and docs
|
|
||||||
|
|
||||||
We use GitHub issues only to discuss bugs and new features. For support please refer to:
|
|
||||||
|
|
||||||
- [Documentation](docs/index.md)
|
|
||||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/guzzle)
|
|
||||||
- [#guzzle](https://app.slack.com/client/T0D2S9JCT/CE6UAAKL4) channel on [PHP-HTTP Slack](https://slack.httplug.io/)
|
|
||||||
- [Gitter](https://gitter.im/guzzle/guzzle)
|
|
||||||
|
|
||||||
|
|
||||||
## Installing Guzzle
|
|
||||||
|
|
||||||
The recommended way to install Guzzle is through
|
|
||||||
[Composer](https://getcomposer.org/).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
composer require guzzlehttp/guzzle
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Version Guidance
|
|
||||||
|
|
||||||
| Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | PHP Version |
|
|
||||||
|---------|---------------------|---------------------|--------------|---------------------|---------------------|-------|--------------|
|
|
||||||
| 3.x | EOL (2016-10-31) | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >=5.3.3,<7.0 |
|
|
||||||
| 4.x | EOL (2016-10-31) | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >=5.4,<7.0 |
|
|
||||||
| 5.x | EOL (2019-10-31) | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >=5.4,<7.4 |
|
|
||||||
| 6.x | EOL (2023-10-31) | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >=5.5,<8.0 |
|
|
||||||
| 7.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes | >=7.2.5,<8.6 |
|
|
||||||
|
|
||||||
[guzzle-3-repo]: https://github.com/guzzle/guzzle3
|
|
||||||
[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
|
|
||||||
[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3
|
|
||||||
[guzzle-6-repo]: https://github.com/guzzle/guzzle/tree/6.5
|
|
||||||
[guzzle-7-repo]: https://github.com/guzzle/guzzle/tree/7.11
|
|
||||||
[guzzle-3-docs]: https://github.com/guzzle/guzzle3/tree/master/docs
|
|
||||||
[guzzle-5-docs]: https://github.com/guzzle/guzzle/tree/5.3/docs
|
|
||||||
[guzzle-6-docs]: https://github.com/guzzle/guzzle/tree/6.5/docs
|
|
||||||
[guzzle-7-docs]: https://github.com/guzzle/guzzle/blob/7.11/docs/index.md
|
|
||||||
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/guzzle/security/policy) for more information.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
|
|
||||||
|
|
||||||
## For Enterprise
|
|
||||||
|
|
||||||
Available as part of the Tidelift Subscription
|
|
||||||
|
|
||||||
The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-guzzle?utm_source=packagist-guzzlehttp-guzzle&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
|
||||||
1251
vendor/guzzlehttp/guzzle/UPGRADING.md
vendored
1251
vendor/guzzlehttp/guzzle/UPGRADING.md
vendored
File diff suppressed because it is too large
Load diff
6
vendor/guzzlehttp/guzzle/package-lock.json
generated
vendored
6
vendor/guzzlehttp/guzzle/package-lock.json
generated
vendored
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"name": "guzzle",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {}
|
|
||||||
}
|
|
||||||
409
vendor/guzzlehttp/guzzle/src/Client.php
vendored
409
vendor/guzzlehttp/guzzle/src/Client.php
vendored
|
|
@ -3,9 +3,11 @@
|
||||||
namespace GuzzleHttp;
|
namespace GuzzleHttp;
|
||||||
|
|
||||||
use GuzzleHttp\Cookie\CookieJar;
|
use GuzzleHttp\Cookie\CookieJar;
|
||||||
|
use GuzzleHttp\Cookie\CookieJarInterface;
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
use GuzzleHttp\Exception\InvalidArgumentException;
|
use GuzzleHttp\Exception\InvalidArgumentException;
|
||||||
use GuzzleHttp\Handler\CurlShareHandleState;
|
use GuzzleHttp\Handler\CurlShareHandleState;
|
||||||
|
use GuzzleHttp\Handler\CurlVersion;
|
||||||
use GuzzleHttp\Promise as P;
|
use GuzzleHttp\Promise as P;
|
||||||
use GuzzleHttp\Promise\PromiseInterface;
|
use GuzzleHttp\Promise\PromiseInterface;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
@ -52,6 +54,20 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
* into relative URIs. Can be a string or instance of UriInterface.
|
* into relative URIs. Can be a string or instance of UriInterface.
|
||||||
* - transport_sharing: (string|null) Transport sharing mode for the
|
* - transport_sharing: (string|null) Transport sharing mode for the
|
||||||
* default handler. Accepts TransportSharing::* or null. Defaults to null.
|
* default handler. Accepts TransportSharing::* or null. Defaults to null.
|
||||||
|
* - max_host_connections: (int|null) Maximum concurrent connections per
|
||||||
|
* host, applied by the default CurlMultiHandler. The default stream
|
||||||
|
* fallback receives the cap as a marker only: it rejects enabled
|
||||||
|
* response streaming ("stream" => true) and does not limit overlapping
|
||||||
|
* buffered calls.
|
||||||
|
* - max_total_connections: (int|null) Maximum concurrent connections
|
||||||
|
* overall, applied by the default CurlMultiHandler. The default stream
|
||||||
|
* fallback receives the cap as a marker only: it rejects enabled
|
||||||
|
* response streaming ("stream" => true) and does not limit overlapping
|
||||||
|
* buffered calls.
|
||||||
|
* - multiplex: (string|null) Multiplexing::NONE to disable multiplexing on
|
||||||
|
* the default CurlMultiHandler; the value also becomes the default
|
||||||
|
* "multiplex" request option. Other Multiplexing::* values act as the
|
||||||
|
* default request option only.
|
||||||
* - **: any request option
|
* - **: any request option
|
||||||
*
|
*
|
||||||
* @param array $config Client configuration settings.
|
* @param array $config Client configuration settings.
|
||||||
|
|
@ -60,16 +76,41 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
*/
|
*/
|
||||||
public function __construct(array $config = [])
|
public function __construct(array $config = [])
|
||||||
{
|
{
|
||||||
|
$handlerOptions = [];
|
||||||
|
foreach (['max_host_connections', 'max_total_connections'] as $capOption) {
|
||||||
|
if (\array_key_exists($capOption, $config)) {
|
||||||
|
if ($config[$capOption] !== null) {
|
||||||
|
$handlerOptions[$capOption] = $config[$capOption];
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($config[$capOption]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deliberately not unset: the value also becomes the default
|
||||||
|
// "multiplex" request option, which the configured handler accepts.
|
||||||
|
$handlerMultiplex = ($config['multiplex'] ?? null) === Multiplexing::NONE;
|
||||||
|
|
||||||
$transportSharing = \array_key_exists('transport_sharing', $config) ? $config['transport_sharing'] : null;
|
$transportSharing = \array_key_exists('transport_sharing', $config) ? $config['transport_sharing'] : null;
|
||||||
$transportSharingMode = CurlShareHandleState::normalizeMode($transportSharing, 'transport_sharing');
|
$transportSharingMode = CurlShareHandleState::normalizeMode($transportSharing, 'transport_sharing');
|
||||||
unset($config['transport_sharing']);
|
unset($config['transport_sharing']);
|
||||||
|
|
||||||
if (!isset($config['handler'])) {
|
if (!isset($config['handler'])) {
|
||||||
$config['handler'] = $transportSharingMode === TransportSharing::NONE
|
if ($transportSharingMode !== TransportSharing::NONE) {
|
||||||
|
$handlerOptions['transport_sharing'] = $transportSharingMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($handlerMultiplex) {
|
||||||
|
$handlerOptions['multiplex'] = Multiplexing::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$config['handler'] = $handlerOptions === []
|
||||||
? HandlerStack::create()
|
? HandlerStack::create()
|
||||||
: HandlerStack::create(Utils::chooseHandler(['transport_sharing' => $transportSharingMode]));
|
: HandlerStack::create(Utils::chooseHandler($handlerOptions));
|
||||||
} elseif (!\is_callable($config['handler'])) {
|
} elseif (!\is_callable($config['handler'])) {
|
||||||
throw new InvalidArgumentException('handler must be a callable');
|
throw new InvalidArgumentException('handler must be a callable');
|
||||||
|
} elseif ($handlerOptions !== []) {
|
||||||
|
throw new InvalidArgumentException('The "max_host_connections" and "max_total_connections" client options require Guzzle to create the default handler. Configure the options on the CurlMultiHandler constructor to apply numeric connection caps, or on the StreamHandler constructor to reject enabled response streaming, when providing a custom handler.');
|
||||||
} elseif ($transportSharingMode === TransportSharing::HANDLER_REQUIRE) {
|
} elseif ($transportSharingMode === TransportSharing::HANDLER_REQUIRE) {
|
||||||
throw new InvalidArgumentException('The "transport_sharing" client option can only require sharing when Guzzle creates the default handler. Configure the "transport_sharing" option on CurlHandler or CurlMultiHandler when providing a custom cURL handler.');
|
throw new InvalidArgumentException('The "transport_sharing" client option can only require sharing when Guzzle creates the default handler. Configure the "transport_sharing" option on CurlHandler or CurlMultiHandler when providing a custom cURL handler.');
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +133,8 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
*/
|
*/
|
||||||
public function __call($method, $args)
|
public function __call($method, $args)
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s::%s() is deprecated and will be removed in 8.0.', __CLASS__, __FUNCTION__);
|
||||||
|
|
||||||
if (\count($args) < 1) {
|
if (\count($args) < 1) {
|
||||||
throw new InvalidArgumentException('Magic request methods require a URI and optional options array');
|
throw new InvalidArgumentException('Magic request methods require a URI and optional options array');
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +144,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
|
|
||||||
$isAsync = \substr($method, -5) === 'Async';
|
$isAsync = \substr($method, -5) === 'Async';
|
||||||
$method = $isAsync ? \substr($method, 0, -5) : $method;
|
$method = $isAsync ? \substr($method, 0, -5) : $method;
|
||||||
$method = \strtoupper($method);
|
$method = Psr7\Utils::asciiToUpper($method);
|
||||||
|
|
||||||
return $isAsync
|
return $isAsync
|
||||||
? $this->requestAsync($method, $uri, $opts)
|
? $this->requestAsync($method, $uri, $opts)
|
||||||
|
|
@ -168,7 +211,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
*/
|
*/
|
||||||
public function requestAsync(string $method, $uri = '', array $options = []): PromiseInterface
|
public function requestAsync(string $method, $uri = '', array $options = []): PromiseInterface
|
||||||
{
|
{
|
||||||
$normalizedMethod = \strtoupper($method);
|
$normalizedMethod = Psr7\Utils::asciiToUpper($method);
|
||||||
if ($method !== $normalizedMethod) {
|
if ($method !== $normalizedMethod) {
|
||||||
\trigger_deprecation(
|
\trigger_deprecation(
|
||||||
'guzzlehttp/guzzle',
|
'guzzlehttp/guzzle',
|
||||||
|
|
@ -192,6 +235,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
if (\is_array($body)) {
|
if (\is_array($body)) {
|
||||||
throw $this->invalidBody();
|
throw $this->invalidBody();
|
||||||
}
|
}
|
||||||
|
$body = self::createBodyStream($body);
|
||||||
$request = new Psr7\Request($method, $uri, $headers, $body, $version);
|
$request = new Psr7\Request($method, $uri, $headers, $body, $version);
|
||||||
// Remove the option so that they are not doubly-applied.
|
// Remove the option so that they are not doubly-applied.
|
||||||
unset($options['headers'], $options['body'], $options['version']);
|
unset($options['headers'], $options['body'], $options['version']);
|
||||||
|
|
@ -214,7 +258,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
*/
|
*/
|
||||||
public function request(string $method, $uri = '', array $options = []): ResponseInterface
|
public function request(string $method, $uri = '', array $options = []): ResponseInterface
|
||||||
{
|
{
|
||||||
$normalizedMethod = \strtoupper($method);
|
$normalizedMethod = Psr7\Utils::asciiToUpper($method);
|
||||||
if ($method !== $normalizedMethod) {
|
if ($method !== $normalizedMethod) {
|
||||||
\trigger_deprecation(
|
\trigger_deprecation(
|
||||||
'guzzlehttp/guzzle',
|
'guzzlehttp/guzzle',
|
||||||
|
|
@ -258,7 +302,11 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
$uri = Utils::idnUriConvert($uri, $idnOptions);
|
$uri = Utils::idnUriConvert($uri, $idnOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
|
if ($uri->getScheme() === '' && $uri->getHost() !== '') {
|
||||||
|
$uri = $uri->withScheme('http');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -307,7 +355,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
// Add the User-Agent header if one was not already set.
|
// Add the User-Agent header if one was not already set.
|
||||||
$hasUserAgent = false;
|
$hasUserAgent = false;
|
||||||
foreach (\array_keys($this->config['headers']) as $name) {
|
foreach (\array_keys($this->config['headers']) as $name) {
|
||||||
if (\strtolower((string) $name) === 'user-agent') {
|
if (Psr7\Utils::asciiToLower((string) $name) === 'user-agent') {
|
||||||
$hasUserAgent = true;
|
$hasUserAgent = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -331,6 +379,8 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
*/
|
*/
|
||||||
private function prepareDefaults(array $options): array
|
private function prepareDefaults(array $options): array
|
||||||
{
|
{
|
||||||
|
self::warnAboutRequestLevelHandler($options);
|
||||||
|
|
||||||
$defaults = $this->config;
|
$defaults = $this->config;
|
||||||
|
|
||||||
if (!empty($defaults['headers'])) {
|
if (!empty($defaults['headers'])) {
|
||||||
|
|
@ -363,7 +413,178 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
|
|
||||||
self::warnAboutInvalidRequestOptionTypes($result);
|
self::warnAboutInvalidRequestOptionTypes($result);
|
||||||
|
|
||||||
return $result;
|
return self::normalizeDeprecatedRequestOptionValues($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize values that guzzlehttp/guzzle 8.0 rejects only after the
|
||||||
|
* corresponding 7.x deprecation has already been emitted.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
private static function normalizeDeprecatedRequestOptionValues(array $options): array
|
||||||
|
{
|
||||||
|
self::normalizeDeprecatedAuthOptionValues($options);
|
||||||
|
self::normalizeDeprecatedTlsFileOptionValues($options, 'cert');
|
||||||
|
self::normalizeDeprecatedTlsFileOptionValues($options, 'ssl_key');
|
||||||
|
self::normalizeDeprecatedStringOptionValues($options);
|
||||||
|
self::normalizeDeprecatedNumericOptionValues($options);
|
||||||
|
self::normalizeDeprecatedIntegerOptionValues($options);
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
private static function canStringifyDeprecatedValue($value): bool
|
||||||
|
{
|
||||||
|
return $value === null
|
||||||
|
|| \is_scalar($value)
|
||||||
|
|| (\is_object($value) && \method_exists($value, '__toString'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
private static function stringifyDeprecatedValue($value): string
|
||||||
|
{
|
||||||
|
if (\is_float($value) && !\is_finite($value)) {
|
||||||
|
return \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_scalar($value)) {
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_object($value) && \method_exists($value, '__toString')) {
|
||||||
|
return $value->__toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \LogicException('Value is not stringable.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
private static function normalizeDeprecatedAuthOptionValues(array &$options): void
|
||||||
|
{
|
||||||
|
if (!isset($options['auth']) || !\is_array($options['auth']) || $options['auth'] === []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ([0, 1] as $index) {
|
||||||
|
if (
|
||||||
|
\array_key_exists($index, $options['auth'])
|
||||||
|
&& !\is_string($options['auth'][$index])
|
||||||
|
&& self::canStringifyDeprecatedValue($options['auth'][$index])
|
||||||
|
) {
|
||||||
|
$options['auth'][$index] = self::stringifyDeprecatedValue($options['auth'][$index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
\array_key_exists(2, $options['auth'])
|
||||||
|
&& $options['auth'][2] !== null
|
||||||
|
&& !\is_string($options['auth'][2])
|
||||||
|
&& self::canStringifyDeprecatedValue($options['auth'][2])
|
||||||
|
) {
|
||||||
|
$options['auth'][2] = self::stringifyDeprecatedValue($options['auth'][2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
private static function normalizeDeprecatedTlsFileOptionValues(array &$options, string $option): void
|
||||||
|
{
|
||||||
|
if (!isset($options[$option]) || !\is_array($options[$option])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ([0, 1] as $index) {
|
||||||
|
if (
|
||||||
|
\array_key_exists($index, $options[$option])
|
||||||
|
&& $options[$option][$index] !== null
|
||||||
|
&& !\is_string($options[$option][$index])
|
||||||
|
&& self::canStringifyDeprecatedValue($options[$option][$index])
|
||||||
|
) {
|
||||||
|
$options[$option][$index] = self::stringifyDeprecatedValue($options[$option][$index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
private static function normalizeDeprecatedStringOptionValues(array &$options): void
|
||||||
|
{
|
||||||
|
foreach (['cert_type', 'force_ip_resolve', 'ssl_key_type'] as $option) {
|
||||||
|
if (
|
||||||
|
\array_key_exists($option, $options)
|
||||||
|
&& !\is_string($options[$option])
|
||||||
|
&& self::canStringifyDeprecatedValue($options[$option])
|
||||||
|
) {
|
||||||
|
$options[$option] = self::stringifyDeprecatedValue($options[$option]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
private static function normalizeDeprecatedNumericOptionValues(array &$options): void
|
||||||
|
{
|
||||||
|
foreach (['connect_timeout', 'delay', 'read_timeout', 'timeout'] as $option) {
|
||||||
|
if (
|
||||||
|
\array_key_exists($option, $options)
|
||||||
|
&& \is_string($options[$option])
|
||||||
|
&& \is_numeric($options[$option])
|
||||||
|
) {
|
||||||
|
$options[$option] = $options[$option] + 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
private static function normalizeDeprecatedIntegerOptionValues(array &$options): void
|
||||||
|
{
|
||||||
|
foreach (['crypto_method', 'crypto_method_max', 'retries'] as $option) {
|
||||||
|
if (!\array_key_exists($option, $options)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_string($options[$option]) && \preg_match('/^-?\d+$/D', $options[$option]) === 1) {
|
||||||
|
$options[$option] = (int) $options[$option];
|
||||||
|
} elseif (
|
||||||
|
\is_float($options[$option])
|
||||||
|
&& \is_finite($options[$option])
|
||||||
|
&& $options[$option] === (float) (int) $options[$option]
|
||||||
|
) {
|
||||||
|
$options[$option] = (int) $options[$option];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function warnAboutRequestLevelHandler(array $options): void
|
||||||
|
{
|
||||||
|
if (!\array_key_exists('handler', $options)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
\trigger_deprecation(
|
||||||
|
'guzzlehttp/guzzle',
|
||||||
|
'7.12',
|
||||||
|
'Passing the "handler" request option is deprecated; guzzlehttp/guzzle 8.0 will ignore request-level handlers. Configure the handler when creating the Client, or use a separate Client instance for requests that need a different handler.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function warnAboutInvalidRequestOptionTypes(array $options): void
|
private static function warnAboutInvalidRequestOptionTypes(array $options): void
|
||||||
|
|
@ -374,6 +595,8 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
|
|
||||||
if (isset($options['allow_redirects']) && \is_array($options['allow_redirects'])) {
|
if (isset($options['allow_redirects']) && \is_array($options['allow_redirects'])) {
|
||||||
self::warnAboutInvalidAllowRedirectsOptionTypes($options['allow_redirects']);
|
self::warnAboutInvalidAllowRedirectsOptionTypes($options['allow_redirects']);
|
||||||
|
} elseif (isset($options['allow_redirects']) && !\is_bool($options['allow_redirects'])) {
|
||||||
|
self::warnInvalidRequestOptionType('allow_redirects', 'bool|array', $options['allow_redirects'], '7.13');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($options['auth'])) {
|
if (isset($options['auth'])) {
|
||||||
|
|
@ -388,9 +611,16 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
self::warnIfPresentAndNotString($options, 'cert_type');
|
self::warnIfPresentAndNotString($options, 'cert_type');
|
||||||
self::warnIfPresentAndNotNumber($options, 'connect_timeout');
|
self::warnIfPresentAndNotNumber($options, 'connect_timeout');
|
||||||
self::warnIfPresentAndNotInt($options, 'crypto_method');
|
self::warnIfPresentAndNotInt($options, 'crypto_method');
|
||||||
|
self::warnIfPresentAndNotInt($options, 'crypto_method_max', null, '7.13');
|
||||||
self::warnIfPresentAndNotBoolOrResource($options, 'debug');
|
self::warnIfPresentAndNotBoolOrResource($options, 'debug');
|
||||||
self::warnIfPresentAndNotBoolOrString($options, 'decode_content');
|
self::warnIfPresentAndNotBoolOrString($options, 'decode_content');
|
||||||
self::warnIfPresentAndNotNumber($options, 'delay');
|
self::warnIfPresentAndNotNumber($options, 'delay');
|
||||||
|
if (isset($options['delay']) && \is_numeric($options['delay'])) {
|
||||||
|
$delay = (float) $options['delay'];
|
||||||
|
if (!\is_finite($delay) || $delay < 0.0) {
|
||||||
|
self::warnInvalidRequestOptionType('delay', 'finite int|float greater than or equal to 0', $options['delay'], '7.13');
|
||||||
|
}
|
||||||
|
}
|
||||||
self::warnIfPresentAndNotBoolOrInt($options, 'expect');
|
self::warnIfPresentAndNotBoolOrInt($options, 'expect');
|
||||||
|
|
||||||
if (isset($options['form_params'])) {
|
if (isset($options['form_params'])) {
|
||||||
|
|
@ -401,6 +631,15 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
self::warnInvalidRequestOptionType('force_ip_resolve', 'string', $options['force_ip_resolve']);
|
self::warnInvalidRequestOptionType('force_ip_resolve', 'string', $options['force_ip_resolve']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isset($options['force_ip_resolve'])
|
||||||
|
&& \is_string($options['force_ip_resolve'])
|
||||||
|
&& $options['force_ip_resolve'] !== 'v4'
|
||||||
|
&& $options['force_ip_resolve'] !== 'v6'
|
||||||
|
) {
|
||||||
|
self::warnInvalidRequestOptionType('force_ip_resolve', '"v4"|"v6"', $options['force_ip_resolve'], '7.13');
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($options['headers'])) {
|
if (isset($options['headers'])) {
|
||||||
self::warnAboutInvalidHeaderOptionTypes($options['headers']);
|
self::warnAboutInvalidHeaderOptionTypes($options['headers']);
|
||||||
}
|
}
|
||||||
|
|
@ -413,8 +652,10 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
|
|
||||||
self::warnIfPresentAndNotCallable($options, 'on_headers');
|
self::warnIfPresentAndNotCallable($options, 'on_headers');
|
||||||
self::warnIfPresentAndNotCallable($options, 'on_stats');
|
self::warnIfPresentAndNotCallable($options, 'on_stats');
|
||||||
|
self::warnIfPresentAndNotCallable($options, 'on_trailers', null, '7.14');
|
||||||
self::warnIfPresentAndNotCallable($options, 'progress');
|
self::warnIfPresentAndNotCallable($options, 'progress');
|
||||||
self::warnIfPresentAndNotStringArray($options, 'protocols', true);
|
self::warnIfPresentAndNotStringArray($options, 'protocols', true);
|
||||||
|
self::warnAboutInvalidProtocolValues($options, 'protocols');
|
||||||
self::warnAboutInvalidProxyOptionTypes($options);
|
self::warnAboutInvalidProxyOptionTypes($options);
|
||||||
|
|
||||||
self::warnIfPresentAndNotNumber($options, 'read_timeout');
|
self::warnIfPresentAndNotNumber($options, 'read_timeout');
|
||||||
|
|
@ -437,6 +678,15 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
if (isset($options['cookies']) && $options['cookies'] === true) {
|
if (isset($options['cookies']) && $options['cookies'] === true) {
|
||||||
self::warnInvalidRequestOptionType('cookies', 'false|CookieJarInterface', $options['cookies']);
|
self::warnInvalidRequestOptionType('cookies', 'false|CookieJarInterface', $options['cookies']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isset($options['cookies'])
|
||||||
|
&& $options['cookies'] !== false
|
||||||
|
&& $options['cookies'] !== true
|
||||||
|
&& !($options['cookies'] instanceof CookieJarInterface)
|
||||||
|
) {
|
||||||
|
self::warnInvalidRequestOptionType('cookies', 'false|CookieJarInterface', $options['cookies'], '7.13');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function warnAboutInvalidAllowRedirectsOptionTypes(array $allowRedirects): void
|
private static function warnAboutInvalidAllowRedirectsOptionTypes(array $allowRedirects): void
|
||||||
|
|
@ -445,6 +695,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
self::warnIfPresentAndNotBool($allowRedirects, 'strict', 'allow_redirects.strict');
|
self::warnIfPresentAndNotBool($allowRedirects, 'strict', 'allow_redirects.strict');
|
||||||
self::warnIfPresentAndNotBool($allowRedirects, 'referer', 'allow_redirects.referer');
|
self::warnIfPresentAndNotBool($allowRedirects, 'referer', 'allow_redirects.referer');
|
||||||
self::warnIfPresentAndNotStringArray($allowRedirects, 'protocols', true, 'allow_redirects.protocols');
|
self::warnIfPresentAndNotStringArray($allowRedirects, 'protocols', true, 'allow_redirects.protocols');
|
||||||
|
self::warnAboutInvalidProtocolValues($allowRedirects, 'protocols', 'allow_redirects.protocols');
|
||||||
self::warnIfPresentAndNotCallable($allowRedirects, 'on_redirect', 'allow_redirects.on_redirect');
|
self::warnIfPresentAndNotCallable($allowRedirects, 'on_redirect', 'allow_redirects.on_redirect');
|
||||||
self::warnIfPresentAndNotBool($allowRedirects, 'track_redirects', 'allow_redirects.track_redirects');
|
self::warnIfPresentAndNotBool($allowRedirects, 'track_redirects', 'allow_redirects.track_redirects');
|
||||||
}
|
}
|
||||||
|
|
@ -700,17 +951,25 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function warnIfPresentAndNotCallable(array $options, string $option, ?string $path = null): void
|
private static function warnIfPresentAndNotCallable(
|
||||||
{
|
array $options,
|
||||||
|
string $option,
|
||||||
|
?string $path = null,
|
||||||
|
string $since = '7.11'
|
||||||
|
): void {
|
||||||
if (\array_key_exists($option, $options) && !\is_callable($options[$option])) {
|
if (\array_key_exists($option, $options) && !\is_callable($options[$option])) {
|
||||||
self::warnInvalidRequestOptionType($path ?? $option, 'callable', $options[$option]);
|
self::warnInvalidRequestOptionType($path ?? $option, 'callable', $options[$option], $since);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function warnIfPresentAndNotInt(array $options, string $option, ?string $path = null): void
|
private static function warnIfPresentAndNotInt(
|
||||||
{
|
array $options,
|
||||||
|
string $option,
|
||||||
|
?string $path = null,
|
||||||
|
string $since = '7.11'
|
||||||
|
): void {
|
||||||
if (\array_key_exists($option, $options) && !\is_int($options[$option])) {
|
if (\array_key_exists($option, $options) && !\is_int($options[$option])) {
|
||||||
self::warnInvalidRequestOptionType($path ?? $option, 'int', $options[$option]);
|
self::warnInvalidRequestOptionType($path ?? $option, 'int', $options[$option], $since);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -752,6 +1011,24 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<array-key, mixed> $options
|
||||||
|
*/
|
||||||
|
private static function warnAboutInvalidProtocolValues(array $options, string $option, ?string $path = null): void
|
||||||
|
{
|
||||||
|
if (!isset($options[$option]) || !\is_array($options[$option])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = $path ?? $option;
|
||||||
|
|
||||||
|
foreach ($options[$option] as $index => $protocol) {
|
||||||
|
if (\is_string($protocol) && $protocol !== 'http' && $protocol !== 'https') {
|
||||||
|
self::warnInvalidRequestOptionType($path.'.'.(string) $index, '"http"|"https"', $protocol, '7.13');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static function warnIfPresentAndNotStringOrNumber(array $options, string $option): void
|
private static function warnIfPresentAndNotStringOrNumber(array $options, string $option): void
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
|
|
@ -767,11 +1044,11 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
/**
|
/**
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
*/
|
*/
|
||||||
private static function warnInvalidRequestOptionType(string $option, string $expected, $value): void
|
private static function warnInvalidRequestOptionType(string $option, string $expected, $value, string $since = '7.11'): void
|
||||||
{
|
{
|
||||||
\trigger_deprecation(
|
\trigger_deprecation(
|
||||||
'guzzlehttp/guzzle',
|
'guzzlehttp/guzzle',
|
||||||
'7.11',
|
$since,
|
||||||
'Passing %s to request option "%s" is deprecated; guzzlehttp/guzzle 8.0 requires %s.',
|
'Passing %s to request option "%s" is deprecated; guzzlehttp/guzzle 8.0 requires %s.',
|
||||||
\get_debug_type($value),
|
\get_debug_type($value),
|
||||||
$option,
|
$option,
|
||||||
|
|
@ -841,7 +1118,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
.'x-www-form-urlencoded requests, and the multipart '
|
.'x-www-form-urlencoded requests, and the multipart '
|
||||||
.'option to send multipart/form-data requests.');
|
.'option to send multipart/form-data requests.');
|
||||||
}
|
}
|
||||||
$options['body'] = \http_build_query($options['form_params'], '', '&');
|
$options['body'] = \http_build_query(self::normalizeNonFiniteFloats($options['form_params'], 'form_params'), '', '&');
|
||||||
unset($options['form_params']);
|
unset($options['form_params']);
|
||||||
// Ensure that we don't have the header in different case and set the new value.
|
// Ensure that we don't have the header in different case and set the new value.
|
||||||
$options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
|
$options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
|
||||||
|
|
@ -854,16 +1131,20 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($options['json'])) {
|
if (isset($options['json'])) {
|
||||||
$options['body'] = Utils::jsonEncode($options['json']);
|
$json = \json_encode($options['json']);
|
||||||
|
if (\JSON_ERROR_NONE !== \json_last_error()) {
|
||||||
|
throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var non-empty-string $json */
|
||||||
|
$options['body'] = $json;
|
||||||
unset($options['json']);
|
unset($options['json']);
|
||||||
// Ensure that we don't have the header in different case and set the new value.
|
// Ensure that we don't have the header in different case and set the new value.
|
||||||
$options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
|
$options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
|
||||||
$options['_conditional']['Content-Type'] = 'application/json';
|
$options['_conditional']['Content-Type'] = 'application/json';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($options['decode_content'])
|
if (isset($options['decode_content']) && \is_string($options['decode_content'])) {
|
||||||
&& $options['decode_content'] !== true
|
|
||||||
) {
|
|
||||||
// Ensure that we don't have the header in different case and set the new value.
|
// Ensure that we don't have the header in different case and set the new value.
|
||||||
$options['_conditional'] = Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']);
|
$options['_conditional'] = Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']);
|
||||||
$modify['set_headers']['Accept-Encoding'] = (string) $options['decode_content'];
|
$modify['set_headers']['Accept-Encoding'] = (string) $options['decode_content'];
|
||||||
|
|
@ -873,13 +1154,13 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
if (\is_array($options['body'])) {
|
if (\is_array($options['body'])) {
|
||||||
throw $this->invalidBody();
|
throw $this->invalidBody();
|
||||||
}
|
}
|
||||||
$modify['body'] = Psr7\Utils::streamFor($options['body']);
|
$modify['body'] = self::createBodyStream($options['body']);
|
||||||
unset($options['body']);
|
unset($options['body']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($options['auth']) && \is_array($options['auth'])) {
|
if (!empty($options['auth']) && \is_array($options['auth'])) {
|
||||||
$value = $options['auth'];
|
$value = $options['auth'];
|
||||||
$type = isset($value[2]) ? \strtolower($value[2]) : 'basic';
|
$type = isset($value[2]) ? Psr7\Utils::asciiToLower($value[2]) : 'basic';
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'basic':
|
case 'basic':
|
||||||
// Ensure that we don't have the header in different case and set the new value.
|
// Ensure that we don't have the header in different case and set the new value.
|
||||||
|
|
@ -893,6 +1174,14 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
$options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]";
|
$options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]";
|
||||||
break;
|
break;
|
||||||
case 'ntlm':
|
case 'ntlm':
|
||||||
|
\trigger_deprecation(
|
||||||
|
'guzzlehttp/guzzle',
|
||||||
|
'7.12',
|
||||||
|
'Passing "ntlm" as the built-in auth type is deprecated; guzzlehttp/guzzle 8.0 will no longer apply NTLM through the "auth" request option. NTLM is also deprecated by curl/libcurl and may be unavailable in current or future libcurl builds. Avoid NTLM; if you must use it temporarily, configure cURL HTTP authentication options directly with a libcurl build that still supports NTLM.'
|
||||||
|
);
|
||||||
|
if (!CurlVersion::supportsNtlm()) {
|
||||||
|
throw new InvalidArgumentException('NTLM authentication is not available because the installed curl/libcurl build does not provide NTLM support.');
|
||||||
|
}
|
||||||
$options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM;
|
$options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM;
|
||||||
$options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]";
|
$options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]";
|
||||||
break;
|
break;
|
||||||
|
|
@ -902,7 +1191,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
if (isset($options['query'])) {
|
if (isset($options['query'])) {
|
||||||
$value = $options['query'];
|
$value = $options['query'];
|
||||||
if (\is_array($value)) {
|
if (\is_array($value)) {
|
||||||
$value = \http_build_query($value, '', '&', \PHP_QUERY_RFC3986);
|
$value = \http_build_query(self::normalizeNonFiniteFloats($value, 'query'), '', '&', \PHP_QUERY_RFC3986);
|
||||||
}
|
}
|
||||||
if (!\is_string($value)) {
|
if (!\is_string($value)) {
|
||||||
throw new InvalidArgumentException('query must be a string or array');
|
throw new InvalidArgumentException('query must be a string or array');
|
||||||
|
|
@ -970,6 +1259,9 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
|
|
||||||
foreach ($value as $index => $item) {
|
foreach ($value as $index => $item) {
|
||||||
if ($item === null || (!\is_string($item) && \is_scalar($item))) {
|
if ($item === null || (!\is_string($item) && \is_scalar($item))) {
|
||||||
|
if (\is_float($item) && !\is_finite($item)) {
|
||||||
|
$item = \is_nan($item) ? 'NAN' : ($item > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
$value[$index] = (string) $item;
|
$value[$index] = (string) $item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -980,6 +1272,9 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($value === null || (!\is_string($value) && \is_scalar($value))) {
|
if ($value === null || (!\is_string($value) && \is_scalar($value))) {
|
||||||
|
if (\is_float($value) && !\is_finite($value)) {
|
||||||
|
$value = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
$headers[$name] = (string) $value;
|
$headers[$name] = (string) $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -987,6 +1282,72 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
|
||||||
return $droppedHeaderNames;
|
return $droppedHeaderNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $body
|
||||||
|
*/
|
||||||
|
private static function createBodyStream($body): StreamInterface
|
||||||
|
{
|
||||||
|
if ($body instanceof StreamInterface) {
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_resource($body) || $body === null || \is_string($body) || $body instanceof \Iterator) {
|
||||||
|
return Psr7\Utils::streamFor($body);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_scalar($body)) {
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.12', 'Passing a non-string scalar to the "body" request option is deprecated; guzzlehttp/guzzle 8.0 will reject non-string scalar bodies.');
|
||||||
|
|
||||||
|
return Psr7\Utils::streamFor(self::stringifyScalar($body));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_object($body) && \method_exists($body, '__toString')) {
|
||||||
|
return Psr7\Utils::streamFor((string) $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_callable($body)) {
|
||||||
|
return Psr7\Utils::streamFor($body);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgumentException(\sprintf(
|
||||||
|
'Passing %s to request option "body" is invalid; expected resource|string|null|int|float|bool|StreamInterface|callable&object|Iterator|Stringable.',
|
||||||
|
\get_debug_type($body)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool|float|int|string $value
|
||||||
|
*/
|
||||||
|
private static function stringifyScalar($value): string
|
||||||
|
{
|
||||||
|
// Normalize non-finite floats to dodge PHP 8.5's (string) NAN
|
||||||
|
// coercion warning while the value is still accepted.
|
||||||
|
if (\is_float($value) && !\is_finite($value)) {
|
||||||
|
$value = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts non-finite floats in the array to the strings PHP coerces
|
||||||
|
* them to, as implicit coercion of NAN emits a warning on PHP 8.5.
|
||||||
|
*/
|
||||||
|
private static function normalizeNonFiniteFloats(array $values, string $option): array
|
||||||
|
{
|
||||||
|
foreach ($values as $key => $value) {
|
||||||
|
if (\is_array($value)) {
|
||||||
|
$values[$key] = self::normalizeNonFiniteFloats($value, $option);
|
||||||
|
} elseif (\is_float($value) && !\is_finite($value)) {
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.12', 'Passing a non-finite float in the "%s" request option is deprecated; guzzlehttp/guzzle 8.0 will reject non-finite floats.', $option);
|
||||||
|
|
||||||
|
$values[$key] = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|int|float $version
|
* @param string|int|float $version
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace GuzzleHttp\Cookie;
|
namespace GuzzleHttp\Cookie;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
|
@ -88,7 +89,7 @@ class CookieJar implements CookieJarInterface
|
||||||
public function getCookieByName(string $name): ?SetCookie
|
public function getCookieByName(string $name): ?SetCookie
|
||||||
{
|
{
|
||||||
foreach ($this->cookies as $cookie) {
|
foreach ($this->cookies as $cookie) {
|
||||||
if ($cookie->getName() !== null && \strcasecmp($cookie->getName(), $name) === 0) {
|
if ($cookie->getName() !== null && Psr7\Utils::caselessEquals($cookie->getName(), $name)) {
|
||||||
return $cookie;
|
return $cookie;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,14 +110,14 @@ class CookieJar implements CookieJarInterface
|
||||||
$this->cookies = [];
|
$this->cookies = [];
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} elseif (!$path) {
|
} elseif ($path === null) {
|
||||||
$this->cookies = \array_filter(
|
$this->cookies = \array_filter(
|
||||||
$this->cookies,
|
$this->cookies,
|
||||||
static function (SetCookie $cookie) use ($domain): bool {
|
static function (SetCookie $cookie) use ($domain): bool {
|
||||||
return $cookie->getDomain() === null || !$cookie->matchesDomain($domain);
|
return $cookie->getDomain() === null || !$cookie->matchesDomain($domain);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} elseif (!$name) {
|
} elseif ($name === null) {
|
||||||
$this->cookies = \array_filter(
|
$this->cookies = \array_filter(
|
||||||
$this->cookies,
|
$this->cookies,
|
||||||
static function (SetCookie $cookie) use ($path, $domain): bool {
|
static function (SetCookie $cookie) use ($path, $domain): bool {
|
||||||
|
|
@ -130,7 +131,7 @@ class CookieJar implements CookieJarInterface
|
||||||
$this->cookies,
|
$this->cookies,
|
||||||
static function (SetCookie $cookie) use ($path, $domain, $name) {
|
static function (SetCookie $cookie) use ($path, $domain, $name) {
|
||||||
return !($cookie->getDomain() !== null
|
return !($cookie->getDomain() !== null
|
||||||
&& $cookie->getName() == $name
|
&& $cookie->getName() === $name
|
||||||
&& $cookie->matchesPath($path)
|
&& $cookie->matchesPath($path)
|
||||||
&& $cookie->matchesDomain($domain));
|
&& $cookie->matchesDomain($domain));
|
||||||
}
|
}
|
||||||
|
|
@ -168,13 +169,22 @@ class CookieJar implements CookieJarInterface
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$maxAge = $cookie->getMaxAge();
|
||||||
|
if ($maxAge !== null && $maxAge <= 0) {
|
||||||
|
if ($cookie->getDomain() !== null) {
|
||||||
|
$this->clear($cookie->getDomain(), $cookie->getPath(), $cookie->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve conflicts with previously set cookies
|
// Resolve conflicts with previously set cookies
|
||||||
foreach ($this->cookies as $i => $c) {
|
foreach ($this->cookies as $i => $c) {
|
||||||
// Two cookies are identical, when their path, and domain are
|
// Two cookies are identical, when their path, and domain are
|
||||||
// identical.
|
// identical.
|
||||||
if ($c->getPath() != $cookie->getPath()
|
if ($c->getPath() !== $cookie->getPath()
|
||||||
|| $c->getDomain() != $cookie->getDomain()
|
|| $c->getDomain() !== $cookie->getDomain()
|
||||||
|| $c->getName() != $cookie->getName()
|
|| $c->getName() !== $cookie->getName()
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -226,7 +236,11 @@ class CookieJar implements CookieJarInterface
|
||||||
if ($cookieHeader = $response->getHeader('Set-Cookie')) {
|
if ($cookieHeader = $response->getHeader('Set-Cookie')) {
|
||||||
foreach ($cookieHeader as $cookie) {
|
foreach ($cookieHeader as $cookie) {
|
||||||
$sc = SetCookie::fromString($cookie);
|
$sc = SetCookie::fromString($cookie);
|
||||||
if (!$sc->getDomain()) {
|
$domain = $sc->getDomain();
|
||||||
|
if ($domain === null || $domain === '') {
|
||||||
|
$sc->setDomain($request->getUri()->getHost());
|
||||||
|
} elseif (\substr($domain, -1) === '.' && '' !== \trim($domain, '.')) {
|
||||||
|
// Keep pure-dot domains rejected by the dot-only fix.
|
||||||
$sc->setDomain($request->getUri()->getHost());
|
$sc->setDomain($request->getUri()->getHost());
|
||||||
}
|
}
|
||||||
if (0 !== \strpos($sc->getPath(), '/')) {
|
if (0 !== \strpos($sc->getPath(), '/')) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace GuzzleHttp\Cookie;
|
namespace GuzzleHttp\Cookie;
|
||||||
|
|
||||||
use GuzzleHttp\Utils;
|
use GuzzleHttp\Exception\InvalidArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persists non-session cookies using a JSON formatted file
|
* Persists non-session cookies using a JSON formatted file
|
||||||
|
|
@ -64,7 +64,12 @@ class FileCookieJar extends CookieJar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$jsonStr = Utils::jsonEncode($json);
|
$jsonStr = \json_encode($json);
|
||||||
|
if (\JSON_ERROR_NONE !== \json_last_error()) {
|
||||||
|
throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var non-empty-string $jsonStr */
|
||||||
if (false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) {
|
if (false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) {
|
||||||
throw new \RuntimeException("Unable to save file {$filename}");
|
throw new \RuntimeException("Unable to save file {$filename}");
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +94,11 @@ class FileCookieJar extends CookieJar
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = Utils::jsonDecode($json, true);
|
$data = \json_decode($json, true);
|
||||||
|
if (\JSON_ERROR_NONE !== \json_last_error()) {
|
||||||
|
throw new InvalidArgumentException('json_decode error: '.\json_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
if (\is_array($data)) {
|
if (\is_array($data)) {
|
||||||
foreach ($data as $cookie) {
|
foreach ($data as $cookie) {
|
||||||
$this->setCookie(new SetCookie($cookie));
|
$this->setCookie(new SetCookie($cookie));
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace GuzzleHttp\Cookie;
|
namespace GuzzleHttp\Cookie;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set-Cookie object
|
* Set-Cookie object
|
||||||
*/
|
*/
|
||||||
|
|
@ -37,7 +39,9 @@ class SetCookie
|
||||||
// Create the default return array
|
// Create the default return array
|
||||||
$data = self::$defaults;
|
$data = self::$defaults;
|
||||||
// Explode the cookie string using a series of semicolons
|
// Explode the cookie string using a series of semicolons
|
||||||
$pieces = \array_filter(\array_map('trim', \explode(';', $cookie)));
|
$pieces = \array_filter(\array_map(static function (string $piece): string {
|
||||||
|
return \trim($piece, " \n\r\t\0\x0B");
|
||||||
|
}, \explode(';', $cookie)));
|
||||||
// The name of the cookie (first kvp) must exist and include an equal sign.
|
// The name of the cookie (first kvp) must exist and include an equal sign.
|
||||||
if (!isset($pieces[0]) || \strpos($pieces[0], '=') === false) {
|
if (!isset($pieces[0]) || \strpos($pieces[0], '=') === false) {
|
||||||
return new self($data);
|
return new self($data);
|
||||||
|
|
@ -46,7 +50,7 @@ class SetCookie
|
||||||
// Add the cookie pieces into the parsed data array
|
// Add the cookie pieces into the parsed data array
|
||||||
foreach ($pieces as $part) {
|
foreach ($pieces as $part) {
|
||||||
$cookieParts = \explode('=', $part, 2);
|
$cookieParts = \explode('=', $part, 2);
|
||||||
$key = \trim($cookieParts[0]);
|
$key = \trim($cookieParts[0], " \n\r\t\0\x0B");
|
||||||
$value = isset($cookieParts[1])
|
$value = isset($cookieParts[1])
|
||||||
? \trim($cookieParts[1], " \n\r\t\0\x0B")
|
? \trim($cookieParts[1], " \n\r\t\0\x0B")
|
||||||
: true;
|
: true;
|
||||||
|
|
@ -57,7 +61,7 @@ class SetCookie
|
||||||
$data['Value'] = $value;
|
$data['Value'] = $value;
|
||||||
} else {
|
} else {
|
||||||
foreach (\array_keys(self::$defaults) as $search) {
|
foreach (\array_keys(self::$defaults) as $search) {
|
||||||
if (!\strcasecmp($search, $key)) {
|
if (Psr7\Utils::caselessEquals($search, $key)) {
|
||||||
if ($search === 'Max-Age') {
|
if ($search === 'Max-Age') {
|
||||||
if (is_numeric($value)) {
|
if (is_numeric($value)) {
|
||||||
$data[$search] = (int) $value;
|
$data[$search] = (int) $value;
|
||||||
|
|
@ -128,14 +132,27 @@ class SetCookie
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the Expires value and turn it into a UNIX timestamp if needed
|
// Extract the Expires value and turn it into a UNIX timestamp if needed
|
||||||
if (!$this->getExpires() && $this->getMaxAge()) {
|
$maxAge = $this->getMaxAge();
|
||||||
|
if (!$this->getExpires() && $maxAge !== null) {
|
||||||
// Calculate the Expires date
|
// Calculate the Expires date
|
||||||
$this->setExpires(\time() + $this->getMaxAge());
|
$this->setExpires(self::maxAgeToExpires($maxAge, \time()));
|
||||||
} elseif (null !== ($expires = $this->getExpires()) && !\is_numeric($expires)) {
|
} elseif (null !== ($expires = $this->getExpires()) && !\is_numeric($expires)) {
|
||||||
$this->setExpires($expires);
|
$this->setExpires($expires);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function maxAgeToExpires(int $maxAge, int $now): int
|
||||||
|
{
|
||||||
|
if ($maxAge <= 0) {
|
||||||
|
return $now - 1;
|
||||||
|
}
|
||||||
|
if ($maxAge > \PHP_INT_MAX - $now) {
|
||||||
|
return \PHP_INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $now + $maxAge;
|
||||||
|
}
|
||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
$str = $this->data['Name'].'='.($this->data['Value'] ?? '').'; ';
|
$str = $this->data['Name'].'='.($this->data['Value'] ?? '').'; ';
|
||||||
|
|
@ -401,7 +418,7 @@ class SetCookie
|
||||||
$cookiePath = $this->getPath();
|
$cookiePath = $this->getPath();
|
||||||
|
|
||||||
// Match on exact matches or when path is the default empty "/"
|
// Match on exact matches or when path is the default empty "/"
|
||||||
if ($cookiePath === '/' || $cookiePath == $requestPath) {
|
if ($cookiePath === '/' || $cookiePath === $requestPath) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -433,22 +450,57 @@ class SetCookie
|
||||||
|
|
||||||
// Remove the leading '.' as per spec in RFC 6265.
|
// Remove the leading '.' as per spec in RFC 6265.
|
||||||
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3
|
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3
|
||||||
$cookieDomain = \ltrim(\strtolower($cookieDomain), '.');
|
$cookieDomain = Psr7\Utils::asciiToLower($cookieDomain);
|
||||||
|
if ($cookieDomain !== '' && $cookieDomain[0] === '.') {
|
||||||
|
/** @var string */
|
||||||
|
$cookieDomain = \substr($cookieDomain, 1);
|
||||||
|
}
|
||||||
|
if ('' === $cookieDomain) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$domain = \strtolower($domain);
|
$domain = Psr7\Utils::asciiToLower($domain);
|
||||||
|
if ($domain === $cookieDomain) {
|
||||||
// Domain not set or exact match.
|
|
||||||
if ('' === $cookieDomain || $domain === $cookieDomain) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IP literals and numeric hosts are exact-match-only per RFC 6265.
|
||||||
|
// Only the exact match above may succeed for those cookie domains.
|
||||||
|
if (self::isIpAddressOrNumericHost($cookieDomain)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Matching the subdomain according to RFC 6265.
|
// Matching the subdomain according to RFC 6265.
|
||||||
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
|
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
|
||||||
if (\filter_var($domain, \FILTER_VALIDATE_IP)) {
|
if (\filter_var($domain, \FILTER_VALIDATE_IP)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (bool) \preg_match('/\.'.\preg_quote($cookieDomain, '/').'$/', $domain);
|
return (bool) \preg_match('/\.'.\preg_quote($cookieDomain, '/').'$/D', $domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function isIpAddressOrNumericHost(string $host): bool
|
||||||
|
{
|
||||||
|
// Strip one root dot before detection so trailing-dot numeric hosts
|
||||||
|
// still cannot be matched by subdomains.
|
||||||
|
if ($host !== '' && \str_ends_with($host, '.')) {
|
||||||
|
$host = \substr($host, 0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\str_starts_with($host, '[') && \str_ends_with($host, ']')) {
|
||||||
|
$host = \substr($host, 1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\filter_var($host, \FILTER_VALIDATE_IP) !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public DNS names do not have an all-numeric rightmost label; treat
|
||||||
|
// those private/internal hosts as exact-match-only too.
|
||||||
|
$labels = \explode('.', $host);
|
||||||
|
$last = (string) \end($labels);
|
||||||
|
|
||||||
|
return $last !== '' && \ctype_digit($last);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -472,10 +524,7 @@ class SetCookie
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any of the invalid characters are present in the cookie name
|
// Check if any of the invalid characters are present in the cookie name
|
||||||
if (\preg_match(
|
if (\preg_match('/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/', $name) !== 0) {
|
||||||
'/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/',
|
|
||||||
$name
|
|
||||||
)) {
|
|
||||||
return 'Cookie name must not contain invalid characters: ASCII '
|
return 'Cookie name must not contain invalid characters: ASCII '
|
||||||
.'Control characters (0-31;127), space, tab and the '
|
.'Control characters (0-31;127), space, tab and the '
|
||||||
.'following characters: ()<>@,;:\"/?={}';
|
.'following characters: ()<>@,;:\"/?={}';
|
||||||
|
|
@ -491,7 +540,7 @@ class SetCookie
|
||||||
// Domains must not be empty, but may be omitted. "0" is not a valid
|
// Domains must not be empty, but may be omitted. "0" is not a valid
|
||||||
// internet domain, but may be used as server name in a private network.
|
// internet domain, but may be used as server name in a private network.
|
||||||
$domain = $this->getDomain();
|
$domain = $this->getDomain();
|
||||||
if ($domain === '') {
|
if ($domain === '' || (null !== $domain && '' === \ltrim(\trim($domain, " \n\r\t\0\x0B"), '.'))) {
|
||||||
return 'The cookie domain must not be empty';
|
return 'The cookie domain must not be empty';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1804
vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
vendored
1804
vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -17,6 +17,11 @@ use Psr\Http\Message\RequestInterface;
|
||||||
*/
|
*/
|
||||||
class CurlHandler
|
class CurlHandler
|
||||||
{
|
{
|
||||||
|
private const KNOWN_CONSTRUCTOR_OPTIONS = [
|
||||||
|
'handle_factory' => true,
|
||||||
|
'transport_sharing' => true,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var CurlFactoryInterface
|
* @var CurlFactoryInterface
|
||||||
*/
|
*/
|
||||||
|
|
@ -37,6 +42,12 @@ class CurlHandler
|
||||||
*/
|
*/
|
||||||
public function __construct(array $options = [])
|
public function __construct(array $options = [])
|
||||||
{
|
{
|
||||||
|
foreach ($options as $name => $_) {
|
||||||
|
if (!isset(self::KNOWN_CONSTRUCTOR_OPTIONS[$name])) {
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.14', \sprintf('The "%s" CurlHandler constructor option is unknown; guzzlehttp/guzzle 8.0 will reject unknown constructor options.', (string) $name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CurlShareHandleState::assertNoRequiredSharingCustomFactoryConflict($options, 'CurlHandler');
|
CurlShareHandleState::assertNoRequiredSharingCustomFactoryConflict($options, 'CurlHandler');
|
||||||
$transportSharing = $options['transport_sharing'] ?? null;
|
$transportSharing = $options['transport_sharing'] ?? null;
|
||||||
$sharingMode = CurlShareHandleState::normalizeMode($transportSharing, 'transport_sharing');
|
$sharingMode = CurlShareHandleState::normalizeMode($transportSharing, 'transport_sharing');
|
||||||
|
|
@ -53,7 +64,7 @@ class CurlHandler
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
$this->factory = $this->shareHandleState !== null
|
$this->factory = $this->shareHandleState !== null
|
||||||
? new CurlFactory(3, $this->shareHandleState->mode, $this->shareHandleState->handle)
|
? new CurlFactory(3, $this->shareHandleState->mode, $this->shareHandleState)
|
||||||
: new CurlFactory(3);
|
: new CurlFactory(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,6 +74,11 @@ class CurlHandler
|
||||||
\usleep($options['delay'] * 1000);
|
\usleep($options['delay'] * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A Multiplexing::NONE request option holds unconditionally here:
|
||||||
|
// transport sharing never shares the connection cache on this
|
||||||
|
// branch, and nothing else executes during the blocking curl_exec(),
|
||||||
|
// so the transfer cannot share its connection with a concurrent
|
||||||
|
// transfer.
|
||||||
$easy = $this->factory->create($request, $options);
|
$easy = $this->factory->create($request, $options);
|
||||||
\curl_exec($easy->handle);
|
\curl_exec($easy->handle);
|
||||||
$easy->errno = \curl_errno($easy->handle);
|
$easy->errno = \curl_errno($easy->handle);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -3,7 +3,6 @@
|
||||||
namespace GuzzleHttp\Handler;
|
namespace GuzzleHttp\Handler;
|
||||||
|
|
||||||
use GuzzleHttp\TransportSharing;
|
use GuzzleHttp\TransportSharing;
|
||||||
use GuzzleHttp\Utils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
|
@ -70,7 +69,7 @@ final class CurlShareHandleState
|
||||||
throw new \InvalidArgumentException(\sprintf(
|
throw new \InvalidArgumentException(\sprintf(
|
||||||
'The "%s" option must be null or a GuzzleHttp\\TransportSharing::* constant; received %s.',
|
'The "%s" option must be null or a GuzzleHttp\\TransportSharing::* constant; received %s.',
|
||||||
$option,
|
$option,
|
||||||
Utils::describeType($sharing)
|
\get_debug_type($sharing)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,7 +107,7 @@ final class CurlShareHandleState
|
||||||
|
|
||||||
self::requireCurlConstant('CURLOPT_SHARE');
|
self::requireCurlConstant('CURLOPT_SHARE');
|
||||||
$shareOption = self::requireCurlConstant('CURLSHOPT_SHARE');
|
$shareOption = self::requireCurlConstant('CURLSHOPT_SHARE');
|
||||||
$locks = self::handlerLocks();
|
$locks = self::handlerLocks($mode);
|
||||||
$handle = curl_share_init();
|
$handle = curl_share_init();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -135,12 +134,23 @@ final class CurlShareHandleState
|
||||||
/**
|
/**
|
||||||
* @return int[]
|
* @return int[]
|
||||||
*/
|
*/
|
||||||
private static function handlerLocks(): array
|
private static function handlerLocks(string $mode): array
|
||||||
{
|
{
|
||||||
return [
|
CurlVersion::ensureHandlerSharingSupported();
|
||||||
|
|
||||||
|
if ($mode === TransportSharing::HANDLER_REQUIRE) {
|
||||||
|
CurlVersion::ensureSslSessionSharingSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
$locks = [
|
||||||
self::requireCurlConstant('CURL_LOCK_DATA_DNS'),
|
self::requireCurlConstant('CURL_LOCK_DATA_DNS'),
|
||||||
self::requireCurlConstant('CURL_LOCK_DATA_SSL_SESSION'),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (CurlVersion::supportsSslSessionSharing()) {
|
||||||
|
$locks[] = self::requireCurlConstant('CURL_LOCK_DATA_SSL_SESSION');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $locks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function requireCurlConstant(string $constant): int
|
private static function requireCurlConstant(string $constant): int
|
||||||
|
|
|
||||||
322
vendor/guzzlehttp/guzzle/src/Handler/CurlVersion.php
vendored
Normal file
322
vendor/guzzlehttp/guzzle/src/Handler/CurlVersion.php
vendored
Normal file
|
|
@ -0,0 +1,322 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace GuzzleHttp\Handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class CurlVersion
|
||||||
|
{
|
||||||
|
private const MIN_VERSION = '7.21.2';
|
||||||
|
|
||||||
|
private const TLS_12_VERSION = '7.34.0';
|
||||||
|
|
||||||
|
private const TLS_13_VERSION = '7.52.0';
|
||||||
|
|
||||||
|
private const CONNECTION_CAP_VERSION = '7.30.0';
|
||||||
|
|
||||||
|
// CURLOPT_PIPEWAIT exists since libcurl 7.43.0, and multi handles have
|
||||||
|
// multiplexed by default since 7.62.0 - but a 7.65.0-7.65.1 regression
|
||||||
|
// dropped that default, which 7.65.2 restored, so 7.65.2 is the floor at
|
||||||
|
// which PIPEWAIT is reliably effective.
|
||||||
|
private const MULTIPLEX_VERSION = '7.65.2';
|
||||||
|
|
||||||
|
// libcurl's connection matcher refuses to hand a transfer wanting
|
||||||
|
// HTTP/1.x a pooled connection that already negotiated HTTP/2 or newer
|
||||||
|
// from 7.77.0: ConnectionExists() in lib/url.c gained the check between
|
||||||
|
// the 7.76.0 and 7.77.0 releases. The HTTP/2 branch of the check
|
||||||
|
// regressed to a debug log in 8.11.0 (curl commit 433d730) and was
|
||||||
|
// restored in 8.13.0 via the negotiation mask (curl commit db72b8d), so
|
||||||
|
// 8.11.0 through 8.12.1 are vulnerable again.
|
||||||
|
private const HTTP_VERSION_REUSE_MATCH_VERSION = '7.77.0';
|
||||||
|
|
||||||
|
private const HTTP_VERSION_REUSE_MATCH_REGRESSION = '8.11.0';
|
||||||
|
|
||||||
|
private const HTTP_VERSION_REUSE_MATCH_RESTORED = '8.13.0';
|
||||||
|
|
||||||
|
// CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE restricts the ALPN offer to h2 only
|
||||||
|
// since libcurl 8.10.0, and connection reuse matching stopped handing
|
||||||
|
// lower-version connections to prior-knowledge transfers in 8.14.0; below
|
||||||
|
// that, a required request could silently be sent over a reused HTTP/1.1
|
||||||
|
// connection.
|
||||||
|
private const REQUIRED_MULTIPLEX_VERSION = '8.14.0';
|
||||||
|
|
||||||
|
// curl 7.52.0 introduced HTTPS proxy support, advertised by a feature bit
|
||||||
|
// (a build can meet the version yet lack the feature). Earlier libcurl
|
||||||
|
// mishandles an https:// proxy: before 7.50.2 it silently downgrades to a
|
||||||
|
// plaintext HTTP proxy, and 7.50.2 through 7.51 reject it at connect time.
|
||||||
|
private const HTTPS_PROXY_VERSION = '7.52.0';
|
||||||
|
|
||||||
|
private const HANDLER_SHARING_VERSION = '7.35.0';
|
||||||
|
|
||||||
|
private const SSL_SESSION_SHARING_VERSION = '8.6.0';
|
||||||
|
|
||||||
|
// curl 7.57.0 added share-handle connection caches through
|
||||||
|
// CURL_LOCK_DATA_CONNECT; older share objects can only hold DNS, TLS
|
||||||
|
// session, and cookie data, never connections.
|
||||||
|
private const SHARE_CONNECTION_CACHE_VERSION = '7.57.0';
|
||||||
|
|
||||||
|
// curl 7.83.1 added proxy TLS-SRP to the connection-reuse match
|
||||||
|
// (CVE-2022-27782); the proxy client certificate was matched from 7.52.0,
|
||||||
|
// so proxy TLS credentials are trusted from 7.83.1 onwards.
|
||||||
|
private const PROXY_TLS_CREDENTIAL_REUSE_VERSION = '7.83.1';
|
||||||
|
|
||||||
|
// curl 8.19.0 fixed proxy tunnel reuse after credential changes
|
||||||
|
// (CVE-2026-3784), but related proxy credential leak flaws were only
|
||||||
|
// fixed in 8.20.0, so connection reuse is trusted from 8.20.0 onwards.
|
||||||
|
private const PROXY_CREDENTIAL_REUSE_VERSION = '8.20.0';
|
||||||
|
|
||||||
|
// curl 7.69.0 started comparing SOCKS proxy credentials when matching
|
||||||
|
// connections for reuse (curl #4835); older libcurl matches a SOCKS proxy
|
||||||
|
// by type, host, and port only.
|
||||||
|
private const SOCKS_PROXY_CREDENTIAL_REUSE_VERSION = '7.69.0';
|
||||||
|
|
||||||
|
private const PROXY_HEADER_SEPARATION_VERSION = '7.37.0';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array{version: string, features: int}|false|null
|
||||||
|
*/
|
||||||
|
private static $versionInfo;
|
||||||
|
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsCurlHandler(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return $version !== null && \version_compare($version, self::MIN_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsTls12(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return self::supportsSsl()
|
||||||
|
&& \defined('CURL_SSLVERSION_TLSv1_2')
|
||||||
|
&& $version !== null
|
||||||
|
&& \version_compare($version, self::TLS_12_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsTls13(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return self::supportsSsl()
|
||||||
|
&& \defined('CURL_SSLVERSION_TLSv1_3')
|
||||||
|
&& $version !== null
|
||||||
|
&& \version_compare($version, self::TLS_13_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsHttp2(): bool
|
||||||
|
{
|
||||||
|
$versionInfo = self::getVersionInfo();
|
||||||
|
|
||||||
|
return self::supportsTls12()
|
||||||
|
&& \defined('CURL_VERSION_HTTP2')
|
||||||
|
&& $versionInfo !== null
|
||||||
|
&& 0 !== (\CURL_VERSION_HTTP2 & $versionInfo['features']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsMultiplex(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return \defined('CURLOPT_PIPEWAIT')
|
||||||
|
&& $version !== null
|
||||||
|
&& \version_compare($version, self::MULTIPLEX_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsHttpVersionReuseMatching(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
if ($version === null || \version_compare($version, self::HTTP_VERSION_REUSE_MATCH_VERSION, '<')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \version_compare($version, self::HTTP_VERSION_REUSE_MATCH_REGRESSION, '<')
|
||||||
|
|| \version_compare($version, self::HTTP_VERSION_REUSE_MATCH_RESTORED, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsConnectionCaps(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return \defined('CURLMOPT_MAX_HOST_CONNECTIONS')
|
||||||
|
&& \defined('CURLMOPT_MAX_TOTAL_CONNECTIONS')
|
||||||
|
&& $version !== null
|
||||||
|
&& \version_compare($version, self::CONNECTION_CAP_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ensureConnectionCapsSupported(string $option): void
|
||||||
|
{
|
||||||
|
if (self::supportsConnectionCaps()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(\sprintf(
|
||||||
|
'The "%s" option requires PHP cURL support for CURLMOPT_MAX_HOST_CONNECTIONS and CURLMOPT_MAX_TOTAL_CONNECTIONS with libcurl %s or newer.',
|
||||||
|
$option,
|
||||||
|
self::CONNECTION_CAP_VERSION
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsRequiredMultiplex(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return \defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE')
|
||||||
|
&& $version !== null
|
||||||
|
&& self::supportsHttp2()
|
||||||
|
&& \version_compare($version, self::REQUIRED_MULTIPLEX_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsHttpsProxy(): bool
|
||||||
|
{
|
||||||
|
$versionInfo = self::getVersionInfo();
|
||||||
|
|
||||||
|
// CURL_VERSION_HTTPS_PROXY is not defined on every supported PHP
|
||||||
|
// version; fall back to the curl.h bit value.
|
||||||
|
$httpsProxyFeature = \defined('CURL_VERSION_HTTPS_PROXY') ? \CURL_VERSION_HTTPS_PROXY : (1 << 21);
|
||||||
|
|
||||||
|
return $versionInfo !== null
|
||||||
|
&& \version_compare($versionInfo['version'], self::HTTPS_PROXY_VERSION, '>=')
|
||||||
|
&& 0 !== ($httpsProxyFeature & $versionInfo['features']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsNtlm(): bool
|
||||||
|
{
|
||||||
|
$versionInfo = self::getVersionInfo();
|
||||||
|
|
||||||
|
// CURL_VERSION_NTLM is not defined on every supported PHP version; fall
|
||||||
|
// back to the curl.h bit value.
|
||||||
|
$ntlmFeature = \defined('CURL_VERSION_NTLM') ? \CURL_VERSION_NTLM : (1 << 4);
|
||||||
|
|
||||||
|
return \defined('CURLAUTH_NTLM')
|
||||||
|
&& $versionInfo !== null
|
||||||
|
&& 0 !== ($ntlmFeature & $versionInfo['features']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsHandlerSharing(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return $version !== null && \version_compare($version, self::HANDLER_SHARING_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ensureHandlerSharingSupported(): void
|
||||||
|
{
|
||||||
|
if (!self::supportsHandlerSharing()) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf(
|
||||||
|
'The "transport_sharing" option requires libcurl %s or higher for cURL share handles.',
|
||||||
|
self::HANDLER_SHARING_VERSION
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsSslSessionSharing(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return self::supportsSsl()
|
||||||
|
&& $version !== null
|
||||||
|
&& \version_compare($version, self::SSL_SESSION_SHARING_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ensureSslSessionSharingSupported(): void
|
||||||
|
{
|
||||||
|
if (!self::supportsSslSessionSharing()) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf(
|
||||||
|
'The "transport_sharing" option requires libcurl %s or higher with SSL support for SSL session sharing.',
|
||||||
|
self::SSL_SESSION_SHARING_VERSION
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsShareConnectionCaches(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
// An undetectable libcurl version is treated as capable so the
|
||||||
|
// opaque share safeguards fail closed.
|
||||||
|
return $version === null || \version_compare($version, self::SHARE_CONNECTION_CACHE_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsProxyTlsCredentialAwareConnectionReuse(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return $version !== null
|
||||||
|
&& \version_compare($version, self::PROXY_TLS_CREDENTIAL_REUSE_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsProxyCredentialAwareConnectionReuse(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return $version !== null
|
||||||
|
&& \version_compare($version, self::PROXY_CREDENTIAL_REUSE_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsSocksProxyCredentialAwareConnectionReuse(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return $version !== null
|
||||||
|
&& \version_compare($version, self::SOCKS_PROXY_CREDENTIAL_REUSE_VERSION, '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supportsProxyHeaderSeparation(): bool
|
||||||
|
{
|
||||||
|
$version = self::getVersion();
|
||||||
|
|
||||||
|
return $version !== null
|
||||||
|
&& \version_compare($version, self::PROXY_HEADER_SEPARATION_VERSION, '>=')
|
||||||
|
&& \defined('CURLOPT_PROXYHEADER')
|
||||||
|
&& \defined('CURLOPT_HEADEROPT')
|
||||||
|
&& \defined('CURLHEADER_SEPARATE');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function supportsSsl(): bool
|
||||||
|
{
|
||||||
|
$versionInfo = self::getVersionInfo();
|
||||||
|
|
||||||
|
return \defined('CURL_VERSION_SSL')
|
||||||
|
&& $versionInfo !== null
|
||||||
|
&& 0 !== (\CURL_VERSION_SSL & $versionInfo['features']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getVersion(): ?string
|
||||||
|
{
|
||||||
|
$versionInfo = self::getVersionInfo();
|
||||||
|
|
||||||
|
return $versionInfo === null ? null : $versionInfo['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{version: string, features: int}|null
|
||||||
|
*/
|
||||||
|
private static function getVersionInfo(): ?array
|
||||||
|
{
|
||||||
|
if (self::$versionInfo === null) {
|
||||||
|
if (!\function_exists('curl_version')) {
|
||||||
|
self::$versionInfo = false;
|
||||||
|
} else {
|
||||||
|
$versionInfo = \curl_version();
|
||||||
|
self::$versionInfo = \is_array($versionInfo)
|
||||||
|
&& isset($versionInfo['version'], $versionInfo['features'])
|
||||||
|
&& \is_string($versionInfo['version'])
|
||||||
|
&& \is_int($versionInfo['features'])
|
||||||
|
? [
|
||||||
|
'version' => $versionInfo['version'],
|
||||||
|
'features' => $versionInfo['features'],
|
||||||
|
]
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$versionInfo === false ? null : self::$versionInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,17 @@ final class EasyHandle
|
||||||
*/
|
*/
|
||||||
public $headers = [];
|
public $headers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Valid trailer lines, retained only when an on_trailers
|
||||||
|
* callback is configured
|
||||||
|
*/
|
||||||
|
public $trailers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool Whether this handle was configured with CURLOPT_PIPEWAIT
|
||||||
|
*/
|
||||||
|
public $usesPipewait = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ResponseInterface|null Received response (if any)
|
* @var ResponseInterface|null Received response (if any)
|
||||||
*/
|
*/
|
||||||
|
|
@ -50,6 +61,19 @@ final class EasyHandle
|
||||||
*/
|
*/
|
||||||
public $errno = 0;
|
public $errno = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null Effective CURLOPT_PROXY value the handle was created with (if any)
|
||||||
|
*/
|
||||||
|
public $effectiveProxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy tunnel or SOCKS proxy section signature for connection-reuse
|
||||||
|
* isolation, or null when the request does not require sectioning.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
public $proxyTunnelSignature;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Throwable|null Exception during on_headers (if any)
|
* @var \Throwable|null Exception during on_headers (if any)
|
||||||
*/
|
*/
|
||||||
|
|
@ -74,7 +98,7 @@ final class EasyHandle
|
||||||
|
|
||||||
$normalizedKeys = Utils::normalizeHeaderKeys($headers);
|
$normalizedKeys = Utils::normalizeHeaderKeys($headers);
|
||||||
|
|
||||||
if (!empty($this->options['decode_content']) && isset($normalizedKeys['content-encoding'])) {
|
if (isset($this->options['decode_content']) && $this->options['decode_content'] !== false && isset($normalizedKeys['content-encoding'])) {
|
||||||
$headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']];
|
$headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']];
|
||||||
unset($headers[$normalizedKeys['content-encoding']]);
|
unset($headers[$normalizedKeys['content-encoding']]);
|
||||||
if (isset($normalizedKeys['content-length'])) {
|
if (isset($normalizedKeys['content-length'])) {
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ final class HeaderProcessor
|
||||||
throw new \RuntimeException('HTTP status code missing from header data');
|
throw new \RuntimeException('HTTP status code missing from header data');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!\preg_match('/^\d{3}$/', $status)) {
|
if (!\preg_match('/^\d{3}$/D', $status)) {
|
||||||
throw new \RuntimeException('HTTP status code is invalid');
|
throw new \RuntimeException('HTTP status code is invalid');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,6 +57,26 @@ final class HeaderProcessor
|
||||||
return [$version, (int) $status, $parts[2] ?? null, Utils::headersFromLines($headers)];
|
return [$version, (int) $status, $parts[2] ?? null, Utils::headersFromLines($headers)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function isStatusLineCandidate(string $line): bool
|
||||||
|
{
|
||||||
|
return \preg_match('/^HTTP\/[0-9]+(?:\.[0-9]+)? [0-9]{3}(?: [^\r\n]*)?(?:\r\n|\r|\n)?$/iD', $line) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isValidHeaderFieldLine(string $line): bool
|
||||||
|
{
|
||||||
|
$parts = \explode(':', $line, 2);
|
||||||
|
|
||||||
|
if (!isset($parts[1])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $parts[0])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*(?:\r\n|\r|\n)?$/D', \trim($parts[1], " \t")) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param non-empty-list<string> $headers
|
* @param non-empty-list<string> $headers
|
||||||
*
|
*
|
||||||
|
|
@ -67,7 +87,7 @@ final class HeaderProcessor
|
||||||
$lastStatusLine = 0;
|
$lastStatusLine = 0;
|
||||||
|
|
||||||
foreach ($headers as $index => $line) {
|
foreach ($headers as $index => $line) {
|
||||||
if (\preg_match('/^HTTP\/\S+\s+/i', $line)) {
|
if (self::isStatusLineCandidate($line)) {
|
||||||
$lastStatusLine = $index;
|
$lastStatusLine = $index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use GuzzleHttp\HandlerStack;
|
||||||
use GuzzleHttp\Promise as P;
|
use GuzzleHttp\Promise as P;
|
||||||
use GuzzleHttp\Promise\PromiseInterface;
|
use GuzzleHttp\Promise\PromiseInterface;
|
||||||
use GuzzleHttp\TransferStats;
|
use GuzzleHttp\TransferStats;
|
||||||
use GuzzleHttp\Utils;
|
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
@ -160,7 +159,7 @@ class MockHandler implements \Countable
|
||||||
) {
|
) {
|
||||||
$this->queue[] = $value;
|
$this->queue[] = $value;
|
||||||
} else {
|
} else {
|
||||||
throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found '.Utils::describeType($value));
|
throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found '.\get_debug_type($value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
vendor/guzzlehttp/guzzle/src/Handler/Proxy.php
vendored
30
vendor/guzzlehttp/guzzle/src/Handler/Proxy.php
vendored
|
|
@ -48,4 +48,34 @@ class Proxy
|
||||||
return empty($options['stream']) ? $default($request, $options) : $streaming($request, $options);
|
return empty($options['stream']) ? $default($request, $options) : $streaming($request, $options);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends requests to a fallback handler when the default cURL handler cannot
|
||||||
|
* honor TLS 1.2 selection.
|
||||||
|
*
|
||||||
|
* @param callable(RequestInterface, array): PromiseInterface $default
|
||||||
|
* @param callable(RequestInterface, array): PromiseInterface $fallback
|
||||||
|
*
|
||||||
|
* @return callable(RequestInterface, array): PromiseInterface Returns the composed handler.
|
||||||
|
*/
|
||||||
|
public static function wrapTlsFallback(callable $default, callable $fallback): callable
|
||||||
|
{
|
||||||
|
return static function (RequestInterface $request, array $options) use ($default, $fallback): PromiseInterface {
|
||||||
|
if (self::requiresTls12Fallback($options)) {
|
||||||
|
return $fallback($request, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default($request, $options);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
private static function requiresTls12Fallback(array $options): bool
|
||||||
|
{
|
||||||
|
return isset($options[RequestOptions::CRYPTO_METHOD])
|
||||||
|
&& $options[RequestOptions::CRYPTO_METHOD] === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
|
||||||
|
&& !CurlVersion::supportsTls12();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
121
vendor/guzzlehttp/guzzle/src/Handler/ProxyEnvironment.php
vendored
Normal file
121
vendor/guzzlehttp/guzzle/src/Handler/ProxyEnvironment.php
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace GuzzleHttp\Handler;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves proxy configuration from the process environment with the same
|
||||||
|
* semantics libcurl applies, so the cURL handlers can pin CURLOPT_PROXY and
|
||||||
|
* CURLOPT_NOPROXY explicitly and libcurl never reads the environment itself.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class ProxyEnvironment
|
||||||
|
{
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the proxy to use for the given request scheme.
|
||||||
|
*
|
||||||
|
* The lookup mirrors libcurl for the http and https schemes the handlers
|
||||||
|
* accept: the lowercase scheme-specific variable first, its uppercase
|
||||||
|
* variant next (except for "http", where uppercase HTTP_PROXY is never
|
||||||
|
* read), then all_proxy/ALL_PROXY.
|
||||||
|
*
|
||||||
|
* @return string|null The proxy to use; null when the environment
|
||||||
|
* configures none.
|
||||||
|
*/
|
||||||
|
public static function getProxyForScheme(string $scheme): ?string
|
||||||
|
{
|
||||||
|
$scheme = Psr7\Utils::asciiToLower($scheme);
|
||||||
|
$candidates = [$scheme.'_proxy'];
|
||||||
|
if ($scheme !== 'http') {
|
||||||
|
// Uppercase HTTP_PROXY is deliberately never consulted: a CGI
|
||||||
|
// request header "Proxy:" becomes HTTP_PROXY in the environment.
|
||||||
|
// See https://httpoxy.org for more information.
|
||||||
|
$candidates[] = Psr7\Utils::asciiToUpper($scheme).'_PROXY';
|
||||||
|
}
|
||||||
|
$candidates[] = 'all_proxy';
|
||||||
|
$candidates[] = 'ALL_PROXY';
|
||||||
|
|
||||||
|
foreach ($candidates as $name) {
|
||||||
|
$value = self::getenv($name);
|
||||||
|
if ($value !== null) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|null The no-proxy list; null when nothing is set.
|
||||||
|
*/
|
||||||
|
public static function getNoProxy(): ?string
|
||||||
|
{
|
||||||
|
foreach (['no_proxy', 'NO_PROXY'] as $name) {
|
||||||
|
$value = self::getenv($name);
|
||||||
|
if ($value !== null) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a no_proxy environment value into matchable entries.
|
||||||
|
*
|
||||||
|
* Mirrors libcurl's tokenization: entries may be separated by commas or
|
||||||
|
* blanks, and a single leading dot is ignored, so ".example.com" bypasses
|
||||||
|
* example.com and its subdomains exactly as a bare domain entry does.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public static function splitNoProxy(string $noProxy): array
|
||||||
|
{
|
||||||
|
$entries = [];
|
||||||
|
|
||||||
|
$split = \preg_split('/[\s,]+/', $noProxy);
|
||||||
|
|
||||||
|
if ($split === false) {
|
||||||
|
throw new \RuntimeException('Unable to split the no_proxy value: '.\preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($split as $entry) {
|
||||||
|
if ($entry !== '' && $entry[0] === '.') {
|
||||||
|
$entry = \substr($entry, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entry !== '') {
|
||||||
|
$entries[] = $entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getenv(string $name): ?string
|
||||||
|
{
|
||||||
|
// Windows environment variables are case-insensitive, so the
|
||||||
|
// lowercase-only httpoxy defence does not hold there. Outside the
|
||||||
|
// CLI SAPI on Windows, environment proxies are not resolved at all
|
||||||
|
// (a safe-side divergence from libcurl).
|
||||||
|
if (\PHP_OS_FAMILY === 'Windows' && \PHP_SAPI !== 'cli') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// local_only: the OS environment and putenv() only - the same
|
||||||
|
// environ(7) libcurl reads. SAPI request environments such as
|
||||||
|
// fastcgi_param or SetEnv are deliberately excluded.
|
||||||
|
$value = \getenv($name, true);
|
||||||
|
|
||||||
|
// libcurl's GetEnv (lib/getenv.c) treats variables set to an empty
|
||||||
|
// string as unset on every version, so the lookup falls through to
|
||||||
|
// the next candidate.
|
||||||
|
return $value === false || $value === '' ? null : $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ namespace GuzzleHttp\Handler;
|
||||||
|
|
||||||
use GuzzleHttp\Exception\ConnectException;
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
use GuzzleHttp\Exception\RequestException;
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use GuzzleHttp\Multiplexing;
|
||||||
use GuzzleHttp\Promise as P;
|
use GuzzleHttp\Promise as P;
|
||||||
use GuzzleHttp\Promise\FulfilledPromise;
|
use GuzzleHttp\Promise\FulfilledPromise;
|
||||||
use GuzzleHttp\Promise\PromiseInterface;
|
use GuzzleHttp\Promise\PromiseInterface;
|
||||||
|
|
@ -23,6 +24,12 @@ use Psr\Http\Message\UriInterface;
|
||||||
*/
|
*/
|
||||||
class StreamHandler
|
class StreamHandler
|
||||||
{
|
{
|
||||||
|
private const KNOWN_CONSTRUCTOR_OPTIONS = [
|
||||||
|
'max_host_connections' => true,
|
||||||
|
'max_total_connections' => true,
|
||||||
|
'transport_sharing' => true,
|
||||||
|
];
|
||||||
|
|
||||||
private const CONNECTION_ERRORS = [
|
private const CONNECTION_ERRORS = [
|
||||||
'php_network_getaddresses:',
|
'php_network_getaddresses:',
|
||||||
'getaddrinfo',
|
'getaddrinfo',
|
||||||
|
|
@ -46,6 +53,61 @@ class StreamHandler
|
||||||
*/
|
*/
|
||||||
private $lastHeaders = [];
|
private $lastHeaders = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $transportSharingMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $connectionCapsConfigured = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts an associative array of options:
|
||||||
|
*
|
||||||
|
* - max_host_connections: Optional positive integer or null. A non-null
|
||||||
|
* value marks the handler as incompatible with enabled response
|
||||||
|
* streaming; the number is not used for stream-handler admission.
|
||||||
|
* - max_total_connections: Optional positive integer or null. A non-null
|
||||||
|
* value marks the handler as incompatible with enabled response
|
||||||
|
* streaming; the number is not used for stream-handler admission.
|
||||||
|
* - transport_sharing: Optional transport sharing mode.
|
||||||
|
*
|
||||||
|
* The stream handler cannot cap streamed connections, so a configured cap
|
||||||
|
* marker rejects enabled response streaming ("stream" => true). Accepted
|
||||||
|
* transfers are buffered and hold at most one connection per in-flight
|
||||||
|
* call, but overlapping buffered calls are not collectively limited.
|
||||||
|
*
|
||||||
|
* @param array{max_host_connections?: mixed, max_total_connections?: mixed, transport_sharing?: mixed} $options Array of options to use with the handler
|
||||||
|
*/
|
||||||
|
public function __construct(array $options = [])
|
||||||
|
{
|
||||||
|
foreach ($options as $name => $_) {
|
||||||
|
if (!isset(self::KNOWN_CONSTRUCTOR_OPTIONS[$name])) {
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.14', \sprintf('The "%s" StreamHandler constructor option is unknown; guzzlehttp/guzzle 8.0 will reject unknown constructor options.', (string) $name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->transportSharingMode = CurlShareHandleState::normalizeMode(
|
||||||
|
$options['transport_sharing'] ?? null,
|
||||||
|
'transport_sharing'
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (['max_host_connections', 'max_total_connections'] as $capOption) {
|
||||||
|
$value = $options[$capOption] ?? null;
|
||||||
|
if ($value === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_int($value) || $value < 1) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf('%s must be a positive integer.', $capOption));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->connectionCapsConfigured = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends an HTTP request.
|
* Sends an HTTP request.
|
||||||
*
|
*
|
||||||
|
|
@ -59,6 +121,29 @@ class StreamHandler
|
||||||
\usleep($options['delay'] * 1000);
|
\usleep($options['delay'] * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$multiplex = $options['multiplex'] ?? null;
|
||||||
|
|
||||||
|
// Multiplexing::NONE is trivially satisfied: the stream handler sends
|
||||||
|
// one HTTP/1.x request per connection and never multiplexes.
|
||||||
|
if (null !== $multiplex && !\in_array($multiplex, [Multiplexing::NONE, Multiplexing::EAGER, Multiplexing::WAIT, Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) {
|
||||||
|
throw new \InvalidArgumentException(\sprintf(
|
||||||
|
'The "multiplex" option must be null or a GuzzleHttp\\Multiplexing::* constant; received %s.',
|
||||||
|
\get_debug_type($multiplex)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) {
|
||||||
|
throw new ConnectException('The stream handler cannot guarantee a multiplexed protocol; required multiplexing needs a cURL handler.', $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->connectionCapsConfigured && !empty($options['stream'])) {
|
||||||
|
throw new \InvalidArgumentException('Enabling the "stream" request option on a stream handler configured with the "max_host_connections" or "max_total_connections" option is not supported because streamed connections cannot be capped.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['on_trailers'])) {
|
||||||
|
throw new \InvalidArgumentException('Passing the "on_trailers" request option to the stream handler is not supported because the stream handler cannot observe trailers.');
|
||||||
|
}
|
||||||
|
|
||||||
$protocolVersion = $request->getProtocolVersion();
|
$protocolVersion = $request->getProtocolVersion();
|
||||||
|
|
||||||
if ('' === $protocolVersion) {
|
if ('' === $protocolVersion) {
|
||||||
|
|
@ -75,6 +160,7 @@ class StreamHandler
|
||||||
$startTime = isset($options['on_stats']) ? Utils::currentTime() : null;
|
$startTime = isset($options['on_stats']) ? Utils::currentTime() : null;
|
||||||
|
|
||||||
self::triggerUnsupportedRequestOptionDeprecations($request, $options);
|
self::triggerUnsupportedRequestOptionDeprecations($request, $options);
|
||||||
|
$this->assertTransportSharingSupported();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Does not support the expect header.
|
// Does not support the expect header.
|
||||||
|
|
@ -84,8 +170,8 @@ class StreamHandler
|
||||||
// the behavior of `CurlHandler`
|
// the behavior of `CurlHandler`
|
||||||
if (
|
if (
|
||||||
(
|
(
|
||||||
0 === \strcasecmp('PUT', $request->getMethod())
|
Psr7\Utils::caselessEquals('PUT', $request->getMethod())
|
||||||
|| 0 === \strcasecmp('POST', $request->getMethod())
|
|| Psr7\Utils::caselessEquals('POST', $request->getMethod())
|
||||||
)
|
)
|
||||||
&& 0 === $request->getBody()->getSize()
|
&& 0 === $request->getBody()->getSize()
|
||||||
) {
|
) {
|
||||||
|
|
@ -155,7 +241,7 @@ class StreamHandler
|
||||||
$stream = Psr7\Utils::streamFor($stream);
|
$stream = Psr7\Utils::streamFor($stream);
|
||||||
$sink = $stream;
|
$sink = $stream;
|
||||||
|
|
||||||
if (\strcasecmp('HEAD', $request->getMethod())) {
|
if (!Psr7\Utils::caselessEquals('HEAD', $request->getMethod())) {
|
||||||
$sink = $this->createSink($stream, $options);
|
$sink = $this->createSink($stream, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,7 +307,7 @@ class StreamHandler
|
||||||
private function checkDecode(array $options, array $headers, $stream): array
|
private function checkDecode(array $options, array $headers, $stream): array
|
||||||
{
|
{
|
||||||
// Automatically decode responses when instructed.
|
// Automatically decode responses when instructed.
|
||||||
if (!empty($options['decode_content'])) {
|
if (isset($options['decode_content']) && $options['decode_content'] !== false) {
|
||||||
$normalizedKeys = Utils::normalizeHeaderKeys($headers);
|
$normalizedKeys = Utils::normalizeHeaderKeys($headers);
|
||||||
if (isset($normalizedKeys['content-encoding'])) {
|
if (isset($normalizedKeys['content-encoding'])) {
|
||||||
$encoding = $headers[$normalizedKeys['content-encoding']];
|
$encoding = $headers[$normalizedKeys['content-encoding']];
|
||||||
|
|
@ -307,7 +393,7 @@ class StreamHandler
|
||||||
$message .= "[$key] $value".\PHP_EOL;
|
$message .= "[$key] $value".\PHP_EOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new \RuntimeException(\trim($message));
|
throw new \RuntimeException(\trim($message, " \n\r\t\0\x0B"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $resource;
|
return $resource;
|
||||||
|
|
@ -323,7 +409,12 @@ class StreamHandler
|
||||||
$methods = \array_flip(\get_class_methods(__CLASS__));
|
$methods = \array_flip(\get_class_methods(__CLASS__));
|
||||||
}
|
}
|
||||||
|
|
||||||
$scheme = $request->getUri()->getScheme();
|
$uri = $request->getUri();
|
||||||
|
$scheme = $uri->getScheme();
|
||||||
|
if ($scheme === '') {
|
||||||
|
throw new RequestException('URI must include a scheme and host. Use an absolute URI, a network-path reference starting with //, or configure a base_uri.', $request);
|
||||||
|
}
|
||||||
|
|
||||||
if (!\in_array($scheme, ['http', 'https'], true)) {
|
if (!\in_array($scheme, ['http', 'https'], true)) {
|
||||||
throw new RequestException(\sprintf("The scheme '%s' is not supported.", $scheme), $request);
|
throw new RequestException(\sprintf("The scheme '%s' is not supported.", $scheme), $request);
|
||||||
}
|
}
|
||||||
|
|
@ -333,6 +424,10 @@ class StreamHandler
|
||||||
throw new RequestException(\sprintf('The scheme "%s" is not allowed by the protocols request option.', $scheme), $request);
|
throw new RequestException(\sprintf('The scheme "%s" is not allowed by the protocols request option.', $scheme), $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($uri->getHost() === '') {
|
||||||
|
throw new RequestException('URI must include a scheme and host. Use an absolute URI, a network-path reference starting with //, or configure a base_uri.', $request);
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP/1.1 streams using the PHP stream wrapper require a
|
// HTTP/1.1 streams using the PHP stream wrapper require a
|
||||||
// Connection: close header
|
// Connection: close header
|
||||||
if ($request->getProtocolVersion() === '1.1'
|
if ($request->getProtocolVersion() === '1.1'
|
||||||
|
|
@ -353,10 +448,19 @@ class StreamHandler
|
||||||
throw new \InvalidArgumentException('on_headers must be callable');
|
throw new \InvalidArgumentException('on_headers must be callable');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::assertTlsVersionRangeForOptions($options);
|
||||||
|
|
||||||
|
$proxyAuthorizationAdded = false;
|
||||||
if (!empty($options)) {
|
if (!empty($options)) {
|
||||||
foreach ($options as $key => $value) {
|
foreach ($options as $key => $value) {
|
||||||
$method = "add_{$key}";
|
$method = "add_{$key}";
|
||||||
if (isset($methods[$method])) {
|
if (isset($methods[$method])) {
|
||||||
|
if ($method === 'add_proxy') {
|
||||||
|
$proxyAuthorizationAdded = $this->add_proxy($request, $context, $value, $params);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$this->{$method}($request, $context, $value, $params);
|
$this->{$method}($request, $context, $value, $params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -366,6 +470,16 @@ class StreamHandler
|
||||||
if (!\is_array($options['stream_context'])) {
|
if (!\is_array($options['stream_context'])) {
|
||||||
throw new \InvalidArgumentException('stream_context must be an array');
|
throw new \InvalidArgumentException('stream_context must be an array');
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
$proxyAuthorizationAdded
|
||||||
|
&& isset($options['stream_context']['http'])
|
||||||
|
&& \is_array($options['stream_context']['http'])
|
||||||
|
&& \array_key_exists('proxy', $options['stream_context']['http'])
|
||||||
|
) {
|
||||||
|
throw new \InvalidArgumentException('stream_context.http.proxy cannot override a proxy after the stream handler has generated a Proxy-Authorization header; configure the final proxy with the "proxy" request option.');
|
||||||
|
}
|
||||||
|
self::triggerConflictingStreamContextOptionDeprecations($options['stream_context']);
|
||||||
|
self::triggerUnsupportedStreamContextOptionDeprecations($options['stream_context']);
|
||||||
$context = \array_replace_recursive($context, $options['stream_context']);
|
$context = \array_replace_recursive($context, $options['stream_context']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -394,7 +508,7 @@ class StreamHandler
|
||||||
$this->lastHeaders = $http_response_header ?? [];
|
$this->lastHeaders = $http_response_header ?? [];
|
||||||
|
|
||||||
if (false === $resource) {
|
if (false === $resource) {
|
||||||
throw new ConnectException(sprintf('Connection refused for URI %s', $uri), $request, null, $context);
|
throw new ConnectException(sprintf('Connection refused for URI %s', Psr7\Utils::redactUserInfo($uri)), $request, null, $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($options['read_timeout'])) {
|
if (isset($options['read_timeout'])) {
|
||||||
|
|
@ -413,7 +527,11 @@ class StreamHandler
|
||||||
{
|
{
|
||||||
$uri = $request->getUri();
|
$uri = $request->getUri();
|
||||||
|
|
||||||
if (isset($options['force_ip_resolve']) && !\filter_var($uri->getHost(), \FILTER_VALIDATE_IP)) {
|
$host = $uri->getHost();
|
||||||
|
$hostForIpCheck = $host !== '' && $host[0] === '[' && \substr($host, -1) === ']'
|
||||||
|
? \substr($host, 1, -1)
|
||||||
|
: $host;
|
||||||
|
if (isset($options['force_ip_resolve']) && !\filter_var($hostForIpCheck, \FILTER_VALIDATE_IP)) {
|
||||||
if ('v4' === $options['force_ip_resolve']) {
|
if ('v4' === $options['force_ip_resolve']) {
|
||||||
$records = \dns_get_record($uri->getHost(), \DNS_A);
|
$records = \dns_get_record($uri->getHost(), \DNS_A);
|
||||||
if (false === $records || !isset($records[0]['ip'])) {
|
if (false === $records || !isset($records[0]['ip'])) {
|
||||||
|
|
@ -439,6 +557,17 @@ class StreamHandler
|
||||||
{
|
{
|
||||||
$headers = '';
|
$headers = '';
|
||||||
foreach ($request->getHeaders() as $name => $value) {
|
foreach ($request->getHeaders() as $name => $value) {
|
||||||
|
// A first-class Proxy-Authorization header is proxy-scoped. Keep
|
||||||
|
// it out of the origin context; add_proxy() adds one
|
||||||
|
// validated canonical line only when Guzzle selects a proxy; PHP
|
||||||
|
// extracts that line for CONNECT and removes it before sending the
|
||||||
|
// tunneled origin request. The caselessEquals() helper is
|
||||||
|
// locale-independent, unlike strcasecmp(), so a locale cannot
|
||||||
|
// make this match miss and re-leak the credential.
|
||||||
|
if (Psr7\Utils::caselessEquals((string) $name, 'Proxy-Authorization')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($value as $val) {
|
foreach ($value as $val) {
|
||||||
$headers .= "$name: $val\r\n";
|
$headers .= "$name: $val\r\n";
|
||||||
}
|
}
|
||||||
|
|
@ -467,21 +596,13 @@ class StreamHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$context['http']['header'] = \rtrim($context['http']['header']);
|
$context['http']['header'] = \rtrim($context['http']['header'], " \n\r\t\0\x0B");
|
||||||
|
|
||||||
return $context;
|
return $context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function triggerUnsupportedRequestOptionDeprecations(RequestInterface $request, array $options): void
|
private static function triggerUnsupportedRequestOptionDeprecations(RequestInterface $request, array $options): void
|
||||||
{
|
{
|
||||||
if (\array_key_exists('transport_sharing', $options)) {
|
|
||||||
$transportSharingMode = CurlShareHandleState::normalizeMode($options['transport_sharing'], 'transport_sharing');
|
|
||||||
|
|
||||||
if ($transportSharingMode === TransportSharing::HANDLER_REQUIRE) {
|
|
||||||
throw new \InvalidArgumentException('The "transport_sharing" option requires transport sharing, but the stream handler does not support it.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
\array_key_exists('curl', $options)
|
\array_key_exists('curl', $options)
|
||||||
&& $options['curl'] !== null
|
&& $options['curl'] !== null
|
||||||
|
|
@ -491,15 +612,170 @@ class StreamHandler
|
||||||
\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing the "curl" request option to the stream handler is deprecated; guzzlehttp/guzzle 8.0 will reject this option because the stream handler ignores cURL options.');
|
\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing the "curl" request option to the stream handler is deprecated; guzzlehttp/guzzle 8.0 will reject this option because the stream handler ignores cURL options.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self::usesDigestAuth($options)) {
|
|
||||||
\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing digest authentication to the stream handler is deprecated; guzzlehttp/guzzle 8.0 will reject digest authentication with the stream handler because it is only supported by cURL handlers.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (\array_key_exists('expect', $options) && $options['expect'] !== false && $request->hasHeader('Expect')) {
|
if (\array_key_exists('expect', $options) && $options['expect'] !== false && $request->hasHeader('Expect')) {
|
||||||
\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing the "expect" request option to the stream handler is deprecated when it adds an Expect header; guzzlehttp/guzzle 8.0 will reject this option because the stream handler does not support Expect: 100-Continue.');
|
\trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing the "expect" request option to the stream handler is deprecated when it adds an Expect header; guzzlehttp/guzzle 8.0 will reject this option because the stream handler does not support Expect: 100-Continue.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function triggerConflictingStreamContextOptionDeprecations(array $streamContext): void
|
||||||
|
{
|
||||||
|
$conflictingOptions = self::conflictingStreamContextOptions();
|
||||||
|
|
||||||
|
foreach ($streamContext as $wrapper => $contextOptions) {
|
||||||
|
if (!\is_string($wrapper) || !isset($conflictingOptions[$wrapper]) || !\is_array($contextOptions)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($contextOptions as $option => $_) {
|
||||||
|
if (!\is_string($option) || !\array_key_exists($option, $conflictingOptions[$wrapper])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
\trigger_deprecation(
|
||||||
|
'guzzlehttp/guzzle',
|
||||||
|
'7.12',
|
||||||
|
\sprintf(
|
||||||
|
'Passing stream_context.%s.%s in the "stream_context" request option is deprecated; guzzlehttp/guzzle 8.0 will reject this option because it conflicts with Guzzle-managed request handling. Use %s instead.',
|
||||||
|
$wrapper,
|
||||||
|
$option,
|
||||||
|
$conflictingOptions[$wrapper][$option]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function triggerUnsupportedStreamContextOptionDeprecations(array $streamContext): void
|
||||||
|
{
|
||||||
|
$unsupportedOptions = self::unsupportedStreamContextOptions($streamContext);
|
||||||
|
if ($unsupportedOptions === []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
\trigger_deprecation(
|
||||||
|
'guzzlehttp/guzzle',
|
||||||
|
'7.12',
|
||||||
|
\sprintf(
|
||||||
|
'Passing PHP stream context options outside the built-in stream handler allow-list to the "stream_context" request option is deprecated; guzzlehttp/guzzle 8.0 will reject stream context options outside the allow-list. Deprecated option%s: %s.',
|
||||||
|
\count($unsupportedOptions) === 1 ? '' : 's',
|
||||||
|
\implode(', ', $unsupportedOptions)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
private static function unsupportedStreamContextOptions(array $streamContext): array
|
||||||
|
{
|
||||||
|
$supportedOptions = self::supportedStreamContextOptions();
|
||||||
|
$conflictingOptions = self::conflictingStreamContextOptions();
|
||||||
|
$unsupportedOptions = [];
|
||||||
|
|
||||||
|
foreach ($streamContext as $wrapper => $contextOptions) {
|
||||||
|
if (!\is_string($wrapper) || !isset($supportedOptions[$wrapper])) {
|
||||||
|
if (\is_array($contextOptions)) {
|
||||||
|
foreach ($contextOptions as $option => $_) {
|
||||||
|
if (\is_string($wrapper) && \is_string($option) && isset($conflictingOptions[$wrapper]) && \array_key_exists($option, $conflictingOptions[$wrapper])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$unsupportedOptions[] = \sprintf('stream_context.%s.%s', (string) $wrapper, (string) $option);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$unsupportedOptions[] = \sprintf('stream_context.%s', (string) $wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_array($contextOptions)) {
|
||||||
|
$unsupportedOptions[] = \sprintf('stream_context.%s', $wrapper);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($contextOptions as $option => $_) {
|
||||||
|
if (\is_string($option) && isset($conflictingOptions[$wrapper]) && \array_key_exists($option, $conflictingOptions[$wrapper])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_string($option) || !\array_key_exists($option, $supportedOptions[$wrapper])) {
|
||||||
|
$unsupportedOptions[] = \sprintf('stream_context.%s.%s', $wrapper, (string) $option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $unsupportedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, array<string, true>>
|
||||||
|
*/
|
||||||
|
private static function supportedStreamContextOptions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'http' => [
|
||||||
|
'request_fulluri' => true,
|
||||||
|
],
|
||||||
|
'socket' => [
|
||||||
|
'bindto' => true,
|
||||||
|
'tcp_nodelay' => true,
|
||||||
|
],
|
||||||
|
'ssl' => [
|
||||||
|
'SNI_enabled' => true,
|
||||||
|
'capture_peer_cert' => true,
|
||||||
|
'capture_peer_cert_chain' => true,
|
||||||
|
'ciphers' => true,
|
||||||
|
'disable_compression' => true,
|
||||||
|
'no_ticket' => true,
|
||||||
|
'peer_fingerprint' => true,
|
||||||
|
'security_level' => true,
|
||||||
|
'verify_depth' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, array<string, string>>
|
||||||
|
*/
|
||||||
|
private static function conflictingStreamContextOptions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'http' => [
|
||||||
|
'content' => 'the request body',
|
||||||
|
'follow_location' => 'the "allow_redirects" request option',
|
||||||
|
'header' => 'the request headers',
|
||||||
|
'max_redirects' => 'the "allow_redirects" request option',
|
||||||
|
'method' => 'the request method',
|
||||||
|
'protocol_version' => 'the request protocol version',
|
||||||
|
'proxy' => 'the "proxy" request option',
|
||||||
|
'timeout' => 'the "timeout" request option',
|
||||||
|
],
|
||||||
|
'ssl' => [
|
||||||
|
'allow_self_signed' => 'the "verify" request option',
|
||||||
|
'cafile' => 'the "verify" request option',
|
||||||
|
'capath' => 'the "verify" request option',
|
||||||
|
'crypto_method' => 'the "crypto_method" request option',
|
||||||
|
'local_cert' => 'the "cert" request option',
|
||||||
|
'local_pk' => 'the "ssl_key" request option',
|
||||||
|
'max_proto_version' => 'the "crypto_method_max" request option',
|
||||||
|
'min_proto_version' => 'the "crypto_method" request option',
|
||||||
|
'passphrase' => 'the "cert" or "ssl_key" request option',
|
||||||
|
'peer_name' => 'the request URI',
|
||||||
|
'verify_peer' => 'the "verify" request option',
|
||||||
|
'verify_peer_name' => 'the "verify" request option',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertTransportSharingSupported(): void
|
||||||
|
{
|
||||||
|
if ($this->transportSharingMode === TransportSharing::HANDLER_REQUIRE) {
|
||||||
|
throw new \InvalidArgumentException('The "transport_sharing" option requires transport sharing, but the stream handler does not support it.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static function isCurlOptionGeneratedByAuth(array $options): bool
|
private static function isCurlOptionGeneratedByAuth(array $options): bool
|
||||||
{
|
{
|
||||||
if (!isset($options['curl']) || !\is_array($options['curl']) || !isset($options['auth'][2]) || !\is_string($options['auth'][2])) {
|
if (!isset($options['curl']) || !\is_array($options['curl']) || !isset($options['auth'][2]) || !\is_string($options['auth'][2])) {
|
||||||
|
|
@ -510,7 +786,7 @@ class StreamHandler
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$type = \strtolower($options['auth'][2]);
|
$type = Psr7\Utils::asciiToLower($options['auth'][2]);
|
||||||
if ($type === 'digest') {
|
if ($type === 'digest') {
|
||||||
$httpAuth = \defined('CURLAUTH_DIGEST') ? \constant('CURLAUTH_DIGEST') : null;
|
$httpAuth = \defined('CURLAUTH_DIGEST') ? \constant('CURLAUTH_DIGEST') : null;
|
||||||
} elseif ($type === 'ntlm') {
|
} elseif ($type === 'ntlm') {
|
||||||
|
|
@ -525,13 +801,6 @@ class StreamHandler
|
||||||
&& $options['curl'][\CURLOPT_HTTPAUTH] === $httpAuth;
|
&& $options['curl'][\CURLOPT_HTTPAUTH] === $httpAuth;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function usesDigestAuth(array $options): bool
|
|
||||||
{
|
|
||||||
return isset($options['auth'][2])
|
|
||||||
&& \is_string($options['auth'][2])
|
|
||||||
&& \strtolower($options['auth'][2]) === 'digest';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $value as passed via Request transfer options.
|
* @param mixed $value as passed via Request transfer options.
|
||||||
*
|
*
|
||||||
|
|
@ -583,7 +852,7 @@ class StreamHandler
|
||||||
throw new \InvalidArgumentException(\sprintf('%s must be a non-empty string', $option));
|
throw new \InvalidArgumentException(\sprintf('%s must be a non-empty string', $option));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\strtoupper($value) !== 'PEM') {
|
if (Psr7\Utils::asciiToUpper($value) !== 'PEM') {
|
||||||
throw new \InvalidArgumentException(\sprintf('The stream handler only supports "PEM" for the %s request option.', $option));
|
throw new \InvalidArgumentException(\sprintf('The stream handler only supports "PEM" for the %s request option.', $option));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -591,7 +860,7 @@ class StreamHandler
|
||||||
/**
|
/**
|
||||||
* @param mixed $value as passed via Request transfer options.
|
* @param mixed $value as passed via Request transfer options.
|
||||||
*/
|
*/
|
||||||
private function add_proxy(RequestInterface $request, array &$options, $value, array &$params): void
|
private function add_proxy(RequestInterface $request, array &$options, $value, array &$params): bool
|
||||||
{
|
{
|
||||||
$uri = null;
|
$uri = null;
|
||||||
|
|
||||||
|
|
@ -610,18 +879,40 @@ class StreamHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$uri) {
|
if (!$uri) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$parsed = $this->parse_proxy($uri);
|
$parsed = $this->parse_proxy($uri);
|
||||||
|
|
||||||
|
// PHP extracts and removes only one Proxy-Authorization line for a
|
||||||
|
// CONNECT tunnel. Serialize exactly one validated first-class value;
|
||||||
|
// more than one could leave a credential in the tunneled origin
|
||||||
|
// request. A first-class value, including an empty one, is
|
||||||
|
// authoritative over Basic credentials embedded in the proxy URI.
|
||||||
|
$managed = $request->getHeader('Proxy-Authorization');
|
||||||
|
if (\count($managed) > 1) {
|
||||||
|
throw new \InvalidArgumentException('The stream handler supports exactly one Proxy-Authorization request header value when a proxy is selected.');
|
||||||
|
}
|
||||||
|
if ($managed !== [] && \strpbrk($managed[0], "\r\n") !== false) {
|
||||||
|
throw new \InvalidArgumentException('Proxy-Authorization request header values must not contain a carriage return or line feed.');
|
||||||
|
}
|
||||||
|
|
||||||
$options['http']['proxy'] = $parsed['proxy'];
|
$options['http']['proxy'] = $parsed['proxy'];
|
||||||
|
|
||||||
if ($parsed['auth']) {
|
if (($managed !== [] || $parsed['auth']) && !isset($options['http']['header'])) {
|
||||||
if (!isset($options['http']['header'])) {
|
$options['http']['header'] = '';
|
||||||
$options['http']['header'] = [];
|
|
||||||
}
|
}
|
||||||
|
if ($managed !== []) {
|
||||||
|
$options['http']['header'] .= "\r\nProxy-Authorization: {$managed[0]}";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} elseif ($parsed['auth']) {
|
||||||
$options['http']['header'] .= "\r\nProxy-Authorization: {$parsed['auth']}";
|
$options['http']['header'] .= "\r\nProxy-Authorization: {$parsed['auth']}";
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -631,16 +922,29 @@ class StreamHandler
|
||||||
{
|
{
|
||||||
$parsed = \parse_url($url);
|
$parsed = \parse_url($url);
|
||||||
|
|
||||||
if ($parsed !== false && isset($parsed['scheme']) && $parsed['scheme'] === 'http') {
|
// parse_url() misreads scheme-less proxy authorities like
|
||||||
if (isset($parsed['host']) && isset($parsed['port'])) {
|
// "user:pass@host"; re-parse only those forms as HTTP.
|
||||||
$auth = null;
|
$schemeLessAuthority = \strpos($url, '://') === false && \strncmp($url, '//', 2) !== 0;
|
||||||
if (isset($parsed['user']) && isset($parsed['pass'])) {
|
if ($schemeLessAuthority) {
|
||||||
$auth = \base64_encode("{$parsed['user']}:{$parsed['pass']}");
|
if (\is_array($parsed) && !isset($parsed['scheme']) && isset($parsed['host'], $parsed['port'])) {
|
||||||
|
$parsed['scheme'] = 'http';
|
||||||
|
} elseif (
|
||||||
|
(!\is_array($parsed) || !isset($parsed['host']))
|
||||||
|
&& (\strpos($url, '@') !== false || \strncmp($url, '[', 1) === 0)
|
||||||
|
) {
|
||||||
|
$parsed = \parse_url('http://'.$url);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_array($parsed) && isset($parsed['scheme']) && Psr7\Utils::caselessEquals($parsed['scheme'], 'http')) {
|
||||||
|
if (isset($parsed['host'], $parsed['port'])) {
|
||||||
|
$user = $parsed['user'] ?? '';
|
||||||
|
$pass = $parsed['pass'] ?? '';
|
||||||
|
$auth = ($user !== '' || $pass !== '') ? 'Basic '.\base64_encode("{$user}:{$pass}") : null;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'proxy' => "tcp://{$parsed['host']}:{$parsed['port']}",
|
'proxy' => "tcp://{$parsed['host']}:{$parsed['port']}",
|
||||||
'auth' => $auth ? "Basic {$auth}" : null,
|
'auth' => $auth,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -681,6 +985,26 @@ class StreamHandler
|
||||||
throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
|
throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value as passed via Request transfer options.
|
||||||
|
*/
|
||||||
|
private function add_crypto_method_max(RequestInterface $request, array &$options, $value, array &$params): void
|
||||||
|
{
|
||||||
|
$options['ssl']['max_proto_version'] = TlsVersion::streamProtocolVersion('crypto_method_max', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function assertTlsVersionRangeForOptions(array $options): void
|
||||||
|
{
|
||||||
|
if (!isset($options['crypto_method_max'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TlsVersion::assertRange(
|
||||||
|
$options['crypto_method'] ?? null,
|
||||||
|
$options['crypto_method_max']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $value as passed via Request transfer options.
|
* @param mixed $value as passed via Request transfer options.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
84
vendor/guzzlehttp/guzzle/src/Handler/TlsVersion.php
vendored
Normal file
84
vendor/guzzlehttp/guzzle/src/Handler/TlsVersion.php
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace GuzzleHttp\Handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class TlsVersion
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public static function ordinal(string $option, $value): int
|
||||||
|
{
|
||||||
|
if ($value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT) {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
if ($value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT) {
|
||||||
|
return 11;
|
||||||
|
}
|
||||||
|
if ($value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT) {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
if (\defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT) {
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(\sprintf('Invalid %s request option: unknown version provided', $option));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $min
|
||||||
|
* @param mixed $max
|
||||||
|
*/
|
||||||
|
public static function assertRange($min, $max): void
|
||||||
|
{
|
||||||
|
if ($min === null || $max === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::ordinal('crypto_method_max', $max) < self::ordinal('crypto_method', $min)) {
|
||||||
|
throw new \InvalidArgumentException('Invalid crypto_method_max request option: maximum TLS version must be greater than or equal to crypto_method');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public static function streamProtocolVersion(string $option, $value): int
|
||||||
|
{
|
||||||
|
if ($value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT) {
|
||||||
|
return self::requireStreamProto('STREAM_CRYPTO_PROTO_TLSv1_0', $option);
|
||||||
|
}
|
||||||
|
if ($value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT) {
|
||||||
|
return self::requireStreamProto('STREAM_CRYPTO_PROTO_TLSv1_1', $option);
|
||||||
|
}
|
||||||
|
if ($value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT) {
|
||||||
|
return self::requireStreamProto('STREAM_CRYPTO_PROTO_TLSv1_2', $option);
|
||||||
|
}
|
||||||
|
if (\defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT) {
|
||||||
|
return self::requireStreamProto('STREAM_CRYPTO_PROTO_TLSv1_3', $option);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(\sprintf('Invalid %s request option: unknown version provided', $option));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a STREAM_CRYPTO_PROTO_* constant. The ssl.max_proto_version
|
||||||
|
* context option and these constants were added in PHP 7.3.0 (TLS 1.3 in
|
||||||
|
* 7.4.0); on older runtimes the option cannot be honored, so reject loudly.
|
||||||
|
*/
|
||||||
|
private static function requireStreamProto(string $constant, string $option): int
|
||||||
|
{
|
||||||
|
if (\defined($constant)) {
|
||||||
|
/** @var int */
|
||||||
|
return \constant($constant);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(\sprintf(
|
||||||
|
'Invalid %s request option: maximum TLS version control is not supported by your version of PHP',
|
||||||
|
$option
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -72,8 +72,7 @@ class MessageFormatter implements MessageFormatterInterface
|
||||||
{
|
{
|
||||||
$cache = [];
|
$cache = [];
|
||||||
|
|
||||||
/** @var string */
|
$result = \preg_replace_callback(
|
||||||
return \preg_replace_callback(
|
|
||||||
'/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
|
'/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
|
||||||
function (array $matches) use ($request, $response, $error, &$cache) {
|
function (array $matches) use ($request, $response, $error, &$cache) {
|
||||||
if (isset($cache[$matches[1]])) {
|
if (isset($cache[$matches[1]])) {
|
||||||
|
|
@ -90,7 +89,7 @@ class MessageFormatter implements MessageFormatterInterface
|
||||||
break;
|
break;
|
||||||
case 'req_headers':
|
case 'req_headers':
|
||||||
$result = \trim($request->getMethod()
|
$result = \trim($request->getMethod()
|
||||||
.' '.$request->getRequestTarget())
|
.' '.$request->getRequestTarget(), " \n\r\t\0\x0B")
|
||||||
.' HTTP/'.$request->getProtocolVersion()."\r\n"
|
.' HTTP/'.$request->getProtocolVersion()."\r\n"
|
||||||
.$this->headers($request);
|
.$this->headers($request);
|
||||||
break;
|
break;
|
||||||
|
|
@ -182,6 +181,12 @@ class MessageFormatter implements MessageFormatterInterface
|
||||||
},
|
},
|
||||||
$this->template
|
$this->template
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($result === null) {
|
||||||
|
throw new \RuntimeException('Unable to format message: '.\preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -194,6 +199,6 @@ class MessageFormatter implements MessageFormatterInterface
|
||||||
$result .= $name.': '.\implode(', ', $values)."\r\n";
|
$result .= $name.': '.\implode(', ', $values)."\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return \trim($result);
|
return \trim($result, " \n\r\t\0\x0B");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
vendor/guzzlehttp/guzzle/src/Multiplexing.php
vendored
Normal file
26
vendor/guzzlehttp/guzzle/src/Multiplexing.php
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace GuzzleHttp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplexing modes for the "multiplex" request option.
|
||||||
|
*
|
||||||
|
* NONE disables multiplexing for a whole handler when passed as the
|
||||||
|
* "multiplex" client configuration option or, when constructing a handler
|
||||||
|
* directly, as the CurlMultiHandler "multiplex" constructor option. As a
|
||||||
|
* request option value it guarantees the transfer does not share its
|
||||||
|
* connection with any concurrent transfer, and is accepted only where that
|
||||||
|
* guarantee holds.
|
||||||
|
*/
|
||||||
|
final class Multiplexing
|
||||||
|
{
|
||||||
|
public const NONE = 'none';
|
||||||
|
public const EAGER = 'eager';
|
||||||
|
public const WAIT = 'wait';
|
||||||
|
public const REQUIRE_EAGER = 'require_eager';
|
||||||
|
public const REQUIRE_WAIT = 'require_wait';
|
||||||
|
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
2
vendor/guzzlehttp/guzzle/src/Pool.php
vendored
2
vendor/guzzlehttp/guzzle/src/Pool.php
vendored
|
|
@ -71,7 +71,7 @@ class Pool implements PromisorInterface
|
||||||
} elseif (\is_callable($rfn)) {
|
} elseif (\is_callable($rfn)) {
|
||||||
yield $key => $rfn($opts);
|
yield $key => $rfn($opts);
|
||||||
} else {
|
} else {
|
||||||
throw new \InvalidArgumentException('Each value yielded by the iterator must be a Psr7\Http\Message\RequestInterface or a callable that returns a promise that fulfills with a Psr7\Message\Http\ResponseInterface object.');
|
throw new \InvalidArgumentException('Each value yielded by the iterator must be a Psr\Http\Message\RequestInterface or a callable that returns a promise that fulfills with a Psr\Http\Message\ResponseInterface object.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace GuzzleHttp;
|
namespace GuzzleHttp;
|
||||||
|
|
||||||
use GuzzleHttp\Exception\BadResponseException;
|
use GuzzleHttp\Exception\BadResponseException;
|
||||||
|
use GuzzleHttp\Exception\RequestException;
|
||||||
use GuzzleHttp\Exception\TooManyRedirectsException;
|
use GuzzleHttp\Exception\TooManyRedirectsException;
|
||||||
use GuzzleHttp\Promise\PromiseInterface;
|
use GuzzleHttp\Promise\PromiseInterface;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
@ -104,6 +105,10 @@ class RedirectMiddleware
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The caller's delay applies once, before the initial request, not
|
||||||
|
// before each followed redirect.
|
||||||
|
unset($options['delay']);
|
||||||
|
|
||||||
$promise = $this($nextRequest, $options);
|
$promise = $this($nextRequest, $options);
|
||||||
|
|
||||||
// Add headers to be able to track history of redirects.
|
// Add headers to be able to track history of redirects.
|
||||||
|
|
@ -169,11 +174,13 @@ class RedirectMiddleware
|
||||||
if ($statusCode == 303
|
if ($statusCode == 303
|
||||||
|| ($statusCode <= 302 && !$options['allow_redirects']['strict'])
|
|| ($statusCode <= 302 && !$options['allow_redirects']['strict'])
|
||||||
) {
|
) {
|
||||||
$safeMethods = ['GET', 'HEAD', 'OPTIONS'];
|
|
||||||
$requestMethod = $request->getMethod();
|
$requestMethod = $request->getMethod();
|
||||||
|
|
||||||
$modify['method'] = in_array($requestMethod, $safeMethods) ? $requestMethod : 'GET';
|
if ($requestMethod !== 'QUERY' || !\in_array($statusCode, [301, 302], true)) {
|
||||||
|
$modify['method'] = \in_array($requestMethod, ['GET', 'HEAD', 'OPTIONS'], true) ? $requestMethod : 'GET';
|
||||||
$modify['body'] = '';
|
$modify['body'] = '';
|
||||||
|
$modify['remove_headers'] = ['Content-Length', 'Transfer-Encoding'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$uri = self::redirectUri($request, $response, $protocols);
|
$uri = self::redirectUri($request, $response, $protocols);
|
||||||
|
|
@ -183,7 +190,20 @@ class RedirectMiddleware
|
||||||
}
|
}
|
||||||
|
|
||||||
$modify['uri'] = $uri;
|
$modify['uri'] = $uri;
|
||||||
|
|
||||||
|
// The body only needs to be rewound when the next request reuses it.
|
||||||
|
if (!isset($modify['body'])) {
|
||||||
|
try {
|
||||||
Psr7\Message::rewindBody($request);
|
Psr7\Message::rewindBody($request);
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
throw new RequestException(
|
||||||
|
'Redirect failed because the request body could not be rewound: '.$e->getMessage(),
|
||||||
|
$request,
|
||||||
|
$response,
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add the Referer header if it is told to do so and only
|
// Add the Referer header if it is told to do so and only
|
||||||
// add the header if we are not redirecting from https to http.
|
// add the header if we are not redirecting from https to http.
|
||||||
|
|
|
||||||
132
vendor/guzzlehttp/guzzle/src/RequestOptions.php
vendored
132
vendor/guzzlehttp/guzzle/src/RequestOptions.php
vendored
|
|
@ -5,7 +5,7 @@ namespace GuzzleHttp;
|
||||||
/**
|
/**
|
||||||
* This class contains a list of built-in Guzzle request options.
|
* This class contains a list of built-in Guzzle request options.
|
||||||
*
|
*
|
||||||
* @see https://github.com/guzzle/guzzle/blob/7.11/docs/request-options.md
|
* @see https://github.com/guzzle/guzzle/blob/7.15/docs/request-options.md
|
||||||
*/
|
*/
|
||||||
final class RequestOptions
|
final class RequestOptions
|
||||||
{
|
{
|
||||||
|
|
@ -20,7 +20,9 @@ final class RequestOptions
|
||||||
* - max: (int, default=5) maximum number of allowed redirects.
|
* - max: (int, default=5) maximum number of allowed redirects.
|
||||||
* - strict: (bool, default=false) Set to true to use strict redirects
|
* - strict: (bool, default=false) Set to true to use strict redirects
|
||||||
* meaning redirect POST requests with POST requests vs. doing what most
|
* meaning redirect POST requests with POST requests vs. doing what most
|
||||||
* browsers do which is redirect POST requests with GET requests
|
* browsers do which is redirect POST requests with GET requests. The
|
||||||
|
* QUERY method keeps its method and body across non-strict 301 and 302
|
||||||
|
* redirects, and a 303 redirect is followed with a body-less GET.
|
||||||
* - referer: (bool, default=false) Set to true to enable the Referer
|
* - referer: (bool, default=false) Set to true to enable the Referer
|
||||||
* header.
|
* header.
|
||||||
* - protocols: (non-empty-array<array-key, string>, default=['http', 'https'])
|
* - protocols: (non-empty-array<array-key, string>, default=['http', 'https'])
|
||||||
|
|
@ -96,6 +98,23 @@ final class RequestOptions
|
||||||
*/
|
*/
|
||||||
public const CRYPTO_METHOD = 'crypto_method';
|
public const CRYPTO_METHOD = 'crypto_method';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* crypto_method_max: (int) A value describing the maximum TLS protocol
|
||||||
|
* version to use.
|
||||||
|
*
|
||||||
|
* This setting must be set to one of the
|
||||||
|
* ``STREAM_CRYPTO_METHOD_TLS*_CLIENT`` constants. On the stream handler,
|
||||||
|
* PHP 7.3 or higher is required to set a maximum TLS version, and PHP 7.4
|
||||||
|
* or higher is required to use TLS 1.3. cURL 7.54.0 or higher is required
|
||||||
|
* in order to specify a maximum TLS version with the cURL handler.
|
||||||
|
*/
|
||||||
|
public const CRYPTO_METHOD_MAX = 'crypto_method_max';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* curl: (array) Raw cURL options to apply when using a built-in cURL handler.
|
||||||
|
*/
|
||||||
|
public const CURL = 'curl';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* debug: (bool|resource) Set to true or set to a PHP stream returned by
|
* debug: (bool|resource) Set to true or set to a PHP stream returned by
|
||||||
* fopen() enable debug output with the HTTP handler used to send a
|
* fopen() enable debug output with the HTTP handler used to send a
|
||||||
|
|
@ -183,6 +202,93 @@ final class RequestOptions
|
||||||
*/
|
*/
|
||||||
public const MULTIPART = 'multipart';
|
public const MULTIPART = 'multipart';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* multiplex: (string) Controls how a request sent through a built-in
|
||||||
|
* cURL handler relates to shared, multiplexed connections: how an HTTP/2
|
||||||
|
* request pursues one, or, with Multiplexing::NONE, whether the transfer
|
||||||
|
* may share its connection at all. When the option is not set,
|
||||||
|
* multiplexing is left to libcurl: nothing waits, and established
|
||||||
|
* multiplex-capable connections are still shared. Use
|
||||||
|
* Multiplexing::EAGER to explicitly never wait for pending connections,
|
||||||
|
* Multiplexing::WAIT to wait on libcurl-eligible pending connections with
|
||||||
|
* CURLOPT_PIPEWAIT, normally to the same origin,
|
||||||
|
* Multiplexing::REQUIRE_EAGER to fail unless a multiplexed protocol is
|
||||||
|
* guaranteed while dialing eagerly, or Multiplexing::REQUIRE_WAIT for the
|
||||||
|
* same guarantee while also waiting on pending connections. The required
|
||||||
|
* modes require a handler that permits actual multiplexing, not merely a
|
||||||
|
* multiplexed protocol, and are rejected on a Multiplexing::NONE handler.
|
||||||
|
* The stream handler ignores EAGER and WAIT, and rejects the required
|
||||||
|
* family; CurlHandler has no multi handle to multiplex over. Explicit
|
||||||
|
* modes reject deprecated raw cURL options they conflict with: the
|
||||||
|
* required family cannot be combined with a raw CURLOPT_HTTP_VERSION,
|
||||||
|
* CURLOPT_URL, or CURLOPT_FOLLOWLOCATION; no explicit mode can be
|
||||||
|
* combined with a raw CURLOPT_PIPEWAIT on the CurlMultiHandler; and
|
||||||
|
* Multiplexing::NONE on a CurlMultiHandler that permits multiplexing
|
||||||
|
* cannot be combined with the raw CURLOPT_HTTP_VERSION, CURLOPT_HTTPAUTH
|
||||||
|
* (including the "auth" request option's "digest" and "ntlm" modes,
|
||||||
|
* which set it), CURLOPT_PROXYAUTH, CURLOPT_FOLLOWLOCATION,
|
||||||
|
* CURLOPT_HTTPHEADER, CURLOPT_ALTSVC, CURLOPT_ALTSVC_CTRL, or
|
||||||
|
* CURLOPT_PROXYTYPE cURL options. The required family also
|
||||||
|
* rejects final CURLOPT_HTTPAUTH masks that permit NTLM, which libcurl
|
||||||
|
* retries over HTTP/1.1. The required family validates its cleartext
|
||||||
|
* proxy rule against the final cURL configuration, after raw options
|
||||||
|
* such as CURLOPT_PROXY and CURLOPT_PRE_PROXY are applied; only the
|
||||||
|
* exact raw CURLOPT_NOPROXY wildcard '*' disables the primary proxy and
|
||||||
|
* pre-proxy there, and raw host-specific patterns are conservatively
|
||||||
|
* treated as leaving them active. These rejections are
|
||||||
|
* configuration-conflict checks, not remote security checks.
|
||||||
|
*
|
||||||
|
* Multiplexing::NONE disables multiplexing for a whole handler when
|
||||||
|
* passed as the "multiplex" client configuration option, which
|
||||||
|
* configures the default handler and also becomes the default request
|
||||||
|
* option, or, when constructing a handler directly, as the
|
||||||
|
* CurlMultiHandler "multiplex" constructor option. A handler
|
||||||
|
* configured with Multiplexing::NONE rejects explicitly requested wait
|
||||||
|
* modes as a configuration conflict when the transfer would actually
|
||||||
|
* wait, and always rejects the required modes, because they require a
|
||||||
|
* handler that permits actual multiplexing, not merely a multiplexed
|
||||||
|
* protocol. As a request option value, Multiplexing::NONE guarantees the
|
||||||
|
* transfer does not share its connection with any concurrent transfer.
|
||||||
|
* Multiplexing::NONE does not force HTTP/1.1: on a Multiplexing::NONE
|
||||||
|
* handler, HTTP/2 still negotiates and each transfer keeps its
|
||||||
|
* connection to itself.
|
||||||
|
*
|
||||||
|
* The request option value is accepted exactly where the guarantee
|
||||||
|
* holds and can be verified: on a CurlMultiHandler configured with
|
||||||
|
* Multiplexing::NONE, for requests whose declared protocol version is
|
||||||
|
* HTTP/1.x, on CurlHandler, and on the stream handler, which never
|
||||||
|
* multiplexes. An HTTP/2 request with a Multiplexing::NONE request
|
||||||
|
* option is rejected on a CurlMultiHandler that permits multiplexing.
|
||||||
|
* On a CurlMultiHandler that permits multiplexing, Multiplexing::NONE
|
||||||
|
* is also rejected with a custom "handle_factory", alongside a raw
|
||||||
|
* CURLMOPT_PIPELINING cURL multi option, and combined with the raw
|
||||||
|
* CURLOPT_HTTP_VERSION, CURLOPT_HTTPAUTH (including the "auth" request
|
||||||
|
* option's "digest" and "ntlm" modes, which set it), CURLOPT_PROXYAUTH,
|
||||||
|
* CURLOPT_FOLLOWLOCATION, CURLOPT_HTTPHEADER, CURLOPT_ALTSVC,
|
||||||
|
* CURLOPT_ALTSVC_CTRL, or CURLOPT_PROXYTYPE cURL options. It is also
|
||||||
|
* rejected when the request carries an Expect: 100-continue header (its
|
||||||
|
* 417 retries select connections outside the safeguards; remove an
|
||||||
|
* explicitly supplied header, or set the "expect" request option to
|
||||||
|
* false to prevent it being added automatically).
|
||||||
|
*
|
||||||
|
* On a client whose multi handler permits multiplexing, the ordinary
|
||||||
|
* non-streaming default stack - both cURL handlers available and no
|
||||||
|
* connection caps forcing multi-only routing - runs synchronous
|
||||||
|
* requests on the CurlHandler path, which satisfies the guarantee for
|
||||||
|
* any protocol version, while asynchronous requests run on the
|
||||||
|
* CurlMultiHandler, so an HTTP/2 request with Multiplexing::NONE
|
||||||
|
* succeeds synchronously and is rejected asynchronously on the same
|
||||||
|
* client. Keep-alive reuse between consecutive transfers is
|
||||||
|
* unaffected, except on libcurl versions below 7.77.0 and from 8.11.0
|
||||||
|
* through 8.12.1, where an accepted HTTP/1.x request on a multiplexing
|
||||||
|
* CurlMultiHandler forces a fresh connection. Custom handlers receive
|
||||||
|
* the "multiplex" option unchanged: its semantics are handler-defined,
|
||||||
|
* Guzzle does not guarantee it is honored, and a client-level
|
||||||
|
* Multiplexing::NONE with a custom handler flows to it as a default
|
||||||
|
* request option without client-side enforcement.
|
||||||
|
*/
|
||||||
|
public const MULTIPLEX = 'multiplex';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* on_headers: (callable) A callable that is invoked when the HTTP headers
|
* on_headers: (callable) A callable that is invoked when the HTTP headers
|
||||||
* of the response have been received but the body has not yet begun to
|
* of the response have been received but the body has not yet begun to
|
||||||
|
|
@ -201,6 +307,17 @@ final class RequestOptions
|
||||||
*/
|
*/
|
||||||
public const ON_STATS = 'on_stats';
|
public const ON_STATS = 'on_stats';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* on_trailers: (callable) A callable that is invoked by the built-in cURL
|
||||||
|
* handlers once per successful transfer, after the response body has been
|
||||||
|
* received, with an associative array of the parsed HTTP trailers followed
|
||||||
|
* by the response. Trailer field names are lowercased and grouped
|
||||||
|
* case-insensitively; values keep their wire order. Malformed trailer
|
||||||
|
* field lines are discarded before parsing. Trailer fields are reported
|
||||||
|
* separately from response headers and are never merged into the response.
|
||||||
|
*/
|
||||||
|
public const ON_TRAILERS = 'on_trailers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* progress: (callable) Defines a function to invoke when transfer
|
* progress: (callable) Defines a function to invoke when transfer
|
||||||
* progress is made. The function accepts the following positional
|
* progress is made. The function accepts the following positional
|
||||||
|
|
@ -270,6 +387,12 @@ final class RequestOptions
|
||||||
*/
|
*/
|
||||||
public const STREAM = 'stream';
|
public const STREAM = 'stream';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stream_context: (array) PHP stream context options to merge into the
|
||||||
|
* context used by the built-in stream handler.
|
||||||
|
*/
|
||||||
|
public const STREAM_CONTEXT = 'stream_context';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* verify: (bool|string, default=true) Describes the SSL certificate
|
* verify: (bool|string, default=true) Describes the SSL certificate
|
||||||
* verification behavior of a request. Set to true to enable SSL
|
* verification behavior of a request. Set to true to enable SSL
|
||||||
|
|
@ -292,6 +415,11 @@ final class RequestOptions
|
||||||
*/
|
*/
|
||||||
public const READ_TIMEOUT = 'read_timeout';
|
public const READ_TIMEOUT = 'read_timeout';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retries: (int) Current retry count used by the retry middleware.
|
||||||
|
*/
|
||||||
|
public const RETRIES = 'retries';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* version: (string|int|float) Specifies the HTTP protocol version to attempt
|
* version: (string|int|float) Specifies the HTTP protocol version to attempt
|
||||||
* to use.
|
* to use.
|
||||||
|
|
|
||||||
596
vendor/guzzlehttp/guzzle/src/Utils.php
vendored
596
vendor/guzzlehttp/guzzle/src/Utils.php
vendored
|
|
@ -6,6 +6,7 @@ use GuzzleHttp\Exception\InvalidArgumentException;
|
||||||
use GuzzleHttp\Handler\CurlHandler;
|
use GuzzleHttp\Handler\CurlHandler;
|
||||||
use GuzzleHttp\Handler\CurlMultiHandler;
|
use GuzzleHttp\Handler\CurlMultiHandler;
|
||||||
use GuzzleHttp\Handler\CurlShareHandleState;
|
use GuzzleHttp\Handler\CurlShareHandleState;
|
||||||
|
use GuzzleHttp\Handler\CurlVersion;
|
||||||
use GuzzleHttp\Handler\Proxy;
|
use GuzzleHttp\Handler\Proxy;
|
||||||
use GuzzleHttp\Handler\StreamHandler;
|
use GuzzleHttp\Handler\StreamHandler;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
@ -20,9 +21,18 @@ final class Utils
|
||||||
*
|
*
|
||||||
* @return string Returns a string containing the type of the variable and
|
* @return string Returns a string containing the type of the variable and
|
||||||
* if a class is provided, the class name.
|
* if a class is provided, the class name.
|
||||||
|
*
|
||||||
|
* @deprecated Utils::describeType() will be removed in guzzlehttp/guzzle:8.0. Use get_debug_type() instead.
|
||||||
*/
|
*/
|
||||||
public static function describeType($input): string
|
public static function describeType($input): string
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation(
|
||||||
|
'guzzlehttp/guzzle',
|
||||||
|
'7.12',
|
||||||
|
'%s() is deprecated and will be removed in 8.0. Use get_debug_type() instead.',
|
||||||
|
__METHOD__
|
||||||
|
);
|
||||||
|
|
||||||
switch (\gettype($input)) {
|
switch (\gettype($input)) {
|
||||||
case 'object':
|
case 'object':
|
||||||
return 'object('.\get_class($input).')';
|
return 'object('.\get_class($input).')';
|
||||||
|
|
@ -35,7 +45,7 @@ final class Utils
|
||||||
/** @var string $varDumpContent */
|
/** @var string $varDumpContent */
|
||||||
$varDumpContent = \ob_get_clean();
|
$varDumpContent = \ob_get_clean();
|
||||||
|
|
||||||
return \str_replace('double(', 'float(', \rtrim($varDumpContent));
|
return \str_replace('double(', 'float(', \rtrim($varDumpContent, " \n\r\t\0\x0B"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +61,7 @@ final class Utils
|
||||||
|
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
$parts = \explode(':', $line, 2);
|
$parts = \explode(':', $line, 2);
|
||||||
$headers[\trim($parts[0])][] = isset($parts[1]) ? \trim($parts[1]) : null;
|
$headers[\trim($parts[0], " \n\r\t\0\x0B")][] = isset($parts[1]) ? \trim($parts[1], " \n\r\t\0\x0B") : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $headers;
|
return $headers;
|
||||||
|
|
@ -81,7 +91,7 @@ final class Utils
|
||||||
*
|
*
|
||||||
* The returned handler is not wrapped by any default middlewares.
|
* The returned handler is not wrapped by any default middlewares.
|
||||||
*
|
*
|
||||||
* @param array{transport_sharing?: mixed} $handlerOptions Handler constructor options.
|
* @param array{transport_sharing?: mixed, max_host_connections?: mixed, max_total_connections?: mixed, multiplex?: mixed} $handlerOptions Handler constructor options.
|
||||||
*
|
*
|
||||||
* @return callable(RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system.
|
* @return callable(RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system.
|
||||||
*
|
*
|
||||||
|
|
@ -89,64 +99,148 @@ final class Utils
|
||||||
*/
|
*/
|
||||||
public static function chooseHandler(array $handlerOptions = []): callable
|
public static function chooseHandler(array $handlerOptions = []): callable
|
||||||
{
|
{
|
||||||
$handler = null;
|
|
||||||
$sharingMode = CurlShareHandleState::normalizeMode($handlerOptions['transport_sharing'] ?? null, 'transport_sharing');
|
$sharingMode = CurlShareHandleState::normalizeMode($handlerOptions['transport_sharing'] ?? null, 'transport_sharing');
|
||||||
$sharingRequested = $sharingMode !== TransportSharing::NONE;
|
$sharingRequired = self::isTransportSharingRequired($sharingMode);
|
||||||
$sharingRequired = $sharingMode === TransportSharing::HANDLER_REQUIRE;
|
$connectionCapsRequired = self::hasConnectionCapOptions($handlerOptions);
|
||||||
$curlHandlerOptions = [];
|
$handler = self::createCurlHandler($sharingMode, $handlerOptions);
|
||||||
$curlSupported = \defined('CURLOPT_CUSTOMREQUEST')
|
|
||||||
&& \function_exists('curl_version')
|
|
||||||
&& version_compare(curl_version()['version'], '7.21.2') >= 0
|
|
||||||
&& (\function_exists('curl_multi_exec') || \function_exists('curl_exec'));
|
|
||||||
|
|
||||||
if ($sharingRequired && !$curlSupported) {
|
if ($sharingRequired && $handler === null) {
|
||||||
throw new \RuntimeException('Required transport sharing requires the PHP cURL extension, curl_exec() or curl_multi_exec(), and libcurl 7.21.2 or higher.');
|
throw new \RuntimeException('Required transport sharing requires the PHP cURL extension, curl_exec() or curl_multi_exec(), and libcurl 7.21.2 or higher.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($curlSupported) {
|
|
||||||
if ($sharingRequested) {
|
|
||||||
$shareState = CurlShareHandleState::fromOption($sharingMode);
|
|
||||||
if ($shareState !== null) {
|
|
||||||
$curlHandlerOptions['transport_sharing'] = $shareState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) {
|
|
||||||
$handler = Proxy::wrapSync(new CurlMultiHandler($curlHandlerOptions), new CurlHandler($curlHandlerOptions));
|
|
||||||
} elseif (\function_exists('curl_exec')) {
|
|
||||||
$handler = new CurlHandler($curlHandlerOptions);
|
|
||||||
} elseif (\function_exists('curl_multi_exec')) {
|
|
||||||
$handler = new CurlMultiHandler($curlHandlerOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (\ini_get('allow_url_fopen')) {
|
if (\ini_get('allow_url_fopen')) {
|
||||||
$streamHandler = new StreamHandler();
|
return self::addStreamHandler($handler, $sharingMode, $sharingRequired, self::connectionCapOptions($handlerOptions));
|
||||||
if ($sharingRequired) {
|
|
||||||
$streamHandler = self::wrapStreamHandlerTransportSharing($streamHandler, $sharingMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
$handler = $handler
|
|
||||||
? Proxy::wrapStreaming($handler, $streamHandler)
|
|
||||||
: $streamHandler;
|
|
||||||
} elseif (!$handler) {
|
|
||||||
throw new \RuntimeException('GuzzleHttp requires cURL, the allow_url_fopen ini setting, or a custom HTTP handler.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($handler !== null) {
|
||||||
return $handler;
|
return $handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function wrapStreamHandlerTransportSharing(callable $handler, string $sharingMode): callable
|
if ($connectionCapsRequired) {
|
||||||
{
|
throw new \RuntimeException('Connection cap options require a cap-capable cURL multi handler or the allow_url_fopen ini setting for stream fallback.');
|
||||||
return static function (RequestInterface $request, array $options) use ($handler, $sharingMode): Promise\PromiseInterface {
|
|
||||||
if (\array_key_exists('transport_sharing', $options)) {
|
|
||||||
CurlShareHandleState::normalizeMode($options['transport_sharing'], 'transport_sharing');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$options['transport_sharing'] = $sharingMode;
|
throw new \RuntimeException('GuzzleHttp requires cURL, the allow_url_fopen ini setting, or a custom HTTP handler.');
|
||||||
|
}
|
||||||
|
|
||||||
return $handler($request, $options);
|
private static function isTransportSharingRequired(string $sharingMode): bool
|
||||||
};
|
{
|
||||||
|
return $sharingMode === TransportSharing::HANDLER_REQUIRE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{max_host_connections?: mixed, max_total_connections?: mixed} $handlerOptions
|
||||||
|
*/
|
||||||
|
private static function hasConnectionCapOptions(array $handlerOptions): bool
|
||||||
|
{
|
||||||
|
return self::connectionCapOptions($handlerOptions) !== [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{max_host_connections?: mixed, max_total_connections?: mixed, multiplex?: mixed} $handlerOptions
|
||||||
|
*
|
||||||
|
* @return (callable(RequestInterface, array): Promise\PromiseInterface)|null
|
||||||
|
*/
|
||||||
|
private static function createCurlHandler(string $sharingMode, array $handlerOptions): ?callable
|
||||||
|
{
|
||||||
|
if (!\defined('CURLOPT_CUSTOMREQUEST') || !CurlVersion::supportsCurlHandler()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$connectionCapOptions = self::connectionCapOptions($handlerOptions);
|
||||||
|
if ($connectionCapOptions !== [] && (!CurlVersion::supportsConnectionCaps() || !\function_exists('curl_multi_exec'))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$curlHandlerOptions = self::createCurlHandlerOptions($sharingMode);
|
||||||
|
$curlMultiHandlerOptions = $curlHandlerOptions + $connectionCapOptions;
|
||||||
|
if (($handlerOptions['multiplex'] ?? null) === Multiplexing::NONE) {
|
||||||
|
// Forwarded to the CurlMultiHandler only: CurlHandler and
|
||||||
|
// StreamHandler validate known options, and both satisfy NONE
|
||||||
|
// per-request without a handler option.
|
||||||
|
$curlMultiHandlerOptions['multiplex'] = Multiplexing::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) {
|
||||||
|
$multiHandler = new CurlMultiHandler($curlMultiHandlerOptions);
|
||||||
|
|
||||||
|
if ($connectionCapOptions !== []) {
|
||||||
|
// Connection caps only govern transfers on the multi handle, so
|
||||||
|
// the synchronous CurlHandler fast path would escape them.
|
||||||
|
return $multiHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Proxy::wrapSync($multiHandler, new CurlHandler($curlHandlerOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($connectionCapOptions === [] && \function_exists('curl_exec')) {
|
||||||
|
return new CurlHandler($curlHandlerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\function_exists('curl_multi_exec')) {
|
||||||
|
return new CurlMultiHandler($curlMultiHandlerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
private static function createCurlHandlerOptions(string $sharingMode): array
|
||||||
|
{
|
||||||
|
if ($sharingMode === TransportSharing::NONE) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$shareState = CurlShareHandleState::fromOption($sharingMode);
|
||||||
|
|
||||||
|
return $shareState === null ? [] : ['transport_sharing' => $shareState];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{max_host_connections?: mixed, max_total_connections?: mixed} $handlerOptions
|
||||||
|
*
|
||||||
|
* @return array{max_host_connections?: int, max_total_connections?: int}
|
||||||
|
*/
|
||||||
|
private static function connectionCapOptions(array $handlerOptions): array
|
||||||
|
{
|
||||||
|
$options = [];
|
||||||
|
foreach (['max_host_connections', 'max_total_connections'] as $capOption) {
|
||||||
|
$value = $handlerOptions[$capOption] ?? null;
|
||||||
|
if ($value === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_int($value) || $value < 1) {
|
||||||
|
throw new InvalidArgumentException(\sprintf('%s must be a positive integer.', $capOption));
|
||||||
|
}
|
||||||
|
|
||||||
|
$options[$capOption] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param (callable(RequestInterface, array): Promise\PromiseInterface)|null $handler
|
||||||
|
* @param array{max_host_connections?: int, max_total_connections?: int} $connectionCapOptions
|
||||||
|
*
|
||||||
|
* @return callable(RequestInterface, array): Promise\PromiseInterface
|
||||||
|
*/
|
||||||
|
private static function addStreamHandler(?callable $handler, string $sharingMode, bool $sharingRequired, array $connectionCapOptions): callable
|
||||||
|
{
|
||||||
|
$streamHandler = new StreamHandler(['transport_sharing' => $sharingMode] + $connectionCapOptions);
|
||||||
|
|
||||||
|
if ($handler === null) {
|
||||||
|
return $streamHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$sharingRequired) {
|
||||||
|
$handler = Proxy::wrapTlsFallback($handler, $streamHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Proxy::wrapStreaming($handler, $streamHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -174,6 +268,8 @@ final class Utils
|
||||||
*/
|
*/
|
||||||
public static function defaultCaBundle(): string
|
public static function defaultCaBundle(): string
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. This method is not needed in PHP 5.6+.', __METHOD__);
|
||||||
|
|
||||||
static $cached = null;
|
static $cached = null;
|
||||||
static $cafiles = [
|
static $cafiles = [
|
||||||
// Red Hat, CentOS, Fedora (provided by the ca-certificates package)
|
// Red Hat, CentOS, Fedora (provided by the ca-certificates package)
|
||||||
|
|
@ -216,14 +312,14 @@ final class Utils
|
||||||
No system CA bundle could be found in any of the the common system locations.
|
No system CA bundle could be found in any of the the common system locations.
|
||||||
PHP versions earlier than 5.6 are not properly configured to use the system's
|
PHP versions earlier than 5.6 are not properly configured to use the system's
|
||||||
CA bundle by default. In order to verify peer certificates, you will need to
|
CA bundle by default. In order to verify peer certificates, you will need to
|
||||||
supply the path on disk to a certificate bundle to the 'verify' request
|
supply the path on disk to a certificate bundle to the 'verify' request option:
|
||||||
option: https://github.com/guzzle/guzzle/blob/7.11/docs/request-options.md#verify. If
|
https://github.com/guzzle/guzzle/blob/7.15/docs/request-options.md#verify. If
|
||||||
you do not need a specific certificate bundle, then Mozilla provides a commonly
|
you do not need a specific certificate bundle, then Mozilla provides a commonly
|
||||||
used CA bundle which can be downloaded here (provided by the maintainer of
|
used CA bundle which can be downloaded here (provided by the maintainer of
|
||||||
cURL): https://curl.haxx.se/ca/cacert.pem. Once you have a CA bundle available
|
cURL): https://curl.se/ca/cacert.pem. Once you have a CA bundle available on
|
||||||
on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path
|
disk, you can set the 'openssl.cafile' PHP ini setting to point to the path to
|
||||||
to the file, allowing you to omit the 'verify' request option. See
|
the file, allowing you to omit the 'verify' request option. See
|
||||||
https://curl.haxx.se/docs/sslcerts.html for more information.
|
https://curl.se/docs/sslcerts.html for more information.
|
||||||
EOT
|
EOT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +332,7 @@ EOT
|
||||||
{
|
{
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach (\array_keys($headers) as $key) {
|
foreach (\array_keys($headers) as $key) {
|
||||||
$result[\strtolower((string) $key)] = $key;
|
$result[Psr7\Utils::asciiToLower((string) $key)] = $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|
@ -275,19 +371,22 @@ EOT
|
||||||
/**
|
/**
|
||||||
* Returns true if the provided host matches any of the no proxy areas.
|
* Returns true if the provided host matches any of the no proxy areas.
|
||||||
*
|
*
|
||||||
* This method will strip a port from the host if it is present. Each pattern
|
* This method will strip a port from the host if it is present. Domain
|
||||||
* can be matched with an exact match (e.g., "foo.com" == "foo.com") or a
|
* patterns are matched case-insensitively. Exact IP literal patterns are
|
||||||
* partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" ==
|
* matched by their normalized binary address.
|
||||||
* "baz.foo.com", but ".foo.com" != "foo.com").
|
|
||||||
*
|
*
|
||||||
* Areas are matched in the following cases:
|
* Areas are matched in the following cases:
|
||||||
* 1. "*" (without quotes) always matches any hosts.
|
* 1. "*" (without quotes) always matches any hosts.
|
||||||
* 2. An exact match.
|
* 2. An exact domain or IP literal match.
|
||||||
* 3. The area starts with "." and the area is the last part of the host. e.g.
|
* 3. A bare domain matches itself and its subdomains. e.g. 'mit.edu' will
|
||||||
|
* match 'mit.edu' and 'foo.mit.edu'.
|
||||||
|
* 4. The area starts with "." and the area is the last part of the host. e.g.
|
||||||
* '.mit.edu' will match any host that ends with '.mit.edu'.
|
* '.mit.edu' will match any host that ends with '.mit.edu'.
|
||||||
|
* 5. IP CIDR entries match IP literal hosts. e.g. '192.168.0.0/16' will
|
||||||
|
* match '192.168.1.10' and 'fd00::/8' will match '[fd00::1]'.
|
||||||
*
|
*
|
||||||
* @param string $host Host to check against the patterns.
|
* @param string $host Host to check against the patterns.
|
||||||
* @param string[] $noProxyArray An array of host patterns.
|
* @param string[] $noProxyArray An array of host or CIDR patterns.
|
||||||
*
|
*
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
|
|
@ -297,43 +396,23 @@ EOT
|
||||||
throw new InvalidArgumentException('Empty host provided');
|
throw new InvalidArgumentException('Empty host provided');
|
||||||
}
|
}
|
||||||
|
|
||||||
$host = self::normalizeNoProxyHost($host, true);
|
$target = self::parseNoProxyHostString($host);
|
||||||
|
if ($target === null) {
|
||||||
foreach ($noProxyArray as $area) {
|
|
||||||
// Always match on wildcards.
|
|
||||||
if ($area === '*') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($area === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$area = self::normalizeNoProxyHost($area, false);
|
|
||||||
|
|
||||||
if ($area === $host) {
|
|
||||||
// Exact matches.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Special match if the area when prefixed with ".". Remove any
|
|
||||||
// existing leading "." and add a new leading ".".
|
|
||||||
$area = '.'.\ltrim($area, '.');
|
|
||||||
if (
|
|
||||||
\strpos($host, ':') === false
|
|
||||||
&& \strpos($area, ':') === false
|
|
||||||
&& \substr($host, -\strlen($area)) === $area
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return self::matchesNoProxyList($target, $noProxyArray);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the provided URI matches any of the no proxy areas.
|
* Returns true if the provided URI matches any of the no proxy areas.
|
||||||
*
|
*
|
||||||
* @param mixed $noProxy No-proxy host patterns.
|
* Matching follows the same rules as isHostInNoProxy(), with the
|
||||||
|
* addition that areas may carry a port (e.g. "example.com:8080" or
|
||||||
|
* "[::1]:8080") which is compared against the URI port (or the scheme
|
||||||
|
* default port when the URI has none).
|
||||||
|
*
|
||||||
|
* @param mixed $noProxy No-proxy host, host-and-port, or CIDR patterns.
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
|
|
@ -347,38 +426,34 @@ EOT
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$host = $uri->getHost();
|
$target = self::parseNoProxyTarget($uri);
|
||||||
if ($host === '') {
|
if ($target === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$port = $uri->getPort();
|
return self::matchesNoProxyList($target, $noProxy);
|
||||||
if ($port === null) {
|
|
||||||
$port = self::getDefaultPort($uri->getScheme());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{type: string, value: string, port: int|null, matchesRoot: bool} $target
|
||||||
|
* @param array<array-key, mixed> $noProxy
|
||||||
|
*/
|
||||||
|
private static function matchesNoProxyList(array $target, array $noProxy): bool
|
||||||
|
{
|
||||||
foreach ($noProxy as $area) {
|
foreach ($noProxy as $area) {
|
||||||
if (!\is_string($area)) {
|
if (!\is_string($area)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$area = \trim($area);
|
$area = \trim($area, " \n\r\t\0\x0B");
|
||||||
|
|
||||||
// Always match on wildcards.
|
// Always match on wildcards.
|
||||||
if ($area === '*') {
|
if ($area === '*') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($area === '') {
|
$rule = self::parseNoProxyRule($area);
|
||||||
continue;
|
if ($rule !== null && self::noProxyRuleMatches($target, $rule)) {
|
||||||
}
|
|
||||||
|
|
||||||
[$area, $areaPort] = self::splitNoProxyHostAndPort($area);
|
|
||||||
if ($areaPort !== null && $areaPort !== $port) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self::isHostInNoProxy($host, [$area])) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -386,58 +461,157 @@ EOT
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function normalizeNoProxyHost(string $host, bool $stripPort): string
|
/**
|
||||||
|
* @return array{type: string, value: string, port: int|null, matchesRoot: bool}|null
|
||||||
|
*/
|
||||||
|
private static function parseNoProxyTarget(UriInterface $uri): ?array
|
||||||
{
|
{
|
||||||
if ($host !== '' && $host[0] === '[') {
|
$host = $uri->getHost();
|
||||||
$closingBracket = \strpos($host, ']');
|
if ($host === '') {
|
||||||
|
return null;
|
||||||
if ($closingBracket !== false) {
|
|
||||||
$address = \substr($host, 1, $closingBracket - 1);
|
|
||||||
$tail = \substr($host, $closingBracket + 1);
|
|
||||||
|
|
||||||
if (
|
|
||||||
($tail === '' || ($stripPort && \preg_match('/^:\d+$/', $tail)))
|
|
||||||
&& \filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)
|
|
||||||
) {
|
|
||||||
return \strtolower($address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\filter_var($host, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
|
return self::parseNoProxyHost($host, $uri->getPort() ?? self::getDefaultPort($uri->getScheme()), true);
|
||||||
return \strtolower($host);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($stripPort) {
|
|
||||||
[$host] = \explode(':', $host, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $host;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{0: string, 1: int|null}
|
* @return array{type: string, value: string, port: int|null, matchesRoot: bool}|null
|
||||||
*/
|
*/
|
||||||
private static function splitNoProxyHostAndPort(string $area): array
|
private static function parseNoProxyHostString(string $host): ?array
|
||||||
|
{
|
||||||
|
$hostAndPort = self::splitNoProxyHostAndPort($host);
|
||||||
|
if ($hostAndPort === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$host] = $hostAndPort;
|
||||||
|
|
||||||
|
return self::parseNoProxyHost($host, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{type: string, value: string, port: int|null, matchesRoot: bool}|array{type: string, value: string, prefix: int}|null
|
||||||
|
*/
|
||||||
|
private static function parseNoProxyRule(string $area): ?array
|
||||||
|
{
|
||||||
|
$area = \trim($area, " \n\r\t\0\x0B");
|
||||||
|
if ($area === '' || $area === '*') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\strpos($area, '/') !== false) {
|
||||||
|
return self::parseNoProxyCidrRule($area);
|
||||||
|
}
|
||||||
|
|
||||||
|
$matchesRoot = true;
|
||||||
|
if ($area[0] === '.') {
|
||||||
|
$matchesRoot = false;
|
||||||
|
$area = \substr($area, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$hostAndPort = self::splitNoProxyHostAndPort($area);
|
||||||
|
if ($hostAndPort === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$host, $port] = $hostAndPort;
|
||||||
|
|
||||||
|
if ($host === '*') {
|
||||||
|
if (!$matchesRoot) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'type' => 'wildcard',
|
||||||
|
'value' => '*',
|
||||||
|
'port' => $port,
|
||||||
|
'matchesRoot' => true,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule = self::parseNoProxyHost($host, $port, $matchesRoot);
|
||||||
|
if ($rule !== null && !$matchesRoot && $rule['type'] === 'ip') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{type: string, value: string, port: int|null, matchesRoot: bool}|null
|
||||||
|
*/
|
||||||
|
private static function parseNoProxyHost(string $host, ?int $port, bool $matchesRoot): ?array
|
||||||
|
{
|
||||||
|
if ($host !== '' && $host[0] === '[') {
|
||||||
|
if (\substr($host, -1) !== ']') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$address = \substr($host, 1, -1);
|
||||||
|
if (!\filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = $address;
|
||||||
|
}
|
||||||
|
|
||||||
|
$packedIp = self::packIpAddress($host);
|
||||||
|
if ($packedIp !== false) {
|
||||||
|
return [
|
||||||
|
'type' => 'ip',
|
||||||
|
'value' => $packedIp,
|
||||||
|
'port' => $port,
|
||||||
|
'matchesRoot' => $matchesRoot,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($host === '' || \strpos($host, ':') !== false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize a single DNS root dot for no-proxy domain matching.
|
||||||
|
if (\substr($host, -1) === '.') {
|
||||||
|
$host = \substr($host, 0, -1);
|
||||||
|
if ($host === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'type' => 'domain',
|
||||||
|
'value' => Psr7\Utils::asciiToLower($host),
|
||||||
|
'port' => $port,
|
||||||
|
'matchesRoot' => $matchesRoot,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{0: string, 1: int|null}|null
|
||||||
|
*/
|
||||||
|
private static function splitNoProxyHostAndPort(string $area): ?array
|
||||||
{
|
{
|
||||||
if ($area !== '' && $area[0] === '[') {
|
if ($area !== '' && $area[0] === '[') {
|
||||||
$closingBracket = \strpos($area, ']');
|
$closingBracket = \strpos($area, ']');
|
||||||
|
if ($closingBracket === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if ($closingBracket !== false) {
|
$host = \substr($area, 0, $closingBracket + 1);
|
||||||
$tail = \substr($area, $closingBracket + 1);
|
$tail = \substr($area, $closingBracket + 1);
|
||||||
if ($tail !== '' && $tail[0] === ':') {
|
if ($tail === '') {
|
||||||
|
return [$host, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tail[0] !== ':') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$port = self::parseNoProxyPort(\substr($tail, 1));
|
$port = self::parseNoProxyPort(\substr($tail, 1));
|
||||||
|
|
||||||
if ($port !== null) {
|
return $port === null ? null : [$host, $port];
|
||||||
return [\substr($area, 0, $closingBracket + 1), $port];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [$area, null];
|
if (self::packIpAddress($area) !== false) {
|
||||||
}
|
|
||||||
|
|
||||||
if (\filter_var($area, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
|
|
||||||
return [$area, null];
|
return [$area, null];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -448,7 +622,7 @@ EOT
|
||||||
|
|
||||||
$port = self::parseNoProxyPort(\substr($area, $colon + 1));
|
$port = self::parseNoProxyPort(\substr($area, $colon + 1));
|
||||||
if ($port === null) {
|
if ($port === null) {
|
||||||
return [$area, null];
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [\substr($area, 0, $colon), $port];
|
return [\substr($area, 0, $colon), $port];
|
||||||
|
|
@ -456,13 +630,131 @@ EOT
|
||||||
|
|
||||||
private static function parseNoProxyPort(string $port): ?int
|
private static function parseNoProxyPort(string $port): ?int
|
||||||
{
|
{
|
||||||
if ($port === '' || !\ctype_digit($port)) {
|
return self::parseBoundedUnsignedInteger($port, 65535);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{type: string, value: string, prefix: int}|null
|
||||||
|
*/
|
||||||
|
private static function parseNoProxyCidrRule(string $area): ?array
|
||||||
|
{
|
||||||
|
$slash = \strpos($area, '/');
|
||||||
|
if ($slash === false) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$port = (int) $port;
|
$prefix = \substr($area, $slash + 1);
|
||||||
|
|
||||||
return $port <= 65535 ? $port : null;
|
$network = \substr($area, 0, $slash);
|
||||||
|
if ($network !== '' && $network[0] === '[' && \substr($network, -1) === ']') {
|
||||||
|
$network = \substr($network, 1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$network = self::packIpAddress($network);
|
||||||
|
if ($network === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$prefix = self::parseBoundedUnsignedInteger($prefix, \strlen($network) * 8);
|
||||||
|
if ($prefix === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'type' => 'cidr',
|
||||||
|
'value' => $network,
|
||||||
|
'prefix' => $prefix,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function parseBoundedUnsignedInteger(string $value, int $max): ?int
|
||||||
|
{
|
||||||
|
if ($value === '' || !\ctype_digit($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$normalized = \ltrim($value, '0');
|
||||||
|
$normalized = $normalized === '' ? '0' : $normalized;
|
||||||
|
$limit = (string) $max;
|
||||||
|
|
||||||
|
if (\strlen($normalized) > \strlen($limit) || (\strlen($normalized) === \strlen($limit) && \strcmp($normalized, $limit) > 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) $normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{type: string, value: string, port: int|null, matchesRoot: bool} $target
|
||||||
|
* @param array{type: string, value: string, port?: int|null, matchesRoot?: bool, prefix?: int|null} $rule
|
||||||
|
*/
|
||||||
|
private static function noProxyRuleMatches(array $target, array $rule): bool
|
||||||
|
{
|
||||||
|
if ($rule['type'] === 'wildcard') {
|
||||||
|
return ($rule['port'] ?? null) === null || $rule['port'] === $target['port'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rule['type'] === 'cidr') {
|
||||||
|
if ($target['type'] !== 'ip' || !isset($rule['prefix'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\strlen($target['value']) !== \strlen($rule['value'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::ipMatchesPrefix($target['value'], $rule['value'], $rule['prefix']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($rule['port'] ?? null) !== null && $rule['port'] !== $target['port']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rule['type'] !== $target['type']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rule['type'] === 'ip') {
|
||||||
|
return $rule['value'] === $target['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($rule['matchesRoot'] ?? false) && $target['value'] === $rule['value']) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$suffix = '.'.$rule['value'];
|
||||||
|
|
||||||
|
return \substr($target['value'], -\strlen($suffix)) === $suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
private static function packIpAddress(string $ip)
|
||||||
|
{
|
||||||
|
if (!\filter_var($ip, \FILTER_VALIDATE_IP)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \inet_pton($ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function ipMatchesPrefix(string $address, string $network, int $prefix): bool
|
||||||
|
{
|
||||||
|
$fullBytes = \intdiv($prefix, 8);
|
||||||
|
$remainingBits = $prefix % 8;
|
||||||
|
|
||||||
|
if ($fullBytes > 0 && \substr($address, 0, $fullBytes) !== \substr($network, 0, $fullBytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($remainingBits === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mask = (0xFF << (8 - $remainingBits)) & 0xFF;
|
||||||
|
|
||||||
|
return (\ord($address[$fullBytes]) & $mask) === (\ord($network[$fullBytes]) & $mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getDefaultPort(string $scheme): ?int
|
private static function getDefaultPort(string $scheme): ?int
|
||||||
|
|
@ -492,9 +784,12 @@ EOT
|
||||||
* @throws InvalidArgumentException if the JSON cannot be decoded.
|
* @throws InvalidArgumentException if the JSON cannot be decoded.
|
||||||
*
|
*
|
||||||
* @see https://www.php.net/manual/en/function.json-decode.php
|
* @see https://www.php.net/manual/en/function.json-decode.php
|
||||||
|
* @deprecated Utils::jsonDecode() will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_decode() instead.
|
||||||
*/
|
*/
|
||||||
public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
|
public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.15', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_decode() instead.', __METHOD__);
|
||||||
|
|
||||||
if ($depth < 1) {
|
if ($depth < 1) {
|
||||||
throw new InvalidArgumentException('json_decode error: Maximum stack depth exceeded');
|
throw new InvalidArgumentException('json_decode error: Maximum stack depth exceeded');
|
||||||
}
|
}
|
||||||
|
|
@ -517,9 +812,12 @@ EOT
|
||||||
* @throws InvalidArgumentException if the JSON cannot be encoded.
|
* @throws InvalidArgumentException if the JSON cannot be encoded.
|
||||||
*
|
*
|
||||||
* @see https://www.php.net/manual/en/function.json-encode.php
|
* @see https://www.php.net/manual/en/function.json-encode.php
|
||||||
|
* @deprecated Utils::jsonEncode() will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_encode() instead.
|
||||||
*/
|
*/
|
||||||
public static function jsonEncode($value, int $options = 0, int $depth = 512): string
|
public static function jsonEncode($value, int $options = 0, int $depth = 512): string
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.15', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_encode() instead.', __METHOD__);
|
||||||
|
|
||||||
$json = \json_encode($value, $options, $depth);
|
$json = \json_encode($value, $options, $depth);
|
||||||
if (\JSON_ERROR_NONE !== \json_last_error()) {
|
if (\JSON_ERROR_NONE !== \json_last_error()) {
|
||||||
throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg());
|
throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg());
|
||||||
|
|
@ -566,7 +864,7 @@ EOT
|
||||||
'guzzlehttp/guzzle',
|
'guzzlehttp/guzzle',
|
||||||
'7.11',
|
'7.11',
|
||||||
'Passing %s as the "idn_conversion" request option is deprecated; guzzlehttp/guzzle 8.0 will reject values that are not true, false, null, or an integer IDNA_* bitmask.',
|
'Passing %s as the "idn_conversion" request option is deprecated; guzzlehttp/guzzle 8.0 will reject values that are not true, false, null, or an integer IDNA_* bitmask.',
|
||||||
self::describeType($value)
|
\get_debug_type($value)
|
||||||
);
|
);
|
||||||
|
|
||||||
return (int) $value;
|
return (int) $value;
|
||||||
|
|
|
||||||
115
vendor/guzzlehttp/guzzle/src/functions.php
vendored
115
vendor/guzzlehttp/guzzle/src/functions.php
vendored
|
|
@ -11,11 +11,26 @@ namespace GuzzleHttp;
|
||||||
* @return string Returns a string containing the type of the variable and
|
* @return string Returns a string containing the type of the variable and
|
||||||
* if a class is provided, the class name.
|
* if a class is provided, the class name.
|
||||||
*
|
*
|
||||||
* @deprecated describe_type will be removed in guzzlehttp/guzzle:8.0. Use Utils::describeType instead.
|
* @deprecated describe_type will be removed in guzzlehttp/guzzle:8.0. Use get_debug_type() instead.
|
||||||
*/
|
*/
|
||||||
function describe_type($input): string
|
function describe_type($input): string
|
||||||
{
|
{
|
||||||
return Utils::describeType($input);
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use get_debug_type() instead.', __FUNCTION__);
|
||||||
|
|
||||||
|
switch (\gettype($input)) {
|
||||||
|
case 'object':
|
||||||
|
return 'object('.\get_class($input).')';
|
||||||
|
case 'array':
|
||||||
|
return 'array('.\count($input).')';
|
||||||
|
default:
|
||||||
|
\ob_start();
|
||||||
|
\var_dump($input);
|
||||||
|
// normalize float vs double
|
||||||
|
/** @var string $varDumpContent */
|
||||||
|
$varDumpContent = \ob_get_clean();
|
||||||
|
|
||||||
|
return \str_replace('double(', 'float(', \rtrim($varDumpContent, " \n\r\t\0\x0B"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -28,6 +43,8 @@ function describe_type($input): string
|
||||||
*/
|
*/
|
||||||
function headers_from_lines(iterable $lines): array
|
function headers_from_lines(iterable $lines): array
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::headersFromLines() instead.', __FUNCTION__);
|
||||||
|
|
||||||
return Utils::headersFromLines($lines);
|
return Utils::headersFromLines($lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,6 +59,8 @@ function headers_from_lines(iterable $lines): array
|
||||||
*/
|
*/
|
||||||
function debug_resource($value = null)
|
function debug_resource($value = null)
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::debugResource() instead.', __FUNCTION__);
|
||||||
|
|
||||||
return Utils::debugResource($value);
|
return Utils::debugResource($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,6 +77,8 @@ function debug_resource($value = null)
|
||||||
*/
|
*/
|
||||||
function choose_handler(): callable
|
function choose_handler(): callable
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::chooseHandler() instead.', __FUNCTION__);
|
||||||
|
|
||||||
return Utils::chooseHandler();
|
return Utils::chooseHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +89,8 @@ function choose_handler(): callable
|
||||||
*/
|
*/
|
||||||
function default_user_agent(): string
|
function default_user_agent(): string
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::defaultUserAgent() instead.', __FUNCTION__);
|
||||||
|
|
||||||
return Utils::defaultUserAgent();
|
return Utils::defaultUserAgent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +111,60 @@ function default_user_agent(): string
|
||||||
*/
|
*/
|
||||||
function default_ca_bundle(): string
|
function default_ca_bundle(): string
|
||||||
{
|
{
|
||||||
return Utils::defaultCaBundle();
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. This function is not needed in PHP 5.6+.', __FUNCTION__);
|
||||||
|
|
||||||
|
static $cached = null;
|
||||||
|
static $cafiles = [
|
||||||
|
// Red Hat, CentOS, Fedora (provided by the ca-certificates package)
|
||||||
|
'/etc/pki/tls/certs/ca-bundle.crt',
|
||||||
|
// Ubuntu, Debian (provided by the ca-certificates package)
|
||||||
|
'/etc/ssl/certs/ca-certificates.crt',
|
||||||
|
// FreeBSD (provided by the ca_root_nss package)
|
||||||
|
'/usr/local/share/certs/ca-root-nss.crt',
|
||||||
|
// SLES 12 (provided by the ca-certificates package)
|
||||||
|
'/var/lib/ca-certificates/ca-bundle.pem',
|
||||||
|
// OS X provided by homebrew (using the default path)
|
||||||
|
'/usr/local/etc/openssl/cert.pem',
|
||||||
|
// Google app engine
|
||||||
|
'/etc/ca-certificates.crt',
|
||||||
|
// Windows?
|
||||||
|
'C:\\windows\\system32\\curl-ca-bundle.crt',
|
||||||
|
'C:\\windows\\curl-ca-bundle.crt',
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($cached) {
|
||||||
|
return $cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ca = \ini_get('openssl.cafile')) {
|
||||||
|
return $cached = $ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ca = \ini_get('curl.cainfo')) {
|
||||||
|
return $cached = $ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($cafiles as $filename) {
|
||||||
|
if (\file_exists($filename)) {
|
||||||
|
return $cached = $filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException(
|
||||||
|
<<< EOT
|
||||||
|
No system CA bundle could be found in any of the the common system locations.
|
||||||
|
PHP versions earlier than 5.6 are not properly configured to use the system's
|
||||||
|
CA bundle by default. In order to verify peer certificates, you will need to
|
||||||
|
supply the path on disk to a certificate bundle to the 'verify' request option:
|
||||||
|
https://github.com/guzzle/guzzle/blob/7.15/docs/request-options.md#verify. If
|
||||||
|
you do not need a specific certificate bundle, then Mozilla provides a commonly
|
||||||
|
used CA bundle which can be downloaded here (provided by the maintainer of
|
||||||
|
cURL): https://curl.se/ca/cacert.pem. Once you have a CA bundle available on
|
||||||
|
disk, you can set the 'openssl.cafile' PHP ini setting to point to the path to
|
||||||
|
the file, allowing you to omit the 'verify' request option. See
|
||||||
|
https://curl.se/docs/sslcerts.html for more information.
|
||||||
|
EOT
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -99,6 +175,8 @@ function default_ca_bundle(): string
|
||||||
*/
|
*/
|
||||||
function normalize_header_keys(array $headers): array
|
function normalize_header_keys(array $headers): array
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::normalizeHeaderKeys() instead.', __FUNCTION__);
|
||||||
|
|
||||||
return Utils::normalizeHeaderKeys($headers);
|
return Utils::normalizeHeaderKeys($headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,6 +203,8 @@ function normalize_header_keys(array $headers): array
|
||||||
*/
|
*/
|
||||||
function is_host_in_noproxy(string $host, array $noProxyArray): bool
|
function is_host_in_noproxy(string $host, array $noProxyArray): bool
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::isHostInNoProxy() instead.', __FUNCTION__);
|
||||||
|
|
||||||
return Utils::isHostInNoProxy($host, $noProxyArray);
|
return Utils::isHostInNoProxy($host, $noProxyArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,11 +222,23 @@ function is_host_in_noproxy(string $host, array $noProxyArray): bool
|
||||||
* @throws Exception\InvalidArgumentException if the JSON cannot be decoded.
|
* @throws Exception\InvalidArgumentException if the JSON cannot be decoded.
|
||||||
*
|
*
|
||||||
* @see https://www.php.net/manual/en/function.json-decode.php
|
* @see https://www.php.net/manual/en/function.json-decode.php
|
||||||
* @deprecated json_decode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonDecode instead.
|
* @deprecated json_decode will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_decode() instead.
|
||||||
*/
|
*/
|
||||||
function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
|
function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
|
||||||
{
|
{
|
||||||
return Utils::jsonDecode($json, $assoc, $depth, $options);
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_decode() instead.', __FUNCTION__);
|
||||||
|
|
||||||
|
if ($depth < 1) {
|
||||||
|
throw new Exception\InvalidArgumentException('json_decode error: Maximum stack depth exceeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = \json_decode($json, $assoc, $depth, $options);
|
||||||
|
if (\JSON_ERROR_NONE !== \json_last_error()) {
|
||||||
|
throw new Exception\InvalidArgumentException('json_decode error: '.\json_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var object|array|string|int|float|bool|null $data */
|
||||||
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -159,9 +251,18 @@ function json_decode(string $json, bool $assoc = false, int $depth = 512, int $o
|
||||||
* @throws Exception\InvalidArgumentException if the JSON cannot be encoded.
|
* @throws Exception\InvalidArgumentException if the JSON cannot be encoded.
|
||||||
*
|
*
|
||||||
* @see https://www.php.net/manual/en/function.json-encode.php
|
* @see https://www.php.net/manual/en/function.json-encode.php
|
||||||
* @deprecated json_encode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonEncode instead.
|
* @deprecated json_encode will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_encode() instead.
|
||||||
*/
|
*/
|
||||||
function json_encode($value, int $options = 0, int $depth = 512): string
|
function json_encode($value, int $options = 0, int $depth = 512): string
|
||||||
{
|
{
|
||||||
return Utils::jsonEncode($value, $options, $depth);
|
\trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_encode() instead.', __FUNCTION__);
|
||||||
|
|
||||||
|
/** @var positive-int $depth */
|
||||||
|
$json = \json_encode($value, $options, $depth);
|
||||||
|
if (\JSON_ERROR_NONE !== \json_last_error()) {
|
||||||
|
throw new Exception\InvalidArgumentException('json_encode error: '.\json_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var non-empty-string $json */
|
||||||
|
return $json;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
215
vendor/guzzlehttp/promises/CHANGELOG.md
vendored
215
vendor/guzzlehttp/promises/CHANGELOG.md
vendored
|
|
@ -1,215 +0,0 @@
|
||||||
# CHANGELOG
|
|
||||||
|
|
||||||
|
|
||||||
## 2.5.0 - 2026-06-02
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Deprecated passing non-iterable inputs to promise collection helpers and `EachPromise`
|
|
||||||
|
|
||||||
|
|
||||||
## 2.4.1 - 2026-05-20
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed cancelling settled coroutines when no current promise remains
|
|
||||||
|
|
||||||
|
|
||||||
## 2.4.0 - 2026-05-20
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Empty `EachPromise` instances now resolve when the task queue runs without `wait()`
|
|
||||||
|
|
||||||
|
|
||||||
## 2.3.1 - 2026-05-19
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed `Utils::inspect()` returning the internal reason array instead of the `AggregateException`
|
|
||||||
|
|
||||||
|
|
||||||
## 2.3.0 - 2025-08-22
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- PHP 8.5 support
|
|
||||||
|
|
||||||
|
|
||||||
## 2.2.0 - 2025-03-27
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Revert "Allow an empty EachPromise to be resolved by running the queue"
|
|
||||||
|
|
||||||
|
|
||||||
## 2.1.0 - 2025-03-27
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Allow an empty EachPromise to be resolved by running the queue
|
|
||||||
|
|
||||||
|
|
||||||
## 2.0.4 - 2024-10-17
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Once settled, don't allow further rejection of additional promises
|
|
||||||
|
|
||||||
|
|
||||||
## 2.0.3 - 2024-07-18
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- PHP 8.4 support
|
|
||||||
|
|
||||||
|
|
||||||
## 2.0.2 - 2023-12-03
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Replaced `call_user_func*` with native calls
|
|
||||||
|
|
||||||
|
|
||||||
## 2.0.1 - 2023-08-03
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- PHP 8.3 support
|
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0 - 2023-05-21
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added PHP 7 type hints
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- All previously non-final non-exception classes have been marked as soft-final
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Dropped PHP < 7.2 support
|
|
||||||
- All functions in the `GuzzleHttp\Promise` namespace
|
|
||||||
|
|
||||||
|
|
||||||
## 1.5.3 - 2023-05-21
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Removed remaining usage of deprecated functions
|
|
||||||
|
|
||||||
|
|
||||||
## 1.5.2 - 2022-08-07
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Officially support PHP 8.2
|
|
||||||
|
|
||||||
|
|
||||||
## 1.5.1 - 2021-10-22
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Revert "Call handler when waiting on fulfilled/rejected Promise"
|
|
||||||
- Fix pool memory leak when empty array of promises provided
|
|
||||||
|
|
||||||
|
|
||||||
## 1.5.0 - 2021-10-07
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Call handler when waiting on fulfilled/rejected Promise
|
|
||||||
- Officially support PHP 8.1
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix manually settle promises generated with `Utils::task`
|
|
||||||
|
|
||||||
|
|
||||||
## 1.4.1 - 2021-02-18
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed `each_limit` skipping promises and failing
|
|
||||||
|
|
||||||
|
|
||||||
## 1.4.0 - 2020-09-30
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Support for PHP 8
|
|
||||||
- Optional `$recursive` flag to `all`
|
|
||||||
- Replaced functions by static methods
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix empty `each` processing
|
|
||||||
- Fix promise handling for Iterators of non-unique keys
|
|
||||||
- Fixed `method_exists` crashes on PHP 8
|
|
||||||
- Memory leak on exceptions
|
|
||||||
|
|
||||||
|
|
||||||
## 1.3.1 - 2016-12-20
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `wait()` foreign promise compatibility
|
|
||||||
|
|
||||||
|
|
||||||
## 1.3.0 - 2016-11-18
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Adds support for custom task queues.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed coroutine promise memory leak.
|
|
||||||
|
|
||||||
|
|
||||||
## 1.2.0 - 2016-05-18
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Update to now catch `\Throwable` on PHP 7+
|
|
||||||
|
|
||||||
|
|
||||||
## 1.1.0 - 2016-03-07
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Update EachPromise to prevent recurring on a iterator when advancing, as this
|
|
||||||
could trigger fatal generator errors.
|
|
||||||
- Update Promise to allow recursive waiting without unwrapping exceptions.
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.3 - 2015-10-15
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Update EachPromise to immediately resolve when the underlying promise iterator
|
|
||||||
is empty. Previously, such a promise would throw an exception when its `wait`
|
|
||||||
function was called.
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.2 - 2015-05-15
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Conditionally require functions.php.
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.1 - 2015-06-24
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Updating EachPromise to call next on the underlying promise iterator as late
|
|
||||||
as possible to ensure that generators that generate new requests based on
|
|
||||||
callbacks are not iterated until after callbacks are invoked.
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0 - 2015-05-12
|
|
||||||
|
|
||||||
- Initial release
|
|
||||||
536
vendor/guzzlehttp/promises/README.md
vendored
536
vendor/guzzlehttp/promises/README.md
vendored
|
|
@ -1,536 +0,0 @@
|
||||||
# Guzzle Promises
|
|
||||||
|
|
||||||
[Promises/A+](https://promisesaplus.com/) implementation that handles promise
|
|
||||||
chaining and resolution iteratively, allowing for "infinite" promise chaining
|
|
||||||
while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
|
|
||||||
for a general introduction to promises.
|
|
||||||
|
|
||||||
- [Features](#features)
|
|
||||||
- [Quick start](#quick-start)
|
|
||||||
- [Synchronous wait](#synchronous-wait)
|
|
||||||
- [Cancellation](#cancellation)
|
|
||||||
- [API](#api)
|
|
||||||
- [Promise](#promise)
|
|
||||||
- [FulfilledPromise](#fulfilledpromise)
|
|
||||||
- [RejectedPromise](#rejectedpromise)
|
|
||||||
- [Promise interop](#promise-interop)
|
|
||||||
- [Implementation notes](#implementation-notes)
|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- [Promises/A+](https://promisesaplus.com/) implementation.
|
|
||||||
- Promise resolution and chaining is handled iteratively, allowing for
|
|
||||||
"infinite" promise chaining.
|
|
||||||
- Promises have a synchronous `wait` method.
|
|
||||||
- Promises can be cancelled.
|
|
||||||
- Works with any object that has a `then` function.
|
|
||||||
- C# style async/await coroutine promises using
|
|
||||||
`GuzzleHttp\Promise\Coroutine::of()`.
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```shell
|
|
||||||
composer require guzzlehttp/promises
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Version Guidance
|
|
||||||
|
|
||||||
| Version | Status | PHP Version |
|
|
||||||
|---------|---------------------|--------------|
|
|
||||||
| 1.x | Security fixes only | >=5.5,<8.3 |
|
|
||||||
| 2.x | Latest | >=7.2.5,<8.6 |
|
|
||||||
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
A *promise* represents the eventual result of an asynchronous operation. The
|
|
||||||
primary way of interacting with a promise is through its `then` method, which
|
|
||||||
registers callbacks to receive either a promise's eventual value or the reason
|
|
||||||
why the promise cannot be fulfilled.
|
|
||||||
|
|
||||||
### Callbacks
|
|
||||||
|
|
||||||
Callbacks are registered with the `then` method by providing an optional
|
|
||||||
`$onFulfilled` followed by an optional `$onRejected` function.
|
|
||||||
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\Promise;
|
|
||||||
|
|
||||||
$promise = new Promise();
|
|
||||||
$promise->then(
|
|
||||||
// $onFulfilled
|
|
||||||
function ($value) {
|
|
||||||
echo 'The promise was fulfilled.';
|
|
||||||
},
|
|
||||||
// $onRejected
|
|
||||||
function ($reason) {
|
|
||||||
echo 'The promise was rejected.';
|
|
||||||
}
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
*Resolving* a promise means that you either fulfill a promise with a *value* or
|
|
||||||
reject a promise with a *reason*. Resolving a promise triggers callbacks
|
|
||||||
registered with the promise's `then` method. These callbacks are triggered
|
|
||||||
only once and in the order in which they were added.
|
|
||||||
|
|
||||||
### Resolving a Promise
|
|
||||||
|
|
||||||
Promises are fulfilled using the `resolve($value)` method. Resolving a promise
|
|
||||||
with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
|
|
||||||
all of the onFulfilled callbacks (resolving a promise with a rejected promise
|
|
||||||
will reject the promise and trigger the `$onRejected` callbacks).
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\Promise;
|
|
||||||
|
|
||||||
$promise = new Promise();
|
|
||||||
$promise
|
|
||||||
->then(function ($value) {
|
|
||||||
// Return a value and don't break the chain
|
|
||||||
return "Hello, " . $value;
|
|
||||||
})
|
|
||||||
// This then is executed after the first then and receives the value
|
|
||||||
// returned from the first then.
|
|
||||||
->then(function ($value) {
|
|
||||||
echo $value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Resolving the promise triggers the $onFulfilled callbacks and outputs
|
|
||||||
// "Hello, reader."
|
|
||||||
$promise->resolve('reader.');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Promise Forwarding
|
|
||||||
|
|
||||||
Promises can be chained one after the other. Each then in the chain is a new
|
|
||||||
promise. The return value of a promise is what's forwarded to the next
|
|
||||||
promise in the chain. Returning a promise in a `then` callback will cause the
|
|
||||||
subsequent promises in the chain to only be fulfilled when the returned promise
|
|
||||||
has been fulfilled. The next promise in the chain will be invoked with the
|
|
||||||
resolved value of the promise.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\Promise;
|
|
||||||
|
|
||||||
$promise = new Promise();
|
|
||||||
$nextPromise = new Promise();
|
|
||||||
|
|
||||||
$promise
|
|
||||||
->then(function ($value) use ($nextPromise) {
|
|
||||||
echo $value;
|
|
||||||
return $nextPromise;
|
|
||||||
})
|
|
||||||
->then(function ($value) {
|
|
||||||
echo $value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Triggers the first callback and outputs "A"
|
|
||||||
$promise->resolve('A');
|
|
||||||
// Triggers the second callback and outputs "B"
|
|
||||||
$nextPromise->resolve('B');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Promise Rejection
|
|
||||||
|
|
||||||
When a promise is rejected, the `$onRejected` callbacks are invoked with the
|
|
||||||
rejection reason.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\Promise;
|
|
||||||
|
|
||||||
$promise = new Promise();
|
|
||||||
$promise->then(null, function ($reason) {
|
|
||||||
echo $reason;
|
|
||||||
});
|
|
||||||
|
|
||||||
$promise->reject('Error!');
|
|
||||||
// Outputs "Error!"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rejection Forwarding
|
|
||||||
|
|
||||||
If an exception is thrown in an `$onRejected` callback, subsequent
|
|
||||||
`$onRejected` callbacks are invoked with the thrown exception as the reason.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\Promise;
|
|
||||||
|
|
||||||
$promise = new Promise();
|
|
||||||
$promise->then(null, function ($reason) {
|
|
||||||
throw new Exception($reason);
|
|
||||||
})->then(null, function ($reason) {
|
|
||||||
assert($reason->getMessage() === 'Error!');
|
|
||||||
});
|
|
||||||
|
|
||||||
$promise->reject('Error!');
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also forward a rejection down the promise chain by returning a
|
|
||||||
`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
|
|
||||||
`$onRejected` callback.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\Promise;
|
|
||||||
use GuzzleHttp\Promise\RejectedPromise;
|
|
||||||
|
|
||||||
$promise = new Promise();
|
|
||||||
$promise->then(null, function ($reason) {
|
|
||||||
return new RejectedPromise($reason);
|
|
||||||
})->then(null, function ($reason) {
|
|
||||||
assert($reason === 'Error!');
|
|
||||||
});
|
|
||||||
|
|
||||||
$promise->reject('Error!');
|
|
||||||
```
|
|
||||||
|
|
||||||
If an exception is not thrown in a `$onRejected` callback and the callback
|
|
||||||
does not return a rejected promise, downstream `$onFulfilled` callbacks are
|
|
||||||
invoked using the value returned from the `$onRejected` callback.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\Promise;
|
|
||||||
|
|
||||||
$promise = new Promise();
|
|
||||||
$promise
|
|
||||||
->then(null, function ($reason) {
|
|
||||||
return "It's ok";
|
|
||||||
})
|
|
||||||
->then(function ($value) {
|
|
||||||
assert($value === "It's ok");
|
|
||||||
});
|
|
||||||
|
|
||||||
$promise->reject('Error!');
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Synchronous Wait
|
|
||||||
|
|
||||||
You can synchronously force promises to complete using a promise's `wait`
|
|
||||||
method. When creating a promise, you can provide a wait function that is used
|
|
||||||
to synchronously force a promise to complete. When a wait function is invoked
|
|
||||||
it is expected to deliver a value to the promise or reject the promise. If the
|
|
||||||
wait function does not deliver a value, then an exception is thrown. The wait
|
|
||||||
function provided to a promise constructor is invoked when the `wait` function
|
|
||||||
of the promise is called.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$promise = new Promise(function () use (&$promise) {
|
|
||||||
$promise->resolve('foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Calling wait will return the value of the promise.
|
|
||||||
echo $promise->wait(); // outputs "foo"
|
|
||||||
```
|
|
||||||
|
|
||||||
If a throwable is encountered while invoking the wait function of a promise,
|
|
||||||
the promise is rejected with the throwable and the throwable is thrown.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$promise = new Promise(function () use (&$promise) {
|
|
||||||
throw new Exception('foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
$promise->wait(); // throws the exception.
|
|
||||||
```
|
|
||||||
|
|
||||||
Calling `wait` on a promise that has been fulfilled will not trigger the wait
|
|
||||||
function. It will simply return the previously resolved value.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$promise = new Promise(function () { die('this is not called!'); });
|
|
||||||
$promise->resolve('foo');
|
|
||||||
echo $promise->wait(); // outputs "foo"
|
|
||||||
```
|
|
||||||
|
|
||||||
Calling `wait` on a promise that has been rejected will throw. If the rejection
|
|
||||||
reason is an instance of `\Throwable` the reason is thrown.
|
|
||||||
Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
|
|
||||||
can be obtained by calling the `getReason` method of the exception.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$promise = new Promise();
|
|
||||||
$promise->reject('foo');
|
|
||||||
$promise->wait();
|
|
||||||
```
|
|
||||||
|
|
||||||
> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
|
|
||||||
|
|
||||||
### Unwrapping a Promise
|
|
||||||
|
|
||||||
When synchronously waiting on a promise, you are joining the state of the
|
|
||||||
promise into the current state of execution (i.e., return the value of the
|
|
||||||
promise if it was fulfilled or throw an exception if it was rejected). This is
|
|
||||||
called "unwrapping" the promise. Waiting on a promise will by default unwrap
|
|
||||||
the promise state.
|
|
||||||
|
|
||||||
You can force a promise to resolve and *not* unwrap the state of the promise
|
|
||||||
by passing `false` to the first argument of the `wait` function:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$promise = new Promise();
|
|
||||||
$promise->reject('foo');
|
|
||||||
// This will not throw an exception. It simply ensures the promise has
|
|
||||||
// been resolved.
|
|
||||||
$promise->wait(false);
|
|
||||||
```
|
|
||||||
|
|
||||||
When unwrapping a promise, the resolved value of the promise will be waited
|
|
||||||
upon until the unwrapped value is not a promise. This means that if you resolve
|
|
||||||
promise A with a promise B and unwrap promise A, the value returned by the
|
|
||||||
wait function will be the value delivered to promise B.
|
|
||||||
|
|
||||||
**Note**: when you do not unwrap the promise, no value is returned.
|
|
||||||
|
|
||||||
|
|
||||||
## Cancellation
|
|
||||||
|
|
||||||
You can cancel a promise that has not yet been fulfilled using the `cancel()`
|
|
||||||
method of a promise. When creating a promise you can provide an optional
|
|
||||||
cancel function that when invoked cancels the action of computing a resolution
|
|
||||||
of the promise.
|
|
||||||
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
### Promise
|
|
||||||
|
|
||||||
When creating a promise object, you can provide an optional `$waitFn` and
|
|
||||||
`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
|
|
||||||
expected to resolve the promise. `$cancelFn` is a function with no arguments
|
|
||||||
that is expected to cancel the computation of a promise. It is invoked when the
|
|
||||||
`cancel()` method of a promise is called.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\Promise;
|
|
||||||
|
|
||||||
$promise = new Promise(
|
|
||||||
function () use (&$promise) {
|
|
||||||
$promise->resolve('waited');
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
// do something that will cancel the promise computation (e.g., close
|
|
||||||
// a socket, cancel a database query, etc...)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
assert('waited' === $promise->wait());
|
|
||||||
```
|
|
||||||
|
|
||||||
A promise has the following methods:
|
|
||||||
|
|
||||||
- `then(?callable $onFulfilled = null, ?callable $onRejected = null) : PromiseInterface`
|
|
||||||
|
|
||||||
Appends fulfillment and rejection handlers to the promise, and returns a new
|
|
||||||
promise resolving to the return value of the called handler. If a handler is
|
|
||||||
omitted, the original fulfillment value or rejection reason is forwarded.
|
|
||||||
|
|
||||||
- `otherwise(callable $onRejected) : PromiseInterface`
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
- `wait($unwrap = true) : mixed`
|
|
||||||
|
|
||||||
Synchronously waits on the promise to complete.
|
|
||||||
|
|
||||||
`$unwrap` controls whether or not the value of the promise is returned for a
|
|
||||||
fulfilled promise or if an exception is thrown if the promise is rejected.
|
|
||||||
This is set to `true` by default.
|
|
||||||
|
|
||||||
- `cancel()`
|
|
||||||
|
|
||||||
Attempts to cancel the promise if possible. The promise being cancelled and
|
|
||||||
the parent most ancestor that has not yet been resolved will also be
|
|
||||||
cancelled. Any promises waiting on the cancelled promise to resolve will also
|
|
||||||
be cancelled.
|
|
||||||
|
|
||||||
- `getState() : string`
|
|
||||||
|
|
||||||
Returns the state of the promise. One of `pending`, `fulfilled`, or
|
|
||||||
`rejected`.
|
|
||||||
|
|
||||||
- `resolve($value)`
|
|
||||||
|
|
||||||
Fulfills the promise with the given `$value`.
|
|
||||||
|
|
||||||
- `reject($reason)`
|
|
||||||
|
|
||||||
Rejects the promise with the given `$reason`.
|
|
||||||
|
|
||||||
|
|
||||||
### FulfilledPromise
|
|
||||||
|
|
||||||
A fulfilled promise can be created to represent a promise that has been
|
|
||||||
fulfilled.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\FulfilledPromise;
|
|
||||||
|
|
||||||
$promise = new FulfilledPromise('value');
|
|
||||||
|
|
||||||
// Fulfilled callbacks are immediately invoked.
|
|
||||||
$promise->then(function ($value) {
|
|
||||||
echo $value;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### RejectedPromise
|
|
||||||
|
|
||||||
A rejected promise can be created to represent a promise that has been
|
|
||||||
rejected.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Promise\RejectedPromise;
|
|
||||||
|
|
||||||
$promise = new RejectedPromise('Error');
|
|
||||||
|
|
||||||
// Rejected callbacks are immediately invoked.
|
|
||||||
$promise->then(null, function ($reason) {
|
|
||||||
echo $reason;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Promise Interoperability
|
|
||||||
|
|
||||||
This library works with foreign promises that have a `then` method. This means
|
|
||||||
you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
|
|
||||||
for example. When a foreign promise is returned inside of a then method
|
|
||||||
callback, promise resolution will occur recursively.
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Create a React promise
|
|
||||||
$deferred = new React\Promise\Deferred();
|
|
||||||
$reactPromise = $deferred->promise();
|
|
||||||
|
|
||||||
// Create a Guzzle promise that is fulfilled with a React promise.
|
|
||||||
$guzzlePromise = new GuzzleHttp\Promise\Promise();
|
|
||||||
$guzzlePromise->then(function ($value) use ($reactPromise) {
|
|
||||||
// Do something something with the value...
|
|
||||||
// Return the React promise
|
|
||||||
return $reactPromise;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Please note that wait and cancel chaining is no longer possible when forwarding
|
|
||||||
a foreign promise. You will need to wrap a third-party promise with a Guzzle
|
|
||||||
promise in order to utilize wait and cancel functions with foreign promises.
|
|
||||||
|
|
||||||
|
|
||||||
### Event Loop Integration
|
|
||||||
|
|
||||||
In order to keep the stack size constant, Guzzle promises are resolved
|
|
||||||
asynchronously using a task queue. When waiting on promises synchronously, the
|
|
||||||
task queue will be automatically run to ensure that the blocking promise and
|
|
||||||
any forwarded promises are resolved. When using promises asynchronously in an
|
|
||||||
event loop, you will need to run the task queue on each tick of the loop. If
|
|
||||||
you do not run the task queue, then promises will not be resolved.
|
|
||||||
|
|
||||||
You can run the task queue using the `run()` method of the global task queue
|
|
||||||
instance.
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Get the global task queue
|
|
||||||
$queue = GuzzleHttp\Promise\Utils::queue();
|
|
||||||
$queue->run();
|
|
||||||
```
|
|
||||||
|
|
||||||
For example, you could use Guzzle promises with React using a short periodic
|
|
||||||
timer. Avoid zero-interval timers because they may keep the loop busy even when
|
|
||||||
there is no promise work to run.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$loop = React\EventLoop\Factory::create();
|
|
||||||
$loop->addPeriodicTimer(0.01, [$queue, 'run']);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Implementation Notes
|
|
||||||
|
|
||||||
### Promise Resolution and Chaining is Handled Iteratively
|
|
||||||
|
|
||||||
By shuffling pending handlers from one owner to another, promises are
|
|
||||||
resolved iteratively, allowing for "infinite" then chaining.
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
require 'vendor/autoload.php';
|
|
||||||
|
|
||||||
use GuzzleHttp\Promise\Promise;
|
|
||||||
|
|
||||||
$parent = new Promise();
|
|
||||||
$p = $parent;
|
|
||||||
|
|
||||||
for ($i = 0; $i < 1000; $i++) {
|
|
||||||
$p = $p->then(function ($v) {
|
|
||||||
// The stack size remains constant (a good thing)
|
|
||||||
echo xdebug_get_stack_depth() . ', ';
|
|
||||||
return $v + 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$parent->resolve(0);
|
|
||||||
var_dump($p->wait()); // int(1000)
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
When a promise is fulfilled or rejected with a non-promise value, the promise
|
|
||||||
then takes ownership of the handlers of each child promise and delivers values
|
|
||||||
down the chain without using recursion.
|
|
||||||
|
|
||||||
When a promise is resolved with another promise, the original promise transfers
|
|
||||||
all of its pending handlers to the new promise. When the new promise is
|
|
||||||
eventually resolved, all of the pending handlers are delivered the forwarded
|
|
||||||
value.
|
|
||||||
|
|
||||||
### A Promise is the Deferred
|
|
||||||
|
|
||||||
Some promise libraries implement promises using a deferred object to represent
|
|
||||||
a computation and a promise object to represent the delivery of the result of
|
|
||||||
the computation. This is a nice separation of computation and delivery because
|
|
||||||
consumers of the promise cannot modify the value that will be eventually
|
|
||||||
delivered.
|
|
||||||
|
|
||||||
One side effect of being able to implement promise resolution and chaining
|
|
||||||
iteratively is that you need to be able for one promise to reach into the state
|
|
||||||
of another promise to shuffle around ownership of handlers. In order to achieve
|
|
||||||
this without making the handlers of a promise publicly mutable, a promise is
|
|
||||||
also the deferred value, allowing promises of the same parent class to reach
|
|
||||||
into and modify the private properties of promises of the same type. While this
|
|
||||||
does allow consumers of the value to modify the resolution or rejection of the
|
|
||||||
deferred, it is a small price to pay for keeping the stack size constant.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$promise = new Promise();
|
|
||||||
$promise->then(function ($value) { echo $value; });
|
|
||||||
// The promise is the deferred value, so you can deliver a value to it.
|
|
||||||
$promise->resolve('foo');
|
|
||||||
// prints "foo"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Upgrading
|
|
||||||
|
|
||||||
See [UPGRADING.md](UPGRADING.md) for package upgrade notes.
|
|
||||||
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/promises/security/policy) for more information.
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
|
|
||||||
|
|
||||||
|
|
||||||
## For Enterprise
|
|
||||||
|
|
||||||
Available as part of the Tidelift Subscription
|
|
||||||
|
|
||||||
The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-promises?utm_source=packagist-guzzlehttp-promises&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
|
||||||
83
vendor/guzzlehttp/promises/UPGRADING.md
vendored
83
vendor/guzzlehttp/promises/UPGRADING.md
vendored
|
|
@ -1,83 +0,0 @@
|
||||||
Guzzle Promises Upgrade Guide
|
|
||||||
=============================
|
|
||||||
|
|
||||||
1.x to 2.0
|
|
||||||
----------
|
|
||||||
|
|
||||||
Guzzle Promises 2.0 is a major release that removes deprecated APIs, raises the
|
|
||||||
minimum PHP version, and adds PHP 7 parameter and return types. Applications that
|
|
||||||
only use the object-oriented API should usually need small changes. Applications
|
|
||||||
that call helper functions, implement package interfaces, extend package classes,
|
|
||||||
or pass invalid argument types need closer review.
|
|
||||||
|
|
||||||
#### PHP Version and Dependencies
|
|
||||||
|
|
||||||
Guzzle Promises 2.0 requires PHP `^7.2.5 || ^8.0`. Guzzle Promises 1.x
|
|
||||||
supported PHP `>=5.5`.
|
|
||||||
|
|
||||||
#### PHP 7 Type Hints and Return Types
|
|
||||||
|
|
||||||
Type hints and return types were added wherever possible. Please make sure:
|
|
||||||
|
|
||||||
- You pass values of the documented type when calling methods and functions.
|
|
||||||
- Classes that implement `PromiseInterface`, `PromisorInterface`, or
|
|
||||||
`TaskQueueInterface` update method signatures to remain compatible.
|
|
||||||
- Classes that extend Guzzle Promises classes update any overridden method
|
|
||||||
signatures to remain compatible.
|
|
||||||
- Code that expected package-specific exceptions for invalid argument types may
|
|
||||||
now receive PHP `TypeError` exceptions instead.
|
|
||||||
|
|
||||||
#### Soft-Final Classes
|
|
||||||
|
|
||||||
All previously non-final non-exception classes are now final or annotated with
|
|
||||||
`@final`. If your code extends one of these classes, replace inheritance with
|
|
||||||
composition or implement the relevant interface directly.
|
|
||||||
|
|
||||||
#### Removed Function API
|
|
||||||
|
|
||||||
The static API was introduced in 1.4.0 to mitigate problems with functions
|
|
||||||
conflicting between global and local copies of the package. The function API was
|
|
||||||
removed in 2.0.0, along with the Composer `files` autoload entry that loaded
|
|
||||||
`src/functions_include.php`.
|
|
||||||
|
|
||||||
Replace namespaced function calls with the corresponding static methods in the
|
|
||||||
`GuzzleHttp\Promise` namespace:
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Before:
|
|
||||||
use function GuzzleHttp\Promise\promise_for;
|
|
||||||
|
|
||||||
$promise = promise_for('value');
|
|
||||||
|
|
||||||
// After:
|
|
||||||
use GuzzleHttp\Promise\Create;
|
|
||||||
|
|
||||||
$promise = Create::promiseFor('value');
|
|
||||||
```
|
|
||||||
|
|
||||||
| Original Function | Replacement Method |
|
|
||||||
|-------------------|--------------------|
|
|
||||||
| `queue` | `Utils::queue` |
|
|
||||||
| `task` | `Utils::task` |
|
|
||||||
| `promise_for` | `Create::promiseFor` |
|
|
||||||
| `rejection_for` | `Create::rejectionFor` |
|
|
||||||
| `exception_for` | `Create::exceptionFor` |
|
|
||||||
| `iter_for` | `Create::iterFor` |
|
|
||||||
| `inspect` | `Utils::inspect` |
|
|
||||||
| `inspect_all` | `Utils::inspectAll` |
|
|
||||||
| `unwrap` | `Utils::unwrap` |
|
|
||||||
| `all` | `Utils::all` |
|
|
||||||
| `some` | `Utils::some` |
|
|
||||||
| `any` | `Utils::any` |
|
|
||||||
| `settle` | `Utils::settle` |
|
|
||||||
| `each` | `Each::of` |
|
|
||||||
| `each_limit` | `Each::ofLimit` |
|
|
||||||
| `each_limit_all` | `Each::ofLimitAll` |
|
|
||||||
| `!is_fulfilled` | `Is::pending` |
|
|
||||||
| `is_fulfilled` | `Is::fulfilled` |
|
|
||||||
| `is_rejected` | `Is::rejected` |
|
|
||||||
| `is_settled` | `Is::settled` |
|
|
||||||
| `coroutine` | `Coroutine::of` |
|
|
||||||
|
|
||||||
For the full 2.0 diff, see
|
|
||||||
https://github.com/guzzle/promises/compare/1.5.3...2.0.0.
|
|
||||||
6
vendor/guzzlehttp/promises/src/Utils.php
vendored
6
vendor/guzzlehttp/promises/src/Utils.php
vendored
|
|
@ -168,6 +168,12 @@ final class Utils
|
||||||
|
|
||||||
if (true === $recursive) {
|
if (true === $recursive) {
|
||||||
$promise = $promise->then(function ($results) use ($recursive, &$promises) {
|
$promise = $promise->then(function ($results) use ($recursive, &$promises) {
|
||||||
|
// A consumed generator cannot be traversed again, so a
|
||||||
|
// recursive pass has nothing further to observe.
|
||||||
|
if ($promises instanceof \Generator) {
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($promises as $promise) {
|
foreach ($promises as $promise) {
|
||||||
if (Is::pending($promise)) {
|
if (Is::pending($promise)) {
|
||||||
return self::all($promises, $recursive);
|
return self::all($promises, $recursive);
|
||||||
|
|
|
||||||
578
vendor/guzzlehttp/psr7/CHANGELOG.md
vendored
578
vendor/guzzlehttp/psr7/CHANGELOG.md
vendored
|
|
@ -1,578 +0,0 @@
|
||||||
# Change Log
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## 2.11.0 - 2026-06-02
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Changed `Utils::modifyRequest()` to reject conflicting URI and `Host` header changes in the same call
|
|
||||||
- Changed `Header::parse()` to split semicolon-separated parameters without repeated regular expression lookaheads
|
|
||||||
- Changed `UriComparator::isCrossOrigin()` so only HTTP and HTTPS missing ports receive implicit default ports
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Deprecated invalid PSR-7 arguments that guzzlehttp/psr7 3.0 will require native types for
|
|
||||||
- Deprecated non-string header values that guzzlehttp/psr7 3.0 will reject
|
|
||||||
- Deprecated empty header value arrays that guzzlehttp/psr7 3.0 will reject
|
|
||||||
- Deprecated URI schemes that do not match guzzlehttp/psr7 3.0 syntax requirements
|
|
||||||
- Deprecated multipart boundary and custom part header metadata that guzzlehttp/psr7 3.0 will reject
|
|
||||||
- Deprecated reliance on automatic uppercasing of request methods; guzzlehttp/psr7 3.0 preserves method casing
|
|
||||||
- Deprecated invalid `Utils::modifyRequest()` change values that guzzlehttp/psr7 3.0 will reject
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed `Utils::copyToStream()` to retry short destination writes instead of dropping the unwritten remainder
|
|
||||||
- Fixed `Header::parse()` splitting of semicolon-separated parameters with escaped quotes
|
|
||||||
|
|
||||||
## 2.10.4 - 2026-05-29
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Apply `UriNormalizer` percent-encoding normalizations to URI fragments
|
|
||||||
- Make `LimitStream::getSize()` return `0` for slices past the underlying stream end
|
|
||||||
- Make `AppendStream::read()` return an empty string when no streams are attached
|
|
||||||
- Make `CachingStream::read()` throw on an incomplete cache-target write instead of silently corrupting replays
|
|
||||||
- Prevent `CachingStream::seek()` from looping indefinitely when the remote stream makes no progress
|
|
||||||
|
|
||||||
## 2.10.3 - 2026-05-27
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed URI parsing for IPv6 literals containing embedded IPv4 addresses
|
|
||||||
- Fixed malformed UTF-8 URI strings being parsed as empty URIs
|
|
||||||
|
|
||||||
## 2.10.2 - 2026-05-25
|
|
||||||
|
|
||||||
### Security
|
|
||||||
|
|
||||||
- Reject control and whitespace characters in URI host components (GHSA-hq7v-mx3g-29hw)
|
|
||||||
- Reject malformed Host values when constructing request URIs (GHSA-34xg-wgjx-8xph)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Make `ServerRequest::fromGlobals()` robust against unexpected HTTP header value types in `$_SERVER`
|
|
||||||
|
|
||||||
## 2.10.1 - 2026-05-20
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix `Utils::modifyRequest()` with numeric header names
|
|
||||||
|
|
||||||
## 2.10.0 - 2026-05-19
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Harden `ServerRequest::fromGlobals()` against malformed `$_SERVER` values
|
|
||||||
- Prevent custom stream metadata from affecting internal size handling
|
|
||||||
- Throw when `StreamWrapper::getResource()` cannot create a resource
|
|
||||||
- Preserve custom request implementations in `Utils::modifyRequest()`
|
|
||||||
- Preserve custom URI implementations in `UriResolver::resolve()`
|
|
||||||
- Make `Uri::__toString()` side-effect-free
|
|
||||||
|
|
||||||
## 2.9.1 - 2026-05-19
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix parsing of relative path references containing a colon in a non-initial path segment
|
|
||||||
- Fix `CachingStream::detach()` returning an incomplete resource before the decorated stream has been fully read
|
|
||||||
- Fix `Message::bodySummary()` returning `null` when truncating printable UTF-8 bodies inside a multibyte character
|
|
||||||
|
|
||||||
## 2.9.0 - 2026-03-10
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added nested array expansion support to `MultipartStream`
|
|
||||||
- Added `@return static` to `MessageTrait` methods
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Updated MIME type mappings
|
|
||||||
|
|
||||||
## 2.8.1 - 2026-03-10
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Encode `+` signs in `Uri::withQueryValue()` and `Uri::withQueryValues()` to prevent them being interpreted as spaces
|
|
||||||
|
|
||||||
## 2.8.0 - 2025-08-23
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Allow empty lists as header values
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- PHP 8.5 support
|
|
||||||
|
|
||||||
## 2.7.1 - 2025-03-27
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed uppercase IPv6 addresses in URI
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Improve uploaded file error message
|
|
||||||
|
|
||||||
## 2.7.0 - 2024-07-18
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Add `Utils::redactUserInfo()` method
|
|
||||||
- Add ability to encode bools as ints in `Query::build`
|
|
||||||
|
|
||||||
## 2.6.3 - 2024-07-18
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Make `StreamWrapper::stream_stat()` return `false` if inner stream's size is `null`
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- PHP 8.4 support
|
|
||||||
|
|
||||||
## 2.6.2 - 2023-12-03
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed another issue with the fact that PHP transforms numeric strings in array keys to ints
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Updated links in docs to their canonical versions
|
|
||||||
- Replaced `call_user_func*` with native calls
|
|
||||||
|
|
||||||
## 2.6.1 - 2023-08-27
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Properly handle the fact that PHP transforms numeric strings in array keys to ints
|
|
||||||
|
|
||||||
## 2.6.0 - 2023-08-03
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Updated the mime type map to add some new entries, fix a couple of invalid entries, and remove an invalid entry
|
|
||||||
- Fallback to `application/octet-stream` if we are unable to guess the content type for a multipart file upload
|
|
||||||
|
|
||||||
## 2.5.1 - 2023-08-03
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Corrected mime type for `.acc` files to `audio/aac`
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- PHP 8.3 support
|
|
||||||
|
|
||||||
## 2.5.0 - 2023-04-17
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Adjusted `psr/http-message` version constraint to `^1.1 || ^2.0`
|
|
||||||
|
|
||||||
## 2.4.5 - 2023-04-17
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Prevent possible warnings on unset variables in `ServerRequest::normalizeNestedFileSpec`
|
|
||||||
- Fixed `Message::bodySummary` when `preg_match` fails
|
|
||||||
- Fixed header validation issue
|
|
||||||
|
|
||||||
## 2.4.4 - 2023-03-09
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Removed the need for `AllowDynamicProperties` in `LazyOpenStream`
|
|
||||||
|
|
||||||
## 2.4.3 - 2022-10-26
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Replaced `sha1(uniqid())` by `bin2hex(random_bytes(20))`
|
|
||||||
|
|
||||||
## 2.4.2 - 2022-10-25
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed erroneous behaviour when combining host and relative path
|
|
||||||
|
|
||||||
## 2.4.1 - 2022-08-28
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Rewind body before reading in `Message::bodySummary`
|
|
||||||
|
|
||||||
## 2.4.0 - 2022-06-20
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added provisional PHP 8.2 support
|
|
||||||
- Added `UriComparator::isCrossOrigin` method
|
|
||||||
|
|
||||||
## 2.3.0 - 2022-06-09
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Added `Header::splitList` method
|
|
||||||
- Added `Utils::tryGetContents` method
|
|
||||||
- Improved `Stream::getContents` method
|
|
||||||
- Updated mimetype mappings
|
|
||||||
|
|
||||||
## 2.2.2 - 2022-06-08
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix `Message::parseRequestUri` for numeric headers
|
|
||||||
- Re-wrap exceptions thrown in `fread` into runtime exceptions
|
|
||||||
- Throw an exception when multipart options is misformatted
|
|
||||||
|
|
||||||
## 2.2.1 - 2022-03-20
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Correct header value validation
|
|
||||||
|
|
||||||
## 2.2.0 - 2022-03-20
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- A more compressive list of mime types
|
|
||||||
- Add JsonSerializable to Uri
|
|
||||||
- Missing return types
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Bug MultipartStream no `uri` metadata
|
|
||||||
- Bug MultipartStream with filename for `data://` streams
|
|
||||||
- Fixed new line handling in MultipartStream
|
|
||||||
- Reduced RAM usage when copying streams
|
|
||||||
- Updated parsing in `Header::normalize()`
|
|
||||||
|
|
||||||
## 2.1.1 - 2022-03-20
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Validate header values properly
|
|
||||||
|
|
||||||
## 2.1.0 - 2021-10-06
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Attempting to create a `Uri` object from a malformed URI will no longer throw a generic
|
|
||||||
`InvalidArgumentException`, but rather a `MalformedUriException`, which inherits from the former
|
|
||||||
for backwards compatibility. Callers relying on the exception being thrown to detect invalid
|
|
||||||
URIs should catch the new exception.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Return `null` in caching stream size if remote size is `null`
|
|
||||||
|
|
||||||
## 2.0.0 - 2021-06-30
|
|
||||||
|
|
||||||
Identical to the RC release.
|
|
||||||
|
|
||||||
## 2.0.0@RC-1 - 2021-04-29
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Handle possibly unset `url` in `stream_get_meta_data`
|
|
||||||
|
|
||||||
## 2.0.0@beta-1 - 2021-03-21
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- PSR-17 factories
|
|
||||||
- Made classes final
|
|
||||||
- PHP7 type hints
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- When building a query string, booleans are represented as 1 and 0.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- PHP < 7.2 support
|
|
||||||
- All functions in the `GuzzleHttp\Psr7` namespace
|
|
||||||
|
|
||||||
## 1.8.1 - 2021-03-21
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Issue parsing IPv6 URLs
|
|
||||||
- Issue modifying ServerRequest lost all its attributes
|
|
||||||
|
|
||||||
## 1.8.0 - 2021-03-21
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Locale independent URL parsing
|
|
||||||
- Most classes got a `@final` annotation to prepare for 2.0
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Issue when creating stream from `php://input` and curl-ext is not installed
|
|
||||||
- Broken `Utils::tryFopen()` on PHP 8
|
|
||||||
|
|
||||||
## 1.7.0 - 2020-09-30
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Replaced functions by static methods
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Converting a non-seekable stream to a string
|
|
||||||
- Handle multiple Set-Cookie correctly
|
|
||||||
- Ignore array keys in header values when merging
|
|
||||||
- Allow multibyte characters to be parsed in `Message:bodySummary()`
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Restored partial HHVM 3 support
|
|
||||||
|
|
||||||
|
|
||||||
## [1.6.1] - 2019-07-02
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Accept null and bool header values again
|
|
||||||
|
|
||||||
|
|
||||||
## [1.6.0] - 2019-06-30
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Allowed version `^3.0` of `ralouphie/getallheaders` dependency (#244)
|
|
||||||
- Added MIME type for WEBP image format (#246)
|
|
||||||
- Added more validation of values according to PSR-7 and RFC standards, e.g. status code range (#250, #272)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Tests don't pass with HHVM 4.0, so HHVM support got dropped. Other libraries like composer have done the same. (#262)
|
|
||||||
- Accept port number 0 to be valid (#270)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixed subsequent reads from `php://input` in ServerRequest (#247)
|
|
||||||
- Fixed readable/writable detection for certain stream modes (#248)
|
|
||||||
- Fixed encoding of special characters in the `userInfo` component of an URI (#253)
|
|
||||||
|
|
||||||
|
|
||||||
## [1.5.2] - 2018-12-04
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Check body size when getting the message summary
|
|
||||||
|
|
||||||
|
|
||||||
## [1.5.1] - 2018-12-04
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Get the summary of a body only if it is readable
|
|
||||||
|
|
||||||
|
|
||||||
## [1.5.0] - 2018-12-03
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Response first-line to response string exception (fixes #145)
|
|
||||||
- A test for #129 behavior
|
|
||||||
- `get_message_body_summary` function in order to get the message summary
|
|
||||||
- `3gp` and `mkv` mime types
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Clarify exception message when stream is detached
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- Deprecated parsing folded header lines as per RFC 7230
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix `AppendStream::detach` to not close streams
|
|
||||||
- `InflateStream` preserves `isSeekable` attribute of the underlying stream
|
|
||||||
- `ServerRequest::getUriFromGlobals` to support URLs in query parameters
|
|
||||||
|
|
||||||
|
|
||||||
Several other fixes and improvements.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.4.2] - 2017-03-20
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing
|
|
||||||
calls to `trigger_error` when deprecated methods are invoked.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.4.1] - 2017-02-27
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Rriggering of silenced deprecation warnings.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Reverted BC break by reintroducing behavior to automagically fix a URI with a
|
|
||||||
relative path and an authority by adding a leading slash to the path. It's only
|
|
||||||
deprecated now.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.4.0] - 2017-02-21
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Added common URI utility methods based on RFC 3986 (see documentation in the readme):
|
|
||||||
- `Uri::isDefaultPort`
|
|
||||||
- `Uri::isAbsolute`
|
|
||||||
- `Uri::isNetworkPathReference`
|
|
||||||
- `Uri::isAbsolutePathReference`
|
|
||||||
- `Uri::isRelativePathReference`
|
|
||||||
- `Uri::isSameDocumentReference`
|
|
||||||
- `Uri::composeComponents`
|
|
||||||
- `UriNormalizer::normalize`
|
|
||||||
- `UriNormalizer::isEquivalent`
|
|
||||||
- `UriResolver::relativize`
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form.
|
|
||||||
- Allow `parse_response` to parse a response without delimiting space and reason.
|
|
||||||
- Ensure each URI modification results in a valid URI according to PSR-7 discussions.
|
|
||||||
Invalid modifications will throw an exception instead of returning a wrong URI or
|
|
||||||
doing some magic.
|
|
||||||
- `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception
|
|
||||||
because the path of a URI with an authority must start with a slash "/" or be empty
|
|
||||||
- `(new Uri())->withScheme('http')` will return `'http://localhost'`
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- `Uri::resolve` in favor of `UriResolver::resolve`
|
|
||||||
- `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `Stream::read` when length parameter <= 0.
|
|
||||||
- `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
|
|
||||||
- `ServerRequest::getUriFromGlobals` when `Host` header contains port.
|
|
||||||
- Compatibility of URIs with `file` scheme and empty host.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.3.1] - 2016-06-25
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `Uri::__toString` for network path references, e.g. `//example.org`.
|
|
||||||
- Missing lowercase normalization for host.
|
|
||||||
- Handling of URI components in case they are `'0'` in a lot of places,
|
|
||||||
e.g. as a user info password.
|
|
||||||
- `Uri::withAddedHeader` to correctly merge headers with different case.
|
|
||||||
- Trimming of header values in `Uri::withAddedHeader`. Header values may
|
|
||||||
be surrounded by whitespace which should be ignored according to RFC 7230
|
|
||||||
Section 3.2.4. This does not apply to header names.
|
|
||||||
- `Uri::withAddedHeader` with an array of header values.
|
|
||||||
- `Uri::resolve` when base path has no slash and handling of fragment.
|
|
||||||
- Handling of encoding in `Uri::with(out)QueryValue` so one can pass the
|
|
||||||
key/value both in encoded as well as decoded form to those methods. This is
|
|
||||||
consistent with withPath, withQuery etc.
|
|
||||||
- `ServerRequest::withoutAttribute` when attribute value is null.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.3.0] - 2016-04-13
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Remaining interfaces needed for full PSR7 compatibility
|
|
||||||
(ServerRequestInterface, UploadedFileInterface, etc.).
|
|
||||||
- Support for stream_for from scalars.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Can now extend Uri.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- A bug in validating request methods by making it more permissive.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.2.3] - 2016-02-18
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
|
|
||||||
streams, which can sometimes return fewer bytes than requested with `fread`.
|
|
||||||
- Handling of gzipped responses with FNAME headers.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.2.2] - 2016-01-22
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Support for URIs without any authority.
|
|
||||||
- Support for HTTP 451 'Unavailable For Legal Reasons.'
|
|
||||||
- Support for using '0' as a filename.
|
|
||||||
- Support for including non-standard ports in Host headers.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.2.1] - 2015-11-02
|
|
||||||
|
|
||||||
### Changes
|
|
||||||
|
|
||||||
- Now supporting negative offsets when seeking to SEEK_END.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.2.0] - 2015-08-15
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Body as `"0"` is now properly added to a response.
|
|
||||||
- Now allowing forward seeking in CachingStream.
|
|
||||||
- Now properly parsing HTTP requests that contain proxy targets in
|
|
||||||
`parse_request`.
|
|
||||||
- functions.php is now conditionally required.
|
|
||||||
- user-info is no longer dropped when resolving URIs.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.1.0] - 2015-06-24
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- URIs can now be relative.
|
|
||||||
- `multipart/form-data` headers are now overridden case-insensitively.
|
|
||||||
- URI paths no longer encode the following characters because they are allowed
|
|
||||||
in URIs: "(", ")", "*", "!", "'"
|
|
||||||
- A port is no longer added to a URI when the scheme is missing and no port is
|
|
||||||
present.
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0 - 2015-05-19
|
|
||||||
|
|
||||||
Initial release.
|
|
||||||
|
|
||||||
Currently unsupported:
|
|
||||||
|
|
||||||
- `Psr\Http\Message\ServerRequestInterface`
|
|
||||||
- `Psr\Http\Message\UploadedFileInterface`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[1.6.0]: https://github.com/guzzle/psr7/compare/1.5.2...1.6.0
|
|
||||||
[1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2
|
|
||||||
[1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1
|
|
||||||
[1.5.0]: https://github.com/guzzle/psr7/compare/1.4.2...1.5.0
|
|
||||||
[1.4.2]: https://github.com/guzzle/psr7/compare/1.4.1...1.4.2
|
|
||||||
[1.4.1]: https://github.com/guzzle/psr7/compare/1.4.0...1.4.1
|
|
||||||
[1.4.0]: https://github.com/guzzle/psr7/compare/1.3.1...1.4.0
|
|
||||||
[1.3.1]: https://github.com/guzzle/psr7/compare/1.3.0...1.3.1
|
|
||||||
[1.3.0]: https://github.com/guzzle/psr7/compare/1.2.3...1.3.0
|
|
||||||
[1.2.3]: https://github.com/guzzle/psr7/compare/1.2.2...1.2.3
|
|
||||||
[1.2.2]: https://github.com/guzzle/psr7/compare/1.2.1...1.2.2
|
|
||||||
[1.2.1]: https://github.com/guzzle/psr7/compare/1.2.0...1.2.1
|
|
||||||
[1.2.0]: https://github.com/guzzle/psr7/compare/1.1.0...1.2.0
|
|
||||||
[1.1.0]: https://github.com/guzzle/psr7/compare/1.0.0...1.1.0
|
|
||||||
880
vendor/guzzlehttp/psr7/README.md
vendored
880
vendor/guzzlehttp/psr7/README.md
vendored
|
|
@ -1,880 +0,0 @@
|
||||||
# PSR-7 Message Implementation
|
|
||||||
|
|
||||||
This repository contains a full [PSR-7](https://www.php-fig.org/psr/psr-7/)
|
|
||||||
message implementation, several stream decorators, and some helpful
|
|
||||||
functionality like query string parsing.
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
This package comes with a number of stream implementations and stream
|
|
||||||
decorators.
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```shell
|
|
||||||
composer require guzzlehttp/psr7
|
|
||||||
```
|
|
||||||
|
|
||||||
## Version Guidance
|
|
||||||
|
|
||||||
| Version | Status | PHP Version |
|
|
||||||
|---------|---------------------|--------------|
|
|
||||||
| 1.x | EOL (2024-06-30) | >=5.4,<8.2 |
|
|
||||||
| 2.x | Latest | >=7.2.5,<8.6 |
|
|
||||||
|
|
||||||
See [UPGRADING.md](UPGRADING.md) for notes on upgrading from 1.x to 2.0.
|
|
||||||
|
|
||||||
|
|
||||||
## AppendStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\AppendStream`
|
|
||||||
|
|
||||||
Reads from multiple streams, one after the other.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Psr7;
|
|
||||||
|
|
||||||
$a = Psr7\Utils::streamFor('abc, ');
|
|
||||||
$b = Psr7\Utils::streamFor('123.');
|
|
||||||
$composed = new Psr7\AppendStream([$a, $b]);
|
|
||||||
|
|
||||||
$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));
|
|
||||||
|
|
||||||
echo $composed; // abc, 123. Above all listen to me.
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## BufferStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\BufferStream`
|
|
||||||
|
|
||||||
Provides a buffer stream that can be written to fill a buffer, and read
|
|
||||||
from to remove bytes from the buffer.
|
|
||||||
|
|
||||||
This stream returns a "hwm" metadata value that tells upstream consumers
|
|
||||||
what the configured high water mark of the stream is, or the maximum
|
|
||||||
preferred size of the buffer.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Psr7;
|
|
||||||
|
|
||||||
// When more than 1024 bytes are in the buffer, it will begin returning
|
|
||||||
// 0 to writes. This is an indication that writers should slow down.
|
|
||||||
$buffer = new Psr7\BufferStream(1024);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## CachingStream
|
|
||||||
|
|
||||||
The CachingStream is used to allow seeking over previously read bytes on
|
|
||||||
non-seekable streams. This can be useful when transferring a non-seekable
|
|
||||||
entity body fails due to needing to rewind the stream (for example, resulting
|
|
||||||
from a redirect). Data that is read from the remote stream will be buffered in
|
|
||||||
a PHP temp stream so that previously read bytes are cached first in memory,
|
|
||||||
then on disk.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Psr7;
|
|
||||||
|
|
||||||
$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
|
|
||||||
$stream = new Psr7\CachingStream($original);
|
|
||||||
|
|
||||||
$stream->read(1024);
|
|
||||||
echo $stream->tell();
|
|
||||||
// 1024
|
|
||||||
|
|
||||||
$stream->seek(0);
|
|
||||||
echo $stream->tell();
|
|
||||||
// 0
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## DroppingStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\DroppingStream`
|
|
||||||
|
|
||||||
Stream decorator that begins dropping data once the size of the underlying
|
|
||||||
stream becomes too full.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Psr7;
|
|
||||||
|
|
||||||
// Create an empty stream
|
|
||||||
$stream = Psr7\Utils::streamFor();
|
|
||||||
|
|
||||||
// Start dropping data when the stream has more than 10 bytes
|
|
||||||
$dropping = new Psr7\DroppingStream($stream, 10);
|
|
||||||
|
|
||||||
$dropping->write('01234567890123456789');
|
|
||||||
echo $stream; // 0123456789
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## FnStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\FnStream`
|
|
||||||
|
|
||||||
Compose stream implementations based on a hash of callables.
|
|
||||||
|
|
||||||
Allows for easy testing and extension of a provided stream without needing
|
|
||||||
to create a concrete class for a simple extension point.
|
|
||||||
|
|
||||||
```php
|
|
||||||
|
|
||||||
use GuzzleHttp\Psr7;
|
|
||||||
|
|
||||||
$stream = Psr7\Utils::streamFor('hi');
|
|
||||||
$fnStream = Psr7\FnStream::decorate($stream, [
|
|
||||||
'rewind' => function () use ($stream) {
|
|
||||||
echo 'About to rewind - ';
|
|
||||||
$stream->rewind();
|
|
||||||
echo 'rewound!';
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
$fnStream->rewind();
|
|
||||||
// Outputs: About to rewind - rewound!
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## InflateStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\InflateStream`
|
|
||||||
|
|
||||||
Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.
|
|
||||||
|
|
||||||
This stream decorator converts the provided stream to a PHP stream resource,
|
|
||||||
then appends the zlib.inflate filter. The stream is then converted back
|
|
||||||
to a Guzzle stream resource to be used as a Guzzle stream.
|
|
||||||
|
|
||||||
|
|
||||||
## LazyOpenStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\LazyOpenStream`
|
|
||||||
|
|
||||||
Lazily reads or writes to a file that is opened only after an IO operation
|
|
||||||
take place on the stream.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Psr7;
|
|
||||||
|
|
||||||
$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
|
|
||||||
// The file has not yet been opened...
|
|
||||||
|
|
||||||
echo $stream->read(10);
|
|
||||||
// The file is opened and read from only when needed.
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## LimitStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\LimitStream`
|
|
||||||
|
|
||||||
LimitStream can be used to read a subset or slice of an existing stream object.
|
|
||||||
This can be useful for breaking a large file into smaller pieces to be sent in
|
|
||||||
chunks (e.g. Amazon S3's multipart upload API).
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Psr7;
|
|
||||||
|
|
||||||
$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
|
|
||||||
echo $original->getSize();
|
|
||||||
// >>> 1048576
|
|
||||||
|
|
||||||
// Limit the size of the body to 1024 bytes and start reading from byte 2048
|
|
||||||
$stream = new Psr7\LimitStream($original, 1024, 2048);
|
|
||||||
echo $stream->getSize();
|
|
||||||
// >>> 1024
|
|
||||||
echo $stream->tell();
|
|
||||||
// >>> 0
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## MultipartStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\MultipartStream`
|
|
||||||
|
|
||||||
Stream that when read returns bytes for a streaming multipart or
|
|
||||||
multipart/form-data stream.
|
|
||||||
|
|
||||||
Each multipart element must contain a `name` and `contents` key. `contents` may
|
|
||||||
be any non-array value accepted by `GuzzleHttp\Psr7\Utils::streamFor()`,
|
|
||||||
including closures and invokable objects. Array contents are recursively
|
|
||||||
expanded into nested form fields.
|
|
||||||
|
|
||||||
|
|
||||||
## NoSeekStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\NoSeekStream`
|
|
||||||
|
|
||||||
NoSeekStream wraps a stream and does not allow seeking.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Psr7;
|
|
||||||
|
|
||||||
$original = Psr7\Utils::streamFor('foo');
|
|
||||||
$noSeek = new Psr7\NoSeekStream($original);
|
|
||||||
|
|
||||||
echo $noSeek->read(3);
|
|
||||||
// foo
|
|
||||||
var_export($noSeek->isSeekable());
|
|
||||||
// false
|
|
||||||
$noSeek->seek(0);
|
|
||||||
var_export($noSeek->read(3));
|
|
||||||
// NULL
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## PumpStream
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\PumpStream`
|
|
||||||
|
|
||||||
Provides a read only stream that pumps data from a PHP callable.
|
|
||||||
|
|
||||||
When invoking the provided callable, the PumpStream will pass the suggested
|
|
||||||
number of bytes to read to the callable. The callable can choose to ignore
|
|
||||||
this value and return fewer or more bytes than requested. Any extra data
|
|
||||||
returned by the provided callable is buffered internally until drained using
|
|
||||||
the read() function of the PumpStream. The provided callable MUST return
|
|
||||||
false or null when there is no more data to read.
|
|
||||||
|
|
||||||
Userland callables that declare no parameters are tolerated by PHP, but
|
|
||||||
length-aware callables remain the recommended formal shape.
|
|
||||||
|
|
||||||
|
|
||||||
## Implementing stream decorators
|
|
||||||
|
|
||||||
Creating a stream decorator is very easy thanks to the
|
|
||||||
`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
|
|
||||||
implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
|
|
||||||
stream. Just `use` the `StreamDecoratorTrait` and implement your custom
|
|
||||||
methods.
|
|
||||||
|
|
||||||
For example, let's say we wanted to call a specific function each time the last
|
|
||||||
byte is read from a stream. This could be implemented by overriding the
|
|
||||||
`read()` method.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use Psr\Http\Message\StreamInterface;
|
|
||||||
use GuzzleHttp\Psr7\StreamDecoratorTrait;
|
|
||||||
|
|
||||||
class EofCallbackStream implements StreamInterface
|
|
||||||
{
|
|
||||||
use StreamDecoratorTrait;
|
|
||||||
|
|
||||||
private $callback;
|
|
||||||
|
|
||||||
private $stream;
|
|
||||||
|
|
||||||
public function __construct(StreamInterface $stream, callable $cb)
|
|
||||||
{
|
|
||||||
$this->stream = $stream;
|
|
||||||
$this->callback = $cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function read($length)
|
|
||||||
{
|
|
||||||
$result = $this->stream->read($length);
|
|
||||||
|
|
||||||
// Invoke the callback when EOF is hit.
|
|
||||||
if ($this->eof()) {
|
|
||||||
($this->callback)();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This decorator could be added to any existing stream and used like so:
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Psr7;
|
|
||||||
|
|
||||||
$original = Psr7\Utils::streamFor('foo');
|
|
||||||
|
|
||||||
$eofStream = new EofCallbackStream($original, function () {
|
|
||||||
echo 'EOF!';
|
|
||||||
});
|
|
||||||
|
|
||||||
$eofStream->read(2);
|
|
||||||
$eofStream->read(1);
|
|
||||||
// echoes "EOF!"
|
|
||||||
$eofStream->seek(0);
|
|
||||||
$eofStream->read(3);
|
|
||||||
// echoes "EOF!"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## PHP StreamWrapper
|
|
||||||
|
|
||||||
You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
|
|
||||||
PSR-7 stream as a PHP stream resource.
|
|
||||||
|
|
||||||
Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
|
|
||||||
stream from a PSR-7 stream.
|
|
||||||
|
|
||||||
```php
|
|
||||||
use GuzzleHttp\Psr7\StreamWrapper;
|
|
||||||
|
|
||||||
$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
|
|
||||||
$resource = StreamWrapper::getResource($stream);
|
|
||||||
echo fread($resource, 6); // outputs hello!
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# Static API
|
|
||||||
|
|
||||||
There are various static methods available under the `GuzzleHttp\Psr7` namespace.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Message::toString`
|
|
||||||
|
|
||||||
`public static function toString(MessageInterface $message): string`
|
|
||||||
|
|
||||||
Returns the string representation of an HTTP message.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
|
|
||||||
echo GuzzleHttp\Psr7\Message::toString($request);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Message::bodySummary`
|
|
||||||
|
|
||||||
`public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null`
|
|
||||||
|
|
||||||
Get a short summary of the message body.
|
|
||||||
|
|
||||||
Will return `null` if the response is not printable.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Message::rewindBody`
|
|
||||||
|
|
||||||
`public static function rewindBody(MessageInterface $message): void`
|
|
||||||
|
|
||||||
Attempts to rewind a message body and throws an exception on failure.
|
|
||||||
|
|
||||||
The body of the message will only be rewound if a call to `tell()`
|
|
||||||
returns a value other than `0`.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Message::parseMessage`
|
|
||||||
|
|
||||||
`public static function parseMessage(string $message): array`
|
|
||||||
|
|
||||||
Parses an HTTP message into an associative array.
|
|
||||||
|
|
||||||
The array contains the "start-line" key containing the start line of
|
|
||||||
the message, "headers" key containing an associative array of header
|
|
||||||
array values, and a "body" key containing the body of the message.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Message::parseRequestUri`
|
|
||||||
|
|
||||||
`public static function parseRequestUri(string $path, array $headers): string`
|
|
||||||
|
|
||||||
Constructs a URI for an HTTP request message.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Message::parseRequest`
|
|
||||||
|
|
||||||
`public static function parseRequest(string $message): Request`
|
|
||||||
|
|
||||||
Parses a request message string into a request object.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Message::parseResponse`
|
|
||||||
|
|
||||||
`public static function parseResponse(string $message): Response`
|
|
||||||
|
|
||||||
Parses a response message string into a response object.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Header::parse`
|
|
||||||
|
|
||||||
`public static function parse(string|array $header): array`
|
|
||||||
|
|
||||||
Parse an array of header values containing ";" separated data into an
|
|
||||||
array of associative arrays representing the header key value pair data
|
|
||||||
of the header. When a parameter does not contain a value, but just
|
|
||||||
contains a key, this function will inject a key with a '' string value.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Header::splitList`
|
|
||||||
|
|
||||||
`public static function splitList(string|string[] $header): string[]`
|
|
||||||
|
|
||||||
Splits a HTTP header defined to contain a comma-separated list into
|
|
||||||
each individual value:
|
|
||||||
|
|
||||||
```
|
|
||||||
$knownEtags = Header::splitList($request->getHeader('if-none-match'));
|
|
||||||
```
|
|
||||||
|
|
||||||
Example headers include `accept`, `cache-control` and `if-none-match`.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Header::normalize` (deprecated)
|
|
||||||
|
|
||||||
`public static function normalize(string|array $header): array`
|
|
||||||
|
|
||||||
`Header::normalize()` is deprecated in favor of [`Header::splitList()`](README.md#guzzlehttppsr7headersplitlist)
|
|
||||||
which performs the same operation with a cleaned up API and improved
|
|
||||||
documentation.
|
|
||||||
|
|
||||||
Converts an array of header values that may contain comma separated
|
|
||||||
headers into an array of headers with no comma separated values.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Query::parse`
|
|
||||||
|
|
||||||
`public static function parse(string $str, int|bool $urlEncoding = true): array`
|
|
||||||
|
|
||||||
Parse a query string into an associative array.
|
|
||||||
|
|
||||||
If multiple values are found for the same key, the value of that key
|
|
||||||
value pair will become an array. This function does not parse nested
|
|
||||||
PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
|
|
||||||
will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Query::build`
|
|
||||||
|
|
||||||
`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string`
|
|
||||||
|
|
||||||
Build a query string from an array of key value pairs.
|
|
||||||
|
|
||||||
This function can use the return value of `parse()` to build a query
|
|
||||||
string. This function does not modify the provided keys when an array is
|
|
||||||
encountered (like `http_build_query()` would).
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::caselessRemove`
|
|
||||||
|
|
||||||
`public static function caselessRemove(iterable<string> $keys, $keys, array $data): array`
|
|
||||||
|
|
||||||
Remove the items given by the keys, case insensitively from the data.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::copyToStream`
|
|
||||||
|
|
||||||
`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void`
|
|
||||||
|
|
||||||
Copy the contents of a stream into another stream until the given number
|
|
||||||
of bytes have been read.
|
|
||||||
|
|
||||||
The copy stops if the destination `write()` returns 0, for example a
|
|
||||||
`BufferStream` at its high water mark or a full `DroppingStream`. For a
|
|
||||||
guaranteed full copy, use a normal writable stream such as a file or
|
|
||||||
`php://temp` stream.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::copyToString`
|
|
||||||
|
|
||||||
`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`
|
|
||||||
|
|
||||||
Copy the contents of a stream into a string until the given number of
|
|
||||||
bytes have been read.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::hash`
|
|
||||||
|
|
||||||
`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`
|
|
||||||
|
|
||||||
Calculate a hash of a stream.
|
|
||||||
|
|
||||||
This method reads the entire stream to calculate a rolling hash, based on
|
|
||||||
PHP's `hash_init` functions.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::modifyRequest`
|
|
||||||
|
|
||||||
`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`
|
|
||||||
|
|
||||||
Clone and modify a request with the given changes.
|
|
||||||
|
|
||||||
This method is useful for reducing the number of clones needed to mutate
|
|
||||||
a message.
|
|
||||||
|
|
||||||
- method: (string) Changes the HTTP method.
|
|
||||||
- set_headers: (array) Sets the given headers.
|
|
||||||
- remove_headers: (array) Remove the given headers.
|
|
||||||
- body: (mixed) Sets the given body. Present non-null values are converted with
|
|
||||||
`GuzzleHttp\Psr7\Utils::streamFor()`, including scalar values, resources,
|
|
||||||
streams, iterators, callable arrays, closures, invokable objects, and
|
|
||||||
objects with `__toString()`. String inputs remain literal bodies.
|
|
||||||
- uri: (UriInterface) Set the URI.
|
|
||||||
- query: (string) Set the query string value of the URI.
|
|
||||||
- version: (string) Set the protocol version.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::readLine`
|
|
||||||
|
|
||||||
`public static function readLine(StreamInterface $stream, ?int $maxLength = null): string`
|
|
||||||
|
|
||||||
Read a line from the stream up to the maximum allowed buffer length.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::redactUserInfo`
|
|
||||||
|
|
||||||
`public static function redactUserInfo(UriInterface $uri): UriInterface`
|
|
||||||
|
|
||||||
Redact the password in the user info part of a URI.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::streamFor`
|
|
||||||
|
|
||||||
`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`
|
|
||||||
|
|
||||||
Create a new stream based on the input type.
|
|
||||||
|
|
||||||
Options is an associative array that can contain the following keys:
|
|
||||||
|
|
||||||
- metadata: Array of custom metadata.
|
|
||||||
- size: Size of the stream.
|
|
||||||
|
|
||||||
This method accepts the following `$resource` types:
|
|
||||||
|
|
||||||
- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
|
|
||||||
- `string`: Creates a stream object that uses the given string as the contents.
|
|
||||||
- `resource`: Creates a stream object that wraps the given PHP stream resource.
|
|
||||||
- `Iterator`: If the provided value implements `Iterator`, then a read-only
|
|
||||||
stream object will be created that wraps the given iterable. Each time the
|
|
||||||
stream is read from, data from the iterator will fill a buffer and will be
|
|
||||||
continuously called until the buffer is equal to the requested read size.
|
|
||||||
Subsequent read calls will first read from the buffer and then call `next`
|
|
||||||
on the underlying iterator until it is exhausted.
|
|
||||||
- `object` with `__toString()`: If the object has the `__toString()` method,
|
|
||||||
the object will be cast to a string and then a stream will be returned that
|
|
||||||
uses the string value.
|
|
||||||
- `NULL`: When `null` is passed, an empty stream object is returned.
|
|
||||||
- `callable`: When a callable array, closure, or invokable object is passed and
|
|
||||||
no earlier resource or object rule applies, a read-only stream object will be
|
|
||||||
created that invokes the given callable. The callable is invoked with the
|
|
||||||
suggested number of bytes to read. The callable can return fewer or more bytes
|
|
||||||
than requested, but MUST return `false` or `null` when there is no more data
|
|
||||||
to return. Any additional bytes will be buffered and used in subsequent reads.
|
|
||||||
String inputs are always treated as string bodies, even when they name
|
|
||||||
callable functions.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
|
|
||||||
$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));
|
|
||||||
|
|
||||||
$generator = function ($bytes) {
|
|
||||||
for ($i = 0; $i < $bytes; $i++) {
|
|
||||||
yield ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::tryFopen`
|
|
||||||
|
|
||||||
`public static function tryFopen(string $filename, string $mode): resource`
|
|
||||||
|
|
||||||
Safely opens a PHP stream resource using a filename.
|
|
||||||
|
|
||||||
When fopen fails, PHP normally raises a warning. This function adds an
|
|
||||||
error handler that checks for errors and throws an exception instead.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::tryGetContents`
|
|
||||||
|
|
||||||
`public static function tryGetContents(resource $stream): string`
|
|
||||||
|
|
||||||
Safely gets the contents of a given stream.
|
|
||||||
|
|
||||||
When stream_get_contents fails, PHP normally raises a warning. This
|
|
||||||
function adds an error handler that checks for errors and throws an
|
|
||||||
exception instead.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\Utils::uriFor`
|
|
||||||
|
|
||||||
`public static function uriFor(string|UriInterface $uri): UriInterface`
|
|
||||||
|
|
||||||
Returns a UriInterface for the given value.
|
|
||||||
|
|
||||||
This function accepts a string or UriInterface and returns a
|
|
||||||
UriInterface for the given value. If the value is already a
|
|
||||||
UriInterface, it is returned as-is.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\MimeType::fromFilename`
|
|
||||||
|
|
||||||
`public static function fromFilename(string $filename): string|null`
|
|
||||||
|
|
||||||
Determines the mimetype of a file by looking at its extension.
|
|
||||||
|
|
||||||
|
|
||||||
## `GuzzleHttp\Psr7\MimeType::fromExtension`
|
|
||||||
|
|
||||||
`public static function fromExtension(string $extension): string|null`
|
|
||||||
|
|
||||||
Maps a file extensions to a mimetype.
|
|
||||||
|
|
||||||
|
|
||||||
# Additional URI Methods
|
|
||||||
|
|
||||||
Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
|
|
||||||
this library also provides additional functionality when working with URIs as static methods.
|
|
||||||
|
|
||||||
## URI Types
|
|
||||||
|
|
||||||
An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
|
|
||||||
An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
|
|
||||||
the base URI. Relative references can be divided into several forms according to
|
|
||||||
[RFC 3986 Section 4.2](https://datatracker.ietf.org/doc/html/rfc3986#section-4.2):
|
|
||||||
|
|
||||||
- network-path references, e.g. `//example.com/path`
|
|
||||||
- absolute-path references, e.g. `/path`
|
|
||||||
- relative-path references, e.g. `subpath`
|
|
||||||
|
|
||||||
The following methods can be used to identify the type of the URI.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::isAbsolute`
|
|
||||||
|
|
||||||
`public static function isAbsolute(UriInterface $uri): bool`
|
|
||||||
|
|
||||||
Whether the URI is absolute, i.e. it has a scheme.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
|
|
||||||
|
|
||||||
`public static function isNetworkPathReference(UriInterface $uri): bool`
|
|
||||||
|
|
||||||
Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
|
|
||||||
termed an network-path reference.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
|
|
||||||
|
|
||||||
`public static function isAbsolutePathReference(UriInterface $uri): bool`
|
|
||||||
|
|
||||||
Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
|
|
||||||
termed an absolute-path reference.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
|
|
||||||
|
|
||||||
`public static function isRelativePathReference(UriInterface $uri): bool`
|
|
||||||
|
|
||||||
Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
|
|
||||||
termed a relative-path reference.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
|
|
||||||
|
|
||||||
`public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool`
|
|
||||||
|
|
||||||
Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
|
|
||||||
fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
|
|
||||||
(apart from its fragment) is considered a same-document reference.
|
|
||||||
|
|
||||||
## URI Components
|
|
||||||
|
|
||||||
Additional methods to work with URI components.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::isDefaultPort`
|
|
||||||
|
|
||||||
`public static function isDefaultPort(UriInterface $uri): bool`
|
|
||||||
|
|
||||||
Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
|
|
||||||
or the standard port. This method can be used independently of the implementation.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::composeComponents`
|
|
||||||
|
|
||||||
`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
|
|
||||||
|
|
||||||
Composes a URI reference string from its various components according to
|
|
||||||
[RFC 3986 Section 5.3](https://datatracker.ietf.org/doc/html/rfc3986#section-5.3). Usually this method does not need
|
|
||||||
to be called manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::fromParts`
|
|
||||||
|
|
||||||
`public static function fromParts(array $parts): UriInterface`
|
|
||||||
|
|
||||||
Creates a URI from a hash of [`parse_url`](https://www.php.net/manual/en/function.parse-url.php) components.
|
|
||||||
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::withQueryValue`
|
|
||||||
|
|
||||||
`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
|
|
||||||
|
|
||||||
Creates a new URI with a specific query string value. Any existing query string values that exactly match the
|
|
||||||
provided key are removed and replaced with the given key value pair. A value of null will set the query string
|
|
||||||
key without a value, e.g. "key" instead of "key=value".
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::withQueryValues`
|
|
||||||
|
|
||||||
`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface`
|
|
||||||
|
|
||||||
Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an
|
|
||||||
associative array of key => value.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
|
|
||||||
|
|
||||||
`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
|
|
||||||
|
|
||||||
Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
|
|
||||||
provided key are removed.
|
|
||||||
|
|
||||||
## Cross-Origin Detection
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\UriComparator` provides methods to determine if a modified URL should be considered cross-origin.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\UriComparator::isCrossOrigin`
|
|
||||||
|
|
||||||
`public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool`
|
|
||||||
|
|
||||||
Determines if a modified URL should be considered cross-origin with respect to an original URL.
|
|
||||||
|
|
||||||
Two URLs are cross-origin when their scheme, host, or effective port differ. Host comparison is case-insensitive, and missing ports use the default port for `http` or `https`. Other schemes do not receive implicit default ports.
|
|
||||||
|
|
||||||
This helper only compares URI origins. It does not implement redirect handling or credential policy.
|
|
||||||
|
|
||||||
## Reference Resolution
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
|
|
||||||
to [RFC 3986 Section 5](https://datatracker.ietf.org/doc/html/rfc3986#section-5). This is for example also what web
|
|
||||||
browsers do when resolving a link in a website based on the current request URI.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\UriResolver::resolve`
|
|
||||||
|
|
||||||
`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
|
|
||||||
|
|
||||||
Converts the relative URI into a new URI that is resolved against the base URI.
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
|
|
||||||
|
|
||||||
`public static function removeDotSegments(string $path): string`
|
|
||||||
|
|
||||||
Removes dot segments from a path and returns the new path according to
|
|
||||||
[RFC 3986 Section 5.2.4](https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4).
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\UriResolver::relativize`
|
|
||||||
|
|
||||||
`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
|
|
||||||
|
|
||||||
Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
|
|
||||||
|
|
||||||
```php
|
|
||||||
(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
|
|
||||||
```
|
|
||||||
|
|
||||||
One use-case is to use the current request URI as base URI and then generate relative links in your documents
|
|
||||||
to reduce the document size or offer self-contained downloadable document archives.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$base = new Uri('http://example.com/a/b/');
|
|
||||||
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
|
|
||||||
echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
|
|
||||||
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
|
|
||||||
echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Normalization and Comparison
|
|
||||||
|
|
||||||
`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
|
|
||||||
[RFC 3986 Section 6](https://datatracker.ietf.org/doc/html/rfc3986#section-6).
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\UriNormalizer::normalize`
|
|
||||||
|
|
||||||
`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
|
|
||||||
|
|
||||||
Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
|
|
||||||
This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
|
|
||||||
of normalizations to apply. The following normalizations are available:
|
|
||||||
|
|
||||||
- `UriNormalizer::PRESERVING_NORMALIZATIONS`
|
|
||||||
|
|
||||||
Default normalizations which only include the ones that preserve semantics.
|
|
||||||
|
|
||||||
- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
|
|
||||||
|
|
||||||
All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
|
|
||||||
|
|
||||||
Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
|
|
||||||
|
|
||||||
- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
|
|
||||||
|
|
||||||
Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
|
|
||||||
ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
|
|
||||||
not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
|
|
||||||
characters by URI normalizers.
|
|
||||||
|
|
||||||
Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
|
|
||||||
|
|
||||||
- `UriNormalizer::CONVERT_EMPTY_PATH`
|
|
||||||
|
|
||||||
Converts the empty path to "/" for http and https URIs.
|
|
||||||
|
|
||||||
Example: `http://example.org` → `http://example.org/`
|
|
||||||
|
|
||||||
- `UriNormalizer::REMOVE_DEFAULT_HOST`
|
|
||||||
|
|
||||||
Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
|
|
||||||
"localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
|
|
||||||
RFC 3986.
|
|
||||||
|
|
||||||
Example: `file://localhost/myfile` → `file:///myfile`
|
|
||||||
|
|
||||||
- `UriNormalizer::REMOVE_DEFAULT_PORT`
|
|
||||||
|
|
||||||
Removes the default port of the given URI scheme from the URI.
|
|
||||||
|
|
||||||
Example: `http://example.org:80/` → `http://example.org/`
|
|
||||||
|
|
||||||
- `UriNormalizer::REMOVE_DOT_SEGMENTS`
|
|
||||||
|
|
||||||
Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
|
|
||||||
change the semantics of the URI reference.
|
|
||||||
|
|
||||||
Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
|
|
||||||
|
|
||||||
- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
|
|
||||||
|
|
||||||
Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
|
|
||||||
and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
|
|
||||||
may change the semantics. Encoded slashes (%2F) are not removed.
|
|
||||||
|
|
||||||
Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
|
|
||||||
|
|
||||||
- `UriNormalizer::SORT_QUERY_PARAMETERS`
|
|
||||||
|
|
||||||
Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
|
|
||||||
significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
|
|
||||||
of the URI.
|
|
||||||
|
|
||||||
Example: `?lang=en&article=fred` → `?article=fred&lang=en`
|
|
||||||
|
|
||||||
### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
|
|
||||||
|
|
||||||
`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
|
|
||||||
|
|
||||||
Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
|
|
||||||
`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
|
|
||||||
This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
|
|
||||||
equivalence or difference of relative references does not mean anything.
|
|
||||||
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information.
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
|
|
||||||
|
|
||||||
|
|
||||||
## For Enterprise
|
|
||||||
|
|
||||||
Available as part of the Tidelift Subscription
|
|
||||||
|
|
||||||
The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-psr7?utm_source=packagist-guzzlehttp-psr7&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
|
||||||
198
vendor/guzzlehttp/psr7/UPGRADING.md
vendored
198
vendor/guzzlehttp/psr7/UPGRADING.md
vendored
|
|
@ -1,198 +0,0 @@
|
||||||
Guzzle PSR-7 Upgrade Guide
|
|
||||||
==========================
|
|
||||||
|
|
||||||
1.x to 2.0
|
|
||||||
----------
|
|
||||||
|
|
||||||
Guzzle PSR-7 2.0 is a major release that removes deprecated APIs, raises the
|
|
||||||
minimum PHP version, and adds PHP 7 parameter and return types. Applications that
|
|
||||||
only depend on PSR-7 interfaces should usually need small changes. Applications
|
|
||||||
that call helper functions, extend package classes, or pass invalid argument
|
|
||||||
types need closer review.
|
|
||||||
|
|
||||||
#### PHP Version and Dependencies
|
|
||||||
|
|
||||||
Guzzle PSR-7 2.0 requires PHP `^7.2.5 || ^8.0`. Guzzle PSR-7 1.x supported PHP
|
|
||||||
`>=5.4.0`.
|
|
||||||
|
|
||||||
Composer dependency changes that can affect upgrades:
|
|
||||||
|
|
||||||
- `ralouphie/getallheaders` v2 support was dropped; 2.0 requires `^3.0`.
|
|
||||||
- `psr/http-factory:^1.0` is required because 2.0 ships PSR-17 factories through `GuzzleHttp\Psr7\HttpFactory`.
|
|
||||||
|
|
||||||
#### PHP 7 Type Hints and Return Types
|
|
||||||
|
|
||||||
Type hints and return types were added wherever possible. Please make sure:
|
|
||||||
|
|
||||||
- You pass values of the documented type when calling methods and functions.
|
|
||||||
- Classes that extend Guzzle PSR-7 classes update any overridden method signatures to remain compatible.
|
|
||||||
- Code that expected package-specific `InvalidArgumentException` exceptions for invalid argument types may now receive PHP `TypeError` exceptions instead.
|
|
||||||
|
|
||||||
Common examples include passing a real integer status code to `Response::__construct()` and passing a string method to `Request::__construct()`.
|
|
||||||
|
|
||||||
#### Removed Function API
|
|
||||||
|
|
||||||
The static API was introduced in 1.7.0 to mitigate problems with functions
|
|
||||||
conflicting between global and local copies of the package. The function API was
|
|
||||||
removed in 2.0.0, along with the Composer `files` autoload entry that loaded
|
|
||||||
`src/functions_include.php`.
|
|
||||||
|
|
||||||
Replace namespaced function calls with the corresponding static methods in the
|
|
||||||
`GuzzleHttp\Psr7` namespace:
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Before:
|
|
||||||
use function GuzzleHttp\Psr7\stream_for;
|
|
||||||
|
|
||||||
$stream = stream_for('body');
|
|
||||||
|
|
||||||
// After:
|
|
||||||
use GuzzleHttp\Psr7\Utils;
|
|
||||||
|
|
||||||
$stream = Utils::streamFor('body');
|
|
||||||
```
|
|
||||||
|
|
||||||
| Original Function | Replacement Method |
|
|
||||||
|-------------------|--------------------|
|
|
||||||
| `str` | `Message::toString` |
|
|
||||||
| `uri_for` | `Utils::uriFor` |
|
|
||||||
| `stream_for` | `Utils::streamFor` |
|
|
||||||
| `parse_header` | `Header::parse` |
|
|
||||||
| `normalize_header` | `Header::normalize` |
|
|
||||||
| `modify_request` | `Utils::modifyRequest` |
|
|
||||||
| `rewind_body` | `Message::rewindBody` |
|
|
||||||
| `try_fopen` | `Utils::tryFopen` |
|
|
||||||
| `copy_to_string` | `Utils::copyToString` |
|
|
||||||
| `copy_to_stream` | `Utils::copyToStream` |
|
|
||||||
| `hash` | `Utils::hash` |
|
|
||||||
| `readline` | `Utils::readLine` |
|
|
||||||
| `parse_request` | `Message::parseRequest` |
|
|
||||||
| `parse_response` | `Message::parseResponse` |
|
|
||||||
| `parse_query` | `Query::parse` |
|
|
||||||
| `build_query` | `Query::build` |
|
|
||||||
| `mimetype_from_filename` | `MimeType::fromFilename` |
|
|
||||||
| `mimetype_from_extension` | `MimeType::fromExtension` |
|
|
||||||
| `_parse_message` | `Message::parseMessage` |
|
|
||||||
| `_parse_request_uri` | `Message::parseRequestUri` |
|
|
||||||
| `get_message_body_summary` | `Message::bodySummary` |
|
|
||||||
| `_caseless_remove` | `Utils::caselessRemove` |
|
|
||||||
|
|
||||||
`Header::normalize()` remains the direct 2.0 replacement for
|
|
||||||
`normalize_header()`. In newer 2.x versions, prefer `Header::splitList()` for
|
|
||||||
new code.
|
|
||||||
|
|
||||||
#### Deprecated URI Methods Removed
|
|
||||||
|
|
||||||
The deprecated `Uri::resolve()` and `Uri::removeDotSegments()` methods were
|
|
||||||
removed. Use `UriResolver` instead.
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Before:
|
|
||||||
$resolved = Uri::resolve($base, '../path');
|
|
||||||
$path = Uri::removeDotSegments('/a/../b');
|
|
||||||
|
|
||||||
// After:
|
|
||||||
use GuzzleHttp\Psr7\UriResolver;
|
|
||||||
use GuzzleHttp\Psr7\Utils;
|
|
||||||
|
|
||||||
$resolved = UriResolver::resolve($base, Utils::uriFor('../path'));
|
|
||||||
$path = UriResolver::removeDotSegments('/a/../b');
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Stricter URI Validation
|
|
||||||
|
|
||||||
Guzzle PSR-7 1.x automatically fixed a URI that combined an authority with a
|
|
||||||
relative path by prepending `/` to the path. That deprecated behavior was removed
|
|
||||||
in 2.0. Such URIs now throw `InvalidArgumentException`.
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Before: automatically converted to //example.com/foo.
|
|
||||||
$uri = (new Uri())->withHost('example.com')->withPath('foo');
|
|
||||||
|
|
||||||
// After: make the absolute path explicit.
|
|
||||||
$uri = (new Uri())->withHost('example.com')->withPath('/foo');
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Header Validation
|
|
||||||
|
|
||||||
Header names are validated more strictly according to RFC 7230 token syntax.
|
|
||||||
Names containing whitespace, `/`, `(`, `)`, `\\`, or other invalid characters are
|
|
||||||
rejected.
|
|
||||||
|
|
||||||
If you construct messages from untrusted or non-standard input, normalize or
|
|
||||||
reject invalid header names before constructing `Request`, `Response`, or
|
|
||||||
`ServerRequest` instances.
|
|
||||||
|
|
||||||
#### Query String Boolean Serialization
|
|
||||||
|
|
||||||
`Query::build()` now serializes booleans as `1` and `0`, matching
|
|
||||||
`http_build_query()` behavior.
|
|
||||||
|
|
||||||
```php
|
|
||||||
Query::build(['enabled' => true, 'disabled' => false]);
|
|
||||||
// enabled=1&disabled=0
|
|
||||||
```
|
|
||||||
|
|
||||||
In current 2.x versions, pass `false` as the third argument if you need textual
|
|
||||||
boolean values:
|
|
||||||
|
|
||||||
```php
|
|
||||||
Query::build(['enabled' => true, 'disabled' => false], PHP_QUERY_RFC3986, false);
|
|
||||||
// enabled=true&disabled=false
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Final Stream and Decorator Classes
|
|
||||||
|
|
||||||
Several classes that were annotated with `@final` in 1.x are declared `final` in
|
|
||||||
2.0:
|
|
||||||
|
|
||||||
- `AppendStream`
|
|
||||||
- `BufferStream`
|
|
||||||
- `CachingStream`
|
|
||||||
- `DroppingStream`
|
|
||||||
- `FnStream`
|
|
||||||
- `InflateStream`
|
|
||||||
- `LazyOpenStream`
|
|
||||||
- `LimitStream`
|
|
||||||
- `MultipartStream`
|
|
||||||
- `NoSeekStream`
|
|
||||||
- `PumpStream`
|
|
||||||
- `StreamWrapper`
|
|
||||||
|
|
||||||
If your code extends one of these classes, replace inheritance with composition.
|
|
||||||
For custom streams, implement `Psr\Http\Message\StreamInterface` directly or use
|
|
||||||
`GuzzleHttp\Psr7\StreamDecoratorTrait` in your own class.
|
|
||||||
|
|
||||||
`Request`, `Response`, `ServerRequest`, `Stream`, `UploadedFile`, and `Uri` remain
|
|
||||||
extendable in 2.0, but overridden methods must have compatible signatures.
|
|
||||||
|
|
||||||
#### Public Constants and Internal Details
|
|
||||||
|
|
||||||
Some constants that were public in 1.x are implementation details in 2.0:
|
|
||||||
|
|
||||||
- `Stream::READABLE_MODES`
|
|
||||||
- `Stream::WRITABLE_MODES`
|
|
||||||
- `Uri::HTTP_DEFAULT_HOST`
|
|
||||||
|
|
||||||
If your code used these constants, define application-specific constants instead
|
|
||||||
of depending on package internals.
|
|
||||||
|
|
||||||
#### Stream Behavior Changes
|
|
||||||
|
|
||||||
`BufferStream::write()` returns `0` instead of `false` when the buffer exceeds
|
|
||||||
its high-water mark. This keeps the method compatible with the `int` return type
|
|
||||||
from `StreamInterface::write()`.
|
|
||||||
|
|
||||||
Several stream `__toString()` implementations now catch `Throwable`. On PHP 7.4
|
|
||||||
and newer, exceptions thrown during stringification are rethrown. Avoid relying
|
|
||||||
on `(string) $stream` to hide read failures; call `getContents()` or `read()` and
|
|
||||||
handle exceptions when failures are possible.
|
|
||||||
|
|
||||||
#### PSR-17 Factories
|
|
||||||
|
|
||||||
Guzzle PSR-7 2.0 adds `GuzzleHttp\Psr7\HttpFactory`, an implementation of the
|
|
||||||
PSR-17 factory interfaces from `psr/http-factory`. This is additive, but it is
|
|
||||||
the reason for the new required dependency.
|
|
||||||
|
|
||||||
For the full 2.0 diff, see
|
|
||||||
https://github.com/guzzle/psr7/compare/1.8.1...2.0.0.
|
|
||||||
6
vendor/guzzlehttp/psr7/src/Header.php
vendored
6
vendor/guzzlehttp/psr7/src/Header.php
vendored
|
|
@ -95,6 +95,8 @@ final class Header
|
||||||
*/
|
*/
|
||||||
public static function normalize($header): array
|
public static function normalize($header): array
|
||||||
{
|
{
|
||||||
|
\trigger_deprecation('guzzlehttp/psr7', '2.3', 'Header::normalize() is deprecated and will be removed in guzzlehttp/psr7 3.0. Use Header::splitList() instead.');
|
||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ((array) $header as $value) {
|
foreach ((array) $header as $value) {
|
||||||
foreach (self::splitList($value) as $parsed) {
|
foreach (self::splitList($value) as $parsed) {
|
||||||
|
|
@ -142,7 +144,7 @@ final class Header
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$isQuoted && $value[$i] === ',') {
|
if (!$isQuoted && $value[$i] === ',') {
|
||||||
$v = \trim($v);
|
$v = \trim($v, " \n\r\t\0\x0B");
|
||||||
if ($v !== '') {
|
if ($v !== '') {
|
||||||
$result[] = $v;
|
$result[] = $v;
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +169,7 @@ final class Header
|
||||||
$v .= $value[$i];
|
$v .= $value[$i];
|
||||||
}
|
}
|
||||||
|
|
||||||
$v = \trim($v);
|
$v = \trim($v, " \n\r\t\0\x0B");
|
||||||
if ($v !== '') {
|
if ($v !== '') {
|
||||||
$result[] = $v;
|
$result[] = $v;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
79
vendor/guzzlehttp/psr7/src/Message.php
vendored
79
vendor/guzzlehttp/psr7/src/Message.php
vendored
|
|
@ -19,7 +19,7 @@ final class Message
|
||||||
{
|
{
|
||||||
if ($message instanceof RequestInterface) {
|
if ($message instanceof RequestInterface) {
|
||||||
$msg = trim($message->getMethod().' '
|
$msg = trim($message->getMethod().' '
|
||||||
.$message->getRequestTarget())
|
.$message->getRequestTarget(), " \n\r\t\0\x0B")
|
||||||
.' HTTP/'.$message->getProtocolVersion();
|
.' HTTP/'.$message->getProtocolVersion();
|
||||||
if (!$message->hasHeader('host')) {
|
if (!$message->hasHeader('host')) {
|
||||||
$msg .= "\r\nHost: ".$message->getUri()->getHost();
|
$msg .= "\r\nHost: ".$message->getUri()->getHost();
|
||||||
|
|
@ -33,7 +33,7 @@ final class Message
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($message->getHeaders() as $name => $values) {
|
foreach ($message->getHeaders() as $name => $values) {
|
||||||
if (is_string($name) && strtolower($name) === 'set-cookie') {
|
if (is_string($name) && Utils::asciiToLower($name) === 'set-cookie') {
|
||||||
foreach ($values as $value) {
|
foreach ($values as $value) {
|
||||||
$msg .= "\r\n{$name}: ".$value;
|
$msg .= "\r\n{$name}: ".$value;
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +181,11 @@ final class Message
|
||||||
|
|
||||||
$messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
|
$messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
|
||||||
|
|
||||||
if ($messageParts === false || count($messageParts) !== 2) {
|
if ($messageParts === false) {
|
||||||
|
throw new \RuntimeException('Unable to split HTTP message: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($messageParts) !== 2) {
|
||||||
throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
|
throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,24 +193,48 @@ final class Message
|
||||||
$rawHeaders .= "\r\n"; // Put back the delimiter we split previously
|
$rawHeaders .= "\r\n"; // Put back the delimiter we split previously
|
||||||
$headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
|
$headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
|
||||||
|
|
||||||
if ($headerParts === false || count($headerParts) !== 2) {
|
if ($headerParts === false) {
|
||||||
|
throw new \RuntimeException('Unable to split HTTP message headers: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($headerParts) !== 2) {
|
||||||
throw new \InvalidArgumentException('Invalid message: Missing status line');
|
throw new \InvalidArgumentException('Invalid message: Missing status line');
|
||||||
}
|
}
|
||||||
|
|
||||||
[$startLine, $rawHeaders] = $headerParts;
|
[$startLine, $rawHeaders] = $headerParts;
|
||||||
|
|
||||||
if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
|
$versionMatch = preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches);
|
||||||
|
|
||||||
|
if ($versionMatch === false) {
|
||||||
|
throw new \RuntimeException('Unable to parse HTTP start line: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($versionMatch === 1 && $matches[1] === '1.0') {
|
||||||
// Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
|
// Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
|
||||||
$rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
|
$rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
|
||||||
|
|
||||||
|
if ($rawHeaders === null) {
|
||||||
|
throw new \RuntimeException('Unable to unfold HTTP headers: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var array[] $headerLines */
|
/** @var array[] $headerLines */
|
||||||
$count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
|
$count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
|
||||||
|
|
||||||
|
if ($count === false) {
|
||||||
|
throw new \RuntimeException('Unable to parse HTTP headers: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
// If these aren't the same, then one line didn't match and there's an invalid header.
|
// If these aren't the same, then one line didn't match and there's an invalid header.
|
||||||
if ($count !== substr_count($rawHeaders, "\n")) {
|
if ($count !== substr_count($rawHeaders, "\n")) {
|
||||||
// Folding is deprecated, see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
|
// Folding is deprecated, see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
|
||||||
if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
|
$hasFoldedHeader = preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders);
|
||||||
|
|
||||||
|
if ($hasFoldedHeader === false) {
|
||||||
|
throw new \RuntimeException('Unable to inspect HTTP header folding: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasFoldedHeader === 1) {
|
||||||
throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
|
throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,8 +265,10 @@ final class Message
|
||||||
$host = self::getHostFromHeaders($headers);
|
$host = self::getHostFromHeaders($headers);
|
||||||
|
|
||||||
// If no host is found, then a full URI cannot be constructed.
|
// If no host is found, then a full URI cannot be constructed.
|
||||||
|
// Collapse leading slashes so an origin-form target cannot be
|
||||||
|
// parsed as a network-path reference with its own authority.
|
||||||
if ($host === null) {
|
if ($host === null) {
|
||||||
return $path;
|
return self::normalizePathForOriginForm($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
$scheme = substr($host, -4) === ':443' ? 'https' : 'http';
|
$scheme = substr($host, -4) === ':443' ? 'https' : 'http';
|
||||||
|
|
@ -246,6 +276,15 @@ final class Message
|
||||||
return $scheme.'://'.$host.'/'.ltrim($path, '/');
|
return $scheme.'://'.$host.'/'.ltrim($path, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function normalizePathForOriginForm(string $path): string
|
||||||
|
{
|
||||||
|
if (0 === strpos($path, '//')) {
|
||||||
|
return '/'.ltrim($path, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $headers Array of headers (each value an array).
|
* @param array $headers Array of headers (each value an array).
|
||||||
*/
|
*/
|
||||||
|
|
@ -255,7 +294,7 @@ final class Message
|
||||||
// Numeric array keys are converted to int by PHP.
|
// Numeric array keys are converted to int by PHP.
|
||||||
$k = (string) $k;
|
$k = (string) $k;
|
||||||
|
|
||||||
return strtolower($k) === 'host';
|
return Utils::asciiToLower($k) === 'host';
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!$hostKey) {
|
if (!$hostKey) {
|
||||||
|
|
@ -278,8 +317,18 @@ final class Message
|
||||||
public static function parseRequest(string $message): RequestInterface
|
public static function parseRequest(string $message): RequestInterface
|
||||||
{
|
{
|
||||||
$data = self::parseMessage($message);
|
$data = self::parseMessage($message);
|
||||||
|
if (strpbrk($data['start-line'], "\r\n") !== false) {
|
||||||
|
throw new \InvalidArgumentException('Invalid request string');
|
||||||
|
}
|
||||||
|
|
||||||
$matches = [];
|
$matches = [];
|
||||||
if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
|
$requestStartLineMatch = preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches);
|
||||||
|
|
||||||
|
if ($requestStartLineMatch === false) {
|
||||||
|
throw new \RuntimeException('Unable to parse request start line: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($requestStartLineMatch === 0) {
|
||||||
throw new \InvalidArgumentException('Invalid request string');
|
throw new \InvalidArgumentException('Invalid request string');
|
||||||
}
|
}
|
||||||
$parts = explode(' ', $data['start-line'], 3);
|
$parts = explode(' ', $data['start-line'], 3);
|
||||||
|
|
@ -304,10 +353,20 @@ final class Message
|
||||||
public static function parseResponse(string $message): ResponseInterface
|
public static function parseResponse(string $message): ResponseInterface
|
||||||
{
|
{
|
||||||
$data = self::parseMessage($message);
|
$data = self::parseMessage($message);
|
||||||
|
if (strpbrk($data['start-line'], "\r\n") !== false) {
|
||||||
|
throw new \InvalidArgumentException('Invalid response string');
|
||||||
|
}
|
||||||
|
|
||||||
// According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
|
// According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
|
||||||
// the space between status-code and reason-phrase is required. But
|
// the space between status-code and reason-phrase is required. But
|
||||||
// browsers accept responses without space and reason as well.
|
// browsers accept responses without space and reason as well.
|
||||||
if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
|
$responseStartLineMatch = preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/D', $data['start-line']);
|
||||||
|
|
||||||
|
if ($responseStartLineMatch === false) {
|
||||||
|
throw new \RuntimeException('Unable to parse response start line: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($responseStartLineMatch === 0) {
|
||||||
throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']);
|
throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']);
|
||||||
}
|
}
|
||||||
$parts = explode(' ', $data['start-line'], 3);
|
$parts = explode(' ', $data['start-line'], 3);
|
||||||
|
|
|
||||||
37
vendor/guzzlehttp/psr7/src/MessageTrait.php
vendored
37
vendor/guzzlehttp/psr7/src/MessageTrait.php
vendored
|
|
@ -43,6 +43,8 @@ trait MessageTrait
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->assertProtocolVersion($version);
|
||||||
|
|
||||||
if ($this->protocol === $version) {
|
if ($this->protocol === $version) {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
@ -60,12 +62,12 @@ trait MessageTrait
|
||||||
|
|
||||||
public function hasHeader($header): bool
|
public function hasHeader($header): bool
|
||||||
{
|
{
|
||||||
return isset($this->headerNames[strtolower($header)]);
|
return isset($this->headerNames[Utils::asciiToLower($header)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHeader($header): array
|
public function getHeader($header): array
|
||||||
{
|
{
|
||||||
$header = strtolower($header);
|
$header = Utils::asciiToLower($header);
|
||||||
|
|
||||||
if (!isset($this->headerNames[$header])) {
|
if (!isset($this->headerNames[$header])) {
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -101,7 +103,7 @@ trait MessageTrait
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$value = $this->normalizeHeaderValue($value);
|
$value = $this->normalizeHeaderValue($value);
|
||||||
$normalized = strtolower($header);
|
$normalized = Utils::asciiToLower($header);
|
||||||
|
|
||||||
$new = clone $this;
|
$new = clone $this;
|
||||||
if (isset($new->headerNames[$normalized])) {
|
if (isset($new->headerNames[$normalized])) {
|
||||||
|
|
@ -133,7 +135,7 @@ trait MessageTrait
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$value = $this->normalizeHeaderValue($value);
|
$value = $this->normalizeHeaderValue($value);
|
||||||
$normalized = strtolower($header);
|
$normalized = Utils::asciiToLower($header);
|
||||||
|
|
||||||
$new = clone $this;
|
$new = clone $this;
|
||||||
if (isset($new->headerNames[$normalized])) {
|
if (isset($new->headerNames[$normalized])) {
|
||||||
|
|
@ -152,7 +154,7 @@ trait MessageTrait
|
||||||
*/
|
*/
|
||||||
public function withoutHeader($header): MessageInterface
|
public function withoutHeader($header): MessageInterface
|
||||||
{
|
{
|
||||||
$normalized = strtolower($header);
|
$normalized = Utils::asciiToLower($header);
|
||||||
|
|
||||||
if (!isset($this->headerNames[$normalized])) {
|
if (!isset($this->headerNames[$normalized])) {
|
||||||
return $this;
|
return $this;
|
||||||
|
|
@ -216,7 +218,7 @@ trait MessageTrait
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$value = $this->normalizeHeaderValue($value);
|
$value = $this->normalizeHeaderValue($value);
|
||||||
$normalized = strtolower($header);
|
$normalized = Utils::asciiToLower($header);
|
||||||
if (isset($this->headerNames[$normalized])) {
|
if (isset($this->headerNames[$normalized])) {
|
||||||
$header = $this->headerNames[$normalized];
|
$header = $this->headerNames[$normalized];
|
||||||
$this->headers[$header] = array_merge($this->headers[$header], $value);
|
$this->headers[$header] = array_merge($this->headers[$header], $value);
|
||||||
|
|
@ -273,6 +275,12 @@ trait MessageTrait
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert non-finite floats explicitly, as implicit coercion of
|
||||||
|
// NAN emits a warning on PHP 8.5.
|
||||||
|
if (is_float($value) && !is_finite($value)) {
|
||||||
|
$value = is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
|
|
||||||
$trimmed = trim((string) $value, " \t");
|
$trimmed = trim((string) $value, " \t");
|
||||||
$this->assertValue($trimmed);
|
$this->assertValue($trimmed);
|
||||||
|
|
||||||
|
|
@ -301,6 +309,23 @@ trait MessageTrait
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $version
|
||||||
|
*/
|
||||||
|
private function assertProtocolVersion($version): void
|
||||||
|
{
|
||||||
|
if (is_string($version)) {
|
||||||
|
$this->assertNoLineSeparators($version, 'Protocol version');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertNoLineSeparators(string $value, string $field): void
|
||||||
|
{
|
||||||
|
if (strpbrk($value, "\r\n") !== false) {
|
||||||
|
throw new \InvalidArgumentException($field.' must not contain CR or LF characters.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
|
* @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
|
||||||
*
|
*
|
||||||
|
|
|
||||||
2
vendor/guzzlehttp/psr7/src/MimeType.php
vendored
2
vendor/guzzlehttp/psr7/src/MimeType.php
vendored
|
|
@ -1300,6 +1300,6 @@ final class MimeType
|
||||||
*/
|
*/
|
||||||
public static function fromExtension(string $extension): ?string
|
public static function fromExtension(string $extension): ?string
|
||||||
{
|
{
|
||||||
return self::MIME_TYPES[strtolower($extension)] ?? null;
|
return self::MIME_TYPES[Utils::asciiToLower($extension)] ?? null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
33
vendor/guzzlehttp/psr7/src/MultipartStream.php
vendored
33
vendor/guzzlehttp/psr7/src/MultipartStream.php
vendored
|
|
@ -26,8 +26,9 @@ final class MultipartStream implements StreamInterface
|
||||||
* @param array $elements Array of associative arrays, each containing a
|
* @param array $elements Array of associative arrays, each containing a
|
||||||
* required "name" key mapping to the form field,
|
* required "name" key mapping to the form field,
|
||||||
* name, a required "contents" key mapping to any
|
* name, a required "contents" key mapping to any
|
||||||
* non-array value accepted by Utils::streamFor(),
|
* non-array value accepted by Utils::streamFor()
|
||||||
* or an array for nested expansion.
|
* (non-string scalar field values are cast to
|
||||||
|
* string), or an array for nested expansion.
|
||||||
* Optional keys include "headers" (associative
|
* Optional keys include "headers" (associative
|
||||||
* array of custom headers) and "filename" (string
|
* array of custom headers) and "filename" (string
|
||||||
* to send as the filename in the part).
|
* to send as the filename in the part).
|
||||||
|
|
@ -77,7 +78,7 @@ final class MultipartStream implements StreamInterface
|
||||||
$str .= "{$key}: {$value}\r\n";
|
$str .= "{$key}: {$value}\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "--{$this->boundary}\r\n".trim($str)."\r\n\r\n";
|
return "--{$this->boundary}\r\n".trim($str, " \n\r\t\0\x0B")."\r\n\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -124,7 +125,27 @@ final class MultipartStream implements StreamInterface
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$element['contents'] = Utils::streamFor($element['contents']);
|
$contents = $element['contents'];
|
||||||
|
if (is_scalar($contents) && !is_string($contents)) {
|
||||||
|
// Multipart field values are byte strings on the wire, so finite
|
||||||
|
// numeric and boolean field values are cast to string here rather
|
||||||
|
// than tripping streamFor()'s non-string-scalar deprecation. Non-finite
|
||||||
|
// floats are deprecated and normalized here too, so the deprecation is
|
||||||
|
// reported against MultipartStream instead of transitively through
|
||||||
|
// streamFor().
|
||||||
|
if (is_float($contents) && !is_finite($contents)) {
|
||||||
|
\trigger_deprecation(
|
||||||
|
'guzzlehttp/psr7',
|
||||||
|
'2.12',
|
||||||
|
'Passing a non-finite float as multipart contents is deprecated; guzzlehttp/psr7 3.0 rejects non-finite floats.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$contents = is_nan($contents) ? 'NAN' : ($contents > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
|
|
||||||
|
$contents = (string) $contents;
|
||||||
|
}
|
||||||
|
$element['contents'] = Utils::streamFor($contents);
|
||||||
|
|
||||||
if (empty($element['filename'])) {
|
if (empty($element['filename'])) {
|
||||||
$uri = $element['contents']->getMetadata('uri');
|
$uri = $element['contents']->getMetadata('uri');
|
||||||
|
|
@ -206,9 +227,9 @@ final class MultipartStream implements StreamInterface
|
||||||
*/
|
*/
|
||||||
private static function getHeader(array $headers, string $key): ?string
|
private static function getHeader(array $headers, string $key): ?string
|
||||||
{
|
{
|
||||||
$lowercaseHeader = strtolower($key);
|
$lowercaseHeader = Utils::asciiToLower($key);
|
||||||
foreach ($headers as $k => $v) {
|
foreach ($headers as $k => $v) {
|
||||||
if (strtolower((string) $k) === $lowercaseHeader) {
|
if (Utils::asciiToLower((string) $k) === $lowercaseHeader) {
|
||||||
return $v;
|
return $v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
vendor/guzzlehttp/psr7/src/Query.php
vendored
27
vendor/guzzlehttp/psr7/src/Query.php
vendored
|
|
@ -96,7 +96,7 @@ final class Query
|
||||||
$k = $encoder((string) $k);
|
$k = $encoder((string) $k);
|
||||||
if (!is_array($v)) {
|
if (!is_array($v)) {
|
||||||
$qs .= $k;
|
$qs .= $k;
|
||||||
$v = is_bool($v) ? $castBool($v) : $v;
|
$v = is_bool($v) ? $castBool($v) : self::normalizeNonFiniteFloat($v);
|
||||||
if ($v !== null) {
|
if ($v !== null) {
|
||||||
$qs .= '='.$encoder((string) $v);
|
$qs .= '='.$encoder((string) $v);
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +104,7 @@ final class Query
|
||||||
} else {
|
} else {
|
||||||
foreach ($v as $vv) {
|
foreach ($v as $vv) {
|
||||||
$qs .= $k;
|
$qs .= $k;
|
||||||
$vv = is_bool($vv) ? $castBool($vv) : $vv;
|
$vv = is_bool($vv) ? $castBool($vv) : self::normalizeNonFiniteFloat($vv);
|
||||||
if ($vv !== null) {
|
if ($vv !== null) {
|
||||||
$qs .= '='.$encoder((string) $vv);
|
$qs .= '='.$encoder((string) $vv);
|
||||||
}
|
}
|
||||||
|
|
@ -115,4 +115,27 @@ final class Query
|
||||||
|
|
||||||
return $qs ? (string) substr($qs, 0, -1) : '';
|
return $qs ? (string) substr($qs, 0, -1) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts non-finite floats to the strings PHP coerces them to, as
|
||||||
|
* implicit coercion of NAN emits a warning on PHP 8.5.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private static function normalizeNonFiniteFloat($value)
|
||||||
|
{
|
||||||
|
if (is_float($value) && !is_finite($value)) {
|
||||||
|
\trigger_deprecation(
|
||||||
|
'guzzlehttp/psr7',
|
||||||
|
'2.12',
|
||||||
|
'Passing a non-finite float to Query::build() is deprecated; guzzlehttp/psr7 3.0 rejects non-finite floats.'
|
||||||
|
);
|
||||||
|
|
||||||
|
return is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
vendor/guzzlehttp/psr7/src/Request.php
vendored
18
vendor/guzzlehttp/psr7/src/Request.php
vendored
|
|
@ -40,12 +40,14 @@ class Request implements RequestInterface
|
||||||
string $version = '1.1'
|
string $version = '1.1'
|
||||||
) {
|
) {
|
||||||
$this->assertMethod($method);
|
$this->assertMethod($method);
|
||||||
|
$this->assertProtocolVersion($version);
|
||||||
|
|
||||||
if (!$uri instanceof UriInterface) {
|
if (!$uri instanceof UriInterface) {
|
||||||
$uri = new Uri($uri);
|
$uri = new Uri($uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::warnOnMethodCasingChange($method);
|
self::warnOnMethodCasingChange($method);
|
||||||
$this->method = strtoupper($method);
|
$this->method = Utils::asciiToUpper($method);
|
||||||
$this->uri = $uri;
|
$this->uri = $uri;
|
||||||
$this->setHeaders($headers);
|
$this->setHeaders($headers);
|
||||||
$this->protocol = $version;
|
$this->protocol = $version;
|
||||||
|
|
@ -78,7 +80,13 @@ class Request implements RequestInterface
|
||||||
|
|
||||||
public function withRequestTarget($requestTarget): RequestInterface
|
public function withRequestTarget($requestTarget): RequestInterface
|
||||||
{
|
{
|
||||||
if (preg_match('#\s#', $requestTarget)) {
|
$hasWhitespace = preg_match('#\s#', $requestTarget);
|
||||||
|
|
||||||
|
if ($hasWhitespace === false) {
|
||||||
|
throw new \RuntimeException('Unable to validate request target: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasWhitespace === 1) {
|
||||||
throw new InvalidArgumentException(
|
throw new InvalidArgumentException(
|
||||||
'Invalid request target provided; cannot contain whitespace'
|
'Invalid request target provided; cannot contain whitespace'
|
||||||
);
|
);
|
||||||
|
|
@ -100,7 +108,7 @@ class Request implements RequestInterface
|
||||||
$this->assertMethod($method);
|
$this->assertMethod($method);
|
||||||
self::warnOnMethodCasingChange($method);
|
self::warnOnMethodCasingChange($method);
|
||||||
$new = clone $this;
|
$new = clone $this;
|
||||||
$new->method = strtoupper($method);
|
$new->method = Utils::asciiToUpper($method);
|
||||||
|
|
||||||
return $new;
|
return $new;
|
||||||
}
|
}
|
||||||
|
|
@ -170,11 +178,13 @@ class Request implements RequestInterface
|
||||||
if (!is_string($method) || $method === '') {
|
if (!is_string($method) || $method === '') {
|
||||||
throw new InvalidArgumentException('Method must be a non-empty string.');
|
throw new InvalidArgumentException('Method must be a non-empty string.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->assertNoLineSeparators($method, 'Method');
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function warnOnMethodCasingChange(string $method): void
|
private static function warnOnMethodCasingChange(string $method): void
|
||||||
{
|
{
|
||||||
if ($method !== strtoupper($method)) {
|
if ($method !== Utils::asciiToUpper($method)) {
|
||||||
\trigger_deprecation(
|
\trigger_deprecation(
|
||||||
'guzzlehttp/psr7',
|
'guzzlehttp/psr7',
|
||||||
'2.11',
|
'2.11',
|
||||||
|
|
|
||||||
12
vendor/guzzlehttp/psr7/src/Response.php
vendored
12
vendor/guzzlehttp/psr7/src/Response.php
vendored
|
|
@ -99,6 +99,7 @@ class Response implements ResponseInterface
|
||||||
?string $reason = null
|
?string $reason = null
|
||||||
) {
|
) {
|
||||||
$this->assertStatusCodeRange($status);
|
$this->assertStatusCodeRange($status);
|
||||||
|
$this->assertProtocolVersion($version);
|
||||||
|
|
||||||
$this->statusCode = $status;
|
$this->statusCode = $status;
|
||||||
|
|
||||||
|
|
@ -108,11 +109,14 @@ class Response implements ResponseInterface
|
||||||
|
|
||||||
$this->setHeaders($headers);
|
$this->setHeaders($headers);
|
||||||
if ($reason == '' && isset(self::PHRASES[$this->statusCode])) {
|
if ($reason == '' && isset(self::PHRASES[$this->statusCode])) {
|
||||||
$this->reasonPhrase = self::PHRASES[$this->statusCode];
|
$reasonPhrase = self::PHRASES[$this->statusCode];
|
||||||
} else {
|
} else {
|
||||||
$this->reasonPhrase = (string) $reason;
|
$reasonPhrase = (string) $reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->assertNoLineSeparators($reasonPhrase, 'Reason phrase');
|
||||||
|
$this->reasonPhrase = $reasonPhrase;
|
||||||
|
|
||||||
$this->protocol = $version;
|
$this->protocol = $version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,7 +159,9 @@ class Response implements ResponseInterface
|
||||||
if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) {
|
if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) {
|
||||||
$reasonPhrase = self::PHRASES[$new->statusCode];
|
$reasonPhrase = self::PHRASES[$new->statusCode];
|
||||||
}
|
}
|
||||||
$new->reasonPhrase = (string) $reasonPhrase;
|
$reasonPhrase = (string) $reasonPhrase;
|
||||||
|
$this->assertNoLineSeparators($reasonPhrase, 'Reason phrase');
|
||||||
|
$new->reasonPhrase = $reasonPhrase;
|
||||||
|
|
||||||
return $new;
|
return $new;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
8
vendor/guzzlehttp/psr7/src/Rfc7230.php
vendored
8
vendor/guzzlehttp/psr7/src/Rfc7230.php
vendored
|
|
@ -68,7 +68,13 @@ final class Rfc7230
|
||||||
|
|
||||||
private static function isValidHostHeaderHost(string $host): bool
|
private static function isValidHostHeaderHost(string $host): bool
|
||||||
{
|
{
|
||||||
if (preg_match('/[\x00-\x20\x7F\/\?#@\\\\]/', $host)) {
|
$invalidHost = preg_match('/[\x00-\x20\x7F\/\?#@\\\\]/', $host);
|
||||||
|
|
||||||
|
if ($invalidHost === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($invalidHost === 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
vendor/guzzlehttp/psr7/src/ServerRequest.php
vendored
6
vendor/guzzlehttp/psr7/src/ServerRequest.php
vendored
|
|
@ -165,7 +165,7 @@ class ServerRequest extends Request implements ServerRequestInterface
|
||||||
*/
|
*/
|
||||||
public static function fromGlobals(): ServerRequestInterface
|
public static function fromGlobals(): ServerRequestInterface
|
||||||
{
|
{
|
||||||
$method = strtoupper(self::getServerParam('REQUEST_METHOD') ?? 'GET');
|
$method = Utils::asciiToUpper(self::getServerParam('REQUEST_METHOD') ?? 'GET');
|
||||||
$headers = self::removeInvalidHostHeader(self::getAllHeaders());
|
$headers = self::removeInvalidHostHeader(self::getAllHeaders());
|
||||||
$uri = self::getUriFromGlobals();
|
$uri = self::getUriFromGlobals();
|
||||||
$body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
|
$body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
|
||||||
|
|
@ -220,7 +220,7 @@ class ServerRequest extends Request implements ServerRequestInterface
|
||||||
private static function removeInvalidHostHeader(array $headers): array
|
private static function removeInvalidHostHeader(array $headers): array
|
||||||
{
|
{
|
||||||
foreach ($headers as $name => $value) {
|
foreach ($headers as $name => $value) {
|
||||||
if (strtolower((string) $name) !== 'host') {
|
if (Utils::asciiToLower((string) $name) !== 'host') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,7 +269,7 @@ class ServerRequest extends Request implements ServerRequestInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
$serverPort = self::getServerParam('SERVER_PORT');
|
$serverPort = self::getServerParam('SERVER_PORT');
|
||||||
if (!$hasPort && $serverPort !== null && preg_match('/^[+-]?\d+$/', $serverPort) === 1) {
|
if (!$hasPort && $serverPort !== null && preg_match('/^[+-]?\d+$/D', $serverPort) === 1) {
|
||||||
$uri = $uri->withPort((int) $serverPort);
|
$uri = $uri->withPort((int) $serverPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
107
vendor/guzzlehttp/psr7/src/Uri.php
vendored
107
vendor/guzzlehttp/psr7/src/Uri.php
vendored
|
|
@ -99,12 +99,30 @@ class Uri implements UriInterface, \JsonSerializable
|
||||||
return self::parsePathNoSchemeReference($url);
|
return self::parsePathNoSchemeReference($url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preserve bracketed IPv6 literals before encoding, including dotted IPv4 tails.
|
// Preserve bracketed IPv6 literals before encoding, including dotted IPv4
|
||||||
|
// tails. DEL (\x7F) is excluded so a raw-DEL host falls through to the
|
||||||
|
// general path and is rejected rather than silently mutated by parse_url().
|
||||||
$prefix = '';
|
$prefix = '';
|
||||||
if (preg_match('%^([0-9A-Za-z+.-]+://\[[0-9:.a-fA-F]+\])(.*?)$%', $url, $matches)) {
|
$ipv6Prefix = preg_match('%\A([0-9A-Za-z+.-]+://\[[^\]\x00-\x20\x7F/?#@]+\])(.*)\z%s', $url, $matches);
|
||||||
|
|
||||||
|
if ($ipv6Prefix === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ipv6Prefix === 1) {
|
||||||
/** @var array{0:string, 1:string, 2:string} $matches */
|
/** @var array{0:string, 1:string, 2:string} $matches */
|
||||||
|
$suffix = $matches[2];
|
||||||
|
|
||||||
|
// After the bracketed host only an optional numeric port and/or a
|
||||||
|
// path, query, or fragment may follow. Anything else (for example
|
||||||
|
// `:80@evil` or `:80x`) would let parse_url() reinterpret a
|
||||||
|
// different host.
|
||||||
|
if (preg_match('%\A(?::[0-9]*)?(?:[/?#].*)?\z%s', $suffix) !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$prefix = $matches[1];
|
$prefix = $matches[1];
|
||||||
$url = $matches[2];
|
$url = $suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var string|null */
|
/** @var string|null */
|
||||||
|
|
@ -371,12 +389,38 @@ class Uri implements UriInterface, \JsonSerializable
|
||||||
$result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
|
$result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
|
||||||
|
|
||||||
foreach ($keyValueArray as $key => $value) {
|
foreach ($keyValueArray as $key => $value) {
|
||||||
$result[] = self::generateQueryString((string) $key, $value !== null ? (string) $value : null);
|
$result[] = self::generateQueryString((string) $key, $value !== null ? self::stringifyQueryValue($value) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $uri->withQuery(implode('&', $result));
|
return $uri->withQuery(implode('&', $result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stringifies a non-null query value, deprecating non-string values that
|
||||||
|
* guzzlehttp/psr7 3.0 will reject. Non-finite floats are normalized to the
|
||||||
|
* strings PHP coerces them to, as implicit coercion of NAN emits a warning
|
||||||
|
* on PHP 8.5.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
private static function stringifyQueryValue($value): string
|
||||||
|
{
|
||||||
|
if (!is_string($value)) {
|
||||||
|
\trigger_deprecation(
|
||||||
|
'guzzlehttp/psr7',
|
||||||
|
'2.12',
|
||||||
|
'Passing %s to Uri::withQueryValues() is deprecated; cast it to a string. guzzlehttp/psr7 3.0 will only accept string or null query values.',
|
||||||
|
\gettype($value)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (is_float($value) && !is_finite($value)) {
|
||||||
|
return is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a URI from a hash of `parse_url` components.
|
* Creates a URI from a hash of `parse_url` components.
|
||||||
*
|
*
|
||||||
|
|
@ -410,7 +454,27 @@ class Uri implements UriInterface, \JsonSerializable
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/[\x00-\x20\x7F]/', $host)) {
|
// Reject control characters and URI authority delimiters so getHost()
|
||||||
|
// cannot disagree with the on-wire authority.
|
||||||
|
$invalidHost = preg_match('/[\x00-\x20\x7F\/\?#@\\\\]/', $host);
|
||||||
|
|
||||||
|
if ($invalidHost === false) {
|
||||||
|
throw new \RuntimeException('Unable to validate URI host: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($invalidHost === 1) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Invalid host: "%s"', $host));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($host, '[') !== false || strpos($host, ']') !== false) {
|
||||||
|
if ($host[0] !== '[' || substr($host, -1) !== ']') {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Invalid host: "%s"', $host));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($host, ':') !== false) {
|
||||||
throw new \InvalidArgumentException(sprintf('Invalid host: "%s"', $host));
|
throw new \InvalidArgumentException(sprintf('Invalid host: "%s"', $host));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -632,7 +696,7 @@ class Uri implements UriInterface, \JsonSerializable
|
||||||
throw new \InvalidArgumentException('Scheme must be a string');
|
throw new \InvalidArgumentException('Scheme must be a string');
|
||||||
}
|
}
|
||||||
|
|
||||||
$scheme = \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
|
$scheme = Utils::asciiToLower($scheme);
|
||||||
|
|
||||||
if ($scheme !== '' && !preg_match('/^[a-z][a-z0-9.+-]*$/D', $scheme)) {
|
if ($scheme !== '' && !preg_match('/^[a-z][a-z0-9.+-]*$/D', $scheme)) {
|
||||||
\trigger_deprecation(
|
\trigger_deprecation(
|
||||||
|
|
@ -657,10 +721,10 @@ class Uri implements UriInterface, \JsonSerializable
|
||||||
throw new \InvalidArgumentException('User info must be a string');
|
throw new \InvalidArgumentException('User info must be a string');
|
||||||
}
|
}
|
||||||
|
|
||||||
return preg_replace_callback(
|
return $this->filterComponent(
|
||||||
'/(?:[^%'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/',
|
'/(?:[^%'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/',
|
||||||
[$this, 'rawurlencodeMatchZero'],
|
$component,
|
||||||
$component
|
'Unable to filter URI user info'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -675,7 +739,7 @@ class Uri implements UriInterface, \JsonSerializable
|
||||||
throw new \InvalidArgumentException('Host must be a string');
|
throw new \InvalidArgumentException('Host must be a string');
|
||||||
}
|
}
|
||||||
|
|
||||||
$host = \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
|
$host = Utils::asciiToLower($host);
|
||||||
self::assertValidHost($host);
|
self::assertValidHost($host);
|
||||||
|
|
||||||
return $host;
|
return $host;
|
||||||
|
|
@ -759,10 +823,10 @@ class Uri implements UriInterface, \JsonSerializable
|
||||||
throw new \InvalidArgumentException('Path must be a string');
|
throw new \InvalidArgumentException('Path must be a string');
|
||||||
}
|
}
|
||||||
|
|
||||||
return preg_replace_callback(
|
return $this->filterComponent(
|
||||||
'/(?:[^'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
|
'/(?:[^'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
|
||||||
[$this, 'rawurlencodeMatchZero'],
|
$path,
|
||||||
$path
|
'Unable to filter URI path'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -779,13 +843,24 @@ class Uri implements UriInterface, \JsonSerializable
|
||||||
throw new \InvalidArgumentException('Query and fragment must be a string');
|
throw new \InvalidArgumentException('Query and fragment must be a string');
|
||||||
}
|
}
|
||||||
|
|
||||||
return preg_replace_callback(
|
return $this->filterComponent(
|
||||||
'/(?:[^'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
|
'/(?:[^'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
|
||||||
[$this, 'rawurlencodeMatchZero'],
|
$str,
|
||||||
$str
|
'Unable to filter URI query or fragment'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function filterComponent(string $pattern, string $component, string $context): string
|
||||||
|
{
|
||||||
|
$filtered = preg_replace_callback($pattern, [$this, 'rawurlencodeMatchZero'], $component);
|
||||||
|
|
||||||
|
if ($filtered === null) {
|
||||||
|
throw new \RuntimeException($context.': '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filtered;
|
||||||
|
}
|
||||||
|
|
||||||
private function rawurlencodeMatchZero(array $match): string
|
private function rawurlencodeMatchZero(array $match): string
|
||||||
{
|
{
|
||||||
return rawurlencode($match[0]);
|
return rawurlencode($match[0]);
|
||||||
|
|
|
||||||
2
vendor/guzzlehttp/psr7/src/UriComparator.php
vendored
2
vendor/guzzlehttp/psr7/src/UriComparator.php
vendored
|
|
@ -19,7 +19,7 @@ final class UriComparator
|
||||||
*/
|
*/
|
||||||
public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool
|
public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool
|
||||||
{
|
{
|
||||||
if (\strcasecmp($original->getHost(), $modified->getHost()) !== 0) {
|
if (!Utils::caselessEquals($original->getHost(), $modified->getHost())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
12
vendor/guzzlehttp/psr7/src/UriNormalizer.php
vendored
12
vendor/guzzlehttp/psr7/src/UriNormalizer.php
vendored
|
|
@ -150,7 +150,13 @@ final class UriNormalizer
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($flags & self::REMOVE_DUPLICATE_SLASHES) {
|
if ($flags & self::REMOVE_DUPLICATE_SLASHES) {
|
||||||
$uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath()));
|
$path = preg_replace('#//++#', '/', $uri->getPath());
|
||||||
|
|
||||||
|
if ($path === null) {
|
||||||
|
throw new \RuntimeException('Unable to remove duplicate slashes from URI path: '.preg_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = $uri->withPath($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') {
|
if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') {
|
||||||
|
|
@ -186,7 +192,7 @@ final class UriNormalizer
|
||||||
$regex = '/(?:%[A-Fa-f0-9]{2})++/';
|
$regex = '/(?:%[A-Fa-f0-9]{2})++/';
|
||||||
|
|
||||||
$callback = function (array $match): string {
|
$callback = function (array $match): string {
|
||||||
return strtoupper($match[0]);
|
return Utils::asciiToUpper($match[0]);
|
||||||
};
|
};
|
||||||
|
|
||||||
return $uri
|
return $uri
|
||||||
|
|
@ -217,7 +223,7 @@ final class UriNormalizer
|
||||||
$normalized = preg_replace_callback($regex, $callback, $component);
|
$normalized = preg_replace_callback($regex, $callback, $component);
|
||||||
|
|
||||||
if ($normalized === null) {
|
if ($normalized === null) {
|
||||||
throw new \RuntimeException('Unable to normalize URI component percent-encoding');
|
throw new \RuntimeException('Unable to normalize URI component percent-encoding: '.preg_last_error_msg());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $normalized;
|
return $normalized;
|
||||||
|
|
|
||||||
88
vendor/guzzlehttp/psr7/src/Utils.php
vendored
88
vendor/guzzlehttp/psr7/src/Utils.php
vendored
|
|
@ -10,6 +10,65 @@ use Psr\Http\Message\UriInterface;
|
||||||
|
|
||||||
final class Utils
|
final class Utils
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Converts ASCII uppercase letters in a string to lowercase.
|
||||||
|
*
|
||||||
|
* Unlike strtolower(), which honors LC_CTYPE before PHP 8.2, the
|
||||||
|
* conversion is locale-independent and leaves every non-ASCII byte
|
||||||
|
* unchanged, as HTTP protocol elements require.
|
||||||
|
*/
|
||||||
|
public static function asciiToLower(string $string): string
|
||||||
|
{
|
||||||
|
return strtr($string, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts ASCII lowercase letters in a string to uppercase.
|
||||||
|
*
|
||||||
|
* Unlike strtoupper(), which honors LC_CTYPE before PHP 8.2, the
|
||||||
|
* conversion is locale-independent and leaves every non-ASCII byte
|
||||||
|
* unchanged, as HTTP protocol elements require.
|
||||||
|
*/
|
||||||
|
public static function asciiToUpper(string $string): string
|
||||||
|
{
|
||||||
|
return strtr($string, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the first character of a string to uppercase when it is an
|
||||||
|
* ASCII lowercase letter.
|
||||||
|
*
|
||||||
|
* Unlike ucfirst(), which honors LC_CTYPE before PHP 8.2, the conversion
|
||||||
|
* is locale-independent and leaves every non-ASCII byte unchanged, as
|
||||||
|
* HTTP protocol elements require.
|
||||||
|
*/
|
||||||
|
public static function asciiUcFirst(string $string): string
|
||||||
|
{
|
||||||
|
if ($string === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::asciiToUpper($string[0]).substr($string, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the haystack contains the needle, comparing ASCII
|
||||||
|
* letters case-insensitively and without locale sensitivity.
|
||||||
|
*/
|
||||||
|
public static function caselessContains(string $haystack, string $needle): bool
|
||||||
|
{
|
||||||
|
return str_contains(self::asciiToLower($haystack), self::asciiToLower($needle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether two strings are equal, comparing ASCII letters
|
||||||
|
* case-insensitively and without locale sensitivity.
|
||||||
|
*/
|
||||||
|
public static function caselessEquals(string $left, string $right): bool
|
||||||
|
{
|
||||||
|
return self::asciiToLower($left) === self::asciiToLower($right);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the items given by the keys, case insensitively from the data.
|
* Remove the items given by the keys, case insensitively from the data.
|
||||||
*
|
*
|
||||||
|
|
@ -20,11 +79,11 @@ final class Utils
|
||||||
$result = [];
|
$result = [];
|
||||||
|
|
||||||
foreach ($keys as &$key) {
|
foreach ($keys as &$key) {
|
||||||
$key = strtolower((string) $key);
|
$key = self::asciiToLower((string) $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($data as $k => $v) {
|
foreach ($data as $k => $v) {
|
||||||
if (!in_array(strtolower((string) $k), $keys)) {
|
if (!in_array(self::asciiToLower((string) $k), $keys)) {
|
||||||
$result[$k] = $v;
|
$result[$k] = $v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -212,7 +271,7 @@ final class Utils
|
||||||
if ($host !== '') {
|
if ($host !== '') {
|
||||||
if (isset($changes['set_headers']) && is_array($changes['set_headers'])) {
|
if (isset($changes['set_headers']) && is_array($changes['set_headers'])) {
|
||||||
foreach (array_keys($changes['set_headers']) as $header) {
|
foreach (array_keys($changes['set_headers']) as $header) {
|
||||||
if (strtolower((string) $header) === 'host') {
|
if (self::asciiToLower((string) $header) === 'host') {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
'Cannot modify request with both a URI containing a host and an explicit Host header.'
|
'Cannot modify request with both a URI containing a host and an explicit Host header.'
|
||||||
);
|
);
|
||||||
|
|
@ -248,7 +307,7 @@ final class Utils
|
||||||
|
|
||||||
$hasHost = false;
|
$hasHost = false;
|
||||||
foreach (array_keys($headers) as $header) {
|
foreach (array_keys($headers) as $header) {
|
||||||
if (strtolower((string) $header) === 'host') {
|
if (self::asciiToLower((string) $header) === 'host') {
|
||||||
$hasHost = true;
|
$hasHost = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -284,7 +343,7 @@ final class Utils
|
||||||
$addedHeaders = [];
|
$addedHeaders = [];
|
||||||
foreach ($headers as $header => $value) {
|
foreach ($headers as $header => $value) {
|
||||||
$header = (string) $header;
|
$header = (string) $header;
|
||||||
$normalized = strtolower($header);
|
$normalized = self::asciiToLower($header);
|
||||||
|
|
||||||
if (isset($addedHeaders[$normalized])) {
|
if (isset($addedHeaders[$normalized])) {
|
||||||
/** @var RequestInterface */
|
/** @var RequestInterface */
|
||||||
|
|
@ -462,6 +521,9 @@ final class Utils
|
||||||
* in subsequent reads. String inputs are always treated as string bodies,
|
* in subsequent reads. String inputs are always treated as string bodies,
|
||||||
* even when they name callable functions.
|
* even when they name callable functions.
|
||||||
*
|
*
|
||||||
|
* Passing a non-string scalar (`int`, `float`, or `bool`) is deprecated; cast
|
||||||
|
* it to a string instead. guzzlehttp/psr7 3.0 will reject non-string scalars.
|
||||||
|
*
|
||||||
* @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
|
* @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
|
||||||
* @param array{size?: int, metadata?: array} $options Additional options
|
* @param array{size?: int, metadata?: array} $options Additional options
|
||||||
*
|
*
|
||||||
|
|
@ -470,6 +532,22 @@ final class Utils
|
||||||
public static function streamFor($resource = '', array $options = []): StreamInterface
|
public static function streamFor($resource = '', array $options = []): StreamInterface
|
||||||
{
|
{
|
||||||
if (is_scalar($resource)) {
|
if (is_scalar($resource)) {
|
||||||
|
if (!is_string($resource)) {
|
||||||
|
\trigger_deprecation(
|
||||||
|
'guzzlehttp/psr7',
|
||||||
|
'2.12',
|
||||||
|
'Passing %s to Utils::streamFor() is deprecated; cast it to a string. guzzlehttp/psr7 3.0 will only accept string, resource, StreamInterface, Stringable, Iterator, callable, or null.',
|
||||||
|
\gettype($resource)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (is_float($resource) && !is_finite($resource)) {
|
||||||
|
// Normalized only to avoid PHP 8.5's (string) NAN warning
|
||||||
|
// while deprecated; 3.0 rejects non-finite floats with every
|
||||||
|
// other non-string scalar.
|
||||||
|
$resource = is_nan($resource) ? 'NAN' : ($resource > 0 ? 'INF' : '-INF');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$stream = self::tryFopen('php://temp', 'r+');
|
$stream = self::tryFopen('php://temp', 'r+');
|
||||||
if ($resource !== '') {
|
if ($resource !== '') {
|
||||||
fwrite($stream, (string) $resource);
|
fwrite($stream, (string) $resource);
|
||||||
|
|
|
||||||
123
vendor/mtdowling/jmespath.php/README.rst
vendored
123
vendor/mtdowling/jmespath.php/README.rst
vendored
|
|
@ -1,123 +0,0 @@
|
||||||
============
|
|
||||||
jmespath.php
|
|
||||||
============
|
|
||||||
|
|
||||||
JMESPath (pronounced "jaymz path") allows you to declaratively specify how to
|
|
||||||
extract elements from a JSON document. *jmespath.php* allows you to use
|
|
||||||
JMESPath in PHP applications with PHP data structures. It requires PHP 7.2.5 or
|
|
||||||
greater and can be installed through `Composer <http://getcomposer.org/doc/00-intro.md>`_
|
|
||||||
using the ``mtdowling/jmespath.php`` package.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
require 'vendor/autoload.php';
|
|
||||||
|
|
||||||
$expression = 'foo.*.baz';
|
|
||||||
|
|
||||||
$data = [
|
|
||||||
'foo' => [
|
|
||||||
'bar' => ['baz' => 1],
|
|
||||||
'bam' => ['baz' => 2],
|
|
||||||
'boo' => ['baz' => 3]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
JmesPath\search($expression, $data);
|
|
||||||
// Returns: [1, 2, 3]
|
|
||||||
|
|
||||||
- `JMESPath Tutorial <http://jmespath.org/tutorial.html>`_
|
|
||||||
- `JMESPath Grammar <http://jmespath.org/specification.html#grammar>`_
|
|
||||||
- `JMESPath Python library <https://github.com/jmespath/jmespath.py>`_
|
|
||||||
|
|
||||||
PHP Usage
|
|
||||||
=========
|
|
||||||
|
|
||||||
The ``JmesPath\search`` function can be used in most cases when using the
|
|
||||||
library. This function utilizes a JMESPath runtime based on your environment.
|
|
||||||
The runtime utilized can be configured using environment variables and may at
|
|
||||||
some point in the future automatically utilize a C extension if available.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$result = JmesPath\search($expression, $data);
|
|
||||||
|
|
||||||
// or, if you require PSR-4 compliance.
|
|
||||||
$result = JmesPath\Env::search($expression, $data);
|
|
||||||
|
|
||||||
Runtimes
|
|
||||||
--------
|
|
||||||
|
|
||||||
jmespath.php utilizes *runtimes*. There are currently two runtimes:
|
|
||||||
AstRuntime and CompilerRuntime.
|
|
||||||
|
|
||||||
AstRuntime is utilized by ``JmesPath\search()`` and ``JmesPath\Env::search()``
|
|
||||||
by default.
|
|
||||||
|
|
||||||
AstRuntime
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
The AstRuntime will parse an expression, cache the resulting AST in memory,
|
|
||||||
and interpret the AST using an external tree visitor. AstRuntime provides a
|
|
||||||
good general approach for interpreting JMESPath expressions that have a low to
|
|
||||||
moderate level of reuse.
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$runtime = new JmesPath\AstRuntime();
|
|
||||||
$runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
|
|
||||||
// > 'baz'
|
|
||||||
|
|
||||||
CompilerRuntime
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
``JmesPath\CompilerRuntime`` provides the most performance for
|
|
||||||
applications that have a moderate to high level of reuse of JMESPath
|
|
||||||
expressions. The CompilerRuntime will walk a JMESPath AST and emit PHP source
|
|
||||||
code, resulting in anywhere from 7x to 60x speed improvements.
|
|
||||||
|
|
||||||
Compiling JMESPath expressions to source code is a slower process than just
|
|
||||||
walking and interpreting a JMESPath AST (via the AstRuntime). However,
|
|
||||||
running the compiled JMESPath code results in much better performance than
|
|
||||||
walking an AST. This essentially means that there is a warm-up period when
|
|
||||||
using the ``CompilerRuntime``, but after the warm-up period, it will provide
|
|
||||||
much better performance.
|
|
||||||
|
|
||||||
Use the CompilerRuntime if you know that you will be executing JMESPath
|
|
||||||
expressions more than once or if you can pre-compile JMESPath expressions
|
|
||||||
before executing them (for example, server-side applications).
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
// Note: The cache directory argument is optional.
|
|
||||||
$runtime = new JmesPath\CompilerRuntime('/path/to/compile/folder');
|
|
||||||
$runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
|
|
||||||
// > 'baz'
|
|
||||||
|
|
||||||
Environment Variables
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
You can utilize the CompilerRuntime in ``JmesPath\search()`` by setting
|
|
||||||
the ``JP_PHP_COMPILE`` environment variable to "on" or to a directory
|
|
||||||
on disk used to store cached expressions.
|
|
||||||
|
|
||||||
Testing
|
|
||||||
=======
|
|
||||||
|
|
||||||
A comprehensive list of test cases can be found at
|
|
||||||
https://github.com/jmespath/jmespath.php/tree/master/tests/compliance.
|
|
||||||
These compliance tests are utilized by jmespath.php to ensure consistency with
|
|
||||||
other implementations, and can serve as examples of the language.
|
|
||||||
|
|
||||||
jmespath.php is tested using PHPUnit. In order to run the tests, you need to
|
|
||||||
first install the dependencies using Composer as described in the *Installation*
|
|
||||||
section. Next you just need to run the tests via make:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
make test
|
|
||||||
|
|
||||||
You can run a suite of performance tests as well:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
make perf
|
|
||||||
|
|
@ -9,7 +9,6 @@ class AstRuntime
|
||||||
private $parser;
|
private $parser;
|
||||||
private $interpreter;
|
private $interpreter;
|
||||||
private $cache = [];
|
private $cache = [];
|
||||||
private $cachedCount = 0;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
?Parser $parser = null,
|
?Parser $parser = null,
|
||||||
|
|
@ -34,10 +33,9 @@ class AstRuntime
|
||||||
public function __invoke($expression, $data)
|
public function __invoke($expression, $data)
|
||||||
{
|
{
|
||||||
if (!isset($this->cache[$expression])) {
|
if (!isset($this->cache[$expression])) {
|
||||||
// Clear the AST cache when it hits 1024 entries
|
// Clear the AST cache when it already holds 1024 entries.
|
||||||
if (++$this->cachedCount > 1024) {
|
if (count($this->cache) >= 1024) {
|
||||||
$this->cache = [];
|
$this->cache = [];
|
||||||
$this->cachedCount = 0;
|
|
||||||
}
|
}
|
||||||
$this->cache[$expression] = $this->parser->parse($expression);
|
$this->cache[$expression] = $this->parser->parse($expression);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,14 @@ namespace JmesPath;
|
||||||
* logic to determine the filename:
|
* logic to determine the filename:
|
||||||
*
|
*
|
||||||
* 1. Start with the string "jmespath_"
|
* 1. Start with the string "jmespath_"
|
||||||
* 2. Append the MD5 checksum of the expression.
|
* 2. Append the MD5 checksum of the expression salted with the PHP version
|
||||||
|
* and compiled-cache epoch.
|
||||||
* 3. Append ".php"
|
* 3. Append ".php"
|
||||||
*/
|
*/
|
||||||
class CompilerRuntime
|
class CompilerRuntime
|
||||||
{
|
{
|
||||||
|
const CACHE_VERSION = 5;
|
||||||
|
|
||||||
private $parser;
|
private $parser;
|
||||||
private $compiler;
|
private $compiler;
|
||||||
private $cacheDir;
|
private $cacheDir;
|
||||||
|
|
@ -51,7 +54,7 @@ class CompilerRuntime
|
||||||
*/
|
*/
|
||||||
public function __invoke($expression, $data)
|
public function __invoke($expression, $data)
|
||||||
{
|
{
|
||||||
$functionName = 'jmespath_' . md5($expression);
|
$functionName = self::functionName($expression);
|
||||||
|
|
||||||
if (!function_exists($functionName)) {
|
if (!function_exists($functionName)) {
|
||||||
$filename = "{$this->cacheDir}/{$functionName}.php";
|
$filename = "{$this->cacheDir}/{$functionName}.php";
|
||||||
|
|
@ -64,6 +67,23 @@ class CompilerRuntime
|
||||||
return $functionName($this->interpreter, $data);
|
return $functionName($this->interpreter, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal Shared with DebugRuntime so cache naming cannot drift.
|
||||||
|
*/
|
||||||
|
public static function functionName($expression)
|
||||||
|
{
|
||||||
|
return 'jmespath_' . md5(
|
||||||
|
'jmespath:' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION
|
||||||
|
. ':' . self::CACHE_VERSION . ':' . $expression
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
public function getCacheDir()
|
||||||
|
{
|
||||||
|
return $this->cacheDir;
|
||||||
|
}
|
||||||
|
|
||||||
private function compile($filename, $expression, $functionName)
|
private function compile($filename, $expression, $functionName)
|
||||||
{
|
{
|
||||||
$code = $this->compiler->visit(
|
$code = $this->compiler->visit(
|
||||||
|
|
@ -72,12 +92,26 @@ class CompilerRuntime
|
||||||
$expression
|
$expression
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!file_put_contents($filename, $code)) {
|
$tempFile = $filename . '.' . bin2hex(random_bytes(12)) . '.tmp';
|
||||||
|
|
||||||
|
if (!file_put_contents($tempFile, $code)) {
|
||||||
throw new \RuntimeException(sprintf(
|
throw new \RuntimeException(sprintf(
|
||||||
'Unable to write the compiled PHP code to: %s (%s)',
|
'Unable to write the compiled PHP code to: %s (%s)',
|
||||||
$filename,
|
$tempFile,
|
||||||
var_export(error_get_last(), true)
|
var_export(error_get_last(), true)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!rename($tempFile, $filename)) {
|
||||||
|
@unlink($tempFile);
|
||||||
|
if (!file_exists($filename)) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
"Unable to move the compiled PHP code to: {$filename}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Another process won the race; its file is equivalent.
|
||||||
|
} elseif (function_exists('opcache_invalidate')) {
|
||||||
|
opcache_invalidate($filename, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,12 +83,12 @@ class DebugRuntime
|
||||||
private function dumpCompiledCode($expression)
|
private function dumpCompiledCode($expression)
|
||||||
{
|
{
|
||||||
fwrite($this->out, "Code\n========\n\n");
|
fwrite($this->out, "Code\n========\n\n");
|
||||||
$dir = sys_get_temp_dir();
|
$functionName = CompilerRuntime::functionName($expression);
|
||||||
$hash = md5($expression);
|
$filename = $this->runtime->getCacheDir() . '/' . $functionName . '.php';
|
||||||
$functionName = "jmespath_{$hash}";
|
|
||||||
$filename = "{$dir}/{$functionName}.php";
|
|
||||||
fwrite($this->out, "File: {$filename}\n\n");
|
fwrite($this->out, "File: {$filename}\n\n");
|
||||||
fprintf($this->out, file_get_contents($filename));
|
fwrite($this->out, is_file($filename)
|
||||||
|
? file_get_contents($filename)
|
||||||
|
: "(not present: {$functionName} was already loaded in this process, likely from another cache directory)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function debugCallback(callable $debugFn, $expression, $data)
|
private function debugCallback(callable $debugFn, $expression, $data)
|
||||||
|
|
|
||||||
5
vendor/mtdowling/jmespath.php/src/Env.php
vendored
5
vendor/mtdowling/jmespath.php/src/Env.php
vendored
|
|
@ -57,7 +57,10 @@ final class Env
|
||||||
public static function cleanCompileDir()
|
public static function cleanCompileDir()
|
||||||
{
|
{
|
||||||
$total = 0;
|
$total = 0;
|
||||||
$compileDir = self::getEnvVariable(self::COMPILE_DIR) ?: sys_get_temp_dir();
|
$compileDir = self::getEnvVariable(self::COMPILE_DIR);
|
||||||
|
if ($compileDir === 'on' || !$compileDir) {
|
||||||
|
$compileDir = sys_get_temp_dir();
|
||||||
|
}
|
||||||
|
|
||||||
foreach (glob("{$compileDir}/jmespath_*.php") as $file) {
|
foreach (glob("{$compileDir}/jmespath_*.php") as $file) {
|
||||||
$total++;
|
$total++;
|
||||||
|
|
|
||||||
135
vendor/mtdowling/jmespath.php/src/FnDispatcher.php
vendored
135
vendor/mtdowling/jmespath.php/src/FnDispatcher.php
vendored
|
|
@ -60,14 +60,20 @@ class FnDispatcher
|
||||||
{
|
{
|
||||||
$this->validate('contains', $args, [['string', 'array'], ['any']]);
|
$this->validate('contains', $args, [['string', 'array'], ['any']]);
|
||||||
if (is_array($args[0])) {
|
if (is_array($args[0])) {
|
||||||
return in_array($args[1], $args[0]);
|
foreach ($args[0] as $value) {
|
||||||
} elseif (is_string($args[1])) {
|
if (Utils::isEqual($value, $args[1])) {
|
||||||
return mb_strpos($args[0], $args[1], 0, 'UTF-8') !== false;
|
return true;
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_string($args[1])
|
||||||
|
? mb_strpos($args[0], $args[1], 0, 'UTF-8') !== false
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
private function fn_ends_with(array $args)
|
private function fn_ends_with(array $args)
|
||||||
{
|
{
|
||||||
$this->validate('ends_with', $args, [['string'], ['string']]);
|
$this->validate('ends_with', $args, [['string'], ['string']]);
|
||||||
|
|
@ -100,7 +106,7 @@ class FnDispatcher
|
||||||
$fn = function ($a, $b, $i) use ($args) {
|
$fn = function ($a, $b, $i) use ($args) {
|
||||||
return $i ? ($a . $args[0] . $b) : $b;
|
return $i ? ($a . $args[0] . $b) : $b;
|
||||||
};
|
};
|
||||||
return $this->reduce('join:0', $args[1], ['string'], $fn);
|
return $args[1] ? $this->reduce('join:0', $args[1], ['string'], $fn) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fn_keys(array $args)
|
private function fn_keys(array $args)
|
||||||
|
|
@ -118,8 +124,8 @@ class FnDispatcher
|
||||||
private function fn_max(array $args)
|
private function fn_max(array $args)
|
||||||
{
|
{
|
||||||
$this->validate('max', $args, [['array']]);
|
$this->validate('max', $args, [['array']]);
|
||||||
$fn = function ($a, $b) {
|
$fn = function ($a, $b, $i) {
|
||||||
return $a >= $b ? $a : $b;
|
return $i && self::compareValues($a, $b) >= 0 ? $a : $b;
|
||||||
};
|
};
|
||||||
return $this->reduce('max:0', $args[0], ['number', 'string'], $fn);
|
return $this->reduce('max:0', $args[0], ['number', 'string'], $fn);
|
||||||
}
|
}
|
||||||
|
|
@ -128,10 +134,21 @@ class FnDispatcher
|
||||||
{
|
{
|
||||||
$this->validate('max_by', $args, [['array'], ['expression']]);
|
$this->validate('max_by', $args, [['array'], ['expression']]);
|
||||||
$expr = $this->wrapExpression('max_by:1', $args[1], ['number', 'string']);
|
$expr = $this->wrapExpression('max_by:1', $args[1], ['number', 'string']);
|
||||||
$fn = function ($carry, $item, $index) use ($expr) {
|
$carryKey = null;
|
||||||
return $index
|
$fn = function ($carry, $item, $index) use ($expr, &$carryKey) {
|
||||||
? ($expr($carry) >= $expr($item) ? $carry : $item)
|
if (!$index) {
|
||||||
: $item;
|
return $item;
|
||||||
|
}
|
||||||
|
if ($index === 1) {
|
||||||
|
$carryKey = $expr($carry);
|
||||||
|
}
|
||||||
|
$itemKey = $expr($item);
|
||||||
|
$this->validateSeq('max_by:0', ['number', 'string'], $carryKey, $itemKey);
|
||||||
|
if (self::compareValues($carryKey, $itemKey) >= 0) {
|
||||||
|
return $carry;
|
||||||
|
}
|
||||||
|
$carryKey = $itemKey;
|
||||||
|
return $item;
|
||||||
};
|
};
|
||||||
return $this->reduce('max_by:1', $args[0], ['any'], $fn);
|
return $this->reduce('max_by:1', $args[0], ['any'], $fn);
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +157,7 @@ class FnDispatcher
|
||||||
{
|
{
|
||||||
$this->validate('min', $args, [['array']]);
|
$this->validate('min', $args, [['array']]);
|
||||||
$fn = function ($a, $b, $i) {
|
$fn = function ($a, $b, $i) {
|
||||||
return $i && $a <= $b ? $a : $b;
|
return $i && self::compareValues($a, $b) <= 0 ? $a : $b;
|
||||||
};
|
};
|
||||||
return $this->reduce('min:0', $args[0], ['number', 'string'], $fn);
|
return $this->reduce('min:0', $args[0], ['number', 'string'], $fn);
|
||||||
}
|
}
|
||||||
|
|
@ -149,9 +166,21 @@ class FnDispatcher
|
||||||
{
|
{
|
||||||
$this->validate('min_by', $args, [['array'], ['expression']]);
|
$this->validate('min_by', $args, [['array'], ['expression']]);
|
||||||
$expr = $this->wrapExpression('min_by:1', $args[1], ['number', 'string']);
|
$expr = $this->wrapExpression('min_by:1', $args[1], ['number', 'string']);
|
||||||
$i = -1;
|
$carryKey = null;
|
||||||
$fn = function ($a, $b) use ($expr, &$i) {
|
$fn = function ($carry, $item, $index) use ($expr, &$carryKey) {
|
||||||
return ++$i ? ($expr($a) <= $expr($b) ? $a : $b) : $b;
|
if (!$index) {
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
if ($index === 1) {
|
||||||
|
$carryKey = $expr($carry);
|
||||||
|
}
|
||||||
|
$itemKey = $expr($item);
|
||||||
|
$this->validateSeq('min_by:0', ['number', 'string'], $carryKey, $itemKey);
|
||||||
|
if (self::compareValues($carryKey, $itemKey) <= 0) {
|
||||||
|
return $carry;
|
||||||
|
}
|
||||||
|
$carryKey = $itemKey;
|
||||||
|
return $item;
|
||||||
};
|
};
|
||||||
return $this->reduce('min_by:1', $args[0], ['any'], $fn);
|
return $this->reduce('min_by:1', $args[0], ['any'], $fn);
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +191,7 @@ class FnDispatcher
|
||||||
if (is_array($args[0])) {
|
if (is_array($args[0])) {
|
||||||
return array_reverse($args[0]);
|
return array_reverse($args[0]);
|
||||||
} elseif (is_string($args[0])) {
|
} elseif (is_string($args[0])) {
|
||||||
return strrev($args[0]);
|
return implode('', array_reverse(mb_str_split($args[0], 1, 'UTF-8')));
|
||||||
} else {
|
} else {
|
||||||
throw new \RuntimeException('Cannot reverse provided argument');
|
throw new \RuntimeException('Cannot reverse provided argument');
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +203,7 @@ class FnDispatcher
|
||||||
$fn = function ($a, $b) {
|
$fn = function ($a, $b) {
|
||||||
return Utils::add($a, $b);
|
return Utils::add($a, $b);
|
||||||
};
|
};
|
||||||
return $this->reduce('sum:0', $args[0], ['number'], $fn);
|
return $args[0] ? $this->reduce('sum:0', $args[0], ['number'], $fn) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fn_sort(array $args)
|
private function fn_sort(array $args)
|
||||||
|
|
@ -183,7 +212,7 @@ class FnDispatcher
|
||||||
$valid = ['string', 'number'];
|
$valid = ['string', 'number'];
|
||||||
return Utils::stableSort($args[0], function ($a, $b) use ($valid) {
|
return Utils::stableSort($args[0], function ($a, $b) use ($valid) {
|
||||||
$this->validateSeq('sort:0', $valid, $a, $b);
|
$this->validateSeq('sort:0', $valid, $a, $b);
|
||||||
return strnatcmp($a, $b);
|
return self::compareValues($a, $b);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,7 +227,7 @@ class FnDispatcher
|
||||||
$va = $expr($a);
|
$va = $expr($a);
|
||||||
$vb = $expr($b);
|
$vb = $expr($b);
|
||||||
$this->validateSeq('sort_by:0', $valid, $va, $vb);
|
$this->validateSeq('sort_by:0', $valid, $va, $vb);
|
||||||
return strnatcmp($va, $vb);
|
return self::compareValues($va, $vb);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -236,14 +265,53 @@ class FnDispatcher
|
||||||
{
|
{
|
||||||
$this->validateArity('to_number', count($args), 1);
|
$this->validateArity('to_number', count($args), 1);
|
||||||
$value = $args[0];
|
$value = $args[0];
|
||||||
$type = Utils::type($value);
|
|
||||||
if ($type == 'number') {
|
if (Utils::type($value) == 'number') {
|
||||||
return $value;
|
return $value;
|
||||||
} elseif ($type == 'string' && is_numeric($value)) {
|
}
|
||||||
return mb_strpos($value, '.', 0, 'UTF-8') ? (float) $value : (int) $value;
|
|
||||||
} else {
|
if (!is_string($value)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->parseJsonNumber($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a string conforming to the JSON number grammar (RFC 8259) into
|
||||||
|
* an int when exactly representable, otherwise a float. Returns null for
|
||||||
|
* non-conforming or non-finite input.
|
||||||
|
*/
|
||||||
|
private function parseJsonNumber($value)
|
||||||
|
{
|
||||||
|
if (!preg_match('/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/D', $value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^-?(?:0|[1-9][0-9]*)$/D', $value)) {
|
||||||
|
return $this->parseJsonInteger($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$number = (float) $value;
|
||||||
|
|
||||||
|
return is_finite($number) ? $number : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseJsonInteger($value)
|
||||||
|
{
|
||||||
|
$negative = $value[0] === '-';
|
||||||
|
$digits = $negative ? substr($value, 1) : $value;
|
||||||
|
$limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
|
||||||
|
|
||||||
|
if (strlen($digits) < strlen($limit)
|
||||||
|
|| (strlen($digits) === strlen($limit) && strcmp($digits, $limit) <= 0)
|
||||||
|
) {
|
||||||
|
return (int) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$number = (float) $value;
|
||||||
|
|
||||||
|
return is_finite($number) ? $number : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fn_values(array $args)
|
private function fn_values(array $args)
|
||||||
|
|
@ -272,7 +340,7 @@ class FnDispatcher
|
||||||
|
|
||||||
private function fn_map(array $args)
|
private function fn_map(array $args)
|
||||||
{
|
{
|
||||||
$this->validate('map', $args, [['expression'], ['any']]);
|
$this->validate('map', $args, [['expression'], ['array']]);
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ($args[1] as $a) {
|
foreach ($args[1] as $a) {
|
||||||
$result[] = $args[0]($a);
|
$result[] = $args[0]($a);
|
||||||
|
|
@ -354,6 +422,21 @@ class FnDispatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two values of the same JMESPath type.
|
||||||
|
*
|
||||||
|
* @param mixed $a Value A
|
||||||
|
* @param mixed $b Value B
|
||||||
|
*
|
||||||
|
* @return int Negative if $a < $b, zero if equal, positive if $a > $b.
|
||||||
|
*/
|
||||||
|
private static function compareValues($a, $b)
|
||||||
|
{
|
||||||
|
return Utils::type($a) === 'string'
|
||||||
|
? strcmp((string) $a, (string) $b)
|
||||||
|
: ($a <=> $b);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduces and validates an array of values to a single value using a fn.
|
* Reduces and validates an array of values to a single value using a fn.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
29
vendor/mtdowling/jmespath.php/src/Lexer.php
vendored
29
vendor/mtdowling/jmespath.php/src/Lexer.php
vendored
|
|
@ -298,9 +298,10 @@ class Lexer
|
||||||
$buffer .= $current;
|
$buffer .= $current;
|
||||||
$current = next($chars);
|
$current = next($chars);
|
||||||
} while ($current !== false && isset($this->numbers[$current]));
|
} while ($current !== false && isset($this->numbers[$current]));
|
||||||
|
$value = $this->parseIndexNumber($buffer);
|
||||||
$tokens[] = [
|
$tokens[] = [
|
||||||
'type' => self::T_NUMBER,
|
'type' => $value === null ? self::T_UNKNOWN : self::T_NUMBER,
|
||||||
'value' => (int)$buffer,
|
'value' => $value === null ? $buffer : $value,
|
||||||
'pos' => $start
|
'pos' => $start
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -417,6 +418,30 @@ class Lexer
|
||||||
return ['type' => $type, 'value' => $buffer, 'pos' => $position];
|
return ['type' => $type, 'value' => $buffer, 'pos' => $position];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a bare index/slice integer token ("-"? digit+). Returns null
|
||||||
|
* when the buffer is a lone "-" or the value cannot be represented as a
|
||||||
|
* PHP integer.
|
||||||
|
*/
|
||||||
|
private function parseIndexNumber($buffer)
|
||||||
|
{
|
||||||
|
if ($buffer === '-') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$negative = $buffer[0] === '-';
|
||||||
|
$digits = ltrim($negative ? substr($buffer, 1) : $buffer, '0') ?: '0';
|
||||||
|
$limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
|
||||||
|
|
||||||
|
if (strlen($digits) > strlen($limit)
|
||||||
|
|| (strlen($digits) === strlen($limit) && strcmp($digits, $limit) > 0)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a JSON token or sets the token type to "unknown" on error.
|
* Parses a JSON token or sets the token type to "unknown" on error.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
12
vendor/mtdowling/jmespath.php/src/Parser.php
vendored
12
vendor/mtdowling/jmespath.php/src/Parser.php
vendored
|
|
@ -5,7 +5,7 @@ use JmesPath\Lexer as T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JMESPath Pratt parser
|
* JMESPath Pratt parser
|
||||||
* @link http://hall.org.ua/halls/wizzard/pdf/Vaughan.Pratt.TDOP.pdf
|
* @link https://dl.acm.org/doi/10.1145/512927.512931
|
||||||
*/
|
*/
|
||||||
class Parser
|
class Parser
|
||||||
{
|
{
|
||||||
|
|
@ -22,6 +22,8 @@ class Parser
|
||||||
T::T_EOF => 0,
|
T::T_EOF => 0,
|
||||||
T::T_QUOTED_IDENTIFIER => 0,
|
T::T_QUOTED_IDENTIFIER => 0,
|
||||||
T::T_IDENTIFIER => 0,
|
T::T_IDENTIFIER => 0,
|
||||||
|
T::T_UNKNOWN => 0,
|
||||||
|
T::T_LITERAL => 0,
|
||||||
T::T_RBRACKET => 0,
|
T::T_RBRACKET => 0,
|
||||||
T::T_RPAREN => 0,
|
T::T_RPAREN => 0,
|
||||||
T::T_COMMA => 0,
|
T::T_COMMA => 0,
|
||||||
|
|
@ -272,6 +274,10 @@ class Parser
|
||||||
|
|
||||||
private function led_lparen(array $left)
|
private function led_lparen(array $left)
|
||||||
{
|
{
|
||||||
|
if (!isset($left['type'], $left['value']) || $left['type'] !== 'field') {
|
||||||
|
throw $this->syntax('Invalid function name');
|
||||||
|
}
|
||||||
|
|
||||||
$args = [];
|
$args = [];
|
||||||
$this->next();
|
$this->next();
|
||||||
|
|
||||||
|
|
@ -347,6 +353,10 @@ class Parser
|
||||||
if ($this->token['type'] == T::T_LBRACKET) {
|
if ($this->token['type'] == T::T_LBRACKET) {
|
||||||
$this->next();
|
$this->next();
|
||||||
return $this->parseMultiSelectList();
|
return $this->parseMultiSelectList();
|
||||||
|
} elseif ($this->token['type'] == T::T_LBRACE) {
|
||||||
|
// Like the multi-select list above, a multi-select hash ends any
|
||||||
|
// projection: tokens that follow apply to the projected list.
|
||||||
|
return $this->nud_lbrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->expr($bp);
|
return $this->expr($bp);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ class SyntaxErrorException extends \InvalidArgumentException
|
||||||
array $token,
|
array $token,
|
||||||
$expression
|
$expression
|
||||||
) {
|
) {
|
||||||
|
$token += ['pos' => mb_strlen($expression, 'UTF-8'), 'value' => null];
|
||||||
$message = sprintf("Syntax error at character %d\n", max($token['pos'], 0))
|
$message = sprintf("Syntax error at character %d\n", max($token['pos'], 0))
|
||||||
. $expression . "\n" . str_repeat(' ', max($token['pos'], 0)) . "^\n";
|
. $expression . "\n" . str_repeat(' ', max($token['pos'], 0)) . "^\n";
|
||||||
$message .= !is_array($expectedTypesOrMessage)
|
$message .= !is_array($expectedTypesOrMessage)
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ class TreeCompiler
|
||||||
return $this
|
return $this
|
||||||
->write('%s = $value;', $a)
|
->write('%s = $value;', $a)
|
||||||
->dispatch($node['children'][0])
|
->dispatch($node['children'][0])
|
||||||
->write('if (!$value && $value !== "0" && $value !== 0) {')
|
->write('if (!Utils::isTruthy($value)) {')
|
||||||
->indent()
|
->indent()
|
||||||
->write('$value = %s;', $a)
|
->write('$value = %s;', $a)
|
||||||
->dispatch($node['children'][1])
|
->dispatch($node['children'][1])
|
||||||
|
|
@ -121,7 +121,7 @@ class TreeCompiler
|
||||||
return $this
|
return $this
|
||||||
->write('%s = $value;', $a)
|
->write('%s = $value;', $a)
|
||||||
->dispatch($node['children'][0])
|
->dispatch($node['children'][0])
|
||||||
->write('if ($value || $value === "0" || $value === 0) {')
|
->write('if (Utils::isTruthy($value)) {')
|
||||||
->indent()
|
->indent()
|
||||||
->write('$value = %s;', $a)
|
->write('$value = %s;', $a)
|
||||||
->dispatch($node['children'][1])
|
->dispatch($node['children'][1])
|
||||||
|
|
@ -257,8 +257,8 @@ class TreeCompiler
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->write(
|
return $this->write(
|
||||||
'$value = Fd::getInstance()->__invoke("%s", %s);',
|
'$value = Fd::getInstance()->__invoke(%s, %s);',
|
||||||
$node['value'], $args
|
var_export($node['value'], true), $args
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -332,7 +332,7 @@ class TreeCompiler
|
||||||
->write('');
|
->write('');
|
||||||
|
|
||||||
if (!isset($node['from'])) {
|
if (!isset($node['from'])) {
|
||||||
$this->write('if (!is_array($value) || !($value instanceof \stdClass)) { $value = null; }');
|
$this->write('if (!is_array($value) && !($value instanceof \stdClass)) { $value = null; }');
|
||||||
} elseif ($node['from'] == 'object') {
|
} elseif ($node['from'] == 'object') {
|
||||||
$this->write('if (!Utils::isObject($value)) { $value = null; }');
|
$this->write('if (!Utils::isObject($value)) { $value = null; }');
|
||||||
} elseif ($node['from'] == 'array') {
|
} elseif ($node['from'] == 'array') {
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,8 @@ class TreeInterpreter
|
||||||
|
|
||||||
case 'projection':
|
case 'projection':
|
||||||
$left = $this->dispatch($node['children'][0], $value);
|
$left = $this->dispatch($node['children'][0], $value);
|
||||||
switch ($node['from']) {
|
$from = isset($node['from']) ? $node['from'] : null;
|
||||||
|
switch ($from) {
|
||||||
case 'object':
|
case 'object':
|
||||||
if (!Utils::isObject($left)) {
|
if (!Utils::isObject($left)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -81,7 +82,7 @@ class TreeInterpreter
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!is_array($left) || !($left instanceof \stdClass)) {
|
if (!is_array($left) && !($left instanceof \stdClass)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
61
vendor/mtdowling/jmespath.php/src/Utils.php
vendored
61
vendor/mtdowling/jmespath.php/src/Utils.php
vendored
|
|
@ -22,7 +22,7 @@ class Utils
|
||||||
public static function isTruthy($value)
|
public static function isTruthy($value)
|
||||||
{
|
{
|
||||||
if (!$value) {
|
if (!$value) {
|
||||||
return $value === 0 || $value === '0';
|
return $value === 0 || $value === 0.0 || $value === '0';
|
||||||
} elseif ($value instanceof \stdClass) {
|
} elseif ($value instanceof \stdClass) {
|
||||||
return (bool) get_object_vars($value);
|
return (bool) get_object_vars($value);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -58,7 +58,8 @@ class Utils
|
||||||
return count($arg) == 0 || $arg->offsetExists(0)
|
return count($arg) == 0 || $arg->offsetExists(0)
|
||||||
? 'array'
|
? 'array'
|
||||||
: 'object';
|
: 'object';
|
||||||
} elseif (method_exists($arg, '__toString')) {
|
} elseif (is_object($arg)) {
|
||||||
|
if (method_exists($arg, '__toString')) {
|
||||||
return 'string';
|
return 'string';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,6 +68,11 @@ class Utils
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Unable to determine JMESPath type from ' . gettype($arg)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the provided value is a JMESPath compatible object.
|
* Determine if the provided value is a JMESPath compatible object.
|
||||||
*
|
*
|
||||||
|
|
@ -106,7 +112,10 @@ class Utils
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON aware value comparison function.
|
* JSON-semantic equality: one number type, structural comparison for arrays
|
||||||
|
* and objects, and no object key-order sensitivity.
|
||||||
|
* Empty arrays and empty objects compare equal because PHP cannot represent
|
||||||
|
* that distinction after associative JSON decoding.
|
||||||
*
|
*
|
||||||
* @param mixed $a First value to compare
|
* @param mixed $a First value to compare
|
||||||
* @param mixed $b Second value to compare
|
* @param mixed $b Second value to compare
|
||||||
|
|
@ -115,15 +124,38 @@ class Utils
|
||||||
*/
|
*/
|
||||||
public static function isEqual($a, $b)
|
public static function isEqual($a, $b)
|
||||||
{
|
{
|
||||||
if ($a === $b) {
|
$typeA = self::type($a);
|
||||||
return true;
|
$typeB = self::type($b);
|
||||||
} elseif ($a instanceof \stdClass) {
|
|
||||||
return self::isEqual((array) $a, $b);
|
if ($typeA !== $typeB) {
|
||||||
} elseif ($b instanceof \stdClass) {
|
return ($typeA === 'array' || $typeA === 'object')
|
||||||
return self::isEqual($a, (array) $b);
|
&& ($typeB === 'array' || $typeB === 'object')
|
||||||
} else {
|
&& (array) $a === []
|
||||||
|
&& (array) $b === [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($typeA === 'number') {
|
||||||
|
return $a == $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($typeA === 'array' || $typeA === 'object') {
|
||||||
|
$a = (array) $a;
|
||||||
|
$b = (array) $b;
|
||||||
|
|
||||||
|
if (count($a) !== count($b)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($a as $key => $value) {
|
||||||
|
if (!array_key_exists($key, $b) || !self::isEqual($value, $b[$key])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a === $b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -160,7 +192,7 @@ class Utils
|
||||||
* @param callable $sortFn Callable used to sort values
|
* @param callable $sortFn Callable used to sort values
|
||||||
*
|
*
|
||||||
* @return array Returns the sorted array
|
* @return array Returns the sorted array
|
||||||
* @link http://en.wikipedia.org/wiki/Schwartzian_transform
|
* @link https://en.wikipedia.org/wiki/Schwartzian_transform
|
||||||
*/
|
*/
|
||||||
public static function stableSort(array $data, callable $sortFn)
|
public static function stableSort(array $data, callable $sortFn)
|
||||||
{
|
{
|
||||||
|
|
@ -192,7 +224,7 @@ class Utils
|
||||||
*/
|
*/
|
||||||
public static function slice($value, $start = null, $stop = null, $step = 1)
|
public static function slice($value, $start = null, $stop = null, $step = 1)
|
||||||
{
|
{
|
||||||
if (!is_array($value) && !is_string($value)) {
|
if (!is_string($value) && !self::isArray($value)) {
|
||||||
throw new \InvalidArgumentException('Expects string or array');
|
throw new \InvalidArgumentException('Expects string or array');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,7 +271,10 @@ class Utils
|
||||||
private static function sliceIndices($subject, $start, $stop, $step)
|
private static function sliceIndices($subject, $start, $stop, $step)
|
||||||
{
|
{
|
||||||
$type = gettype($subject);
|
$type = gettype($subject);
|
||||||
$len = $type == 'string' ? mb_strlen($subject, 'UTF-8') : count($subject);
|
if ($type == 'string') {
|
||||||
|
$subject = mb_str_split($subject, 1, 'UTF-8');
|
||||||
|
}
|
||||||
|
$len = count($subject);
|
||||||
list($start, $stop, $step) = self::adjustSlice($len, $start, $stop, $step);
|
list($start, $stop, $step) = self::adjustSlice($len, $start, $stop, $step);
|
||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
CHANGELOG
|
|
||||||
=========
|
|
||||||
|
|
||||||
The changelog is maintained for all Symfony contracts at the following URL:
|
|
||||||
https://github.com/symfony/contracts/blob/main/CHANGELOG.md
|
|
||||||
26
vendor/symfony/deprecation-contracts/README.md
vendored
26
vendor/symfony/deprecation-contracts/README.md
vendored
|
|
@ -1,26 +0,0 @@
|
||||||
Symfony Deprecation Contracts
|
|
||||||
=============================
|
|
||||||
|
|
||||||
A generic function and convention to trigger deprecation notices.
|
|
||||||
|
|
||||||
This package provides a single global function named `trigger_deprecation()` that triggers silenced deprecation notices.
|
|
||||||
|
|
||||||
By using a custom PHP error handler such as the one provided by the Symfony ErrorHandler component,
|
|
||||||
the triggered deprecations can be caught and logged for later discovery, both on dev and prod environments.
|
|
||||||
|
|
||||||
The function requires at least 3 arguments:
|
|
||||||
- the name of the Composer package that is triggering the deprecation
|
|
||||||
- the version of the package that introduced the deprecation
|
|
||||||
- the message of the deprecation
|
|
||||||
- more arguments can be provided: they will be inserted in the message using `printf()` formatting
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```php
|
|
||||||
trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin');
|
|
||||||
```
|
|
||||||
|
|
||||||
This will generate the following message:
|
|
||||||
`Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.`
|
|
||||||
|
|
||||||
While not recommended, the deprecation notices can be completely ignored by declaring an empty
|
|
||||||
`function trigger_deprecation() {}` in your application.
|
|
||||||
97
vendor/symfony/filesystem/CHANGELOG.md
vendored
97
vendor/symfony/filesystem/CHANGELOG.md
vendored
|
|
@ -1,97 +0,0 @@
|
||||||
CHANGELOG
|
|
||||||
=========
|
|
||||||
|
|
||||||
8.1
|
|
||||||
---
|
|
||||||
|
|
||||||
* Deprecate calling `Filesystem::mirror()` with option `copy_on_windows`, use option `follow_symlinks` instead.
|
|
||||||
|
|
||||||
7.1
|
|
||||||
---
|
|
||||||
|
|
||||||
* Add the `Filesystem::readFile()` method
|
|
||||||
|
|
||||||
7.0
|
|
||||||
---
|
|
||||||
|
|
||||||
* Add argument `$lock` to `Filesystem::appendToFile()`
|
|
||||||
|
|
||||||
5.4
|
|
||||||
---
|
|
||||||
|
|
||||||
* Add `Path` class
|
|
||||||
* Add `$lock` argument to `Filesystem::appendToFile()`
|
|
||||||
|
|
||||||
5.0.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* `Filesystem::dumpFile()` and `appendToFile()` don't accept arrays anymore
|
|
||||||
|
|
||||||
4.4.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* support for passing a `null` value to `Filesystem::isAbsolutePath()` is deprecated and will be removed in 5.0
|
|
||||||
* `tempnam()` now accepts a third argument `$suffix`.
|
|
||||||
|
|
||||||
4.3.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* support for passing arrays to `Filesystem::dumpFile()` is deprecated and will be removed in 5.0
|
|
||||||
* support for passing arrays to `Filesystem::appendToFile()` is deprecated and will be removed in 5.0
|
|
||||||
|
|
||||||
4.0.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* removed `LockHandler`
|
|
||||||
* Support for passing relative paths to `Filesystem::makePathRelative()` has been removed.
|
|
||||||
|
|
||||||
3.4.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* support for passing relative paths to `Filesystem::makePathRelative()` is deprecated and will be removed in 4.0
|
|
||||||
|
|
||||||
3.3.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* added `appendToFile()` to append contents to existing files
|
|
||||||
|
|
||||||
3.2.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* added `readlink()` as a platform independent method to read links
|
|
||||||
|
|
||||||
3.0.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* removed `$mode` argument from `Filesystem::dumpFile()`
|
|
||||||
|
|
||||||
2.8.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* added tempnam() a stream aware version of PHP's native tempnam()
|
|
||||||
|
|
||||||
2.6.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* added LockHandler
|
|
||||||
|
|
||||||
2.3.12
|
|
||||||
------
|
|
||||||
|
|
||||||
* deprecated dumpFile() file mode argument.
|
|
||||||
|
|
||||||
2.3.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* added the dumpFile() method to atomically write files
|
|
||||||
|
|
||||||
2.2.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* added a delete option for the mirror() method
|
|
||||||
|
|
||||||
2.1.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
* 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value
|
|
||||||
* created the component
|
|
||||||
23
vendor/symfony/filesystem/README.md
vendored
23
vendor/symfony/filesystem/README.md
vendored
|
|
@ -1,23 +0,0 @@
|
||||||
Filesystem Component
|
|
||||||
====================
|
|
||||||
|
|
||||||
The Filesystem component provides basic utilities for the filesystem.
|
|
||||||
|
|
||||||
Sponsor
|
|
||||||
-------
|
|
||||||
|
|
||||||
This package is looking for a [backer][1].
|
|
||||||
|
|
||||||
Help Symfony by [sponsoring][3] its development!
|
|
||||||
|
|
||||||
Resources
|
|
||||||
---------
|
|
||||||
|
|
||||||
* [Documentation](https://symfony.com/doc/current/components/filesystem.html)
|
|
||||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
|
||||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
|
||||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
|
||||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
|
||||||
|
|
||||||
[1]: https://symfony.com/backers
|
|
||||||
[3]: https://symfony.com/sponsor
|
|
||||||
12
vendor/symfony/polyfill-ctype/README.md
vendored
12
vendor/symfony/polyfill-ctype/README.md
vendored
|
|
@ -1,12 +0,0 @@
|
||||||
Symfony Polyfill / Ctype
|
|
||||||
========================
|
|
||||||
|
|
||||||
This component provides `ctype_*` functions to users who run php versions without the ctype extension.
|
|
||||||
|
|
||||||
More information can be found in the
|
|
||||||
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
|
|
||||||
|
|
||||||
License
|
|
||||||
=======
|
|
||||||
|
|
||||||
This library is released under the [MIT license](LICENSE).
|
|
||||||
28
vendor/symfony/polyfill-mbstring/Mbstring.php
vendored
28
vendor/symfony/polyfill-mbstring/Mbstring.php
vendored
|
|
@ -157,10 +157,6 @@ final class Mbstring
|
||||||
$fromEncoding = 'UTF-8';
|
$fromEncoding = 'UTF-8';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($fromEncoding === $toEncoding) {
|
|
||||||
return $s;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::iconv($fromEncoding, $toEncoding, $s);
|
return self::iconv($fromEncoding, $toEncoding, $s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -877,18 +873,20 @@ final class Mbstring
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return string|false */
|
/** @return string|false */
|
||||||
public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null)
|
public static function mb_scrub(?string $string, ?string $encoding = null): string
|
||||||
{
|
{
|
||||||
if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
|
if (null === $encoding) {
|
||||||
if (\PHP_VERSION_ID < 80000) {
|
$encoding = self::mb_internal_encoding();
|
||||||
trigger_error('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH', \E_USER_WARNING);
|
} elseif (!self::assertEncoding($encoding, 'mb_scrub(): Argument #2 ($encoding) must be a valid encoding, "%s" given')) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH');
|
return self::mb_convert_encoding((string) $string, $encoding, $encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return string|false */
|
||||||
|
public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null)
|
||||||
|
{
|
||||||
if (null === $encoding) {
|
if (null === $encoding) {
|
||||||
$encoding = self::mb_internal_encoding();
|
$encoding = self::mb_internal_encoding();
|
||||||
} elseif (!self::assertEncoding($encoding, 'mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given')) {
|
} elseif (!self::assertEncoding($encoding, 'mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given')) {
|
||||||
|
|
@ -905,6 +903,16 @@ final class Mbstring
|
||||||
throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string');
|
throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
|
||||||
|
if (\PHP_VERSION_ID < 80000) {
|
||||||
|
trigger_error('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH', \E_USER_WARNING);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH');
|
||||||
|
}
|
||||||
|
|
||||||
$paddingRequired = $length - self::mb_strlen($string, $encoding);
|
$paddingRequired = $length - self::mb_strlen($string, $encoding);
|
||||||
|
|
||||||
if ($paddingRequired < 1) {
|
if ($paddingRequired < 1) {
|
||||||
|
|
|
||||||
13
vendor/symfony/polyfill-mbstring/README.md
vendored
13
vendor/symfony/polyfill-mbstring/README.md
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
Symfony Polyfill / Mbstring
|
|
||||||
===========================
|
|
||||||
|
|
||||||
This component provides a partial, native PHP implementation for the
|
|
||||||
[Mbstring](https://php.net/mbstring) extension.
|
|
||||||
|
|
||||||
More information can be found in the
|
|
||||||
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
|
|
||||||
|
|
||||||
License
|
|
||||||
=======
|
|
||||||
|
|
||||||
This library is released under the [MIT license](LICENSE).
|
|
||||||
|
|
@ -122,7 +122,7 @@ if (!function_exists('mb_chr')) {
|
||||||
function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); }
|
function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); }
|
||||||
}
|
}
|
||||||
if (!function_exists('mb_scrub')) {
|
if (!function_exists('mb_scrub')) {
|
||||||
function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); }
|
function mb_scrub($string, $encoding = null) { return p\Mbstring::mb_scrub($string, $encoding); }
|
||||||
}
|
}
|
||||||
if (!function_exists('mb_str_split')) {
|
if (!function_exists('mb_str_split')) {
|
||||||
function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); }
|
function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); }
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ if (!function_exists('mb_chr')) {
|
||||||
function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); }
|
function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); }
|
||||||
}
|
}
|
||||||
if (!function_exists('mb_scrub')) {
|
if (!function_exists('mb_scrub')) {
|
||||||
function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); }
|
function mb_scrub(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_scrub($string, $encoding); }
|
||||||
}
|
}
|
||||||
if (!function_exists('mb_str_split')) {
|
if (!function_exists('mb_str_split')) {
|
||||||
function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); }
|
function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); }
|
||||||
|
|
|
||||||
25
vendor/symfony/polyfill-php80/README.md
vendored
25
vendor/symfony/polyfill-php80/README.md
vendored
|
|
@ -1,25 +0,0 @@
|
||||||
Symfony Polyfill / Php80
|
|
||||||
========================
|
|
||||||
|
|
||||||
This component provides features added to PHP 8.0 core:
|
|
||||||
|
|
||||||
- [`Stringable`](https://php.net/stringable) interface
|
|
||||||
- [`fdiv`](https://php.net/fdiv)
|
|
||||||
- [`ValueError`](https://php.net/valueerror) class
|
|
||||||
- [`UnhandledMatchError`](https://php.net/unhandledmatcherror) class
|
|
||||||
- `FILTER_VALIDATE_BOOL` constant
|
|
||||||
- [`get_debug_type`](https://php.net/get_debug_type)
|
|
||||||
- [`PhpToken`](https://php.net/phptoken) class
|
|
||||||
- [`preg_last_error_msg`](https://php.net/preg_last_error_msg)
|
|
||||||
- [`str_contains`](https://php.net/str_contains)
|
|
||||||
- [`str_starts_with`](https://php.net/str_starts_with)
|
|
||||||
- [`str_ends_with`](https://php.net/str_ends_with)
|
|
||||||
- [`get_resource_id`](https://php.net/get_resource_id)
|
|
||||||
|
|
||||||
More information can be found in the
|
|
||||||
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
|
|
||||||
|
|
||||||
License
|
|
||||||
=======
|
|
||||||
|
|
||||||
This library is released under the [MIT license](LICENSE).
|
|
||||||
Loading…
Reference in a new issue