Przeglądaj źródła

#4956 Xero: Update 3d party library

Alex Trofimov 1 rok temu
rodzic
commit
ac23e304a5
100 zmienionych plików z 4474 dodań i 1964 usunięć
  1. 9 0
      modules/boonex/xero/composer.json
  2. 1 0
      modules/boonex/xero/plugins/.htaccess
  3. 6 1
      modules/boonex/xero/plugins/autoload.php
  4. 102 11
      modules/boonex/xero/plugins/composer/ClassLoader.php
  5. 21 6
      modules/boonex/xero/plugins/composer/InstalledVersions.php
  6. 1 1
      modules/boonex/xero/plugins/composer/autoload_classmap.php
  7. 1 2
      modules/boonex/xero/plugins/composer/autoload_files.php
  8. 1 1
      modules/boonex/xero/plugins/composer/autoload_namespaces.php
  9. 1 1
      modules/boonex/xero/plugins/composer/autoload_psr4.php
  10. 16 34
      modules/boonex/xero/plugins/composer/autoload_real.php
  11. 4 5
      modules/boonex/xero/plugins/composer/autoload_static.php
  12. 129 224
      modules/boonex/xero/plugins/composer/installed.json
  13. 41 59
      modules/boonex/xero/plugins/composer/installed.php
  14. 2 2
      modules/boonex/xero/plugins/composer/platform_check.php
  15. 198 0
      modules/boonex/xero/plugins/firebase/php-jwt/CHANGELOG.md
  16. 244 108
      modules/boonex/xero/plugins/firebase/php-jwt/README.md
  17. 9 3
      modules/boonex/xero/plugins/firebase/php-jwt/composer.json
  18. 12 1
      modules/boonex/xero/plugins/firebase/php-jwt/src/BeforeValidException.php
  19. 274 0
      modules/boonex/xero/plugins/firebase/php-jwt/src/CachedKeySet.php
  20. 12 1
      modules/boonex/xero/plugins/firebase/php-jwt/src/ExpiredException.php
  21. 209 26
      modules/boonex/xero/plugins/firebase/php-jwt/src/JWK.php
  22. 246 190
      modules/boonex/xero/plugins/firebase/php-jwt/src/JWT.php
  23. 20 0
      modules/boonex/xero/plugins/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php
  24. 16 20
      modules/boonex/xero/plugins/firebase/php-jwt/src/Key.php
  25. 196 3
      modules/boonex/xero/plugins/guzzlehttp/guzzle/CHANGELOG.md
  26. 15 15
      modules/boonex/xero/plugins/guzzlehttp/guzzle/README.md
  27. 8 8
      modules/boonex/xero/plugins/guzzlehttp/guzzle/UPGRADING.md
  28. 41 8
      modules/boonex/xero/plugins/guzzlehttp/guzzle/composer.json
  29. 3 3
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/BodySummarizer.php
  30. 23 17
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Client.php
  31. 27 33
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Cookie/CookieJar.php
  32. 2 1
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
  33. 1 1
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
  34. 73 25
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Cookie/SetCookie.php
  35. 1 1
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Exception/BadResponseException.php
  36. 1 1
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Exception/ConnectException.php
  37. 7 23
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Exception/RequestException.php
  38. 179 38
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/CurlFactory.php
  39. 32 5
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
  40. 1 1
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/EasyHandle.php
  41. 2 2
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php
  42. 6 5
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/MockHandler.php
  43. 6 6
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/Proxy.php
  44. 69 28
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/StreamHandler.php
  45. 7 7
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/HandlerStack.php
  46. 9 8
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/MessageFormatter.php
  47. 17 9
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Middleware.php
  48. 1 1
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Pool.php
  49. 3 2
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
  50. 25 13
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/RedirectMiddleware.php
  51. 15 5
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/RequestOptions.php
  52. 8 5
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/RetryMiddleware.php
  53. 28 26
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Utils.php
  54. 4 4
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/functions.php
  55. 1 1
      modules/boonex/xero/plugins/guzzlehttp/guzzle/src/functions_include.php
  56. 0 9
      modules/boonex/xero/plugins/guzzlehttp/guzzle/vendor-bin/php-cs-fixer/composer.json
  57. 0 10
      modules/boonex/xero/plugins/guzzlehttp/guzzle/vendor-bin/phpstan/composer.json
  58. 0 9
      modules/boonex/xero/plugins/guzzlehttp/guzzle/vendor-bin/psalm/composer.json
  59. 78 1
      modules/boonex/xero/plugins/guzzlehttp/promises/CHANGELOG.md
  60. 0 13
      modules/boonex/xero/plugins/guzzlehttp/promises/Makefile
  61. 43 31
      modules/boonex/xero/plugins/guzzlehttp/promises/README.md
  62. 10 10
      modules/boonex/xero/plugins/guzzlehttp/promises/composer.json
  63. 3 1
      modules/boonex/xero/plugins/guzzlehttp/promises/src/AggregateException.php
  64. 2 0
      modules/boonex/xero/plugins/guzzlehttp/promises/src/CancellationException.php
  65. 18 25
      modules/boonex/xero/plugins/guzzlehttp/promises/src/Coroutine.php
  66. 8 13
      modules/boonex/xero/plugins/guzzlehttp/promises/src/Create.php
  67. 17 26
      modules/boonex/xero/plugins/guzzlehttp/promises/src/Each.php
  68. 26 33
      modules/boonex/xero/plugins/guzzlehttp/promises/src/EachPromise.php
  69. 19 14
      modules/boonex/xero/plugins/guzzlehttp/promises/src/FulfilledPromise.php
  70. 6 12
      modules/boonex/xero/plugins/guzzlehttp/promises/src/Is.php
  71. 31 28
      modules/boonex/xero/plugins/guzzlehttp/promises/src/Promise.php
  72. 16 22
      modules/boonex/xero/plugins/guzzlehttp/promises/src/PromiseInterface.php
  73. 3 3
      modules/boonex/xero/plugins/guzzlehttp/promises/src/PromisorInterface.php
  74. 19 15
      modules/boonex/xero/plugins/guzzlehttp/promises/src/RejectedPromise.php
  75. 8 7
      modules/boonex/xero/plugins/guzzlehttp/promises/src/RejectionException.php
  76. 10 6
      modules/boonex/xero/plugins/guzzlehttp/promises/src/TaskQueue.php
  77. 5 5
      modules/boonex/xero/plugins/guzzlehttp/promises/src/TaskQueueInterface.php
  78. 29 44
      modules/boonex/xero/plugins/guzzlehttp/promises/src/Utils.php
  79. 0 363
      modules/boonex/xero/plugins/guzzlehttp/promises/src/functions.php
  80. 0 6
      modules/boonex/xero/plugins/guzzlehttp/promises/src/functions_include.php
  81. 151 2
      modules/boonex/xero/plugins/guzzlehttp/psr7/CHANGELOG.md
  82. 79 15
      modules/boonex/xero/plugins/guzzlehttp/psr7/README.md
  83. 10 6
      modules/boonex/xero/plugins/guzzlehttp/psr7/composer.json
  84. 7 8
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/AppendStream.php
  85. 0 2
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/BufferStream.php
  86. 6 1
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/CachingStream.php
  87. 3 0
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/DroppingStream.php
  88. 22 21
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/FnStream.php
  89. 85 20
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/Header.php
  90. 4 10
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/HttpFactory.php
  91. 7 4
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/InflateStream.php
  92. 9 0
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/LazyOpenStream.php
  93. 3 0
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/LimitStream.php
  94. 22 18
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/Message.php
  95. 50 20
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/MessageTrait.php
  96. 1230 101
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/MimeType.php
  97. 25 13
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/MultipartStream.php
  98. 3 0
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/NoSeekStream.php
  99. 6 6
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/PumpStream.php
  100. 15 10
      modules/boonex/xero/plugins/guzzlehttp/psr7/src/Query.php

+ 9 - 0
modules/boonex/xero/composer.json

@@ -0,0 +1,9 @@
+{
+    "name": "unacms/xero",
+    "require": {
+        "xeroapi/xero-php-oauth2": "^8.0"
+    },
+    "config": {
+        "vendor-dir": "plugins"
+    }
+}

+ 1 - 0
modules/boonex/xero/plugins/.htaccess

@@ -0,0 +1 @@
+Deny from all

+ 6 - 1
modules/boonex/xero/plugins/autoload.php

@@ -2,6 +2,11 @@
 
 // autoload.php @generated by Composer
 
+if (PHP_VERSION_ID < 50600) {
+    echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
+    exit(1);
+}
+
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInit149f91391669b920a35008b5a484fc45::getLoader();
+return ComposerAutoloaderInit6726b59655ec5ccb037128eda4a4e3a5::getLoader();

+ 102 - 11
modules/boonex/xero/plugins/composer/ClassLoader.php

@@ -42,30 +42,75 @@ namespace Composer\Autoload;
  */
 class ClassLoader
 {
+    /** @var ?string */
     private $vendorDir;
 
     // PSR-4
+    /**
+     * @var array[]
+     * @psalm-var array<string, array<string, int>>
+     */
     private $prefixLengthsPsr4 = array();
+    /**
+     * @var array[]
+     * @psalm-var array<string, array<int, string>>
+     */
     private $prefixDirsPsr4 = array();
+    /**
+     * @var array[]
+     * @psalm-var array<string, string>
+     */
     private $fallbackDirsPsr4 = array();
 
     // PSR-0
+    /**
+     * @var array[]
+     * @psalm-var array<string, array<string, string[]>>
+     */
     private $prefixesPsr0 = array();
+    /**
+     * @var array[]
+     * @psalm-var array<string, string>
+     */
     private $fallbackDirsPsr0 = array();
 
+    /** @var bool */
     private $useIncludePath = false;
+
+    /**
+     * @var string[]
+     * @psalm-var array<string, string>
+     */
     private $classMap = array();
+
+    /** @var bool */
     private $classMapAuthoritative = false;
+
+    /**
+     * @var bool[]
+     * @psalm-var array<string, bool>
+     */
     private $missingClasses = array();
+
+    /** @var ?string */
     private $apcuPrefix;
 
+    /**
+     * @var self[]
+     */
     private static $registeredLoaders = array();
 
+    /**
+     * @param ?string $vendorDir
+     */
     public function __construct($vendorDir = null)
     {
         $this->vendorDir = $vendorDir;
     }
 
+    /**
+     * @return string[]
+     */
     public function getPrefixes()
     {
         if (!empty($this->prefixesPsr0)) {
@@ -75,28 +120,47 @@ class ClassLoader
         return array();
     }
 
+    /**
+     * @return array[]
+     * @psalm-return array<string, array<int, string>>
+     */
     public function getPrefixesPsr4()
     {
         return $this->prefixDirsPsr4;
     }
 
+    /**
+     * @return array[]
+     * @psalm-return array<string, string>
+     */
     public function getFallbackDirs()
     {
         return $this->fallbackDirsPsr0;
     }
 
+    /**
+     * @return array[]
+     * @psalm-return array<string, string>
+     */
     public function getFallbackDirsPsr4()
     {
         return $this->fallbackDirsPsr4;
     }
 
+    /**
+     * @return string[] Array of classname => path
+     * @psalm-return array<string, string>
+     */
     public function getClassMap()
     {
         return $this->classMap;
     }
 
     /**
-     * @param array $classMap Class to filename map
+     * @param string[] $classMap Class to filename map
+     * @psalm-param array<string, string> $classMap
+     *
+     * @return void
      */
     public function addClassMap(array $classMap)
     {
@@ -111,9 +175,11 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix, either
      * appending or prepending to the ones previously set for this prefix.
      *
-     * @param string       $prefix  The prefix
-     * @param array|string $paths   The PSR-0 root directories
-     * @param bool         $prepend Whether to prepend the directories
+     * @param string          $prefix  The prefix
+     * @param string[]|string $paths   The PSR-0 root directories
+     * @param bool            $prepend Whether to prepend the directories
+     *
+     * @return void
      */
     public function add($prefix, $paths, $prepend = false)
     {
@@ -156,11 +222,13 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace, either
      * appending or prepending to the ones previously set for this namespace.
      *
-     * @param string       $prefix  The prefix/namespace, with trailing '\\'
-     * @param array|string $paths   The PSR-4 base directories
-     * @param bool         $prepend Whether to prepend the directories
+     * @param string          $prefix  The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths   The PSR-4 base directories
+     * @param bool            $prepend Whether to prepend the directories
      *
      * @throws \InvalidArgumentException
+     *
+     * @return void
      */
     public function addPsr4($prefix, $paths, $prepend = false)
     {
@@ -204,8 +272,10 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix,
      * replacing any others previously set for this prefix.
      *
-     * @param string       $prefix The prefix
-     * @param array|string $paths  The PSR-0 base directories
+     * @param string          $prefix The prefix
+     * @param string[]|string $paths  The PSR-0 base directories
+     *
+     * @return void
      */
     public function set($prefix, $paths)
     {
@@ -220,10 +290,12 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace,
      * replacing any others previously set for this namespace.
      *
-     * @param string       $prefix The prefix/namespace, with trailing '\\'
-     * @param array|string $paths  The PSR-4 base directories
+     * @param string          $prefix The prefix/namespace, with trailing '\\'
+     * @param string[]|string $paths  The PSR-4 base directories
      *
      * @throws \InvalidArgumentException
+     *
+     * @return void
      */
     public function setPsr4($prefix, $paths)
     {
@@ -243,6 +315,8 @@ class ClassLoader
      * Turns on searching the include path for class files.
      *
      * @param bool $useIncludePath
+     *
+     * @return void
      */
     public function setUseIncludePath($useIncludePath)
     {
@@ -265,6 +339,8 @@ class ClassLoader
      * that have not been registered with the class map.
      *
      * @param bool $classMapAuthoritative
+     *
+     * @return void
      */
     public function setClassMapAuthoritative($classMapAuthoritative)
     {
@@ -285,6 +361,8 @@ class ClassLoader
      * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
      *
      * @param string|null $apcuPrefix
+     *
+     * @return void
      */
     public function setApcuPrefix($apcuPrefix)
     {
@@ -305,6 +383,8 @@ class ClassLoader
      * Registers this instance as an autoloader.
      *
      * @param bool $prepend Whether to prepend the autoloader or not
+     *
+     * @return void
      */
     public function register($prepend = false)
     {
@@ -324,6 +404,8 @@ class ClassLoader
 
     /**
      * Unregisters this instance as an autoloader.
+     *
+     * @return void
      */
     public function unregister()
     {
@@ -403,6 +485,11 @@ class ClassLoader
         return self::$registeredLoaders;
     }
 
+    /**
+     * @param  string       $class
+     * @param  string       $ext
+     * @return string|false
+     */
     private function findFileWithExtension($class, $ext)
     {
         // PSR-4 lookup
@@ -474,6 +561,10 @@ class ClassLoader
  * Scope isolated include.
  *
  * Prevents access to $this/self from included files.
+ *
+ * @param  string $file
+ * @return void
+ * @private
  */
 function includeFile($file)
 {

+ 21 - 6
modules/boonex/xero/plugins/composer/InstalledVersions.php

@@ -20,12 +20,27 @@ use Composer\Semver\VersionParser;
  *
  * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
  *
- * To require it's presence, you can require `composer-runtime-api ^2.0`
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ *
+ * @final
  */
 class InstalledVersions
 {
+    /**
+     * @var mixed[]|null
+     * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
+     */
     private static $installed;
+
+    /**
+     * @var bool|null
+     */
     private static $canGetVendors;
+
+    /**
+     * @var array[]
+     * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
+     */
     private static $installedByVendor = array();
 
     /**
@@ -228,7 +243,7 @@ class InstalledVersions
 
     /**
      * @return array
-     * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}
+     * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
      */
     public static function getRootPackage()
     {
@@ -242,7 +257,7 @@ class InstalledVersions
      *
      * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
      * @return array[]
-     * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}
+     * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
      */
     public static function getRawData()
     {
@@ -265,7 +280,7 @@ class InstalledVersions
      * Returns the raw data of all installed.php which are currently loaded for custom implementations
      *
      * @return array[]
-     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
+     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
      */
     public static function getAllRawData()
     {
@@ -288,7 +303,7 @@ class InstalledVersions
      * @param  array[] $data A vendor/composer/installed.php data set
      * @return void
      *
-     * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} $data
+     * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
      */
     public static function reload($data)
     {
@@ -298,7 +313,7 @@ class InstalledVersions
 
     /**
      * @return array[]
-     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
+     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
      */
     private static function getInstalled()
     {

+ 1 - 1
modules/boonex/xero/plugins/composer/autoload_classmap.php

@@ -2,7 +2,7 @@
 
 // autoload_classmap.php @generated by Composer
 
-$vendorDir = dirname(dirname(__FILE__));
+$vendorDir = dirname(__DIR__);
 $baseDir = dirname($vendorDir);
 
 return array(

+ 1 - 2
modules/boonex/xero/plugins/composer/autoload_files.php

@@ -2,12 +2,11 @@
 
 // autoload_files.php @generated by Composer
 
-$vendorDir = dirname(dirname(__FILE__));
+$vendorDir = dirname(__DIR__);
 $baseDir = dirname($vendorDir);
 
 return array(
     '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
-    'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
     '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
     '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
 );

+ 1 - 1
modules/boonex/xero/plugins/composer/autoload_namespaces.php

@@ -2,7 +2,7 @@
 
 // autoload_namespaces.php @generated by Composer
 
-$vendorDir = dirname(dirname(__FILE__));
+$vendorDir = dirname(__DIR__);
 $baseDir = dirname($vendorDir);
 
 return array(

+ 1 - 1
modules/boonex/xero/plugins/composer/autoload_psr4.php

@@ -2,7 +2,7 @@
 
 // autoload_psr4.php @generated by Composer
 
-$vendorDir = dirname(dirname(__FILE__));
+$vendorDir = dirname(__DIR__);
 $baseDir = dirname($vendorDir);
 
 return array(

+ 16 - 34
modules/boonex/xero/plugins/composer/autoload_real.php

@@ -2,7 +2,7 @@
 
 // autoload_real.php @generated by Composer
 
-class ComposerAutoloaderInit149f91391669b920a35008b5a484fc45
+class ComposerAutoloaderInit6726b59655ec5ccb037128eda4a4e3a5
 {
     private static $loader;
 
@@ -24,52 +24,34 @@ class ComposerAutoloaderInit149f91391669b920a35008b5a484fc45
 
         require __DIR__ . '/platform_check.php';
 
-        spl_autoload_register(array('ComposerAutoloaderInit149f91391669b920a35008b5a484fc45', 'loadClassLoader'), true, true);
-        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
-        spl_autoload_unregister(array('ComposerAutoloaderInit149f91391669b920a35008b5a484fc45', 'loadClassLoader'));
+        spl_autoload_register(array('ComposerAutoloaderInit6726b59655ec5ccb037128eda4a4e3a5', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
+        spl_autoload_unregister(array('ComposerAutoloaderInit6726b59655ec5ccb037128eda4a4e3a5', 'loadClassLoader'));
 
-        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
-        if ($useStaticLoader) {
-            require __DIR__ . '/autoload_static.php';
-
-            call_user_func(\Composer\Autoload\ComposerStaticInit149f91391669b920a35008b5a484fc45::getInitializer($loader));
-        } else {
-            $map = require __DIR__ . '/autoload_namespaces.php';
-            foreach ($map as $namespace => $path) {
-                $loader->set($namespace, $path);
-            }
-
-            $map = require __DIR__ . '/autoload_psr4.php';
-            foreach ($map as $namespace => $path) {
-                $loader->setPsr4($namespace, $path);
-            }
-
-            $classMap = require __DIR__ . '/autoload_classmap.php';
-            if ($classMap) {
-                $loader->addClassMap($classMap);
-            }
-        }
+        require __DIR__ . '/autoload_static.php';
+        call_user_func(\Composer\Autoload\ComposerStaticInit6726b59655ec5ccb037128eda4a4e3a5::getInitializer($loader));
 
         $loader->register(true);
 
-        if ($useStaticLoader) {
-            $includeFiles = Composer\Autoload\ComposerStaticInit149f91391669b920a35008b5a484fc45::$files;
-        } else {
-            $includeFiles = require __DIR__ . '/autoload_files.php';
-        }
+        $includeFiles = \Composer\Autoload\ComposerStaticInit6726b59655ec5ccb037128eda4a4e3a5::$files;
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequire149f91391669b920a35008b5a484fc45($fileIdentifier, $file);
+            composerRequire6726b59655ec5ccb037128eda4a4e3a5($fileIdentifier, $file);
         }
 
         return $loader;
     }
 }
 
-function composerRequire149f91391669b920a35008b5a484fc45($fileIdentifier, $file)
+/**
+ * @param string $fileIdentifier
+ * @param string $file
+ * @return void
+ */
+function composerRequire6726b59655ec5ccb037128eda4a4e3a5($fileIdentifier, $file)
 {
     if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
-        require $file;
-
         $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+
+        require $file;
     }
 }

+ 4 - 5
modules/boonex/xero/plugins/composer/autoload_static.php

@@ -4,11 +4,10 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInit149f91391669b920a35008b5a484fc45
+class ComposerStaticInit6726b59655ec5ccb037128eda4a4e3a5
 {
     public static $files = array (
         '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
-        'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
         '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
         '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
     );
@@ -82,9 +81,9 @@ class ComposerStaticInit149f91391669b920a35008b5a484fc45
     public static function getInitializer(ClassLoader $loader)
     {
         return \Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInit149f91391669b920a35008b5a484fc45::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInit149f91391669b920a35008b5a484fc45::$prefixDirsPsr4;
-            $loader->classMap = ComposerStaticInit149f91391669b920a35008b5a484fc45::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInit6726b59655ec5ccb037128eda4a4e3a5::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit6726b59655ec5ccb037128eda4a4e3a5::$prefixDirsPsr4;
+            $loader->classMap = ComposerStaticInit6726b59655ec5ccb037128eda4a4e3a5::$classMap;
 
         }, null, ClassLoader::class);
     }

+ 129 - 224
modules/boonex/xero/plugins/composer/installed.json

@@ -2,29 +2,35 @@
     "packages": [
         {
             "name": "firebase/php-jwt",
-            "version": "v5.5.1",
-            "version_normalized": "5.5.1.0",
+            "version": "v6.11.0",
+            "version_normalized": "6.11.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/firebase/php-jwt.git",
-                "reference": "83b609028194aa042ea33b5af2d41a7427de80e6"
+                "reference": "8f718f4dfc9c5d5f0c994cdfd103921b43592712"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/firebase/php-jwt/zipball/83b609028194aa042ea33b5af2d41a7427de80e6",
-                "reference": "83b609028194aa042ea33b5af2d41a7427de80e6",
+                "url": "https://api.github.com/repos/firebase/php-jwt/zipball/8f718f4dfc9c5d5f0c994cdfd103921b43592712",
+                "reference": "8f718f4dfc9c5d5f0c994cdfd103921b43592712",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": "^8.0"
             },
             "require-dev": {
-                "phpunit/phpunit": ">=4.8 <=9"
+                "guzzlehttp/guzzle": "^7.4",
+                "phpspec/prophecy-phpunit": "^2.0",
+                "phpunit/phpunit": "^9.5",
+                "psr/cache": "^2.0||^3.0",
+                "psr/http-client": "^1.0",
+                "psr/http-factory": "^1.0"
             },
             "suggest": {
+                "ext-sodium": "Support EdDSA (Ed25519) signatures",
                 "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
             },
-            "time": "2021-11-08T20:18:51+00:00",
+            "time": "2025-01-23T05:11:06+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -56,41 +62,42 @@
             ],
             "support": {
                 "issues": "https://github.com/firebase/php-jwt/issues",
-                "source": "https://github.com/firebase/php-jwt/tree/v5.5.1"
+                "source": "https://github.com/firebase/php-jwt/tree/v6.11.0"
             },
             "install-path": "../firebase/php-jwt"
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.4.0",
-            "version_normalized": "7.4.0.0",
+            "version": "7.9.3",
+            "version_normalized": "7.9.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94"
+                "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/868b3571a039f0ebc11ac8f344f4080babe2cb94",
-                "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
+                "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
-                "guzzlehttp/promises": "^1.5",
-                "guzzlehttp/psr7": "^1.8.3 || ^2.1",
+                "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+                "guzzlehttp/psr7": "^2.7.0",
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-client": "^1.0",
-                "symfony/deprecation-contracts": "^2.2"
+                "symfony/deprecation-contracts": "^2.2 || ^3.0"
             },
             "provide": {
                 "psr/http-client-implementation": "1.0"
             },
             "require-dev": {
-                "bamarni/composer-bin-plugin": "^1.4.1",
+                "bamarni/composer-bin-plugin": "^1.8.2",
                 "ext-curl": "*",
-                "php-http/client-integration-tests": "^3.0",
-                "phpunit/phpunit": "^8.5.5 || ^9.3.5",
+                "guzzle/client-integration-tests": "3.0.2",
+                "php-http/message-factory": "^1.1",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20",
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
             },
             "suggest": {
@@ -98,21 +105,22 @@
                 "ext-intl": "Required for Internationalized Domain Name (IDN) support",
                 "psr/log": "Required for using the Log middleware"
             },
-            "time": "2021-10-18T09:52:00+00:00",
+            "time": "2025-03-27T13:37:11+00:00",
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-master": "7.4-dev"
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
                 }
             },
             "installation-source": "dist",
             "autoload": {
-                "psr-4": {
-                    "GuzzleHttp\\": "src/"
-                },
                 "files": [
                     "src/functions_include.php"
-                ]
+                ],
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
@@ -169,7 +177,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.4.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.9.3"
             },
             "funding": [
                 {
@@ -189,40 +197,39 @@
         },
         {
             "name": "guzzlehttp/promises",
-            "version": "1.5.1",
-            "version_normalized": "1.5.1.0",
+            "version": "2.2.0",
+            "version_normalized": "2.2.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/promises.git",
-                "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
+                "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
-                "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c",
+                "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5"
+                "php": "^7.2.5 || ^8.0"
             },
             "require-dev": {
-                "symfony/phpunit-bridge": "^4.4 || ^5.1"
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20"
             },
-            "time": "2021-10-22T20:56:57+00:00",
+            "time": "2025-03-27T13:27:01+00:00",
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-master": "1.5-dev"
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
                 }
             },
             "installation-source": "dist",
             "autoload": {
                 "psr-4": {
                     "GuzzleHttp\\Promise\\": "src/"
-                },
-                "files": [
-                    "src/functions_include.php"
-                ]
+                }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
@@ -256,7 +263,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/promises/issues",
-                "source": "https://github.com/guzzle/promises/tree/1.5.1"
+                "source": "https://github.com/guzzle/promises/tree/2.2.0"
             },
             "funding": [
                 {
@@ -276,23 +283,23 @@
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.1.0",
-            "version_normalized": "2.1.0.0",
+            "version": "2.7.1",
+            "version_normalized": "2.7.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72"
+                "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72",
-                "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16",
+                "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-factory": "^1.0",
-                "psr/http-message": "^1.0",
+                "psr/http-message": "^1.1 || ^2.0",
                 "ralouphie/getallheaders": "^3.0"
             },
             "provide": {
@@ -300,18 +307,19 @@
                 "psr/http-message-implementation": "1.0"
             },
             "require-dev": {
-                "bamarni/composer-bin-plugin": "^1.4.1",
-                "http-interop/http-factory-tests": "^0.9",
-                "phpunit/phpunit": "^8.5.8 || ^9.3.10"
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "http-interop/http-factory-tests": "0.9.0",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20"
             },
             "suggest": {
                 "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
             },
-            "time": "2021-10-06T17:43:30+00:00",
+            "time": "2025-03-27T12:30:47+00:00",
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-master": "2.1-dev"
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
                 }
             },
             "installation-source": "dist",
@@ -374,7 +382,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.1.0"
+                "source": "https://github.com/guzzle/psr7/tree/2.7.1"
             },
             "funding": [
                 {
@@ -394,37 +402,32 @@
         },
         {
             "name": "league/oauth2-client",
-            "version": "2.6.0",
-            "version_normalized": "2.6.0.0",
+            "version": "2.8.1",
+            "version_normalized": "2.8.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/oauth2-client.git",
-                "reference": "badb01e62383430706433191b82506b6df24ad98"
+                "reference": "9df2924ca644736c835fc60466a3a60390d334f9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/badb01e62383430706433191b82506b6df24ad98",
-                "reference": "badb01e62383430706433191b82506b6df24ad98",
+                "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9",
+                "reference": "9df2924ca644736c835fc60466a3a60390d334f9",
                 "shasum": ""
             },
             "require": {
-                "guzzlehttp/guzzle": "^6.0 || ^7.0",
-                "paragonie/random_compat": "^1 || ^2 || ^9.99",
-                "php": "^5.6 || ^7.0 || ^8.0"
+                "ext-json": "*",
+                "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
+                "php": "^7.1 || >=8.0.0 <8.5.0"
             },
             "require-dev": {
-                "mockery/mockery": "^1.3",
-                "php-parallel-lint/php-parallel-lint": "^1.2",
-                "phpunit/phpunit": "^5.7 || ^6.0 || ^9.3",
-                "squizlabs/php_codesniffer": "^2.3 || ^3.0"
+                "mockery/mockery": "^1.3.5",
+                "php-parallel-lint/php-parallel-lint": "^1.4",
+                "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
+                "squizlabs/php_codesniffer": "^3.11"
             },
-            "time": "2020-10-28T02:03:40+00:00",
+            "time": "2025-02-26T04:37:30+00:00",
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-2.x": "2.0.x-dev"
-                }
-            },
             "installation-source": "dist",
             "autoload": {
                 "psr-4": {
@@ -461,128 +464,30 @@
             ],
             "support": {
                 "issues": "https://github.com/thephpleague/oauth2-client/issues",
-                "source": "https://github.com/thephpleague/oauth2-client/tree/2.6.0"
+                "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1"
             },
             "install-path": "../league/oauth2-client"
         },
-        {
-            "name": "monolog/monolog",
-            "version": "1.0.0",
-            "version_normalized": "1.0.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/Seldaek/monolog.git",
-                "reference": "433b98d4218c181bae01865901aac045585e8a1a"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/433b98d4218c181bae01865901aac045585e8a1a",
-                "reference": "433b98d4218c181bae01865901aac045585e8a1a",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.3.0"
-            },
-            "time": "2011-07-07T16:21:02+00:00",
-            "type": "library",
-            "installation-source": "dist",
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Jordi Boggiano",
-                    "email": "j.boggiano@seld.be",
-                    "homepage": "http://seld.be",
-                    "role": "Developer"
-                }
-            ],
-            "description": "Logging for PHP 5.3",
-            "homepage": "http://github.com/Seldaek/monolog",
-            "keywords": [
-                "log",
-                "logging"
-            ],
-            "support": {
-                "issues": "https://github.com/Seldaek/monolog/issues",
-                "source": "https://github.com/Seldaek/monolog/tree/1.0.0"
-            },
-            "install-path": "../monolog/monolog"
-        },
-        {
-            "name": "paragonie/random_compat",
-            "version": "v9.99.100",
-            "version_normalized": "9.99.100.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/paragonie/random_compat.git",
-                "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
-                "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">= 7"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "4.*|5.*",
-                "vimeo/psalm": "^1"
-            },
-            "suggest": {
-                "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
-            },
-            "time": "2020-10-15T08:29:30+00:00",
-            "type": "library",
-            "installation-source": "dist",
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Paragon Initiative Enterprises",
-                    "email": "security@paragonie.com",
-                    "homepage": "https://paragonie.com"
-                }
-            ],
-            "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
-            "keywords": [
-                "csprng",
-                "polyfill",
-                "pseudorandom",
-                "random"
-            ],
-            "support": {
-                "email": "info@paragonie.com",
-                "issues": "https://github.com/paragonie/random_compat/issues",
-                "source": "https://github.com/paragonie/random_compat"
-            },
-            "install-path": "../paragonie/random_compat"
-        },
         {
             "name": "psr/http-client",
-            "version": "1.0.1",
-            "version_normalized": "1.0.1.0",
+            "version": "1.0.3",
+            "version_normalized": "1.0.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-client.git",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.0 || ^8.0",
-                "psr/http-message": "^1.0"
+                "psr/http-message": "^1.0 || ^2.0"
             },
-            "time": "2020-06-29T06:28:15+00:00",
+            "time": "2023-09-23T14:17:50+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -602,7 +507,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP clients",
@@ -614,30 +519,30 @@
                 "psr-18"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-client/tree/master"
+                "source": "https://github.com/php-fig/http-client"
             },
             "install-path": "../psr/http-client"
         },
         {
             "name": "psr/http-factory",
-            "version": "1.0.1",
-            "version_normalized": "1.0.1.0",
+            "version": "1.1.0",
+            "version_normalized": "1.1.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-factory.git",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.0.0",
-                "psr/http-message": "^1.0"
+                "php": ">=7.1",
+                "psr/http-message": "^1.0 || ^2.0"
             },
-            "time": "2019-04-30T12:38:16+00:00",
+            "time": "2024-04-15T12:06:14+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -657,10 +562,10 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
-            "description": "Common interfaces for PSR-7 HTTP message factories",
+            "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
             "keywords": [
                 "factory",
                 "http",
@@ -672,33 +577,33 @@
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-factory/tree/master"
+                "source": "https://github.com/php-fig/http-factory"
             },
             "install-path": "../psr/http-factory"
         },
         {
             "name": "psr/http-message",
-            "version": "1.0.1",
-            "version_normalized": "1.0.1.0",
+            "version": "2.0",
+            "version_normalized": "2.0.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-message.git",
-                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
-                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": "^7.2 || ^8.0"
             },
-            "time": "2016-08-06T14:39:51+00:00",
+            "time": "2023-04-04T09:54:51+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "installation-source": "dist",
@@ -714,7 +619,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP messages",
@@ -728,7 +633,7 @@
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-message/tree/master"
+                "source": "https://github.com/php-fig/http-message/tree/2.0"
             },
             "install-path": "../psr/http-message"
         },
@@ -781,31 +686,31 @@
         },
         {
             "name": "symfony/deprecation-contracts",
-            "version": "v2.4.0",
-            "version_normalized": "2.4.0.0",
+            "version": "v3.5.1",
+            "version_normalized": "3.5.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/deprecation-contracts.git",
-                "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627"
+                "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627",
-                "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
+                "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.1"
+                "php": ">=8.1"
             },
-            "time": "2021-03-23T23:28:01+00:00",
+            "time": "2024-09-25T14:20:29+00:00",
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "2.4-dev"
-                },
                 "thanks": {
-                    "name": "symfony/contracts",
-                    "url": "https://github.com/symfony/contracts"
+                    "url": "https://github.com/symfony/contracts",
+                    "name": "symfony/contracts"
+                },
+                "branch-alias": {
+                    "dev-main": "3.5-dev"
                 }
             },
             "installation-source": "dist",
@@ -831,7 +736,7 @@
             "description": "A generic function and convention to trigger deprecation notices",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0"
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
             },
             "funding": [
                 {
@@ -851,24 +756,24 @@
         },
         {
             "name": "xeroapi/xero-php-oauth2",
-            "version": "2.5.4",
-            "version_normalized": "2.5.4.0",
+            "version": "8.0.0",
+            "version_normalized": "8.0.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/XeroAPI/xero-php-oauth2.git",
-                "reference": "d99c06123ab66095d0798afb34fc7ccdce08d5cb"
+                "reference": "f0635a76358aaff3e3cf7c66843dbc450a23ea4a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/XeroAPI/xero-php-oauth2/zipball/d99c06123ab66095d0798afb34fc7ccdce08d5cb",
-                "reference": "d99c06123ab66095d0798afb34fc7ccdce08d5cb",
+                "url": "https://api.github.com/repos/XeroAPI/xero-php-oauth2/zipball/f0635a76358aaff3e3cf7c66843dbc450a23ea4a",
+                "reference": "f0635a76358aaff3e3cf7c66843dbc450a23ea4a",
                 "shasum": ""
             },
             "require": {
                 "ext-curl": "*",
                 "ext-json": "*",
                 "ext-mbstring": "*",
-                "firebase/php-jwt": "^5.0",
+                "firebase/php-jwt": "^6.0",
                 "guzzlehttp/guzzle": "^6.2 | ^7.0",
                 "league/oauth2-client": "^2.5.0",
                 "php": ">=5.6"
@@ -878,7 +783,7 @@
                 "phpunit/phpunit": "^4.8",
                 "squizlabs/php_codesniffer": "~2.6"
             },
-            "time": "2021-05-05T18:46:40+00:00",
+            "time": "2025-01-31T12:27:08+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -908,7 +813,7 @@
             ],
             "support": {
                 "issues": "https://github.com/XeroAPI/xero-php-oauth2/issues",
-                "source": "https://github.com/XeroAPI/xero-php-oauth2/tree/2.5.4"
+                "source": "https://github.com/XeroAPI/xero-php-oauth2/tree/8.0.0"
             },
             "install-path": "../xeroapi/xero-php-oauth2"
         }

+ 41 - 59
modules/boonex/xero/plugins/composer/installed.php

@@ -2,84 +2,66 @@
     'root' => array(
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'type' => 'project',
+        'type' => 'library',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
-        'reference' => '0650c1097551e5c4980577e93c872f70677e755f',
-        'name' => 'xeroapi/xero-php-oauth2-starter',
+        'reference' => '38dab5619f60706c80de41ba08da829137837044',
+        'name' => 'unacms/xero',
         'dev' => true,
     ),
     'versions' => array(
         'firebase/php-jwt' => array(
-            'pretty_version' => 'v5.5.1',
-            'version' => '5.5.1.0',
+            'pretty_version' => 'v6.11.0',
+            'version' => '6.11.0.0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../firebase/php-jwt',
             'aliases' => array(),
-            'reference' => '83b609028194aa042ea33b5af2d41a7427de80e6',
+            'reference' => '8f718f4dfc9c5d5f0c994cdfd103921b43592712',
             'dev_requirement' => false,
         ),
         'guzzlehttp/guzzle' => array(
-            'pretty_version' => '7.4.0',
-            'version' => '7.4.0.0',
+            'pretty_version' => '7.9.3',
+            'version' => '7.9.3.0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
             'aliases' => array(),
-            'reference' => '868b3571a039f0ebc11ac8f344f4080babe2cb94',
+            'reference' => '7b2f29fe81dc4da0ca0ea7d42107a0845946ea77',
             'dev_requirement' => false,
         ),
         'guzzlehttp/promises' => array(
-            'pretty_version' => '1.5.1',
-            'version' => '1.5.1.0',
+            'pretty_version' => '2.2.0',
+            'version' => '2.2.0.0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../guzzlehttp/promises',
             'aliases' => array(),
-            'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da',
+            'reference' => '7c69f28996b0a6920945dd20b3857e499d9ca96c',
             'dev_requirement' => false,
         ),
         'guzzlehttp/psr7' => array(
-            'pretty_version' => '2.1.0',
-            'version' => '2.1.0.0',
+            'pretty_version' => '2.7.1',
+            'version' => '2.7.1.0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../guzzlehttp/psr7',
             'aliases' => array(),
-            'reference' => '089edd38f5b8abba6cb01567c2a8aaa47cec4c72',
+            'reference' => 'c2270caaabe631b3b44c85f99e5a04bbb8060d16',
             'dev_requirement' => false,
         ),
         'league/oauth2-client' => array(
-            'pretty_version' => '2.6.0',
-            'version' => '2.6.0.0',
+            'pretty_version' => '2.8.1',
+            'version' => '2.8.1.0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../league/oauth2-client',
             'aliases' => array(),
-            'reference' => 'badb01e62383430706433191b82506b6df24ad98',
-            'dev_requirement' => false,
-        ),
-        'monolog/monolog' => array(
-            'pretty_version' => '1.0.0',
-            'version' => '1.0.0.0',
-            'type' => 'library',
-            'install_path' => __DIR__ . '/../monolog/monolog',
-            'aliases' => array(),
-            'reference' => '433b98d4218c181bae01865901aac045585e8a1a',
-            'dev_requirement' => false,
-        ),
-        'paragonie/random_compat' => array(
-            'pretty_version' => 'v9.99.100',
-            'version' => '9.99.100.0',
-            'type' => 'library',
-            'install_path' => __DIR__ . '/../paragonie/random_compat',
-            'aliases' => array(),
-            'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
+            'reference' => '9df2924ca644736c835fc60466a3a60390d334f9',
             'dev_requirement' => false,
         ),
         'psr/http-client' => array(
-            'pretty_version' => '1.0.1',
-            'version' => '1.0.1.0',
+            'pretty_version' => '1.0.3',
+            'version' => '1.0.3.0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../psr/http-client',
             'aliases' => array(),
-            'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
+            'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
             'dev_requirement' => false,
         ),
         'psr/http-client-implementation' => array(
@@ -89,12 +71,12 @@
             ),
         ),
         'psr/http-factory' => array(
-            'pretty_version' => '1.0.1',
-            'version' => '1.0.1.0',
+            'pretty_version' => '1.1.0',
+            'version' => '1.1.0.0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../psr/http-factory',
             'aliases' => array(),
-            'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be',
+            'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
             'dev_requirement' => false,
         ),
         'psr/http-factory-implementation' => array(
@@ -104,12 +86,12 @@
             ),
         ),
         'psr/http-message' => array(
-            'pretty_version' => '1.0.1',
-            'version' => '1.0.1.0',
+            'pretty_version' => '2.0',
+            'version' => '2.0.0.0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../psr/http-message',
             'aliases' => array(),
-            'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
+            'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
             'dev_requirement' => false,
         ),
         'psr/http-message-implementation' => array(
@@ -128,30 +110,30 @@
             'dev_requirement' => false,
         ),
         'symfony/deprecation-contracts' => array(
-            'pretty_version' => 'v2.4.0',
-            'version' => '2.4.0.0',
+            'pretty_version' => 'v3.5.1',
+            'version' => '3.5.1.0',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
             'aliases' => array(),
-            'reference' => '5f38c8804a9e97d23e0c8d63341088cd8a22d627',
+            'reference' => '74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6',
             'dev_requirement' => false,
         ),
-        'xeroapi/xero-php-oauth2' => array(
-            'pretty_version' => '2.5.4',
-            'version' => '2.5.4.0',
+        'unacms/xero' => array(
+            'pretty_version' => 'dev-master',
+            'version' => 'dev-master',
             'type' => 'library',
-            'install_path' => __DIR__ . '/../xeroapi/xero-php-oauth2',
+            'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
-            'reference' => 'd99c06123ab66095d0798afb34fc7ccdce08d5cb',
+            'reference' => '38dab5619f60706c80de41ba08da829137837044',
             'dev_requirement' => false,
         ),
-        'xeroapi/xero-php-oauth2-starter' => array(
-            'pretty_version' => 'dev-master',
-            'version' => 'dev-master',
-            'type' => 'project',
-            'install_path' => __DIR__ . '/../../',
+        'xeroapi/xero-php-oauth2' => array(
+            'pretty_version' => '8.0.0',
+            'version' => '8.0.0.0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../xeroapi/xero-php-oauth2',
             'aliases' => array(),
-            'reference' => '0650c1097551e5c4980577e93c872f70677e755f',
+            'reference' => 'f0635a76358aaff3e3cf7c66843dbc450a23ea4a',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
modules/boonex/xero/plugins/composer/platform_check.php

@@ -4,8 +4,8 @@
 
 $issues = array();
 
-if (!(PHP_VERSION_ID >= 70205)) {
-    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.5". You are running ' . PHP_VERSION . '.';
+if (!(PHP_VERSION_ID >= 80100)) {
+    $issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.';
 }
 
 if ($issues) {

+ 198 - 0
modules/boonex/xero/plugins/firebase/php-jwt/CHANGELOG.md

@@ -0,0 +1,198 @@
+# Changelog
+
+## [6.11.0](https://github.com/firebase/php-jwt/compare/v6.10.2...v6.11.0) (2025-01-23)
+
+
+### Features
+
+* support octet typed JWK ([#587](https://github.com/firebase/php-jwt/issues/587)) ([7cb8a26](https://github.com/firebase/php-jwt/commit/7cb8a265fa81edf2fa6ef8098f5bc5ae573c33ad))
+
+
+### Bug Fixes
+
+* refactor constructor Key to use PHP 8.0 syntax ([#577](https://github.com/firebase/php-jwt/issues/577)) ([29fa2ce](https://github.com/firebase/php-jwt/commit/29fa2ce9e0582cd397711eec1e80c05ce20fabca))
+
+## [6.10.2](https://github.com/firebase/php-jwt/compare/v6.10.1...v6.10.2) (2024-11-24)
+
+
+### Bug Fixes
+
+* Mitigate PHP8.4 deprecation warnings ([#570](https://github.com/firebase/php-jwt/issues/570)) ([76808fa](https://github.com/firebase/php-jwt/commit/76808fa227f3811aa5cdb3bf81233714b799a5b5))
+* support php 8.4 ([#583](https://github.com/firebase/php-jwt/issues/583)) ([e3d68b0](https://github.com/firebase/php-jwt/commit/e3d68b044421339443c74199edd020e03fb1887e))
+
+## [6.10.1](https://github.com/firebase/php-jwt/compare/v6.10.0...v6.10.1) (2024-05-18)
+
+
+### Bug Fixes
+
+* ensure ratelimit expiry is set every time ([#556](https://github.com/firebase/php-jwt/issues/556)) ([09cb208](https://github.com/firebase/php-jwt/commit/09cb2081c2c3bc0f61e2f2a5fbea5741f7498648))
+* ratelimit cache expiration ([#550](https://github.com/firebase/php-jwt/issues/550)) ([dda7250](https://github.com/firebase/php-jwt/commit/dda725033585ece30ff8cae8937320d7e9f18bae))
+
+## [6.10.0](https://github.com/firebase/php-jwt/compare/v6.9.0...v6.10.0) (2023-11-28)
+
+
+### Features
+
+* allow typ header override ([#546](https://github.com/firebase/php-jwt/issues/546)) ([79cb30b](https://github.com/firebase/php-jwt/commit/79cb30b729a22931b2fbd6b53f20629a83031ba9))
+
+## [6.9.0](https://github.com/firebase/php-jwt/compare/v6.8.1...v6.9.0) (2023-10-04)
+
+
+### Features
+
+* add payload to jwt exception ([#521](https://github.com/firebase/php-jwt/issues/521)) ([175edf9](https://github.com/firebase/php-jwt/commit/175edf958bb61922ec135b2333acf5622f2238a2))
+
+## [6.8.1](https://github.com/firebase/php-jwt/compare/v6.8.0...v6.8.1) (2023-07-14)
+
+
+### Bug Fixes
+
+* accept float claims but round down to ignore them ([#492](https://github.com/firebase/php-jwt/issues/492)) ([3936842](https://github.com/firebase/php-jwt/commit/39368423beeaacb3002afa7dcb75baebf204fe7e))
+* different BeforeValidException messages for nbf and iat ([#526](https://github.com/firebase/php-jwt/issues/526)) ([0a53cf2](https://github.com/firebase/php-jwt/commit/0a53cf2986e45c2bcbf1a269f313ebf56a154ee4))
+
+## [6.8.0](https://github.com/firebase/php-jwt/compare/v6.7.0...v6.8.0) (2023-06-14)
+
+
+### Features
+
+* add support for P-384 curve ([#515](https://github.com/firebase/php-jwt/issues/515)) ([5de4323](https://github.com/firebase/php-jwt/commit/5de4323f4baf4d70bca8663bd87682a69c656c3d))
+
+
+### Bug Fixes
+
+* handle invalid http responses ([#508](https://github.com/firebase/php-jwt/issues/508)) ([91c39c7](https://github.com/firebase/php-jwt/commit/91c39c72b22fc3e1191e574089552c1f2041c718))
+
+## [6.7.0](https://github.com/firebase/php-jwt/compare/v6.6.0...v6.7.0) (2023-06-14)
+
+
+### Features
+
+* add ed25519 support to JWK (public keys) ([#452](https://github.com/firebase/php-jwt/issues/452)) ([e53979a](https://github.com/firebase/php-jwt/commit/e53979abae927de916a75b9d239cfda8ce32be2a))
+
+## [6.6.0](https://github.com/firebase/php-jwt/compare/v6.5.0...v6.6.0) (2023-06-13)
+
+
+### Features
+
+* allow get headers when decoding token ([#442](https://github.com/firebase/php-jwt/issues/442)) ([fb85f47](https://github.com/firebase/php-jwt/commit/fb85f47cfaeffdd94faf8defdf07164abcdad6c3))
+
+
+### Bug Fixes
+
+* only check iat if nbf is not used ([#493](https://github.com/firebase/php-jwt/issues/493)) ([398ccd2](https://github.com/firebase/php-jwt/commit/398ccd25ea12fa84b9e4f1085d5ff448c21ec797))
+
+## [6.5.0](https://github.com/firebase/php-jwt/compare/v6.4.0...v6.5.0) (2023-05-12)
+
+
+### Bug Fixes
+
+* allow KID of '0' ([#505](https://github.com/firebase/php-jwt/issues/505)) ([9dc46a9](https://github.com/firebase/php-jwt/commit/9dc46a9c3e5801294249cfd2554c5363c9f9326a))
+
+
+### Miscellaneous Chores
+
+* drop support for PHP 7.3 ([#495](https://github.com/firebase/php-jwt/issues/495))
+
+## [6.4.0](https://github.com/firebase/php-jwt/compare/v6.3.2...v6.4.0) (2023-02-08)
+
+
+### Features
+
+* add support for W3C ES256K ([#462](https://github.com/firebase/php-jwt/issues/462)) ([213924f](https://github.com/firebase/php-jwt/commit/213924f51936291fbbca99158b11bd4ae56c2c95))
+* improve caching by only decoding jwks when necessary ([#486](https://github.com/firebase/php-jwt/issues/486)) ([78d3ed1](https://github.com/firebase/php-jwt/commit/78d3ed1073553f7d0bbffa6c2010009a0d483d5c))
+
+## [6.3.2](https://github.com/firebase/php-jwt/compare/v6.3.1...v6.3.2) (2022-11-01)
+
+
+### Bug Fixes
+
+* check kid before using as array index ([bad1b04](https://github.com/firebase/php-jwt/commit/bad1b040d0c736bbf86814c6b5ae614f517cf7bd))
+
+## [6.3.1](https://github.com/firebase/php-jwt/compare/v6.3.0...v6.3.1) (2022-11-01)
+
+
+### Bug Fixes
+
+* casing of GET for PSR compat ([#451](https://github.com/firebase/php-jwt/issues/451)) ([60b52b7](https://github.com/firebase/php-jwt/commit/60b52b71978790eafcf3b95cfbd83db0439e8d22))
+* string interpolation format for php 8.2 ([#446](https://github.com/firebase/php-jwt/issues/446)) ([2e07d8a](https://github.com/firebase/php-jwt/commit/2e07d8a1524d12b69b110ad649f17461d068b8f2))
+
+## 6.3.0 / 2022-07-15
+
+ - Added ES256 support to JWK parsing ([#399](https://github.com/firebase/php-jwt/pull/399))
+ - Fixed potential caching error in `CachedKeySet` by caching jwks as strings ([#435](https://github.com/firebase/php-jwt/pull/435))
+
+## 6.2.0 / 2022-05-14
+
+ - Added `CachedKeySet` ([#397](https://github.com/firebase/php-jwt/pull/397))
+ - Added `$defaultAlg` parameter to `JWT::parseKey` and `JWT::parseKeySet` ([#426](https://github.com/firebase/php-jwt/pull/426)).
+
+## 6.1.0 / 2022-03-23
+
+ - Drop support for PHP 5.3, 5.4, 5.5, 5.6, and 7.0
+ - Add parameter typing and return types where possible
+
+## 6.0.0 / 2022-01-24
+
+ - **Backwards-Compatibility Breaking Changes**: See the [Release Notes](https://github.com/firebase/php-jwt/releases/tag/v6.0.0) for more information.
+ - New Key object to prevent key/algorithm type confusion (#365)
+ - Add JWK support (#273)
+ - Add ES256 support (#256)
+ - Add ES384 support (#324)
+ - Add Ed25519 support (#343)
+
+## 5.0.0 / 2017-06-26
+- Support RS384 and RS512.
+  See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!
+- Add an example for RS256 openssl.
+  See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)!
+- Detect invalid Base64 encoding in signature.
+  See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)!
+- Update `JWT::verify` to handle OpenSSL errors.
+  See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)!
+- Add `array` type hinting to `decode` method
+  See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)!
+- Add all JSON error types.
+  See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)!
+- Bugfix 'kid' not in given key list.
+  See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)!
+- Miscellaneous cleanup, documentation and test fixes.
+  See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115),
+  [#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and
+  [#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman),
+  [@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)!
+
+## 4.0.0 / 2016-07-17
+- Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)!
+- Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)!
+- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)!
+- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)!
+
+## 3.0.0 / 2015-07-22
+- Minimum PHP version updated from `5.2.0` to `5.3.0`.
+- Add `\Firebase\JWT` namespace. See
+[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to
+[@Dashron](https://github.com/Dashron)!
+- Require a non-empty key to decode and verify a JWT. See
+[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to
+[@sjones608](https://github.com/sjones608)!
+- Cleaner documentation blocks in the code. See
+[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to
+[@johanderuijter](https://github.com/johanderuijter)!
+
+## 2.2.0 / 2015-06-22
+- Add support for adding custom, optional JWT headers to `JWT::encode()`. See
+[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to
+[@mcocaro](https://github.com/mcocaro)!
+
+## 2.1.0 / 2015-05-20
+- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew
+between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)!
+- Add support for passing an object implementing the `ArrayAccess` interface for
+`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)!
+
+## 2.0.0 / 2015-04-01
+- **Note**: It is strongly recommended that you update to > v2.0.0 to address
+  known security vulnerabilities in prior versions when both symmetric and
+  asymmetric keys are used together.
+- Update signature for `JWT::decode(...)` to require an array of supported
+  algorithms to use when verifying token signatures.

+ 244 - 108
modules/boonex/xero/plugins/firebase/php-jwt/README.md

@@ -1,4 +1,4 @@
-[![Build Status](https://travis-ci.org/firebase/php-jwt.png?branch=master)](https://travis-ci.org/firebase/php-jwt)
+![Build Status](https://github.com/firebase/php-jwt/actions/workflows/tests.yml/badge.svg)
 [![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt)
 [![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt)
 [![License](https://poser.pugx.org/firebase/php-jwt/license)](https://packagist.org/packages/firebase/php-jwt)
@@ -17,7 +17,7 @@ composer require firebase/php-jwt
 ```
 
 Optionally, install the `paragonie/sodium_compat` package from composer if your
-php is < 7.2 or does not have libsodium installed:
+php env does not have libsodium installed:
 
 ```bash
 composer require paragonie/sodium_compat
@@ -29,13 +29,13 @@ Example
 use Firebase\JWT\JWT;
 use Firebase\JWT\Key;
 
-$key = "example_key";
-$payload = array(
-    "iss" => "http://example.org",
-    "aud" => "http://example.com",
-    "iat" => 1356999524,
-    "nbf" => 1357000000
-);
+$key = 'example_key';
+$payload = [
+    'iss' => 'http://example.org',
+    'aud' => 'http://example.com',
+    'iat' => 1356999524,
+    'nbf' => 1357000000
+];
 
 /**
  * IMPORTANT:
@@ -45,9 +45,13 @@ $payload = array(
  */
 $jwt = JWT::encode($payload, $key, 'HS256');
 $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
-
 print_r($decoded);
 
+// Pass a stdClass in as the third parameter to get the decoded header values
+$headers = new stdClass();
+$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers);
+print_r($headers);
+
 /*
  NOTE: This will now be an object instead of an associative array. To get
  an associative array, you will need to cast it as such:
@@ -65,6 +69,40 @@ $decoded_array = (array) $decoded;
 JWT::$leeway = 60; // $leeway in seconds
 $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
 ```
+Example encode/decode headers
+-------
+Decoding the JWT headers without verifying the JWT first is NOT recommended, and is not supported by
+this library. This is because without verifying the JWT, the header values could have been tampered with.
+Any value pulled from an unverified header should be treated as if it could be any string sent in from an
+attacker.  If this is something you still want to do in your application for whatever reason, it's possible to
+decode the header values manually simply by calling `json_decode` and `base64_decode` on the JWT
+header part:
+```php
+use Firebase\JWT\JWT;
+
+$key = 'example_key';
+$payload = [
+    'iss' => 'http://example.org',
+    'aud' => 'http://example.com',
+    'iat' => 1356999524,
+    'nbf' => 1357000000
+];
+
+$headers = [
+    'x-forwarded-for' => 'www.google.com'
+];
+
+// Encode headers in the JWT string
+$jwt = JWT::encode($payload, $key, 'HS256', null, $headers);
+
+// Decode headers from the JWT string WITHOUT validation
+// **IMPORTANT**: This operation is vulnerable to attacks, as the JWT has not yet been verified.
+// These headers could be any value sent by an attacker.
+list($headersB64, $payloadB64, $sig) = explode('.', $jwt);
+$decoded = json_decode(base64_decode($headersB64), true);
+
+print_r($decoded);
+```
 Example with RS256 (openssl)
 ----------------------------
 ```php
@@ -73,37 +111,52 @@ use Firebase\JWT\Key;
 
 $privateKey = <<<EOD
 -----BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/Rn
-vuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t0tyazyZ8JXw+KgXTxldMPEL9
-5+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB
-AoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxz
-bWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg+6wGyd/LrGVEB+Xre0J
-Nil0GReM2AHDNZUYRv+HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1
-cP59UHiHjPZYC0loEsk7s+hUmT3QHerAQJMZWC11Qrn2N+ybwwNblDKv+s5qgMQ5
-5tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDck
-ZkcvY3SK2iRIL4c9yY6hlIhs+K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwe
-k90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGOb
-qaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13k
-eUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH+N2LtujCdkQZjXAsGdm
-B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM=
+MIIEowIBAAKCAQEAuzWHNM5f+amCjQztc5QTfJfzCC5J4nuW+L/aOxZ4f8J3Frew
+M2c/dufrnmedsApb0By7WhaHlcqCh/ScAPyJhzkPYLae7bTVro3hok0zDITR8F6S
+JGL42JAEUk+ILkPI+DONM0+3vzk6Kvfe548tu4czCuqU8BGVOlnp6IqBHhAswNMM
+78pos/2z0CjPM4tbeXqSTTbNkXRboxjU29vSopcT51koWOgiTf3C7nJUoMWZHZI5
+HqnIhPAG9yv8HAgNk6CMk2CadVHDo4IxjxTzTTqo1SCSH2pooJl9O8at6kkRYsrZ
+WwsKlOFE2LUce7ObnXsYihStBUDoeBQlGG/BwQIDAQABAoIBAFtGaOqNKGwggn9k
+6yzr6GhZ6Wt2rh1Xpq8XUz514UBhPxD7dFRLpbzCrLVpzY80LbmVGJ9+1pJozyWc
+VKeCeUdNwbqkr240Oe7GTFmGjDoxU+5/HX/SJYPpC8JZ9oqgEA87iz+WQX9hVoP2
+oF6EB4ckDvXmk8FMwVZW2l2/kd5mrEVbDaXKxhvUDf52iVD+sGIlTif7mBgR99/b
+c3qiCnxCMmfYUnT2eh7Vv2LhCR/G9S6C3R4lA71rEyiU3KgsGfg0d82/XWXbegJW
+h3QbWNtQLxTuIvLq5aAryV3PfaHlPgdgK0ft6ocU2de2FagFka3nfVEyC7IUsNTK
+bq6nhAECgYEA7d/0DPOIaItl/8BWKyCuAHMss47j0wlGbBSHdJIiS55akMvnAG0M
+39y22Qqfzh1at9kBFeYeFIIU82ZLF3xOcE3z6pJZ4Dyvx4BYdXH77odo9uVK9s1l
+3T3BlMcqd1hvZLMS7dviyH79jZo4CXSHiKzc7pQ2YfK5eKxKqONeXuECgYEAyXlG
+vonaus/YTb1IBei9HwaccnQ/1HRn6MvfDjb7JJDIBhNClGPt6xRlzBbSZ73c2QEC
+6Fu9h36K/HZ2qcLd2bXiNyhIV7b6tVKk+0Psoj0dL9EbhsD1OsmE1nTPyAc9XZbb
+OPYxy+dpBCUA8/1U9+uiFoCa7mIbWcSQ+39gHuECgYAz82pQfct30aH4JiBrkNqP
+nJfRq05UY70uk5k1u0ikLTRoVS/hJu/d4E1Kv4hBMqYCavFSwAwnvHUo51lVCr/y
+xQOVYlsgnwBg2MX4+GjmIkqpSVCC8D7j/73MaWb746OIYZervQ8dbKahi2HbpsiG
+8AHcVSA/agxZr38qvWV54QKBgCD5TlDE8x18AuTGQ9FjxAAd7uD0kbXNz2vUYg9L
+hFL5tyL3aAAtUrUUw4xhd9IuysRhW/53dU+FsG2dXdJu6CxHjlyEpUJl2iZu/j15
+YnMzGWHIEX8+eWRDsw/+Ujtko/B7TinGcWPz3cYl4EAOiCeDUyXnqnO1btCEUU44
+DJ1BAoGBAJuPD27ErTSVtId90+M4zFPNibFP50KprVdc8CR37BE7r8vuGgNYXmnI
+RLnGP9p3pVgFCktORuYS2J/6t84I3+A17nEoB4xvhTLeAinAW/uTQOUmNicOP4Ek
+2MsLL2kHgL8bLTmvXV4FX+PXphrDKg1XxzOYn0otuoqdAQrkK4og
 -----END RSA PRIVATE KEY-----
 EOD;
 
 $publicKey = <<<EOD
 -----BEGIN PUBLIC KEY-----
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H
-4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t
-0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4
-ehde/zUxo6UvS7UrBQIDAQAB
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzWHNM5f+amCjQztc5QT
+fJfzCC5J4nuW+L/aOxZ4f8J3FrewM2c/dufrnmedsApb0By7WhaHlcqCh/ScAPyJ
+hzkPYLae7bTVro3hok0zDITR8F6SJGL42JAEUk+ILkPI+DONM0+3vzk6Kvfe548t
+u4czCuqU8BGVOlnp6IqBHhAswNMM78pos/2z0CjPM4tbeXqSTTbNkXRboxjU29vS
+opcT51koWOgiTf3C7nJUoMWZHZI5HqnIhPAG9yv8HAgNk6CMk2CadVHDo4IxjxTz
+TTqo1SCSH2pooJl9O8at6kkRYsrZWwsKlOFE2LUce7ObnXsYihStBUDoeBQlGG/B
+wQIDAQAB
 -----END PUBLIC KEY-----
 EOD;
 
-$payload = array(
-    "iss" => "example.org",
-    "aud" => "example.com",
-    "iat" => 1356999524,
-    "nbf" => 1357000000
-);
+$payload = [
+    'iss' => 'example.org',
+    'aud' => 'example.com',
+    'iat' => 1356999524,
+    'nbf' => 1357000000
+];
 
 $jwt = JWT::encode($payload, $privateKey, 'RS256');
 echo "Encode:\n" . print_r($jwt, true) . "\n";
@@ -139,12 +192,12 @@ $privateKey = openssl_pkey_get_private(
     $passphrase
 );
 
-$payload = array(
-    "iss" => "example.org",
-    "aud" => "example.com",
-    "iat" => 1356999524,
-    "nbf" => 1357000000
-);
+$payload = [
+    'iss' => 'example.org',
+    'aud' => 'example.com',
+    'iat' => 1356999524,
+    'nbf' => 1357000000
+];
 
 $jwt = JWT::encode($payload, $privateKey, 'RS256');
 echo "Encode:\n" . print_r($jwt, true) . "\n";
@@ -173,12 +226,12 @@ $privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));
 
 $publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
 
-$payload = array(
-    "iss" => "example.org",
-    "aud" => "example.com",
-    "iat" => 1356999524,
-    "nbf" => 1357000000
-);
+$payload = [
+    'iss' => 'example.org',
+    'aud' => 'example.com',
+    'iat' => 1356999524,
+    'nbf' => 1357000000
+];
 
 $jwt = JWT::encode($payload, $privateKey, 'EdDSA');
 echo "Encode:\n" . print_r($jwt, true) . "\n";
@@ -187,6 +240,44 @@ $decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA'));
 echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
 ````
 
+Example with multiple keys
+--------------------------
+```php
+use Firebase\JWT\JWT;
+use Firebase\JWT\Key;
+
+// Example RSA keys from previous example
+// $privateKey1 = '...';
+// $publicKey1 = '...';
+
+// Example EdDSA keys from previous example
+// $privateKey2 = '...';
+// $publicKey2 = '...';
+
+$payload = [
+    'iss' => 'example.org',
+    'aud' => 'example.com',
+    'iat' => 1356999524,
+    'nbf' => 1357000000
+];
+
+$jwt1 = JWT::encode($payload, $privateKey1, 'RS256', 'kid1');
+$jwt2 = JWT::encode($payload, $privateKey2, 'EdDSA', 'kid2');
+echo "Encode 1:\n" . print_r($jwt1, true) . "\n";
+echo "Encode 2:\n" . print_r($jwt2, true) . "\n";
+
+$keys = [
+    'kid1' => new Key($publicKey1, 'RS256'),
+    'kid2' => new Key($publicKey2, 'EdDSA'),
+];
+
+$decoded1 = JWT::decode($jwt1, $keys);
+$decoded2 = JWT::decode($jwt2, $keys);
+
+echo "Decode 1:\n" . print_r((array) $decoded1, true) . "\n";
+echo "Decode 2:\n" . print_r((array) $decoded2, true) . "\n";
+```
+
 Using JWKs
 ----------
 
@@ -198,72 +289,117 @@ use Firebase\JWT\JWT;
 // this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
 $jwks = ['keys' => []];
 
-// JWK::parseKeySet($jwks) returns an associative array of **kid** to private
-// key. Pass this as the second parameter to JWT::decode.
-// NOTE: The deprecated $supportedAlgorithm must be supplied when parsing from JWK.
-JWT::decode($payload, JWK::parseKeySet($jwks), $supportedAlgorithm);
+// JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key
+// objects. Pass this as the second parameter to JWT::decode.
+JWT::decode($payload, JWK::parseKeySet($jwks));
+```
+
+Using Cached Key Sets
+---------------------
+
+The `CachedKeySet` class can be used to fetch and cache JWKS (JSON Web Key Sets) from a public URI.
+This has the following advantages:
+
+1. The results are cached for performance.
+2. If an unrecognized key is requested, the cache is refreshed, to accomodate for key rotation.
+3. If rate limiting is enabled, the JWKS URI will not make more than 10 requests a second.
+
+```php
+use Firebase\JWT\CachedKeySet;
+use Firebase\JWT\JWT;
+
+// The URI for the JWKS you wish to cache the results from
+$jwksUri = 'https://www.gstatic.com/iap/verify/public_key-jwk';
+
+// Create an HTTP client (can be any PSR-7 compatible HTTP client)
+$httpClient = new GuzzleHttp\Client();
+
+// Create an HTTP request factory (can be any PSR-17 compatible HTTP request factory)
+$httpFactory = new GuzzleHttp\Psr\HttpFactory();
+
+// Create a cache item pool (can be any PSR-6 compatible cache item pool)
+$cacheItemPool = Phpfastcache\CacheManager::getInstance('files');
+
+$keySet = new CachedKeySet(
+    $jwksUri,
+    $httpClient,
+    $httpFactory,
+    $cacheItemPool,
+    null, // $expiresAfter int seconds to set the JWKS to expire
+    true  // $rateLimit    true to enable rate limit of 10 RPS on lookup of invalid keys
+);
+
+$jwt = 'eyJhbGci...'; // Some JWT signed by a key from the $jwkUri above
+$decoded = JWT::decode($jwt, $keySet);
 ```
 
-Changelog
----------
-
-#### 5.0.0 / 2017-06-26
-- Support RS384 and RS512.
-  See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!
-- Add an example for RS256 openssl.
-  See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)!
-- Detect invalid Base64 encoding in signature.
-  See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)!
-- Update `JWT::verify` to handle OpenSSL errors.
-  See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)!
-- Add `array` type hinting to `decode` method
-  See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)!
-- Add all JSON error types.
-  See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)!
-- Bugfix 'kid' not in given key list.
-  See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)!
-- Miscellaneous cleanup, documentation and test fixes.
-  See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115),
-  [#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and
-  [#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman),
-  [@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)!
-
-#### 4.0.0 / 2016-07-17
-- Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)!
-- Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)!
-- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)!
-- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)!
-
-#### 3.0.0 / 2015-07-22
-- Minimum PHP version updated from `5.2.0` to `5.3.0`.
-- Add `\Firebase\JWT` namespace. See
-[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to
-[@Dashron](https://github.com/Dashron)!
-- Require a non-empty key to decode and verify a JWT. See
-[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to
-[@sjones608](https://github.com/sjones608)!
-- Cleaner documentation blocks in the code. See
-[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to
-[@johanderuijter](https://github.com/johanderuijter)!
-
-#### 2.2.0 / 2015-06-22
-- Add support for adding custom, optional JWT headers to `JWT::encode()`. See
-[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to
-[@mcocaro](https://github.com/mcocaro)!
-
-#### 2.1.0 / 2015-05-20
-- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew
-between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)!
-- Add support for passing an object implementing the `ArrayAccess` interface for
-`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)!
-
-#### 2.0.0 / 2015-04-01
-- **Note**: It is strongly recommended that you update to > v2.0.0 to address
-  known security vulnerabilities in prior versions when both symmetric and
-  asymmetric keys are used together.
-- Update signature for `JWT::decode(...)` to require an array of supported
-  algorithms to use when verifying token signatures.
+Miscellaneous
+-------------
+
+#### Exception Handling
+
+When a call to `JWT::decode` is invalid, it will throw one of the following exceptions:
 
+```php
+use Firebase\JWT\JWT;
+use Firebase\JWT\SignatureInvalidException;
+use Firebase\JWT\BeforeValidException;
+use Firebase\JWT\ExpiredException;
+use DomainException;
+use InvalidArgumentException;
+use UnexpectedValueException;
+
+try {
+    $decoded = JWT::decode($payload, $keys);
+} catch (InvalidArgumentException $e) {
+    // provided key/key-array is empty or malformed.
+} catch (DomainException $e) {
+    // provided algorithm is unsupported OR
+    // provided key is invalid OR
+    // unknown error thrown in openSSL or libsodium OR
+    // libsodium is required but not available.
+} catch (SignatureInvalidException $e) {
+    // provided JWT signature verification failed.
+} catch (BeforeValidException $e) {
+    // provided JWT is trying to be used before "nbf" claim OR
+    // provided JWT is trying to be used before "iat" claim.
+} catch (ExpiredException $e) {
+    // provided JWT is trying to be used after "exp" claim.
+} catch (UnexpectedValueException $e) {
+    // provided JWT is malformed OR
+    // provided JWT is missing an algorithm / using an unsupported algorithm OR
+    // provided JWT algorithm does not match provided key OR
+    // provided key ID in key/key-array is empty or invalid.
+}
+```
+
+All exceptions in the `Firebase\JWT` namespace extend `UnexpectedValueException`, and can be simplified
+like this:
+
+```php
+use Firebase\JWT\JWT;
+use UnexpectedValueException;
+try {
+    $decoded = JWT::decode($payload, $keys);
+} catch (LogicException $e) {
+    // errors having to do with environmental setup or malformed JWT Keys
+} catch (UnexpectedValueException $e) {
+    // errors having to do with JWT signature and claims
+}
+```
+
+#### Casting to array
+
+The return value of `JWT::decode` is the generic PHP object `stdClass`. If you'd like to handle with arrays
+instead, you can do the following:
+
+```php
+// return type is stdClass
+$decoded = JWT::decode($payload, $keys);
+
+// cast to array
+$decoded = json_decode(json_encode($decoded), true);
+```
 
 Tests
 -----

+ 9 - 3
modules/boonex/xero/plugins/firebase/php-jwt/composer.json

@@ -20,10 +20,11 @@
     ],
     "license": "BSD-3-Clause",
     "require": {
-        "php": ">=5.3.0"
+        "php": "^8.0"
     },
     "suggest": {
-        "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
+        "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present",
+        "ext-sodium": "Support EdDSA (Ed25519) signatures"
     },
     "autoload": {
         "psr-4": {
@@ -31,6 +32,11 @@
         }
     },
     "require-dev": {
-        "phpunit/phpunit": ">=4.8 <=9"
+        "guzzlehttp/guzzle": "^7.4",
+        "phpspec/prophecy-phpunit": "^2.0",
+        "phpunit/phpunit": "^9.5",
+        "psr/cache": "^2.0||^3.0",
+        "psr/http-client": "^1.0",
+        "psr/http-factory": "^1.0"
     }
 }

+ 12 - 1
modules/boonex/xero/plugins/firebase/php-jwt/src/BeforeValidException.php

@@ -2,6 +2,17 @@
 
 namespace Firebase\JWT;
 
-class BeforeValidException extends \UnexpectedValueException
+class BeforeValidException extends \UnexpectedValueException implements JWTExceptionWithPayloadInterface
 {
+    private object $payload;
+
+    public function setPayload(object $payload): void
+    {
+        $this->payload = $payload;
+    }
+
+    public function getPayload(): object
+    {
+        return $this->payload;
+    }
 }

+ 274 - 0
modules/boonex/xero/plugins/firebase/php-jwt/src/CachedKeySet.php

@@ -0,0 +1,274 @@
+<?php
+
+namespace Firebase\JWT;
+
+use ArrayAccess;
+use InvalidArgumentException;
+use LogicException;
+use OutOfBoundsException;
+use Psr\Cache\CacheItemInterface;
+use Psr\Cache\CacheItemPoolInterface;
+use Psr\Http\Client\ClientInterface;
+use Psr\Http\Message\RequestFactoryInterface;
+use RuntimeException;
+use UnexpectedValueException;
+
+/**
+ * @implements ArrayAccess<string, Key>
+ */
+class CachedKeySet implements ArrayAccess
+{
+    /**
+     * @var string
+     */
+    private $jwksUri;
+    /**
+     * @var ClientInterface
+     */
+    private $httpClient;
+    /**
+     * @var RequestFactoryInterface
+     */
+    private $httpFactory;
+    /**
+     * @var CacheItemPoolInterface
+     */
+    private $cache;
+    /**
+     * @var ?int
+     */
+    private $expiresAfter;
+    /**
+     * @var ?CacheItemInterface
+     */
+    private $cacheItem;
+    /**
+     * @var array<string, array<mixed>>
+     */
+    private $keySet;
+    /**
+     * @var string
+     */
+    private $cacheKey;
+    /**
+     * @var string
+     */
+    private $cacheKeyPrefix = 'jwks';
+    /**
+     * @var int
+     */
+    private $maxKeyLength = 64;
+    /**
+     * @var bool
+     */
+    private $rateLimit;
+    /**
+     * @var string
+     */
+    private $rateLimitCacheKey;
+    /**
+     * @var int
+     */
+    private $maxCallsPerMinute = 10;
+    /**
+     * @var string|null
+     */
+    private $defaultAlg;
+
+    public function __construct(
+        string $jwksUri,
+        ClientInterface $httpClient,
+        RequestFactoryInterface $httpFactory,
+        CacheItemPoolInterface $cache,
+        ?int $expiresAfter = null,
+        bool $rateLimit = false,
+        ?string $defaultAlg = null
+    ) {
+        $this->jwksUri = $jwksUri;
+        $this->httpClient = $httpClient;
+        $this->httpFactory = $httpFactory;
+        $this->cache = $cache;
+        $this->expiresAfter = $expiresAfter;
+        $this->rateLimit = $rateLimit;
+        $this->defaultAlg = $defaultAlg;
+        $this->setCacheKeys();
+    }
+
+    /**
+     * @param string $keyId
+     * @return Key
+     */
+    public function offsetGet($keyId): Key
+    {
+        if (!$this->keyIdExists($keyId)) {
+            throw new OutOfBoundsException('Key ID not found');
+        }
+        return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg);
+    }
+
+    /**
+     * @param string $keyId
+     * @return bool
+     */
+    public function offsetExists($keyId): bool
+    {
+        return $this->keyIdExists($keyId);
+    }
+
+    /**
+     * @param string $offset
+     * @param Key $value
+     */
+    public function offsetSet($offset, $value): void
+    {
+        throw new LogicException('Method not implemented');
+    }
+
+    /**
+     * @param string $offset
+     */
+    public function offsetUnset($offset): void
+    {
+        throw new LogicException('Method not implemented');
+    }
+
+    /**
+     * @return array<mixed>
+     */
+    private function formatJwksForCache(string $jwks): array
+    {
+        $jwks = json_decode($jwks, true);
+
+        if (!isset($jwks['keys'])) {
+            throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
+        }
+
+        if (empty($jwks['keys'])) {
+            throw new InvalidArgumentException('JWK Set did not contain any keys');
+        }
+
+        $keys = [];
+        foreach ($jwks['keys'] as $k => $v) {
+            $kid = isset($v['kid']) ? $v['kid'] : $k;
+            $keys[(string) $kid] = $v;
+        }
+
+        return $keys;
+    }
+
+    private function keyIdExists(string $keyId): bool
+    {
+        if (null === $this->keySet) {
+            $item = $this->getCacheItem();
+            // Try to load keys from cache
+            if ($item->isHit()) {
+                // item found! retrieve it
+                $this->keySet = $item->get();
+                // If the cached item is a string, the JWKS response was cached (previous behavior).
+                // Parse this into expected format array<kid, jwk> instead.
+                if (\is_string($this->keySet)) {
+                    $this->keySet = $this->formatJwksForCache($this->keySet);
+                }
+            }
+        }
+
+        if (!isset($this->keySet[$keyId])) {
+            if ($this->rateLimitExceeded()) {
+                return false;
+            }
+            $request = $this->httpFactory->createRequest('GET', $this->jwksUri);
+            $jwksResponse = $this->httpClient->sendRequest($request);
+            if ($jwksResponse->getStatusCode() !== 200) {
+                throw new UnexpectedValueException(
+                    \sprintf('HTTP Error: %d %s for URI "%s"',
+                        $jwksResponse->getStatusCode(),
+                        $jwksResponse->getReasonPhrase(),
+                        $this->jwksUri,
+                    ),
+                    $jwksResponse->getStatusCode()
+                );
+            }
+            $this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody());
+
+            if (!isset($this->keySet[$keyId])) {
+                return false;
+            }
+
+            $item = $this->getCacheItem();
+            $item->set($this->keySet);
+            if ($this->expiresAfter) {
+                $item->expiresAfter($this->expiresAfter);
+            }
+            $this->cache->save($item);
+        }
+
+        return true;
+    }
+
+    private function rateLimitExceeded(): bool
+    {
+        if (!$this->rateLimit) {
+            return false;
+        }
+
+        $cacheItem = $this->cache->getItem($this->rateLimitCacheKey);
+
+        $cacheItemData = [];
+        if ($cacheItem->isHit() && \is_array($data = $cacheItem->get())) {
+            $cacheItemData = $data;
+        }
+
+        $callsPerMinute = $cacheItemData['callsPerMinute'] ?? 0;
+        $expiry = $cacheItemData['expiry'] ?? new \DateTime('+60 seconds', new \DateTimeZone('UTC'));
+
+        if (++$callsPerMinute > $this->maxCallsPerMinute) {
+            return true;
+        }
+
+        $cacheItem->set(['expiry' => $expiry, 'callsPerMinute' => $callsPerMinute]);
+        $cacheItem->expiresAt($expiry);
+        $this->cache->save($cacheItem);
+        return false;
+    }
+
+    private function getCacheItem(): CacheItemInterface
+    {
+        if (\is_null($this->cacheItem)) {
+            $this->cacheItem = $this->cache->getItem($this->cacheKey);
+        }
+
+        return $this->cacheItem;
+    }
+
+    private function setCacheKeys(): void
+    {
+        if (empty($this->jwksUri)) {
+            throw new RuntimeException('JWKS URI is empty');
+        }
+
+        // ensure we do not have illegal characters
+        $key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $this->jwksUri);
+
+        // add prefix
+        $key = $this->cacheKeyPrefix . $key;
+
+        // Hash keys if they exceed $maxKeyLength of 64
+        if (\strlen($key) > $this->maxKeyLength) {
+            $key = substr(hash('sha256', $key), 0, $this->maxKeyLength);
+        }
+
+        $this->cacheKey = $key;
+
+        if ($this->rateLimit) {
+            // add prefix
+            $rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key;
+
+            // Hash keys if they exceed $maxKeyLength of 64
+            if (\strlen($rateLimitKey) > $this->maxKeyLength) {
+                $rateLimitKey = substr(hash('sha256', $rateLimitKey), 0, $this->maxKeyLength);
+            }
+
+            $this->rateLimitCacheKey = $rateLimitKey;
+        }
+    }
+}

+ 12 - 1
modules/boonex/xero/plugins/firebase/php-jwt/src/ExpiredException.php

@@ -2,6 +2,17 @@
 
 namespace Firebase\JWT;
 
-class ExpiredException extends \UnexpectedValueException
+class ExpiredException extends \UnexpectedValueException implements JWTExceptionWithPayloadInterface
 {
+    private object $payload;
+
+    public function setPayload(object $payload): void
+    {
+        $this->payload = $payload;
+    }
+
+    public function getPayload(): object
+    {
+        return $this->payload;
+    }
 }

+ 209 - 26
modules/boonex/xero/plugins/firebase/php-jwt/src/JWK.php

@@ -20,12 +20,31 @@ use UnexpectedValueException;
  */
 class JWK
 {
+    private const OID = '1.2.840.10045.2.1';
+    private const ASN1_OBJECT_IDENTIFIER = 0x06;
+    private const ASN1_SEQUENCE = 0x10; // also defined in JWT
+    private const ASN1_BIT_STRING = 0x03;
+    private const EC_CURVES = [
+        'P-256' => '1.2.840.10045.3.1.7', // Len: 64
+        'secp256k1' => '1.3.132.0.10', // Len: 64
+        'P-384' => '1.3.132.0.34', // Len: 96
+        // 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
+    ];
+
+    // For keys with "kty" equal to "OKP" (Octet Key Pair), the "crv" parameter must contain the key subtype.
+    // This library supports the following subtypes:
+    private const OKP_SUBTYPES = [
+        'Ed25519' => true, // RFC 8037
+    ];
+
     /**
      * Parse a set of JWK keys
      *
-     * @param array $jwks The JSON Web Key Set as an associative array
+     * @param array<mixed> $jwks The JSON Web Key Set as an associative array
+     * @param string       $defaultAlg The algorithm for the Key object if "alg" is not set in the
+     *                                 JSON Web Key Set
      *
-     * @return array An associative array that represents the set of keys
+     * @return array<string, Key> An associative array of key IDs (kid) to Key objects
      *
      * @throws InvalidArgumentException     Provided JWK Set is empty
      * @throws UnexpectedValueException     Provided JWK Set was invalid
@@ -33,21 +52,22 @@ class JWK
      *
      * @uses parseKey
      */
-    public static function parseKeySet(array $jwks)
+    public static function parseKeySet(array $jwks, ?string $defaultAlg = null): array
     {
-        $keys = array();
+        $keys = [];
 
         if (!isset($jwks['keys'])) {
             throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
         }
+
         if (empty($jwks['keys'])) {
             throw new InvalidArgumentException('JWK Set did not contain any keys');
         }
 
         foreach ($jwks['keys'] as $k => $v) {
             $kid = isset($v['kid']) ? $v['kid'] : $k;
-            if ($key = self::parseKey($v)) {
-                $keys[$kid] = $key;
+            if ($key = self::parseKey($v, $defaultAlg)) {
+                $keys[(string) $kid] = $key;
             }
         }
 
@@ -61,9 +81,11 @@ class JWK
     /**
      * Parse a JWK key
      *
-     * @param array $jwk An individual JWK
+     * @param array<mixed> $jwk An individual JWK
+     * @param string       $defaultAlg The algorithm for the Key object if "alg" is not set in the
+     *                                 JSON Web Key Set
      *
-     * @return resource|array An associative array that represents the key
+     * @return Key The key object for the JWK
      *
      * @throws InvalidArgumentException     Provided JWK is empty
      * @throws UnexpectedValueException     Provided JWK was invalid
@@ -71,15 +93,27 @@ class JWK
      *
      * @uses createPemFromModulusAndExponent
      */
-    public static function parseKey(array $jwk)
+    public static function parseKey(array $jwk, ?string $defaultAlg = null): ?Key
     {
         if (empty($jwk)) {
             throw new InvalidArgumentException('JWK must not be empty');
         }
+
         if (!isset($jwk['kty'])) {
             throw new UnexpectedValueException('JWK must contain a "kty" parameter');
         }
 
+        if (!isset($jwk['alg'])) {
+            if (\is_null($defaultAlg)) {
+                // The "alg" parameter is optional in a KTY, but an algorithm is required
+                // for parsing in this library. Use the $defaultAlg parameter when parsing the
+                // key set in order to prevent this error.
+                // @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
+                throw new UnexpectedValueException('JWK must contain an "alg" parameter');
+            }
+            $jwk['alg'] = $defaultAlg;
+        }
+
         switch ($jwk['kty']) {
             case 'RSA':
                 if (!empty($jwk['d'])) {
@@ -96,11 +130,98 @@ class JWK
                         'OpenSSL error: ' . \openssl_error_string()
                     );
                 }
-                return $publicKey;
+                return new Key($publicKey, $jwk['alg']);
+            case 'EC':
+                if (isset($jwk['d'])) {
+                    // The key is actually a private key
+                    throw new UnexpectedValueException('Key data must be for a public key');
+                }
+
+                if (empty($jwk['crv'])) {
+                    throw new UnexpectedValueException('crv not set');
+                }
+
+                if (!isset(self::EC_CURVES[$jwk['crv']])) {
+                    throw new DomainException('Unrecognised or unsupported EC curve');
+                }
+
+                if (empty($jwk['x']) || empty($jwk['y'])) {
+                    throw new UnexpectedValueException('x and y not set');
+                }
+
+                $publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
+                return new Key($publicKey, $jwk['alg']);
+            case 'OKP':
+                if (isset($jwk['d'])) {
+                    // The key is actually a private key
+                    throw new UnexpectedValueException('Key data must be for a public key');
+                }
+
+                if (!isset($jwk['crv'])) {
+                    throw new UnexpectedValueException('crv not set');
+                }
+
+                if (empty(self::OKP_SUBTYPES[$jwk['crv']])) {
+                    throw new DomainException('Unrecognised or unsupported OKP key subtype');
+                }
+
+                if (empty($jwk['x'])) {
+                    throw new UnexpectedValueException('x not set');
+                }
+
+                // This library works internally with EdDSA keys (Ed25519) encoded in standard base64.
+                $publicKey = JWT::convertBase64urlToBase64($jwk['x']);
+                return new Key($publicKey, $jwk['alg']);
+            case 'oct':
+                if (!isset($jwk['k'])) {
+                    throw new UnexpectedValueException('k not set');
+                }
+
+                return new Key(JWT::urlsafeB64Decode($jwk['k']), $jwk['alg']);
             default:
-                // Currently only RSA is supported
                 break;
         }
+
+        return null;
+    }
+
+    /**
+     * Converts the EC JWK values to pem format.
+     *
+     * @param   string  $crv The EC curve (only P-256 & P-384 is supported)
+     * @param   string  $x   The EC x-coordinate
+     * @param   string  $y   The EC y-coordinate
+     *
+     * @return  string
+     */
+    private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
+    {
+        $pem =
+            self::encodeDER(
+                self::ASN1_SEQUENCE,
+                self::encodeDER(
+                    self::ASN1_SEQUENCE,
+                    self::encodeDER(
+                        self::ASN1_OBJECT_IDENTIFIER,
+                        self::encodeOID(self::OID)
+                    )
+                    . self::encodeDER(
+                        self::ASN1_OBJECT_IDENTIFIER,
+                        self::encodeOID(self::EC_CURVES[$crv])
+                    )
+                ) .
+                self::encodeDER(
+                    self::ASN1_BIT_STRING,
+                    \chr(0x00) . \chr(0x04)
+                    . JWT::urlsafeB64Decode($x)
+                    . JWT::urlsafeB64Decode($y)
+                )
+            );
+
+        return \sprintf(
+            "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
+            wordwrap(base64_encode($pem), 64, "\n", true)
+        );
     }
 
     /**
@@ -113,22 +234,22 @@ class JWK
      *
      * @uses encodeLength
      */
-    private static function createPemFromModulusAndExponent($n, $e)
-    {
-        $modulus = JWT::urlsafeB64Decode($n);
-        $publicExponent = JWT::urlsafeB64Decode($e);
+    private static function createPemFromModulusAndExponent(
+        string $n,
+        string $e
+    ): string {
+        $mod = JWT::urlsafeB64Decode($n);
+        $exp = JWT::urlsafeB64Decode($e);
 
-        $components = array(
-            'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus),
-            'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent)
-        );
+        $modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
+        $publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
 
         $rsaPublicKey = \pack(
             'Ca*a*a*',
             48,
-            self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
-            $components['modulus'],
-            $components['publicExponent']
+            self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
+            $modulus,
+            $publicExponent
         );
 
         // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
@@ -143,11 +264,9 @@ class JWK
             $rsaOID . $rsaPublicKey
         );
 
-        $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
+        return "-----BEGIN PUBLIC KEY-----\r\n" .
             \chunk_split(\base64_encode($rsaPublicKey), 64) .
             '-----END PUBLIC KEY-----';
-
-        return $rsaPublicKey;
     }
 
     /**
@@ -159,7 +278,7 @@ class JWK
      * @param int $length
      * @return string
      */
-    private static function encodeLength($length)
+    private static function encodeLength(int $length): string
     {
         if ($length <= 0x7F) {
             return \chr($length);
@@ -169,4 +288,68 @@ class JWK
 
         return \pack('Ca*', 0x80 | \strlen($temp), $temp);
     }
+
+    /**
+     * Encodes a value into a DER object.
+     * Also defined in Firebase\JWT\JWT
+     *
+     * @param   int     $type DER tag
+     * @param   string  $value the value to encode
+     * @return  string  the encoded object
+     */
+    private static function encodeDER(int $type, string $value): string
+    {
+        $tag_header = 0;
+        if ($type === self::ASN1_SEQUENCE) {
+            $tag_header |= 0x20;
+        }
+
+        // Type
+        $der = \chr($tag_header | $type);
+
+        // Length
+        $der .= \chr(\strlen($value));
+
+        return $der . $value;
+    }
+
+    /**
+     * Encodes a string into a DER-encoded OID.
+     *
+     * @param   string $oid the OID string
+     * @return  string the binary DER-encoded OID
+     */
+    private static function encodeOID(string $oid): string
+    {
+        $octets = explode('.', $oid);
+
+        // Get the first octet
+        $first = (int) array_shift($octets);
+        $second = (int) array_shift($octets);
+        $oid = \chr($first * 40 + $second);
+
+        // Iterate over subsequent octets
+        foreach ($octets as $octet) {
+            if ($octet == 0) {
+                $oid .= \chr(0x00);
+                continue;
+            }
+            $bin = '';
+
+            while ($octet) {
+                $bin .= \chr(0x80 | ($octet & 0x7f));
+                $octet >>= 7;
+            }
+            $bin[0] = $bin[0] & \chr(0x7f);
+
+            // Convert to big endian if necessary
+            if (pack('V', 65534) == pack('L', 65534)) {
+                $oid .= strrev($bin);
+            } else {
+                $oid .= $bin;
+            }
+        }
+
+        return $oid;
+    }
 }

+ 246 - 190
modules/boonex/xero/plugins/firebase/php-jwt/src/JWT.php

@@ -3,12 +3,14 @@
 namespace Firebase\JWT;
 
 use ArrayAccess;
+use DateTime;
 use DomainException;
 use Exception;
 use InvalidArgumentException;
 use OpenSSLAsymmetricKey;
+use OpenSSLCertificate;
+use stdClass;
 use UnexpectedValueException;
-use DateTime;
 
 /**
  * JSON Web Token implementation, based on this spec:
@@ -25,52 +27,63 @@ use DateTime;
  */
 class JWT
 {
-    const ASN1_INTEGER = 0x02;
-    const ASN1_SEQUENCE = 0x10;
-    const ASN1_BIT_STRING = 0x03;
+    private const ASN1_INTEGER = 0x02;
+    private const ASN1_SEQUENCE = 0x10;
+    private const ASN1_BIT_STRING = 0x03;
 
     /**
      * When checking nbf, iat or expiration times,
      * we want to provide some extra leeway time to
      * account for clock skew.
+     *
+     * @var int
      */
     public static $leeway = 0;
 
     /**
      * Allow the current timestamp to be specified.
      * Useful for fixing a value within unit testing.
-     *
      * Will default to PHP time() value if null.
+     *
+     * @var ?int
      */
     public static $timestamp = null;
 
-    public static $supported_algs = array(
-        'ES384' => array('openssl', 'SHA384'),
-        'ES256' => array('openssl', 'SHA256'),
-        'HS256' => array('hash_hmac', 'SHA256'),
-        'HS384' => array('hash_hmac', 'SHA384'),
-        'HS512' => array('hash_hmac', 'SHA512'),
-        'RS256' => array('openssl', 'SHA256'),
-        'RS384' => array('openssl', 'SHA384'),
-        'RS512' => array('openssl', 'SHA512'),
-        'EdDSA' => array('sodium_crypto', 'EdDSA'),
-    );
+    /**
+     * @var array<string, string[]>
+     */
+    public static $supported_algs = [
+        'ES384' => ['openssl', 'SHA384'],
+        'ES256' => ['openssl', 'SHA256'],
+        'ES256K' => ['openssl', 'SHA256'],
+        'HS256' => ['hash_hmac', 'SHA256'],
+        'HS384' => ['hash_hmac', 'SHA384'],
+        'HS512' => ['hash_hmac', 'SHA512'],
+        'RS256' => ['openssl', 'SHA256'],
+        'RS384' => ['openssl', 'SHA384'],
+        'RS512' => ['openssl', 'SHA512'],
+        'EdDSA' => ['sodium_crypto', 'EdDSA'],
+    ];
 
     /**
      * Decodes a JWT string into a PHP object.
      *
-     * @param string                    $jwt            The JWT
-     * @param Key|array<Key>|mixed      $keyOrKeyArray  The Key or array of Key objects.
-     *                                                  If the algorithm used is asymmetric, this is the public key
-     *                                                  Each Key object contains an algorithm and matching key.
-     *                                                  Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
-     *                                                  'HS512', 'RS256', 'RS384', and 'RS512'
-     * @param array                     $allowed_algs   [DEPRECATED] List of supported verification algorithms. Only
-     *                                                  should be used for backwards  compatibility.
-     *
-     * @return object The JWT's payload as a PHP object
-     *
-     * @throws InvalidArgumentException     Provided JWT was empty
+     * @param string                 $jwt            The JWT
+     * @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray  The Key or associative array of key IDs
+     *                                                                      (kid) to Key objects.
+     *                                                                      If the algorithm used is asymmetric, this is
+     *                                                                      the public key.
+     *                                                                      Each Key object contains an algorithm and
+     *                                                                      matching key.
+     *                                                                      Supported algorithms are 'ES384','ES256',
+     *                                                                      'HS256', 'HS384', 'HS512', 'RS256', 'RS384'
+     *                                                                      and 'RS512'.
+     * @param stdClass               $headers                               Optional. Populates stdClass with headers.
+     *
+     * @return stdClass The JWT's payload as a PHP object
+     *
+     * @throws InvalidArgumentException     Provided key/key-array was empty or malformed
+     * @throws DomainException              Provided JWT is malformed
      * @throws UnexpectedValueException     Provided JWT was invalid
      * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
      * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
@@ -80,27 +93,41 @@ class JWT
      * @uses jsonDecode
      * @uses urlsafeB64Decode
      */
-    public static function decode($jwt, $keyOrKeyArray, array $allowed_algs = array())
-    {
+    public static function decode(
+        string $jwt,
+        $keyOrKeyArray,
+        ?stdClass &$headers = null
+    ): stdClass {
+        // Validate JWT
         $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
 
         if (empty($keyOrKeyArray)) {
             throw new InvalidArgumentException('Key may not be empty');
         }
         $tks = \explode('.', $jwt);
-        if (\count($tks) != 3) {
+        if (\count($tks) !== 3) {
             throw new UnexpectedValueException('Wrong number of segments');
         }
         list($headb64, $bodyb64, $cryptob64) = $tks;
-        if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
+        $headerRaw = static::urlsafeB64Decode($headb64);
+        if (null === ($header = static::jsonDecode($headerRaw))) {
             throw new UnexpectedValueException('Invalid header encoding');
         }
-        if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
+        if ($headers !== null) {
+            $headers = $header;
+        }
+        $payloadRaw = static::urlsafeB64Decode($bodyb64);
+        if (null === ($payload = static::jsonDecode($payloadRaw))) {
             throw new UnexpectedValueException('Invalid claims encoding');
         }
-        if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
-            throw new UnexpectedValueException('Invalid signature encoding');
+        if (\is_array($payload)) {
+            // prevent PHP Fatal Error in edge-cases when payload is empty array
+            $payload = (object) $payload;
+        }
+        if (!$payload instanceof stdClass) {
+            throw new UnexpectedValueException('Payload must be a JSON object');
         }
+        $sig = static::urlsafeB64Decode($cryptob64);
         if (empty($header->alg)) {
             throw new UnexpectedValueException('Empty algorithm');
         }
@@ -108,88 +135,85 @@ class JWT
             throw new UnexpectedValueException('Algorithm not supported');
         }
 
-        list($keyMaterial, $algorithm) = self::getKeyMaterialAndAlgorithm(
-            $keyOrKeyArray,
-            empty($header->kid) ? null : $header->kid
-        );
+        $key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null);
 
-        if (empty($algorithm)) {
-            // Use deprecated "allowed_algs" to determine if the algorithm is supported.
-            // This opens up the possibility of an attack in some implementations.
-            // @see https://github.com/firebase/php-jwt/issues/351
-            if (!\in_array($header->alg, $allowed_algs)) {
-                throw new UnexpectedValueException('Algorithm not allowed');
-            }
-        } else {
-            // Check the algorithm
-            if (!self::constantTimeEquals($algorithm, $header->alg)) {
-                // See issue #351
-                throw new UnexpectedValueException('Incorrect key for this algorithm');
-            }
+        // Check the algorithm
+        if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
+            // See issue #351
+            throw new UnexpectedValueException('Incorrect key for this algorithm');
         }
-        if ($header->alg === 'ES256' || $header->alg === 'ES384') {
-            // OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures
+        if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) {
+            // OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures
             $sig = self::signatureToDER($sig);
         }
-
-        if (!static::verify("$headb64.$bodyb64", $sig, $keyMaterial, $header->alg)) {
+        if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) {
             throw new SignatureInvalidException('Signature verification failed');
         }
 
         // Check the nbf if it is defined. This is the time that the
         // token can actually be used. If it's not yet that time, abort.
-        if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
-            throw new BeforeValidException(
-                'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
+        if (isset($payload->nbf) && floor($payload->nbf) > ($timestamp + static::$leeway)) {
+            $ex = new BeforeValidException(
+                'Cannot handle token with nbf prior to ' . \date(DateTime::ISO8601, (int) $payload->nbf)
             );
+            $ex->setPayload($payload);
+            throw $ex;
         }
 
         // Check that this token has been created before 'now'. This prevents
         // using tokens that have been created for later use (and haven't
         // correctly used the nbf claim).
-        if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
-            throw new BeforeValidException(
-                'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
+        if (!isset($payload->nbf) && isset($payload->iat) && floor($payload->iat) > ($timestamp + static::$leeway)) {
+            $ex = new BeforeValidException(
+                'Cannot handle token with iat prior to ' . \date(DateTime::ISO8601, (int) $payload->iat)
             );
+            $ex->setPayload($payload);
+            throw $ex;
         }
 
         // Check if this token has expired.
         if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
-            throw new ExpiredException('Expired token');
+            $ex = new ExpiredException('Expired token');
+            $ex->setPayload($payload);
+            throw $ex;
         }
 
         return $payload;
     }
 
     /**
-     * Converts and signs a PHP object or array into a JWT string.
+     * Converts and signs a PHP array into a JWT string.
      *
-     * @param object|array      $payload    PHP object or array
-     * @param string|resource   $key        The secret key.
-     *                                      If the algorithm used is asymmetric, this is the private key
-     * @param string            $alg        The signing algorithm.
-     *                                      Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
-     *                                      'HS512', 'RS256', 'RS384', and 'RS512'
-     * @param mixed             $keyId
-     * @param array             $head       An array with header elements to attach
+     * @param array<mixed>          $payload PHP array
+     * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
+     * @param string                $alg     Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
+     *                                       'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
+     * @param string                $keyId
+     * @param array<string, string> $head    An array with header elements to attach
      *
      * @return string A signed JWT
      *
      * @uses jsonEncode
      * @uses urlsafeB64Encode
      */
-    public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
-    {
-        $header = array('typ' => 'JWT', 'alg' => $alg);
+    public static function encode(
+        array $payload,
+        $key,
+        string $alg,
+        ?string $keyId = null,
+        ?array $head = null
+    ): string {
+        $header = ['typ' => 'JWT'];
+        if (isset($head)) {
+            $header = \array_merge($header, $head);
+        }
+        $header['alg'] = $alg;
         if ($keyId !== null) {
             $header['kid'] = $keyId;
         }
-        if (isset($head) && \is_array($head)) {
-            $header = \array_merge($head, $header);
-        }
-        $segments = array();
-        $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
-        $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
+        $segments = [];
+        $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header));
+        $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload));
         $signing_input = \implode('.', $segments);
 
         $signature = static::sign($signing_input, $key, $alg);
@@ -201,67 +225,87 @@ class JWT
     /**
      * Sign a string with a given key and algorithm.
      *
-     * @param string            $msg    The message to sign
-     * @param string|resource   $key    The secret key
-     * @param string            $alg    The signing algorithm.
-     *                                  Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
-     *                                  'HS512', 'RS256', 'RS384', and 'RS512'
+     * @param string $msg  The message to sign
+     * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate  $key  The secret key.
+     * @param string $alg  Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256',
+     *                    'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
      *
      * @return string An encrypted message
      *
      * @throws DomainException Unsupported algorithm or bad key was specified
      */
-    public static function sign($msg, $key, $alg = 'HS256')
-    {
+    public static function sign(
+        string $msg,
+        $key,
+        string $alg
+    ): string {
         if (empty(static::$supported_algs[$alg])) {
             throw new DomainException('Algorithm not supported');
         }
         list($function, $algorithm) = static::$supported_algs[$alg];
         switch ($function) {
             case 'hash_hmac':
+                if (!\is_string($key)) {
+                    throw new InvalidArgumentException('key must be a string when using hmac');
+                }
                 return \hash_hmac($algorithm, $msg, $key, true);
             case 'openssl':
                 $signature = '';
-                $success = \openssl_sign($msg, $signature, $key, $algorithm);
+                if (!\is_resource($key) && !openssl_pkey_get_private($key)) {
+                    throw new DomainException('OpenSSL unable to validate key');
+                }
+                $success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
                 if (!$success) {
-                    throw new DomainException("OpenSSL unable to sign data");
+                    throw new DomainException('OpenSSL unable to sign data');
                 }
-                if ($alg === 'ES256') {
+                if ($alg === 'ES256' || $alg === 'ES256K') {
                     $signature = self::signatureFromDER($signature, 256);
                 } elseif ($alg === 'ES384') {
                     $signature = self::signatureFromDER($signature, 384);
                 }
                 return $signature;
             case 'sodium_crypto':
-                if (!function_exists('sodium_crypto_sign_detached')) {
+                if (!\function_exists('sodium_crypto_sign_detached')) {
                     throw new DomainException('libsodium is not available');
                 }
+                if (!\is_string($key)) {
+                    throw new InvalidArgumentException('key must be a string when using EdDSA');
+                }
                 try {
                     // The last non-empty line is used as the key.
                     $lines = array_filter(explode("\n", $key));
-                    $key = base64_decode(end($lines));
+                    $key = base64_decode((string) end($lines));
+                    if (\strlen($key) === 0) {
+                        throw new DomainException('Key cannot be empty string');
+                    }
                     return sodium_crypto_sign_detached($msg, $key);
                 } catch (Exception $e) {
                     throw new DomainException($e->getMessage(), 0, $e);
                 }
         }
+
+        throw new DomainException('Algorithm not supported');
     }
 
     /**
      * Verify a signature with the message, key and method. Not all methods
      * are symmetric, so we must have a separate verify and sign method.
      *
-     * @param string            $msg        The original message (header and body)
-     * @param string            $signature  The original signature
-     * @param string|resource   $key        For HS*, a string key works. for RS*, must be a resource of an openssl public key
-     * @param string            $alg        The algorithm
+     * @param string $msg         The original message (header and body)
+     * @param string $signature   The original signature
+     * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate  $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
+     * @param string $alg         The algorithm
      *
      * @return bool
      *
      * @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
      */
-    private static function verify($msg, $signature, $key, $alg)
-    {
+    private static function verify(
+        string $msg,
+        string $signature,
+        $keyMaterial,
+        string $alg
+    ): bool {
         if (empty(static::$supported_algs[$alg])) {
             throw new DomainException('Algorithm not supported');
         }
@@ -269,10 +313,11 @@ class JWT
         list($function, $algorithm) = static::$supported_algs[$alg];
         switch ($function) {
             case 'openssl':
-                $success = \openssl_verify($msg, $signature, $key, $algorithm);
+                $success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
                 if ($success === 1) {
                     return true;
-                } elseif ($success === 0) {
+                }
+                if ($success === 0) {
                     return false;
                 }
                 // returns 1 on success, 0 on failure, -1 on error.
@@ -280,21 +325,33 @@ class JWT
                     'OpenSSL error: ' . \openssl_error_string()
                 );
             case 'sodium_crypto':
-              if (!function_exists('sodium_crypto_sign_verify_detached')) {
-                  throw new DomainException('libsodium is not available');
-              }
-              try {
-                  // The last non-empty line is used as the key.
-                  $lines = array_filter(explode("\n", $key));
-                  $key = base64_decode(end($lines));
-                  return sodium_crypto_sign_verify_detached($signature, $msg, $key);
-              } catch (Exception $e) {
-                  throw new DomainException($e->getMessage(), 0, $e);
-              }
+                if (!\function_exists('sodium_crypto_sign_verify_detached')) {
+                    throw new DomainException('libsodium is not available');
+                }
+                if (!\is_string($keyMaterial)) {
+                    throw new InvalidArgumentException('key must be a string when using EdDSA');
+                }
+                try {
+                    // The last non-empty line is used as the key.
+                    $lines = array_filter(explode("\n", $keyMaterial));
+                    $key = base64_decode((string) end($lines));
+                    if (\strlen($key) === 0) {
+                        throw new DomainException('Key cannot be empty string');
+                    }
+                    if (\strlen($signature) === 0) {
+                        throw new DomainException('Signature cannot be empty string');
+                    }
+                    return sodium_crypto_sign_verify_detached($signature, $msg, $key);
+                } catch (Exception $e) {
+                    throw new DomainException($e->getMessage(), 0, $e);
+                }
             case 'hash_hmac':
             default:
-                $hash = \hash_hmac($algorithm, $msg, $key, true);
-                return self::constantTimeEquals($signature, $hash);
+                if (!\is_string($keyMaterial)) {
+                    throw new InvalidArgumentException('key must be a string when using hmac');
+                }
+                $hash = \hash_hmac($algorithm, $msg, $keyMaterial, true);
+                return self::constantTimeEquals($hash, $signature);
         }
     }
 
@@ -303,30 +360,16 @@ class JWT
      *
      * @param string $input JSON string
      *
-     * @return object Object representation of JSON string
+     * @return mixed The decoded JSON string
      *
      * @throws DomainException Provided string was invalid JSON
      */
-    public static function jsonDecode($input)
+    public static function jsonDecode(string $input)
     {
-        if (\version_compare(PHP_VERSION, '5.4.0', '>=') && !(\defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
-            /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
-             * to specify that large ints (like Steam Transaction IDs) should be treated as
-             * strings, rather than the PHP default behaviour of converting them to floats.
-             */
-            $obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
-        } else {
-            /** Not all servers will support that, however, so for older versions we must
-             * manually detect large ints in the JSON string and quote them (thus converting
-             *them to strings) before decoding, hence the preg_replace() call.
-             */
-            $max_int_length = \strlen((string) PHP_INT_MAX) - 1;
-            $json_without_bigints = \preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
-            $obj = \json_decode($json_without_bigints);
-        }
+        $obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
 
         if ($errno = \json_last_error()) {
-            static::handleJsonError($errno);
+            self::handleJsonError($errno);
         } elseif ($obj === null && $input !== 'null') {
             throw new DomainException('Null result with non-null input');
         }
@@ -334,22 +377,25 @@ class JWT
     }
 
     /**
-     * Encode a PHP object into a JSON string.
+     * Encode a PHP array into a JSON string.
      *
-     * @param object|array $input A PHP object or array
+     * @param array<mixed> $input A PHP array
      *
-     * @return string JSON representation of the PHP object or array
+     * @return string JSON representation of the PHP array
      *
      * @throws DomainException Provided object could not be encoded to valid JSON
      */
-    public static function jsonEncode($input)
+    public static function jsonEncode(array $input): string
     {
-        $json = \json_encode($input);
+        $json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
         if ($errno = \json_last_error()) {
-            static::handleJsonError($errno);
-        } elseif ($json === 'null' && $input !== null) {
+            self::handleJsonError($errno);
+        } elseif ($json === 'null') {
             throw new DomainException('Null result with non-null input');
         }
+        if ($json === false) {
+            throw new DomainException('Provided object could not be encoded to valid JSON');
+        }
         return $json;
     }
 
@@ -359,15 +405,32 @@ class JWT
      * @param string $input A Base64 encoded string
      *
      * @return string A decoded string
+     *
+     * @throws InvalidArgumentException invalid base64 characters
+     */
+    public static function urlsafeB64Decode(string $input): string
+    {
+        return \base64_decode(self::convertBase64UrlToBase64($input));
+    }
+
+    /**
+     * Convert a string in the base64url (URL-safe Base64) encoding to standard base64.
+     *
+     * @param string $input A Base64 encoded string with URL-safe characters (-_ and no padding)
+     *
+     * @return string A Base64 encoded string with standard characters (+/) and padding (=), when
+     * needed.
+     *
+     * @see https://www.rfc-editor.org/rfc/rfc4648
      */
-    public static function urlsafeB64Decode($input)
+    public static function convertBase64UrlToBase64(string $input): string
     {
         $remainder = \strlen($input) % 4;
         if ($remainder) {
             $padlen = 4 - $remainder;
             $input .= \str_repeat('=', $padlen);
         }
-        return \base64_decode(\strtr($input, '-_', '+/'));
+        return \strtr($input, '-_', '+/');
     }
 
     /**
@@ -377,7 +440,7 @@ class JWT
      *
      * @return string The base64 encode of what you passed in
      */
-    public static function urlsafeB64Encode($input)
+    public static function urlsafeB64Encode(string $input): string
     {
         return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
     }
@@ -386,67 +449,54 @@ class JWT
     /**
      * Determine if an algorithm has been provided for each Key
      *
-     * @param Key|array<Key>|mixed $keyOrKeyArray
-     * @param string|null $kid
+     * @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray
+     * @param string|null            $kid
      *
      * @throws UnexpectedValueException
      *
-     * @return array containing the keyMaterial and algorithm
+     * @return Key
      */
-    private static function getKeyMaterialAndAlgorithm($keyOrKeyArray, $kid = null)
-    {
-        if (
-            is_string($keyOrKeyArray)
-            || is_resource($keyOrKeyArray)
-            || $keyOrKeyArray instanceof OpenSSLAsymmetricKey
-        ) {
-            return array($keyOrKeyArray, null);
-        }
-
+    private static function getKey(
+        $keyOrKeyArray,
+        ?string $kid
+    ): Key {
         if ($keyOrKeyArray instanceof Key) {
-            return array($keyOrKeyArray->getKeyMaterial(), $keyOrKeyArray->getAlgorithm());
+            return $keyOrKeyArray;
         }
 
-        if (is_array($keyOrKeyArray) || $keyOrKeyArray instanceof ArrayAccess) {
-            if (!isset($kid)) {
-                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
-            }
-            if (!isset($keyOrKeyArray[$kid])) {
-                throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
-            }
-
-            $key = $keyOrKeyArray[$kid];
+        if (empty($kid) && $kid !== '0') {
+            throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
+        }
 
-            if ($key instanceof Key) {
-                return array($key->getKeyMaterial(), $key->getAlgorithm());
-            }
+        if ($keyOrKeyArray instanceof CachedKeySet) {
+            // Skip "isset" check, as this will automatically refresh if not set
+            return $keyOrKeyArray[$kid];
+        }
 
-            return array($key, null);
+        if (!isset($keyOrKeyArray[$kid])) {
+            throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
         }
 
-        throw new UnexpectedValueException(
-            '$keyOrKeyArray must be a string|resource key, an array of string|resource keys, '
-            . 'an instance of Firebase\JWT\Key key or an array of Firebase\JWT\Key keys'
-        );
+        return $keyOrKeyArray[$kid];
     }
 
     /**
-     * @param string $left
-     * @param string $right
+     * @param string $left  The string of known length to compare against
+     * @param string $right The user-supplied string
      * @return bool
      */
-    public static function constantTimeEquals($left, $right)
+    public static function constantTimeEquals(string $left, string $right): bool
     {
         if (\function_exists('hash_equals')) {
             return \hash_equals($left, $right);
         }
-        $len = \min(static::safeStrlen($left), static::safeStrlen($right));
+        $len = \min(self::safeStrlen($left), self::safeStrlen($right));
 
         $status = 0;
         for ($i = 0; $i < $len; $i++) {
             $status |= (\ord($left[$i]) ^ \ord($right[$i]));
         }
-        $status |= (static::safeStrlen($left) ^ static::safeStrlen($right));
+        $status |= (self::safeStrlen($left) ^ self::safeStrlen($right));
 
         return ($status === 0);
     }
@@ -456,17 +506,19 @@ class JWT
      *
      * @param int $errno An error number from json_last_error()
      *
+     * @throws DomainException
+     *
      * @return void
      */
-    private static function handleJsonError($errno)
+    private static function handleJsonError(int $errno): void
     {
-        $messages = array(
+        $messages = [
             JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
             JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
             JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
             JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
             JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
-        );
+        ];
         throw new DomainException(
             isset($messages[$errno])
             ? $messages[$errno]
@@ -481,7 +533,7 @@ class JWT
      *
      * @return int
      */
-    private static function safeStrlen($str)
+    private static function safeStrlen(string $str): int
     {
         if (\function_exists('mb_strlen')) {
             return \mb_strlen($str, '8bit');
@@ -495,10 +547,11 @@ class JWT
      * @param   string $sig The ECDSA signature to convert
      * @return  string The encoded DER object
      */
-    private static function signatureToDER($sig)
+    private static function signatureToDER(string $sig): string
     {
         // Separate the signature into r-value and s-value
-        list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2));
+        $length = max(1, (int) (\strlen($sig) / 2));
+        list($r, $s) = \str_split($sig, $length);
 
         // Trim leading zeros
         $r = \ltrim($r, "\x00");
@@ -525,9 +578,10 @@ class JWT
      *
      * @param   int     $type DER tag
      * @param   string  $value the value to encode
+     *
      * @return  string  the encoded object
      */
-    private static function encodeDER($type, $value)
+    private static function encodeDER(int $type, string $value): string
     {
         $tag_header = 0;
         if ($type === self::ASN1_SEQUENCE) {
@@ -548,9 +602,10 @@ class JWT
      *
      * @param   string  $der binary signature in DER format
      * @param   int     $keySize the number of bits in the key
+     *
      * @return  string  the signature
      */
-    private static function signatureFromDER($der, $keySize)
+    private static function signatureFromDER(string $der, int $keySize): string
     {
         // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
         list($offset, $_) = self::readDER($der);
@@ -575,9 +630,10 @@ class JWT
      * @param string $der the binary data in DER format
      * @param int $offset the offset of the data stream containing the object
      * to decode
-     * @return array [$offset, $data] the new offset and the decoded object
+     *
+     * @return array{int, string|null} the new offset and the decoded object
      */
-    private static function readDER($der, $offset = 0)
+    private static function readDER(string $der, int $offset = 0): array
     {
         $pos = $offset;
         $size = \strlen($der);
@@ -595,7 +651,7 @@ class JWT
         }
 
         // Value
-        if ($type == self::ASN1_BIT_STRING) {
+        if ($type === self::ASN1_BIT_STRING) {
             $pos++; // Skip the first contents octet (padding indicator)
             $data = \substr($der, $pos, $len - 1);
             $pos += $len - 1;
@@ -606,6 +662,6 @@ class JWT
             $data = null;
         }
 
-        return array($pos, $data);
+        return [$pos, $data];
     }
 }

+ 20 - 0
modules/boonex/xero/plugins/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php

@@ -0,0 +1,20 @@
+<?php
+namespace Firebase\JWT;
+
+interface JWTExceptionWithPayloadInterface
+{
+    /**
+     * Get the payload that caused this exception.
+     *
+     * @return object
+     */
+    public function getPayload(): object;
+
+    /**
+     * Get the payload that caused this exception.
+     *
+     * @param object $payload
+     * @return void
+     */
+    public function setPayload(object $payload): void;
+}

+ 16 - 20
modules/boonex/xero/plugins/firebase/php-jwt/src/Key.php

@@ -4,39 +4,35 @@ namespace Firebase\JWT;
 
 use InvalidArgumentException;
 use OpenSSLAsymmetricKey;
+use OpenSSLCertificate;
+use TypeError;
 
 class Key
 {
-    /** @var string $algorithm */
-    private $algorithm;
-
-    /** @var string|resource|OpenSSLAsymmetricKey $keyMaterial */
-    private $keyMaterial;
-
     /**
-     * @param string|resource|OpenSSLAsymmetricKey $keyMaterial
+     * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
      * @param string $algorithm
      */
-    public function __construct($keyMaterial, $algorithm)
-    {
+    public function __construct(
+        private $keyMaterial,
+        private string $algorithm
+    ) {
         if (
-            !is_string($keyMaterial)
-            && !is_resource($keyMaterial)
+            !\is_string($keyMaterial)
             && !$keyMaterial instanceof OpenSSLAsymmetricKey
+            && !$keyMaterial instanceof OpenSSLCertificate
+            && !\is_resource($keyMaterial)
         ) {
-            throw new InvalidArgumentException('Type error: $keyMaterial must be a string, resource, or OpenSSLAsymmetricKey');
+            throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
         }
 
         if (empty($keyMaterial)) {
-            throw new InvalidArgumentException('Type error: $keyMaterial must not be empty');
+            throw new InvalidArgumentException('Key material must not be empty');
         }
 
-        if (!is_string($algorithm)|| empty($keyMaterial)) {
-            throw new InvalidArgumentException('Type error: $algorithm must be a string');
+        if (empty($algorithm)) {
+            throw new InvalidArgumentException('Algorithm must not be empty');
         }
-
-        $this->keyMaterial = $keyMaterial;
-        $this->algorithm = $algorithm;
     }
 
     /**
@@ -44,13 +40,13 @@ class Key
      *
      * @return string
      */
-    public function getAlgorithm()
+    public function getAlgorithm(): string
     {
         return $this->algorithm;
     }
 
     /**
-     * @return string|resource|OpenSSLAsymmetricKey
+     * @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
      */
     public function getKeyMaterial()
     {

+ 196 - 3
modules/boonex/xero/plugins/guzzlehttp/guzzle/CHANGELOG.md

@@ -2,6 +2,166 @@
 
 Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version.
 
+
+## 7.9.3 - 2025-03-27
+
+### Changed
+
+- Remove explicit content-length header for GET requests
+- Improve compatibility with bad servers for boolean cookie values
+
+
+## 7.9.2 - 2024-07-24
+
+### Fixed
+
+- Adjusted handler selection to use cURL if its version is 7.21.2 or higher, rather than 7.34.0
+
+
+## 7.9.1 - 2024-07-19
+
+### Fixed
+
+- Fix TLS 1.3 check for HTTP/2 requests
+
+
+## 7.9.0 - 2024-07-18
+
+### Changed
+
+- Improve protocol version checks to provide feedback around unsupported protocols
+- Only select the cURL handler by default if 7.34.0 or higher is linked
+- Improved `CurlMultiHandler` to avoid busy wait if possible
+- Dropped support for EOL `guzzlehttp/psr7` v1
+- Improved URI user info redaction in errors
+
+## 7.8.2 - 2024-07-18
+
+### Added
+
+- Support for PHP 8.4
+
+
+## 7.8.1 - 2023-12-03
+
+### Changed
+
+- Updated links in docs to their canonical versions
+- Replaced `call_user_func*` with native calls
+
+
+## 7.8.0 - 2023-08-27
+
+### Added
+
+- Support for PHP 8.3
+- Added automatic closing of handles on `CurlFactory` object destruction
+
+
+## 7.7.1 - 2023-08-27
+
+### Changed
+
+- Remove the need for `AllowDynamicProperties` in `CurlMultiHandler`
+
+
+## 7.7.0 - 2023-05-21
+
+### Added
+
+- Support `guzzlehttp/promises` v2
+
+
+## 7.6.1 - 2023-05-15
+
+### Fixed
+
+- Fix `SetCookie::fromString` MaxAge deprecation warning and skip invalid MaxAge values
+
+
+## 7.6.0 - 2023-05-14
+
+### Added
+
+- Support for setting the minimum TLS version in a unified way
+- Apply on request the version set in options parameters
+
+
+## 7.5.2 - 2023-05-14
+
+### Fixed
+
+- Fixed set cookie constructor validation
+- Fixed handling of files with `'0'` body
+
+### Changed
+
+- Corrected docs and default connect timeout value to 300 seconds
+
+
+## 7.5.1 - 2023-04-17
+
+### Fixed
+
+- Fixed `NO_PROXY` settings so that setting the `proxy` option to `no` overrides the env variable
+
+### Changed
+
+- Adjusted `guzzlehttp/psr7` version constraint to `^1.9.1 || ^2.4.5`
+
+
+## 7.5.0 - 2022-08-28
+
+### Added
+
+- Support PHP 8.2
+- Add request to delay closure params
+
+
+## 7.4.5 - 2022-06-20
+
+### Fixed
+
+* Fix change in port should be considered a change in origin
+* Fix `CURLOPT_HTTPAUTH` option not cleared on change of origin
+
+
+## 7.4.4 - 2022-06-09
+
+### Fixed
+
+* Fix failure to strip Authorization header on HTTP downgrade
+* Fix failure to strip the Cookie header on change in host or HTTP downgrade
+
+
+## 7.4.3 - 2022-05-25
+
+### Fixed
+
+* Fix cross-domain cookie leakage
+
+
+## 7.4.2 - 2022-03-20
+
+### Fixed
+
+- Remove curl auth on cross-domain redirects to align with the Authorization HTTP header
+- Reject non-HTTP schemes in StreamHandler
+- Set a default ssl.peer_name context in StreamHandler to allow `force_ip_resolve`
+
+
+## 7.4.1 - 2021-12-06
+
+### Changed
+
+- Replaced implicit URI to string coercion [#2946](https://github.com/guzzle/guzzle/pull/2946)
+- Allow `symfony/deprecation-contracts` version 3 [#2961](https://github.com/guzzle/guzzle/pull/2961)
+
+### Fixed
+
+- Only close curl handle if it's done [#2950](https://github.com/guzzle/guzzle/pull/2950)
+
+
 ## 7.4.0 - 2021-10-18
 
 ### Added
@@ -19,6 +179,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 
 - Be more strict with types [#2914](https://github.com/guzzle/guzzle/pull/2914), [#2917](https://github.com/guzzle/guzzle/pull/2917), [#2919](https://github.com/guzzle/guzzle/pull/2919), [#2945](https://github.com/guzzle/guzzle/pull/2945)
 
+
 ## 7.3.0 - 2021-03-23
 
 ### Added
@@ -31,6 +192,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 
 - Handle exceptions on invalid header consistently between PHP versions and handlers [#2872](https://github.com/guzzle/guzzle/pull/2872)
 
+
 ## 7.2.0 - 2020-10-10
 
 ### Added
@@ -53,6 +215,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 
 - Using environment variable GUZZLE_CURL_SELECT_TIMEOUT [#2786](https://github.com/guzzle/guzzle/pull/2786)
 
+
 ## 7.1.1 - 2020-09-30
 
 ### Fixed
@@ -64,6 +227,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 - We dont connect curl `sink` on HEAD requests.
 - Removed some PHP 5 workarounds
 
+
 ## 7.1.0 - 2020-09-22
 
 ### Added
@@ -86,14 +250,17 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 - `Utils::defaultCaBundle()`
 - `CurlFactory::LOW_CURL_VERSION_NUMBER`
 
+
 ## 7.0.1 - 2020-06-27
 
 * Fix multiply defined functions fatal error [#2699](https://github.com/guzzle/guzzle/pull/2699)
 
+
 ## 7.0.0 - 2020-06-27
 
 No changes since 7.0.0-rc1.
 
+
 ## 7.0.0-rc1 - 2020-06-15
 
 ### Changed
@@ -101,6 +268,7 @@ No changes since 7.0.0-rc1.
 * Use error level for logging errors in Middleware [#2629](https://github.com/guzzle/guzzle/pull/2629)
 * Disabled IDN support by default and require ext-intl to use it [#2675](https://github.com/guzzle/guzzle/pull/2675)
 
+
 ## 7.0.0-beta2 - 2020-05-25
 
 ### Added
@@ -126,6 +294,7 @@ No changes since 7.0.0-rc1.
 
 * Pool option `pool_size` [#2528](https://github.com/guzzle/guzzle/pull/2528)
 
+
 ## 7.0.0-beta1 - 2019-12-30
 
 The diff might look very big but 95% of Guzzle users will be able to upgrade without modification.
@@ -159,15 +328,18 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * `uri_template()` and `UriTemplate` [#2440](https://github.com/guzzle/guzzle/pull/2440)
 * Request options `save_to` and `exceptions` [#2464](https://github.com/guzzle/guzzle/pull/2464)
 
+
 ## 6.5.2 - 2019-12-23
 
 * idn_to_ascii() fix for old PHP versions [#2489](https://github.com/guzzle/guzzle/pull/2489)
 
+
 ## 6.5.1 - 2019-12-21
 
 * Better defaults for PHP installations with old ICU lib [#2454](https://github.com/guzzle/guzzle/pull/2454)
 * IDN support for redirects [#2424](https://github.com/guzzle/guzzle/pull/2424)
 
+
 ## 6.5.0 - 2019-12-07
 
 * Improvement: Added support for reset internal queue in MockHandler. [#2143](https://github.com/guzzle/guzzle/pull/2143)
@@ -177,11 +349,13 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Fix: Prevent undefined offset when using array for ssl_key options. [#2348](https://github.com/guzzle/guzzle/pull/2348)
 * Deprecated `ClientInterface::VERSION`
 
+
 ## 6.4.1 - 2019-10-23
 
 * No `guzzle.phar` was created in 6.4.0 due expired API token. This release will fix that
 * Added `parent::__construct()` to `FileCookieJar` and `SessionCookieJar`
 
+
 ## 6.4.0 - 2019-10-23
 
 * Improvement: Improved error messages when using curl < 7.21.2 [#2108](https://github.com/guzzle/guzzle/pull/2108)
@@ -194,6 +368,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Fix: Prevent concurrent writes to file when saving `CookieJar` [#2335](https://github.com/guzzle/guzzle/pull/2335)
 * Improvement: Update `MockHandler` so we can test transfer time [#2362](https://github.com/guzzle/guzzle/pull/2362)
 
+
 ## 6.3.3 - 2018-04-22
 
 * Fix: Default headers when decode_content is specified
@@ -235,13 +410,14 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684)
 * Improvement:  	Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827)
 
-
 + Minor code cleanups, documentation fixes and clarifications.
 
+
 ## 6.2.3 - 2017-02-28
 
 * Fix deprecations with guzzle/psr7 version 1.4
 
+
 ## 6.2.2 - 2016-10-08
 
 * Allow to pass nullable Response to delay callable
@@ -249,6 +425,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Fix drain case where content-length is the literal string zero
 * Obfuscate in-URL credentials in exceptions
 
+
 ## 6.2.1 - 2016-07-18
 
 * Address HTTP_PROXY security vulnerability, CVE-2016-5385:
@@ -259,6 +436,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
   a server does not honor `Connection: close`.
 * Ignore URI fragment when sending requests.
 
+
 ## 6.2.0 - 2016-03-21
 
 * Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`.
@@ -278,6 +456,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Bug fix: provide an empty string to `http_build_query` for HHVM workaround.
   https://github.com/guzzle/guzzle/pull/1367
 
+
 ## 6.1.1 - 2015-11-22
 
 * Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler
@@ -293,6 +472,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Bug fix: fixed regression where MockHandler was not using `sink`.
   https://github.com/guzzle/guzzle/pull/1292
 
+
 ## 6.1.0 - 2015-09-08
 
 * Feature: Added the `on_stats` request option to provide access to transfer
@@ -327,6 +507,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set.
   https://github.com/guzzle/guzzle/pull/1189
 
+
 ## 6.0.2 - 2015-07-04
 
 * Fixed a memory leak in the curl handlers in which references to callbacks
@@ -344,6 +525,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Functions are now conditionally required using an additional level of
   indirection to help with global Composer installations.
 
+
 ## 6.0.1 - 2015-05-27
 
 * Fixed a bug with serializing the `query` request option where the `&`
@@ -352,6 +534,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
   use `form_params` or `multipart` instead.
 * Various doc fixes.
 
+
 ## 6.0.0 - 2015-05-26
 
 * See the UPGRADING.md document for more information.
@@ -376,6 +559,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * `$maxHandles` has been removed from CurlMultiHandler.
 * `MultipartPostBody` is now part of the `guzzlehttp/psr7` package.
 
+
 ## 5.3.0 - 2015-05-19
 
 * Mock now supports `save_to`
@@ -386,6 +570,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated.
 * URL scheme is now always lowercased.
 
+
 ## 6.0.0-beta.1
 
 * Requires PHP >= 5.5
@@ -438,6 +623,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * `GuzzleHttp\QueryParser` has been replaced with the
   `GuzzleHttp\Psr7\parse_query`.
 
+
 ## 5.2.0 - 2015-01-27
 
 * Added `AppliesHeadersInterface` to make applying headers to a request based
@@ -448,6 +634,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
   RingBridge.
 * Added a guard in the Pool class to not use recursion for request retries.
 
+
 ## 5.1.0 - 2014-12-19
 
 * Pool class no longer uses recursion when a request is intercepted.
@@ -468,6 +655,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Exceptions thrown in the `end` event are now correctly wrapped with Guzzle
   specific exceptions if necessary.
 
+
 ## 5.0.3 - 2014-11-03
 
 This change updates query strings so that they are treated as un-encoded values
@@ -482,6 +670,7 @@ string that should not be parsed or encoded (unless a call to getQuery() is
 subsequently made, forcing the query-string to be converted into a Query
 object).
 
+
 ## 5.0.2 - 2014-10-30
 
 * Added a trailing `\r\n` to multipart/form-data payloads. See
@@ -501,7 +690,9 @@ object).
   * Note: This has been changed in 5.0.3 to now encode query string values by
     default unless the `rawString` argument is provided when setting the query
     string on a URL: Now allowing many more characters to be present in the
-    query string without being percent encoded. See https://tools.ietf.org/html/rfc3986#appendix-A
+    query string without being percent encoded. See
+    https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
+
 
 ## 5.0.1 - 2014-10-16
 
@@ -514,6 +705,7 @@ Bugfix release.
 * Fixed an issue where transfer statistics were not being populated in the
   RingBridge. https://github.com/guzzle/guzzle/issues/866
 
+
 ## 5.0.0 - 2014-10-12
 
 Adding support for non-blocking responses and some minor API cleanup.
@@ -595,6 +787,7 @@ interfaces.
       argument. They now accept an associative array of options, including the
       "size" key and "metadata" key which can be used to provide custom metadata.
 
+
 ## 4.2.2 - 2014-09-08
 
 * Fixed a memory leak in the CurlAdapter when reusing cURL handles.
@@ -1037,7 +1230,7 @@ interfaces.
 
 ## 3.4.0 - 2013-04-11
 
-* Bug fix: URLs are now resolved correctly based on https://tools.ietf.org/html/rfc3986#section-5.2. #289
+* Bug fix: URLs are now resolved correctly based on https://datatracker.ietf.org/doc/html/rfc3986#section-5.2. #289
 * Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
 * Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
 * Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.

+ 15 - 15
modules/boonex/xero/plugins/guzzlehttp/guzzle/README.md

@@ -3,7 +3,7 @@
 # Guzzle, PHP HTTP client
 
 [![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases)
-[![Build Status](https://img.shields.io/github/workflow/status/guzzle/guzzle/CI?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
+[![Build Status](https://img.shields.io/github/actions/workflow/status/guzzle/guzzle/ci.yml?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
 [![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle)
 
 Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
@@ -42,9 +42,9 @@ $promise->wait();
 
 We use GitHub issues only to discuss bugs and new features. For support please refer to:
 
-- [Documentation](http://guzzlephp.org/)
-- [Stack Overflow](http://stackoverflow.com/questions/tagged/guzzle)
-- [#guzzle](https://app.slack.com/client/T0D2S9JCT/CE6UAAKL4) channel on [PHP-HTTP Slack](http://slack.httplug.io/)
+- [Documentation](https://docs.guzzlephp.org)
+- [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)
 
 
@@ -60,23 +60,23 @@ composer require guzzlehttp/guzzle
 
 ## Version Guidance
 
-| Version | Status     | Packagist           | Namespace    | Repo                | Docs                | PSR-7 | PHP Version |
-|---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------|
-| 3.x     | EOL        | `guzzle/guzzle`     | `Guzzle`     | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No    | >= 5.3.3    |
-| 4.x     | EOL        | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A                 | No    | >= 5.4      |
-| 5.x     | EOL        | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No    | >= 5.4      |
-| 6.x     | Security fixes | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes   | >= 5.5      |
-| 7.x     | Latest     | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes   | >= 7.2      |
+| 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.5 |
 
 [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
-[guzzle-3-docs]: http://guzzle3.readthedocs.org
-[guzzle-5-docs]: http://docs.guzzlephp.org/en/5.3/
-[guzzle-6-docs]: http://docs.guzzlephp.org/en/6.5/
-[guzzle-7-docs]: http://docs.guzzlephp.org/en/latest/
+[guzzle-3-docs]: https://guzzle3.readthedocs.io/
+[guzzle-5-docs]: https://docs.guzzlephp.org/en/5.3/
+[guzzle-6-docs]: https://docs.guzzlephp.org/en/6.5/
+[guzzle-7-docs]: https://docs.guzzlephp.org/en/latest/
 
 
 ## Security

+ 8 - 8
modules/boonex/xero/plugins/guzzlehttp/guzzle/UPGRADING.md

@@ -27,7 +27,7 @@ Please make sure:
 - Function `GuzzleHttp\Exception\RequestException::getResponseBodySummary` is removed.
   Use `\GuzzleHttp\Psr7\get_message_body_summary` as an alternative.
 - Function `GuzzleHttp\Cookie\CookieJar::getCookieValue` is removed.
-- Request option `exception` is removed. Please use `http_errors`.
+- Request option `exceptions` is removed. Please use `http_errors`.
 - Request option `save_to` is removed. Please use `sink`.
 - Pool option `pool_size` is removed. Please use `concurrency`.
 - We now look for environment variables in the `$_SERVER` super global, due to thread safety issues with `getenv`. We continue to fallback to `getenv` in CLI environments, for maximum compatibility.
@@ -189,11 +189,11 @@ $client = new GuzzleHttp\Client(['handler' => $handler]);
 
 ## POST Requests
 
-This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)
+This version added the [`form_params`](https://docs.guzzlephp.org/en/latest/request-options.html#form_params)
 and `multipart` request options. `form_params` is an associative array of
 strings or array of strings and is used to serialize an
 `application/x-www-form-urlencoded` POST request. The
-[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)
+[`multipart`](https://docs.guzzlephp.org/en/latest/request-options.html#multipart)
 option is now used to send a multipart/form-data POST request.
 
 `GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
@@ -209,7 +209,7 @@ The `base_url` option has been renamed to `base_uri`.
 
 ## Rewritten Adapter Layer
 
-Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send
+Guzzle now uses [RingPHP](https://ringphp.readthedocs.org/en/latest) to send
 HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
 is still supported, but it has now been renamed to `handler`. Instead of
 passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
@@ -575,7 +575,7 @@ You can intercept a request and inject a response using the `intercept()` event
 of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
 `GuzzleHttp\Event\ErrorEvent` event.
 
-See: http://docs.guzzlephp.org/en/latest/events.html
+See: https://docs.guzzlephp.org/en/latest/events.html
 
 ## Inflection
 
@@ -668,9 +668,9 @@ in separate repositories:
 
 The service description layer of Guzzle has moved into two separate packages:
 
-- http://github.com/guzzle/command Provides a high level abstraction over web
+- https://github.com/guzzle/command Provides a high level abstraction over web
   services by representing web service operations using commands.
-- http://github.com/guzzle/guzzle-services Provides an implementation of
+- https://github.com/guzzle/guzzle-services Provides an implementation of
   guzzle/command that provides request serialization and response parsing using
   Guzzle service descriptions.
 
@@ -870,7 +870,7 @@ HeaderInterface (e.g. toArray(), getAll(), etc.).
 3.3 to 3.4
 ----------
 
-Base URLs of a client now follow the rules of https://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.
+Base URLs of a client now follow the rules of https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2 when merging URLs.
 
 3.2 to 3.3
 ----------

+ 41 - 8
modules/boonex/xero/plugins/guzzlehttp/guzzle/composer.json

@@ -50,22 +50,51 @@
             "homepage": "https://github.com/Tobion"
         }
     ],
+    "repositories": [
+        {
+            "type": "package",
+            "package": {
+                "name": "guzzle/client-integration-tests",
+                "version": "v3.0.2",
+                "dist": {
+                    "url": "https://codeload.github.com/guzzle/client-integration-tests/zip/2c025848417c1135031fdf9c728ee53d0a7ceaee",
+                    "type": "zip"
+                },
+                "require": {
+                    "php": "^7.2.5 || ^8.0",
+                    "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.11",
+                    "php-http/message": "^1.0 || ^2.0",
+                    "guzzlehttp/psr7": "^1.7 || ^2.0",
+                    "th3n3rd/cartesian-product": "^0.3"
+                },
+                "autoload": {
+                    "psr-4": {
+                        "Http\\Client\\Tests\\": "src/"
+                    }
+                },
+                "bin": [
+                    "bin/http_test_server"
+                ]
+            }
+        }
+    ],
     "require": {
         "php": "^7.2.5 || ^8.0",
         "ext-json": "*",
-        "guzzlehttp/promises": "^1.5",
-        "guzzlehttp/psr7": "^1.8.3 || ^2.1",
+        "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+        "guzzlehttp/psr7": "^2.7.0",
         "psr/http-client": "^1.0",
-        "symfony/deprecation-contracts": "^2.2"
+        "symfony/deprecation-contracts": "^2.2 || ^3.0"
     },
     "provide": {
         "psr/http-client-implementation": "1.0"
     },
     "require-dev": {
         "ext-curl": "*",
-        "bamarni/composer-bin-plugin": "^1.4.1",
-        "php-http/client-integration-tests": "^3.0",
-        "phpunit/phpunit": "^8.5.5 || ^9.3.5",
+        "bamarni/composer-bin-plugin": "^1.8.2",
+        "guzzle/client-integration-tests": "3.0.2",
+        "php-http/message-factory": "^1.1",
+        "phpunit/phpunit": "^8.5.39 || ^9.6.20",
         "psr/log": "^1.1 || ^2.0 || ^3.0"
     },
     "suggest": {
@@ -74,12 +103,16 @@
         "psr/log": "Required for using the Log middleware"
     },
     "config": {
+        "allow-plugins": {
+            "bamarni/composer-bin-plugin": true
+        },
         "preferred-install": "dist",
         "sort-packages": true
     },
     "extra": {
-        "branch-alias": {
-            "dev-master": "7.4-dev"
+        "bamarni-bin": {
+            "bin-links": true,
+            "forward-command": false
         }
     },
     "autoload": {

+ 3 - 3
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/BodySummarizer.php

@@ -11,7 +11,7 @@ final class BodySummarizer implements BodySummarizerInterface
      */
     private $truncateAt;
 
-    public function __construct(int $truncateAt = null)
+    public function __construct(?int $truncateAt = null)
     {
         $this->truncateAt = $truncateAt;
     }
@@ -22,7 +22,7 @@ final class BodySummarizer implements BodySummarizerInterface
     public function summarize(MessageInterface $message): ?string
     {
         return $this->truncateAt === null
-            ? \GuzzleHttp\Psr7\Message::bodySummary($message)
-            : \GuzzleHttp\Psr7\Message::bodySummary($message, $this->truncateAt);
+            ? Psr7\Message::bodySummary($message)
+            : Psr7\Message::bodySummary($message, $this->truncateAt);
     }
 }

+ 23 - 17
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Client.php

@@ -52,7 +52,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
      *
      * @param array $config Client configuration settings.
      *
-     * @see \GuzzleHttp\RequestOptions for a list of available request options.
+     * @see RequestOptions for a list of available request options.
      */
     public function __construct(array $config = [])
     {
@@ -120,13 +120,14 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
     public function send(RequestInterface $request, array $options = []): ResponseInterface
     {
         $options[RequestOptions::SYNCHRONOUS] = true;
+
         return $this->sendAsync($request, $options)->wait();
     }
 
     /**
      * The HttpClient PSR (PSR-18) specify this method.
      *
-     * @inheritDoc
+     * {@inheritDoc}
      */
     public function sendRequest(RequestInterface $request): ResponseInterface
     {
@@ -184,6 +185,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
     public function request(string $method, $uri = '', array $options = []): ResponseInterface
     {
         $options[RequestOptions::SYNCHRONOUS] = true;
+
         return $this->requestAsync($method, $uri, $options)->wait();
     }
 
@@ -228,11 +230,11 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
     {
         $defaults = [
             'allow_redirects' => RedirectMiddleware::$defaultSettings,
-            'http_errors'     => true,
-            'decode_content'  => true,
-            'verify'          => true,
-            'cookies'         => false,
-            'idn_conversion'  => false,
+            'http_errors' => true,
+            'decode_content' => true,
+            'verify' => true,
+            'cookies' => false,
+            'idn_conversion' => false,
         ];
 
         // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
@@ -354,10 +356,10 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
         if (isset($options['form_params'])) {
             if (isset($options['multipart'])) {
                 throw new InvalidArgumentException('You cannot use '
-                    . 'form_params and multipart at the same time. Use the '
-                    . 'form_params option if you want to send application/'
-                    . 'x-www-form-urlencoded requests, and the multipart '
-                    . 'option to send multipart/form-data requests.');
+                    .'form_params and multipart at the same time. Use the '
+                    .'form_params option if you want to send application/'
+                    .'x-www-form-urlencoded requests, and the multipart '
+                    .'option to send multipart/form-data requests.');
             }
             $options['body'] = \http_build_query($options['form_params'], '', '&');
             unset($options['form_params']);
@@ -403,7 +405,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
                     // Ensure that we don't have the header in different case and set the new value.
                     $modify['set_headers'] = Psr7\Utils::caselessRemove(['Authorization'], $modify['set_headers']);
                     $modify['set_headers']['Authorization'] = 'Basic '
-                        . \base64_encode("$value[0]:$value[1]");
+                        .\base64_encode("$value[0]:$value[1]");
                     break;
                 case 'digest':
                     // @todo: Do not rely on curl
@@ -437,13 +439,17 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
             }
         }
 
+        if (isset($options['version'])) {
+            $modify['version'] = $options['version'];
+        }
+
         $request = Psr7\Utils::modifyRequest($request, $modify);
         if ($request->getBody() instanceof Psr7\MultipartStream) {
             // Use a multipart/form-data POST if a Content-Type is not set.
             // 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']['Content-Type'] = 'multipart/form-data; boundary='
-                . $request->getBody()->getBoundary();
+                .$request->getBody()->getBoundary();
         }
 
         // Merge in conditional headers if they are not present.
@@ -469,9 +475,9 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
     private function invalidBody(): InvalidArgumentException
     {
         return new InvalidArgumentException('Passing in the "body" request '
-            . 'option as an array to send a request is not supported. '
-            . 'Please use the "form_params" request option to send a '
-            . 'application/x-www-form-urlencoded request, or the "multipart" '
-            . 'request option to send a multipart/form-data request.');
+            .'option as an array to send a request is not supported. '
+            .'Please use the "form_params" request option to send a '
+            .'application/x-www-form-urlencoded request, or the "multipart" '
+            .'request option to send a multipart/form-data request.');
     }
 }

+ 27 - 33
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Cookie/CookieJar.php

@@ -50,10 +50,10 @@ class CookieJar implements CookieJarInterface
         $cookieJar = new self();
         foreach ($cookies as $name => $value) {
             $cookieJar->setCookie(new SetCookie([
-                'Domain'  => $domain,
-                'Name'    => $name,
-                'Value'   => $value,
-                'Discard' => true
+                'Domain' => $domain,
+                'Name' => $name,
+                'Value' => $value,
+                'Discard' => true,
             ]));
         }
 
@@ -96,9 +96,6 @@ class CookieJar implements CookieJarInterface
         return null;
     }
 
-    /**
-     * @inheritDoc
-     */
     public function toArray(): array
     {
         return \array_map(static function (SetCookie $cookie): array {
@@ -106,13 +103,11 @@ class CookieJar implements CookieJarInterface
         }, $this->getIterator()->getArrayCopy());
     }
 
-    /**
-     * @inheritDoc
-     */
     public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void
     {
         if (!$domain) {
             $this->cookies = [];
+
             return;
         } elseif (!$path) {
             $this->cookies = \array_filter(
@@ -125,25 +120,22 @@ class CookieJar implements CookieJarInterface
             $this->cookies = \array_filter(
                 $this->cookies,
                 static function (SetCookie $cookie) use ($path, $domain): bool {
-                    return !($cookie->matchesPath($path) &&
-                        $cookie->matchesDomain($domain));
+                    return !($cookie->matchesPath($path)
+                        && $cookie->matchesDomain($domain));
                 }
             );
         } else {
             $this->cookies = \array_filter(
                 $this->cookies,
                 static function (SetCookie $cookie) use ($path, $domain, $name) {
-                    return !($cookie->getName() == $name &&
-                        $cookie->matchesPath($path) &&
-                        $cookie->matchesDomain($domain));
+                    return !($cookie->getName() == $name
+                        && $cookie->matchesPath($path)
+                        && $cookie->matchesDomain($domain));
                 }
             );
         }
     }
 
-    /**
-     * @inheritDoc
-     */
     public function clearSessionCookies(): void
     {
         $this->cookies = \array_filter(
@@ -154,9 +146,6 @@ class CookieJar implements CookieJarInterface
         );
     }
 
-    /**
-     * @inheritDoc
-     */
     public function setCookie(SetCookie $cookie): bool
     {
         // If the name string is empty (but not 0), ignore the set-cookie
@@ -170,20 +159,20 @@ class CookieJar implements CookieJarInterface
         $result = $cookie->validate();
         if ($result !== true) {
             if ($this->strictMode) {
-                throw new \RuntimeException('Invalid cookie: ' . $result);
+                throw new \RuntimeException('Invalid cookie: '.$result);
             }
             $this->removeCookieIfEmpty($cookie);
+
             return false;
         }
 
         // Resolve conflicts with previously set cookies
         foreach ($this->cookies as $i => $c) {
-
             // Two cookies are identical, when their path, and domain are
             // identical.
-            if ($c->getPath() != $cookie->getPath() ||
-                $c->getDomain() != $cookie->getDomain() ||
-                $c->getName() != $cookie->getName()
+            if ($c->getPath() != $cookie->getPath()
+                || $c->getDomain() != $cookie->getDomain()
+                || $c->getName() != $cookie->getName()
             ) {
                 continue;
             }
@@ -241,6 +230,11 @@ class CookieJar implements CookieJarInterface
                 if (0 !== \strpos($sc->getPath(), '/')) {
                     $sc->setPath($this->getCookiePathFromRequest($request));
                 }
+                if (!$sc->matchesDomain($request->getUri()->getHost())) {
+                    continue;
+                }
+                // Note: At this point `$sc->getDomain()` being a public suffix should
+                // be rejected, but we don't want to pull in the full PSL dependency.
                 $this->setCookie($sc);
             }
         }
@@ -249,7 +243,7 @@ class CookieJar implements CookieJarInterface
     /**
      * Computes cookie path following RFC 6265 section 5.1.4
      *
-     * @link https://tools.ietf.org/html/rfc6265#section-5.1.4
+     * @see https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4
      */
     private function getCookiePathFromRequest(RequestInterface $request): string
     {
@@ -280,13 +274,13 @@ class CookieJar implements CookieJarInterface
         $path = $uri->getPath() ?: '/';
 
         foreach ($this->cookies as $cookie) {
-            if ($cookie->matchesPath($path) &&
-                $cookie->matchesDomain($host) &&
-                !$cookie->isExpired() &&
-                (!$cookie->getSecure() || $scheme === 'https')
+            if ($cookie->matchesPath($path)
+                && $cookie->matchesDomain($host)
+                && !$cookie->isExpired()
+                && (!$cookie->getSecure() || $scheme === 'https')
             ) {
-                $values[] = $cookie->getName() . '='
-                    . $cookie->getValue();
+                $values[] = $cookie->getName().'='
+                    .$cookie->getValue();
             }
         }
 

+ 2 - 1
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php

@@ -13,7 +13,8 @@ use Psr\Http\Message\ResponseInterface;
  * necessary. Subclasses are also responsible for storing and retrieving
  * cookies from a file, database, etc.
  *
- * @link https://docs.python.org/2/library/cookielib.html Inspiration
+ * @see https://docs.python.org/2/library/cookielib.html Inspiration
+ *
  * @extends \IteratorAggregate<SetCookie>
  */
 interface CookieJarInterface extends \Countable, \IteratorAggregate

+ 1 - 1
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php

@@ -71,7 +71,7 @@ class SessionCookieJar extends CookieJar
                 $this->setCookie(new SetCookie($cookie));
             }
         } elseif (\strlen($data)) {
-            throw new \RuntimeException("Invalid cookie data");
+            throw new \RuntimeException('Invalid cookie data');
         }
     }
 }

+ 73 - 25
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Cookie/SetCookie.php

@@ -11,15 +11,15 @@ class SetCookie
      * @var array
      */
     private static $defaults = [
-        'Name'     => null,
-        'Value'    => null,
-        'Domain'   => null,
-        'Path'     => '/',
-        'Max-Age'  => null,
-        'Expires'  => null,
-        'Secure'   => false,
-        'Discard'  => false,
-        'HttpOnly' => false
+        'Name' => null,
+        'Value' => null,
+        'Domain' => null,
+        'Path' => '/',
+        'Max-Age' => null,
+        'Expires' => null,
+        'Secure' => false,
+        'Discard' => false,
+        'HttpOnly' => false,
     ];
 
     /**
@@ -58,7 +58,17 @@ class SetCookie
             } else {
                 foreach (\array_keys(self::$defaults) as $search) {
                     if (!\strcasecmp($search, $key)) {
-                        $data[$search] = $value;
+                        if ($search === 'Max-Age') {
+                            if (is_numeric($value)) {
+                                $data[$search] = (int) $value;
+                            }
+                        } elseif ($search === 'Secure' || $search === 'Discard' || $search === 'HttpOnly') {
+                            if ($value) {
+                                $data[$search] = true;
+                            }
+                        } else {
+                            $data[$search] = $value;
+                        }
                         continue 2;
                     }
                 }
@@ -74,13 +84,49 @@ class SetCookie
      */
     public function __construct(array $data = [])
     {
-        /** @var array|null $replaced will be null in case of replace error */
-        $replaced = \array_replace(self::$defaults, $data);
-        if ($replaced === null) {
-            throw new \InvalidArgumentException('Unable to replace the default values for the Cookie.');
+        $this->data = self::$defaults;
+
+        if (isset($data['Name'])) {
+            $this->setName($data['Name']);
+        }
+
+        if (isset($data['Value'])) {
+            $this->setValue($data['Value']);
+        }
+
+        if (isset($data['Domain'])) {
+            $this->setDomain($data['Domain']);
+        }
+
+        if (isset($data['Path'])) {
+            $this->setPath($data['Path']);
+        }
+
+        if (isset($data['Max-Age'])) {
+            $this->setMaxAge($data['Max-Age']);
+        }
+
+        if (isset($data['Expires'])) {
+            $this->setExpires($data['Expires']);
+        }
+
+        if (isset($data['Secure'])) {
+            $this->setSecure($data['Secure']);
+        }
+
+        if (isset($data['Discard'])) {
+            $this->setDiscard($data['Discard']);
+        }
+
+        if (isset($data['HttpOnly'])) {
+            $this->setHttpOnly($data['HttpOnly']);
+        }
+
+        // Set the remaining values that don't have extra validation logic
+        foreach (array_diff(array_keys($data), array_keys(self::$defaults)) as $key) {
+            $this->data[$key] = $data[$key];
         }
 
-        $this->data = $replaced;
         // Extract the Expires value and turn it into a UNIX timestamp if needed
         if (!$this->getExpires() && $this->getMaxAge()) {
             // Calculate the Expires date
@@ -92,13 +138,13 @@ class SetCookie
 
     public function __toString()
     {
-        $str = $this->data['Name'] . '=' . ($this->data['Value'] ?? '') . '; ';
+        $str = $this->data['Name'].'='.($this->data['Value'] ?? '').'; ';
         foreach ($this->data as $k => $v) {
             if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) {
                 if ($k === 'Expires') {
-                    $str .= 'Expires=' . \gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
+                    $str .= 'Expires='.\gmdate('D, d M Y H:i:s \G\M\T', $v).'; ';
                 } else {
-                    $str .= ($v === true ? $k : "{$k}={$v}") . '; ';
+                    $str .= ($v === true ? $k : "{$k}={$v}").'; ';
                 }
             }
         }
@@ -378,21 +424,23 @@ class SetCookie
         }
 
         // Remove the leading '.' as per spec in RFC 6265.
-        // https://tools.ietf.org/html/rfc6265#section-5.2.3
-        $cookieDomain = \ltrim($cookieDomain, '.');
+        // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3
+        $cookieDomain = \ltrim(\strtolower($cookieDomain), '.');
+
+        $domain = \strtolower($domain);
 
         // Domain not set or exact match.
-        if (!$cookieDomain || !\strcasecmp($domain, $cookieDomain)) {
+        if ('' === $cookieDomain || $domain === $cookieDomain) {
             return true;
         }
 
         // Matching the subdomain according to RFC 6265.
-        // https://tools.ietf.org/html/rfc6265#section-5.1.3
+        // https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
         if (\filter_var($domain, \FILTER_VALIDATE_IP)) {
             return false;
         }
 
-        return (bool) \preg_match('/\.' . \preg_quote($cookieDomain, '/') . '$/', $domain);
+        return (bool) \preg_match('/\.'.\preg_quote($cookieDomain, '/').'$/', $domain);
     }
 
     /**
@@ -421,8 +469,8 @@ class SetCookie
             $name
         )) {
             return 'Cookie name must not contain invalid characters: ASCII '
-                . 'Control characters (0-31;127), space, tab and the '
-                . 'following characters: ()<>@,;:\"/?={}';
+                .'Control characters (0-31;127), space, tab and the '
+                .'following characters: ()<>@,;:\"/?={}';
         }
 
         // Value must not be null. 0 and empty string are valid. Empty strings

+ 1 - 1
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Exception/BadResponseException.php

@@ -14,7 +14,7 @@ class BadResponseException extends RequestException
         string $message,
         RequestInterface $request,
         ResponseInterface $response,
-        \Throwable $previous = null,
+        ?\Throwable $previous = null,
         array $handlerContext = []
     ) {
         parent::__construct($message, $request, $response, $previous, $handlerContext);

+ 1 - 1
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Exception/ConnectException.php

@@ -25,7 +25,7 @@ class ConnectException extends TransferException implements NetworkExceptionInte
     public function __construct(
         string $message,
         RequestInterface $request,
-        \Throwable $previous = null,
+        ?\Throwable $previous = null,
         array $handlerContext = []
     ) {
         parent::__construct($message, 0, $previous);

+ 7 - 23
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Exception/RequestException.php

@@ -7,7 +7,6 @@ use GuzzleHttp\BodySummarizerInterface;
 use Psr\Http\Client\RequestExceptionInterface;
 use Psr\Http\Message\RequestInterface;
 use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\UriInterface;
 
 /**
  * HTTP Request exception
@@ -32,8 +31,8 @@ class RequestException extends TransferException implements RequestExceptionInte
     public function __construct(
         string $message,
         RequestInterface $request,
-        ResponseInterface $response = null,
-        \Throwable $previous = null,
+        ?ResponseInterface $response = null,
+        ?\Throwable $previous = null,
         array $handlerContext = []
     ) {
         // Set the code of the exception if the response is set and not future.
@@ -63,10 +62,10 @@ class RequestException extends TransferException implements RequestExceptionInte
      */
     public static function create(
         RequestInterface $request,
-        ResponseInterface $response = null,
-        \Throwable $previous = null,
+        ?ResponseInterface $response = null,
+        ?\Throwable $previous = null,
         array $handlerContext = [],
-        BodySummarizerInterface $bodySummarizer = null
+        ?BodySummarizerInterface $bodySummarizer = null
     ): self {
         if (!$response) {
             return new self(
@@ -90,8 +89,7 @@ class RequestException extends TransferException implements RequestExceptionInte
             $className = __CLASS__;
         }
 
-        $uri = $request->getUri();
-        $uri = static::obfuscateUri($uri);
+        $uri = \GuzzleHttp\Psr7\Utils::redactUserInfo($request->getUri());
 
         // Client Error: `GET /` resulted in a `404 Not Found` response:
         // <html> ... (truncated)
@@ -99,7 +97,7 @@ class RequestException extends TransferException implements RequestExceptionInte
             '%s: `%s %s` resulted in a `%s %s` response',
             $label,
             $request->getMethod(),
-            $uri,
+            $uri->__toString(),
             $response->getStatusCode(),
             $response->getReasonPhrase()
         );
@@ -113,20 +111,6 @@ class RequestException extends TransferException implements RequestExceptionInte
         return new $className($message, $request, $response, $previous, $handlerContext);
     }
 
-    /**
-     * Obfuscates URI if there is a username and a password present
-     */
-    private static function obfuscateUri(UriInterface $uri): UriInterface
-    {
-        $userInfo = $uri->getUserInfo();
-
-        if (false !== ($pos = \strpos($userInfo, ':'))) {
-            return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***');
-        }
-
-        return $uri;
-    }
-
     /**
      * Get the request that caused the exception
      */

+ 179 - 38
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/CurlFactory.php

@@ -11,6 +11,7 @@ use GuzzleHttp\Psr7\LazyOpenStream;
 use GuzzleHttp\TransferStats;
 use GuzzleHttp\Utils;
 use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\UriInterface;
 
 /**
  * Creates curl resources from a request
@@ -46,12 +47,22 @@ class CurlFactory implements CurlFactoryInterface
 
     public function create(RequestInterface $request, array $options): EasyHandle
     {
+        $protocolVersion = $request->getProtocolVersion();
+
+        if ('2' === $protocolVersion || '2.0' === $protocolVersion) {
+            if (!self::supportsHttp2()) {
+                throw new ConnectException('HTTP/2 is supported by the cURL handler, however libcurl is built without HTTP/2 support.', $request);
+            }
+        } elseif ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) {
+            throw new ConnectException(sprintf('HTTP/%s is not supported by the cURL handler.', $protocolVersion), $request);
+        }
+
         if (isset($options['curl']['body_as_string'])) {
             $options['_body_as_string'] = $options['curl']['body_as_string'];
             unset($options['curl']['body_as_string']);
         }
 
-        $easy = new EasyHandle;
+        $easy = new EasyHandle();
         $easy->request = $request;
         $easy->options = $options;
         $conf = $this->getDefaultConf($easy);
@@ -72,6 +83,42 @@ class CurlFactory implements CurlFactoryInterface
         return $easy;
     }
 
+    private static function supportsHttp2(): bool
+    {
+        static $supportsHttp2 = null;
+
+        if (null === $supportsHttp2) {
+            $supportsHttp2 = self::supportsTls12()
+                && defined('CURL_VERSION_HTTP2')
+                && (\CURL_VERSION_HTTP2 & \curl_version()['features']);
+        }
+
+        return $supportsHttp2;
+    }
+
+    private static function supportsTls12(): bool
+    {
+        static $supportsTls12 = null;
+
+        if (null === $supportsTls12) {
+            $supportsTls12 = \CURL_SSLVERSION_TLSv1_2 & \curl_version()['features'];
+        }
+
+        return $supportsTls12;
+    }
+
+    private static function supportsTls13(): bool
+    {
+        static $supportsTls13 = null;
+
+        if (null === $supportsTls13) {
+            $supportsTls13 = defined('CURL_SSLVERSION_TLSv1_3')
+                && (\CURL_SSLVERSION_TLSv1_3 & \curl_version()['features']);
+        }
+
+        return $supportsTls13;
+    }
+
     public function release(EasyHandle $easy): void
     {
         $resource = $easy->handle;
@@ -147,7 +194,7 @@ class CurlFactory implements CurlFactoryInterface
             'error' => \curl_error($easy->handle),
             'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME),
         ] + \curl_getinfo($easy->handle);
-        $ctx[self::CURL_VERSION_STR] = \curl_version()['version'];
+        $ctx[self::CURL_VERSION_STR] = self::getCurlVersion();
         $factory->release($easy);
 
         // Retry when nothing is present or when curl failed to rewind.
@@ -158,14 +205,25 @@ class CurlFactory implements CurlFactoryInterface
         return self::createRejection($easy, $ctx);
     }
 
+    private static function getCurlVersion(): string
+    {
+        static $curlVersion = null;
+
+        if (null === $curlVersion) {
+            $curlVersion = \curl_version()['version'];
+        }
+
+        return $curlVersion;
+    }
+
     private static function createRejection(EasyHandle $easy, array $ctx): PromiseInterface
     {
         static $connectionErrors = [
-            \CURLE_OPERATION_TIMEOUTED  => true,
+            \CURLE_OPERATION_TIMEOUTED => true,
             \CURLE_COULDNT_RESOLVE_HOST => true,
-            \CURLE_COULDNT_CONNECT      => true,
-            \CURLE_SSL_CONNECT_ERROR    => true,
-            \CURLE_GOT_NOTHING          => true,
+            \CURLE_COULDNT_CONNECT => true,
+            \CURLE_SSL_CONNECT_ERROR => true,
+            \CURLE_GOT_NOTHING => true,
         ];
 
         if ($easy->createResponseException) {
@@ -194,15 +252,22 @@ class CurlFactory implements CurlFactoryInterface
             );
         }
 
+        $uri = $easy->request->getUri();
+
+        $sanitizedError = self::sanitizeCurlError($ctx['error'] ?? '', $uri);
+
         $message = \sprintf(
             'cURL error %s: %s (%s)',
             $ctx['errno'],
-            $ctx['error'],
+            $sanitizedError,
             'see https://curl.haxx.se/libcurl/c/libcurl-errors.html'
         );
-        $uriString = (string) $easy->request->getUri();
-        if ($uriString !== '' && false === \strpos($ctx['error'], $uriString)) {
-            $message .= \sprintf(' for %s', $uriString);
+
+        if ('' !== $sanitizedError) {
+            $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($uri)->__toString();
+            if ($redactedUriString !== '' && false === \strpos($sanitizedError, $redactedUriString)) {
+                $message .= \sprintf(' for %s', $redactedUriString);
+            }
         }
 
         // Create a connection exception if it was a specific error code.
@@ -213,18 +278,36 @@ class CurlFactory implements CurlFactoryInterface
         return P\Create::rejectionFor($error);
     }
 
+    private static function sanitizeCurlError(string $error, UriInterface $uri): string
+    {
+        if ('' === $error) {
+            return $error;
+        }
+
+        $baseUri = $uri->withQuery('')->withFragment('');
+        $baseUriString = $baseUri->__toString();
+
+        if ('' === $baseUriString) {
+            return $error;
+        }
+
+        $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($baseUri)->__toString();
+
+        return str_replace($baseUriString, $redactedUriString, $error);
+    }
+
     /**
      * @return array<int|string, mixed>
      */
     private function getDefaultConf(EasyHandle $easy): array
     {
         $conf = [
-            '_headers'              => $easy->request->getHeaders(),
-            \CURLOPT_CUSTOMREQUEST  => $easy->request->getMethod(),
-            \CURLOPT_URL            => (string) $easy->request->getUri()->withFragment(''),
+            '_headers' => $easy->request->getHeaders(),
+            \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
+            \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
             \CURLOPT_RETURNTRANSFER => false,
-            \CURLOPT_HEADER         => false,
-            \CURLOPT_CONNECTTIMEOUT => 150,
+            \CURLOPT_HEADER => false,
+            \CURLOPT_CONNECTTIMEOUT => 300,
         ];
 
         if (\defined('CURLOPT_PROTOCOLS')) {
@@ -232,10 +315,11 @@ class CurlFactory implements CurlFactoryInterface
         }
 
         $version = $easy->request->getProtocolVersion();
-        if ($version == 1.1) {
-            $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1;
-        } elseif ($version == 2.0) {
+
+        if ('2' === $version || '2.0' === $version) {
             $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0;
+        } elseif ('1.1' === $version) {
+            $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1;
         } else {
             $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0;
         }
@@ -250,12 +334,13 @@ class CurlFactory implements CurlFactoryInterface
 
         if ($size === null || $size > 0) {
             $this->applyBody($easy->request, $easy->options, $conf);
+
             return;
         }
 
         $method = $easy->request->getMethod();
         if ($method === 'PUT' || $method === 'POST') {
-            // See https://tools.ietf.org/html/rfc7230#section-3.3.2
+            // See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
             if (!$easy->request->hasHeader('Content-Length')) {
                 $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
             }
@@ -341,6 +426,7 @@ class CurlFactory implements CurlFactoryInterface
         foreach (\array_keys($options['_headers']) as $key) {
             if (!\strcasecmp($key, $name)) {
                 unset($options['_headers'][$key]);
+
                 return;
             }
         }
@@ -365,11 +451,11 @@ class CurlFactory implements CurlFactoryInterface
                     // If it's a directory or a link to a directory use CURLOPT_CAPATH.
                     // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
                     if (
-                        \is_dir($options['verify']) ||
-                        (
-                            \is_link($options['verify']) === true &&
-                            ($verifyLink = \readlink($options['verify'])) !== false &&
-                            \is_dir($verifyLink)
+                        \is_dir($options['verify'])
+                        || (
+                            \is_link($options['verify']) === true
+                            && ($verifyLink = \readlink($options['verify'])) !== false
+                            && \is_dir($verifyLink)
                         )
                     ) {
                         $conf[\CURLOPT_CAPATH] = $options['verify'];
@@ -388,8 +474,10 @@ class CurlFactory implements CurlFactoryInterface
                 // The empty string enables all available decoders and implicitly
                 // sets a matching 'Accept-Encoding' header.
                 $conf[\CURLOPT_ENCODING] = '';
-                // But as the user did not specify any acceptable encodings we need
-                // to overwrite this implicit header with an empty one.
+                // But as the user did not specify any encoding preference,
+                // let's leave it up to server by preventing curl from sending
+                // the header, which will be interpreted as 'Accept-Encoding: *'.
+                // https://www.rfc-editor.org/rfc/rfc9110#field.accept-encoding
                 $conf[\CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
             }
         }
@@ -443,13 +531,53 @@ class CurlFactory implements CurlFactoryInterface
                 $scheme = $easy->request->getUri()->getScheme();
                 if (isset($options['proxy'][$scheme])) {
                     $host = $easy->request->getUri()->getHost();
-                    if (!isset($options['proxy']['no']) || !Utils::isHostInNoProxy($host, $options['proxy']['no'])) {
+                    if (isset($options['proxy']['no']) && Utils::isHostInNoProxy($host, $options['proxy']['no'])) {
+                        unset($conf[\CURLOPT_PROXY]);
+                    } else {
                         $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme];
                     }
                 }
             }
         }
 
+        if (isset($options['crypto_method'])) {
+            $protocolVersion = $easy->request->getProtocolVersion();
+
+            // If HTTP/2, upgrade TLS 1.0 and 1.1 to 1.2
+            if ('2' === $protocolVersion || '2.0' === $protocolVersion) {
+                if (
+                    \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']
+                    || \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']
+                    || \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']
+                ) {
+                    $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2;
+                } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) {
+                    if (!self::supportsTls13()) {
+                        throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL');
+                    }
+                    $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3;
+                } else {
+                    throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
+                }
+            } elseif (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) {
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0;
+            } elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) {
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1;
+            } elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) {
+                if (!self::supportsTls12()) {
+                    throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL');
+                }
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2;
+            } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) {
+                if (!self::supportsTls13()) {
+                    throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL');
+                }
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3;
+            } else {
+                throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
+            }
+        }
+
         if (isset($options['cert'])) {
             $cert = $options['cert'];
             if (\is_array($cert)) {
@@ -459,8 +587,8 @@ class CurlFactory implements CurlFactoryInterface
             if (!\file_exists($cert)) {
                 throw new \InvalidArgumentException("SSL certificate not found: {$cert}");
             }
-            # OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files.
-            # see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html
+            // OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files.
+            // see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html
             $ext = pathinfo($cert, \PATHINFO_EXTENSION);
             if (preg_match('#^(der|p12)$#i', $ext)) {
                 $conf[\CURLOPT_SSLCERTTYPE] = strtoupper($ext);
@@ -523,9 +651,10 @@ class CurlFactory implements CurlFactoryInterface
             }
         } catch (\RuntimeException $e) {
             $ctx['error'] = 'The connection unexpectedly failed without '
-                . 'providing an error. The request would have been retried, '
-                . 'but attempting to rewind the request body failed. '
-                . 'Exception: ' . $e;
+                .'providing an error. The request would have been retried, '
+                .'but attempting to rewind the request body failed. '
+                .'Exception: '.$e;
+
             return self::createRejection($easy, $ctx);
         }
 
@@ -534,14 +663,15 @@ class CurlFactory implements CurlFactoryInterface
             $easy->options['_curl_retries'] = 1;
         } elseif ($easy->options['_curl_retries'] == 2) {
             $ctx['error'] = 'The cURL request was retried 3 times '
-                . 'and did not succeed. The most likely reason for the failure '
-                . 'is that cURL was unable to rewind the body of the request '
-                . 'and subsequent retries resulted in the same error. Turn on '
-                . 'the debug option to see what went wrong. See '
-                . 'https://bugs.php.net/bug.php?id=47204 for more information.';
+                .'and did not succeed. The most likely reason for the failure '
+                .'is that cURL was unable to rewind the body of the request '
+                .'and subsequent retries resulted in the same error. Turn on '
+                .'the debug option to see what went wrong. See '
+                .'https://bugs.php.net/bug.php?id=47204 for more information.';
+
             return self::createRejection($easy, $ctx);
         } else {
-            $easy->options['_curl_retries']++;
+            ++$easy->options['_curl_retries'];
         }
 
         return $handler($easy->request, $easy->options);
@@ -571,6 +701,7 @@ class CurlFactory implements CurlFactoryInterface
                     $easy->createResponse();
                 } catch (\Exception $e) {
                     $easy->createResponseException = $e;
+
                     return -1;
                 }
                 if ($onHeaders !== null) {
@@ -580,6 +711,7 @@ class CurlFactory implements CurlFactoryInterface
                         // Associate the exception with the handle and trigger
                         // a curl header write error by returning 0.
                         $easy->onHeadersException = $e;
+
                         return -1;
                     }
                 }
@@ -589,7 +721,16 @@ class CurlFactory implements CurlFactoryInterface
             } else {
                 $easy->headers[] = $value;
             }
+
             return \strlen($h);
         };
     }
+
+    public function __destruct()
+    {
+        foreach ($this->handles as $id => $handle) {
+            \curl_close($handle);
+            unset($this->handles[$id]);
+        }
+    }
 }

+ 32 - 5
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php

@@ -2,6 +2,7 @@
 
 namespace GuzzleHttp\Handler;
 
+use Closure;
 use GuzzleHttp\Promise as P;
 use GuzzleHttp\Promise\Promise;
 use GuzzleHttp\Promise\PromiseInterface;
@@ -15,8 +16,6 @@ use Psr\Http\Message\RequestInterface;
  * associative array of curl option constants mapping to values in the
  * **curl** key of the provided request options.
  *
- * @property resource|\CurlMultiHandle $_mh Internal use only. Lazy loaded multi-handle.
- *
  * @final
  */
 class CurlMultiHandler
@@ -32,9 +31,9 @@ class CurlMultiHandler
     private $selectTimeout;
 
     /**
-     * @var resource|\CurlMultiHandle|null the currently executing resource in `curl_multi_exec`.
+     * @var int Will be higher than 0 when `curl_multi_exec` is still running.
      */
-    private $active;
+    private $active = 0;
 
     /**
      * @var array Request entry handles, indexed by handle id in `addRequest`.
@@ -55,6 +54,9 @@ class CurlMultiHandler
      */
     private $options = [];
 
+    /** @var resource|\CurlMultiHandle */
+    private $_mh;
+
     /**
      * This handler accepts the following options:
      *
@@ -78,6 +80,10 @@ class CurlMultiHandler
         }
 
         $this->options = $options['options'] ?? [];
+
+        // unsetting the property forces the first access to go through
+        // __get().
+        unset($this->_mh);
     }
 
     /**
@@ -154,6 +160,9 @@ class CurlMultiHandler
             }
         }
 
+        // Run curl_multi_exec in the queue to enable other async tasks to run
+        P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue']));
+
         // Step through the task queue which may add additional requests.
         P\Utils::queue()->run();
 
@@ -163,11 +172,25 @@ class CurlMultiHandler
             \usleep(250);
         }
 
-        while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM);
+        while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) {
+            // Prevent busy looping for slow HTTP requests.
+            \curl_multi_select($this->_mh, $this->selectTimeout);
+        }
 
         $this->processMessages();
     }
 
+    /**
+     * Runs \curl_multi_exec() inside the event loop, to prevent busy looping
+     */
+    private function tickInQueue(): void
+    {
+        if (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) {
+            \curl_multi_select($this->_mh, 0);
+            P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue']));
+        }
+    }
+
     /**
      * Runs until all outstanding connections have completed.
      */
@@ -225,6 +248,10 @@ class CurlMultiHandler
     private function processMessages(): void
     {
         while ($done = \curl_multi_info_read($this->_mh)) {
+            if ($done['msg'] !== \CURLMSG_DONE) {
+                // if it's not done, then it would be premature to remove the handle. ref https://github.com/guzzle/guzzle/pull/2892#issuecomment-945150216
+                continue;
+            }
             $id = (int) $done['handle'];
             \curl_multi_remove_handle($this->_mh, $done['handle']);
 

+ 1 - 1
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/EasyHandle.php

@@ -106,7 +106,7 @@ final class EasyHandle
      */
     public function __get($name)
     {
-        $msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: ' . $name;
+        $msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: '.$name;
         throw new \BadMethodCallException($msg);
     }
 }

+ 2 - 2
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php

@@ -14,9 +14,9 @@ final class HeaderProcessor
      *
      * @param string[] $headers
      *
-     * @throws \RuntimeException
-     *
      * @return array{0:string, 1:int, 2:?string, 3:array}
+     *
+     * @throws \RuntimeException
      */
     public static function parseHeaders(array $headers): array
     {

+ 6 - 5
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/MockHandler.php

@@ -52,21 +52,21 @@ class MockHandler implements \Countable
      * @param callable|null $onFulfilled Callback to invoke when the return value is fulfilled.
      * @param callable|null $onRejected  Callback to invoke when the return value is rejected.
      */
-    public static function createWithMiddleware(array $queue = null, callable $onFulfilled = null, callable $onRejected = null): HandlerStack
+    public static function createWithMiddleware(?array $queue = null, ?callable $onFulfilled = null, ?callable $onRejected = null): HandlerStack
     {
         return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
     }
 
     /**
      * The passed in value must be an array of
-     * {@see \Psr\Http\Message\ResponseInterface} objects, Exceptions,
+     * {@see ResponseInterface} objects, Exceptions,
      * callables, or Promises.
      *
      * @param array<int, mixed>|null $queue       The parameters to be passed to the append function, as an indexed array.
      * @param callable|null          $onFulfilled Callback to invoke when the return value is fulfilled.
      * @param callable|null          $onRejected  Callback to invoke when the return value is rejected.
      */
-    public function __construct(array $queue = null, callable $onFulfilled = null, callable $onRejected = null)
+    public function __construct(?array $queue = null, ?callable $onFulfilled = null, ?callable $onRejected = null)
     {
         $this->onFulfilled = $onFulfilled;
         $this->onRejected = $onRejected;
@@ -138,6 +138,7 @@ class MockHandler implements \Countable
                 if ($this->onRejected) {
                     ($this->onRejected)($reason);
                 }
+
                 return P\Create::rejectionFor($reason);
             }
         );
@@ -159,7 +160,7 @@ class MockHandler implements \Countable
             ) {
                 $this->queue[] = $value;
             } 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 '.Utils::describeType($value));
             }
         }
     }
@@ -199,7 +200,7 @@ class MockHandler implements \Countable
     private function invokeStats(
         RequestInterface $request,
         array $options,
-        ResponseInterface $response = null,
+        ?ResponseInterface $response = null,
         $reason = null
     ): void {
         if (isset($options['on_stats'])) {

+ 6 - 6
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/Proxy.php

@@ -17,10 +17,10 @@ class Proxy
      * Sends synchronous requests to a specific handler while sending all other
      * requests to another handler.
      *
-     * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $default Handler used for normal responses
-     * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $sync    Handler used for synchronous responses.
+     * @param callable(RequestInterface, array): PromiseInterface $default Handler used for normal responses
+     * @param callable(RequestInterface, array): PromiseInterface $sync    Handler used for synchronous responses.
      *
-     * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the composed handler.
+     * @return callable(RequestInterface, array): PromiseInterface Returns the composed handler.
      */
     public static function wrapSync(callable $default, callable $sync): callable
     {
@@ -37,10 +37,10 @@ class Proxy
      * performance benefits of curl while still supporting true streaming
      * through the StreamHandler.
      *
-     * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $default   Handler used for non-streaming responses
-     * @param callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface $streaming Handler used for streaming responses
+     * @param callable(RequestInterface, array): PromiseInterface $default   Handler used for non-streaming responses
+     * @param callable(RequestInterface, array): PromiseInterface $streaming Handler used for streaming responses
      *
-     * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the composed handler.
+     * @return callable(RequestInterface, array): PromiseInterface Returns the composed handler.
      */
     public static function wrapStreaming(callable $default, callable $streaming): callable
     {

+ 69 - 28
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Handler/StreamHandler.php

@@ -40,6 +40,12 @@ class StreamHandler
             \usleep($options['delay'] * 1000);
         }
 
+        $protocolVersion = $request->getProtocolVersion();
+
+        if ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) {
+            throw new ConnectException(sprintf('HTTP/%s is not supported by the stream handler.', $protocolVersion), $request);
+        }
+
         $startTime = isset($options['on_stats']) ? Utils::currentTime() : null;
 
         try {
@@ -47,8 +53,14 @@ class StreamHandler
             $request = $request->withoutHeader('Expect');
 
             // Append a content-length header if body size is zero to match
-            // cURL's behavior.
-            if (0 === $request->getBody()->getSize()) {
+            // the behavior of `CurlHandler`
+            if (
+                (
+                    0 === \strcasecmp('PUT', $request->getMethod())
+                    || 0 === \strcasecmp('POST', $request->getMethod())
+                )
+                && 0 === $request->getBody()->getSize()
+            ) {
                 $request = $request->withHeader('Content-Length', '0');
             }
 
@@ -67,7 +79,7 @@ class StreamHandler
             if (false !== \strpos($message, 'getaddrinfo') // DNS lookup failed
                 || false !== \strpos($message, 'Connection refused')
                 || false !== \strpos($message, "couldn't connect to host") // error on HHVM
-                || false !== \strpos($message, "connection attempt failed")
+                || false !== \strpos($message, 'connection attempt failed')
             ) {
                 $e = new ConnectException($e->getMessage(), $request, $e);
             } else {
@@ -83,8 +95,8 @@ class StreamHandler
         array $options,
         RequestInterface $request,
         ?float $startTime,
-        ResponseInterface $response = null,
-        \Throwable $error = null
+        ?ResponseInterface $response = null,
+        ?\Throwable $error = null
     ): void {
         if (isset($options['on_stats'])) {
             $stats = new TransferStats($request, $response, Utils::currentTime() - $startTime, $error, []);
@@ -231,9 +243,10 @@ class StreamHandler
         \set_error_handler(static function ($_, $msg, $file, $line) use (&$errors): bool {
             $errors[] = [
                 'message' => $msg,
-                'file'    => $file,
-                'line'    => $line
+                'file' => $file,
+                'line' => $line,
             ];
+
             return true;
         });
 
@@ -247,7 +260,7 @@ class StreamHandler
             $message = 'Error creating resource: ';
             foreach ($errors as $err) {
                 foreach ($err as $key => $value) {
-                    $message .= "[$key] $value" . \PHP_EOL;
+                    $message .= "[$key] $value".\PHP_EOL;
                 }
             }
             throw new \RuntimeException(\trim($message));
@@ -266,9 +279,13 @@ class StreamHandler
             $methods = \array_flip(\get_class_methods(__CLASS__));
         }
 
+        if (!\in_array($request->getUri()->getScheme(), ['http', 'https'])) {
+            throw new RequestException(\sprintf("The scheme '%s' is not supported.", $request->getUri()->getScheme()), $request);
+        }
+
         // HTTP/1.1 streams using the PHP stream wrapper require a
         // Connection: close header
-        if ($request->getProtocolVersion() == '1.1'
+        if ($request->getProtocolVersion() === '1.1'
             && !$request->hasHeader('Connection')
         ) {
             $request = $request->withHeader('Connection', 'close');
@@ -318,7 +335,7 @@ class StreamHandler
         return $this->createResource(
             function () use ($uri, &$http_response_header, $contextResource, $context, $options, $request) {
                 $resource = @\fopen((string) $uri, 'r', false, $contextResource);
-                $this->lastHeaders = $http_response_header;
+                $this->lastHeaders = $http_response_header ?? [];
 
                 if (false === $resource) {
                     throw new ConnectException(sprintf('Connection refused for URI %s', $uri), $request, null, $context);
@@ -346,6 +363,7 @@ class StreamHandler
                 if (false === $records || !isset($records[0]['ip'])) {
                     throw new ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
                 }
+
                 return $uri->withHost($records[0]['ip']);
             }
             if ('v6' === $options['force_ip_resolve']) {
@@ -353,7 +371,8 @@ class StreamHandler
                 if (false === $records || !isset($records[0]['ipv6'])) {
                     throw new ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
                 }
-                return $uri->withHost('[' . $records[0]['ipv6'] . ']');
+
+                return $uri->withHost('['.$records[0]['ipv6'].']');
             }
         }
 
@@ -371,17 +390,20 @@ class StreamHandler
 
         $context = [
             'http' => [
-                'method'           => $request->getMethod(),
-                'header'           => $headers,
+                'method' => $request->getMethod(),
+                'header' => $headers,
                 'protocol_version' => $request->getProtocolVersion(),
-                'ignore_errors'    => true,
-                'follow_location'  => 0,
+                'ignore_errors' => true,
+                'follow_location' => 0,
+            ],
+            'ssl' => [
+                'peer_name' => $request->getUri()->getHost(),
             ],
         ];
 
         $body = (string) $request->getBody();
 
-        if (!empty($body)) {
+        if ('' !== $body) {
             $context['http']['content'] = $body;
             // Prevent the HTTP handler from adding a Content-Type header.
             if (!$request->hasHeader('Content-Type')) {
@@ -465,6 +487,25 @@ class StreamHandler
         }
     }
 
+    /**
+     * @param mixed $value as passed via Request transfer options.
+     */
+    private function add_crypto_method(RequestInterface $request, array &$options, $value, array &$params): void
+    {
+        if (
+            $value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT
+            || $value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT
+            || $value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+            || (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT)
+        ) {
+            $options['http']['crypto_method'] = $value;
+
+            return;
+        }
+
+        throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
+    }
+
     /**
      * @param mixed $value as passed via Request transfer options.
      */
@@ -535,27 +576,27 @@ class StreamHandler
         }
 
         static $map = [
-            \STREAM_NOTIFY_CONNECT       => 'CONNECT',
+            \STREAM_NOTIFY_CONNECT => 'CONNECT',
             \STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
-            \STREAM_NOTIFY_AUTH_RESULT   => 'AUTH_RESULT',
-            \STREAM_NOTIFY_MIME_TYPE_IS  => 'MIME_TYPE_IS',
-            \STREAM_NOTIFY_FILE_SIZE_IS  => 'FILE_SIZE_IS',
-            \STREAM_NOTIFY_REDIRECTED    => 'REDIRECTED',
-            \STREAM_NOTIFY_PROGRESS      => 'PROGRESS',
-            \STREAM_NOTIFY_FAILURE       => 'FAILURE',
-            \STREAM_NOTIFY_COMPLETED     => 'COMPLETED',
-            \STREAM_NOTIFY_RESOLVE       => 'RESOLVE',
+            \STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
+            \STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
+            \STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
+            \STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
+            \STREAM_NOTIFY_PROGRESS => 'PROGRESS',
+            \STREAM_NOTIFY_FAILURE => 'FAILURE',
+            \STREAM_NOTIFY_COMPLETED => 'COMPLETED',
+            \STREAM_NOTIFY_RESOLVE => 'RESOLVE',
         ];
         static $args = ['severity', 'message', 'message_code', 'bytes_transferred', 'bytes_max'];
 
         $value = Utils::debugResource($value);
-        $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
+        $ident = $request->getMethod().' '.$request->getUri()->withFragment('');
         self::addNotification(
             $params,
             static function (int $code, ...$passed) use ($ident, $value, $map, $args): void {
                 \fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
                 foreach (\array_filter($passed) as $i => $v) {
-                    \fwrite($value, $args[$i] . ': "' . $v . '" ');
+                    \fwrite($value, $args[$i].': "'.$v.'" ');
                 }
                 \fwrite($value, "\n");
             }
@@ -570,7 +611,7 @@ class StreamHandler
         } else {
             $params['notification'] = self::callArray([
                 $params['notification'],
-                $notify
+                $notify,
             ]);
         }
     }

+ 7 - 7
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/HandlerStack.php

@@ -58,7 +58,7 @@ class HandlerStack
     /**
      * @param (callable(RequestInterface, array): PromiseInterface)|null $handler Underlying HTTP handler.
      */
-    public function __construct(callable $handler = null)
+    public function __construct(?callable $handler = null)
     {
         $this->handler = $handler;
     }
@@ -86,14 +86,14 @@ class HandlerStack
         $stack = [];
 
         if ($this->handler !== null) {
-            $stack[] = "0) Handler: " . $this->debugCallable($this->handler);
+            $stack[] = '0) Handler: '.$this->debugCallable($this->handler);
         }
 
         $result = '';
         foreach (\array_reverse($this->stack) as $tuple) {
-            $depth++;
+            ++$depth;
             $str = "{$depth}) Name: '{$tuple[1]}', ";
-            $str .= "Function: " . $this->debugCallable($tuple[0]);
+            $str .= 'Function: '.$this->debugCallable($tuple[0]);
             $result = "> {$str}\n{$result}";
             $stack[] = $str;
         }
@@ -122,7 +122,7 @@ class HandlerStack
      */
     public function hasHandler(): bool
     {
-        return $this->handler !== null ;
+        return $this->handler !== null;
     }
 
     /**
@@ -266,10 +266,10 @@ class HandlerStack
         if (\is_array($fn)) {
             return \is_string($fn[0])
                 ? "callable({$fn[0]}::{$fn[1]})"
-                : "callable(['" . \get_class($fn[0]) . "', '{$fn[1]}'])";
+                : "callable(['".\get_class($fn[0])."', '{$fn[1]}'])";
         }
 
         /** @var object $fn */
-        return 'callable(' . \spl_object_hash($fn) . ')';
+        return 'callable('.\spl_object_hash($fn).')';
     }
 }

+ 9 - 8
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/MessageFormatter.php

@@ -40,11 +40,11 @@ class MessageFormatter implements MessageFormatterInterface
     /**
      * Apache Common Log Format.
      *
-     * @link https://httpd.apache.org/docs/2.4/logs.html#common
+     * @see https://httpd.apache.org/docs/2.4/logs.html#common
      *
      * @var string
      */
-    public const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
+    public const CLF = '{hostname} {req_header_User-Agent} - [{date_common_log}] "{method} {target} HTTP/{version}" {code} {res_header_Content-Length}';
     public const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
     public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
 
@@ -90,9 +90,9 @@ class MessageFormatter implements MessageFormatterInterface
                         break;
                     case 'req_headers':
                         $result = \trim($request->getMethod()
-                                . ' ' . $request->getRequestTarget())
-                            . ' HTTP/' . $request->getProtocolVersion() . "\r\n"
-                            . $this->headers($request);
+                                .' '.$request->getRequestTarget())
+                            .' HTTP/'.$request->getProtocolVersion()."\r\n"
+                            .$this->headers($request);
                         break;
                     case 'res_headers':
                         $result = $response ?
@@ -101,7 +101,7 @@ class MessageFormatter implements MessageFormatterInterface
                                 $response->getProtocolVersion(),
                                 $response->getStatusCode(),
                                 $response->getReasonPhrase()
-                            ) . "\r\n" . $this->headers($response)
+                            )."\r\n".$this->headers($response)
                             : 'NULL';
                         break;
                     case 'req_body':
@@ -137,7 +137,7 @@ class MessageFormatter implements MessageFormatterInterface
                         break;
                     case 'uri':
                     case 'url':
-                        $result = $request->getUri();
+                        $result = $request->getUri()->__toString();
                         break;
                     case 'target':
                         $result = $request->getRequestTarget();
@@ -177,6 +177,7 @@ class MessageFormatter implements MessageFormatterInterface
                 }
 
                 $cache[$matches[1]] = $result;
+
                 return $result;
             },
             $this->template
@@ -190,7 +191,7 @@ class MessageFormatter implements MessageFormatterInterface
     {
         $result = '';
         foreach ($message->getHeaders() as $name => $values) {
-            $result .= $name . ': ' . \implode(', ', $values) . "\r\n";
+            $result .= $name.': '.\implode(', ', $values)."\r\n";
         }
 
         return \trim($result);

+ 17 - 9
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Middleware.php

@@ -34,10 +34,12 @@ final class Middleware
                 }
                 $cookieJar = $options['cookies'];
                 $request = $cookieJar->withCookieHeader($request);
+
                 return $handler($request, $options)
                     ->then(
                         static function (ResponseInterface $response) use ($cookieJar, $request): ResponseInterface {
                             $cookieJar->extractCookies($request, $response);
+
                             return $response;
                         }
                     );
@@ -53,13 +55,14 @@ final class Middleware
      *
      * @return callable(callable): callable Returns a function that accepts the next handler.
      */
-    public static function httpErrors(BodySummarizerInterface $bodySummarizer = null): callable
+    public static function httpErrors(?BodySummarizerInterface $bodySummarizer = null): callable
     {
         return static function (callable $handler) use ($bodySummarizer): callable {
             return static function ($request, array $options) use ($handler, $bodySummarizer) {
                 if (empty($options['http_errors'])) {
                     return $handler($request, $options);
                 }
+
                 return $handler($request, $options)->then(
                     static function (ResponseInterface $response) use ($request, $bodySummarizer) {
                         $code = $response->getStatusCode();
@@ -93,20 +96,22 @@ final class Middleware
                 return $handler($request, $options)->then(
                     static function ($value) use ($request, &$container, $options) {
                         $container[] = [
-                            'request'  => $request,
+                            'request' => $request,
                             'response' => $value,
-                            'error'    => null,
-                            'options'  => $options
+                            'error' => null,
+                            'options' => $options,
                         ];
+
                         return $value;
                     },
                     static function ($reason) use ($request, &$container, $options) {
                         $container[] = [
-                            'request'  => $request,
+                            'request' => $request,
                             'response' => null,
-                            'error'    => $reason,
-                            'options'  => $options
+                            'error' => $reason,
+                            'options' => $options,
                         ];
+
                         return P\Create::rejectionFor($reason);
                     }
                 );
@@ -127,7 +132,7 @@ final class Middleware
      *
      * @return callable Returns a function that accepts the next handler.
      */
-    public static function tap(callable $before = null, callable $after = null): callable
+    public static function tap(?callable $before = null, ?callable $after = null): callable
     {
         return static function (callable $handler) use ($before, $after): callable {
             return static function (RequestInterface $request, array $options) use ($handler, $before, $after) {
@@ -138,6 +143,7 @@ final class Middleware
                 if ($after) {
                     $after($request, $options, $response);
                 }
+
                 return $response;
             };
         };
@@ -170,7 +176,7 @@ final class Middleware
      *
      * @return callable Returns a function that accepts the next handler.
      */
-    public static function retry(callable $decider, callable $delay = null): callable
+    public static function retry(callable $decider, ?callable $delay = null): callable
     {
         return static function (callable $handler) use ($decider, $delay): RetryMiddleware {
             return new RetryMiddleware($decider, $handler, $delay);
@@ -202,12 +208,14 @@ final class Middleware
                     static function ($response) use ($logger, $request, $formatter, $logLevel): ResponseInterface {
                         $message = $formatter->format($request, $response);
                         $logger->log($logLevel, $message);
+
                         return $response;
                     },
                     static function ($reason) use ($logger, $request, $formatter): PromiseInterface {
                         $response = $reason instanceof RequestException ? $reason->getResponse() : null;
                         $message = $formatter->format($request, $response, P\Create::exceptionFor($reason));
                         $logger->error($message);
+
                         return P\Create::rejectionFor($reason);
                     }
                 );

+ 1 - 1
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Pool.php

@@ -86,7 +86,7 @@ class Pool implements PromisorInterface
      * @param ClientInterface $client   Client used to send the requests
      * @param array|\Iterator $requests Requests to send concurrently.
      * @param array           $options  Passes through the options available in
-     *                                  {@see \GuzzleHttp\Pool::__construct}
+     *                                  {@see Pool::__construct}
      *
      * @return array Returns an array containing the response or an exception
      *               in the same order that the requests were sent.

+ 3 - 2
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php

@@ -76,14 +76,15 @@ class PrepareBodyMiddleware
 
         $expect = $options['expect'] ?? null;
 
-        // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
-        if ($expect === false || $request->getProtocolVersion() < 1.1) {
+        // Return if disabled or using HTTP/1.0
+        if ($expect === false || $request->getProtocolVersion() === '1.0') {
             return;
         }
 
         // The expect header is unconditionally enabled
         if ($expect === true) {
             $modify['set_headers']['Expect'] = '100-Continue';
+
             return;
         }
 

+ 25 - 13
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/RedirectMiddleware.php

@@ -27,10 +27,10 @@ class RedirectMiddleware
      * @var array
      */
     public static $defaultSettings = [
-        'max'             => 5,
-        'protocols'       => ['http', 'https'],
-        'strict'          => false,
-        'referer'         => false,
+        'max' => 5,
+        'protocols' => ['http', 'https'],
+        'strict' => false,
+        'referer' => false,
         'track_redirects' => false,
     ];
 
@@ -88,6 +88,14 @@ class RedirectMiddleware
         $this->guardMax($request, $response, $options);
         $nextRequest = $this->modifyRequest($request, $options, $response);
 
+        // If authorization is handled by curl, unset it if URI is cross-origin.
+        if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $nextRequest->getUri()) && defined('\CURLOPT_HTTPAUTH')) {
+            unset(
+                $options['curl'][\CURLOPT_HTTPAUTH],
+                $options['curl'][\CURLOPT_USERPWD]
+            );
+        }
+
         if (isset($options['allow_redirects']['on_redirect'])) {
             ($options['allow_redirects']['on_redirect'])(
                 $request,
@@ -132,7 +140,7 @@ class RedirectMiddleware
     }
 
     /**
-     * Check for too many redirects
+     * Check for too many redirects.
      *
      * @throws TooManyRedirectsException Too many redirects.
      */
@@ -158,8 +166,8 @@ class RedirectMiddleware
         // not forcing RFC compliance, but rather emulating what all browsers
         // would do.
         $statusCode = $response->getStatusCode();
-        if ($statusCode == 303 ||
-            ($statusCode <= 302 && !$options['allow_redirects']['strict'])
+        if ($statusCode == 303
+            || ($statusCode <= 302 && !$options['allow_redirects']['strict'])
         ) {
             $safeMethods = ['GET', 'HEAD', 'OPTIONS'];
             $requestMethod = $request->getMethod();
@@ -168,7 +176,7 @@ class RedirectMiddleware
             $modify['body'] = '';
         }
 
-        $uri = $this->redirectUri($request, $response, $protocols);
+        $uri = self::redirectUri($request, $response, $protocols);
         if (isset($options['idn_conversion']) && ($options['idn_conversion'] !== false)) {
             $idnOptions = ($options['idn_conversion'] === true) ? \IDNA_DEFAULT : $options['idn_conversion'];
             $uri = Utils::idnUriConvert($uri, $idnOptions);
@@ -188,19 +196,23 @@ class RedirectMiddleware
             $modify['remove_headers'][] = 'Referer';
         }
 
-        // Remove Authorization header if host is different.
-        if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
+        // Remove Authorization and Cookie headers if URI is cross-origin.
+        if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $modify['uri'])) {
             $modify['remove_headers'][] = 'Authorization';
+            $modify['remove_headers'][] = 'Cookie';
         }
 
         return Psr7\Utils::modifyRequest($request, $modify);
     }
 
     /**
-     * Set the appropriate URL on the request based on the location header
+     * Set the appropriate URL on the request based on the location header.
      */
-    private function redirectUri(RequestInterface $request, ResponseInterface $response, array $protocols): UriInterface
-    {
+    private static function redirectUri(
+        RequestInterface $request,
+        ResponseInterface $response,
+        array $protocols
+    ): UriInterface {
         $location = Psr7\UriResolver::resolve(
             $request->getUri(),
             new Psr7\Uri($response->getHeaderLine('Location'))

+ 15 - 5
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/RequestOptions.php

@@ -5,9 +5,7 @@ namespace GuzzleHttp;
 /**
  * This class contains a list of built-in Guzzle request options.
  *
- * More documentation for each option can be found at http://guzzlephp.org/.
- *
- * @link http://docs.guzzlephp.org/en/v6/request-options.html
+ * @see https://docs.guzzlephp.org/en/latest/request-options.html
  */
 final class RequestOptions
 {
@@ -63,17 +61,29 @@ final class RequestOptions
      * Specifies whether or not cookies are used in a request or what cookie
      * jar to use or what cookies to send. This option only works if your
      * handler has the `cookie` middleware. Valid values are `false` and
-     * an instance of {@see \GuzzleHttp\Cookie\CookieJarInterface}.
+     * an instance of {@see Cookie\CookieJarInterface}.
      */
     public const COOKIES = 'cookies';
 
     /**
      * connect_timeout: (float, default=0) Float describing the number of
      * seconds to wait while trying to connect to a server. Use 0 to wait
-     * indefinitely (the default behavior).
+     * 300 seconds (the default behavior).
      */
     public const CONNECT_TIMEOUT = 'connect_timeout';
 
+    /**
+     * crypto_method: (int) A value describing the minimum TLS protocol
+     * version to use.
+     *
+     * This setting must be set to one of the
+     * ``STREAM_CRYPTO_METHOD_TLS*_CLIENT`` constants. PHP 7.4 or higher is
+     * required in order to use TLS 1.3, and cURL 7.34.0 or higher is required
+     * in order to specify a crypto method, with cURL 7.52.0 or higher being
+     * required to use TLS 1.3.
+     */
+    public const CRYPTO_METHOD = 'crypto_method';
+
     /**
      * 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

+ 8 - 5
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/RetryMiddleware.php

@@ -40,11 +40,11 @@ class RetryMiddleware
      *                                                                         and returns the number of
      *                                                                         milliseconds to delay.
      */
-    public function __construct(callable $decider, callable $nextHandler, callable $delay = null)
+    public function __construct(callable $decider, callable $nextHandler, ?callable $delay = null)
     {
         $this->decider = $decider;
         $this->nextHandler = $nextHandler;
-        $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
+        $this->delay = $delay ?: __CLASS__.'::exponentialDelay';
     }
 
     /**
@@ -54,7 +54,7 @@ class RetryMiddleware
      */
     public static function exponentialDelay(int $retries): int
     {
-        return (int) \pow(2, $retries - 1) * 1000;
+        return (int) 2 ** ($retries - 1) * 1000;
     }
 
     public function __invoke(RequestInterface $request, array $options): PromiseInterface
@@ -64,6 +64,7 @@ class RetryMiddleware
         }
 
         $fn = $this->nextHandler;
+
         return $fn($request, $options)
             ->then(
                 $this->onFulfilled($request, $options),
@@ -85,6 +86,7 @@ class RetryMiddleware
             )) {
                 return $value;
             }
+
             return $this->doRetry($request, $options, $value);
         };
     }
@@ -103,13 +105,14 @@ class RetryMiddleware
             )) {
                 return P\Create::rejectionFor($reason);
             }
+
             return $this->doRetry($req, $options);
         };
     }
 
-    private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null): PromiseInterface
+    private function doRetry(RequestInterface $request, array $options, ?ResponseInterface $response = null): PromiseInterface
     {
-        $options['delay'] = ($this->delay)(++$options['retries'], $response);
+        $options['delay'] = ($this->delay)(++$options['retries'], $response, $request);
 
         return $this($request, $options);
     }

+ 28 - 26
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/Utils.php

@@ -23,9 +23,9 @@ final class Utils
     {
         switch (\gettype($input)) {
             case 'object':
-                return 'object(' . \get_class($input) . ')';
+                return 'object('.\get_class($input).')';
             case 'array':
-                return 'array(' . \count($input) . ')';
+                return 'array('.\count($input).')';
             default:
                 \ob_start();
                 \var_dump($input);
@@ -71,7 +71,7 @@ final class Utils
             return \STDOUT;
         }
 
-        return \GuzzleHttp\Psr7\Utils::tryFopen('php://output', 'w');
+        return Psr7\Utils::tryFopen('php://output', 'w');
     }
 
     /**
@@ -79,19 +79,22 @@ final class Utils
      *
      * The returned handler is not wrapped by any default middlewares.
      *
-     * @throws \RuntimeException if no viable Handler is available.
+     * @return callable(\Psr\Http\Message\RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system.
      *
-     * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system.
+     * @throws \RuntimeException if no viable Handler is available.
      */
     public static function chooseHandler(): callable
     {
         $handler = null;
-        if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) {
-            $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
-        } elseif (\function_exists('curl_exec')) {
-            $handler = new CurlHandler();
-        } elseif (\function_exists('curl_multi_exec')) {
-            $handler = new CurlMultiHandler();
+
+        if (\defined('CURLOPT_CUSTOMREQUEST') && \function_exists('curl_version') && version_compare(curl_version()['version'], '7.21.2') >= 0) {
+            if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) {
+                $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
+            } elseif (\function_exists('curl_exec')) {
+                $handler = new CurlHandler();
+            } elseif (\function_exists('curl_multi_exec')) {
+                $handler = new CurlMultiHandler();
+            }
         }
 
         if (\ini_get('allow_url_fopen')) {
@@ -173,14 +176,13 @@ 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: http://docs.guzzlephp.org/en/latest/clients.html#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.haxx.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.haxx.se/docs/sslcerts.html for more
-information.
+option: https://docs.guzzlephp.org/en/latest/request-options.html#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.haxx.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.haxx.se/docs/sslcerts.html for more information.
 EOT
         );
     }
@@ -244,8 +246,8 @@ EOT
             }
             // Special match if the area when prefixed with ".". Remove any
             // existing leading "." and add a new leading ".".
-            $area = '.' . \ltrim($area, '.');
-            if (\substr($host, -(\strlen($area))) === $area) {
+            $area = '.'.\ltrim($area, '.');
+            if (\substr($host, -\strlen($area)) === $area) {
                 return true;
             }
         }
@@ -266,13 +268,13 @@ EOT
      *
      * @throws InvalidArgumentException if the JSON cannot be decoded.
      *
-     * @link https://www.php.net/manual/en/function.json-decode.php
+     * @see https://www.php.net/manual/en/function.json-decode.php
      */
     public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
     {
         $data = \json_decode($json, $assoc, $depth, $options);
         if (\JSON_ERROR_NONE !== \json_last_error()) {
-            throw new InvalidArgumentException('json_decode error: ' . \json_last_error_msg());
+            throw new InvalidArgumentException('json_decode error: '.\json_last_error_msg());
         }
 
         return $data;
@@ -287,13 +289,13 @@ EOT
      *
      * @throws InvalidArgumentException if the JSON cannot be encoded.
      *
-     * @link https://www.php.net/manual/en/function.json-encode.php
+     * @see https://www.php.net/manual/en/function.json-encode.php
      */
     public static function jsonEncode($value, int $options = 0, int $depth = 512): string
     {
         $json = \json_encode($value, $options, $depth);
         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());
         }
 
         /** @var string */
@@ -338,7 +340,7 @@ EOT
 
                 $errorMessage = 'IDN conversion failed';
                 if ($errors) {
-                    $errorMessage .= ' (errors: ' . implode(', ', $errors) . ')';
+                    $errorMessage .= ' (errors: '.implode(', ', $errors).')';
                 }
 
                 throw new InvalidArgumentException($errorMessage);

+ 4 - 4
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/functions.php

@@ -50,9 +50,9 @@ function debug_resource($value = null)
  *
  * The returned handler is not wrapped by any default middlewares.
  *
- * @throws \RuntimeException if no viable Handler is available.
+ * @return callable(\Psr\Http\Message\RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system.
  *
- * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system.
+ * @throws \RuntimeException if no viable Handler is available.
  *
  * @deprecated choose_handler will be removed in guzzlehttp/guzzle:8.0. Use Utils::chooseHandler instead.
  */
@@ -141,7 +141,7 @@ function is_host_in_noproxy(string $host, array $noProxyArray): bool
  *
  * @throws Exception\InvalidArgumentException if the JSON cannot be decoded.
  *
- * @link 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.
  */
 function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
@@ -158,7 +158,7 @@ function json_decode(string $json, bool $assoc = false, int $depth = 512, int $o
  *
  * @throws Exception\InvalidArgumentException if the JSON cannot be encoded.
  *
- * @link 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.
  */
 function json_encode($value, int $options = 0, int $depth = 512): string

+ 1 - 1
modules/boonex/xero/plugins/guzzlehttp/guzzle/src/functions_include.php

@@ -2,5 +2,5 @@
 
 // Don't redefine the functions if included multiple times.
 if (!\function_exists('GuzzleHttp\describe_type')) {
-    require __DIR__ . '/functions.php';
+    require __DIR__.'/functions.php';
 }

+ 0 - 9
modules/boonex/xero/plugins/guzzlehttp/guzzle/vendor-bin/php-cs-fixer/composer.json

@@ -1,9 +0,0 @@
-{
-    "require": {
-        "php": "^7.2.5 || ^8.0",
-        "friendsofphp/php-cs-fixer": "3.2.1"
-    },
-    "config": {
-        "preferred-install": "dist"
-    }
-}

+ 0 - 10
modules/boonex/xero/plugins/guzzlehttp/guzzle/vendor-bin/phpstan/composer.json

@@ -1,10 +0,0 @@
-{
-    "require": {
-        "php": "^7.2.5 || ^8.0",
-        "phpstan/phpstan": "0.12.99",
-        "phpstan/phpstan-deprecation-rules": "0.12.6"
-    },
-    "config": {
-        "preferred-install": "dist"
-    }
-}

+ 0 - 9
modules/boonex/xero/plugins/guzzlehttp/guzzle/vendor-bin/psalm/composer.json

@@ -1,9 +0,0 @@
-{
-    "require": {
-        "php": "^7.2.5 || ^8.0",
-        "psalm/phar": "4.10.0"
-    },
-    "config": {
-        "preferred-install": "dist"
-    }
-}

+ 78 - 1
modules/boonex/xero/plugins/guzzlehttp/promises/CHANGELOG.md

@@ -1,5 +1,78 @@
 # CHANGELOG
 
+
+## 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
@@ -7,15 +80,18 @@
 - 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
+- Fix manually settle promises generated with `Utils::task`
+
 
 ## 1.4.1 - 2021-02-18
 
@@ -23,6 +99,7 @@
 
 - Fixed `each_limit` skipping promises and failing
 
+
 ## 1.4.0 - 2020-09-30
 
 ### Added

+ 0 - 13
modules/boonex/xero/plugins/guzzlehttp/promises/Makefile

@@ -1,13 +0,0 @@
-all: clean test
-
-test:
-	vendor/bin/phpunit
-
-coverage:
-	vendor/bin/phpunit --coverage-html=artifacts/coverage
-
-view-coverage:
-	open artifacts/coverage/index.html
-
-clean:
-	rm -rf artifacts/*

+ 43 - 31
modules/boonex/xero/plugins/guzzlehttp/promises/README.md

@@ -17,7 +17,7 @@ for a general introduction to promises.
 - [Implementation notes](#implementation-notes)
 
 
-# Features
+## Features
 
 - [Promises/A+](https://promisesaplus.com/) implementation.
 - Promise resolution and chaining is handled iteratively, allowing for
@@ -29,15 +29,29 @@ for a general introduction to promises.
   `GuzzleHttp\Promise\Coroutine::of()`.
 
 
-# Quick start
+## 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.5 |
+
+
+## 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
 
 Callbacks are registered with the `then` method by providing an optional 
 `$onFulfilled` followed by an optional `$onRejected` function.
@@ -60,12 +74,11 @@ $promise->then(
 ```
 
 *Resolving* a promise means that you either fulfill a promise with a *value* or
-reject a promise with a *reason*. Resolving a promises triggers callbacks
-registered with the promises's `then` method. These callbacks are triggered
+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
+### 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
@@ -92,8 +105,7 @@ $promise
 $promise->resolve('reader.');
 ```
 
-
-## Promise forwarding
+### 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
@@ -123,7 +135,7 @@ $promise->resolve('A');
 $nextPromise->resolve('B');
 ```
 
-## Promise rejection
+### Promise Rejection
 
 When a promise is rejected, the `$onRejected` callbacks are invoked with the
 rejection reason.
@@ -140,7 +152,7 @@ $promise->reject('Error!');
 // Outputs "Error!"
 ```
 
-## Rejection forwarding
+### Rejection Forwarding
 
 If an exception is thrown in an `$onRejected` callback, subsequent
 `$onRejected` callbacks are invoked with the thrown exception as the reason.
@@ -195,7 +207,8 @@ $promise
 $promise->reject('Error!');
 ```
 
-# Synchronous wait
+
+## 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
@@ -247,8 +260,7 @@ $promise->wait();
 
 > PHP Fatal error:  Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
 
-
-## Unwrapping a promise
+### 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
@@ -275,7 +287,7 @@ wait function will be the value delivered to promise B.
 **Note**: when you do not unwrap the promise, no value is returned.
 
 
-# Cancellation
+## 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
@@ -283,10 +295,9 @@ cancel function that when invoked cancels the action of computing a resolution
 of the promise.
 
 
-# API
+## API
 
-
-## Promise
+### 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
@@ -349,7 +360,7 @@ A promise has the following methods:
   Rejects the promise with the given `$reason`.
 
 
-## FulfilledPromise
+### FulfilledPromise
 
 A fulfilled promise can be created to represent a promise that has been
 fulfilled.
@@ -366,7 +377,7 @@ $promise->then(function ($value) {
 ```
 
 
-## RejectedPromise
+### RejectedPromise
 
 A rejected promise can be created to represent a promise that has been
 rejected.
@@ -383,7 +394,7 @@ $promise->then(null, function ($reason) {
 ```
 
 
-# Promise interop
+## 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)
@@ -409,7 +420,7 @@ 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
+### 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
@@ -434,13 +445,10 @@ $loop = React\EventLoop\Factory::create();
 $loop->addPeriodicTimer(0, [$queue, 'run']);
 ```
 
-*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
-
 
-# Implementation notes
+## Implementation Notes
 
-
-## Promise resolution and chaining is handled iteratively
+### 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.
@@ -476,8 +484,7 @@ 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.
+### 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
@@ -505,7 +512,10 @@ $promise->resolve('foo');
 
 ## Upgrading from Function API
 
-A static API was first introduced in 1.4.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API will be removed in 2.0.0. A migration table has been provided here for your convenience:
+A static API was first introduced in 1.4.0, in order to mitigate problems with
+functions conflicting between global and local copies of the package. The
+function API was removed in 2.0.0. A migration table has been provided here for
+your convenience:
 
 | Original Function | Replacement Method |
 |----------------|----------------|
@@ -536,10 +546,12 @@ A static API was first introduced in 1.4.0, in order to mitigate problems with f
 
 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

+ 10 - 10
modules/boonex/xero/plugins/guzzlehttp/promises/composer.json

@@ -26,32 +26,32 @@
         }
     ],
     "require": {
-        "php": ">=5.5"
+        "php": "^7.2.5 || ^8.0"
     },
     "require-dev": {
-        "symfony/phpunit-bridge": "^4.4 || ^5.1"
+        "bamarni/composer-bin-plugin": "^1.8.2",
+        "phpunit/phpunit": "^8.5.39 || ^9.6.20"
     },
     "autoload": {
         "psr-4": {
             "GuzzleHttp\\Promise\\": "src/"
-        },
-        "files": ["src/functions_include.php"]
+        }
     },
     "autoload-dev": {
         "psr-4": {
             "GuzzleHttp\\Promise\\Tests\\": "tests/"
         }
     },
-    "scripts": {
-        "test": "vendor/bin/simple-phpunit",
-        "test-ci": "vendor/bin/simple-phpunit --coverage-text"
-    },
     "extra": {
-        "branch-alias": {
-            "dev-master": "1.5-dev"
+        "bamarni-bin": {
+            "bin-links": true,
+            "forward-command": false
         }
     },
     "config": {
+        "allow-plugins": {
+            "bamarni/composer-bin-plugin": true
+        },
         "preferred-install": "dist",
         "sort-packages": true
     }

+ 3 - 1
modules/boonex/xero/plugins/guzzlehttp/promises/src/AggregateException.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -7,7 +9,7 @@ namespace GuzzleHttp\Promise;
  */
 class AggregateException extends RejectionException
 {
-    public function __construct($msg, array $reasons)
+    public function __construct(string $msg, array $reasons)
     {
         parent::__construct(
             $reasons,

+ 2 - 0
modules/boonex/xero/plugins/guzzlehttp/promises/src/CancellationException.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**

+ 18 - 25
modules/boonex/xero/plugins/guzzlehttp/promises/src/Coroutine.php

@@ -1,8 +1,9 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
-use Exception;
 use Generator;
 use Throwable;
 
@@ -27,7 +28,7 @@ use Throwable;
  *         $value = (yield createPromise('a'));
  *         try {
  *             $value = (yield createPromise($value . 'b'));
- *         } catch (\Exception $e) {
+ *         } catch (\Throwable $e) {
  *             // The promise was rejected.
  *         }
  *         yield $value . 'c';
@@ -40,7 +41,7 @@ use Throwable;
  *
  * @return Promise
  *
- * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
+ * @see https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
  */
 final class Coroutine implements PromiseInterface
 {
@@ -62,15 +63,13 @@ final class Coroutine implements PromiseInterface
     public function __construct(callable $generatorFn)
     {
         $this->generator = $generatorFn();
-        $this->result = new Promise(function () {
+        $this->result = new Promise(function (): void {
             while (isset($this->currentPromise)) {
                 $this->currentPromise->wait();
             }
         });
         try {
             $this->nextCoroutine($this->generator->current());
-        } catch (\Exception $exception) {
-            $this->result->reject($exception);
         } catch (Throwable $throwable) {
             $this->result->reject($throwable);
         }
@@ -78,53 +77,51 @@ final class Coroutine implements PromiseInterface
 
     /**
      * Create a new coroutine.
-     *
-     * @return self
      */
-    public static function of(callable $generatorFn)
+    public static function of(callable $generatorFn): self
     {
         return new self($generatorFn);
     }
 
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         return $this->result->then($onFulfilled, $onRejected);
     }
 
-    public function otherwise(callable $onRejected)
+    public function otherwise(callable $onRejected): PromiseInterface
     {
         return $this->result->otherwise($onRejected);
     }
 
-    public function wait($unwrap = true)
+    public function wait(bool $unwrap = true)
     {
         return $this->result->wait($unwrap);
     }
 
-    public function getState()
+    public function getState(): string
     {
         return $this->result->getState();
     }
 
-    public function resolve($value)
+    public function resolve($value): void
     {
         $this->result->resolve($value);
     }
 
-    public function reject($reason)
+    public function reject($reason): void
     {
         $this->result->reject($reason);
     }
 
-    public function cancel()
+    public function cancel(): void
     {
         $this->currentPromise->cancel();
         $this->result->cancel();
     }
 
-    private function nextCoroutine($yielded)
+    private function nextCoroutine($yielded): void
     {
         $this->currentPromise = Create::promiseFor($yielded)
             ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
@@ -133,7 +130,7 @@ final class Coroutine implements PromiseInterface
     /**
      * @internal
      */
-    public function _handleSuccess($value)
+    public function _handleSuccess($value): void
     {
         unset($this->currentPromise);
         try {
@@ -143,8 +140,6 @@ final class Coroutine implements PromiseInterface
             } else {
                 $this->result->resolve($value);
             }
-        } catch (Exception $exception) {
-            $this->result->reject($exception);
         } catch (Throwable $throwable) {
             $this->result->reject($throwable);
         }
@@ -153,15 +148,13 @@ final class Coroutine implements PromiseInterface
     /**
      * @internal
      */
-    public function _handleFailure($reason)
+    public function _handleFailure($reason): void
     {
         unset($this->currentPromise);
         try {
             $nextYield = $this->generator->throw(Create::exceptionFor($reason));
             // The throw was caught, so keep iterating on the coroutine
             $this->nextCoroutine($nextYield);
-        } catch (Exception $exception) {
-            $this->result->reject($exception);
         } catch (Throwable $throwable) {
             $this->result->reject($throwable);
         }

+ 8 - 13
modules/boonex/xero/plugins/guzzlehttp/promises/src/Create.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 final class Create
@@ -8,10 +10,8 @@ final class Create
      * Creates a promise for a value if the value is not a promise.
      *
      * @param mixed $value Promise or value.
-     *
-     * @return PromiseInterface
      */
-    public static function promiseFor($value)
+    public static function promiseFor($value): PromiseInterface
     {
         if ($value instanceof PromiseInterface) {
             return $value;
@@ -23,6 +23,7 @@ final class Create
             $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
             $promise = new Promise($wfn, $cfn);
             $value->then([$promise, 'resolve'], [$promise, 'reject']);
+
             return $promise;
         }
 
@@ -34,10 +35,8 @@ final class Create
      * If the provided reason is a promise, then it is returned as-is.
      *
      * @param mixed $reason Promise or reason.
-     *
-     * @return PromiseInterface
      */
-    public static function rejectionFor($reason)
+    public static function rejectionFor($reason): PromiseInterface
     {
         if ($reason instanceof PromiseInterface) {
             return $reason;
@@ -50,12 +49,10 @@ final class Create
      * Create an exception for a rejected promise value.
      *
      * @param mixed $reason
-     *
-     * @return \Exception|\Throwable
      */
-    public static function exceptionFor($reason)
+    public static function exceptionFor($reason): \Throwable
     {
-        if ($reason instanceof \Exception || $reason instanceof \Throwable) {
+        if ($reason instanceof \Throwable) {
             return $reason;
         }
 
@@ -66,10 +63,8 @@ final class Create
      * Returns an iterator for the given value.
      *
      * @param mixed $value
-     *
-     * @return \Iterator
      */
-    public static function iterFor($value)
+    public static function iterFor($value): \Iterator
     {
         if ($value instanceof \Iterator) {
             return $value;

+ 17 - 26
modules/boonex/xero/plugins/guzzlehttp/promises/src/Each.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 final class Each
@@ -17,20 +19,16 @@ final class Each
      * index, and the aggregate promise. The callback can invoke any necessary
      * side effects and choose to resolve or reject the aggregate if needed.
      *
-     * @param mixed    $iterable    Iterator or array to iterate over.
-     * @param callable $onFulfilled
-     * @param callable $onRejected
-     *
-     * @return PromiseInterface
+     * @param mixed $iterable Iterator or array to iterate over.
      */
     public static function of(
         $iterable,
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         return (new EachPromise($iterable, [
             'fulfilled' => $onFulfilled,
-            'rejected'  => $onRejected
+            'rejected' => $onRejected,
         ]))->promise();
     }
 
@@ -44,21 +42,17 @@ final class Each
      *
      * @param mixed        $iterable
      * @param int|callable $concurrency
-     * @param callable     $onFulfilled
-     * @param callable     $onRejected
-     *
-     * @return PromiseInterface
      */
     public static function ofLimit(
         $iterable,
         $concurrency,
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         return (new EachPromise($iterable, [
-            'fulfilled'   => $onFulfilled,
-            'rejected'    => $onRejected,
-            'concurrency' => $concurrency
+            'fulfilled' => $onFulfilled,
+            'rejected' => $onRejected,
+            'concurrency' => $concurrency,
         ]))->promise();
     }
 
@@ -69,20 +63,17 @@ final class Each
      *
      * @param mixed        $iterable
      * @param int|callable $concurrency
-     * @param callable     $onFulfilled
-     *
-     * @return PromiseInterface
      */
     public static function ofLimitAll(
         $iterable,
         $concurrency,
-        callable $onFulfilled = null
-    ) {
-        return each_limit(
+        ?callable $onFulfilled = null
+    ): PromiseInterface {
+        return self::ofLimit(
             $iterable,
             $concurrency,
             $onFulfilled,
-            function ($reason, $idx, PromiseInterface $aggregate) {
+            function ($reason, $idx, PromiseInterface $aggregate): void {
                 $aggregate->reject($reason);
             }
         );

+ 26 - 33
modules/boonex/xero/plugins/guzzlehttp/promises/src/EachPromise.php

@@ -1,10 +1,14 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
  * Represents a promise that iterates over many promises and invokes
  * side-effect functions in the process.
+ *
+ * @final
  */
 class EachPromise implements PromisorInterface
 {
@@ -69,7 +73,7 @@ class EachPromise implements PromisorInterface
     }
 
     /** @psalm-suppress InvalidNullableReturnType */
-    public function promise()
+    public function promise(): PromiseInterface
     {
         if ($this->aggregate) {
             return $this->aggregate;
@@ -81,30 +85,19 @@ class EachPromise implements PromisorInterface
             $this->iterable->rewind();
             $this->refillPending();
         } catch (\Throwable $e) {
-            /**
-             * @psalm-suppress NullReference
-             * @phpstan-ignore-next-line
-             */
-            $this->aggregate->reject($e);
-        } catch (\Exception $e) {
-            /**
-             * @psalm-suppress NullReference
-             * @phpstan-ignore-next-line
-             */
             $this->aggregate->reject($e);
         }
 
         /**
          * @psalm-suppress NullableReturnStatement
-         * @phpstan-ignore-next-line
          */
         return $this->aggregate;
     }
 
-    private function createPromise()
+    private function createPromise(): void
     {
         $this->mutex = false;
-        $this->aggregate = new Promise(function () {
+        $this->aggregate = new Promise(function (): void {
             if ($this->checkIfFinished()) {
                 return;
             }
@@ -121,7 +114,7 @@ class EachPromise implements PromisorInterface
         });
 
         // Clear the references when the promise is resolved.
-        $clearFn = function () {
+        $clearFn = function (): void {
             $this->iterable = $this->concurrency = $this->pending = null;
             $this->onFulfilled = $this->onRejected = null;
             $this->nextPendingIndex = 0;
@@ -130,17 +123,19 @@ class EachPromise implements PromisorInterface
         $this->aggregate->then($clearFn, $clearFn);
     }
 
-    private function refillPending()
+    private function refillPending(): void
     {
         if (!$this->concurrency) {
             // Add all pending promises.
-            while ($this->addPending() && $this->advanceIterator());
+            while ($this->addPending() && $this->advanceIterator()) {
+            }
+
             return;
         }
 
         // Add only up to N pending promises.
         $concurrency = is_callable($this->concurrency)
-            ? call_user_func($this->concurrency, count($this->pending))
+            ? ($this->concurrency)(count($this->pending))
             : $this->concurrency;
         $concurrency = max($concurrency - count($this->pending), 0);
         // Concurrency may be set to 0 to disallow new promises.
@@ -155,10 +150,11 @@ class EachPromise implements PromisorInterface
         // next value to yield until promise callbacks are called.
         while (--$concurrency
             && $this->advanceIterator()
-            && $this->addPending());
+            && $this->addPending()) {
+        }
     }
 
-    private function addPending()
+    private function addPending(): bool
     {
         if (!$this->iterable || !$this->iterable->valid()) {
             return false;
@@ -172,10 +168,9 @@ class EachPromise implements PromisorInterface
         $idx = $this->nextPendingIndex++;
 
         $this->pending[$idx] = $promise->then(
-            function ($value) use ($idx, $key) {
+            function ($value) use ($idx, $key): void {
                 if ($this->onFulfilled) {
-                    call_user_func(
-                        $this->onFulfilled,
+                    ($this->onFulfilled)(
                         $value,
                         $key,
                         $this->aggregate
@@ -183,10 +178,9 @@ class EachPromise implements PromisorInterface
                 }
                 $this->step($idx);
             },
-            function ($reason) use ($idx, $key) {
+            function ($reason) use ($idx, $key): void {
                 if ($this->onRejected) {
-                    call_user_func(
-                        $this->onRejected,
+                    ($this->onRejected)(
                         $reason,
                         $key,
                         $this->aggregate
@@ -199,7 +193,7 @@ class EachPromise implements PromisorInterface
         return true;
     }
 
-    private function advanceIterator()
+    private function advanceIterator(): bool
     {
         // Place a lock on the iterator so that we ensure to not recurse,
         // preventing fatal generator errors.
@@ -212,19 +206,17 @@ class EachPromise implements PromisorInterface
         try {
             $this->iterable->next();
             $this->mutex = false;
+
             return true;
         } catch (\Throwable $e) {
             $this->aggregate->reject($e);
             $this->mutex = false;
-            return false;
-        } catch (\Exception $e) {
-            $this->aggregate->reject($e);
-            $this->mutex = false;
+
             return false;
         }
     }
 
-    private function step($idx)
+    private function step(int $idx): void
     {
         // If the promise was already resolved, then ignore this step.
         if (Is::settled($this->aggregate)) {
@@ -242,11 +234,12 @@ class EachPromise implements PromisorInterface
         }
     }
 
-    private function checkIfFinished()
+    private function checkIfFinished(): bool
     {
         if (!$this->pending && !$this->iterable->valid()) {
             // Resolve the promise if there's nothing left to do.
             $this->aggregate->resolve(null);
+
             return true;
         }
 

+ 19 - 14
modules/boonex/xero/plugins/guzzlehttp/promises/src/FulfilledPromise.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -7,11 +9,16 @@ namespace GuzzleHttp\Promise;
  *
  * Thenning off of this promise will invoke the onFulfilled callback
  * immediately and ignore other callbacks.
+ *
+ * @final
  */
 class FulfilledPromise implements PromiseInterface
 {
     private $value;
 
+    /**
+     * @param mixed $value
+     */
     public function __construct($value)
     {
         if (is_object($value) && method_exists($value, 'then')) {
@@ -24,9 +31,9 @@ class FulfilledPromise implements PromiseInterface
     }
 
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         // Return itself if there is no onFulfilled function.
         if (!$onFulfilled) {
             return $this;
@@ -35,14 +42,12 @@ class FulfilledPromise implements PromiseInterface
         $queue = Utils::queue();
         $p = new Promise([$queue, 'run']);
         $value = $this->value;
-        $queue->add(static function () use ($p, $value, $onFulfilled) {
+        $queue->add(static function () use ($p, $value, $onFulfilled): void {
             if (Is::pending($p)) {
                 try {
                     $p->resolve($onFulfilled($value));
                 } catch (\Throwable $e) {
                     $p->reject($e);
-                } catch (\Exception $e) {
-                    $p->reject($e);
                 }
             }
         });
@@ -50,34 +55,34 @@ class FulfilledPromise implements PromiseInterface
         return $p;
     }
 
-    public function otherwise(callable $onRejected)
+    public function otherwise(callable $onRejected): PromiseInterface
     {
         return $this->then(null, $onRejected);
     }
 
-    public function wait($unwrap = true, $defaultDelivery = null)
+    public function wait(bool $unwrap = true)
     {
         return $unwrap ? $this->value : null;
     }
 
-    public function getState()
+    public function getState(): string
     {
         return self::FULFILLED;
     }
 
-    public function resolve($value)
+    public function resolve($value): void
     {
         if ($value !== $this->value) {
-            throw new \LogicException("Cannot resolve a fulfilled promise");
+            throw new \LogicException('Cannot resolve a fulfilled promise');
         }
     }
 
-    public function reject($reason)
+    public function reject($reason): void
     {
-        throw new \LogicException("Cannot reject a fulfilled promise");
+        throw new \LogicException('Cannot reject a fulfilled promise');
     }
 
-    public function cancel()
+    public function cancel(): void
     {
         // pass
     }

+ 6 - 12
modules/boonex/xero/plugins/guzzlehttp/promises/src/Is.php

@@ -1,45 +1,39 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 final class Is
 {
     /**
      * Returns true if a promise is pending.
-     *
-     * @return bool
      */
-    public static function pending(PromiseInterface $promise)
+    public static function pending(PromiseInterface $promise): bool
     {
         return $promise->getState() === PromiseInterface::PENDING;
     }
 
     /**
      * Returns true if a promise is fulfilled or rejected.
-     *
-     * @return bool
      */
-    public static function settled(PromiseInterface $promise)
+    public static function settled(PromiseInterface $promise): bool
     {
         return $promise->getState() !== PromiseInterface::PENDING;
     }
 
     /**
      * Returns true if a promise is fulfilled.
-     *
-     * @return bool
      */
-    public static function fulfilled(PromiseInterface $promise)
+    public static function fulfilled(PromiseInterface $promise): bool
     {
         return $promise->getState() === PromiseInterface::FULFILLED;
     }
 
     /**
      * Returns true if a promise is rejected.
-     *
-     * @return bool
      */
-    public static function rejected(PromiseInterface $promise)
+    public static function rejected(PromiseInterface $promise): bool
     {
         return $promise->getState() === PromiseInterface::REJECTED;
     }

+ 31 - 28
modules/boonex/xero/plugins/guzzlehttp/promises/src/Promise.php

@@ -1,11 +1,15 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
  * Promises/A+ implementation that avoids recursion when possible.
  *
- * @link https://promisesaplus.com/
+ * @see https://promisesaplus.com/
+ *
+ * @final
  */
 class Promise implements PromiseInterface
 {
@@ -21,43 +25,46 @@ class Promise implements PromiseInterface
      * @param callable $cancelFn Fn that when invoked cancels the promise.
      */
     public function __construct(
-        callable $waitFn = null,
-        callable $cancelFn = null
+        ?callable $waitFn = null,
+        ?callable $cancelFn = null
     ) {
         $this->waitFn = $waitFn;
         $this->cancelFn = $cancelFn;
     }
 
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         if ($this->state === self::PENDING) {
             $p = new Promise(null, [$this, 'cancel']);
             $this->handlers[] = [$p, $onFulfilled, $onRejected];
             $p->waitList = $this->waitList;
             $p->waitList[] = $this;
+
             return $p;
         }
 
         // Return a fulfilled promise and immediately invoke any callbacks.
         if ($this->state === self::FULFILLED) {
             $promise = Create::promiseFor($this->result);
+
             return $onFulfilled ? $promise->then($onFulfilled) : $promise;
         }
 
         // It's either cancelled or rejected, so return a rejected promise
         // and immediately invoke any callbacks.
         $rejection = Create::rejectionFor($this->result);
+
         return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
     }
 
-    public function otherwise(callable $onRejected)
+    public function otherwise(callable $onRejected): PromiseInterface
     {
         return $this->then(null, $onRejected);
     }
 
-    public function wait($unwrap = true)
+    public function wait(bool $unwrap = true)
     {
         $this->waitIfPending();
 
@@ -73,12 +80,12 @@ class Promise implements PromiseInterface
         }
     }
 
-    public function getState()
+    public function getState(): string
     {
         return $this->state;
     }
 
-    public function cancel()
+    public function cancel(): void
     {
         if ($this->state !== self::PENDING) {
             return;
@@ -93,8 +100,6 @@ class Promise implements PromiseInterface
                 $fn();
             } catch (\Throwable $e) {
                 $this->reject($e);
-            } catch (\Exception $e) {
-                $this->reject($e);
             }
         }
 
@@ -105,17 +110,17 @@ class Promise implements PromiseInterface
         }
     }
 
-    public function resolve($value)
+    public function resolve($value): void
     {
         $this->settle(self::FULFILLED, $value);
     }
 
-    public function reject($reason)
+    public function reject($reason): void
     {
         $this->settle(self::REJECTED, $reason);
     }
 
-    private function settle($state, $value)
+    private function settle(string $state, $value): void
     {
         if ($this->state !== self::PENDING) {
             // Ignore calls with the same resolution.
@@ -148,7 +153,7 @@ class Promise implements PromiseInterface
         if (!is_object($value) || !method_exists($value, 'then')) {
             $id = $state === self::FULFILLED ? 1 : 2;
             // It's a success, so resolve the handlers in the queue.
-            Utils::queue()->add(static function () use ($id, $value, $handlers) {
+            Utils::queue()->add(static function () use ($id, $value, $handlers): void {
                 foreach ($handlers as $handler) {
                     self::callHandler($id, $value, $handler);
                 }
@@ -159,12 +164,12 @@ class Promise implements PromiseInterface
         } else {
             // Resolve the handlers when the forwarded promise is resolved.
             $value->then(
-                static function ($value) use ($handlers) {
+                static function ($value) use ($handlers): void {
                     foreach ($handlers as $handler) {
                         self::callHandler(1, $value, $handler);
                     }
                 },
-                static function ($reason) use ($handlers) {
+                static function ($reason) use ($handlers): void {
                     foreach ($handlers as $handler) {
                         self::callHandler(2, $reason, $handler);
                     }
@@ -180,7 +185,7 @@ class Promise implements PromiseInterface
      * @param mixed $value   Value to pass to the callback.
      * @param array $handler Array of handler data (promise and callbacks).
      */
-    private static function callHandler($index, $value, array $handler)
+    private static function callHandler(int $index, $value, array $handler): void
     {
         /** @var PromiseInterface $promise */
         $promise = $handler[0];
@@ -211,12 +216,10 @@ class Promise implements PromiseInterface
             }
         } catch (\Throwable $reason) {
             $promise->reject($reason);
-        } catch (\Exception $reason) {
-            $promise->reject($reason);
         }
     }
 
-    private function waitIfPending()
+    private function waitIfPending(): void
     {
         if ($this->state !== self::PENDING) {
             return;
@@ -227,9 +230,9 @@ class Promise implements PromiseInterface
         } else {
             // If there's no wait function, then reject the promise.
             $this->reject('Cannot wait on a promise that has '
-                . 'no internal wait function. You must provide a wait '
-                . 'function when constructing the promise to be able to '
-                . 'wait on a promise.');
+                .'no internal wait function. You must provide a wait '
+                .'function when constructing the promise to be able to '
+                .'wait on a promise.');
         }
 
         Utils::queue()->run();
@@ -240,13 +243,13 @@ class Promise implements PromiseInterface
         }
     }
 
-    private function invokeWaitFn()
+    private function invokeWaitFn(): void
     {
         try {
             $wfn = $this->waitFn;
             $this->waitFn = null;
             $wfn(true);
-        } catch (\Exception $reason) {
+        } catch (\Throwable $reason) {
             if ($this->state === self::PENDING) {
                 // The promise has not been resolved yet, so reject the promise
                 // with the exception.
@@ -259,7 +262,7 @@ class Promise implements PromiseInterface
         }
     }
 
-    private function invokeWaitList()
+    private function invokeWaitList(): void
     {
         $waitList = $this->waitList;
         $this->waitList = null;

+ 16 - 22
modules/boonex/xero/plugins/guzzlehttp/promises/src/PromiseInterface.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -9,13 +11,13 @@ namespace GuzzleHttp\Promise;
  * which registers callbacks to receive either a promise’s eventual value or
  * the reason why the promise cannot be fulfilled.
  *
- * @link https://promisesaplus.com/
+ * @see https://promisesaplus.com/
  */
 interface PromiseInterface
 {
-    const PENDING = 'pending';
-    const FULFILLED = 'fulfilled';
-    const REJECTED = 'rejected';
+    public const PENDING = 'pending';
+    public const FULFILLED = 'fulfilled';
+    public const REJECTED = 'rejected';
 
     /**
      * Appends fulfillment and rejection handlers to the promise, and returns
@@ -23,13 +25,11 @@ interface PromiseInterface
      *
      * @param callable $onFulfilled Invoked when the promise fulfills.
      * @param callable $onRejected  Invoked when the promise is rejected.
-     *
-     * @return PromiseInterface
      */
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    );
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface;
 
     /**
      * Appends a rejection handler callback to the promise, and returns a new
@@ -38,20 +38,16 @@ interface PromiseInterface
      * fulfilled.
      *
      * @param callable $onRejected Invoked when the promise is rejected.
-     *
-     * @return PromiseInterface
      */
-    public function otherwise(callable $onRejected);
+    public function otherwise(callable $onRejected): PromiseInterface;
 
     /**
      * Get the state of the promise ("pending", "rejected", or "fulfilled").
      *
      * The three states can be checked against the constants defined on
      * PromiseInterface: PENDING, FULFILLED, and REJECTED.
-     *
-     * @return string
      */
-    public function getState();
+    public function getState(): string;
 
     /**
      * Resolve the promise with the given value.
@@ -60,7 +56,7 @@ interface PromiseInterface
      *
      * @throws \RuntimeException if the promise is already resolved.
      */
-    public function resolve($value);
+    public function resolve($value): void;
 
     /**
      * Reject the promise with the given reason.
@@ -69,14 +65,14 @@ interface PromiseInterface
      *
      * @throws \RuntimeException if the promise is already resolved.
      */
-    public function reject($reason);
+    public function reject($reason): void;
 
     /**
      * Cancels the promise if possible.
      *
-     * @link https://github.com/promises-aplus/cancellation-spec/issues/7
+     * @see https://github.com/promises-aplus/cancellation-spec/issues/7
      */
-    public function cancel();
+    public function cancel(): void;
 
     /**
      * Waits until the promise completes if possible.
@@ -86,12 +82,10 @@ interface PromiseInterface
      *
      * If the promise cannot be waited on, then the promise will be rejected.
      *
-     * @param bool $unwrap
-     *
      * @return mixed
      *
      * @throws \LogicException if the promise has no wait function or if the
      *                         promise does not settle after waiting.
      */
-    public function wait($unwrap = true);
+    public function wait(bool $unwrap = true);
 }

+ 3 - 3
modules/boonex/xero/plugins/guzzlehttp/promises/src/PromisorInterface.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -9,8 +11,6 @@ interface PromisorInterface
 {
     /**
      * Returns a promise.
-     *
-     * @return PromiseInterface
      */
-    public function promise();
+    public function promise(): PromiseInterface;
 }

+ 19 - 15
modules/boonex/xero/plugins/guzzlehttp/promises/src/RejectedPromise.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -7,11 +9,16 @@ namespace GuzzleHttp\Promise;
  *
  * Thenning off of this promise will invoke the onRejected callback
  * immediately and ignore other callbacks.
+ *
+ * @final
  */
 class RejectedPromise implements PromiseInterface
 {
     private $reason;
 
+    /**
+     * @param mixed $reason
+     */
     public function __construct($reason)
     {
         if (is_object($reason) && method_exists($reason, 'then')) {
@@ -24,9 +31,9 @@ class RejectedPromise implements PromiseInterface
     }
 
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         // If there's no onRejected callback then just return self.
         if (!$onRejected) {
             return $this;
@@ -35,7 +42,7 @@ class RejectedPromise implements PromiseInterface
         $queue = Utils::queue();
         $reason = $this->reason;
         $p = new Promise([$queue, 'run']);
-        $queue->add(static function () use ($p, $reason, $onRejected) {
+        $queue->add(static function () use ($p, $reason, $onRejected): void {
             if (Is::pending($p)) {
                 try {
                     // Return a resolved promise if onRejected does not throw.
@@ -43,9 +50,6 @@ class RejectedPromise implements PromiseInterface
                 } catch (\Throwable $e) {
                     // onRejected threw, so return a rejected promise.
                     $p->reject($e);
-                } catch (\Exception $e) {
-                    // onRejected threw, so return a rejected promise.
-                    $p->reject($e);
                 }
             }
         });
@@ -53,12 +57,12 @@ class RejectedPromise implements PromiseInterface
         return $p;
     }
 
-    public function otherwise(callable $onRejected)
+    public function otherwise(callable $onRejected): PromiseInterface
     {
         return $this->then(null, $onRejected);
     }
 
-    public function wait($unwrap = true, $defaultDelivery = null)
+    public function wait(bool $unwrap = true)
     {
         if ($unwrap) {
             throw Create::exceptionFor($this->reason);
@@ -67,24 +71,24 @@ class RejectedPromise implements PromiseInterface
         return null;
     }
 
-    public function getState()
+    public function getState(): string
     {
         return self::REJECTED;
     }
 
-    public function resolve($value)
+    public function resolve($value): void
     {
-        throw new \LogicException("Cannot resolve a rejected promise");
+        throw new \LogicException('Cannot resolve a rejected promise');
     }
 
-    public function reject($reason)
+    public function reject($reason): void
     {
         if ($reason !== $this->reason) {
-            throw new \LogicException("Cannot reject a rejected promise");
+            throw new \LogicException('Cannot reject a rejected promise');
         }
     }
 
-    public function cancel()
+    public function cancel(): void
     {
         // pass
     }

+ 8 - 7
modules/boonex/xero/plugins/guzzlehttp/promises/src/RejectionException.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -13,24 +15,23 @@ class RejectionException extends \RuntimeException
     private $reason;
 
     /**
-     * @param mixed  $reason      Rejection reason.
-     * @param string $description Optional description
+     * @param mixed       $reason      Rejection reason.
+     * @param string|null $description Optional description.
      */
-    public function __construct($reason, $description = null)
+    public function __construct($reason, ?string $description = null)
     {
         $this->reason = $reason;
 
         $message = 'The promise was rejected';
 
         if ($description) {
-            $message .= ' with reason: ' . $description;
+            $message .= ' with reason: '.$description;
         } elseif (is_string($reason)
             || (is_object($reason) && method_exists($reason, '__toString'))
         ) {
-            $message .= ' with reason: ' . $this->reason;
+            $message .= ' with reason: '.$this->reason;
         } elseif ($reason instanceof \JsonSerializable) {
-            $message .= ' with reason: '
-                . json_encode($this->reason, JSON_PRETTY_PRINT);
+            $message .= ' with reason: '.json_encode($this->reason, JSON_PRETTY_PRINT);
         }
 
         parent::__construct($message);

+ 10 - 6
modules/boonex/xero/plugins/guzzlehttp/promises/src/TaskQueue.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -10,16 +12,18 @@ namespace GuzzleHttp\Promise;
  * by calling the `run()` function of the global task queue in an event loop.
  *
  *     GuzzleHttp\Promise\Utils::queue()->run();
+ *
+ * @final
  */
 class TaskQueue implements TaskQueueInterface
 {
     private $enableShutdown = true;
     private $queue = [];
 
-    public function __construct($withShutdown = true)
+    public function __construct(bool $withShutdown = true)
     {
         if ($withShutdown) {
-            register_shutdown_function(function () {
+            register_shutdown_function(function (): void {
                 if ($this->enableShutdown) {
                     // Only run the tasks if an E_ERROR didn't occur.
                     $err = error_get_last();
@@ -31,17 +35,17 @@ class TaskQueue implements TaskQueueInterface
         }
     }
 
-    public function isEmpty()
+    public function isEmpty(): bool
     {
         return !$this->queue;
     }
 
-    public function add(callable $task)
+    public function add(callable $task): void
     {
         $this->queue[] = $task;
     }
 
-    public function run()
+    public function run(): void
     {
         while ($task = array_shift($this->queue)) {
             /** @var callable $task */
@@ -60,7 +64,7 @@ class TaskQueue implements TaskQueueInterface
      *
      * Note: This shutdown will occur before any destructors are triggered.
      */
-    public function disableShutdown()
+    public function disableShutdown(): void
     {
         $this->enableShutdown = false;
     }

+ 5 - 5
modules/boonex/xero/plugins/guzzlehttp/promises/src/TaskQueueInterface.php

@@ -1,24 +1,24 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 interface TaskQueueInterface
 {
     /**
      * Returns true if the queue is empty.
-     *
-     * @return bool
      */
-    public function isEmpty();
+    public function isEmpty(): bool;
 
     /**
      * Adds a task to the queue that will be executed the next time run is
      * called.
      */
-    public function add(callable $task);
+    public function add(callable $task): void;
 
     /**
      * Execute all of the pending task in the queue.
      */
-    public function run();
+    public function run(): void;
 }

+ 29 - 44
modules/boonex/xero/plugins/guzzlehttp/promises/src/Utils.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 final class Utils
@@ -17,11 +19,9 @@ final class Utils
      * }
      * </code>
      *
-     * @param TaskQueueInterface $assign Optionally specify a new queue instance.
-     *
-     * @return TaskQueueInterface
+     * @param TaskQueueInterface|null $assign Optionally specify a new queue instance.
      */
-    public static function queue(TaskQueueInterface $assign = null)
+    public static function queue(?TaskQueueInterface $assign = null): TaskQueueInterface
     {
         static $queue;
 
@@ -39,22 +39,18 @@ final class Utils
      * returns a promise that is fulfilled or rejected with the result.
      *
      * @param callable $task Task function to run.
-     *
-     * @return PromiseInterface
      */
-    public static function task(callable $task)
+    public static function task(callable $task): PromiseInterface
     {
         $queue = self::queue();
         $promise = new Promise([$queue, 'run']);
-        $queue->add(function () use ($task, $promise) {
+        $queue->add(function () use ($task, $promise): void {
             try {
                 if (Is::pending($promise)) {
                     $promise->resolve($task());
                 }
             } catch (\Throwable $e) {
                 $promise->reject($e);
-            } catch (\Exception $e) {
-                $promise->reject($e);
             }
         });
 
@@ -72,22 +68,18 @@ final class Utils
      * key mapping to the rejection reason of the promise.
      *
      * @param PromiseInterface $promise Promise or value.
-     *
-     * @return array
      */
-    public static function inspect(PromiseInterface $promise)
+    public static function inspect(PromiseInterface $promise): array
     {
         try {
             return [
                 'state' => PromiseInterface::FULFILLED,
-                'value' => $promise->wait()
+                'value' => $promise->wait(),
             ];
         } catch (RejectionException $e) {
             return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
         } catch (\Throwable $e) {
             return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
-        } catch (\Exception $e) {
-            return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
         }
     }
 
@@ -100,14 +92,12 @@ final class Utils
      * @see inspect for the inspection state array format.
      *
      * @param PromiseInterface[] $promises Traversable of promises to wait upon.
-     *
-     * @return array
      */
-    public static function inspectAll($promises)
+    public static function inspectAll($promises): array
     {
         $results = [];
         foreach ($promises as $key => $promise) {
-            $results[$key] = inspect($promise);
+            $results[$key] = self::inspect($promise);
         }
 
         return $results;
@@ -122,12 +112,9 @@ final class Utils
      *
      * @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
      *
-     * @return array
-     *
-     * @throws \Exception on error
-     * @throws \Throwable on error in PHP >=7
+     * @throws \Throwable on error
      */
-    public static function unwrap($promises)
+    public static function unwrap($promises): array
     {
         $results = [];
         foreach ($promises as $key => $promise) {
@@ -147,22 +134,23 @@ final class Utils
      *
      * @param mixed $promises  Promises or values.
      * @param bool  $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
-     *
-     * @return PromiseInterface
      */
-    public static function all($promises, $recursive = false)
+    public static function all($promises, bool $recursive = false): PromiseInterface
     {
         $results = [];
         $promise = Each::of(
             $promises,
-            function ($value, $idx) use (&$results) {
+            function ($value, $idx) use (&$results): void {
                 $results[$idx] = $value;
             },
-            function ($reason, $idx, Promise $aggregate) {
-                $aggregate->reject($reason);
+            function ($reason, $idx, Promise $aggregate): void {
+                if (Is::pending($aggregate)) {
+                    $aggregate->reject($reason);
+                }
             }
         )->then(function () use (&$results) {
             ksort($results);
+
             return $results;
         });
 
@@ -173,6 +161,7 @@ final class Utils
                         return self::all($promises, $recursive);
                     }
                 }
+
                 return $results;
             });
         }
@@ -193,17 +182,15 @@ final class Utils
      *
      * @param int   $count    Total number of promises.
      * @param mixed $promises Promises or values.
-     *
-     * @return PromiseInterface
      */
-    public static function some($count, $promises)
+    public static function some(int $count, $promises): PromiseInterface
     {
         $results = [];
         $rejections = [];
 
         return Each::of(
             $promises,
-            function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
+            function ($value, $idx, PromiseInterface $p) use (&$results, $count): void {
                 if (Is::settled($p)) {
                     return;
                 }
@@ -212,7 +199,7 @@ final class Utils
                     $p->resolve(null);
                 }
             },
-            function ($reason) use (&$rejections) {
+            function ($reason) use (&$rejections): void {
                 $rejections[] = $reason;
             }
         )->then(
@@ -224,6 +211,7 @@ final class Utils
                     );
                 }
                 ksort($results);
+
                 return array_values($results);
             }
         );
@@ -234,10 +222,8 @@ final class Utils
      * fulfillment value is not an array of 1 but the value directly.
      *
      * @param mixed $promises Promises or values.
-     *
-     * @return PromiseInterface
      */
-    public static function any($promises)
+    public static function any($promises): PromiseInterface
     {
         return self::some(1, $promises)->then(function ($values) {
             return $values[0];
@@ -253,23 +239,22 @@ final class Utils
      * @see inspect for the inspection state array format.
      *
      * @param mixed $promises Promises or values.
-     *
-     * @return PromiseInterface
      */
-    public static function settle($promises)
+    public static function settle($promises): PromiseInterface
     {
         $results = [];
 
         return Each::of(
             $promises,
-            function ($value, $idx) use (&$results) {
+            function ($value, $idx) use (&$results): void {
                 $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
             },
-            function ($reason, $idx) use (&$results) {
+            function ($reason, $idx) use (&$results): void {
                 $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
             }
         )->then(function () use (&$results) {
             ksort($results);
+
             return $results;
         });
     }

+ 0 - 363
modules/boonex/xero/plugins/guzzlehttp/promises/src/functions.php

@@ -1,363 +0,0 @@
-<?php
-
-namespace GuzzleHttp\Promise;
-
-/**
- * Get the global task queue used for promise resolution.
- *
- * This task queue MUST be run in an event loop in order for promises to be
- * settled asynchronously. It will be automatically run when synchronously
- * waiting on a promise.
- *
- * <code>
- * while ($eventLoop->isRunning()) {
- *     GuzzleHttp\Promise\queue()->run();
- * }
- * </code>
- *
- * @param TaskQueueInterface $assign Optionally specify a new queue instance.
- *
- * @return TaskQueueInterface
- *
- * @deprecated queue will be removed in guzzlehttp/promises:2.0. Use Utils::queue instead.
- */
-function queue(TaskQueueInterface $assign = null)
-{
-    return Utils::queue($assign);
-}
-
-/**
- * Adds a function to run in the task queue when it is next `run()` and returns
- * a promise that is fulfilled or rejected with the result.
- *
- * @param callable $task Task function to run.
- *
- * @return PromiseInterface
- *
- * @deprecated task will be removed in guzzlehttp/promises:2.0. Use Utils::task instead.
- */
-function task(callable $task)
-{
-    return Utils::task($task);
-}
-
-/**
- * Creates a promise for a value if the value is not a promise.
- *
- * @param mixed $value Promise or value.
- *
- * @return PromiseInterface
- *
- * @deprecated promise_for will be removed in guzzlehttp/promises:2.0. Use Create::promiseFor instead.
- */
-function promise_for($value)
-{
-    return Create::promiseFor($value);
-}
-
-/**
- * Creates a rejected promise for a reason if the reason is not a promise. If
- * the provided reason is a promise, then it is returned as-is.
- *
- * @param mixed $reason Promise or reason.
- *
- * @return PromiseInterface
- *
- * @deprecated rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead.
- */
-function rejection_for($reason)
-{
-    return Create::rejectionFor($reason);
-}
-
-/**
- * Create an exception for a rejected promise value.
- *
- * @param mixed $reason
- *
- * @return \Exception|\Throwable
- *
- * @deprecated exception_for will be removed in guzzlehttp/promises:2.0. Use Create::exceptionFor instead.
- */
-function exception_for($reason)
-{
-    return Create::exceptionFor($reason);
-}
-
-/**
- * Returns an iterator for the given value.
- *
- * @param mixed $value
- *
- * @return \Iterator
- *
- * @deprecated iter_for will be removed in guzzlehttp/promises:2.0. Use Create::iterFor instead.
- */
-function iter_for($value)
-{
-    return Create::iterFor($value);
-}
-
-/**
- * Synchronously waits on a promise to resolve and returns an inspection state
- * array.
- *
- * Returns a state associative array containing a "state" key mapping to a
- * valid promise state. If the state of the promise is "fulfilled", the array
- * will contain a "value" key mapping to the fulfilled value of the promise. If
- * the promise is rejected, the array will contain a "reason" key mapping to
- * the rejection reason of the promise.
- *
- * @param PromiseInterface $promise Promise or value.
- *
- * @return array
- *
- * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspect instead.
- */
-function inspect(PromiseInterface $promise)
-{
-    return Utils::inspect($promise);
-}
-
-/**
- * Waits on all of the provided promises, but does not unwrap rejected promises
- * as thrown exception.
- *
- * Returns an array of inspection state arrays.
- *
- * @see inspect for the inspection state array format.
- *
- * @param PromiseInterface[] $promises Traversable of promises to wait upon.
- *
- * @return array
- *
- * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspectAll instead.
- */
-function inspect_all($promises)
-{
-    return Utils::inspectAll($promises);
-}
-
-/**
- * Waits on all of the provided promises and returns the fulfilled values.
- *
- * Returns an array that contains the value of each promise (in the same order
- * the promises were provided). An exception is thrown if any of the promises
- * are rejected.
- *
- * @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
- *
- * @return array
- *
- * @throws \Exception on error
- * @throws \Throwable on error in PHP >=7
- *
- * @deprecated unwrap will be removed in guzzlehttp/promises:2.0. Use Utils::unwrap instead.
- */
-function unwrap($promises)
-{
-    return Utils::unwrap($promises);
-}
-
-/**
- * Given an array of promises, return a promise that is fulfilled when all the
- * items in the array are fulfilled.
- *
- * The promise's fulfillment value is an array with fulfillment values at
- * respective positions to the original array. If any promise in the array
- * rejects, the returned promise is rejected with the rejection reason.
- *
- * @param mixed $promises  Promises or values.
- * @param bool  $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
- *
- * @return PromiseInterface
- *
- * @deprecated all will be removed in guzzlehttp/promises:2.0. Use Utils::all instead.
- */
-function all($promises, $recursive = false)
-{
-    return Utils::all($promises, $recursive);
-}
-
-/**
- * Initiate a competitive race between multiple promises or values (values will
- * become immediately fulfilled promises).
- *
- * When count amount of promises have been fulfilled, the returned promise is
- * fulfilled with an array that contains the fulfillment values of the winners
- * in order of resolution.
- *
- * This promise is rejected with a {@see AggregateException} if the number of
- * fulfilled promises is less than the desired $count.
- *
- * @param int   $count    Total number of promises.
- * @param mixed $promises Promises or values.
- *
- * @return PromiseInterface
- *
- * @deprecated some will be removed in guzzlehttp/promises:2.0. Use Utils::some instead.
- */
-function some($count, $promises)
-{
-    return Utils::some($count, $promises);
-}
-
-/**
- * Like some(), with 1 as count. However, if the promise fulfills, the
- * fulfillment value is not an array of 1 but the value directly.
- *
- * @param mixed $promises Promises or values.
- *
- * @return PromiseInterface
- *
- * @deprecated any will be removed in guzzlehttp/promises:2.0. Use Utils::any instead.
- */
-function any($promises)
-{
-    return Utils::any($promises);
-}
-
-/**
- * Returns a promise that is fulfilled when all of the provided promises have
- * been fulfilled or rejected.
- *
- * The returned promise is fulfilled with an array of inspection state arrays.
- *
- * @see inspect for the inspection state array format.
- *
- * @param mixed $promises Promises or values.
- *
- * @return PromiseInterface
- *
- * @deprecated settle will be removed in guzzlehttp/promises:2.0. Use Utils::settle instead.
- */
-function settle($promises)
-{
-    return Utils::settle($promises);
-}
-
-/**
- * Given an iterator that yields promises or values, returns a promise that is
- * fulfilled with a null value when the iterator has been consumed or the
- * aggregate promise has been fulfilled or rejected.
- *
- * $onFulfilled is a function that accepts the fulfilled value, iterator index,
- * and the aggregate promise. The callback can invoke any necessary side
- * effects and choose to resolve or reject the aggregate if needed.
- *
- * $onRejected is a function that accepts the rejection reason, iterator index,
- * and the aggregate promise. The callback can invoke any necessary side
- * effects and choose to resolve or reject the aggregate if needed.
- *
- * @param mixed    $iterable    Iterator or array to iterate over.
- * @param callable $onFulfilled
- * @param callable $onRejected
- *
- * @return PromiseInterface
- *
- * @deprecated each will be removed in guzzlehttp/promises:2.0. Use Each::of instead.
- */
-function each(
-    $iterable,
-    callable $onFulfilled = null,
-    callable $onRejected = null
-) {
-    return Each::of($iterable, $onFulfilled, $onRejected);
-}
-
-/**
- * Like each, but only allows a certain number of outstanding promises at any
- * given time.
- *
- * $concurrency may be an integer or a function that accepts the number of
- * pending promises and returns a numeric concurrency limit value to allow for
- * dynamic a concurrency size.
- *
- * @param mixed        $iterable
- * @param int|callable $concurrency
- * @param callable     $onFulfilled
- * @param callable     $onRejected
- *
- * @return PromiseInterface
- *
- * @deprecated each_limit will be removed in guzzlehttp/promises:2.0. Use Each::ofLimit instead.
- */
-function each_limit(
-    $iterable,
-    $concurrency,
-    callable $onFulfilled = null,
-    callable $onRejected = null
-) {
-    return Each::ofLimit($iterable, $concurrency, $onFulfilled, $onRejected);
-}
-
-/**
- * Like each_limit, but ensures that no promise in the given $iterable argument
- * is rejected. If any promise is rejected, then the aggregate promise is
- * rejected with the encountered rejection.
- *
- * @param mixed        $iterable
- * @param int|callable $concurrency
- * @param callable     $onFulfilled
- *
- * @return PromiseInterface
- *
- * @deprecated each_limit_all will be removed in guzzlehttp/promises:2.0. Use Each::ofLimitAll instead.
- */
-function each_limit_all(
-    $iterable,
-    $concurrency,
-    callable $onFulfilled = null
-) {
-    return Each::ofLimitAll($iterable, $concurrency, $onFulfilled);
-}
-
-/**
- * Returns true if a promise is fulfilled.
- *
- * @return bool
- *
- * @deprecated is_fulfilled will be removed in guzzlehttp/promises:2.0. Use Is::fulfilled instead.
- */
-function is_fulfilled(PromiseInterface $promise)
-{
-    return Is::fulfilled($promise);
-}
-
-/**
- * Returns true if a promise is rejected.
- *
- * @return bool
- *
- * @deprecated is_rejected will be removed in guzzlehttp/promises:2.0. Use Is::rejected instead.
- */
-function is_rejected(PromiseInterface $promise)
-{
-    return Is::rejected($promise);
-}
-
-/**
- * Returns true if a promise is fulfilled or rejected.
- *
- * @return bool
- *
- * @deprecated is_settled will be removed in guzzlehttp/promises:2.0. Use Is::settled instead.
- */
-function is_settled(PromiseInterface $promise)
-{
-    return Is::settled($promise);
-}
-
-/**
- * Create a new coroutine.
- *
- * @see Coroutine
- *
- * @return PromiseInterface
- *
- * @deprecated coroutine will be removed in guzzlehttp/promises:2.0. Use Coroutine::of instead.
- */
-function coroutine(callable $generatorFn)
-{
-    return Coroutine::of($generatorFn);
-}

+ 0 - 6
modules/boonex/xero/plugins/guzzlehttp/promises/src/functions_include.php

@@ -1,6 +0,0 @@
-<?php
-
-// Don't redefine the functions if included multiple times.
-if (!function_exists('GuzzleHttp\Promise\promise_for')) {
-    require __DIR__ . '/functions.php';
-}

+ 151 - 2
modules/boonex/xero/plugins/guzzlehttp/psr7/CHANGELOG.md

@@ -5,7 +5,156 @@ 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).
 
-## Unreleased
+## 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
 
@@ -45,7 +194,7 @@ Identical to the RC release.
 ### Removed
 
 - PHP < 7.2 support
-- All functions in the Guzzle\Psr7 namespace
+- All functions in the `GuzzleHttp\Psr7` namespace
 
 ## 1.8.1 - 2021-03-21
 

+ 79 - 15
modules/boonex/xero/plugins/guzzlehttp/psr7/README.md

@@ -1,6 +1,6 @@
 # PSR-7 Message Implementation
 
-This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/)
+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.
 
@@ -8,12 +8,26 @@ functionality like query string parsing.
 ![Static analysis](https://github.com/guzzle/psr7/workflows/Static%20analysis/badge.svg)
 
 
-# Stream implementation
+## 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.5 |
+
+
 ## AppendStream
 
 `GuzzleHttp\Psr7\AppendStream`
@@ -245,6 +259,8 @@ class EofCallbackStream implements StreamInterface
 
     private $callback;
 
+    private $stream;
+
     public function __construct(StreamInterface $stream, callable $cb)
     {
         $this->stream = $stream;
@@ -257,7 +273,7 @@ class EofCallbackStream implements StreamInterface
 
         // Invoke the callback when EOF is hit.
         if ($this->eof()) {
-            call_user_func($this->callback);
+            ($this->callback)();
         }
 
         return $result;
@@ -380,10 +396,28 @@ 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::normalize`
+## `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.
 
@@ -402,7 +436,7 @@ will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
 
 ## `GuzzleHttp\Psr7\Query::build`
 
-`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string`
+`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.
 
@@ -464,11 +498,18 @@ a message.
 
 ## `GuzzleHttp\Psr7\Utils::readLine`
 
-`public static function readLine(StreamInterface $stream, int $maxLength = null): string`
+`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`
@@ -527,6 +568,17 @@ 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`
@@ -592,7 +644,7 @@ this library also provides additional functionality when working with URIs as st
 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://tools.ietf.org/html/rfc3986#section-4.2):
+[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`
@@ -629,7 +681,7 @@ termed a relative-path reference.
 
 ### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
 
-`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
+`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
@@ -651,14 +703,14 @@ or the standard port. This method can be used independently of the implementatio
 `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://tools.ietf.org/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`.
+[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`](http://php.net/manual/en/function.parse-url.php) components.
+Creates a URI from a hash of [`parse_url`](https://www.php.net/manual/en/function.parse-url.php) components.
 
 
 ### `GuzzleHttp\Psr7\Uri::withQueryValue`
@@ -683,11 +735,21 @@ associative array of key => value.
 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.
+
 ## 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://tools.ietf.org/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.
+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`
 
@@ -700,7 +762,7 @@ Converts the relative URI into a new URI that is resolved against the base URI.
 `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://tools.ietf.org/html/rfc3986#section-5.2.4).
+[RFC 3986 Section 5.2.4](https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4).
 
 ### `GuzzleHttp\Psr7\UriResolver::relativize`
 
@@ -726,7 +788,7 @@ echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // pr
 ## Normalization and Comparison
 
 `GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
-[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).
+[RFC 3986 Section 6](https://datatracker.ietf.org/doc/html/rfc3986#section-6).
 
 ### `GuzzleHttp\Psr7\UriNormalizer::normalize`
 
@@ -812,10 +874,12 @@ equivalence or difference of relative references does not mean anything.
 
 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

+ 10 - 6
modules/boonex/xero/plugins/guzzlehttp/psr7/composer.json

@@ -52,7 +52,7 @@
     "require": {
         "php": "^7.2.5 || ^8.0",
         "psr/http-factory": "^1.0",
-        "psr/http-message": "^1.0",
+        "psr/http-message": "^1.1 || ^2.0",
         "ralouphie/getallheaders": "^3.0"
     },
     "provide": {
@@ -60,9 +60,9 @@
         "psr/http-message-implementation": "1.0"
     },
     "require-dev": {
-        "bamarni/composer-bin-plugin": "^1.4.1",
-        "http-interop/http-factory-tests": "^0.9",
-        "phpunit/phpunit": "^8.5.8 || ^9.3.10"
+        "bamarni/composer-bin-plugin": "^1.8.2",
+        "http-interop/http-factory-tests": "0.9.0",
+        "phpunit/phpunit": "^8.5.39 || ^9.6.20"
     },
     "suggest": {
         "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
@@ -78,11 +78,15 @@
         }
     },
     "extra": {
-        "branch-alias": {
-            "dev-master": "2.1-dev"
+        "bamarni-bin": {
+            "bin-links": true,
+            "forward-command": false
         }
     },
     "config": {
+        "allow-plugins": {
+            "bamarni/composer-bin-plugin": true
+        },
         "preferred-install": "dist",
         "sort-packages": true
     }

+ 7 - 8
modules/boonex/xero/plugins/guzzlehttp/psr7/src/AppendStream.php

@@ -40,12 +40,14 @@ final class AppendStream implements StreamInterface
     {
         try {
             $this->rewind();
+
             return $this->getContents();
         } catch (\Throwable $e) {
             if (\PHP_VERSION_ID >= 70400) {
                 throw $e;
             }
             trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
             return '';
         }
     }
@@ -138,9 +140,9 @@ final class AppendStream implements StreamInterface
 
     public function eof(): bool
     {
-        return !$this->streams ||
-            ($this->current >= count($this->streams) - 1 &&
-             $this->streams[$this->current]->eof());
+        return !$this->streams
+            || ($this->current >= count($this->streams) - 1
+             && $this->streams[$this->current]->eof());
     }
 
     public function rewind(): void
@@ -167,7 +169,7 @@ final class AppendStream implements StreamInterface
                 $stream->rewind();
             } catch (\Exception $e) {
                 throw new \RuntimeException('Unable to seek stream '
-                    . $i . ' of the AppendStream', 0, $e);
+                    .$i.' of the AppendStream', 0, $e);
             }
         }
 
@@ -191,14 +193,13 @@ final class AppendStream implements StreamInterface
         $progressToNext = false;
 
         while ($remaining > 0) {
-
             // Progress to the next stream if needed.
             if ($progressToNext || $this->streams[$this->current]->eof()) {
                 $progressToNext = false;
                 if ($this->current === $total) {
                     break;
                 }
-                $this->current++;
+                ++$this->current;
             }
 
             $result = $this->streams[$this->current]->read($remaining);
@@ -238,8 +239,6 @@ final class AppendStream implements StreamInterface
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)

+ 0 - 2
modules/boonex/xero/plugins/guzzlehttp/psr7/src/BufferStream.php

@@ -134,8 +134,6 @@ final class BufferStream implements StreamInterface
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)

+ 6 - 1
modules/boonex/xero/plugins/guzzlehttp/psr7/src/CachingStream.php

@@ -20,6 +20,11 @@ final class CachingStream implements StreamInterface
     /** @var int Number of bytes to skip reading due to a write on the buffer */
     private $skipReadBytes = 0;
 
+    /**
+     * @var StreamInterface
+     */
+    private $stream;
+
     /**
      * We will treat the buffer object as the body of the stream
      *
@@ -28,7 +33,7 @@ final class CachingStream implements StreamInterface
      */
     public function __construct(
         StreamInterface $stream,
-        StreamInterface $target = null
+        ?StreamInterface $target = null
     ) {
         $this->remoteStream = $stream;
         $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+'));

+ 3 - 0
modules/boonex/xero/plugins/guzzlehttp/psr7/src/DroppingStream.php

@@ -17,6 +17,9 @@ final class DroppingStream implements StreamInterface
     /** @var int */
     private $maxLength;
 
+    /** @var StreamInterface */
+    private $stream;
+
     /**
      * @param StreamInterface $stream    Underlying stream to decorate.
      * @param int             $maxLength Maximum size before dropping data.

+ 22 - 21
modules/boonex/xero/plugins/guzzlehttp/psr7/src/FnStream.php

@@ -12,12 +12,13 @@ use Psr\Http\Message\StreamInterface;
  * Allows for easy testing and extension of a provided stream without needing
  * to create a concrete class for a simple extension point.
  */
+#[\AllowDynamicProperties]
 final class FnStream implements StreamInterface
 {
     private const SLOTS = [
         '__toString', 'close', 'detach', 'rewind',
         'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
-        'isReadable', 'read', 'getContents', 'getMetadata'
+        'isReadable', 'read', 'getContents', 'getMetadata',
     ];
 
     /** @var array<string, callable> */
@@ -32,7 +33,7 @@ final class FnStream implements StreamInterface
 
         // Create the functions on the class
         foreach ($methods as $name => $fn) {
-            $this->{'_fn_' . $name} = $fn;
+            $this->{'_fn_'.$name} = $fn;
         }
     }
 
@@ -44,7 +45,7 @@ final class FnStream implements StreamInterface
     public function __get(string $name): void
     {
         throw new \BadMethodCallException(str_replace('_fn_', '', $name)
-            . '() is not implemented in the FnStream');
+            .'() is not implemented in the FnStream');
     }
 
     /**
@@ -53,7 +54,7 @@ final class FnStream implements StreamInterface
     public function __destruct()
     {
         if (isset($this->_fn_close)) {
-            call_user_func($this->_fn_close);
+            ($this->_fn_close)();
         }
     }
 
@@ -92,88 +93,88 @@ final class FnStream implements StreamInterface
     public function __toString(): string
     {
         try {
-            return call_user_func($this->_fn___toString);
+            /** @var string */
+            return ($this->_fn___toString)();
         } catch (\Throwable $e) {
             if (\PHP_VERSION_ID >= 70400) {
                 throw $e;
             }
             trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
             return '';
         }
     }
 
     public function close(): void
     {
-        call_user_func($this->_fn_close);
+        ($this->_fn_close)();
     }
 
     public function detach()
     {
-        return call_user_func($this->_fn_detach);
+        return ($this->_fn_detach)();
     }
 
     public function getSize(): ?int
     {
-        return call_user_func($this->_fn_getSize);
+        return ($this->_fn_getSize)();
     }
 
     public function tell(): int
     {
-        return call_user_func($this->_fn_tell);
+        return ($this->_fn_tell)();
     }
 
     public function eof(): bool
     {
-        return call_user_func($this->_fn_eof);
+        return ($this->_fn_eof)();
     }
 
     public function isSeekable(): bool
     {
-        return call_user_func($this->_fn_isSeekable);
+        return ($this->_fn_isSeekable)();
     }
 
     public function rewind(): void
     {
-        call_user_func($this->_fn_rewind);
+        ($this->_fn_rewind)();
     }
 
     public function seek($offset, $whence = SEEK_SET): void
     {
-        call_user_func($this->_fn_seek, $offset, $whence);
+        ($this->_fn_seek)($offset, $whence);
     }
 
     public function isWritable(): bool
     {
-        return call_user_func($this->_fn_isWritable);
+        return ($this->_fn_isWritable)();
     }
 
     public function write($string): int
     {
-        return call_user_func($this->_fn_write, $string);
+        return ($this->_fn_write)($string);
     }
 
     public function isReadable(): bool
     {
-        return call_user_func($this->_fn_isReadable);
+        return ($this->_fn_isReadable)();
     }
 
     public function read($length): string
     {
-        return call_user_func($this->_fn_read, $length);
+        return ($this->_fn_read)($length);
     }
 
     public function getContents(): string
     {
-        return call_user_func($this->_fn_getContents);
+        return ($this->_fn_getContents)();
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)
     {
-        return call_user_func($this->_fn_getMetadata, $key);
+        return ($this->_fn_getMetadata)($key);
     }
 }

+ 85 - 20
modules/boonex/xero/plugins/guzzlehttp/psr7/src/Header.php

@@ -19,20 +19,22 @@ final class Header
         static $trimmed = "\"'  \n\t\r";
         $params = $matches = [];
 
-        foreach (self::normalize($header) as $val) {
-            $part = [];
-            foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
-                if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
-                    $m = $matches[0];
-                    if (isset($m[1])) {
-                        $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
-                    } else {
-                        $part[] = trim($m[0], $trimmed);
+        foreach ((array) $header as $value) {
+            foreach (self::splitList($value) as $val) {
+                $part = [];
+                foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) ?: [] as $kvp) {
+                    if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
+                        $m = $matches[0];
+                        if (isset($m[1])) {
+                            $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
+                        } else {
+                            $part[] = trim($m[0], $trimmed);
+                        }
                     }
                 }
-            }
-            if ($part) {
-                $params[] = $part;
+                if ($part) {
+                    $params[] = $part;
+                }
             }
         }
 
@@ -44,23 +46,86 @@ final class Header
      * headers into an array of headers with no comma separated values.
      *
      * @param string|array $header Header to normalize.
+     *
+     * @deprecated Use self::splitList() instead.
      */
     public static function normalize($header): array
     {
-        if (!is_array($header)) {
-            return array_map('trim', explode(',', $header));
+        $result = [];
+        foreach ((array) $header as $value) {
+            foreach (self::splitList($value) as $parsed) {
+                $result[] = $parsed;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Splits a HTTP header defined to contain a comma-separated list into
+     * each individual value. Empty values will be removed.
+     *
+     * Example headers include 'accept', 'cache-control' and 'if-none-match'.
+     *
+     * This method must not be used to parse headers that are not defined as
+     * a list, such as 'user-agent' or 'set-cookie'.
+     *
+     * @param string|string[] $values Header value as returned by MessageInterface::getHeader()
+     *
+     * @return string[]
+     */
+    public static function splitList($values): array
+    {
+        if (!\is_array($values)) {
+            $values = [$values];
         }
 
         $result = [];
-        foreach ($header as $value) {
-            foreach ((array) $value as $v) {
-                if (strpos($v, ',') === false) {
-                    $result[] = $v;
+        foreach ($values as $value) {
+            if (!\is_string($value)) {
+                throw new \TypeError('$header must either be a string or an array containing strings.');
+            }
+
+            $v = '';
+            $isQuoted = false;
+            $isEscaped = false;
+            for ($i = 0, $max = \strlen($value); $i < $max; ++$i) {
+                if ($isEscaped) {
+                    $v .= $value[$i];
+                    $isEscaped = false;
+
                     continue;
                 }
-                foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
-                    $result[] = trim($vv);
+
+                if (!$isQuoted && $value[$i] === ',') {
+                    $v = \trim($v);
+                    if ($v !== '') {
+                        $result[] = $v;
+                    }
+
+                    $v = '';
+                    continue;
                 }
+
+                if ($isQuoted && $value[$i] === '\\') {
+                    $isEscaped = true;
+                    $v .= $value[$i];
+
+                    continue;
+                }
+                if ($value[$i] === '"') {
+                    $isQuoted = !$isQuoted;
+                    $v .= $value[$i];
+
+                    continue;
+                }
+
+                $v .= $value[$i];
+            }
+
+            $v = \trim($v);
+            if ($v !== '') {
+                $result[] = $v;
             }
         }
 

+ 4 - 10
modules/boonex/xero/plugins/guzzlehttp/psr7/src/HttpFactory.php

@@ -23,20 +23,14 @@ use Psr\Http\Message\UriInterface;
  * Note: in consuming code it is recommended to require the implemented interfaces
  * and inject the instance of this class multiple times.
  */
-final class HttpFactory implements
-    RequestFactoryInterface,
-    ResponseFactoryInterface,
-    ServerRequestFactoryInterface,
-    StreamFactoryInterface,
-    UploadedFileFactoryInterface,
-    UriFactoryInterface
+final class HttpFactory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
 {
     public function createUploadedFile(
         StreamInterface $stream,
-        int $size = null,
+        ?int $size = null,
         int $error = \UPLOAD_ERR_OK,
-        string $clientFilename = null,
-        string $clientMediaType = null
+        ?string $clientFilename = null,
+        ?string $clientMediaType = null
     ): UploadedFileInterface {
         if ($size === null) {
             $size = $stream->getSize();

+ 7 - 4
modules/boonex/xero/plugins/guzzlehttp/psr7/src/InflateStream.php

@@ -13,19 +13,22 @@ use Psr\Http\Message\StreamInterface;
  * then appends the zlib.inflate filter. The stream is then converted back
  * to a Guzzle stream resource to be used as a Guzzle stream.
  *
- * @link http://tools.ietf.org/html/rfc1950
- * @link http://tools.ietf.org/html/rfc1952
- * @link http://php.net/manual/en/filters.compression.php
+ * @see https://datatracker.ietf.org/doc/html/rfc1950
+ * @see https://datatracker.ietf.org/doc/html/rfc1952
+ * @see https://www.php.net/manual/en/filters.compression.php
  */
 final class InflateStream implements StreamInterface
 {
     use StreamDecoratorTrait;
 
+    /** @var StreamInterface */
+    private $stream;
+
     public function __construct(StreamInterface $stream)
     {
         $resource = StreamWrapper::getResource($stream);
         // Specify window=15+32, so zlib will use header detection to both gzip (with header) and zlib data
-        // See http://www.zlib.net/manual.html#Advanced definition of inflateInit2
+        // See https://www.zlib.net/manual.html#Advanced definition of inflateInit2
         // "Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
         // Default window size is 15.
         stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15 + 32]);

+ 9 - 0
modules/boonex/xero/plugins/guzzlehttp/psr7/src/LazyOpenStream.php

@@ -20,6 +20,11 @@ final class LazyOpenStream implements StreamInterface
     /** @var string */
     private $mode;
 
+    /**
+     * @var StreamInterface
+     */
+    private $stream;
+
     /**
      * @param string $filename File to lazily open
      * @param string $mode     fopen mode to use when opening the stream
@@ -28,6 +33,10 @@ final class LazyOpenStream implements StreamInterface
     {
         $this->filename = $filename;
         $this->mode = $mode;
+
+        // unsetting the property forces the first access to go through
+        // __get().
+        unset($this->stream);
     }
 
     /**

+ 3 - 0
modules/boonex/xero/plugins/guzzlehttp/psr7/src/LimitStream.php

@@ -19,6 +19,9 @@ final class LimitStream implements StreamInterface
     /** @var int Limit the number of bytes that can be read */
     private $limit;
 
+    /** @var StreamInterface */
+    private $stream;
+
     /**
      * @param StreamInterface $stream Stream to wrap
      * @param int             $limit  Total number of bytes to allow to be read

+ 22 - 18
modules/boonex/xero/plugins/guzzlehttp/psr7/src/Message.php

@@ -18,31 +18,31 @@ final class Message
     public static function toString(MessageInterface $message): string
     {
         if ($message instanceof RequestInterface) {
-            $msg = trim($message->getMethod() . ' '
-                    . $message->getRequestTarget())
-                . ' HTTP/' . $message->getProtocolVersion();
+            $msg = trim($message->getMethod().' '
+                    .$message->getRequestTarget())
+                .' HTTP/'.$message->getProtocolVersion();
             if (!$message->hasHeader('host')) {
-                $msg .= "\r\nHost: " . $message->getUri()->getHost();
+                $msg .= "\r\nHost: ".$message->getUri()->getHost();
             }
         } elseif ($message instanceof ResponseInterface) {
-            $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
-                . $message->getStatusCode() . ' '
-                . $message->getReasonPhrase();
+            $msg = 'HTTP/'.$message->getProtocolVersion().' '
+                .$message->getStatusCode().' '
+                .$message->getReasonPhrase();
         } else {
             throw new \InvalidArgumentException('Unknown message type');
         }
 
         foreach ($message->getHeaders() as $name => $values) {
-            if (strtolower($name) === 'set-cookie') {
+            if (is_string($name) && strtolower($name) === 'set-cookie') {
                 foreach ($values as $value) {
-                    $msg .= "\r\n{$name}: " . $value;
+                    $msg .= "\r\n{$name}: ".$value;
                 }
             } else {
-                $msg .= "\r\n{$name}: " . implode(', ', $values);
+                $msg .= "\r\n{$name}: ".implode(', ', $values);
             }
         }
 
-        return "{$msg}\r\n\r\n" . $message->getBody();
+        return "{$msg}\r\n\r\n".$message->getBody();
     }
 
     /**
@@ -67,6 +67,7 @@ final class Message
             return null;
         }
 
+        $body->rewind();
         $summary = $body->read($truncateAt);
         $body->rewind();
 
@@ -76,7 +77,7 @@ final class Message
 
         // Matches any printable character, including unicode characters:
         // letters, marks, numbers, punctuation, spacing, and separators.
-        if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary)) {
+        if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary) !== 0) {
             return null;
         }
 
@@ -145,7 +146,7 @@ final class Message
 
         // If these aren't the same, then one line didn't match and there's an invalid header.
         if ($count !== substr_count($rawHeaders, "\n")) {
-            // Folding is deprecated, see https://tools.ietf.org/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)) {
                 throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
             }
@@ -175,6 +176,9 @@ final class Message
     public static function parseRequestUri(string $path, array $headers): string
     {
         $hostKey = array_filter(array_keys($headers), function ($k) {
+            // Numeric array keys are converted to int by PHP.
+            $k = (string) $k;
+
             return strtolower($k) === 'host';
         });
 
@@ -186,7 +190,7 @@ final class Message
         $host = $headers[reset($hostKey)][0];
         $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
 
-        return $scheme . '://' . $host . '/' . ltrim($path, '/');
+        return $scheme.'://'.$host.'/'.ltrim($path, '/');
     }
 
     /**
@@ -223,11 +227,11 @@ final class Message
     public static function parseResponse(string $message): ResponseInterface
     {
         $data = self::parseMessage($message);
-        // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
-        // between status-code and reason-phrase is required. But browsers accept
-        // responses without space and reason as well.
+        // According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
+        // the space between status-code and reason-phrase is required. But
+        // browsers accept responses without space and reason as well.
         if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
-            throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']);
+            throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']);
         }
         $parts = explode(' ', $data['start-line'], 3);
 

+ 50 - 20
modules/boonex/xero/plugins/guzzlehttp/psr7/src/MessageTrait.php

@@ -12,11 +12,11 @@ use Psr\Http\Message\StreamInterface;
  */
 trait MessageTrait
 {
-    /** @var array<string, string[]> Map of all registered headers, as original name => array of values */
+    /** @var string[][] Map of all registered headers, as original name => array of values */
     private $headers = [];
 
-    /** @var array<string, string> Map of lowercase header name => original name at registration */
-    private $headerNames  = [];
+    /** @var string[] Map of lowercase header name => original name at registration */
+    private $headerNames = [];
 
     /** @var string */
     private $protocol = '1.1';
@@ -37,6 +37,7 @@ trait MessageTrait
 
         $new = clone $this;
         $new->protocol = $version;
+
         return $new;
     }
 
@@ -135,21 +136,20 @@ trait MessageTrait
 
         $new = clone $this;
         $new->stream = $body;
+
         return $new;
     }
 
     /**
-     * @param array<string|int, string|string[]> $headers
+     * @param (string|string[])[] $headers
      */
     private function setHeaders(array $headers): void
     {
         $this->headerNames = $this->headers = [];
         foreach ($headers as $header => $value) {
-            if (is_int($header)) {
-                // Numeric array keys are converted to int by PHP but having a header name '123' is not forbidden by the spec
-                // and also allowed in withHeader(). So we need to cast it to string again for the following assertion to pass.
-                $header = (string) $header;
-            }
+            // Numeric array keys are converted to int by PHP.
+            $header = (string) $header;
+
             $this->assertHeader($header);
             $value = $this->normalizeHeaderValue($value);
             $normalized = strtolower($header);
@@ -171,14 +171,14 @@ trait MessageTrait
     private function normalizeHeaderValue($value): array
     {
         if (!is_array($value)) {
-            return $this->trimHeaderValues([$value]);
+            return $this->trimAndValidateHeaderValues([$value]);
         }
 
         if (count($value) === 0) {
             throw new \InvalidArgumentException('Header value can not be an empty array.');
         }
 
-        return $this->trimHeaderValues($value);
+        return $this->trimAndValidateHeaderValues($value);
     }
 
     /**
@@ -193,9 +193,9 @@ trait MessageTrait
      *
      * @return string[] Trimmed header values
      *
-     * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
+     * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
      */
-    private function trimHeaderValues(array $values): array
+    private function trimAndValidateHeaderValues(array $values): array
     {
         return array_map(function ($value) {
             if (!is_scalar($value) && null !== $value) {
@@ -205,12 +205,15 @@ trait MessageTrait
                 ));
             }
 
-            return trim((string) $value, " \t");
+            $trimmed = trim((string) $value, " \t");
+            $this->assertValue($trimmed);
+
+            return $trimmed;
         }, array_values($values));
     }
 
     /**
-     * @see https://tools.ietf.org/html/rfc7230#section-3.2
+     * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
      *
      * @param mixed $header
      */
@@ -223,12 +226,39 @@ trait MessageTrait
             ));
         }
 
-        if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) {
+        if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) {
+            throw new \InvalidArgumentException(
+                sprintf('"%s" is not valid header name.', $header)
+            );
+        }
+    }
+
+    /**
+     * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
+     *
+     * field-value    = *( field-content / obs-fold )
+     * field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+     * field-vchar    = VCHAR / obs-text
+     * VCHAR          = %x21-7E
+     * obs-text       = %x80-FF
+     * obs-fold       = CRLF 1*( SP / HTAB )
+     */
+    private function assertValue(string $value): void
+    {
+        // The regular expression intentionally does not support the obs-fold production, because as
+        // per RFC 7230#3.2.4:
+        //
+        // A sender MUST NOT generate a message that includes
+        // line folding (i.e., that has any field-value that contains a match to
+        // the obs-fold rule) unless the message is intended for packaging
+        // within the message/http media type.
+        //
+        // Clients must not send a request with line folding and a server sending folded headers is
+        // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
+        // folding is not likely to break any legitimate use case.
+        if (!preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) {
             throw new \InvalidArgumentException(
-                sprintf(
-                    '"%s" is not valid header name',
-                    $header
-                )
+                sprintf('"%s" is not valid header value.', $value)
             );
         }
     }

Plik diff jest za duży
+ 1230 - 101
modules/boonex/xero/plugins/guzzlehttp/psr7/src/MimeType.php


+ 25 - 13
modules/boonex/xero/plugins/guzzlehttp/psr7/src/MultipartStream.php

@@ -17,6 +17,9 @@ final class MultipartStream implements StreamInterface
     /** @var string */
     private $boundary;
 
+    /** @var StreamInterface */
+    private $stream;
+
     /**
      * @param array  $elements Array of associative arrays, each containing a
      *                         required "name" key mapping to the form field,
@@ -29,9 +32,9 @@ final class MultipartStream implements StreamInterface
      *
      * @throws \InvalidArgumentException
      */
-    public function __construct(array $elements = [], string $boundary = null)
+    public function __construct(array $elements = [], ?string $boundary = null)
     {
-        $this->boundary = $boundary ?: sha1(uniqid('', true));
+        $this->boundary = $boundary ?: bin2hex(random_bytes(20));
         $this->stream = $this->createStream($elements);
     }
 
@@ -48,7 +51,7 @@ final class MultipartStream implements StreamInterface
     /**
      * Get the headers needed before transferring the content of a POST file
      *
-     * @param array<string, string> $headers
+     * @param string[] $headers
      */
     private function getHeaders(array $headers): string
     {
@@ -57,7 +60,7 @@ final class MultipartStream implements StreamInterface
             $str .= "{$key}: {$value}\r\n";
         }
 
-        return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
+        return "--{$this->boundary}\r\n".trim($str)."\r\n\r\n";
     }
 
     /**
@@ -68,6 +71,9 @@ final class MultipartStream implements StreamInterface
         $stream = new AppendStream();
 
         foreach ($elements as $element) {
+            if (!is_array($element)) {
+                throw new \UnexpectedValueException('An array is expected');
+            }
             $this->addElement($stream, $element);
         }
 
@@ -89,7 +95,7 @@ final class MultipartStream implements StreamInterface
 
         if (empty($element['filename'])) {
             $uri = $element['contents']->getMetadata('uri');
-            if (substr($uri, 0, 6) !== 'php://') {
+            if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') {
                 $element['filename'] = $uri;
             }
         }
@@ -106,10 +112,15 @@ final class MultipartStream implements StreamInterface
         $stream->addStream(Utils::streamFor("\r\n"));
     }
 
+    /**
+     * @param string[] $headers
+     *
+     * @return array{0: StreamInterface, 1: string[]}
+     */
     private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array
     {
         // Set a default content-disposition header if one was no provided
-        $disposition = $this->getHeader($headers, 'content-disposition');
+        $disposition = self::getHeader($headers, 'content-disposition');
         if (!$disposition) {
             $headers['Content-Disposition'] = ($filename === '0' || $filename)
                 ? sprintf(
@@ -121,7 +132,7 @@ final class MultipartStream implements StreamInterface
         }
 
         // Set a default content-length header if one was no provided
-        $length = $this->getHeader($headers, 'content-length');
+        $length = self::getHeader($headers, 'content-length');
         if (!$length) {
             if ($length = $stream->getSize()) {
                 $headers['Content-Length'] = (string) $length;
@@ -129,21 +140,22 @@ final class MultipartStream implements StreamInterface
         }
 
         // Set a default Content-Type if one was not supplied
-        $type = $this->getHeader($headers, 'content-type');
+        $type = self::getHeader($headers, 'content-type');
         if (!$type && ($filename === '0' || $filename)) {
-            if ($type = MimeType::fromFilename($filename)) {
-                $headers['Content-Type'] = $type;
-            }
+            $headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream';
         }
 
         return [$stream, $headers];
     }
 
-    private function getHeader(array $headers, string $key)
+    /**
+     * @param string[] $headers
+     */
+    private static function getHeader(array $headers, string $key): ?string
     {
         $lowercaseHeader = strtolower($key);
         foreach ($headers as $k => $v) {
-            if (strtolower($k) === $lowercaseHeader) {
+            if (strtolower((string) $k) === $lowercaseHeader) {
                 return $v;
             }
         }

+ 3 - 0
modules/boonex/xero/plugins/guzzlehttp/psr7/src/NoSeekStream.php

@@ -13,6 +13,9 @@ final class NoSeekStream implements StreamInterface
 {
     use StreamDecoratorTrait;
 
+    /** @var StreamInterface */
+    private $stream;
+
     public function seek($offset, $whence = SEEK_SET): void
     {
         throw new \RuntimeException('Cannot seek a NoSeekStream');

+ 6 - 6
modules/boonex/xero/plugins/guzzlehttp/psr7/src/PumpStream.php

@@ -18,7 +18,7 @@ use Psr\Http\Message\StreamInterface;
  */
 final class PumpStream implements StreamInterface
 {
-    /** @var callable|null */
+    /** @var callable(int): (string|false|null)|null */
     private $source;
 
     /** @var int|null */
@@ -34,7 +34,7 @@ final class PumpStream implements StreamInterface
     private $buffer;
 
     /**
-     * @param callable(int): (string|null|false)  $source  Source of the stream data. The callable MAY
+     * @param callable(int): (string|false|null)  $source  Source of the stream data. The callable MAY
      *                                                     accept an integer argument used to control the
      *                                                     amount of data to return. The callable MUST
      *                                                     return a string when called, or false|null on error
@@ -60,6 +60,7 @@ final class PumpStream implements StreamInterface
                 throw $e;
             }
             trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
             return '';
         }
     }
@@ -149,8 +150,6 @@ final class PumpStream implements StreamInterface
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)
@@ -164,11 +163,12 @@ final class PumpStream implements StreamInterface
 
     private function pump(int $length): void
     {
-        if ($this->source) {
+        if ($this->source !== null) {
             do {
-                $data = call_user_func($this->source, $length);
+                $data = ($this->source)($length);
                 if ($data === false || $data === null) {
                     $this->source = null;
+
                     return;
                 }
                 $this->buffer->write($data);

+ 15 - 10
modules/boonex/xero/plugins/guzzlehttp/psr7/src/Query.php

@@ -43,7 +43,7 @@ final class Query
             $parts = explode('=', $kvp, 2);
             $key = $decoder($parts[0]);
             $value = isset($parts[1]) ? $decoder($parts[1]) : null;
-            if (!isset($result[$key])) {
+            if (!array_key_exists($key, $result)) {
                 $result[$key] = $value;
             } else {
                 if (!is_array($result[$key])) {
@@ -63,12 +63,15 @@ final class Query
      * string. This function does not modify the provided keys when an array is
      * encountered (like `http_build_query()` would).
      *
-     * @param array     $params   Query string parameters.
-     * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
-     *                            to encode using RFC3986, or PHP_QUERY_RFC1738
-     *                            to encode using RFC1738.
+     * @param array     $params           Query string parameters.
+     * @param int|false $encoding         Set to false to not encode,
+     *                                    PHP_QUERY_RFC3986 to encode using
+     *                                    RFC3986, or PHP_QUERY_RFC1738 to
+     *                                    encode using RFC1738.
+     * @param bool      $treatBoolsAsInts Set to true to encode as 0/1, and
+     *                                    false as false/true.
      */
-    public static function build(array $params, $encoding = PHP_QUERY_RFC3986): string
+    public static function build(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string
     {
         if (!$params) {
             return '';
@@ -86,22 +89,24 @@ final class Query
             throw new \InvalidArgumentException('Invalid type');
         }
 
+        $castBool = $treatBoolsAsInts ? static function ($v) { return (int) $v; } : static function ($v) { return $v ? 'true' : 'false'; };
+
         $qs = '';
         foreach ($params as $k => $v) {
             $k = $encoder((string) $k);
             if (!is_array($v)) {
                 $qs .= $k;
-                $v = is_bool($v) ? (int) $v : $v;
+                $v = is_bool($v) ? $castBool($v) : $v;
                 if ($v !== null) {
-                    $qs .= '=' . $encoder((string) $v);
+                    $qs .= '='.$encoder((string) $v);
                 }
                 $qs .= '&';
             } else {
                 foreach ($v as $vv) {
                     $qs .= $k;
-                    $vv = is_bool($vv) ? (int) $vv : $vv;
+                    $vv = is_bool($vv) ? $castBool($vv) : $vv;
                     if ($vv !== null) {
-                        $qs .= '=' . $encoder((string) $vv);
+                        $qs .= '='.$encoder((string) $vv);
                     }
                     $qs .= '&';
                 }

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików